Compare commits
63 Commits
dev
...
fix/ignore
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f953dbd49f | ||
|
|
01e7e559ba | ||
| b6bfef7715 | |||
| bf75f72bcd | |||
|
|
b0cb020d76 | ||
|
|
4239ffa00c | ||
|
|
427b6f8086 | ||
|
|
fbd2351073 | ||
|
|
150e90d19b | ||
|
|
a6cf8b13c6 | ||
|
|
d8ae8471ab | ||
|
|
0e65f8e68c | ||
|
|
3912ad593b | ||
|
|
1f6eaaeccc | ||
| 40f188c2e2 | |||
| 0aae3801b7 | |||
| 26902b7616 | |||
| ffaf2c77e0 | |||
| 23f7f7cd48 | |||
| c2879766eb | |||
| 01cc08d5f3 | |||
| c386e1194b | |||
| 67ed4e03eb | |||
| c7d1f80e79 | |||
| 7531e58114 | |||
| c1e329cb5a | |||
| dad52bf20e | |||
| 5fb6d03d9c | |||
| 1ef757d10f | |||
| 444abb83c2 | |||
| e835dcf6b1 | |||
| e61a19fbbf | |||
| a937b854cd | |||
| 6d8f17ac00 | |||
| 78dd4239cc | |||
| 39c5ca357e | |||
| 8249dbf03e | |||
| c4e30ee4a6 | |||
| 1291d20e55 | |||
| fb8d57c0ee | |||
| e79de0326c | |||
| de335af3c1 | |||
| 4be0bcd813 | |||
|
|
b3f46d8e84 | ||
|
|
e97246ee55 | ||
|
|
159fb73919 | ||
| 7c35a5675c | |||
| 45adddb7f7 | |||
|
|
4aad2bb292 | ||
|
|
4c9eca217e | ||
|
|
ec6700e1cb | ||
|
|
b55595108f | ||
|
|
8b3ab4a3a9 | ||
|
|
046b7926c4 | ||
|
|
5626466107 | ||
|
|
6c674bd596 | ||
|
|
9465a2b2a7 | ||
| 484d8cf4cb | |||
| 863f9c8077 | |||
| a8983074dd | |||
| e031c18ecb | |||
| ba075c3b14 | |||
| 32936c2aa5 |
3
.gitmodules
vendored
@@ -1,9 +1,6 @@
|
||||
[submodule "firka/lib/l10n"]
|
||||
path = firka/lib/l10n
|
||||
url = https://github.com/QwIT-Development/firka-localization
|
||||
[submodule "firka/android/app/src/main/java/org/brotli"]
|
||||
path = firka/android/app/src/main/java/org/brotli
|
||||
url = https://git.firka.app/firka/org_brotli
|
||||
[submodule "firka_wear/lib/l10n"]
|
||||
path = firka_wear/lib/l10n
|
||||
url = https://github.com/qwit-development/firka-localization
|
||||
|
||||
@@ -1,35 +1,19 @@
|
||||
# Flutter telepítése
|
||||
|
||||
A firka androidra való lebuildeléséhez kötelező a saját Flutter fork használata, illetve minden más fajta --release buildhez is.
|
||||
|
||||
A Flutter telepítéséhez a dokumentáció [itt](https://docs.flutter.dev/get-started/install) található.
|
||||
|
||||
A Flutter zip letöltése helyett a custom engine-t cloneold le ([https://git.firka.app/firka/flutter/](https://git.firka.app/firka/flutter/))
|
||||
|
||||
# Brotli
|
||||
|
||||
A firka brotlival compresseli a libflutter-t buildelés közben ezért szükséges a projekt
|
||||
buildeléséhez hogy a brotli a PATH-ben legyen
|
||||
|
||||
## Windows
|
||||
- Töltsd le a `brotli-x64-windows-static.zip`-et a [google/brotli github repoból](https://github.com/google/brotli/releases/latest)
|
||||
- Csomagold ki valahol (pl. C:\Users\\<username>\dev\brotli)
|
||||
- Add hozzá a mappát ahova kicsomagoltad (C:\Users\\<username>\dev\brotli) a PATH-hez
|
||||
- Ne felejtsd el újraindítani az IDE-det illetve parancssorodat utánna hogy frissüljön a PATH
|
||||
|
||||
## Linux/MacOS
|
||||
Telepítsd fel a brotli packaget a distro-d package managerével
|
||||
A projekt jelenleg a 3.41.2-es Flutter SDK-t használja.
|
||||
|
||||
# Keystore
|
||||
|
||||
[Secrets dokumentáció](secrets/README.md)
|
||||
|
||||
# Flutter l10n
|
||||
# Fileok generálása
|
||||
|
||||
Flutter l10n fileok generálása
|
||||
Flutter l10n és egyéb fileok generálása
|
||||
|
||||
```shell
|
||||
Flutter gen-l10n --template-arb-file app_hu.arb
|
||||
$ cd firka # vagy firka_wear
|
||||
$ dart run scripts/codegen.dart
|
||||
```
|
||||
|
||||
# Android debug build
|
||||
@@ -42,20 +26,10 @@ $ Flutter build apk --debug --target-platform android-arm,android-arm64,android-
|
||||
|
||||
# Android release build
|
||||
|
||||
A release buildhez közelező egy keystore használata, illetve a saját Flutter engineünk használata.
|
||||
A release buildhez közelező egy keystore használata.
|
||||
|
||||
## Custom Flutter engine setupolása
|
||||
## Release appbundle buildelése (firka és firka_wear)
|
||||
|
||||
```shell
|
||||
$ git clone https://git.firka.app/firka/flutter
|
||||
$ cd flutter
|
||||
$ . dev/tools/envsetup.sh
|
||||
$ gclient sync -D
|
||||
$ ./dev/tools/build_release.sh
|
||||
```
|
||||
|
||||
## Release apk buildelése
|
||||
|
||||
```shell
|
||||
$ ./tools/linux/build_apk.sh main
|
||||
$ ./build.sh
|
||||
```
|
||||
@@ -1,41 +1,24 @@
|
||||
# Installing flutter
|
||||
# Installing Flutter
|
||||
|
||||
To build firka you will have to use our custom Flutter fork,
|
||||
and to make a release build you will have to use our custom
|
||||
flutter engine.
|
||||
The documentation for installing flutter can be found [here](https://docs.flutter.dev/get-started/install).
|
||||
|
||||
Instead of downloading the regular flutter zip, clone it from ([https://git.firka.app/firka/flutter/](https://git.firka.app/firka/flutter/)).
|
||||
|
||||
# Brotli
|
||||
|
||||
Firka uses brotli to compress libflutter during the build process to make the app smaller,
|
||||
so building Firka requires you to have brotli in your path
|
||||
|
||||
## Windows
|
||||
- Download `brotli-x64-windows-static.zip` from [google/brotli](https://github.com/google/brotli/releases/latest)
|
||||
- Extract it to somewhere like C:\Users\\<username>\dev\brotli
|
||||
- Add the directory (ex. C:\Users\\<username>\dev\brotli) to your PATH
|
||||
- Don't forget to restart your IDE or terminal sessions for the PATH variable to update
|
||||
|
||||
## Linux/MacOS
|
||||
Install it using your distro's package manager
|
||||
Flutter installation documentation can be found [here](https://docs.flutter.dev/get-started/install).
|
||||
The project currently uses Flutter SDK 3.41.2.
|
||||
|
||||
# Keystore
|
||||
|
||||
[Secrets docs](secrets/README_en.md)
|
||||
[Secrets documentation](secrets/README.md)
|
||||
|
||||
# Flutter l10n
|
||||
# Generating files
|
||||
|
||||
Generating flutter l10n files
|
||||
Generating Flutter l10n and other files
|
||||
|
||||
```shell
|
||||
flutter gen-l10n --template-arb-file app_hu.arb
|
||||
$ cd firka # or firka_wear
|
||||
$ dart run scripts/codegen.dart
|
||||
```
|
||||
|
||||
# Android debug build
|
||||
|
||||
The dev build doesn't require using a custom keystore
|
||||
The dev build does not require using a keystore
|
||||
```shell
|
||||
$ cd firka
|
||||
$ flutter build apk --debug --target-platform android-arm,android-arm64,android-x64
|
||||
@@ -43,20 +26,10 @@ $ flutter build apk --debug --target-platform android-arm,android-arm64,android-
|
||||
|
||||
# Android release build
|
||||
|
||||
The release build requires using a custom keystore and our custom flutter fork
|
||||
The release build requires using a keystore.
|
||||
|
||||
## Setting up our flutter engine fork
|
||||
## Building the release appbundle (firka and firka_wear)
|
||||
|
||||
```shell
|
||||
$ git clone https://git.firka.app/firka/flutter
|
||||
$ cd flutter
|
||||
$ . dev/tools/envsetup.sh
|
||||
$ gclient sync -D
|
||||
$ ./dev/tools/build_release.sh
|
||||
$ ./build.sh
|
||||
```
|
||||
|
||||
## Building the release apk
|
||||
|
||||
```shell
|
||||
$ ./tools/linux/build_apk.sh main
|
||||
```
|
||||
52
build.ps1
Normal file
@@ -0,0 +1,52 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$ROOT = $PSScriptRoot
|
||||
$SHA = (git -C $ROOT rev-parse --short HEAD)
|
||||
$COMMIT_COUNT = [int](git -C $ROOT rev-list --count HEAD)
|
||||
|
||||
function Build-App {
|
||||
param([string]$App)
|
||||
|
||||
$pubspec = Join-Path $ROOT $App "pubspec.yaml"
|
||||
if (-not (Test-Path $pubspec)) {
|
||||
Write-Error "Not found: $pubspec"
|
||||
}
|
||||
|
||||
$versionLine = Get-Content $pubspec | Select-String -Pattern '^\s*version:\s*' | Select-Object -First 1
|
||||
if (-not $versionLine) {
|
||||
Write-Error "No version line in $pubspec"
|
||||
}
|
||||
$line = $versionLine.Line
|
||||
if ($line -match '^\s*version:\s*([^+\s]+)') {
|
||||
$baseVersion = $Matches[1].Trim()
|
||||
} else {
|
||||
Write-Error "Could not parse version from: $line"
|
||||
}
|
||||
|
||||
$buildName = "${baseVersion}-${SHA}"
|
||||
$versionCode = 2000 + $COMMIT_COUNT
|
||||
if ($App -eq "firka_wear") {
|
||||
$versionCode += 1
|
||||
}
|
||||
|
||||
Write-Host "Building $App : version $buildName (version code: $versionCode)"
|
||||
Push-Location (Join-Path $ROOT $App)
|
||||
try {
|
||||
flutter pub get
|
||||
dart run scripts/codegen.dart
|
||||
flutter build appbundle --build-name="$buildName" --build-number="$versionCode" --verbose
|
||||
} finally {
|
||||
Pop-Location
|
||||
}
|
||||
}
|
||||
|
||||
$target = if ($args.Count -gt 0) { $args[0] } else { "all" }
|
||||
|
||||
switch ($target) {
|
||||
"firka" { Build-App firka }
|
||||
"firka_wear" { Build-App firka_wear }
|
||||
"all" { Build-App firka; Build-App firka_wear }
|
||||
default {
|
||||
Write-Error "Usage: $MyInvocation.MyCommand.Name [firka|firka_wear|all]"
|
||||
}
|
||||
}
|
||||
41
build.sh
Normal file
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")" && pwd)"
|
||||
SHA=$(git -C "$ROOT" rev-parse --short HEAD)
|
||||
COMMIT_COUNT=$(git -C "$ROOT" rev-list --count HEAD)
|
||||
|
||||
build_app() {
|
||||
local APP="$1"
|
||||
local PUBSPEC="$ROOT/$APP/pubspec.yaml"
|
||||
if [[ ! -f "$PUBSPEC" ]]; then
|
||||
echo "Not found: $PUBSPEC" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local VERSION_LINE BASE_VERSION BUILD_NAME VERSION_CODE
|
||||
VERSION_LINE=$(grep -E '^\s*version:\s*' "$PUBSPEC" | head -1)
|
||||
BASE_VERSION=$(echo "$VERSION_LINE" | sed -E 's/^[[:space:]]*version:[[:space:]]*([^+]+).*/\1/' | tr -d ' ')
|
||||
BUILD_NAME="${BASE_VERSION}-${SHA}"
|
||||
|
||||
VERSION_CODE=$((2000 + COMMIT_COUNT))
|
||||
[[ "$APP" == "firka_wear" ]] && VERSION_CODE=$((VERSION_CODE + 1))
|
||||
|
||||
echo "Building $APP: version $BUILD_NAME (version code: $VERSION_CODE)"
|
||||
cd "$ROOT/$APP"
|
||||
|
||||
flutter pub get
|
||||
dart run scripts/codegen.dart
|
||||
|
||||
flutter build appbundle --build-name="$BUILD_NAME" --build-number="$VERSION_CODE" --verbose
|
||||
}
|
||||
|
||||
case "${1:-all}" in
|
||||
firka) build_app firka ;;
|
||||
firka_wear) build_app firka_wear ;;
|
||||
all) build_app firka && build_app firka_wear ;;
|
||||
*)
|
||||
echo "Usage: $0 [firka|firka_wear|all]" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
1
firka/android/app/proguard-rules.pro
vendored
@@ -1,2 +1 @@
|
||||
-keep class org.brotli.** { *; }
|
||||
-keep class app.firka.naplo.glance.** { *; }
|
||||
@@ -11,7 +11,6 @@
|
||||
android:name=".AppMain"
|
||||
android:icon="@mipmap/launcher_icon">
|
||||
|
||||
|
||||
<service
|
||||
android:name=".WearSyncForegroundService"
|
||||
android:exported="false"
|
||||
|
||||
BIN
firka/assets/icons/button/colorwheel.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
15
firka/assets/icons/dave.svg
Normal file
|
After Width: | Height: | Size: 18 KiB |
14
firka/assets/images/carousel/card1.svg
Normal file
|
After Width: | Height: | Size: 15 KiB |
6
firka/assets/images/carousel/card2.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="108" height="48" viewBox="0 0 108 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="108" height="48" rx="12" fill="#F3FBDE"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M30.6555 15.6657C30.968 15.3533 31.3919 15.1777 31.8338 15.1777C32.2758 15.1777 32.6996 15.3533 33.0122 15.6657L34.3338 16.9874C34.6463 17.2999 34.8218 17.7238 34.8218 18.1657C34.8218 18.6077 34.6463 19.0315 34.3338 19.344L33.0122 20.6657L29.3338 16.9874L30.6555 15.6657ZM28.1555 18.1657L23.9888 22.3324C23.6762 22.6449 23.5006 23.0687 23.5005 23.5107V24.8324C23.5005 25.2744 23.6761 25.6983 23.9886 26.0109C24.3012 26.3235 24.7251 26.499 25.1672 26.499H26.4888C26.9308 26.499 27.3547 26.3233 27.6672 26.0107L31.8338 21.844L28.1555 18.1657Z" fill="#A7DC22"/>
|
||||
<path d="M21.0005 25.666H20.1672C19.7251 25.666 19.3012 25.8416 18.9886 26.1542C18.6761 26.4667 18.5005 26.8907 18.5005 27.3327C18.5005 27.7747 18.6761 28.1986 18.9886 28.5112C19.3012 28.8238 19.7251 28.9993 20.1672 28.9993H31.8338C32.2758 28.9993 32.6998 29.1749 33.0123 29.4875C33.3249 29.8001 33.5005 30.224 33.5005 30.666C33.5005 31.108 33.3249 31.532 33.0123 31.8445C32.6998 32.1571 32.2758 32.3327 31.8338 32.3327H28.5005" stroke="#A7DC22" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M47.648 29.5V19.996H44.208V18.3H52.944V19.996H49.504V29.5H47.648ZM56.7006 29.692C55.922 29.692 55.2286 29.516 54.6206 29.164C54.0233 28.8013 53.5486 28.3053 53.1966 27.676C52.8553 27.0467 52.6846 26.3213 52.6846 25.5C52.6846 24.6787 52.8606 23.9533 53.2126 23.324C53.5646 22.6947 54.0446 22.204 54.6526 21.852C55.2713 21.4893 55.9753 21.308 56.7646 21.308C57.4793 21.308 58.1246 21.4947 58.7006 21.868C59.2766 22.2307 59.73 22.7587 60.0606 23.452C60.402 24.1453 60.5726 24.972 60.5726 25.932H54.2846L54.5246 25.708C54.5246 26.1987 54.6313 26.6253 54.8446 26.988C55.058 27.34 55.3406 27.612 55.6926 27.804C56.0446 27.996 56.434 28.092 56.8606 28.092C57.3513 28.092 57.7566 27.9853 58.0766 27.772C58.3966 27.548 58.6473 27.26 58.8286 26.908L60.4126 27.58C60.1886 28.0067 59.9006 28.38 59.5486 28.7C59.2073 29.02 58.7966 29.2653 58.3166 29.436C57.8473 29.6067 57.3086 29.692 56.7006 29.692ZM54.6366 24.78L54.3806 24.556H58.8926L58.6526 24.78C58.6526 24.3427 58.5566 23.9853 58.3646 23.708C58.1726 23.42 57.9273 23.2067 57.6286 23.068C57.3406 22.9187 57.0366 22.844 56.7166 22.844C56.3966 22.844 56.0766 22.9187 55.7566 23.068C55.4366 23.2067 55.17 23.42 54.9566 23.708C54.7433 23.9853 54.6366 24.3427 54.6366 24.78ZM55.5326 20.46L57.3886 18.3H59.4686L57.4046 20.46H55.5326ZM61.9764 29.5V21.5H63.6564L63.7364 22.572C63.9817 22.156 64.2964 21.8413 64.6804 21.628C65.0644 21.4147 65.5017 21.308 65.9924 21.308C66.6324 21.308 67.1764 21.452 67.6244 21.74C68.0724 22.028 68.3977 22.4653 68.6004 23.052C68.835 22.4867 69.1657 22.0547 69.5924 21.756C70.019 21.4573 70.5204 21.308 71.0964 21.308C72.0244 21.308 72.739 21.6067 73.2404 22.204C73.7417 22.7907 73.987 23.6973 73.9764 24.924V29.5H72.2004V25.404C72.2004 24.764 72.131 24.2733 71.9924 23.932C71.8537 23.58 71.667 23.3347 71.4324 23.196C71.1977 23.0573 70.9257 22.988 70.6164 22.988C70.0617 22.9773 69.6297 23.1747 69.3204 23.58C69.0217 23.9853 68.8724 24.5667 68.8724 25.324V29.5H67.0804V25.404C67.0804 24.764 67.011 24.2733 66.8724 23.932C66.7444 23.58 66.563 23.3347 66.3284 23.196C66.0937 23.0573 65.8217 22.988 65.5124 22.988C64.9577 22.9773 64.5257 23.1747 64.2164 23.58C63.9177 23.9853 63.7684 24.5667 63.7684 25.324V29.5H61.9764ZM80.6845 29.5L80.6045 27.996V25.388C80.6045 24.844 80.5458 24.3907 80.4285 24.028C80.3218 23.6547 80.1405 23.372 79.8845 23.18C79.6392 22.9773 79.3085 22.876 78.8925 22.876C78.5085 22.876 78.1725 22.956 77.8845 23.116C77.5965 23.276 77.3512 23.5267 77.1485 23.868L75.5805 23.292C75.7512 22.94 75.9752 22.6147 76.2525 22.316C76.5405 22.0067 76.8978 21.7613 77.3245 21.58C77.7618 21.3987 78.2845 21.308 78.8925 21.308C79.6712 21.308 80.3218 21.4627 80.8445 21.772C81.3672 22.0707 81.7512 22.5027 81.9965 23.068C82.2525 23.6333 82.3805 24.316 82.3805 25.116L82.3325 29.5H80.6845ZM78.3805 29.692C77.4205 29.692 76.6738 29.4787 76.1405 29.052C75.6178 28.6253 75.3565 28.0227 75.3565 27.244C75.3565 26.412 75.6338 25.7773 76.1885 25.34C76.7538 24.9027 77.5378 24.684 78.5405 24.684H80.6845V26.06H79.1165C78.4018 26.06 77.9005 26.1613 77.6125 26.364C77.3245 26.556 77.1805 26.8333 77.1805 27.196C77.1805 27.5053 77.3032 27.7507 77.5485 27.932C77.8045 28.1027 78.1565 28.188 78.6045 28.188C79.0098 28.188 79.3618 28.0973 79.6605 27.916C79.9592 27.7347 80.1885 27.4947 80.3485 27.196C80.5192 26.8973 80.6045 26.5613 80.6045 26.188H81.1325C81.1325 27.276 80.9138 28.1347 80.4765 28.764C80.0392 29.3827 79.3405 29.692 78.3805 29.692ZM77.5965 20.46L79.4525 18.3H81.5325L79.4685 20.46H77.5965ZM85.829 27.26L84.741 26.028L88.901 21.5H91.061L85.829 27.26ZM84.117 29.5V18.3H85.909V29.5H84.117ZM89.205 29.5L86.389 25.356L87.557 24.108L91.333 29.5H89.205Z" fill="#394C0A"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.8 KiB |
5
firka/assets/images/carousel/card3.svg
Normal file
|
After Width: | Height: | Size: 17 KiB |
5
firka/assets/images/carousel/card4.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg width="108" height="48" viewBox="0 0 108 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="108" height="48" rx="12" fill="#F3FBDE"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M26.8026 15.9055C26.7524 15.8382 26.6891 15.7817 26.6166 15.7394C26.544 15.6971 26.4637 15.6699 26.3804 15.6593C26.2971 15.6487 26.2125 15.655 26.1317 15.6778C26.0509 15.7007 25.9755 15.7396 25.9101 15.7922C24.3129 17.0742 23.2594 18.9127 22.9609 20.9388C22.4141 20.5413 21.9344 20.0589 21.5401 19.5097C21.4866 19.4349 21.4173 19.3729 21.3371 19.328C21.2569 19.2831 21.1678 19.2564 21.0761 19.2499C20.9845 19.2434 20.8925 19.2572 20.8067 19.2902C20.721 19.3233 20.6436 19.3749 20.5801 19.4413C19.4795 20.5926 18.774 22.0644 18.5658 23.6435C18.3576 25.2225 18.6575 26.8268 19.4221 28.2241C20.1866 29.6213 21.3761 30.7388 22.8182 31.4148C24.2604 32.0908 25.8803 32.2902 27.4433 31.984C29.0063 31.6778 30.4312 30.882 31.5117 29.7118C32.5922 28.5416 33.2721 27.0579 33.453 25.4755C33.6338 23.893 33.3062 22.2941 32.5176 20.9104C31.729 19.5266 30.5204 18.4298 29.0667 17.7788C28.1732 17.3443 27.3967 16.7019 26.8026 15.9055ZM29.1251 25.8755C29.1248 26.3275 29.0264 26.7741 28.8367 27.1844C28.6471 27.5948 28.3707 27.959 28.0266 28.2522C27.6825 28.5453 27.2789 28.7603 26.8437 28.8823C26.4084 29.0044 25.9519 29.0305 25.5056 28.959C25.0592 28.8875 24.6337 28.7199 24.2584 28.468C23.8831 28.2161 23.5669 27.8857 23.3317 27.4998C23.0964 27.1138 22.9477 26.6814 22.8958 26.2323C22.8438 25.7833 22.8899 25.3283 23.0309 24.8988C23.5542 25.2863 24.1559 25.5738 24.8084 25.7322C24.986 24.5915 25.5528 23.5475 26.4126 22.7772C27.1634 22.8772 27.8524 23.2465 28.3513 23.8164C28.8503 24.3863 29.1252 25.118 29.1251 25.8755Z" fill="#A7DC22"/>
|
||||
<path d="M49.008 29.692C48.4213 29.692 47.8827 29.6227 47.392 29.484C46.912 29.3453 46.4853 29.1533 46.112 28.908C45.7493 28.6627 45.4453 28.3907 45.2 28.092C44.9653 27.7827 44.8 27.4627 44.704 27.132L46.528 26.572C46.6667 26.9667 46.9387 27.3133 47.344 27.612C47.7493 27.9107 48.2507 28.0653 48.848 28.076C49.5413 28.076 50.0907 27.932 50.496 27.644C50.9013 27.356 51.104 26.9773 51.104 26.508C51.104 26.0813 50.9333 25.7347 50.592 25.468C50.2507 25.1907 49.792 24.9773 49.216 24.828L47.84 24.476C47.3173 24.3373 46.8427 24.1347 46.416 23.868C46 23.6013 45.6693 23.2653 45.424 22.86C45.1893 22.4547 45.072 21.9747 45.072 21.42C45.072 20.3747 45.4133 19.564 46.096 18.988C46.7787 18.4013 47.7547 18.108 49.024 18.108C49.7387 18.108 50.3627 18.22 50.896 18.444C51.44 18.6573 51.888 18.956 52.24 19.34C52.592 19.7133 52.8533 20.14 53.024 20.62L51.232 21.196C51.072 20.7693 50.7947 20.4173 50.4 20.14C50.0053 19.8627 49.5147 19.724 48.928 19.724C48.32 19.724 47.84 19.868 47.488 20.156C47.1467 20.444 46.976 20.844 46.976 21.356C46.976 21.772 47.1093 22.0973 47.376 22.332C47.6533 22.556 48.0267 22.7267 48.496 22.844L49.872 23.18C50.8747 23.4253 51.6533 23.8467 52.208 24.444C52.7627 25.0413 53.04 25.7027 53.04 26.428C53.04 27.068 52.8853 27.6333 52.576 28.124C52.2667 28.6147 51.808 28.9987 51.2 29.276C50.6027 29.5533 49.872 29.692 49.008 29.692ZM57.9416 29.692C57.099 29.692 56.4536 29.484 56.0056 29.068C55.5683 28.6413 55.3496 28.0333 55.3496 27.244V19.004H57.1256V26.908C57.1256 27.2813 57.211 27.564 57.3816 27.756C57.563 27.948 57.8243 28.044 58.1656 28.044C58.2723 28.044 58.3896 28.0227 58.5176 27.98C58.6456 27.9373 58.7896 27.8573 58.9496 27.74L59.6056 29.1C59.3283 29.292 59.051 29.436 58.7736 29.532C58.4963 29.6387 58.219 29.692 57.9416 29.692ZM54.0216 23.036V21.5H59.2856V23.036H54.0216ZM62.2229 25.244C62.2229 24.38 62.3882 23.6707 62.7189 23.116C63.0495 22.5613 63.4762 22.1507 63.9989 21.884C64.5322 21.6067 65.0869 21.468 65.6629 21.468V23.18C65.1722 23.18 64.7082 23.2493 64.2709 23.388C63.8442 23.516 63.4975 23.7293 63.2309 24.028C62.9642 24.3267 62.8309 24.7213 62.8309 25.212L62.2229 25.244ZM61.0389 29.5V21.5H62.8309V29.5H61.0389ZM70.4194 29.692C69.6407 29.692 68.9474 29.516 68.3394 29.164C67.742 28.8013 67.2674 28.3053 66.9154 27.676C66.574 27.0467 66.4034 26.3213 66.4034 25.5C66.4034 24.6787 66.5794 23.9533 66.9314 23.324C67.2834 22.6947 67.7634 22.204 68.3714 21.852C68.99 21.4893 69.694 21.308 70.4834 21.308C71.198 21.308 71.8434 21.4947 72.4194 21.868C72.9954 22.2307 73.4487 22.7587 73.7794 23.452C74.1207 24.1453 74.2914 24.972 74.2914 25.932H68.0034L68.2434 25.708C68.2434 26.1987 68.35 26.6253 68.5634 26.988C68.7767 27.34 69.0594 27.612 69.4114 27.804C69.7634 27.996 70.1527 28.092 70.5794 28.092C71.07 28.092 71.4754 27.9853 71.7954 27.772C72.1154 27.548 72.366 27.26 72.5474 26.908L74.1314 27.58C73.9074 28.0067 73.6194 28.38 73.2674 28.7C72.926 29.02 72.5154 29.2653 72.0354 29.436C71.566 29.6067 71.0274 29.692 70.4194 29.692ZM68.3554 24.78L68.0994 24.556H72.6114L72.3714 24.78C72.3714 24.3427 72.2754 23.9853 72.0834 23.708C71.8914 23.42 71.646 23.2067 71.3474 23.068C71.0594 22.9187 70.7554 22.844 70.4354 22.844C70.1154 22.844 69.7954 22.9187 69.4754 23.068C69.1554 23.2067 68.8887 23.42 68.6754 23.708C68.462 23.9853 68.3554 24.3427 68.3554 24.78ZM80.5439 29.5L80.4639 27.996V25.388C80.4639 24.844 80.4052 24.3907 80.2879 24.028C80.1812 23.6547 79.9999 23.372 79.7439 23.18C79.4985 22.9773 79.1679 22.876 78.7519 22.876C78.3679 22.876 78.0319 22.956 77.7439 23.116C77.4559 23.276 77.2105 23.5267 77.0079 23.868L75.4399 23.292C75.6105 22.94 75.8345 22.6147 76.1119 22.316C76.3999 22.0067 76.7572 21.7613 77.1839 21.58C77.6212 21.3987 78.1439 21.308 78.7519 21.308C79.5305 21.308 80.1812 21.4627 80.7039 21.772C81.2265 22.0707 81.6105 22.5027 81.8559 23.068C82.1119 23.6333 82.2399 24.316 82.2399 25.116L82.1919 29.5H80.5439ZM78.2399 29.692C77.2799 29.692 76.5332 29.4787 75.9999 29.052C75.4772 28.6253 75.2159 28.0227 75.2159 27.244C75.2159 26.412 75.4932 25.7773 76.0479 25.34C76.6132 24.9027 77.3972 24.684 78.3999 24.684H80.5439V26.06H78.9759C78.2612 26.06 77.7599 26.1613 77.4719 26.364C77.1839 26.556 77.0399 26.8333 77.0399 27.196C77.0399 27.5053 77.1625 27.7507 77.4079 27.932C77.6639 28.1027 78.0159 28.188 78.4639 28.188C78.8692 28.188 79.2212 28.0973 79.5199 27.916C79.8185 27.7347 80.0479 27.4947 80.2079 27.196C80.3785 26.8973 80.4639 26.5613 80.4639 26.188H80.9919C80.9919 27.276 80.7732 28.1347 80.3359 28.764C79.8985 29.3827 79.1999 29.692 78.2399 29.692ZM85.7665 27.26L84.6785 26.028L88.8385 21.5H90.9985L85.7665 27.26ZM84.0545 29.5V18.3H85.8465V29.5H84.0545ZM89.1425 29.5L86.3265 25.356L87.4945 24.108L91.2705 29.5H89.1425Z" fill="#394C0A"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.3 KiB |
6
firka/assets/images/carousel/card5.svg
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
5
firka/assets/images/carousel/card6.svg
Normal file
|
After Width: | Height: | Size: 20 KiB |
6
firka/assets/images/carousel/card7.svg
Normal file
|
After Width: | Height: | Size: 14 KiB |
21
firka/codegen-lock.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
icons:
|
||||
"flutter_launcher_icons.yaml": "c600507ca0df7cebd0f708124842512a14ed3d597b779176200d6ba25b1335b1"
|
||||
"pubspec.yaml": "a6ae0bd67a2b6226bec2dfd55437b3db2b7ca4a03f315c1a0a8c2f4b505c7c87"
|
||||
"assets/images/logos/colored_logo.webp": "4b4fa99d144fe6694aa4487ba1b26aeecafae41e3c877836cd7da28d61a77983"
|
||||
"assets/images/logos/monochrome_logo.png": "188d2b0a64c70323b09bcee721663d6698fb557066f20ddaec97bba6869c1c6c"
|
||||
"assets/images/logos/colored_logo_without_mustache.png": "d11cff9f38985885873bfdd2d84e61f8fab03803eada94d4caac1545ef3685f3"
|
||||
"assets/images/logos/colored_logo_only_mustache.png": "bad6220c11bdfb1dfe04e5173bd2ebedd3999689d4b3a68fc63dc520c96dd33b"
|
||||
l10n:
|
||||
"l10n.yml": "a57bc304cac4a2b0235593586f17f400a5165d67fc9aadeaa11893cfa36ee082"
|
||||
"lib/l10n/app_de.arb": "ecfbf13bd33be9d27a2b54bfd8fb61e46c1a1dce905869d3f30cd05b4aecf258"
|
||||
"lib/l10n/app_en.arb": "8bb0064984deb02eefaec24c13ee019eb38758e115b678ce85335fd4d1b6b6d9"
|
||||
"lib/l10n/app_hu.arb": "2d2c568a3767a5b0bcfde7d27f221fd74b653256bbe41789756c78422a159e4d"
|
||||
isar:
|
||||
"lib/data/models/app_settings_model.dart": "5eb5af345f1347f104257f0999763650fe2673f9da1754bd12d3f756fe5c9723"
|
||||
"lib/data/models/generic_cache_model.dart": "79151d0467fb5d40c532eaaa08ad7c7e24a34304199280fbf49cf6e5adcce6bc"
|
||||
"lib/data/models/homework_cache_model.dart": "45789970b27d5790cdc54c292ef2f5feaa5f4e293b8a8862fd676d5eb3e25d29"
|
||||
"lib/data/models/timetable_cache_model.dart": "b972bf51e399f8d20d4f9ad660082d4cc4a9798df9ac9d6ec9ef6ac640205572"
|
||||
"lib/data/models/token_model.dart": "8c957cd07e473827d78fd8fd4fb6c1336b636a69c25c93618e1e7f94b7cf0683"
|
||||
splash:
|
||||
"flutter_native_splash.yaml": "0fd4a85d6f950d97298e99916927649940ffcfdadfc136ceee126fed0dbaa9f2"
|
||||
"assets/images/logos/splash.png": "88fbebc3d686cb9095bcce362029b69978b1b14270e465e91d962b1425db1152"
|
||||
@@ -2,15 +2,15 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
|
||||
<string>$(TeamIdentifierPrefix)app.firka.firka</string>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.app.firka.firkaa</string>
|
||||
<string>group.app.firka.firka</string>
|
||||
</array>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)app.firka.shared</string>
|
||||
</array>
|
||||
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
|
||||
<string>$(TeamIdentifierPrefix)app.firka.firkaa</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -31,7 +31,7 @@ class WatchL10n {
|
||||
private let languageKey = "watch_language"
|
||||
private let syncWithiPhoneKey = "watch_sync_language_with_iphone"
|
||||
private let lastAppliedSharedLanguageVersionKey = "watch_last_applied_shared_language_version"
|
||||
private static let appGroupID = "group.app.firka.firkaa"
|
||||
private static let appGroupID = "group.app.firka.firka"
|
||||
private var appGroupDefaults: UserDefaults? {
|
||||
UserDefaults(suiteName: Self.appGroupID)
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class DataStore {
|
||||
(error == "token_expired" || error == "no_token") && recoveryAttempted && !isRecoveringToken
|
||||
}
|
||||
|
||||
private let appGroupID = "group.app.firka.firkaa"
|
||||
private let appGroupID = "group.app.firka.firka"
|
||||
private let cacheFileName = "watch_data.json"
|
||||
private let lastHandledSessionStateVersionKey = "firka.watch.last_handled_session_state_version"
|
||||
private let lastHandledSessionActiveStudentIdNormKey = "firka.watch.last_handled_session_active_student_id_norm"
|
||||
|
||||
@@ -5,7 +5,7 @@ import SwiftUI
|
||||
// MARK: - Complication Localization Helper
|
||||
|
||||
private struct ComplicationL10n {
|
||||
private static let appGroupID = "group.app.firka.firkaa"
|
||||
private static let appGroupID = "group.app.firka.firka"
|
||||
|
||||
enum Language: String {
|
||||
case hungarian = "hu"
|
||||
@@ -63,7 +63,7 @@ private struct ComplicationL10n {
|
||||
// MARK: - Watch Cache Loader
|
||||
|
||||
private struct WatchCacheLoader {
|
||||
private static let appGroupID = "group.app.firka.firkaa"
|
||||
private static let appGroupID = "group.app.firka.firka"
|
||||
private static let cacheFileName = "watch_data.json"
|
||||
|
||||
static func loadWidgetData() -> WidgetData? {
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
|
||||
<string>$(TeamIdentifierPrefix)app.firka.firka</string>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.app.firka.firkaa</string>
|
||||
<string>group.app.firka.firka</string>
|
||||
</array>
|
||||
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
|
||||
<string>$(TeamIdentifierPrefix)app.firka.firkaa</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.app.firka.firkaa</string>
|
||||
</array>
|
||||
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
|
||||
<string>$(TeamIdentifierPrefix)$(CFBundleIdentifier)</string>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.app.firka.firka</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -2,7 +2,7 @@ import WidgetKit
|
||||
import SwiftUI
|
||||
import AppIntents
|
||||
|
||||
private let appGroup = "group.app.firka.firkaa"
|
||||
private let appGroup = "group.app.firka.firka"
|
||||
|
||||
// MARK: - Navigation Intents (iOS 16+, used by Controls and Shortcuts)
|
||||
|
||||
@@ -46,7 +46,7 @@ struct OpenTimetableIntent: AppIntent {
|
||||
|
||||
@available(iOS 18.0, *)
|
||||
struct HomeControl: ControlWidget {
|
||||
static let kind = "app.firka.firkaa.control.home"
|
||||
static let kind = "app.firka.firka.control.home"
|
||||
|
||||
var body: some ControlWidgetConfiguration {
|
||||
StaticControlConfiguration(kind: Self.kind) {
|
||||
@@ -63,7 +63,7 @@ struct HomeControl: ControlWidget {
|
||||
|
||||
@available(iOS 18.0, *)
|
||||
struct GradesControl: ControlWidget {
|
||||
static let kind = "app.firka.firkaa.control.grades"
|
||||
static let kind = "app.firka.firka.control.grades"
|
||||
|
||||
var body: some ControlWidgetConfiguration {
|
||||
StaticControlConfiguration(kind: Self.kind) {
|
||||
@@ -80,7 +80,7 @@ struct GradesControl: ControlWidget {
|
||||
|
||||
@available(iOS 18.0, *)
|
||||
struct TimetableControl: ControlWidget {
|
||||
static let kind = "app.firka.firkaa.control.timetable"
|
||||
static let kind = "app.firka.firka.control.timetable"
|
||||
|
||||
var body: some ControlWidgetConfiguration {
|
||||
StaticControlConfiguration(kind: Self.kind) {
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
<dict>
|
||||
<key>aps-environment</key>
|
||||
<string>development</string>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.app.firka.firkaa</string>
|
||||
</array>
|
||||
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
|
||||
<string>$(TeamIdentifierPrefix)$(CFBundleIdentifier)</string>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.app.firka.firka</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 70;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
@@ -222,35 +222,35 @@
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
4F0EA0512F2BD2A2003CC89E /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
|
||||
4F0EA0512F2BD2A2003CC89E /* Exceptions for "HomeWidgetsExtension" folder in "Runner" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
Controls/AppControls.swift,
|
||||
);
|
||||
target = 97C146ED1CF9000F007C117D /* Runner */;
|
||||
};
|
||||
4F4E70D02EF565FF00C90AD1 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
|
||||
4F4E70D02EF565FF00C90AD1 /* Exceptions for "LiveActivityWidget" folder in "Runner" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
ActivityAttributes.swift,
|
||||
);
|
||||
target = 97C146ED1CF9000F007C117D /* Runner */;
|
||||
};
|
||||
4F5966082F2F0EB100A3DB03 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
|
||||
4F5966082F2F0EB100A3DB03 /* Exceptions for "FirkaWatchComplications" folder in "FirkaWatchComplicationsExtension" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
Info.plist,
|
||||
);
|
||||
target = 4F5965FC2F2F0EAF00A3DB03 /* FirkaWatchComplicationsExtension */;
|
||||
};
|
||||
4F6C1D3E2ECD3FBD00F819D7 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
|
||||
4F6C1D3E2ECD3FBD00F819D7 /* Exceptions for "LiveActivityWidget" folder in "LiveActivityWidget" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
Info.plist,
|
||||
);
|
||||
target = 4F30C7642E8FBF9D008BB46C /* LiveActivityWidget */;
|
||||
};
|
||||
4FE64E472F27B07B006F9205 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
|
||||
4FE64E472F27B07B006F9205 /* Exceptions for "HomeWidgetsExtension" folder in "HomeWidgetsExtension" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
Info.plist,
|
||||
@@ -260,10 +260,55 @@
|
||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
4F30C76A2E8FBF9D008BB46C /* LiveActivityWidget */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (4F4E70D02EF565FF00C90AD1 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 4F6C1D3E2ECD3FBD00F819D7 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = LiveActivityWidget; sourceTree = "<group>"; };
|
||||
4F5966002F2F0EAF00A3DB03 /* FirkaWatchComplications */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (4F5966082F2F0EB100A3DB03 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = FirkaWatchComplications; sourceTree = "<group>"; };
|
||||
4FE64E362F27B07A006F9205 /* HomeWidgetsExtension */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (4F0EA0512F2BD2A2003CC89E /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 4FE64E472F27B07B006F9205 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = HomeWidgetsExtension; sourceTree = "<group>"; };
|
||||
4FF81B7B2F2EB4C100E95BA0 /* FirkaWatch Watch App */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = "FirkaWatch Watch App"; sourceTree = "<group>"; };
|
||||
4F30C76A2E8FBF9D008BB46C /* LiveActivityWidget */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
4F4E70D02EF565FF00C90AD1 /* Exceptions for "LiveActivityWidget" folder in "Runner" target */,
|
||||
4F6C1D3E2ECD3FBD00F819D7 /* Exceptions for "LiveActivityWidget" folder in "LiveActivityWidget" target */,
|
||||
);
|
||||
explicitFileTypes = {
|
||||
};
|
||||
explicitFolders = (
|
||||
);
|
||||
path = LiveActivityWidget;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4F5966002F2F0EAF00A3DB03 /* FirkaWatchComplications */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
4F5966082F2F0EB100A3DB03 /* Exceptions for "FirkaWatchComplications" folder in "FirkaWatchComplicationsExtension" target */,
|
||||
);
|
||||
explicitFileTypes = {
|
||||
};
|
||||
explicitFolders = (
|
||||
);
|
||||
path = FirkaWatchComplications;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4FE64E362F27B07A006F9205 /* HomeWidgetsExtension */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
4F0EA0512F2BD2A2003CC89E /* Exceptions for "HomeWidgetsExtension" folder in "Runner" target */,
|
||||
4FE64E472F27B07B006F9205 /* Exceptions for "HomeWidgetsExtension" folder in "HomeWidgetsExtension" target */,
|
||||
);
|
||||
explicitFileTypes = {
|
||||
};
|
||||
explicitFolders = (
|
||||
);
|
||||
path = HomeWidgetsExtension;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4FF81B7B2F2EB4C100E95BA0 /* FirkaWatch Watch App */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
);
|
||||
explicitFileTypes = {
|
||||
};
|
||||
explicitFolders = (
|
||||
);
|
||||
path = "FirkaWatch Watch App";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -711,14 +756,10 @@
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||
@@ -775,7 +816,7 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
|
||||
shellScript = "set -e\nNATIVE_ASSETS_DIR=\"$FLUTTER_APPLICATION_PATH/$FLUTTER_BUILD_DIR/native_assets\"\ncase \"$FLUTTER_BUILD_DIR\" in\n /*) NATIVE_ASSETS_DIR=\"$FLUTTER_BUILD_DIR/native_assets\" ;;\nesac\nif [ -d \"$FLUTTER_APPLICATION_PATH/.dart_tool/hooks_runner\" ]; then\n find \"$FLUTTER_APPLICATION_PATH/.dart_tool/hooks_runner\" -exec xattr -c {} \\; 2>/dev/null || true\nfi\nif [ -d \"$NATIVE_ASSETS_DIR\" ]; then\n find \"$NATIVE_ASSETS_DIR\" -exec xattr -c {} \\; 2>/dev/null || true\nfi\n/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
|
||||
};
|
||||
D576F90540C8E625A9A12317 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
@@ -807,14 +848,10 @@
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||
@@ -1030,27 +1067,27 @@
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
249021D4217E4FDB00AE95B9 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
249021D4217E4FDB00AE95B9 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1101;
|
||||
DEVELOPMENT_TEAM = UT7MSP4GWZ;
|
||||
CURRENT_PROJECT_VERSION = 1102;
|
||||
DEVELOPMENT_TEAM = R9PZGUCNJ3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Firka Testing";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education";
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Firka;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.9;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firkaa;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firka;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
@@ -1071,7 +1108,8 @@
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1101;
|
||||
CURRENT_PROJECT_VERSION = 1102;
|
||||
DEVELOPMENT_TEAM = R9PZGUCNJ3;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firka.RunnerTests;
|
||||
@@ -1090,7 +1128,8 @@
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1101;
|
||||
CURRENT_PROJECT_VERSION = 1102;
|
||||
DEVELOPMENT_TEAM = R9PZGUCNJ3;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firka.RunnerTests;
|
||||
@@ -1107,7 +1146,8 @@
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1101;
|
||||
CURRENT_PROJECT_VERSION = 1102;
|
||||
DEVELOPMENT_TEAM = R9PZGUCNJ3;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firka.RunnerTests;
|
||||
@@ -1122,9 +1162,9 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9740EEB41CF90195004384FC /* WidgetExtension.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
@@ -1134,8 +1174,8 @@
|
||||
CODE_SIGN_ENTITLEMENTS = LiveActivityWidget.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1101;
|
||||
DEVELOPMENT_TEAM = UT7MSP4GWZ;
|
||||
CURRENT_PROJECT_VERSION = 1102;
|
||||
DEVELOPMENT_TEAM = R9PZGUCNJ3;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1153,7 +1193,7 @@
|
||||
MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)";
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firkaa.LiveActivityWidget;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firka.LiveActivityWidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -1173,9 +1213,9 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9740EEB41CF90195004384FC /* WidgetExtension.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
@@ -1185,8 +1225,8 @@
|
||||
CODE_SIGN_ENTITLEMENTS = LiveActivityWidget.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1101;
|
||||
DEVELOPMENT_TEAM = UT7MSP4GWZ;
|
||||
CURRENT_PROJECT_VERSION = 1102;
|
||||
DEVELOPMENT_TEAM = R9PZGUCNJ3;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1203,7 +1243,7 @@
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)";
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firkaa.LiveActivityWidget;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firka.LiveActivityWidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -1221,9 +1261,9 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9740EEB41CF90195004384FC /* WidgetExtension.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
@@ -1233,8 +1273,8 @@
|
||||
CODE_SIGN_ENTITLEMENTS = LiveActivityWidget.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1101;
|
||||
DEVELOPMENT_TEAM = UT7MSP4GWZ;
|
||||
CURRENT_PROJECT_VERSION = 1102;
|
||||
DEVELOPMENT_TEAM = R9PZGUCNJ3;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1251,7 +1291,7 @@
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)";
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firkaa.LiveActivityWidget;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firka.LiveActivityWidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -1269,9 +1309,9 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD)";
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
@@ -1280,8 +1320,8 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = FirkaWatchComplicationsExtension.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1101;
|
||||
DEVELOPMENT_TEAM = UT7MSP4GWZ;
|
||||
CURRENT_PROJECT_VERSION = 1102;
|
||||
DEVELOPMENT_TEAM = R9PZGUCNJ3;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1301,7 +1341,7 @@
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firkaa.watchkitapp.complications;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firka.watchkitapp.complications;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = watchos;
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -1322,9 +1362,9 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD)";
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
@@ -1333,8 +1373,8 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = FirkaWatchComplicationsExtension.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1101;
|
||||
DEVELOPMENT_TEAM = UT7MSP4GWZ;
|
||||
CURRENT_PROJECT_VERSION = 1102;
|
||||
DEVELOPMENT_TEAM = R9PZGUCNJ3;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1352,7 +1392,7 @@
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firkaa.watchkitapp.complications;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firka.watchkitapp.complications;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = watchos;
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -1372,9 +1412,9 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD)";
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
@@ -1383,8 +1423,8 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = FirkaWatchComplicationsExtension.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1101;
|
||||
DEVELOPMENT_TEAM = UT7MSP4GWZ;
|
||||
CURRENT_PROJECT_VERSION = 1102;
|
||||
DEVELOPMENT_TEAM = R9PZGUCNJ3;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1402,7 +1442,7 @@
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firkaa.watchkitapp.complications;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firka.watchkitapp.complications;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = watchos;
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -1422,9 +1462,9 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9740EEB41CF90195004384FC /* WidgetExtension.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
@@ -1434,8 +1474,8 @@
|
||||
CODE_SIGN_ENTITLEMENTS = HomeWidgetsExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1101;
|
||||
DEVELOPMENT_TEAM = UT7MSP4GWZ;
|
||||
CURRENT_PROJECT_VERSION = 1102;
|
||||
DEVELOPMENT_TEAM = R9PZGUCNJ3;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1454,7 +1494,7 @@
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firkaa.HomeWidgetsExtension;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firka.HomeWidgetsExtension;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -1473,9 +1513,9 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9740EEB41CF90195004384FC /* WidgetExtension.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
@@ -1485,8 +1525,8 @@
|
||||
CODE_SIGN_ENTITLEMENTS = HomeWidgetsExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1101;
|
||||
DEVELOPMENT_TEAM = UT7MSP4GWZ;
|
||||
CURRENT_PROJECT_VERSION = 1102;
|
||||
DEVELOPMENT_TEAM = R9PZGUCNJ3;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1503,7 +1543,7 @@
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)";
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firkaa.HomeWidgetsExtension;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firka.HomeWidgetsExtension;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -1520,9 +1560,9 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9740EEB41CF90195004384FC /* WidgetExtension.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
@@ -1532,8 +1572,8 @@
|
||||
CODE_SIGN_ENTITLEMENTS = HomeWidgetsExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1101;
|
||||
DEVELOPMENT_TEAM = UT7MSP4GWZ;
|
||||
CURRENT_PROJECT_VERSION = 1102;
|
||||
DEVELOPMENT_TEAM = R9PZGUCNJ3;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1550,7 +1590,7 @@
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)";
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firkaa.HomeWidgetsExtension;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firka.HomeWidgetsExtension;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SKIP_INSTALL = YES;
|
||||
@@ -1563,13 +1603,13 @@
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
4FF81B9C2F2EB4C300E95BA0 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
4FF81B9C2F2EB4C300E95BA0 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD)";
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
@@ -1579,14 +1619,15 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "FirkaWatch Watch App/FirkaWatch Watch App.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = UT7MSP4GWZ;
|
||||
DEVELOPMENT_TEAM = R9PZGUCNJ3;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = FirkaWatch;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = app.firka.firkaa;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = FirkaWatch;
|
||||
INFOPLIST_KEY_NSSupportsLiveActivities = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = app.firka.firka;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@@ -1596,7 +1637,7 @@
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firkaa.watchkitapp;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firka.watchkitapp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SDKROOT = watchos;
|
||||
@@ -1615,13 +1656,13 @@
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
4FF81B9D2F2EB4C300E95BA0 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
4FF81B9D2F2EB4C300E95BA0 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD)";
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
@@ -1631,14 +1672,15 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "FirkaWatch Watch App/FirkaWatch Watch App.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = UT7MSP4GWZ;
|
||||
DEVELOPMENT_TEAM = R9PZGUCNJ3;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = FirkaWatch;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = app.firka.firkaa;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = FirkaWatch;
|
||||
INFOPLIST_KEY_NSSupportsLiveActivities = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = app.firka.firka;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@@ -1646,7 +1688,7 @@
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firkaa.watchkitapp;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firka.watchkitapp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SDKROOT = watchos;
|
||||
@@ -1664,13 +1706,13 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
4FF81B9E2F2EB4C300E95BA0 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
4FF81B9E2F2EB4C300E95BA0 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD)";
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AppIcon;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
@@ -1680,14 +1722,15 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "FirkaWatch Watch App/FirkaWatch Watch App.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = UT7MSP4GWZ;
|
||||
DEVELOPMENT_TEAM = R9PZGUCNJ3;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = FirkaWatch;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = app.firka.firkaa;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = FirkaWatch;
|
||||
INFOPLIST_KEY_NSSupportsLiveActivities = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||
INFOPLIST_KEY_WKCompanionAppBundleIdentifier = app.firka.firka;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@@ -1695,7 +1738,7 @@
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firkaa.watchkitapp;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firka.watchkitapp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SDKROOT = watchos;
|
||||
@@ -1828,27 +1871,27 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
97C147061CF9000F007C117D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||
buildSettings = {
|
||||
97C147061CF9000F007C117D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1101;
|
||||
DEVELOPMENT_TEAM = UT7MSP4GWZ;
|
||||
CURRENT_PROJECT_VERSION = 1102;
|
||||
DEVELOPMENT_TEAM = R9PZGUCNJ3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Firka Testing";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education";
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Firka;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.9;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firkaa;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firka;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
@@ -1864,27 +1907,27 @@
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
97C147071CF9000F007C117D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
97C147071CF9000F007C117D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1101;
|
||||
DEVELOPMENT_TEAM = UT7MSP4GWZ;
|
||||
CURRENT_PROJECT_VERSION = 1102;
|
||||
DEVELOPMENT_TEAM = R9PZGUCNJ3;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Firka Testing";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education";
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Firka;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.education";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.9;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firkaa;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.firka.firka;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
|
||||
|
||||
@@ -30,8 +30,8 @@ import BackgroundTasks
|
||||
widgetDeepLinkChannel = FlutterMethodChannel(name: "firka.app/widget_deep_link", binaryMessenger: controller.binaryMessenger)
|
||||
widgetDeepLinkChannel?.setMethodCallHandler { [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) in
|
||||
if call.method == "getPendingDeepLink" {
|
||||
if let controlNav = UserDefaults(suiteName: "group.app.firka.firkaa")?.string(forKey: "controlNavigation") {
|
||||
UserDefaults(suiteName: "group.app.firka.firkaa")?.removeObject(forKey: "controlNavigation")
|
||||
if let controlNav = UserDefaults(suiteName: "group.app.firka.firka")?.string(forKey: "controlNavigation") {
|
||||
UserDefaults(suiteName: "group.app.firka.firka")?.removeObject(forKey: "controlNavigation")
|
||||
result(controlNav)
|
||||
} else if let link = self?.pendingWidgetDeepLink {
|
||||
self?.pendingWidgetDeepLink = nil
|
||||
|
||||
|
Before Width: | Height: | Size: 660 B After Width: | Height: | Size: 652 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 13 KiB |
@@ -11,7 +11,7 @@ class HomeWidgetMethodChannel {
|
||||
switch call.method {
|
||||
case "getAppGroupDirectory":
|
||||
if let containerURL = FileManager.default.containerURL(
|
||||
forSecurityApplicationGroupIdentifier: "group.app.firka.firkaa"
|
||||
forSecurityApplicationGroupIdentifier: "group.app.firka.firka"
|
||||
) {
|
||||
result(containerURL.path)
|
||||
} else {
|
||||
|
||||
@@ -1,89 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||
<array>
|
||||
<string>app.firka.timetable.refresh</string>
|
||||
</array>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleAllowMixedLocalizations</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<dict>
|
||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||
<array>
|
||||
<string>app.firka.timetable.refresh</string>
|
||||
</array>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleAllowMixedLocalizations</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Firka Testing</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleLocalizations</key>
|
||||
<array>
|
||||
<string>en</string>
|
||||
<key>CFBundleLocalizations</key>
|
||||
<array>
|
||||
<string>en</string>
|
||||
<string>hu</string>
|
||||
<string>de</string>
|
||||
</array>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Firka Testing</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>firka</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>app.firka.firkaa</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>firka</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1101</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<string>hu</string>
|
||||
<string>de</string>
|
||||
</array>
|
||||
<key>CFBundleName</key>
|
||||
<string>firka</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>app.firka.firka</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>firka</string>
|
||||
</array>
|
||||
</dict>
|
||||
<key>NSSupportsLiveActivities</key>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1102</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
<key>NSSupportsLiveActivitiesFrequentUpdates</key>
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>remote-notification</string>
|
||||
<string>fetch</string>
|
||||
<string>processing</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>NSSupportsLiveActivities</key>
|
||||
<true/>
|
||||
<key>NSSupportsLiveActivitiesFrequentUpdates</key>
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>remote-notification</string>
|
||||
<string>fetch</string>
|
||||
<string>processing</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<false/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
<dict>
|
||||
<key>aps-environment</key>
|
||||
<string>development</string>
|
||||
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
|
||||
<string>$(TeamIdentifierPrefix)app.firka.firka</string>
|
||||
<key>com.apple.developer.usernotifications.time-sensitive</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.app.firka.firkaa</string>
|
||||
<string>group.app.firka.firka</string>
|
||||
</array>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)app.firka.shared</string>
|
||||
</array>
|
||||
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
|
||||
<string>$(TeamIdentifierPrefix)app.firka.firkaa</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -32,7 +32,7 @@ enum TokenError: Error {
|
||||
class TokenManager {
|
||||
static let shared = TokenManager()
|
||||
|
||||
private let appGroupID = "group.app.firka.firkaa"
|
||||
private let appGroupID = "group.app.firka.firka"
|
||||
private let tokenFileName = "watch_token.json"
|
||||
|
||||
private static let keychainService = "app.firka.watch.token"
|
||||
|
||||
@@ -12,7 +12,7 @@ struct WidgetData: Codable {
|
||||
|
||||
static func load() -> WidgetData? {
|
||||
guard let containerURL = FileManager.default.containerURL(
|
||||
forSecurityApplicationGroupIdentifier: "group.app.firka.firkaa"
|
||||
forSecurityApplicationGroupIdentifier: "group.app.firka.firka"
|
||||
) else {
|
||||
lastError = "No App Group container"
|
||||
return nil
|
||||
|
||||
@@ -12,8 +12,8 @@ import 'package:kreta_api/kreta_api.dart' hide KretaEndpoints;
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/core/bloc/reauth_cubit.dart';
|
||||
import 'package:firka/data/models/token_model.dart';
|
||||
import 'package:firka/data/util.dart';
|
||||
import 'package:firka/core/debug_helper.dart';
|
||||
import 'package:firka/data/util.dart';
|
||||
import 'package:firka/services/active_account_helper.dart';
|
||||
import 'package:firka/services/watch_sync_helper.dart';
|
||||
import '../consts.dart';
|
||||
|
||||
@@ -16,6 +16,7 @@ import 'package:firka/core/bloc/theme_cubit.dart';
|
||||
import 'package:firka/core/firka_bundle.dart';
|
||||
import 'package:firka/routing/app_router.dart';
|
||||
import 'package:firka/services/watch_sync_helper.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/phone/pages/extras/main_wear_pair.dart';
|
||||
import 'package:firka/l10n/app_localizations.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
@@ -34,6 +35,23 @@ class _InitializationScreenState extends State<InitializationScreen> {
|
||||
const Duration(seconds: 20),
|
||||
);
|
||||
|
||||
ThemeData _buildTheme(FirkaStyle style) {
|
||||
return ThemeData(
|
||||
scaffoldBackgroundColor: style.colors.background,
|
||||
canvasColor: style.colors.background,
|
||||
bottomSheetTheme: const BottomSheetThemeData(
|
||||
backgroundColor: Colors.transparent,
|
||||
),
|
||||
colorScheme: ColorScheme.fromSeed(
|
||||
seedColor: style.colors.accent,
|
||||
brightness: style.isLight ? Brightness.light : Brightness.dark,
|
||||
background: style.colors.background,
|
||||
surface: style.colors.card,
|
||||
),
|
||||
useMaterial3: false,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<AppInitialization>(
|
||||
@@ -180,40 +198,47 @@ class _InitializationScreenState extends State<InitializationScreen> {
|
||||
BlocProvider<ReauthCubit>.value(value: reauthCubit),
|
||||
BlocProvider<HomeRefreshCubit>.value(value: homeRefreshCubit),
|
||||
],
|
||||
child: MaterialApp.router(
|
||||
title: 'Firka',
|
||||
key: const ValueKey('firkaApp'),
|
||||
routerConfig: _router!,
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.lightGreen,
|
||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||
),
|
||||
localizationsDelegates: const [
|
||||
AppLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
builder: (context, child) {
|
||||
return BlocBuilder<ThemeCubit, ThemeState>(
|
||||
builder: (context, themeState) {
|
||||
final isLight = themeState.isLightMode;
|
||||
final overlay = SystemUiOverlayStyle(
|
||||
statusBarColor: Colors.transparent,
|
||||
statusBarIconBrightness: isLight
|
||||
? Brightness.dark
|
||||
: Brightness.light,
|
||||
statusBarBrightness: isLight
|
||||
? Brightness.light
|
||||
: Brightness.dark,
|
||||
systemStatusBarContrastEnforced: false,
|
||||
);
|
||||
child: BlocBuilder<ThemeCubit, ThemeState>(
|
||||
builder: (context, themeState) {
|
||||
final isLight = themeState.isLightMode;
|
||||
final overlay = SystemUiOverlayStyle(
|
||||
statusBarColor: Colors.transparent,
|
||||
statusBarIconBrightness:
|
||||
isLight ? Brightness.dark : Brightness.light,
|
||||
statusBarBrightness:
|
||||
isLight ? Brightness.light : Brightness.dark,
|
||||
systemStatusBarContrastEnforced: false,
|
||||
);
|
||||
|
||||
SystemChrome.setSystemUIOverlayStyle(overlay);
|
||||
final themeMode =
|
||||
isLight ? ThemeMode.light : ThemeMode.dark;
|
||||
|
||||
final fallbackBg = isLight
|
||||
? lightStyle.colors.background
|
||||
: darkStyle.colors.background;
|
||||
|
||||
SystemChrome.setSystemUIOverlayStyle(overlay);
|
||||
|
||||
return MaterialApp.router(
|
||||
title: 'Firka',
|
||||
key: const ValueKey('firkaApp'),
|
||||
routerConfig: _router!,
|
||||
theme: _buildTheme(lightStyle),
|
||||
darkTheme: _buildTheme(darkStyle),
|
||||
themeMode: themeMode,
|
||||
localizationsDelegates: const [
|
||||
AppLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
builder: (context, child) {
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: overlay,
|
||||
child: child ?? const SizedBox.shrink(),
|
||||
child: ColoredBox(
|
||||
color: fallbackBg,
|
||||
child: child ?? const SizedBox.shrink(),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import 'package:kreta_api/kreta_api.dart';
|
||||
|
||||
bool _isPercentageGrade(Grade grade) {
|
||||
final name = grade.valueType.name?.toLowerCase() ?? '';
|
||||
return name.contains('szazalek') || name.contains('percent');
|
||||
}
|
||||
|
||||
double calculateAverage(List<Grade> sortedGrades) {
|
||||
double totalWeight = 0.0;
|
||||
double weightedSum = 0.0;
|
||||
|
||||
for (final grade in sortedGrades) {
|
||||
if (_isPercentageGrade(grade)) continue;
|
||||
|
||||
final value = grade.numericValue;
|
||||
final weight = grade.weightPercentage;
|
||||
|
||||
|
||||
@@ -1,17 +1 @@
|
||||
DateTime? debugFakeTime;
|
||||
DateTime? debugSetAt;
|
||||
var debugTimeAdvance = false;
|
||||
|
||||
DateTime timeNow() {
|
||||
if (debugFakeTime != null) {
|
||||
if (debugTimeAdvance && debugSetAt != null) {
|
||||
var diff = DateTime.now().difference(debugSetAt!);
|
||||
|
||||
return debugFakeTime!.add(diff);
|
||||
} else {
|
||||
return debugFakeTime!;
|
||||
}
|
||||
} else {
|
||||
return DateTime.now();
|
||||
}
|
||||
}
|
||||
export 'package:firka_common/core/debug_helper.dart';
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import 'package:kreta_api/kreta_api.dart';
|
||||
import 'package:firka/core/debug_helper.dart';
|
||||
import 'package:firka/l10n/app_localizations.dart';
|
||||
import 'package:firka_common/core/debug_helper.dart';
|
||||
import 'package:firka_common/core/extensions.dart';
|
||||
import 'package:kreta_api/kreta_api.dart';
|
||||
|
||||
export 'package:firka_common/core/extensions.dart';
|
||||
|
||||
extension TimetableExtension on Iterable<Lesson> {
|
||||
List<Lesson> getAllSeqs(Lesson reference) {
|
||||
@@ -59,26 +62,6 @@ extension TimetableExtension on Iterable<Lesson> {
|
||||
}
|
||||
}
|
||||
|
||||
extension IterableExtensionMap on Iterable<MapEntry<String, dynamic>> {
|
||||
Map<String, dynamic> toMap() {
|
||||
var map = <String, dynamic>{};
|
||||
for (var item in this) {
|
||||
map[item.key] = item.value;
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
extension IterableExtension<T> on Iterable<T> {
|
||||
T? firstWhereOrNull(bool Function(T element) test) {
|
||||
for (var element in this) {
|
||||
if (test(element)) return element;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
extension DurationExtension on Duration {
|
||||
String formatDuration() {
|
||||
String hours = inHours.toString().padLeft(2, '0');
|
||||
|
||||
@@ -1,62 +1,12 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:brotli/brotli.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class FirkaBundle extends CachingAssetBundle {
|
||||
// final bool _compressedBundle = !kDebugMode && Platform.isAndroid;
|
||||
final bool _compressedBundle = false;
|
||||
|
||||
Map<String, dynamic>? index;
|
||||
|
||||
Future<Map<String, dynamic>> loadIndex() async {
|
||||
var indexBrotli = await rootBundle.load("assets/firka.i");
|
||||
var indexStr = brotli.decodeToString(indexBrotli.buffer.asInt8List());
|
||||
|
||||
return Future.value(jsonDecode(indexStr));
|
||||
}
|
||||
|
||||
ByteData decode(Codec<List<int>, List<int>> codec, ByteData data) {
|
||||
var dec = codec.decode(data.buffer.asInt8List());
|
||||
var b = ByteData(dec.length);
|
||||
var l = b.buffer.asInt8List();
|
||||
|
||||
for (var i = 0; i < dec.length; i++) {
|
||||
l[i] = dec[i];
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ByteData> load(String key) async {
|
||||
if (!_compressedBundle) {
|
||||
logger.finest(
|
||||
"Loading asset from root bundle: assets/flutter_assets/$key",
|
||||
);
|
||||
return rootBundle.load(key);
|
||||
} else {
|
||||
index ??= await loadIndex();
|
||||
|
||||
final gzip = GZipCodec();
|
||||
|
||||
logger.finest(
|
||||
"Loading asset from firka bundle: assets/flutter_assets/$key",
|
||||
);
|
||||
switch (index!["assets/flutter_assets/$key"]!) {
|
||||
case "b": // brotli
|
||||
return decode(brotli, await rootBundle.load(key));
|
||||
case "g": // gzip
|
||||
return decode(gzip, await rootBundle.load(key));
|
||||
case "r": // raw
|
||||
return rootBundle.load(key);
|
||||
default:
|
||||
logger.shout("Unknown file format: ${index![key]!}");
|
||||
throw "Unknown file format: ${index![key]!}";
|
||||
}
|
||||
}
|
||||
logger.finest(
|
||||
"Loading asset from root bundle: assets/flutter_assets/$key",
|
||||
);
|
||||
return rootBundle.load(key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,152 +1 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
|
||||
enum ClassIcon {
|
||||
mathematics,
|
||||
grammar,
|
||||
literature,
|
||||
history,
|
||||
geography,
|
||||
art,
|
||||
physics,
|
||||
music,
|
||||
pe,
|
||||
chemistry,
|
||||
biology,
|
||||
env,
|
||||
religion,
|
||||
economics,
|
||||
it,
|
||||
code,
|
||||
networking,
|
||||
theatre,
|
||||
film,
|
||||
electricalEngineering,
|
||||
mechanicalEngineering,
|
||||
technika,
|
||||
dance,
|
||||
philosophy,
|
||||
ofo,
|
||||
diligence,
|
||||
attitude,
|
||||
language,
|
||||
linux,
|
||||
database,
|
||||
applications,
|
||||
project,
|
||||
}
|
||||
|
||||
Map<ClassIcon, RegExp> _descriptors = {
|
||||
ClassIcon.mathematics: RegExp(r'mate(k|matika)'),
|
||||
ClassIcon.grammar: RegExp(r'magyar nyelv|nyelvtan'),
|
||||
ClassIcon.literature: RegExp(r'irodalom'),
|
||||
ClassIcon.history: RegExp(r'tor(i|tenelem)'),
|
||||
ClassIcon.geography: RegExp(r'foldrajz'),
|
||||
ClassIcon.art: RegExp(r'rajz|muvtori|muveszet|vizualis'),
|
||||
ClassIcon.physics: RegExp(r'fizika'),
|
||||
ClassIcon.music: RegExp(r'^enek|zene|szolfezs|zongora|korus'),
|
||||
ClassIcon.pe: RegExp(r'^tes(i|tneveles)|sport|edzeselmelet'),
|
||||
ClassIcon.chemistry: RegExp(r'kemia'),
|
||||
ClassIcon.biology: RegExp(r'biologia'),
|
||||
ClassIcon.env: RegExp(
|
||||
r'kornyezet|termeszet ?(tudomany|ismeret)|hon( es nep)?ismeret',
|
||||
),
|
||||
ClassIcon.religion: RegExp(r'(hit|erkolcs)tan|vallas|etika|bibliaismeret'),
|
||||
ClassIcon.economics: RegExp(r'penzugy|gazdasag'),
|
||||
ClassIcon.it: RegExp(r'informatika|szoftver|iroda|digitalis'),
|
||||
ClassIcon.code: RegExp(r'prog|alkalmazas'),
|
||||
ClassIcon.networking: RegExp(r'halozat'),
|
||||
ClassIcon.theatre: RegExp(r'szinhaz'),
|
||||
ClassIcon.film: RegExp(r'film|media'),
|
||||
ClassIcon.electricalEngineering: RegExp(r'elektro(tech)?nika'),
|
||||
ClassIcon.mechanicalEngineering: RegExp(r'gepesz|mernok|ipar'),
|
||||
ClassIcon.technika: RegExp(r'technika'),
|
||||
ClassIcon.dance: RegExp(r'tanc'),
|
||||
ClassIcon.philosophy: RegExp(r'filozofia'),
|
||||
ClassIcon.ofo: RegExp(r'osztaly(fonoki|kozosseg)|kozossegi|neveles'),
|
||||
ClassIcon.diligence: RegExp(r'szorgalom'),
|
||||
ClassIcon.attitude: RegExp(r'magatartas'),
|
||||
ClassIcon.language: RegExp(
|
||||
r'angol|nemet|francia|olasz|orosz|spanyol|latin|kinai|nyelv',
|
||||
),
|
||||
ClassIcon.linux: RegExp(r'linux'),
|
||||
ClassIcon.database: RegExp(r'adatbazis.*'),
|
||||
ClassIcon.applications: RegExp(r'asztali alkalmazasok'),
|
||||
ClassIcon.project: RegExp(r'projekt'),
|
||||
};
|
||||
|
||||
Map<ClassIcon, Uint8List> _iconMap = {
|
||||
ClassIcon.mathematics: Majesticon.calculatorSolid,
|
||||
ClassIcon.grammar: Majesticon.bookSolid,
|
||||
ClassIcon.literature: Majesticon.bookOpenSolid,
|
||||
ClassIcon.history: Majesticon.compass2Solid,
|
||||
ClassIcon.geography: Majesticon.globeEarth2Solid,
|
||||
ClassIcon.art: Majesticon.editPen2Solid,
|
||||
// ClassIcon.physics: ,
|
||||
ClassIcon.music: Majesticon.musicNoteSolid,
|
||||
// ClassIcon.pe: ,
|
||||
ClassIcon.chemistry: Majesticon.testTubeFilledSolid,
|
||||
ClassIcon.biology: Majesticon.covidSolid,
|
||||
// ClassIcon.env: ,
|
||||
// ClassIcon.religion: ,
|
||||
// ClassIcon.economics: ,
|
||||
ClassIcon.it: Majesticon.laptopSolid,
|
||||
ClassIcon.code: Majesticon.curlyBracesSolid,
|
||||
ClassIcon.networking: Majesticon.cloudSolid,
|
||||
// ClassIcon.theatre: ,
|
||||
// ClassIcon.film: ,
|
||||
// ClassIcon.electricalEngineering: ,
|
||||
// ClassIcon.mechanicalEngineering: ,
|
||||
ClassIcon.technika: Majesticon.ruler2Solid,
|
||||
// ClassIcon.dance: ,
|
||||
// ClassIcon.philosophy: ,
|
||||
// ClassIcon.ofo: ,
|
||||
// ClassIcon.diligence: ,
|
||||
// ClassIcon.attitude: ,
|
||||
ClassIcon.language: Majesticon.tooltipsSolid,
|
||||
// ClassIcon.linux: ,
|
||||
ClassIcon.database: Majesticon.dataSolid,
|
||||
// ClassIcon.applications: ,
|
||||
// ClassIcon.project: ,
|
||||
};
|
||||
|
||||
ClassIcon? getIconType(String uid, String className, String category) {
|
||||
ClassIcon? icon;
|
||||
if (category.toLowerCase() == "matematika") {
|
||||
icon = ClassIcon.mathematics;
|
||||
}
|
||||
|
||||
if (icon == null) {
|
||||
for (var desc in _descriptors.entries) {
|
||||
if (desc.value.hasMatch(
|
||||
className
|
||||
.replaceAll("ö", "o")
|
||||
.replaceAll("ü", "u")
|
||||
.replaceAll("ó", "o")
|
||||
.replaceAll("ő", "o")
|
||||
.replaceAll("ú", "u")
|
||||
.replaceAll("é", "e")
|
||||
.replaceAll("á", "a")
|
||||
.replaceAll("ű", "u")
|
||||
.replaceAll("í", "i")
|
||||
.toLowerCase(),
|
||||
)) {
|
||||
icon = desc.key;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
Uint8List getIconData(ClassIcon? icon) {
|
||||
if (icon == null) return Majesticon.alertCircleSolid;
|
||||
|
||||
var iconData = _iconMap[icon];
|
||||
iconData ??= Majesticon.alertCircleSolid;
|
||||
|
||||
return iconData;
|
||||
}
|
||||
export 'package:firka_common/core/icon_helper.dart';
|
||||
|
||||
@@ -1,9 +1 @@
|
||||
List<T> listToTyped<T>(List<dynamic> dynamicList) {
|
||||
var newList = List<T>.empty(growable: true);
|
||||
|
||||
for (var item in dynamicList) {
|
||||
newList.add(item as T);
|
||||
}
|
||||
|
||||
return newList;
|
||||
}
|
||||
export 'package:firka_common/core/json_helper.dart';
|
||||
|
||||
@@ -3,8 +3,8 @@ import 'dart:convert';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
|
||||
import 'package:kreta_api/kreta_api.dart';
|
||||
import 'package:firka/core/debug_helper.dart';
|
||||
import 'package:firka/core/extensions.dart';
|
||||
import 'package:firka_common/core/debug_helper.dart';
|
||||
import 'package:isar_community/isar.dart';
|
||||
|
||||
part 'token_model.g.dart';
|
||||
|
||||
@@ -322,6 +322,11 @@ class WidgetCacheHelper {
|
||||
var sum = 0.0;
|
||||
|
||||
for (var grade in grades) {
|
||||
final name = grade.valueType.name?.toLowerCase() ?? '';
|
||||
final isPercentage =
|
||||
name.contains('szazalek') || name.contains('percent');
|
||||
if (isPercentage) continue;
|
||||
|
||||
if (grade.numericValue != null) {
|
||||
var weight = (grade.weightPercentage ?? 100) / 100.0;
|
||||
weightTotal += weight;
|
||||
|
||||
@@ -1048,20 +1048,12 @@ class _GradeCalculatorSheetContent extends StatefulWidget {
|
||||
class _GradeCalculatorSheetContentState
|
||||
extends State<_GradeCalculatorSheetContent> {
|
||||
int selectedGrade = 3;
|
||||
int weightPercent = 100;
|
||||
int weightIndex = 1; // 0-based index into _snapPoints
|
||||
final List<(int grade, int weight)> entries = [];
|
||||
|
||||
double get _weightedAverage {
|
||||
if (entries.isEmpty) return 0;
|
||||
double sum = 0;
|
||||
double weightTotal = 0;
|
||||
for (final e in entries) {
|
||||
final w = e.$2 / 100.0;
|
||||
weightTotal += w;
|
||||
sum += e.$1 * w;
|
||||
}
|
||||
return weightTotal > 0 ? sum / weightTotal : 0;
|
||||
}
|
||||
static const _snapPoints = [50, 100, 200, 300, 500];
|
||||
|
||||
int get weightPercent => _snapPoints[weightIndex];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -1196,11 +1188,12 @@ class _GradeCalculatorSheetContentState
|
||||
trackHeight: 8,
|
||||
),
|
||||
child: Slider(
|
||||
value: weightPercent.toDouble(),
|
||||
min: 1,
|
||||
max: 500,
|
||||
divisions: 499,
|
||||
onChanged: (v) => setState(() => weightPercent = v.round()),
|
||||
value: weightIndex.toDouble(),
|
||||
min: 0,
|
||||
max: (_snapPoints.length - 1).toDouble(),
|
||||
divisions: _snapPoints.length - 1,
|
||||
label: '${weightPercent}%',
|
||||
onChanged: (v) => setState(() => weightIndex = v.round()),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -1245,15 +1238,6 @@ class _GradeCalculatorSheetContentState
|
||||
),
|
||||
),
|
||||
),
|
||||
if (entries.isNotEmpty) ...[
|
||||
SizedBox(height: 16),
|
||||
Text(
|
||||
'${widget.data.l10n.subject_avg}: ${_weightedAverage.toStringAsFixed(2)}',
|
||||
style: appStyle.fonts.B_14R.apply(
|
||||
color: appStyle.colors.textPrimary,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,134 +1 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'package:firka/core/bloc/theme_cubit.dart';
|
||||
import 'package:firka/ui/components/firka_shadow.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
|
||||
enum Attach { none, bottom, top }
|
||||
|
||||
class FirkaCard extends StatelessWidget {
|
||||
final List<Widget> left;
|
||||
final List<Widget>? center;
|
||||
final double? height;
|
||||
final List<Widget>? right;
|
||||
final bool shadow;
|
||||
final Widget? extra;
|
||||
final Attach? attached;
|
||||
final Color? color;
|
||||
|
||||
const FirkaCard({
|
||||
required this.left,
|
||||
this.shadow = true,
|
||||
this.center,
|
||||
this.right,
|
||||
this.extra,
|
||||
this.attached,
|
||||
this.color,
|
||||
this.height,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var right = this.right ?? [];
|
||||
|
||||
var attached = this.attached != null ? this.attached! : Attach.none;
|
||||
final defaultRounding = 16.0;
|
||||
final attachedRounding = 8.0;
|
||||
final isLight = context.watch<ThemeCubit>().state.isLightMode;
|
||||
|
||||
if (extra != null) {
|
||||
return SizedBox(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: height,
|
||||
child: FirkaShadow(
|
||||
shadow: shadow,
|
||||
child: Card(
|
||||
color: color ?? appStyle.colors.card,
|
||||
shadowColor: isLight && shadow ? null : Colors.transparent,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(
|
||||
attached == Attach.top ? attachedRounding : defaultRounding,
|
||||
),
|
||||
topRight: Radius.circular(
|
||||
attached == Attach.top ? attachedRounding : defaultRounding,
|
||||
),
|
||||
bottomLeft: Radius.circular(
|
||||
attached == Attach.bottom
|
||||
? attachedRounding
|
||||
: defaultRounding,
|
||||
),
|
||||
bottomRight: Radius.circular(
|
||||
attached == Attach.bottom
|
||||
? attachedRounding
|
||||
: defaultRounding,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(children: left),
|
||||
Row(children: center ?? []),
|
||||
Row(children: right),
|
||||
],
|
||||
),
|
||||
extra ?? SizedBox(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return SizedBox(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: height,
|
||||
child: FirkaShadow(
|
||||
shadow: shadow,
|
||||
child: Card(
|
||||
color: color ?? appStyle.colors.card,
|
||||
shadowColor: isLight && shadow ? null : Colors.transparent,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(
|
||||
attached == Attach.top ? attachedRounding : defaultRounding,
|
||||
),
|
||||
topRight: Radius.circular(
|
||||
attached == Attach.top ? attachedRounding : defaultRounding,
|
||||
),
|
||||
bottomLeft: Radius.circular(
|
||||
attached == Attach.bottom
|
||||
? attachedRounding
|
||||
: defaultRounding,
|
||||
),
|
||||
bottomRight: Radius.circular(
|
||||
attached == Attach.bottom
|
||||
? attachedRounding
|
||||
: defaultRounding,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(children: left),
|
||||
Row(children: center ?? []),
|
||||
Row(children: right),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
export 'package:firka_common/ui/components/firka_card.dart';
|
||||
|
||||
@@ -1,45 +1 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'package:firka/core/bloc/theme_cubit.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
|
||||
class FirkaShadow extends StatelessWidget {
|
||||
final Widget child;
|
||||
final bool shadow;
|
||||
|
||||
const FirkaShadow({required this.shadow, required this.child, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final borderRadius = BorderRadius.circular(8.0);
|
||||
|
||||
final shadowBox = BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
shape: BoxShape.rectangle,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: appStyle.colors.shadowColor,
|
||||
spreadRadius: -4,
|
||||
blurRadius: 0,
|
||||
offset: Offset(0, 2),
|
||||
),
|
||||
],
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
);
|
||||
|
||||
if (!shadow) {
|
||||
return ClipRRect(borderRadius: borderRadius, child: child);
|
||||
}
|
||||
|
||||
final isLight = context.watch<ThemeCubit>().state.isLightMode;
|
||||
if (isLight) {
|
||||
return child;
|
||||
} else {
|
||||
return Container(
|
||||
decoration: shadowBox,
|
||||
child: ClipRRect(borderRadius: borderRadius, child: child),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
export 'package:firka_common/ui/components/firka_shadow.dart';
|
||||
|
||||
@@ -1,97 +1 @@
|
||||
import 'package:kreta_api/kreta_api.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/components/grade_helpers.dart';
|
||||
|
||||
class GradeWidget extends StatelessWidget {
|
||||
const GradeWidget(this.grade, {super.key})
|
||||
: gradeValue = null,
|
||||
_fromValue = false;
|
||||
|
||||
const GradeWidget.gradeValue(int value, {super.key})
|
||||
: grade = null,
|
||||
gradeValue = value,
|
||||
_fromValue = true;
|
||||
|
||||
final Grade? grade;
|
||||
final int? gradeValue;
|
||||
final bool _fromValue;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_fromValue && gradeValue != null) {
|
||||
return _buildNumericCircle(
|
||||
gradeValue!,
|
||||
getGradeColor(gradeValue!.toDouble()),
|
||||
);
|
||||
}
|
||||
|
||||
final g = grade!;
|
||||
Color gradeColor = appStyle.colors.grade1;
|
||||
final gradeStr = g.numericValue?.toString() ?? '0';
|
||||
|
||||
if (g.valueType.name == 'Szazalekos') {
|
||||
if (g.numericValue != null) {
|
||||
gradeColor = getGradeColor(
|
||||
percentageToGrade(g.numericValue!).toDouble(),
|
||||
);
|
||||
}
|
||||
|
||||
final str = g.strValue.replaceAll('%', '');
|
||||
return Card(
|
||||
shape: const CircleBorder(),
|
||||
shadowColor: Colors.transparent,
|
||||
color: gradeColor.withAlpha(38),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(str, style: appStyle.fonts.P_14.copyWith(color: gradeColor)),
|
||||
Text('%', style: appStyle.fonts.P_12.copyWith(color: gradeColor)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (g.numericValue != null) {
|
||||
gradeColor = getGradeColor(g.numericValue!.toDouble());
|
||||
}
|
||||
|
||||
if (gradeStr == '0') {
|
||||
return Card(
|
||||
shadowColor: Colors.transparent,
|
||||
color: gradeColor.withAlpha(38),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 8, right: 8, top: 2, bottom: 2),
|
||||
child: Text(
|
||||
g.strValue,
|
||||
style: appStyle.fonts.H_H1.copyWith(
|
||||
fontSize: 16,
|
||||
color: gradeColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return _buildNumericCircle(g.numericValue!, gradeColor);
|
||||
}
|
||||
|
||||
Widget _buildNumericCircle(int value, Color gradeColor) {
|
||||
return Card(
|
||||
shape: const CircleBorder(),
|
||||
shadowColor: Colors.transparent,
|
||||
color: gradeColor.withAlpha(38),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 8, right: 8),
|
||||
child: Text(
|
||||
value.toString(),
|
||||
style: appStyle.fonts.H_H1.copyWith(fontSize: 24, color: gradeColor),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
export 'package:firka_common/ui/components/grade.dart';
|
||||
|
||||
@@ -1,95 +1 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:firka/core/settings.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:kreta_api/kreta_api.dart';
|
||||
|
||||
int roundGrade(double grade) {
|
||||
final rounding = initData.settings
|
||||
.group("settings")
|
||||
.subGroup("application")
|
||||
.subGroup("rounding");
|
||||
if (grade < 1 + rounding.dbl("1")) {
|
||||
return 1;
|
||||
}
|
||||
if (grade < 2 + rounding.dbl("2")) {
|
||||
return 2;
|
||||
}
|
||||
if (grade < 3 + rounding.dbl("3")) {
|
||||
return 3;
|
||||
}
|
||||
if (grade < 4 + rounding.dbl("4")) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
return 5;
|
||||
}
|
||||
|
||||
int percentageToGrade(int grade) {
|
||||
if (grade < 50) {
|
||||
return 1;
|
||||
}
|
||||
if (grade < 60) {
|
||||
return 2;
|
||||
}
|
||||
if (grade < 70) {
|
||||
return 3;
|
||||
}
|
||||
if (grade < 80) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
return 5;
|
||||
}
|
||||
|
||||
Color getGradeColor(double grade) {
|
||||
switch (roundGrade(grade)) {
|
||||
case 2:
|
||||
return appStyle.colors.grade2;
|
||||
case 3:
|
||||
return appStyle.colors.grade3;
|
||||
case 4:
|
||||
return appStyle.colors.grade4;
|
||||
case 5:
|
||||
return appStyle.colors.grade5;
|
||||
default:
|
||||
return appStyle.colors.grade1;
|
||||
}
|
||||
}
|
||||
|
||||
(int total, List<int> countsByGrade) getGradeDistribution(List<Grade> grades) {
|
||||
final filtered = grades
|
||||
.where((g) => g.type.name != "felevi_jegy_ertekeles")
|
||||
.toList();
|
||||
final counts = [0, 0, 0, 0, 0];
|
||||
for (final g in filtered) {
|
||||
if (g.numericValue == null) continue;
|
||||
final value = g.valueType.name == "Szazalekos"
|
||||
? percentageToGrade(g.numericValue!.round())
|
||||
: g.numericValue!.round().clamp(1, 5);
|
||||
counts[value - 1]++;
|
||||
}
|
||||
return (filtered.length, counts);
|
||||
}
|
||||
|
||||
extension GradeListExtension on List<Grade> {
|
||||
double getAverageBySubject(Subject subject) {
|
||||
var weightTotal = 0.00;
|
||||
var sum = 0.00;
|
||||
|
||||
for (var grade in this) {
|
||||
if (grade.subject.uid == subject.uid) {
|
||||
if (grade.numericValue != null) {
|
||||
var weight = (grade.weightPercentage ?? 100) / 100.0;
|
||||
weightTotal += weight;
|
||||
|
||||
sum += grade.numericValue! * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sum / weightTotal;
|
||||
}
|
||||
}
|
||||
export 'package:firka_common/ui/components/grade_helpers.dart';
|
||||
|
||||
@@ -26,7 +26,7 @@ void showReauthBottomSheet(
|
||||
elevation: 100,
|
||||
isScrollControlled: true,
|
||||
isDismissible: true,
|
||||
enableDrag: true,
|
||||
enableDrag: false,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (BuildContext context) {
|
||||
return Container(
|
||||
|
||||
@@ -14,6 +14,7 @@ import 'package:firka/core/debug_helper.dart';
|
||||
import 'package:firka/core/state/firka_state.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/core/bloc/home_refresh_cubit.dart';
|
||||
import 'package:firka/core/settings.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/shared/delayed_spinner.dart';
|
||||
|
||||
@@ -145,18 +146,6 @@ class _HomeGradesScreen extends FirkaState<HomeGradesScreen> {
|
||||
|
||||
double avg = double.nan;
|
||||
if (subjectGrades.isNotEmpty) {
|
||||
for (var grade in subjectGrades) {
|
||||
if (grade.valueType.name == "Szazalekos") {
|
||||
grade.valueType = NameUidDesc(
|
||||
uid: "1,Osztalyzat",
|
||||
name: "Osztalyzat",
|
||||
description: "",
|
||||
);
|
||||
if (grade.numericValue != null) {
|
||||
grade.numericValue = percentageToGrade(grade.numericValue!);
|
||||
}
|
||||
}
|
||||
}
|
||||
avg = grades!.response!.getAverageBySubject(subject);
|
||||
}
|
||||
|
||||
@@ -197,7 +186,17 @@ class _HomeGradesScreen extends FirkaState<HomeGradesScreen> {
|
||||
if (!avg.isNaN) {
|
||||
subjectCount++;
|
||||
subjectAvg += avg;
|
||||
subjectAvgRounded += roundGrade(avg);
|
||||
final rounding = widget.data.settings
|
||||
.group("settings")
|
||||
.subGroup("application")
|
||||
.subGroup("rounding");
|
||||
subjectAvgRounded += roundGrade(
|
||||
avg,
|
||||
t1: rounding.dbl("1"),
|
||||
t2: rounding.dbl("2"),
|
||||
t3: rounding.dbl("3"),
|
||||
t4: rounding.dbl("4"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +208,17 @@ class _HomeGradesScreen extends FirkaState<HomeGradesScreen> {
|
||||
subjectAvgRounded = 0.00;
|
||||
}
|
||||
|
||||
var subjectAvgColor = getGradeColor(subjectAvg);
|
||||
final rounding = widget.data.settings
|
||||
.group("settings")
|
||||
.subGroup("application")
|
||||
.subGroup("rounding");
|
||||
var subjectAvgColor = getGradeColor(
|
||||
subjectAvg,
|
||||
t1: rounding.dbl("1"),
|
||||
t2: rounding.dbl("2"),
|
||||
t3: rounding.dbl("3"),
|
||||
t4: rounding.dbl("4"),
|
||||
);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 20.0, right: 20.0, top: 12.0),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:firka/ui/phone/widgets/grade_summary_bar.dart';
|
||||
import 'package:kreta_api/kreta_api.dart';
|
||||
import 'package:firka/core/extensions.dart';
|
||||
import 'package:firka/ui/components/common_bottom_sheets.dart';
|
||||
@@ -131,7 +132,17 @@ class _HomeGradesSubjectScreen extends FirkaState<HomeGradesSubjectScreen> {
|
||||
}).toList();
|
||||
|
||||
var gradeWidgets = List<Widget>.empty(growable: true);
|
||||
gradeWidgets.addAll(ghostGradeWidgets);
|
||||
if (ghostGradeWidgets.isNotEmpty) {
|
||||
gradeWidgets.add(
|
||||
Text(
|
||||
widget.data.l10n.ghost_grades,
|
||||
style: appStyle.fonts.B_16R.apply(
|
||||
color: appStyle.colors.textPrimary,
|
||||
),
|
||||
),
|
||||
);
|
||||
gradeWidgets.addAll(ghostGradeWidgets);
|
||||
}
|
||||
|
||||
for (var group in groups.entries) {
|
||||
gradeWidgets.add(SizedBox(height: 8));
|
||||
@@ -302,6 +313,12 @@ class _HomeGradesSubjectScreen extends FirkaState<HomeGradesSubjectScreen> {
|
||||
GradeChartWithInteraction(
|
||||
grades: _gradesWithGhosts(aGrade.subject),
|
||||
),
|
||||
SizedBox(height: 2),
|
||||
GradeSummaryBar(
|
||||
grades: _gradesWithGhosts(aGrade.subject),
|
||||
l10n: widget.data.l10n,
|
||||
showAverage: ghostGradeWidgets.isNotEmpty,
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 4),
|
||||
|
||||
@@ -55,7 +55,7 @@ class _HomeMainScreen extends FirkaState<HomeMainScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> fetchData({bool cacheOnly = true}) async {
|
||||
Future<void> fetchData({bool cacheOnly = false}) async {
|
||||
final midnight = now.getMidnight();
|
||||
|
||||
var lessonsFetched = 0;
|
||||
|
||||
@@ -410,6 +410,11 @@ class _HomeScreenState extends FirkaState<HomeScreen>
|
||||
await LiveActivityService.showConsentScreenIfNeeded();
|
||||
});
|
||||
}
|
||||
if (Platform.isIOS) {
|
||||
Future.delayed(const Duration(seconds: 4), () {
|
||||
if (!_disposed) _runLiveActivityLoginIfNeeded();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _preloadImages() async {
|
||||
@@ -535,10 +540,35 @@ class _HomeScreenState extends FirkaState<HomeScreen>
|
||||
|
||||
if (Platform.isIOS) {
|
||||
_refreshLiveActivityOnResume();
|
||||
_runLiveActivityLoginIfNeeded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fallback: if Live Activity login never ran (e.g. prefetch bailed on lifecycle
|
||||
/// or fetchData didn't complete), run it once when app is resumed.
|
||||
void _runLiveActivityLoginIfNeeded() {
|
||||
if (_didRunLiveActivityLogin || _disposed) return;
|
||||
Future.delayed(const Duration(milliseconds: 500), () async {
|
||||
if (_disposed || _didRunLiveActivityLogin) return;
|
||||
_didRunLiveActivityLogin = true;
|
||||
final token = pickActiveToken(
|
||||
tokens: initData.tokens,
|
||||
settings: initData.settings,
|
||||
preferredStudentIdNorm: initData.client.model.studentIdNorm,
|
||||
);
|
||||
final studentName = token?.studentId ?? 'Student';
|
||||
LiveActivityService.onUserLogin(
|
||||
client: initData.client,
|
||||
studentName: studentName,
|
||||
settingsStore: initData.settings,
|
||||
).catchError((e, st) {
|
||||
_didRunLiveActivityLogin = false;
|
||||
logger.severe('LiveActivity registration failed: $e', e, st);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void _refreshLiveActivityOnResume() async {
|
||||
if (!_hasCompletedFirstPrefetch) return;
|
||||
try {
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:carousel_slider/carousel_slider.dart';
|
||||
import 'package:firka/core/firka_bundle.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:firka/ui/phone/widgets/login_webview.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:sensors_plus/sensors_plus.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:vibration/vibration.dart';
|
||||
|
||||
import 'package:firka/core/bloc/theme_cubit.dart';
|
||||
import 'package:firka/core/state/firka_state.dart';
|
||||
@@ -15,16 +21,13 @@ import 'package:firka/core/image_preloader.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:firka/ui/shared/delayed_spinner.dart';
|
||||
|
||||
// TODO: Replace these with actual privacy policy URLs
|
||||
const String _privacyUrlHungarian =
|
||||
'https://github.com/QwIT-Development/privacy-policy/blob/master/README.md';
|
||||
const String _privacyUrlOther = 'https://firka.app/privacy';
|
||||
|
||||
class LoginScreen extends StatefulWidget {
|
||||
final AppInitialization data;
|
||||
|
||||
const LoginScreen(this.data, {super.key});
|
||||
|
||||
@override
|
||||
State<LoginScreen> createState() => _LoginScreenState();
|
||||
}
|
||||
@@ -36,22 +39,16 @@ class _LoginScreenState extends FirkaState<LoginScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_loginWebView = LoginWebviewWidget(widget.data);
|
||||
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
||||
|
||||
_preloadImages();
|
||||
}
|
||||
|
||||
// Method to get the appropriate privacy policy URL based on language
|
||||
String _getPrivacyPolicyUrl() {
|
||||
// Check if current language is Hungarian by examining the locale
|
||||
final locale = Localizations.localeOf(context).languageCode;
|
||||
return locale == 'hu' ? _privacyUrlHungarian : _privacyUrlOther;
|
||||
}
|
||||
|
||||
// Method to launch privacy policy URL
|
||||
Future<void> _launchPrivacyPolicy() async {
|
||||
final url = _getPrivacyPolicyUrl();
|
||||
try {
|
||||
@@ -72,11 +69,10 @@ class _LoginScreenState extends FirkaState<LoginScreen> {
|
||||
"assets/images/carousel_dark/slide3.webp",
|
||||
"assets/images/carousel_dark/slide4.webp",
|
||||
"assets/images/logos/colored_logo.webp",
|
||||
"assets/images/logos/loading.gif",
|
||||
];
|
||||
|
||||
try {
|
||||
await ImagePreloader.preloadMultipleAssets(FirkaBundle(), imagePaths);
|
||||
|
||||
setState(() {
|
||||
_preloadDone = true;
|
||||
});
|
||||
@@ -116,6 +112,7 @@ class _LoginScreenState extends FirkaState<LoginScreen> {
|
||||
final paddingWidthHorizontal =
|
||||
MediaQuery.of(context).size.width -
|
||||
MediaQuery.of(context).size.width * 0.95;
|
||||
|
||||
List<Map<String, Object>> slides = [
|
||||
{
|
||||
'title': widget.data.l10n.title1,
|
||||
@@ -124,7 +121,6 @@ class _LoginScreenState extends FirkaState<LoginScreen> {
|
||||
'background': 'assets/images/carousel/slide1_background.webp',
|
||||
'foreground': '',
|
||||
'rotation': 180.00,
|
||||
// „Mi nekünk két szám típusunk van, int (egy 32 bites szám) meg a double (egy 64 bites tört szám), KURVA ANYÁDAT”
|
||||
'scale': 1.5,
|
||||
'x': 0.00,
|
||||
'y': 150.00,
|
||||
@@ -136,7 +132,6 @@ class _LoginScreenState extends FirkaState<LoginScreen> {
|
||||
'background': 'assets/images/carousel/slide2_background.webp',
|
||||
'foreground': '',
|
||||
'rotation': 180.00,
|
||||
//Mivel radiáns, és nullával nem lehet osztani (remélem tudtad), ezért ha eggyel osztunk akkor egy marad
|
||||
'scale': 1.55,
|
||||
'x': 10.00,
|
||||
'y': 160.00,
|
||||
@@ -155,15 +150,15 @@ class _LoginScreenState extends FirkaState<LoginScreen> {
|
||||
{
|
||||
'title': widget.data.l10n.title4,
|
||||
'subtitle': widget.data.l10n.subtitle4,
|
||||
'picture': 'assets/images/$carousel/slide4.webp',
|
||||
'picture': '',
|
||||
'background': 'assets/images/carousel/slide4_background.webp',
|
||||
'foreground': '',
|
||||
'rotation': 180.00,
|
||||
'scale': 1.35,
|
||||
'x': -5.00,
|
||||
'y': 80.00,
|
||||
'cards': true,
|
||||
},
|
||||
//TODO: implement simulated physics so that the little boxes can move like the phone moves
|
||||
];
|
||||
|
||||
return MaterialApp(
|
||||
@@ -211,43 +206,106 @@ class _LoginScreenState extends FirkaState<LoginScreen> {
|
||||
Expanded(
|
||||
child: CarouselSlider.builder(
|
||||
itemCount: slides.length,
|
||||
itemBuilder: (context, index, realIndex) => Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsetsGeometry.symmetric(
|
||||
horizontal: paddingWidthHorizontal,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
slides[index]['title']! as String,
|
||||
style: appStyle.fonts.H_18px.copyWith(
|
||||
color: appStyle.colors.textPrimary,
|
||||
itemBuilder: (context, index, realIndex) {
|
||||
final isCards = slides[index]['cards'] == true;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsetsGeometry.symmetric(
|
||||
horizontal: paddingWidthHorizontal,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
slides[index]['title']! as String,
|
||||
style: appStyle.fonts.H_18px.copyWith(
|
||||
color: appStyle.colors.textPrimary,
|
||||
),
|
||||
softWrap: true,
|
||||
overflow: TextOverflow.visible,
|
||||
),
|
||||
softWrap: true,
|
||||
overflow: TextOverflow.visible,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
slides[index]['subtitle']! as String,
|
||||
style: appStyle.fonts.B_16R.copyWith(
|
||||
color: appStyle.colors.textPrimary,
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
slides[index]['subtitle']! as String,
|
||||
style: appStyle.fonts.B_16R.copyWith(
|
||||
color: appStyle.colors.textPrimary,
|
||||
),
|
||||
softWrap: true,
|
||||
overflow: TextOverflow.visible,
|
||||
),
|
||||
softWrap: true,
|
||||
overflow: TextOverflow.visible,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Stack(
|
||||
children: [
|
||||
slides[index]['background']! == ''
|
||||
? SizedBox()
|
||||
: ClipRect(
|
||||
if (isCards)
|
||||
Expanded(
|
||||
child: Stack(
|
||||
children: [
|
||||
if ((slides[index]['background'] ?? '')
|
||||
.toString()
|
||||
.isNotEmpty)
|
||||
ClipRect(
|
||||
clipper: ImageClipper(
|
||||
MediaQuery.of(context),
|
||||
),
|
||||
child: Transform.rotate(
|
||||
angle:
|
||||
-math.pi /
|
||||
(slides[index]['rotation']!
|
||||
as double),
|
||||
child: Transform.translate(
|
||||
offset: Offset(
|
||||
slides[index]['x'] as double,
|
||||
slides[index]['y'] as double,
|
||||
),
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(
|
||||
context,
|
||||
).size.width,
|
||||
child: Transform.scale(
|
||||
scale:
|
||||
slides[index]['scale']
|
||||
as double,
|
||||
child: Image.asset(
|
||||
slides[index]['background']!
|
||||
as String,
|
||||
bundle: DefaultAssetBundle.of(
|
||||
context,
|
||||
),
|
||||
fit: BoxFit.contain,
|
||||
width: double.infinity,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8),
|
||||
child: LayoutBuilder(
|
||||
builder: (ctx, constraints) =>
|
||||
_FloatingCardsSlide(
|
||||
width: constraints.maxWidth,
|
||||
height: constraints.maxHeight,
|
||||
topPadding: 30,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
else
|
||||
Stack(
|
||||
children: [
|
||||
if ((slides[index]['background'] ?? '')
|
||||
.toString()
|
||||
.isNotEmpty)
|
||||
ClipRect(
|
||||
clipper: ImageClipper(
|
||||
MediaQuery.of(context),
|
||||
),
|
||||
@@ -283,31 +341,44 @@ class _LoginScreenState extends FirkaState<LoginScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
SizedBox(height: 73),
|
||||
Padding(
|
||||
padding: EdgeInsetsGeometry.symmetric(
|
||||
horizontal: 18,
|
||||
),
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: Image(
|
||||
image: PreloadedImageProvider(
|
||||
DefaultAssetBundle.of(context),
|
||||
slides[index]['picture']! as String,
|
||||
Column(
|
||||
children: [
|
||||
const SizedBox(height: 73),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
// 10 padding each side
|
||||
// LayoutBuilder receives the inset width so the physics walls match the edges
|
||||
),
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(
|
||||
context,
|
||||
).size.width,
|
||||
child:
|
||||
(slides[index]['picture'] ?? '')
|
||||
.toString()
|
||||
.isNotEmpty
|
||||
? Image(
|
||||
image: PreloadedImageProvider(
|
||||
DefaultAssetBundle.of(
|
||||
context,
|
||||
),
|
||||
slides[index]['picture']!
|
||||
as String,
|
||||
),
|
||||
fit: BoxFit.cover,
|
||||
width: double.infinity,
|
||||
alignment: Alignment.center,
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
fit: BoxFit.cover,
|
||||
width: double.infinity,
|
||||
alignment: Alignment.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
slides[index]['foreground']! == ''
|
||||
? SizedBox()
|
||||
: SizedBox(
|
||||
if ((slides[index]['foreground'] ?? '')
|
||||
.toString()
|
||||
.isNotEmpty)
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: ClipRect(
|
||||
clipBehavior: Clip.none,
|
||||
@@ -342,10 +413,11 @@ class _LoginScreenState extends FirkaState<LoginScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
options: CarouselOptions(
|
||||
height: double.infinity,
|
||||
autoPlay: false,
|
||||
@@ -368,8 +440,8 @@ class _LoginScreenState extends FirkaState<LoginScreen> {
|
||||
colors: [
|
||||
appStyle.colors.background.withAlpha(0),
|
||||
appStyle.colors.background,
|
||||
], // customize colors
|
||||
stops: [0.0, 0.5], // percentages (0% → 50% → 100%)
|
||||
],
|
||||
stops: const [0.0, 0.5],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
),
|
||||
@@ -418,7 +490,7 @@ class _LoginScreenState extends FirkaState<LoginScreen> {
|
||||
13,
|
||||
),
|
||||
blurRadius: 2,
|
||||
offset: Offset(0, 1),
|
||||
offset: const Offset(0, 1),
|
||||
spreadRadius: 0,
|
||||
),
|
||||
],
|
||||
@@ -429,7 +501,9 @@ class _LoginScreenState extends FirkaState<LoginScreen> {
|
||||
textAlign: TextAlign.center,
|
||||
style: appStyle.fonts.H_16px.copyWith(
|
||||
color: appStyle.colors.textPrimaryLight,
|
||||
fontVariations: [FontVariation("wght", 800)],
|
||||
fontVariations: const [
|
||||
FontVariation("wght", 800),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -459,10 +533,453 @@ class _LoginScreenState extends FirkaState<LoginScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
//card config
|
||||
class _CardConfig {
|
||||
final String asset;
|
||||
final Offset baseOffset;
|
||||
final double size;
|
||||
final double parallax;
|
||||
final double glide;
|
||||
final double aspect;
|
||||
|
||||
const _CardConfig({
|
||||
required this.asset,
|
||||
required this.baseOffset,
|
||||
required this.size,
|
||||
required this.parallax,
|
||||
required this.glide,
|
||||
this.aspect = 0.72,
|
||||
});
|
||||
}
|
||||
|
||||
//floating card part
|
||||
class _FloatingCardsSlide extends StatefulWidget {
|
||||
final double width;
|
||||
final double height;
|
||||
final double topPadding;
|
||||
const _FloatingCardsSlide({
|
||||
required this.width,
|
||||
required this.height,
|
||||
this.topPadding = 0,
|
||||
});
|
||||
@override
|
||||
State<_FloatingCardsSlide> createState() => _FloatingCardsSlideState();
|
||||
}
|
||||
|
||||
class _FloatingCardsSlideState extends State<_FloatingCardsSlide>
|
||||
with SingleTickerProviderStateMixin {
|
||||
static const double _friction = 0.878;
|
||||
static const double _cardHeight =
|
||||
45; //not in pixels, idk what unit but it works :p
|
||||
// cap speeds so impacts stay tame
|
||||
static const double _maxSpeed = _cardHeight * 1.2;
|
||||
static const double _tiltForce = 0.05;
|
||||
static const double _bounceDamping = 0.45;
|
||||
static const double _collisionRestitution = 1.0;
|
||||
|
||||
//minimum speed it has to go to trigger a vibration, so the phone doesn't turn into a bomb if the cards are touching
|
||||
static const double _vibrateSpeedThreshold = _maxSpeed * 0.09;
|
||||
|
||||
static const List<_CardConfig> _cards = [
|
||||
_CardConfig(
|
||||
asset: 'assets/images/carousel/card1.svg',
|
||||
baseOffset: Offset(-100, -15),
|
||||
size: _cardHeight,
|
||||
parallax: 7.5,
|
||||
glide: 1.05,
|
||||
aspect: 4.48,
|
||||
), // viewBox 215x48
|
||||
_CardConfig(
|
||||
asset: 'assets/images/carousel/card2.svg',
|
||||
baseOffset: Offset(-8, -35),
|
||||
size: _cardHeight,
|
||||
parallax: 9.0,
|
||||
glide: 1.12,
|
||||
aspect: 2.25,
|
||||
), // viewBox 108x48
|
||||
_CardConfig(
|
||||
asset: 'assets/images/carousel/card3.svg',
|
||||
baseOffset: Offset(88, -5),
|
||||
size: _cardHeight,
|
||||
parallax: 7.0,
|
||||
glide: 1.0,
|
||||
aspect: 4.13,
|
||||
), // viewBox 198x48
|
||||
_CardConfig(
|
||||
asset: 'assets/images/carousel/card4.svg',
|
||||
baseOffset: Offset(-60, 55),
|
||||
size: _cardHeight,
|
||||
parallax: 9.5,
|
||||
glide: 1.15,
|
||||
aspect: 2.25,
|
||||
), // viewBox 108x48
|
||||
_CardConfig(
|
||||
asset: 'assets/images/carousel/card5.svg',
|
||||
baseOffset: Offset(52, 80),
|
||||
size: _cardHeight,
|
||||
parallax: 10.5,
|
||||
glide: 1.18,
|
||||
aspect: 3.02,
|
||||
), // viewBox 145x48
|
||||
_CardConfig(
|
||||
asset: 'assets/images/carousel/card6.svg',
|
||||
baseOffset: Offset(128, 18),
|
||||
size: _cardHeight,
|
||||
parallax: 7.5,
|
||||
glide: 0.95,
|
||||
aspect: 5.63,
|
||||
), // viewBox 270x48
|
||||
_CardConfig(
|
||||
asset: 'assets/images/carousel/card7.svg',
|
||||
baseOffset: Offset(-138, 22),
|
||||
size: _cardHeight,
|
||||
parallax: 8.5,
|
||||
glide: 1.05,
|
||||
aspect: 3.94,
|
||||
), // viewBox 189x48
|
||||
];
|
||||
|
||||
late Ticker _ticker;
|
||||
List<Offset> _positions = [];
|
||||
List<Offset> _velocities = [];
|
||||
|
||||
Offset _tilt = Offset.zero;
|
||||
Offset? _baseline;
|
||||
StreamSubscription<AccelerometerEvent>? _accelerometerSub;
|
||||
Duration? _lastTick;
|
||||
AccelerometerEvent? _lastAccelEvent;
|
||||
|
||||
double _sceneWidth = 0;
|
||||
double _sceneHeight = 0;
|
||||
|
||||
//we needed a cooldown, so that again, the phone doesn't turn into a bomb | EDIT: actually this was fixed by the minimum speed, so we don't need it anymore
|
||||
// DateTime? _lastVibration;
|
||||
|
||||
Offset _clampVel(Offset v) => Offset(
|
||||
v.dx.clamp(-_maxSpeed, _maxSpeed),
|
||||
v.dy.clamp(-_maxSpeed, _maxSpeed),
|
||||
);
|
||||
|
||||
void _maybeVibrate() {
|
||||
// final now = DateTime.now();
|
||||
// if (_lastVibration != null &&
|
||||
// now.difference(_lastVibration!).inMilliseconds < 2) //first used 50 but it wasn't good enough, so now it's 2
|
||||
// return;
|
||||
// _lastVibration = now;
|
||||
Vibration.vibrate(duration: 20);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_positions = _cards
|
||||
.map((c) => c.baseOffset + const Offset(0, 300))
|
||||
.toList();
|
||||
|
||||
final rng = math.Random();
|
||||
_velocities = List.generate(_cards.length, (i) {
|
||||
final jitter = (rng.nextDouble() - 0.5) * 3.0;
|
||||
return Offset(jitter, -9.0 * _cards[i].glide);
|
||||
});
|
||||
|
||||
_ticker = createTicker(_tick)..start();
|
||||
|
||||
_accelerometerSub = accelerometerEventStream(
|
||||
samplingPeriod: SensorInterval.gameInterval,
|
||||
).listen(_handleTilt);
|
||||
}
|
||||
|
||||
void _handleTilt(AccelerometerEvent event) {
|
||||
_lastAccelEvent = event;
|
||||
final raw = Offset(event.x, event.y);
|
||||
_baseline ??= raw;
|
||||
final rel = raw - _baseline!;
|
||||
_tilt = Offset(
|
||||
(_tilt.dx * 0.88 + rel.dx * 0.12).clamp(-6.5, 6.5),
|
||||
(_tilt.dy * 0.88 + rel.dy * 0.12).clamp(-6.5, 6.5),
|
||||
);
|
||||
}
|
||||
|
||||
void _tick(Duration elapsed) {
|
||||
if (_positions.isEmpty || _velocities.isEmpty) return;
|
||||
if (_lastTick == null) {
|
||||
_lastTick = elapsed;
|
||||
return;
|
||||
}
|
||||
|
||||
final dt = ((elapsed - _lastTick!).inMicroseconds / 16667.0).clamp(
|
||||
0.0,
|
||||
4.0,
|
||||
);
|
||||
_lastTick = elapsed;
|
||||
|
||||
if (_sceneWidth == 0 || _sceneHeight == 0) return;
|
||||
|
||||
bool collidedThisTick = false;
|
||||
bool wallHitThisTick = false;
|
||||
|
||||
setState(() {
|
||||
final slope = Offset(-_tilt.dx, _tilt.dy);
|
||||
final n = _cards.length;
|
||||
|
||||
//friction
|
||||
for (int i = 0; i < n; i++) {
|
||||
final card = _cards[i];
|
||||
_velocities[i] += slope * card.parallax * _tiltForce * dt;
|
||||
_velocities[i] *= math.pow(_friction, dt).toDouble();
|
||||
_velocities[i] = _clampVel(_velocities[i]);
|
||||
_positions[i] += _velocities[i] * dt;
|
||||
}
|
||||
|
||||
// card to card collison and wall stuff
|
||||
//
|
||||
// running both together in a loop means that when card a is against a
|
||||
// wall and card b pushes into it, the wall clamp on the a card.
|
||||
// it goes back through the collision math wizard on the next loop,
|
||||
// so car b receives the correct reaction instead of having a mating session with card a.
|
||||
// five times is enough i think, more on slower end devices might cause issues idk tho
|
||||
final double halfW = _sceneWidth / 2;
|
||||
final double halfH = _sceneHeight / 2;
|
||||
|
||||
for (int iter = 0; iter < 5; iter++) {
|
||||
//here's the card to card magic, meow i'm going crazy :3
|
||||
for (int i = 0; i < n - 1; i++) {
|
||||
for (int j = i + 1; j < n; j++) {
|
||||
final wi = _cards[i].size * _cards[i].aspect;
|
||||
final wj = _cards[j].size * _cards[j].aspect;
|
||||
final hi = _cards[i].size;
|
||||
final hj = _cards[j].size;
|
||||
|
||||
final pi = _positions[i];
|
||||
final pj = _positions[j];
|
||||
|
||||
final overlapX = (wi + wj) / 2 - (pj.dx - pi.dx).abs();
|
||||
final overlapY = (hi + hj) / 2 - (pj.dy - pi.dy).abs();
|
||||
|
||||
if (overlapX > 0 && overlapY > 0) {
|
||||
if (overlapX < overlapY) {
|
||||
final sign = pj.dx > pi.dx ? 1.0 : -1.0;
|
||||
_positions[i] = Offset(pi.dx - sign * overlapX / 2, pi.dy);
|
||||
_positions[j] = Offset(pj.dx + sign * overlapX / 2, pj.dy);
|
||||
|
||||
final viX = _velocities[i].dx;
|
||||
final vjX = _velocities[j].dx;
|
||||
if ((viX - vjX) * sign > 0) {
|
||||
final impulse = (viX - vjX) * _collisionRestitution;
|
||||
_velocities[i] = _clampVel(
|
||||
Offset(_velocities[i].dx - impulse, _velocities[i].dy),
|
||||
);
|
||||
_velocities[j] = _clampVel(
|
||||
Offset(_velocities[j].dx + impulse, _velocities[j].dy),
|
||||
);
|
||||
if ((viX - vjX).abs() > _vibrateSpeedThreshold) {
|
||||
collidedThisTick = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final sign = pj.dy > pi.dy ? 1.0 : -1.0;
|
||||
_positions[i] = Offset(pi.dx, pi.dy - sign * overlapY / 2);
|
||||
_positions[j] = Offset(pj.dx, pj.dy + sign * overlapY / 2);
|
||||
|
||||
final viY = _velocities[i].dy;
|
||||
final vjY = _velocities[j].dy;
|
||||
if ((viY - vjY) * sign > 0) {
|
||||
final impulse = (viY - vjY) * _collisionRestitution;
|
||||
_velocities[i] = _clampVel(
|
||||
Offset(_velocities[i].dx, _velocities[i].dy - impulse),
|
||||
);
|
||||
_velocities[j] = _clampVel(
|
||||
Offset(_velocities[j].dx, _velocities[j].dy + impulse),
|
||||
);
|
||||
if ((viY - vjY).abs() > _vibrateSpeedThreshold) {
|
||||
collidedThisTick = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// wall collision, runs every loop, explained before
|
||||
// feeds back into the next collision loop.
|
||||
for (int i = 0; i < n; i++) {
|
||||
final card = _cards[i];
|
||||
final double cardW = card.size * card.aspect;
|
||||
final double cardH = card.size;
|
||||
|
||||
final double minX = -halfW + cardW / 2;
|
||||
final double maxX = halfW - cardW / 2;
|
||||
final double minY = -halfH + cardH / 2;
|
||||
final double maxY = halfH - cardH / 2;
|
||||
|
||||
Offset pos = _positions[i];
|
||||
double vx = _velocities[i].dx;
|
||||
double vy = _velocities[i].dy;
|
||||
|
||||
// capture incoming velocity components before any bounce damping
|
||||
final double preVx = vx;
|
||||
final double preVy = vy;
|
||||
double impactSpeed = 0.0; // normal to the wall
|
||||
|
||||
bool hit = false;
|
||||
|
||||
if (pos.dx < minX) {
|
||||
pos = Offset(minX, pos.dy);
|
||||
vx = vx.abs() * _bounceDamping;
|
||||
impactSpeed = math.max(impactSpeed, preVx.abs());
|
||||
hit = true;
|
||||
} else if (pos.dx > maxX) {
|
||||
pos = Offset(maxX, pos.dy);
|
||||
vx = -vx.abs() * _bounceDamping;
|
||||
impactSpeed = math.max(impactSpeed, preVx.abs());
|
||||
hit = true;
|
||||
}
|
||||
|
||||
if (pos.dy < minY) {
|
||||
pos = Offset(pos.dx, minY);
|
||||
vy = vy.abs() * _bounceDamping;
|
||||
impactSpeed = math.max(impactSpeed, preVy.abs());
|
||||
hit = true;
|
||||
} else if (pos.dy > maxY) {
|
||||
pos = Offset(pos.dx, maxY);
|
||||
vy = -vy.abs() * _bounceDamping;
|
||||
impactSpeed = math.max(impactSpeed, preVy.abs());
|
||||
hit = true;
|
||||
}
|
||||
|
||||
_velocities[i] = _clampVel(Offset(vx, vy));
|
||||
_positions[i] = pos;
|
||||
|
||||
// vibrate only when the component toward the wall exceeded threshold
|
||||
if (hit && impactSpeed > _vibrateSpeedThreshold) {
|
||||
wallHitThisTick = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// vibration on collisions (card-card or walls)
|
||||
if (collidedThisTick || wallHitThisTick) _maybeVibrate();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_ticker.dispose();
|
||||
_accelerometerSub?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final double totalHeight = widget.height;
|
||||
_sceneWidth = widget.width;
|
||||
_sceneHeight = math.max(0, totalHeight - widget.topPadding);
|
||||
|
||||
final Offset center = Offset(
|
||||
_sceneWidth / 2,
|
||||
widget.topPadding + _sceneHeight / 2,
|
||||
);
|
||||
|
||||
return SizedBox(
|
||||
width: _sceneWidth,
|
||||
height: totalHeight,
|
||||
child: Stack(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
children: [
|
||||
//background rectangle
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: widget.topPadding,
|
||||
bottom: 0,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: appStyle.colors.buttonSecondaryFill, //button color xdddd
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(32),
|
||||
topRight: Radius.circular(32),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
...List.generate(_cards.length, (i) {
|
||||
final card = _cards[i];
|
||||
final double cardWidth = card.size * card.aspect;
|
||||
final double cardHeight = card.size;
|
||||
final Offset pos =
|
||||
center + _positions[i] - Offset(cardWidth / 2, cardHeight / 2);
|
||||
return Positioned(
|
||||
left: pos.dx,
|
||||
top: pos.dy,
|
||||
width: cardWidth,
|
||||
height: cardHeight,
|
||||
child: SvgPicture.asset(
|
||||
card.asset,
|
||||
width: cardWidth,
|
||||
height: cardHeight,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
);
|
||||
}),
|
||||
if (kDebugMode) _buildDebugOverlay(center),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDebugOverlay(Offset center) {
|
||||
final firstPos = _positions.isNotEmpty ? _positions.first : Offset.zero;
|
||||
final firstVel = _velocities.isNotEmpty ? _velocities.first : Offset.zero;
|
||||
final accel = _lastAccelEvent;
|
||||
final speed = firstVel.distance;
|
||||
|
||||
String formatOffset(Offset o) =>
|
||||
'(${o.dx.toStringAsFixed(2)}, ${o.dy.toStringAsFixed(2)})';
|
||||
|
||||
return Positioned(
|
||||
left: 12,
|
||||
top: 12,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withOpacity(0.55),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
width: math.min(260, _sceneWidth - 24),
|
||||
child: DefaultTextStyle(
|
||||
style: appStyle.fonts.B_12R.copyWith(
|
||||
color: Colors.white,
|
||||
height: 1.25,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text('Sim Debug'),
|
||||
const SizedBox(height: 4),
|
||||
Text('tilt: ${formatOffset(_tilt)}'),
|
||||
if (accel != null)
|
||||
Text(
|
||||
'accel: (${accel.x.toStringAsFixed(2)}, ${accel.y.toStringAsFixed(2)}, ${accel.z.toStringAsFixed(2)})',
|
||||
),
|
||||
Text('baseline set: ${_baseline != null}'),
|
||||
Text('scene: ${_sceneWidth.toStringAsFixed(0)} x ${_sceneHeight.toStringAsFixed(0)}'),
|
||||
Text('center: ${formatOffset(center)}'),
|
||||
Text('card[0] pos: ${formatOffset(firstPos)}'),
|
||||
Text('card[0] vel: ${formatOffset(firstVel)} | |v|=${speed.toStringAsFixed(2)}'),
|
||||
Text('cards: ${_cards.length}'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// this sucks :3
|
||||
class ImageClipper extends CustomClipper<Rect> {
|
||||
final MediaQueryData _mediaQuery;
|
||||
|
||||
ImageClipper(this._mediaQuery);
|
||||
|
||||
@override
|
||||
@@ -476,7 +993,5 @@ class ImageClipper extends CustomClipper<Rect> {
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldReclip(covariant CustomClipper<Rect> oldClipper) {
|
||||
return false;
|
||||
}
|
||||
bool shouldReclip(covariant CustomClipper<Rect> oldClipper) => false;
|
||||
}
|
||||
|
||||
@@ -157,6 +157,11 @@ class _SettingsScreenState extends FirkaState<SettingsScreen> {
|
||||
item.iconType!,
|
||||
item.iconData!,
|
||||
color: appStyle.colors.accent,
|
||||
package:
|
||||
item.iconType == FirkaIconType.icons ||
|
||||
item.iconType == FirkaIconType.majesticonsLocal
|
||||
? 'firka'
|
||||
: null,
|
||||
),
|
||||
);
|
||||
cardWidgets.add(SizedBox(width: 8));
|
||||
@@ -224,6 +229,12 @@ class _SettingsScreenState extends FirkaState<SettingsScreen> {
|
||||
item.iconType!,
|
||||
item.iconData!,
|
||||
color: appStyle.colors.accent,
|
||||
package:
|
||||
item.iconType == FirkaIconType.icons ||
|
||||
item.iconType ==
|
||||
FirkaIconType.majesticonsLocal
|
||||
? 'firka'
|
||||
: null,
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
],
|
||||
@@ -265,6 +276,12 @@ class _SettingsScreenState extends FirkaState<SettingsScreen> {
|
||||
item.iconType!,
|
||||
item.iconData!,
|
||||
color: appStyle.colors.accent,
|
||||
package:
|
||||
item.iconType == FirkaIconType.icons ||
|
||||
item.iconType ==
|
||||
FirkaIconType.majesticonsLocal
|
||||
? 'firka'
|
||||
: null,
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
],
|
||||
@@ -912,6 +929,7 @@ class _SettingsScreenState extends FirkaState<SettingsScreen> {
|
||||
showModalBottomSheet<void>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
enableDrag: false,
|
||||
builder: (BuildContext context) {
|
||||
return LoginWebviewWidget(widget.data);
|
||||
},
|
||||
@@ -1067,6 +1085,12 @@ class _SettingsScreenState extends FirkaState<SettingsScreen> {
|
||||
item.iconType!,
|
||||
item.iconData!,
|
||||
color: appStyle.colors.accent,
|
||||
package:
|
||||
item.iconType == FirkaIconType.icons ||
|
||||
item.iconType ==
|
||||
FirkaIconType.majesticonsLocal
|
||||
? 'firka'
|
||||
: null,
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
],
|
||||
|
||||
@@ -33,6 +33,10 @@ class _GradeChartState extends State<GradeChart> {
|
||||
double totalWeight = 0;
|
||||
for (final g in grades) {
|
||||
if (g.subject.uid != subjectUid) continue;
|
||||
final name = g.valueType.name?.toLowerCase() ?? '';
|
||||
final isPercentage =
|
||||
name.contains('szazalek') || name.contains('percent');
|
||||
if (isPercentage) continue;
|
||||
final v = g.numericValue;
|
||||
final w = g.weightPercentage;
|
||||
if (v != null && w != null) {
|
||||
@@ -93,6 +97,27 @@ class _GradeChartState extends State<GradeChart> {
|
||||
}
|
||||
}
|
||||
|
||||
List<FlSpot> _smoothSpots(List<FlSpot> input) {
|
||||
if (input.length < 3) return input;
|
||||
|
||||
final smoothed = <FlSpot>[];
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
if (i == 0 || i == input.length - 1) {
|
||||
smoothed.add(input[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
final prev = input[i - 1].y;
|
||||
final curr = input[i].y;
|
||||
final next = input[i + 1].y;
|
||||
final blended = (0.25 * prev) + (0.5 * curr) + (0.25 * next);
|
||||
|
||||
smoothed.add(FlSpot(input[i].x, blended));
|
||||
}
|
||||
|
||||
return smoothed;
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant GradeChart oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
@@ -213,8 +238,10 @@ class _GradeChartState extends State<GradeChart> {
|
||||
}
|
||||
|
||||
LineChartData avgData() {
|
||||
var firstX = spots.first.x;
|
||||
var lastX = spots.last.x;
|
||||
final smoothedSpots = _smoothSpots(spots);
|
||||
|
||||
var firstX = smoothedSpots.first.x;
|
||||
var lastX = smoothedSpots.last.x;
|
||||
if (firstX == lastX) {
|
||||
lastX = firstX + 1;
|
||||
}
|
||||
@@ -359,12 +386,12 @@ class _GradeChartState extends State<GradeChart> {
|
||||
|
||||
lineBarsData: [
|
||||
LineChartBarData(
|
||||
spots: spots,
|
||||
spots: smoothedSpots,
|
||||
isCurved: true,
|
||||
curveSmoothness: 0.35,
|
||||
curveSmoothness: 0.5,
|
||||
showingIndicators: _touchedIndex != null ? [_touchedIndex!] : [],
|
||||
gradient: LinearGradient(
|
||||
colors: [for (final s in spots) colorForY(s.y)],
|
||||
colors: [for (final s in smoothedSpots) colorForY(s.y)],
|
||||
),
|
||||
barWidth: 5,
|
||||
isStrokeCapRound: true,
|
||||
@@ -373,7 +400,8 @@ class _GradeChartState extends State<GradeChart> {
|
||||
show: true,
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
for (final s in spots) colorForY(s.y).withValues(alpha: 0.1),
|
||||
for (final s in smoothedSpots)
|
||||
colorForY(s.y).withValues(alpha: 0.1),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:firka/core/average_helper.dart';
|
||||
import 'package:kreta_api/kreta_api.dart';
|
||||
import 'package:firka/ui/components/grade.dart';
|
||||
import 'package:firka/ui/components/grade_helpers.dart';
|
||||
@@ -11,8 +12,14 @@ import 'package:firka/ui/theme/style.dart';
|
||||
class GradeSummaryBar extends StatefulWidget {
|
||||
final List<Grade> grades;
|
||||
final AppLocalizations l10n;
|
||||
final bool showAverage;
|
||||
|
||||
const GradeSummaryBar({super.key, required this.grades, required this.l10n});
|
||||
const GradeSummaryBar({
|
||||
super.key,
|
||||
required this.grades,
|
||||
required this.l10n,
|
||||
this.showAverage = false,
|
||||
});
|
||||
|
||||
@override
|
||||
State<GradeSummaryBar> createState() => _GradeSummaryBarState();
|
||||
@@ -32,6 +39,9 @@ class _GradeSummaryBarState extends State<GradeSummaryBar> {
|
||||
appStyle.colors.grade5,
|
||||
];
|
||||
final totalCounted = countsByGrade.reduce((a, b) => a + b);
|
||||
final averageText = widget.showAverage
|
||||
? calculateAverage(widget.grades).toStringAsFixed(2)
|
||||
: '';
|
||||
|
||||
return Card(
|
||||
shadowColor: Colors.transparent,
|
||||
@@ -49,7 +59,9 @@ class _GradeSummaryBarState extends State<GradeSummaryBar> {
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
widget.l10n.gradesCount(total),
|
||||
widget.showAverage
|
||||
? '${widget.l10n.gradesCount(total)} ($averageText)'
|
||||
: widget.l10n.gradesCount(total),
|
||||
style: appStyle.fonts.B_16SB.apply(
|
||||
color: appStyle.colors.textPrimary,
|
||||
),
|
||||
|
||||
@@ -6,7 +6,9 @@ import 'package:firka/services/live_activity_service.dart';
|
||||
import 'package:firka/app/app_state.dart';
|
||||
import 'package:firka/app/initialization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:isar_community/isar.dart';
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
import 'package:firka/services/watch_sync_helper.dart';
|
||||
@@ -194,24 +196,64 @@ class _LoginWebviewWidgetState extends FirkaState<LoginWebviewWidget>
|
||||
final safePadding = mediaQuery.padding;
|
||||
|
||||
return Material(
|
||||
color: appStyle.colors.card,
|
||||
color: appStyle.colors.background, //why was this card? :sob:
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: 61 + safePadding.top,
|
||||
left: 12,
|
||||
right: 12,
|
||||
bottom: 0 + safePadding.bottom,
|
||||
bottom: safePadding.bottom,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
widget.data.l10n.runningInDomainBrowser,
|
||||
style: appStyle.fonts.B_16R.copyWith(
|
||||
color: appStyle.colors.textPrimary,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 2),
|
||||
child: SvgPicture.asset(
|
||||
"assets/icons/dave.svg",
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
widget.data.l10n.runningInDomainBrowser,
|
||||
style: appStyle.fonts.B_16R.copyWith(
|
||||
color: appStyle.colors.textPrimary,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
GestureDetector(
|
||||
onTap: () => Navigator.of(context).pop(),
|
||||
child: Container(
|
||||
width: 36,
|
||||
height: 36,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: appStyle.colors.buttonSecondaryFill,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: appStyle.colors.shadowColor,
|
||||
offset: const Offset(0, 1),
|
||||
blurRadius: appStyle.colors.shadowBlur.toDouble(),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Majesticon(
|
||||
Majesticon.multiplySolid,
|
||||
color: appStyle.colors.accent,
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const SizedBox(height: 22),
|
||||
Expanded(
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
@@ -234,15 +276,10 @@ class _LoginWebviewWidgetState extends FirkaState<LoginWebviewWidget>
|
||||
child: Container(
|
||||
color: appStyle.colors.background,
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: 32,
|
||||
height: 32,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 3,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
appStyle.colors.accent,
|
||||
),
|
||||
),
|
||||
child: Image.asset(
|
||||
"assets/images/logos/loading.gif",
|
||||
width: 50,
|
||||
height: 50,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -253,6 +290,104 @@ class _LoginWebviewWidgetState extends FirkaState<LoginWebviewWidget>
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 42,
|
||||
decoration: BoxDecoration(
|
||||
color: appStyle.colors.card,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
"eKréta/Bejelentkezés",
|
||||
style: appStyle.fonts.B_14R.copyWith(
|
||||
fontSize: 16,
|
||||
color: appStyle.colors.textPrimary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
width: 34,
|
||||
height: 34,
|
||||
decoration: BoxDecoration(
|
||||
color: appStyle.colors.buttonSecondaryFill,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: appStyle.colors.shadowColor,
|
||||
offset: const Offset(0, 1),
|
||||
blurRadius: appStyle.colors.shadowBlur.toDouble(),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Center(
|
||||
child: Image.asset(
|
||||
"assets/icons/button/colorwheel.png",
|
||||
width: 22,
|
||||
height: 22,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
width: 34,
|
||||
height: 34,
|
||||
decoration: BoxDecoration(
|
||||
color: appStyle.colors.buttonSecondaryFill,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: appStyle.colors.shadowColor,
|
||||
offset: const Offset(0, 1),
|
||||
blurRadius: appStyle.colors.shadowBlur.toDouble(),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Center(
|
||||
child: Majesticon(
|
||||
Majesticon.chevronLeftLine,
|
||||
color: appStyle.colors.secondary,
|
||||
size: 22,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
width: 34,
|
||||
height: 34,
|
||||
decoration: BoxDecoration(
|
||||
color: appStyle.colors.buttonSecondaryFill,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: appStyle.colors.shadowColor,
|
||||
offset: const Offset(0, 1),
|
||||
blurRadius: appStyle.colors.shadowBlur.toDouble(),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Center(
|
||||
child: Majesticon(
|
||||
Majesticon.menuLine,
|
||||
color: appStyle.colors.secondary,
|
||||
size: 22,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,35 +1 @@
|
||||
import 'package:firka/core/icon_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
|
||||
class ClassIconWidget extends StatelessWidget {
|
||||
final String _uid;
|
||||
final String _className;
|
||||
final String _category;
|
||||
final Color color;
|
||||
final double? size;
|
||||
|
||||
const ClassIconWidget({
|
||||
super.key,
|
||||
required String uid,
|
||||
required String className,
|
||||
required String category,
|
||||
this.color = Colors.white,
|
||||
this.size,
|
||||
}) : _className = className,
|
||||
_uid = uid,
|
||||
_category = category;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var iconCategory = getIconType(_uid, _className, _category);
|
||||
|
||||
return FirkaIconWidget(
|
||||
FirkaIconType.majesticons,
|
||||
getIconData(iconCategory),
|
||||
color: color,
|
||||
size: size,
|
||||
);
|
||||
}
|
||||
}
|
||||
export 'package:firka_common/ui/shared/class_icon.dart';
|
||||
|
||||
@@ -1,21 +1 @@
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CounterDigitWidget extends StatelessWidget {
|
||||
final String c;
|
||||
final TextStyle? style;
|
||||
|
||||
const CounterDigitWidget(this.c, this.style, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
shadowColor: Colors.transparent,
|
||||
color: appStyle.colors.buttonSecondaryFill,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4),
|
||||
child: Text(c, style: style),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
export 'package:firka_common/ui/shared/counter_digit.dart';
|
||||
|
||||
@@ -1,45 +1 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:firka/core/state/firka_state.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
|
||||
class DelayedSpinnerWidget extends StatefulWidget {
|
||||
const DelayedSpinnerWidget({super.key});
|
||||
|
||||
@override
|
||||
State<DelayedSpinnerWidget> createState() => _DelayedSpinner();
|
||||
}
|
||||
|
||||
class _DelayedSpinner extends FirkaState<DelayedSpinnerWidget> {
|
||||
Timer? timer;
|
||||
bool showSpinner = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
timer = Timer(Duration(milliseconds: 50), () {
|
||||
setState(() {
|
||||
showSpinner = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (showSpinner) {
|
||||
return CircularProgressIndicator(color: appStyle.colors.accent);
|
||||
} else {
|
||||
return SizedBox();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
|
||||
timer?.cancel();
|
||||
}
|
||||
}
|
||||
export 'package:firka_common/ui/shared/delayed_spinner.dart';
|
||||
|
||||
@@ -1,42 +1 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
|
||||
enum FirkaIconType { icons, majesticons, majesticonsLocal }
|
||||
|
||||
class FirkaIconWidget extends StatelessWidget {
|
||||
final FirkaIconType iconType;
|
||||
final Object iconData;
|
||||
final Color color;
|
||||
final double? size;
|
||||
|
||||
const FirkaIconWidget(
|
||||
this.iconType,
|
||||
this.iconData, {
|
||||
super.key,
|
||||
this.color = Colors.white,
|
||||
this.size,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
switch (iconType) {
|
||||
case FirkaIconType.icons:
|
||||
return SvgPicture.asset(
|
||||
'assets/icons/${iconData as String}.svg',
|
||||
color: color,
|
||||
height: size,
|
||||
);
|
||||
case FirkaIconType.majesticons:
|
||||
return Majesticon(iconData as Uint8List, color: color, size: size);
|
||||
case FirkaIconType.majesticonsLocal:
|
||||
return SvgPicture.asset(
|
||||
'assets/majesticons/${iconData as String}.svg',
|
||||
color: color,
|
||||
height: size,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
export 'package:firka_common/ui/shared/firka_icon.dart';
|
||||
|
||||
@@ -1,60 +1 @@
|
||||
import 'package:kreta_api/kreta_api.dart';
|
||||
import 'package:firka/ui/components/firka_card.dart';
|
||||
import 'package:firka/ui/components/grade_helpers.dart';
|
||||
import 'package:firka/ui/shared/class_icon.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
|
||||
class GradeSmallCard extends FirkaCard {
|
||||
final List<Grade> grades;
|
||||
final Subject subject;
|
||||
|
||||
GradeSmallCard(this.grades, this.subject, {super.key})
|
||||
: super(
|
||||
left: [
|
||||
ClassIconWidget(
|
||||
uid: subject.uid,
|
||||
className: subject.name,
|
||||
category: subject.category.name!,
|
||||
color: appStyle.colors.accent,
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child: Text(
|
||||
subject.name,
|
||||
style: appStyle.fonts.B_16SB.apply(
|
||||
color: appStyle.colors.textPrimary,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
right: [
|
||||
grades.getAverageBySubject(subject).isNaN
|
||||
? SizedBox()
|
||||
: Card(
|
||||
shadowColor: Colors.transparent,
|
||||
color: getGradeColor(
|
||||
grades.getAverageBySubject(subject),
|
||||
).withAlpha(38),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 8,
|
||||
right: 8,
|
||||
top: 4,
|
||||
bottom: 4,
|
||||
),
|
||||
child: Text(
|
||||
grades.getAverageBySubject(subject).toStringAsFixed(2),
|
||||
style: appStyle.fonts.B_16SB.apply(
|
||||
color: getGradeColor(
|
||||
grades.getAverageBySubject(subject),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
export 'package:firka_common/ui/shared/grade_small_card.dart';
|
||||
|
||||
@@ -1,309 +1 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// Design system token names; ignore non_constant_identifier_names for consistency with design specs
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
|
||||
class FirkaFonts {
|
||||
TextStyle H_H1;
|
||||
TextStyle H_18px;
|
||||
TextStyle H_H2;
|
||||
TextStyle H_16px;
|
||||
TextStyle H_14px;
|
||||
TextStyle H_12px;
|
||||
|
||||
TextStyle H_16px_trimmed;
|
||||
|
||||
TextStyle B_16R;
|
||||
TextStyle B_16SB;
|
||||
TextStyle B_15SB;
|
||||
|
||||
TextStyle B_14R;
|
||||
TextStyle B_14SB;
|
||||
|
||||
TextStyle B_12R;
|
||||
TextStyle B_12SB;
|
||||
|
||||
TextStyle P_14;
|
||||
TextStyle P_12;
|
||||
|
||||
FirkaFonts({
|
||||
required this.H_H1,
|
||||
required this.H_18px,
|
||||
required this.H_H2,
|
||||
required this.H_16px,
|
||||
required this.H_14px,
|
||||
required this.H_12px,
|
||||
required this.H_16px_trimmed,
|
||||
required this.B_16R,
|
||||
required this.B_16SB,
|
||||
required this.B_15SB,
|
||||
required this.B_14R,
|
||||
required this.B_14SB,
|
||||
required this.B_12R,
|
||||
required this.B_12SB,
|
||||
required this.P_14,
|
||||
required this.P_12,
|
||||
});
|
||||
}
|
||||
|
||||
class FirkaColors {
|
||||
Color background;
|
||||
Color backgroundAmoled;
|
||||
Color background0p;
|
||||
Color success;
|
||||
int shadowBlur;
|
||||
|
||||
Color textPrimary;
|
||||
Color textSecondary;
|
||||
Color textTertiary;
|
||||
|
||||
Color textPrimaryLight;
|
||||
Color textSecondaryLight;
|
||||
Color textTertiaryLight;
|
||||
|
||||
Color card;
|
||||
Color cardTranslucent;
|
||||
|
||||
Color buttonSecondaryFill;
|
||||
|
||||
Color accent;
|
||||
Color secondary;
|
||||
Color shadowColor;
|
||||
Color a10p; // 10%
|
||||
Color a15p; // 15%
|
||||
|
||||
Color warningAccent;
|
||||
Color warningText;
|
||||
Color warning15p;
|
||||
Color warningCard;
|
||||
|
||||
Color errorAccent;
|
||||
Color errorText;
|
||||
Color error15p;
|
||||
Color errorCard;
|
||||
|
||||
Color grade5;
|
||||
Color grade4;
|
||||
Color grade3;
|
||||
Color grade2;
|
||||
Color grade1;
|
||||
|
||||
FirkaColors({
|
||||
required this.background,
|
||||
required this.backgroundAmoled,
|
||||
required this.background0p,
|
||||
required this.success,
|
||||
required this.shadowBlur,
|
||||
required this.textPrimary,
|
||||
required this.textSecondary,
|
||||
required this.textTertiary,
|
||||
required this.textPrimaryLight,
|
||||
required this.textSecondaryLight,
|
||||
required this.textTertiaryLight,
|
||||
required this.card,
|
||||
required this.cardTranslucent,
|
||||
required this.buttonSecondaryFill,
|
||||
required this.accent,
|
||||
required this.secondary,
|
||||
required this.shadowColor,
|
||||
required this.a10p,
|
||||
required this.a15p,
|
||||
required this.warningAccent,
|
||||
required this.warningText,
|
||||
required this.warning15p,
|
||||
required this.warningCard,
|
||||
required this.errorAccent,
|
||||
required this.errorText,
|
||||
required this.error15p,
|
||||
required this.errorCard,
|
||||
required this.grade5,
|
||||
required this.grade4,
|
||||
required this.grade3,
|
||||
required this.grade2,
|
||||
required this.grade1,
|
||||
});
|
||||
}
|
||||
|
||||
class FirkaStyle {
|
||||
FirkaColors colors;
|
||||
FirkaFonts fonts;
|
||||
bool isLight;
|
||||
|
||||
FirkaStyle({
|
||||
required this.isLight,
|
||||
required this.colors,
|
||||
required this.fonts,
|
||||
});
|
||||
}
|
||||
|
||||
final _defaultFonts = FirkaFonts(
|
||||
H_H1: TextStyle(
|
||||
fontSize: 30,
|
||||
fontFamily: 'Montserrat',
|
||||
fontVariations: [FontVariation("wght", 700)],
|
||||
),
|
||||
H_18px: TextStyle(
|
||||
fontSize: 18,
|
||||
fontFamily: 'Montserrat',
|
||||
fontVariations: [FontVariation("wght", 700)],
|
||||
),
|
||||
H_H2: TextStyle(
|
||||
fontSize: 20,
|
||||
fontFamily: 'Montserrat',
|
||||
fontVariations: [FontVariation("wght", 700)],
|
||||
),
|
||||
H_16px: TextStyle(
|
||||
fontSize: 16,
|
||||
fontFamily: 'Montserrat',
|
||||
fontVariations: [FontVariation("wght", 600)],
|
||||
),
|
||||
H_14px: TextStyle(
|
||||
fontSize: 14,
|
||||
fontFamily: 'Montserrat',
|
||||
fontVariations: [FontVariation("wght", 600)],
|
||||
),
|
||||
H_12px: TextStyle(
|
||||
fontSize: 12,
|
||||
fontFamily: 'Montserrat',
|
||||
fontVariations: [FontVariation("wght", 600)],
|
||||
),
|
||||
H_16px_trimmed: TextStyle(
|
||||
fontSize: 16,
|
||||
fontFamily: 'Montserrat',
|
||||
fontVariations: [FontVariation("wght", 600)],
|
||||
height: 1.3,
|
||||
),
|
||||
B_16R: TextStyle(
|
||||
fontSize: 16,
|
||||
fontFamily: 'Figtree',
|
||||
fontVariations: [FontVariation("wght", 600)],
|
||||
height: 1.3,
|
||||
),
|
||||
B_16SB: TextStyle(
|
||||
fontSize: 16,
|
||||
fontFamily: 'Figtree',
|
||||
fontVariations: [FontVariation("wght", 700)],
|
||||
height: 1.3,
|
||||
),
|
||||
B_14R: TextStyle(
|
||||
fontSize: 14,
|
||||
fontFamily: 'Figtree',
|
||||
fontVariations: [FontVariation("wght", 600)],
|
||||
height: 1.3,
|
||||
),
|
||||
B_14SB: TextStyle(
|
||||
fontSize: 14,
|
||||
fontFamily: 'Figtree',
|
||||
fontVariations: [FontVariation("wght", 700)],
|
||||
height: 1.3,
|
||||
),
|
||||
B_15SB: TextStyle(
|
||||
fontSize: 15,
|
||||
fontFamily: 'Figtree',
|
||||
fontVariations: [FontVariation("wght", 700)],
|
||||
height: 1.3,
|
||||
),
|
||||
B_12R: TextStyle(
|
||||
fontSize: 12,
|
||||
fontFamily: 'Figtree',
|
||||
fontVariations: [FontVariation("wght", 600)],
|
||||
height: 1.3,
|
||||
),
|
||||
B_12SB: TextStyle(
|
||||
fontSize: 12,
|
||||
fontFamily: 'Figtree',
|
||||
fontVariations: [FontVariation("wght", 700)],
|
||||
height: 1.3,
|
||||
),
|
||||
P_14: TextStyle(
|
||||
fontSize: 14,
|
||||
fontFamily: 'RobotoMono',
|
||||
fontVariations: [FontVariation("wght", 700)],
|
||||
),
|
||||
P_12: TextStyle(
|
||||
fontSize: 12,
|
||||
fontFamily: 'RobotoMono',
|
||||
fontVariations: [FontVariation("wght", 700)],
|
||||
),
|
||||
);
|
||||
|
||||
final FirkaStyle lightStyle = FirkaStyle(
|
||||
isLight: true,
|
||||
colors: FirkaColors(
|
||||
background: Color(0xFFFAFFF0),
|
||||
backgroundAmoled: Colors.black,
|
||||
background0p: Color(0x00fafff0),
|
||||
success: Color(0xFF92EA3B),
|
||||
shadowBlur: 2,
|
||||
textPrimary: Color(0xFF394C0A),
|
||||
textSecondary: Color(0xCC394C0A),
|
||||
textTertiary: Color(0x80394C0A),
|
||||
textPrimaryLight: Color(0xFF394C0A),
|
||||
textSecondaryLight: Color(0xCC394C0A),
|
||||
textTertiaryLight: Color(0x80394C0A),
|
||||
card: Color(0xFFF3FBDE),
|
||||
cardTranslucent: Color(0x80F3FBDE),
|
||||
buttonSecondaryFill: Color(0xFFFEFFFD),
|
||||
accent: Color(0xFFA7DC22),
|
||||
secondary: Color(0xFF6E8F1B),
|
||||
shadowColor: Color(0x33647e22),
|
||||
a10p: Color(0x1aa7dc22),
|
||||
a15p: Color(0x26a7dc22),
|
||||
warningAccent: Color(0xFFFFA046),
|
||||
warningText: Color(0xFF8F531B),
|
||||
warning15p: Color(0x26FFA046),
|
||||
warningCard: Color(0xFFFAEBDC),
|
||||
errorAccent: Color(0xFFFF54A1),
|
||||
errorText: Color(0xFF8F1B4F),
|
||||
error15p: Color(0x26FF54A1),
|
||||
errorCard: Color(0xFFFADCE9),
|
||||
grade5: Color(0xFF22CCAD),
|
||||
grade4: Color(0xFF92EA3B),
|
||||
grade3: Color(0xFFF9CF00),
|
||||
grade2: Color(0xFFFFA046),
|
||||
grade1: Color(0xFFFF54A1),
|
||||
),
|
||||
fonts: _defaultFonts,
|
||||
);
|
||||
|
||||
final FirkaStyle darkStyle = FirkaStyle(
|
||||
isLight: false,
|
||||
colors: FirkaColors(
|
||||
background: Color(0xFF0D1202),
|
||||
backgroundAmoled: Colors.black,
|
||||
background0p: Color(0x00fafff0),
|
||||
success: Color(0xFF92EA3B),
|
||||
shadowBlur: 0,
|
||||
textPrimary: Color(0xFFEAF7CC),
|
||||
textSecondary: Color(0xB3EAF7CC),
|
||||
textTertiary: Color(0x80EAF7CC),
|
||||
textPrimaryLight: Color(0xFF394C0A),
|
||||
textSecondaryLight: Color(0xCC394C0A),
|
||||
textTertiaryLight: Color(0x80394C0A),
|
||||
card: Color(0xFF141905),
|
||||
cardTranslucent: Color(0x80141905),
|
||||
buttonSecondaryFill: Color(0xFF20290B),
|
||||
accent: Color(0xFFA7DC22),
|
||||
secondary: Color(0xFFCBEE71),
|
||||
shadowColor: Color(0x26CBEE71),
|
||||
a10p: Color(0x1AA7DC22),
|
||||
a15p: Color(0x26A7DC22),
|
||||
warningAccent: Color(0xFFFFA046),
|
||||
warningText: Color(0xFFF0B37A),
|
||||
warning15p: Color(0x26FFA046),
|
||||
warningCard: Color(0xFF201203),
|
||||
errorAccent: Color(0xFFFF54A1),
|
||||
errorText: Color(0xFFF59EC5),
|
||||
error15p: Color(0x26FF54A1),
|
||||
errorCard: Color(0xFF1E030F),
|
||||
grade5: Color(0xFF22CCAD),
|
||||
grade4: Color(0xFF92EA3B),
|
||||
grade3: Color(0xFFF9CF00),
|
||||
grade2: Color(0xFFFFA046),
|
||||
grade1: Color(0xFFFF54A1),
|
||||
),
|
||||
fonts: _defaultFonts,
|
||||
);
|
||||
|
||||
FirkaStyle appStyle = lightStyle;
|
||||
FirkaStyle wearStyle = darkStyle;
|
||||
export 'package:firka_common/ui/theme/style.dart';
|
||||
|
||||
@@ -2,7 +2,7 @@ name: firka
|
||||
description: "Firka, Alternatív e-Kréta kliens."
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.1.0+1040
|
||||
version: 1.1.1+2001
|
||||
|
||||
environment:
|
||||
sdk: ^3.11.0
|
||||
@@ -10,6 +10,8 @@ environment:
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
firka_common:
|
||||
path: ../firka_common
|
||||
kreta_api:
|
||||
path: ../kreta_api
|
||||
|
||||
@@ -36,7 +38,6 @@ dependencies:
|
||||
flutter_arc_text: ^0.6.0
|
||||
flutter_svg: ^1.1.6
|
||||
home_widget: ^0.8.0
|
||||
brotli: ^0.6.0
|
||||
crypto: ^3.0.6
|
||||
transparent_pointer: ^1.0.1
|
||||
flutter_staggered_grid_view: ^0.7.0
|
||||
@@ -45,6 +46,8 @@ dependencies:
|
||||
live_activities: ^2.4.1
|
||||
logging: ^1.3.0
|
||||
share_plus: ^12.0.0
|
||||
sensors_plus: ^6.1.1
|
||||
flutter_physics: ^0.2.0
|
||||
url_launcher: ^6.3.2
|
||||
shared_preferences: ^2.3.4
|
||||
flutter_dotenv: ^5.2.1
|
||||
@@ -55,11 +58,13 @@ dependencies:
|
||||
flutter_native_splash: ^2.4.7
|
||||
go_router: ^17.1.0
|
||||
flutter_bloc: ^9.0.0
|
||||
vibration: ^3.1.8
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^6.0.0
|
||||
yaml: ^3.1.2
|
||||
isar_community_generator: 3.3.0
|
||||
android_notification_icons: ^0.0.1
|
||||
integration_test:
|
||||
@@ -76,6 +81,7 @@ flutter:
|
||||
- .env
|
||||
- assets/images/logos/colored_logo.webp
|
||||
- assets/images/logos/dave.svg
|
||||
- assets/images/logos/loading.gif
|
||||
- assets/images/carousel/
|
||||
- assets/images/carousel_dark/
|
||||
- assets/images/icons/
|
||||
@@ -84,10 +90,10 @@ flutter:
|
||||
- assets/images/bubble.svg
|
||||
- assets/icons/
|
||||
- assets/majesticons/
|
||||
- assets/firka.i
|
||||
- assets/swears/DirtyWords.xml
|
||||
- assets/images/cactus_error_screen.png
|
||||
- assets/images/logos/dave_error.png
|
||||
- assets/icons/button/colorwheel.png
|
||||
|
||||
fonts:
|
||||
- family: Montserrat
|
||||
|
||||
@@ -1,42 +1,78 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:image/image.dart' as img;
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:yaml/yaml.dart' as yaml;
|
||||
|
||||
const _lockFileName = 'codegen-lock.yaml';
|
||||
|
||||
void main() async {
|
||||
final root = _projectRoot();
|
||||
var ran = false;
|
||||
|
||||
if (_iconsOutOfDate(root)) {
|
||||
stdout.writeln('Icons out of date, running flutter_launcher_icons...');
|
||||
await _run('dart', ['run', 'flutter_launcher_icons'], root);
|
||||
ran = true;
|
||||
final inputs = _iconsInputs(root);
|
||||
final manifestFile = File(p.join(root, 'android/app/src/main/AndroidManifest.xml'));
|
||||
String? manifestBackup;
|
||||
if (manifestFile.existsSync()) {
|
||||
manifestBackup = manifestFile.readAsStringSync();
|
||||
}
|
||||
late ProcessResult iconResult;
|
||||
try {
|
||||
stdout.writeln('Icons out of date, running flutter_launcher_icons...');
|
||||
iconResult = await Process.run(
|
||||
'dart',
|
||||
['run', 'flutter_launcher_icons'],
|
||||
workingDirectory: root,
|
||||
runInShell: true,
|
||||
);
|
||||
if (iconResult.exitCode == 0) {
|
||||
_updateLockWithHashes(root, 'icons', _computeHashes(root, inputs));
|
||||
ran = true;
|
||||
}
|
||||
} finally {
|
||||
if (manifestBackup != null) {
|
||||
manifestFile.writeAsStringSync(manifestBackup);
|
||||
}
|
||||
}
|
||||
if (iconResult.exitCode != 0) {
|
||||
stderr.write(iconResult.stderr);
|
||||
exit(iconResult.exitCode);
|
||||
}
|
||||
}
|
||||
|
||||
if (_l10nOutOfDate(root)) {
|
||||
final inputs = _l10nInputs(root);
|
||||
stdout.writeln('l10n out of date, running flutter gen-l10n...');
|
||||
await _run('flutter', [
|
||||
'gen-l10n',
|
||||
'--template-arb-file',
|
||||
'app_hu.arb',
|
||||
], root);
|
||||
_updateLockWithHashes(root, 'l10n', _computeHashes(root, inputs));
|
||||
ran = true;
|
||||
}
|
||||
|
||||
if (_isarOutOfDate(root)) {
|
||||
if (_isarOutOfDate(root) || _isarGeneratedFilesMissing(root)) {
|
||||
final inputs = _isarInputs(root);
|
||||
final hashes = _computeHashes(root, inputs);
|
||||
stdout.writeln(
|
||||
'Isar generated dart files out of date, running build_runner...',
|
||||
'Isar generated dart files out of date or missing, running build_runner...',
|
||||
);
|
||||
await _run('dart', ['run', 'build_runner', 'build'], root);
|
||||
_updateLockWithHashes(root, 'isar', hashes);
|
||||
ran = true;
|
||||
}
|
||||
|
||||
if (_splashOutOfDate(root)) {
|
||||
final inputs = _splashInputs(root);
|
||||
await _generateAndroid12SplashImage(root);
|
||||
stdout.writeln(
|
||||
'Splash out of date, running flutter_native_splash:create...',
|
||||
);
|
||||
await _run('dart', ['run', 'flutter_native_splash:create'], root);
|
||||
_updateLockWithHashes(root, 'splash', _computeHashes(root, inputs));
|
||||
ran = true;
|
||||
}
|
||||
|
||||
@@ -47,7 +83,92 @@ void main() async {
|
||||
|
||||
String _projectRoot() {
|
||||
final script = p.canonicalize(Platform.script.toFilePath());
|
||||
return p.dirname(p.dirname(script));
|
||||
return p.canonicalize(p.dirname(p.dirname(script)));
|
||||
}
|
||||
|
||||
String _lockPath(String root) => p.join(root, _lockFileName);
|
||||
|
||||
Map<String, Map<String, String>>? _readLock(String root) {
|
||||
final file = File(_lockPath(root));
|
||||
if (!file.existsSync()) return null;
|
||||
try {
|
||||
final content = file.readAsStringSync();
|
||||
final decoded = yaml.loadYaml(content);
|
||||
if (decoded is! Map) return null;
|
||||
final result = <String, Map<String, String>>{};
|
||||
for (final entry in decoded.entries) {
|
||||
if (entry.value is! Map) continue;
|
||||
final inner = entry.value as Map;
|
||||
result[entry.key.toString()] = inner.map(
|
||||
(k, v) => MapEntry(
|
||||
Platform.isWindows ? k.toString().toLowerCase() : k.toString(),
|
||||
v?.toString() ?? '',
|
||||
),
|
||||
);
|
||||
}
|
||||
return result;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void _writeLock(String root, Map<String, Map<String, String>> lock) {
|
||||
final buf = StringBuffer();
|
||||
for (final stepEntry in lock.entries) {
|
||||
buf.writeln('${stepEntry.key}:');
|
||||
for (final fileEntry in stepEntry.value.entries) {
|
||||
buf.writeln(
|
||||
' "${_escapeYaml(fileEntry.key)}": "${_escapeYaml(fileEntry.value)}"',
|
||||
);
|
||||
}
|
||||
}
|
||||
File(_lockPath(root)).writeAsStringSync(buf.toString());
|
||||
}
|
||||
|
||||
String _escapeYaml(String s) =>
|
||||
s.replaceAll('\\', '\\\\').replaceAll('"', '\\"');
|
||||
|
||||
String _fileHash(File file) {
|
||||
final bytes = file.readAsBytesSync();
|
||||
final digest = sha256.convert(bytes);
|
||||
return digest.toString();
|
||||
}
|
||||
|
||||
String _relativePath(String root, File file) {
|
||||
final rel = p.relative(file.path, from: root);
|
||||
final normalized = rel.replaceAll('\\', '/');
|
||||
return Platform.isWindows ? normalized.toLowerCase() : normalized;
|
||||
}
|
||||
|
||||
bool _hashesMatch(
|
||||
String root,
|
||||
String stepName,
|
||||
List<File> inputs,
|
||||
Map<String, Map<String, String>>? lock,
|
||||
) {
|
||||
if (lock == null || !lock.containsKey(stepName)) return false;
|
||||
final stepHashes = lock[stepName]!;
|
||||
for (final f in inputs) {
|
||||
final rel = _relativePath(root, f);
|
||||
final stored = stepHashes[rel];
|
||||
if (stored == null || stored != _fileHash(f)) return false;
|
||||
}
|
||||
if (stepHashes.length != inputs.length) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Map<String, String> _computeHashes(String root, List<File> inputs) {
|
||||
return {for (final f in inputs) _relativePath(root, f): _fileHash(f)};
|
||||
}
|
||||
|
||||
void _updateLockWithHashes(
|
||||
String root,
|
||||
String stepName,
|
||||
Map<String, String> hashes,
|
||||
) {
|
||||
final lock = _readLock(root) ?? <String, Map<String, String>>{};
|
||||
lock[stepName] = Map.from(hashes);
|
||||
_writeLock(root, lock);
|
||||
}
|
||||
|
||||
DateTime? _modified(File file) {
|
||||
@@ -65,7 +186,7 @@ bool _anyNewerThan(Iterable<File> inputs, File output) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _iconsOutOfDate(String root) {
|
||||
List<File> _iconsInputs(String root) {
|
||||
final config = File(p.join(root, 'flutter_launcher_icons.yaml'));
|
||||
final pubspec = File(p.join(root, 'pubspec.yaml'));
|
||||
final imagePath = File(p.join(root, 'assets/images/logos/colored_logo.webp'));
|
||||
@@ -78,25 +199,25 @@ bool _iconsOutOfDate(String root) {
|
||||
final foreground = File(
|
||||
p.join(root, 'assets/images/logos/colored_logo_only_mustache.png'),
|
||||
);
|
||||
return [config, pubspec, imagePath, monochrome, background, foreground]
|
||||
.where((f) => f.existsSync())
|
||||
.map((f) => File(p.canonicalize(f.path)))
|
||||
.toList();
|
||||
}
|
||||
|
||||
final inputs = [
|
||||
config,
|
||||
pubspec,
|
||||
imagePath,
|
||||
monochrome,
|
||||
background,
|
||||
foreground,
|
||||
].where((f) => f.existsSync()).map((f) => File(p.canonicalize(f.path)));
|
||||
bool _iconsOutOfDate(String root) {
|
||||
final inputs = _iconsInputs(root);
|
||||
final output = File(
|
||||
p.join(
|
||||
root,
|
||||
'android/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml',
|
||||
),
|
||||
);
|
||||
return _anyNewerThan(inputs, output);
|
||||
if (!_anyNewerThan(inputs, output)) return false;
|
||||
return !_hashesMatch(root, 'icons', inputs, _readLock(root));
|
||||
}
|
||||
|
||||
bool _l10nOutOfDate(String root) {
|
||||
List<File> _l10nInputs(String root) {
|
||||
final l10nDir = p.join(root, 'lib/l10n');
|
||||
final l10nYml = File(p.join(root, 'l10n.yml'));
|
||||
final arbs = Directory(l10nDir)
|
||||
@@ -105,41 +226,72 @@ bool _l10nOutOfDate(String root) {
|
||||
.where((f) => f.path.endsWith('.arb'))
|
||||
.map((f) => File(p.canonicalize(f.path)))
|
||||
.toList();
|
||||
final inputs = [l10nYml, ...arbs].where((f) => f.existsSync()).cast<File>();
|
||||
final output = File(p.join(root, 'lib/l10n/app_localizations.dart'));
|
||||
return _anyNewerThan(inputs, output);
|
||||
return [l10nYml, ...arbs].where((f) => f.existsSync()).cast<File>().toList();
|
||||
}
|
||||
|
||||
bool _isarOutOfDate(String root) {
|
||||
final modelsDir = p.join(root, 'lib/data/models');
|
||||
if (!Directory(modelsDir).existsSync()) return false;
|
||||
bool _l10nOutOfDate(String root) {
|
||||
final inputs = _l10nInputs(root);
|
||||
final output = File(p.join(root, 'lib/l10n/app_localizations.dart'));
|
||||
if (!_anyNewerThan(inputs, output)) return false;
|
||||
return !_hashesMatch(root, 'l10n', inputs, _readLock(root));
|
||||
}
|
||||
|
||||
List<File> _isarInputs(String root) {
|
||||
final modelsDir = p.join(root, 'lib/data/models');
|
||||
if (!Directory(modelsDir).existsSync()) return [];
|
||||
final list = <File>[];
|
||||
for (final entity in Directory(modelsDir).listSync()) {
|
||||
if (entity is! File || !entity.path.endsWith('.dart')) continue;
|
||||
final content = entity.readAsStringSync();
|
||||
if (!content.contains("part '") || !content.contains('.g.dart')) continue;
|
||||
list.add(File(p.canonicalize(entity.path)));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
final baseName = p.basenameWithoutExtension(entity.path);
|
||||
final gPath = p.join(modelsDir, '$baseName.g.dart');
|
||||
final dartFile = File(p.canonicalize(entity.path));
|
||||
final gFile = File(gPath);
|
||||
if (_anyNewerThan([dartFile], gFile)) return true;
|
||||
bool _isarOutOfDate(String root) {
|
||||
final inputs = _isarInputs(root);
|
||||
if (inputs.isEmpty) return false;
|
||||
final modelsDir = p.join(root, 'lib/data/models');
|
||||
for (final dartFile in inputs) {
|
||||
final baseName = p.basenameWithoutExtension(dartFile.path);
|
||||
final gFile = File(p.join(modelsDir, '$baseName.g.dart'));
|
||||
if (_anyNewerThan([dartFile], gFile)) {
|
||||
return !_hashesMatch(root, 'isar', inputs, _readLock(root));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _splashOutOfDate(String root) {
|
||||
bool _isarGeneratedFilesMissing(String root) {
|
||||
final inputs = _isarInputs(root);
|
||||
if (inputs.isEmpty) return false;
|
||||
final modelsDir = p.join(root, 'lib/data/models');
|
||||
for (final dartFile in inputs) {
|
||||
final baseName = p.basenameWithoutExtension(dartFile.path);
|
||||
final gFile = File(p.join(modelsDir, '$baseName.g.dart'));
|
||||
if (!gFile.existsSync()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
List<File> _splashInputs(String root) {
|
||||
final config = File(p.join(root, 'flutter_native_splash.yaml'));
|
||||
final splashImage = File(p.join(root, 'assets/images/logos/splash.png'));
|
||||
final inputs = [config, splashImage]
|
||||
return [config, splashImage]
|
||||
.where((f) => f.existsSync())
|
||||
.map((f) => File(p.canonicalize(f.path)))
|
||||
.toList();
|
||||
}
|
||||
|
||||
bool _splashOutOfDate(String root) {
|
||||
final inputs = _splashInputs(root);
|
||||
if (inputs.isEmpty) return false;
|
||||
final output = File(
|
||||
p.join(root, 'android/app/src/main/res/drawable/launch_background.xml'),
|
||||
);
|
||||
return _anyNewerThan(inputs, output);
|
||||
if (!_anyNewerThan(inputs, output)) return false;
|
||||
return !_hashesMatch(root, 'splash', inputs, _readLock(root));
|
||||
}
|
||||
|
||||
Future<void> _generateAndroid12SplashImage(String root) async {
|
||||
|
||||
1
firka/vendor/fmb_dart
vendored
17
firka_common/lib/core/debug_helper.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
DateTime? debugFakeTime;
|
||||
DateTime? debugSetAt;
|
||||
var debugTimeAdvance = false;
|
||||
|
||||
DateTime timeNow() {
|
||||
if (debugFakeTime != null) {
|
||||
if (debugTimeAdvance && debugSetAt != null) {
|
||||
var diff = DateTime.now().difference(debugSetAt!);
|
||||
|
||||
return debugFakeTime!.add(diff);
|
||||
} else {
|
||||
return debugFakeTime!;
|
||||
}
|
||||
} else {
|
||||
return DateTime.now();
|
||||
}
|
||||
}
|
||||
19
firka_common/lib/core/extensions.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
extension IterableExtensionMap on Iterable<MapEntry<String, dynamic>> {
|
||||
Map<String, dynamic> toMap() {
|
||||
var map = <String, dynamic>{};
|
||||
for (var item in this) {
|
||||
map[item.key] = item.value;
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
extension IterableExtension<T> on Iterable<T> {
|
||||
T? firstWhereOrNull(bool Function(T element) test) {
|
||||
for (var element in this) {
|
||||
if (test(element)) return element;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
135
firka_common/lib/core/icon_helper.dart
Normal file
@@ -0,0 +1,135 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
|
||||
enum ClassIcon {
|
||||
mathematics,
|
||||
grammar,
|
||||
literature,
|
||||
history,
|
||||
geography,
|
||||
art,
|
||||
physics,
|
||||
music,
|
||||
pe,
|
||||
chemistry,
|
||||
biology,
|
||||
env,
|
||||
religion,
|
||||
economics,
|
||||
it,
|
||||
code,
|
||||
networking,
|
||||
theatre,
|
||||
film,
|
||||
electricalEngineering,
|
||||
mechanicalEngineering,
|
||||
technika,
|
||||
dance,
|
||||
philosophy,
|
||||
ofo,
|
||||
diligence,
|
||||
attitude,
|
||||
language,
|
||||
linux,
|
||||
database,
|
||||
applications,
|
||||
project,
|
||||
}
|
||||
|
||||
Map<ClassIcon, RegExp> _descriptors = {
|
||||
ClassIcon.mathematics: RegExp(r'mate(k|matika)'),
|
||||
ClassIcon.grammar: RegExp(r'magyar nyelv|nyelvtan'),
|
||||
ClassIcon.literature: RegExp(r'irodalom'),
|
||||
ClassIcon.history: RegExp(r'tor(i|tenelem)'),
|
||||
ClassIcon.geography: RegExp(r'foldrajz'),
|
||||
ClassIcon.art: RegExp(r'rajz|muvtori|muveszet|vizualis'),
|
||||
ClassIcon.physics: RegExp(r'fizika'),
|
||||
ClassIcon.music: RegExp(r'^enek|zene|szolfezs|zongora|korus'),
|
||||
ClassIcon.pe: RegExp(r'^tes(i|tneveles)|sport|edzeselmelet'),
|
||||
ClassIcon.chemistry: RegExp(r'kemia'),
|
||||
ClassIcon.biology: RegExp(r'biologia'),
|
||||
ClassIcon.env: RegExp(
|
||||
r'kornyezet|termeszet ?(tudomany|ismeret)|hon( es nep)?ismeret',
|
||||
),
|
||||
ClassIcon.religion: RegExp(r'(hit|erkolcs)tan|vallas|etika|bibliaismeret'),
|
||||
ClassIcon.economics: RegExp(r'penzugy|gazdasag'),
|
||||
ClassIcon.it: RegExp(r'informatika|szoftver|iroda|digitalis'),
|
||||
ClassIcon.code: RegExp(r'prog|alkalmazas'),
|
||||
ClassIcon.networking: RegExp(r'halozat'),
|
||||
ClassIcon.theatre: RegExp(r'szinhaz'),
|
||||
ClassIcon.film: RegExp(r'film|media'),
|
||||
ClassIcon.electricalEngineering: RegExp(r'elektro(tech)?nika'),
|
||||
ClassIcon.mechanicalEngineering: RegExp(r'gepesz|mernok|ipar'),
|
||||
ClassIcon.technika: RegExp(r'technika'),
|
||||
ClassIcon.dance: RegExp(r'tanc'),
|
||||
ClassIcon.philosophy: RegExp(r'filozofia'),
|
||||
ClassIcon.ofo: RegExp(r'osztaly(fonoki|kozosseg)|kozossegi|neveles'),
|
||||
ClassIcon.diligence: RegExp(r'szorgalom'),
|
||||
ClassIcon.attitude: RegExp(r'magatartas'),
|
||||
ClassIcon.language: RegExp(
|
||||
r'angol|nemet|francia|olasz|orosz|spanyol|latin|kinai|nyelv',
|
||||
),
|
||||
ClassIcon.linux: RegExp(r'linux'),
|
||||
ClassIcon.database: RegExp(r'adatbazis.*'),
|
||||
ClassIcon.applications: RegExp(r'asztali alkalmazasok'),
|
||||
ClassIcon.project: RegExp(r'projekt'),
|
||||
};
|
||||
|
||||
Map<ClassIcon, Uint8List> _iconMap = {
|
||||
ClassIcon.mathematics: Majesticon.calculatorSolid,
|
||||
ClassIcon.grammar: Majesticon.bookSolid,
|
||||
ClassIcon.literature: Majesticon.bookOpenSolid,
|
||||
ClassIcon.history: Majesticon.compass2Solid,
|
||||
ClassIcon.geography: Majesticon.globeEarth2Solid,
|
||||
ClassIcon.art: Majesticon.editPen2Solid,
|
||||
ClassIcon.music: Majesticon.musicNoteSolid,
|
||||
ClassIcon.chemistry: Majesticon.testTubeFilledSolid,
|
||||
ClassIcon.biology: Majesticon.covidSolid,
|
||||
ClassIcon.it: Majesticon.laptopSolid,
|
||||
ClassIcon.code: Majesticon.curlyBracesSolid,
|
||||
ClassIcon.networking: Majesticon.cloudSolid,
|
||||
ClassIcon.technika: Majesticon.ruler2Solid,
|
||||
ClassIcon.language: Majesticon.tooltipsSolid,
|
||||
ClassIcon.database: Majesticon.dataSolid,
|
||||
};
|
||||
|
||||
ClassIcon? getIconType(String uid, String className, String category) {
|
||||
ClassIcon? icon;
|
||||
if (category.toLowerCase() == "matematika") {
|
||||
icon = ClassIcon.mathematics;
|
||||
}
|
||||
|
||||
if (icon == null) {
|
||||
for (var desc in _descriptors.entries) {
|
||||
if (desc.value.hasMatch(
|
||||
className
|
||||
.replaceAll("ö", "o")
|
||||
.replaceAll("ü", "u")
|
||||
.replaceAll("ó", "o")
|
||||
.replaceAll("ő", "o")
|
||||
.replaceAll("ú", "u")
|
||||
.replaceAll("é", "e")
|
||||
.replaceAll("á", "a")
|
||||
.replaceAll("ű", "u")
|
||||
.replaceAll("í", "i")
|
||||
.toLowerCase(),
|
||||
)) {
|
||||
icon = desc.key;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
Uint8List getIconData(ClassIcon? icon) {
|
||||
if (icon == null) return Majesticon.alertCircleSolid;
|
||||
|
||||
var iconData = _iconMap[icon];
|
||||
iconData ??= Majesticon.alertCircleSolid;
|
||||
|
||||
return iconData;
|
||||
}
|
||||
9
firka_common/lib/core/json_helper.dart
Normal file
@@ -0,0 +1,9 @@
|
||||
List<T> listToTyped<T>(List<dynamic> dynamicList) {
|
||||
var newList = List<T>.empty(growable: true);
|
||||
|
||||
for (var item in dynamicList) {
|
||||
newList.add(item as T);
|
||||
}
|
||||
|
||||
return newList;
|
||||
}
|
||||
16
firka_common/lib/firka_common.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
library firka_common;
|
||||
|
||||
export 'core/debug_helper.dart';
|
||||
export 'core/extensions.dart';
|
||||
export 'core/icon_helper.dart';
|
||||
export 'core/json_helper.dart';
|
||||
export 'ui/components/firka_card.dart';
|
||||
export 'ui/components/firka_shadow.dart';
|
||||
export 'ui/components/grade.dart';
|
||||
export 'ui/components/grade_helpers.dart';
|
||||
export 'ui/shared/class_icon.dart';
|
||||
export 'ui/shared/counter_digit.dart';
|
||||
export 'ui/shared/delayed_spinner.dart';
|
||||
export 'ui/shared/firka_icon.dart';
|
||||
export 'ui/shared/grade_small_card.dart';
|
||||
export 'ui/theme/style.dart';
|
||||
137
firka_common/lib/ui/components/firka_card.dart
Normal file
@@ -0,0 +1,137 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:firka_common/ui/components/firka_shadow.dart';
|
||||
import 'package:firka_common/ui/theme/style.dart';
|
||||
|
||||
enum Attach { none, bottom, top }
|
||||
|
||||
class FirkaCard extends StatelessWidget {
|
||||
final List<Widget> left;
|
||||
final List<Widget>? center;
|
||||
final double? height;
|
||||
final List<Widget>? right;
|
||||
final bool shadow;
|
||||
final Widget? extra;
|
||||
final Attach? attached;
|
||||
final Color? color;
|
||||
final bool? isLightMode;
|
||||
|
||||
const FirkaCard({
|
||||
required this.left,
|
||||
this.shadow = true,
|
||||
this.center,
|
||||
this.right,
|
||||
this.extra,
|
||||
this.attached,
|
||||
this.color,
|
||||
this.height,
|
||||
this.isLightMode,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var right = this.right ?? [];
|
||||
|
||||
var attached = this.attached != null ? this.attached! : Attach.none;
|
||||
final defaultRounding = 16.0;
|
||||
final attachedRounding = 8.0;
|
||||
final isLight =
|
||||
isLightMode ?? Theme.of(context).brightness == Brightness.light;
|
||||
|
||||
if (extra != null) {
|
||||
return SizedBox(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: height,
|
||||
child: FirkaShadow(
|
||||
shadow: shadow,
|
||||
isLightMode: isLight,
|
||||
child: Card(
|
||||
color: color ?? appStyle.colors.card,
|
||||
shadowColor: isLight && shadow ? null : Colors.transparent,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(
|
||||
attached == Attach.top ? attachedRounding : defaultRounding,
|
||||
),
|
||||
topRight: Radius.circular(
|
||||
attached == Attach.top ? attachedRounding : defaultRounding,
|
||||
),
|
||||
bottomLeft: Radius.circular(
|
||||
attached == Attach.bottom
|
||||
? attachedRounding
|
||||
: defaultRounding,
|
||||
),
|
||||
bottomRight: Radius.circular(
|
||||
attached == Attach.bottom
|
||||
? attachedRounding
|
||||
: defaultRounding,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(children: left),
|
||||
Row(children: center ?? []),
|
||||
Row(children: right),
|
||||
],
|
||||
),
|
||||
extra ?? const SizedBox(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return SizedBox(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: height,
|
||||
child: FirkaShadow(
|
||||
shadow: shadow,
|
||||
isLightMode: isLight,
|
||||
child: Card(
|
||||
color: color ?? appStyle.colors.card,
|
||||
shadowColor: isLight && shadow ? null : Colors.transparent,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(
|
||||
attached == Attach.top ? attachedRounding : defaultRounding,
|
||||
),
|
||||
topRight: Radius.circular(
|
||||
attached == Attach.top ? attachedRounding : defaultRounding,
|
||||
),
|
||||
bottomLeft: Radius.circular(
|
||||
attached == Attach.bottom
|
||||
? attachedRounding
|
||||
: defaultRounding,
|
||||
),
|
||||
bottomRight: Radius.circular(
|
||||
attached == Attach.bottom
|
||||
? attachedRounding
|
||||
: defaultRounding,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(children: left),
|
||||
Row(children: center ?? []),
|
||||
Row(children: right),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
52
firka_common/lib/ui/components/firka_shadow.dart
Normal file
@@ -0,0 +1,52 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:firka_common/ui/theme/style.dart';
|
||||
|
||||
class FirkaShadow extends StatelessWidget {
|
||||
final Widget child;
|
||||
final bool shadow;
|
||||
final double radius;
|
||||
final bool? isLightMode;
|
||||
|
||||
const FirkaShadow({
|
||||
required this.shadow,
|
||||
required this.child,
|
||||
this.radius = 16.0,
|
||||
this.isLightMode,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isLight =
|
||||
isLightMode ?? Theme.of(context).brightness == Brightness.light;
|
||||
final borderRadius = BorderRadius.circular(radius);
|
||||
|
||||
final shadowBox = BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
shape: BoxShape.rectangle,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: appStyle.colors.shadowColor,
|
||||
spreadRadius: -4,
|
||||
blurRadius: 0,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
borderRadius: BorderRadius.all(Radius.circular(radius)),
|
||||
);
|
||||
|
||||
if (!shadow) {
|
||||
return ClipRRect(borderRadius: borderRadius, child: child);
|
||||
}
|
||||
|
||||
if (isLight) {
|
||||
return child;
|
||||
} else {
|
||||
return Container(
|
||||
decoration: shadowBox,
|
||||
child: ClipRRect(borderRadius: borderRadius, child: child),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
97
firka_common/lib/ui/components/grade.dart
Normal file
@@ -0,0 +1,97 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:kreta_api/kreta_api.dart';
|
||||
|
||||
import 'package:firka_common/ui/components/grade_helpers.dart';
|
||||
import 'package:firka_common/ui/theme/style.dart';
|
||||
|
||||
class GradeWidget extends StatelessWidget {
|
||||
const GradeWidget(this.grade, {super.key})
|
||||
: gradeValue = null,
|
||||
_fromValue = false;
|
||||
|
||||
const GradeWidget.gradeValue(int value, {super.key})
|
||||
: grade = null,
|
||||
gradeValue = value,
|
||||
_fromValue = true;
|
||||
|
||||
final Grade? grade;
|
||||
final int? gradeValue;
|
||||
final bool _fromValue;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_fromValue && gradeValue != null) {
|
||||
return _buildNumericCircle(
|
||||
gradeValue!,
|
||||
getGradeColor(gradeValue!.toDouble()),
|
||||
);
|
||||
}
|
||||
|
||||
final g = grade!;
|
||||
Color gradeColor = appStyle.colors.grade1;
|
||||
final gradeStr = g.numericValue?.toString() ?? '0';
|
||||
|
||||
if (g.valueType.name == 'Szazalekos') {
|
||||
if (g.numericValue != null) {
|
||||
gradeColor = getGradeColor(
|
||||
percentageToGrade(g.numericValue!).toDouble(),
|
||||
);
|
||||
}
|
||||
|
||||
final str = g.strValue.replaceAll('%', '');
|
||||
return Card(
|
||||
shape: const CircleBorder(),
|
||||
shadowColor: Colors.transparent,
|
||||
color: gradeColor.withAlpha(38),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(str, style: appStyle.fonts.P_14.copyWith(color: gradeColor)),
|
||||
Text('%', style: appStyle.fonts.P_12.copyWith(color: gradeColor)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (g.numericValue != null) {
|
||||
gradeColor = getGradeColor(g.numericValue!.toDouble());
|
||||
}
|
||||
|
||||
if (gradeStr == '0') {
|
||||
return Card(
|
||||
shadowColor: Colors.transparent,
|
||||
color: gradeColor.withAlpha(38),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 8, right: 8, top: 2, bottom: 2),
|
||||
child: Text(
|
||||
g.strValue,
|
||||
style: appStyle.fonts.H_H1.copyWith(
|
||||
fontSize: 16,
|
||||
color: gradeColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return _buildNumericCircle(g.numericValue!, gradeColor);
|
||||
}
|
||||
|
||||
Widget _buildNumericCircle(int value, Color gradeColor) {
|
||||
return Card(
|
||||
shape: const CircleBorder(),
|
||||
shadowColor: Colors.transparent,
|
||||
color: gradeColor.withAlpha(38),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 8, right: 8),
|
||||
child: Text(
|
||||
value.toString(),
|
||||
style: appStyle.fonts.H_H1.copyWith(fontSize: 24, color: gradeColor),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
112
firka_common/lib/ui/components/grade_helpers.dart
Normal file
@@ -0,0 +1,112 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:firka_common/ui/theme/style.dart';
|
||||
import 'package:kreta_api/kreta_api.dart';
|
||||
|
||||
int roundGrade(
|
||||
double grade, {
|
||||
double t1 = 1,
|
||||
double t2 = 0.5,
|
||||
double t3 = 0.5,
|
||||
double t4 = 0.5,
|
||||
}) {
|
||||
if (grade < 1 + t1) {
|
||||
return 1;
|
||||
}
|
||||
if (grade < 2 + t2) {
|
||||
return 2;
|
||||
}
|
||||
if (grade < 3 + t3) {
|
||||
return 3;
|
||||
}
|
||||
if (grade < 4 + t4) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
return 5;
|
||||
}
|
||||
|
||||
int percentageToGrade(int grade) {
|
||||
if (grade < 50) {
|
||||
return 1;
|
||||
}
|
||||
if (grade < 60) {
|
||||
return 2;
|
||||
}
|
||||
if (grade < 70) {
|
||||
return 3;
|
||||
}
|
||||
if (grade < 80) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
return 5;
|
||||
}
|
||||
|
||||
Color getGradeColor(
|
||||
double grade, {
|
||||
double t1 = 1,
|
||||
double t2 = 0.5,
|
||||
double t3 = 0.5,
|
||||
double t4 = 0.5,
|
||||
}) {
|
||||
switch (roundGrade(grade, t1: t1, t2: t2, t3: t3, t4: t4)) {
|
||||
case 2:
|
||||
return appStyle.colors.grade2;
|
||||
case 3:
|
||||
return appStyle.colors.grade3;
|
||||
case 4:
|
||||
return appStyle.colors.grade4;
|
||||
case 5:
|
||||
return appStyle.colors.grade5;
|
||||
default:
|
||||
return appStyle.colors.grade1;
|
||||
}
|
||||
}
|
||||
|
||||
(int total, List<int> countsByGrade) getGradeDistribution(List<Grade> grades) {
|
||||
final filtered = grades
|
||||
.where((g) => g.type.name != "felevi_jegy_ertekeles")
|
||||
.toList();
|
||||
final counts = [0, 0, 0, 0, 0];
|
||||
for (final g in filtered) {
|
||||
if (g.numericValue == null) continue;
|
||||
final value = g.valueType.name == "Szazalekos"
|
||||
? percentageToGrade(g.numericValue!.round())
|
||||
: g.numericValue!.round().clamp(1, 5);
|
||||
counts[value - 1]++;
|
||||
}
|
||||
return (filtered.length, counts);
|
||||
}
|
||||
|
||||
extension GradeListExtension on List<Grade> {
|
||||
double getAverageBySubject(Subject subject) {
|
||||
var weightTotal = 0.00;
|
||||
var sum = 0.00;
|
||||
|
||||
for (var grade in this) {
|
||||
if (grade.subject.uid == subject.uid) {
|
||||
final name = grade.valueType.name?.toLowerCase() ?? '';
|
||||
final isPercentage =
|
||||
name.contains('szazalek') || name.contains('percent');
|
||||
|
||||
if (isPercentage) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (grade.numericValue != null) {
|
||||
var weight = (grade.weightPercentage ?? 100) / 100.0;
|
||||
weightTotal += weight;
|
||||
|
||||
sum += grade.numericValue! * weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (weightTotal == 0) {
|
||||
return double.nan;
|
||||
}
|
||||
|
||||
return sum / weightTotal;
|
||||
}
|
||||
}
|
||||
35
firka_common/lib/ui/shared/class_icon.dart
Normal file
@@ -0,0 +1,35 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:firka_common/core/icon_helper.dart';
|
||||
import 'package:firka_common/ui/shared/firka_icon.dart';
|
||||
|
||||
class ClassIconWidget extends StatelessWidget {
|
||||
final String _uid;
|
||||
final String _className;
|
||||
final String _category;
|
||||
final Color color;
|
||||
final double? size;
|
||||
|
||||
const ClassIconWidget({
|
||||
super.key,
|
||||
required String uid,
|
||||
required String className,
|
||||
required String category,
|
||||
this.color = Colors.white,
|
||||
this.size,
|
||||
}) : _className = className,
|
||||
_uid = uid,
|
||||
_category = category;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var iconCategory = getIconType(_uid, _className, _category);
|
||||
|
||||
return FirkaIconWidget(
|
||||
FirkaIconType.majesticons,
|
||||
getIconData(iconCategory),
|
||||
color: color,
|
||||
size: size,
|
||||
);
|
||||
}
|
||||
}
|
||||
22
firka_common/lib/ui/shared/counter_digit.dart
Normal file
@@ -0,0 +1,22 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:firka_common/ui/theme/style.dart';
|
||||
|
||||
class CounterDigitWidget extends StatelessWidget {
|
||||
final String c;
|
||||
final TextStyle? style;
|
||||
|
||||
const CounterDigitWidget(this.c, this.style, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
shadowColor: Colors.transparent,
|
||||
color: appStyle.colors.buttonSecondaryFill,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4),
|
||||
child: Text(c, style: style),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
48
firka_common/lib/ui/shared/delayed_spinner.dart
Normal file
@@ -0,0 +1,48 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:firka_common/ui/theme/style.dart';
|
||||
|
||||
class DelayedSpinnerWidget extends StatefulWidget {
|
||||
final Color? color;
|
||||
|
||||
const DelayedSpinnerWidget({super.key, this.color});
|
||||
|
||||
@override
|
||||
State<DelayedSpinnerWidget> createState() => _DelayedSpinner();
|
||||
}
|
||||
|
||||
class _DelayedSpinner extends State<DelayedSpinnerWidget> {
|
||||
Timer? timer;
|
||||
bool showSpinner = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
timer = Timer(const Duration(milliseconds: 50), () {
|
||||
setState(() {
|
||||
showSpinner = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (showSpinner) {
|
||||
return CircularProgressIndicator(
|
||||
color: widget.color ?? appStyle.colors.accent,
|
||||
);
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
|
||||
timer?.cancel();
|
||||
}
|
||||
}
|
||||
46
firka_common/lib/ui/shared/firka_icon.dart
Normal file
@@ -0,0 +1,46 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:majesticons_flutter/majesticons_flutter.dart';
|
||||
|
||||
enum FirkaIconType { icons, majesticons, majesticonsLocal }
|
||||
|
||||
class FirkaIconWidget extends StatelessWidget {
|
||||
final FirkaIconType iconType;
|
||||
final Object iconData;
|
||||
final Color color;
|
||||
final double? size;
|
||||
final String? package;
|
||||
|
||||
const FirkaIconWidget(
|
||||
this.iconType,
|
||||
this.iconData, {
|
||||
super.key,
|
||||
this.color = Colors.white,
|
||||
this.size,
|
||||
this.package,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
switch (iconType) {
|
||||
case FirkaIconType.icons:
|
||||
return SvgPicture.asset(
|
||||
'assets/icons/${iconData as String}.svg',
|
||||
color: color,
|
||||
height: size,
|
||||
package: package,
|
||||
);
|
||||
case FirkaIconType.majesticons:
|
||||
return Majesticon(iconData as Uint8List, color: color, size: size);
|
||||
case FirkaIconType.majesticonsLocal:
|
||||
return SvgPicture.asset(
|
||||
'assets/majesticons/${iconData as String}.svg',
|
||||
color: color,
|
||||
height: size,
|
||||
package: package,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
60
firka_common/lib/ui/shared/grade_small_card.dart
Normal file
@@ -0,0 +1,60 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:kreta_api/kreta_api.dart';
|
||||
|
||||
import 'package:firka_common/ui/components/firka_card.dart';
|
||||
import 'package:firka_common/ui/components/grade_helpers.dart';
|
||||
import 'package:firka_common/ui/shared/class_icon.dart';
|
||||
import 'package:firka_common/ui/theme/style.dart';
|
||||
|
||||
class GradeSmallCard extends FirkaCard {
|
||||
final List<Grade> grades;
|
||||
final Subject subject;
|
||||
|
||||
GradeSmallCard(this.grades, this.subject, {super.key})
|
||||
: super(
|
||||
left: [
|
||||
ClassIconWidget(
|
||||
uid: subject.uid,
|
||||
className: subject.name,
|
||||
category: subject.category.name!,
|
||||
color: appStyle.colors.accent,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child: Text(
|
||||
subject.name,
|
||||
style: appStyle.fonts.B_16SB.apply(
|
||||
color: appStyle.colors.textPrimary,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
right: [
|
||||
grades.getAverageBySubject(subject).isNaN
|
||||
? const SizedBox()
|
||||
: Card(
|
||||
shadowColor: Colors.transparent,
|
||||
color: getGradeColor(
|
||||
grades.getAverageBySubject(subject),
|
||||
).withAlpha(38),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 8,
|
||||
right: 8,
|
||||
top: 4,
|
||||
bottom: 4,
|
||||
),
|
||||
child: Text(
|
||||
grades.getAverageBySubject(subject).toStringAsFixed(2),
|
||||
style: appStyle.fonts.B_16SB.apply(
|
||||
color: getGradeColor(
|
||||
grades.getAverageBySubject(subject),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
309
firka_common/lib/ui/theme/style.dart
Normal file
@@ -0,0 +1,309 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// Design system token names; ignore non_constant_identifier_names for consistency with design specs
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
|
||||
class FirkaFonts {
|
||||
TextStyle H_H1;
|
||||
TextStyle H_18px;
|
||||
TextStyle H_H2;
|
||||
TextStyle H_16px;
|
||||
TextStyle H_14px;
|
||||
TextStyle H_12px;
|
||||
|
||||
TextStyle H_16px_trimmed;
|
||||
|
||||
TextStyle B_16R;
|
||||
TextStyle B_16SB;
|
||||
TextStyle B_15SB;
|
||||
|
||||
TextStyle B_14R;
|
||||
TextStyle B_14SB;
|
||||
|
||||
TextStyle B_12R;
|
||||
TextStyle B_12SB;
|
||||
|
||||
TextStyle P_14;
|
||||
TextStyle P_12;
|
||||
|
||||
FirkaFonts({
|
||||
required this.H_H1,
|
||||
required this.H_18px,
|
||||
required this.H_H2,
|
||||
required this.H_16px,
|
||||
required this.H_14px,
|
||||
required this.H_12px,
|
||||
required this.H_16px_trimmed,
|
||||
required this.B_16R,
|
||||
required this.B_16SB,
|
||||
required this.B_15SB,
|
||||
required this.B_14R,
|
||||
required this.B_14SB,
|
||||
required this.B_12R,
|
||||
required this.B_12SB,
|
||||
required this.P_14,
|
||||
required this.P_12,
|
||||
});
|
||||
}
|
||||
|
||||
class FirkaColors {
|
||||
Color background;
|
||||
Color backgroundAmoled;
|
||||
Color background0p;
|
||||
Color success;
|
||||
int shadowBlur;
|
||||
|
||||
Color textPrimary;
|
||||
Color textSecondary;
|
||||
Color textTertiary;
|
||||
|
||||
Color textPrimaryLight;
|
||||
Color textSecondaryLight;
|
||||
Color textTertiaryLight;
|
||||
|
||||
Color card;
|
||||
Color cardTranslucent;
|
||||
|
||||
Color buttonSecondaryFill;
|
||||
|
||||
Color accent;
|
||||
Color secondary;
|
||||
Color shadowColor;
|
||||
Color a10p; // 10%
|
||||
Color a15p; // 15%
|
||||
|
||||
Color warningAccent;
|
||||
Color warningText;
|
||||
Color warning15p;
|
||||
Color warningCard;
|
||||
|
||||
Color errorAccent;
|
||||
Color errorText;
|
||||
Color error15p;
|
||||
Color errorCard;
|
||||
|
||||
Color grade5;
|
||||
Color grade4;
|
||||
Color grade3;
|
||||
Color grade2;
|
||||
Color grade1;
|
||||
|
||||
FirkaColors({
|
||||
required this.background,
|
||||
required this.backgroundAmoled,
|
||||
required this.background0p,
|
||||
required this.success,
|
||||
required this.shadowBlur,
|
||||
required this.textPrimary,
|
||||
required this.textSecondary,
|
||||
required this.textTertiary,
|
||||
required this.textPrimaryLight,
|
||||
required this.textSecondaryLight,
|
||||
required this.textTertiaryLight,
|
||||
required this.card,
|
||||
required this.cardTranslucent,
|
||||
required this.buttonSecondaryFill,
|
||||
required this.accent,
|
||||
required this.secondary,
|
||||
required this.shadowColor,
|
||||
required this.a10p,
|
||||
required this.a15p,
|
||||
required this.warningAccent,
|
||||
required this.warningText,
|
||||
required this.warning15p,
|
||||
required this.warningCard,
|
||||
required this.errorAccent,
|
||||
required this.errorText,
|
||||
required this.error15p,
|
||||
required this.errorCard,
|
||||
required this.grade5,
|
||||
required this.grade4,
|
||||
required this.grade3,
|
||||
required this.grade2,
|
||||
required this.grade1,
|
||||
});
|
||||
}
|
||||
|
||||
class FirkaStyle {
|
||||
FirkaColors colors;
|
||||
FirkaFonts fonts;
|
||||
bool isLight;
|
||||
|
||||
FirkaStyle({
|
||||
required this.isLight,
|
||||
required this.colors,
|
||||
required this.fonts,
|
||||
});
|
||||
}
|
||||
|
||||
final _defaultFonts = FirkaFonts(
|
||||
H_H1: TextStyle(
|
||||
fontSize: 30,
|
||||
fontFamily: 'Montserrat',
|
||||
fontVariations: [FontVariation("wght", 700)],
|
||||
),
|
||||
H_18px: TextStyle(
|
||||
fontSize: 18,
|
||||
fontFamily: 'Montserrat',
|
||||
fontVariations: [FontVariation("wght", 700)],
|
||||
),
|
||||
H_H2: TextStyle(
|
||||
fontSize: 20,
|
||||
fontFamily: 'Montserrat',
|
||||
fontVariations: [FontVariation("wght", 700)],
|
||||
),
|
||||
H_16px: TextStyle(
|
||||
fontSize: 16,
|
||||
fontFamily: 'Montserrat',
|
||||
fontVariations: [FontVariation("wght", 600)],
|
||||
),
|
||||
H_14px: TextStyle(
|
||||
fontSize: 14,
|
||||
fontFamily: 'Montserrat',
|
||||
fontVariations: [FontVariation("wght", 600)],
|
||||
),
|
||||
H_12px: TextStyle(
|
||||
fontSize: 12,
|
||||
fontFamily: 'Montserrat',
|
||||
fontVariations: [FontVariation("wght", 600)],
|
||||
),
|
||||
H_16px_trimmed: TextStyle(
|
||||
fontSize: 16,
|
||||
fontFamily: 'Montserrat',
|
||||
fontVariations: [FontVariation("wght", 600)],
|
||||
height: 1.3,
|
||||
),
|
||||
B_16R: TextStyle(
|
||||
fontSize: 16,
|
||||
fontFamily: 'Figtree',
|
||||
fontVariations: [FontVariation("wght", 600)],
|
||||
height: 1.3,
|
||||
),
|
||||
B_16SB: TextStyle(
|
||||
fontSize: 16,
|
||||
fontFamily: 'Figtree',
|
||||
fontVariations: [FontVariation("wght", 700)],
|
||||
height: 1.3,
|
||||
),
|
||||
B_14R: TextStyle(
|
||||
fontSize: 14,
|
||||
fontFamily: 'Figtree',
|
||||
fontVariations: [FontVariation("wght", 600)],
|
||||
height: 1.3,
|
||||
),
|
||||
B_14SB: TextStyle(
|
||||
fontSize: 14,
|
||||
fontFamily: 'Figtree',
|
||||
fontVariations: [FontVariation("wght", 700)],
|
||||
height: 1.3,
|
||||
),
|
||||
B_15SB: TextStyle(
|
||||
fontSize: 15,
|
||||
fontFamily: 'Figtree',
|
||||
fontVariations: [FontVariation("wght", 700)],
|
||||
height: 1.3,
|
||||
),
|
||||
B_12R: TextStyle(
|
||||
fontSize: 12,
|
||||
fontFamily: 'Figtree',
|
||||
fontVariations: [FontVariation("wght", 600)],
|
||||
height: 1.3,
|
||||
),
|
||||
B_12SB: TextStyle(
|
||||
fontSize: 12,
|
||||
fontFamily: 'Figtree',
|
||||
fontVariations: [FontVariation("wght", 700)],
|
||||
height: 1.3,
|
||||
),
|
||||
P_14: TextStyle(
|
||||
fontSize: 14,
|
||||
fontFamily: 'RobotoMono',
|
||||
fontVariations: [FontVariation("wght", 700)],
|
||||
),
|
||||
P_12: TextStyle(
|
||||
fontSize: 12,
|
||||
fontFamily: 'RobotoMono',
|
||||
fontVariations: [FontVariation("wght", 700)],
|
||||
),
|
||||
);
|
||||
|
||||
final FirkaStyle lightStyle = FirkaStyle(
|
||||
isLight: true,
|
||||
colors: FirkaColors(
|
||||
background: Color(0xFFFAFFF0),
|
||||
backgroundAmoled: Colors.black,
|
||||
background0p: Color(0x00fafff0),
|
||||
success: Color(0xFF92EA3B),
|
||||
shadowBlur: 2,
|
||||
textPrimary: Color(0xFF394C0A),
|
||||
textSecondary: Color(0xCC394C0A),
|
||||
textTertiary: Color(0x80394C0A),
|
||||
textPrimaryLight: Color(0xFF394C0A),
|
||||
textSecondaryLight: Color(0xCC394C0A),
|
||||
textTertiaryLight: Color(0x80394C0A),
|
||||
card: Color(0xFFF3FBDE),
|
||||
cardTranslucent: Color(0x80F3FBDE),
|
||||
buttonSecondaryFill: Color(0xFFFEFFFD),
|
||||
accent: Color(0xFFA7DC22),
|
||||
secondary: Color(0xFF6E8F1B),
|
||||
shadowColor: Color(0x33647e22),
|
||||
a10p: Color(0x1aa7dc22),
|
||||
a15p: Color(0x26a7dc22),
|
||||
warningAccent: Color(0xFFFFA046),
|
||||
warningText: Color(0xFF8F531B),
|
||||
warning15p: Color(0x26FFA046),
|
||||
warningCard: Color(0xFFFAEBDC),
|
||||
errorAccent: Color(0xFFFF54A1),
|
||||
errorText: Color(0xFF8F1B4F),
|
||||
error15p: Color(0x26FF54A1),
|
||||
errorCard: Color(0xFFFADCE9),
|
||||
grade5: Color(0xFF22CCAD),
|
||||
grade4: Color(0xFF92EA3B),
|
||||
grade3: Color(0xFFF9CF00),
|
||||
grade2: Color(0xFFFFA046),
|
||||
grade1: Color(0xFFFF54A1),
|
||||
),
|
||||
fonts: _defaultFonts,
|
||||
);
|
||||
|
||||
final FirkaStyle darkStyle = FirkaStyle(
|
||||
isLight: false,
|
||||
colors: FirkaColors(
|
||||
background: Color(0xFF0D1202),
|
||||
backgroundAmoled: Colors.black,
|
||||
background0p: Color(0x00fafff0),
|
||||
success: Color(0xFF92EA3B),
|
||||
shadowBlur: 0,
|
||||
textPrimary: Color(0xFFEAF7CC),
|
||||
textSecondary: Color(0xB3EAF7CC),
|
||||
textTertiary: Color(0x80EAF7CC),
|
||||
textPrimaryLight: Color(0xFF394C0A),
|
||||
textSecondaryLight: Color(0xCC394C0A),
|
||||
textTertiaryLight: Color(0x80394C0A),
|
||||
card: Color(0xFF141905),
|
||||
cardTranslucent: Color(0x80141905),
|
||||
buttonSecondaryFill: Color(0xFF20290B),
|
||||
accent: Color(0xFFA7DC22),
|
||||
secondary: Color(0xFFCBEE71),
|
||||
shadowColor: Color(0x26CBEE71),
|
||||
a10p: Color(0x1AA7DC22),
|
||||
a15p: Color(0x26A7DC22),
|
||||
warningAccent: Color(0xFFFFA046),
|
||||
warningText: Color(0xFFF0B37A),
|
||||
warning15p: Color(0x26FFA046),
|
||||
warningCard: Color(0xFF201203),
|
||||
errorAccent: Color(0xFFFF54A1),
|
||||
errorText: Color(0xFFF59EC5),
|
||||
error15p: Color(0x26FF54A1),
|
||||
errorCard: Color(0xFF1E030F),
|
||||
grade5: Color(0xFF22CCAD),
|
||||
grade4: Color(0xFF92EA3B),
|
||||
grade3: Color(0xFFF9CF00),
|
||||
grade2: Color(0xFFFFA046),
|
||||
grade1: Color(0xFFFF54A1),
|
||||
),
|
||||
fonts: _defaultFonts,
|
||||
);
|
||||
|
||||
FirkaStyle appStyle = lightStyle;
|
||||
FirkaStyle wearStyle = darkStyle;
|
||||
21
firka_common/pubspec.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
name: firka_common
|
||||
description: Shared widgets, data structures, and helpers for firka and firka_wear.
|
||||
publish_to: "none"
|
||||
version: 0.1.0
|
||||
|
||||
environment:
|
||||
sdk: ^3.11.0
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
kreta_api:
|
||||
path: ../kreta_api
|
||||
majesticons_flutter: ^0.0.1
|
||||
flutter_svg: ^1.1.6
|
||||
intl: any
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^6.0.0
|
||||
@@ -1,15 +1,5 @@
|
||||
import org.apache.commons.io.FileUtils
|
||||
import java.io.FileInputStream
|
||||
import java.security.MessageDigest
|
||||
import java.util.Properties
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.Future
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipFile
|
||||
import java.util.zip.ZipOutputStream
|
||||
import java.util.zip.ZipOutputStream.DEFLATED
|
||||
import java.util.zip.ZipOutputStream.STORED
|
||||
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
@@ -114,724 +104,3 @@ fun checkReleaseKey() {
|
||||
throw Exception("Release keystore not found!")
|
||||
}
|
||||
}
|
||||
|
||||
fun transformApks(debug: Boolean, i : Int = 0) {
|
||||
try {
|
||||
_transformApks(debug)
|
||||
} catch (e: Exception) {
|
||||
if (i < 5) {
|
||||
e.printStackTrace()
|
||||
|
||||
println("Retrying: ${i + 1}")
|
||||
transformApks(debug, i + 1)
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun _transformApks(debug: Boolean) {
|
||||
println("Starting APK transformation process...")
|
||||
|
||||
val buildDir = project.buildDir
|
||||
val apkDir = File(buildDir, "outputs/flutter-apk")
|
||||
val apks = getApks(debug)
|
||||
var c = 0
|
||||
apks
|
||||
.forEach { c++; transformAndSignApk(apkDir, it.nameWithoutExtension, debug) }
|
||||
|
||||
println("Transformed: $c apks")
|
||||
}
|
||||
|
||||
fun transformAndSignApk(apkDir: File, name: String, debug: Boolean) {
|
||||
val originalApk = File(apkDir, "$name.apk")
|
||||
val transformedApk = File(apkDir, "$name-transformed.apk")
|
||||
val finalApk = File(apkDir, "$name-resigned.apk")
|
||||
val finalIdsig = File(apkDir, "$name-resigned.apk.idsig")
|
||||
|
||||
if (!originalApk.exists()) {
|
||||
throw GradleException("Original APK not found at: ${originalApk.absolutePath}")
|
||||
}
|
||||
|
||||
if (transformedApk.exists()) transformedApk.delete()
|
||||
if (finalApk.exists()) finalApk.delete()
|
||||
|
||||
println("Original APK: ${originalApk.absolutePath}")
|
||||
|
||||
try {
|
||||
println("Transforming APK...")
|
||||
transformApk(originalApk, transformedApk, if (debug) { "6" } else {"Z"})
|
||||
|
||||
if (debug) {
|
||||
println("Signing with debug key...")
|
||||
signWithDebugKey(transformedApk, finalApk)
|
||||
} else {
|
||||
println("Signing with release key...")
|
||||
signWithReleaseKey(transformedApk, finalApk)
|
||||
}
|
||||
|
||||
if (finalApk.exists()) {
|
||||
originalApk.delete()
|
||||
finalIdsig.delete()
|
||||
finalApk.renameTo(originalApk)
|
||||
println("APK successfully transformed")
|
||||
println("Final APK: ${originalApk.absolutePath}")
|
||||
}
|
||||
|
||||
transformedApk.delete()
|
||||
} catch (e: Exception) {
|
||||
throw GradleException("Failed to transform and resign APK: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun transformApk(input: File, output: File, compressionLevel: String = "Z") {
|
||||
val tempDir = File(project.buildDir, "tmp/apk-transform")
|
||||
val cacheDir = File(project.buildDir, "cache")
|
||||
val optipngCacheDir = File(cacheDir, "optipng")
|
||||
val assetCompressionDir = File(cacheDir, "assets")
|
||||
tempDir.deleteRecursively()
|
||||
tempDir.mkdirs()
|
||||
if (!optipngCacheDir.exists()) optipngCacheDir.mkdirs()
|
||||
if (!assetCompressionDir.exists()) assetCompressionDir.mkdirs()
|
||||
|
||||
val brotli = findToolInPath("brotli")
|
||||
?: throw Exception("Brotli not found in path")
|
||||
val optipng = findToolInPath("optipng")
|
||||
|
||||
if (optipng == null || optipng.isEmpty()) {
|
||||
println("Optipng was not found in PATH, optimizing images will be skipped.")
|
||||
}
|
||||
|
||||
copy {
|
||||
from(zipTree(input))
|
||||
into(tempDir)
|
||||
}
|
||||
|
||||
val metaInf = File(tempDir, "META-INF")
|
||||
val metaInfFiles = metaInf.listFiles()
|
||||
for (file in metaInfFiles!!) {
|
||||
if (file.name.endsWith("MF") || file.name.endsWith("SF")
|
||||
|| file.name.endsWith("RSA")) {
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
|
||||
val arches = File(tempDir, "lib").listFiles()
|
||||
val compressedLibs = mutableMapOf<String, String>()
|
||||
for (arch in arches!!) {
|
||||
val libFlutter = File(arch, "libflutter.so")
|
||||
|
||||
if (!libFlutter.exists()) continue
|
||||
|
||||
val compressedFlutter = File(arch, "libflutter-br.so")
|
||||
|
||||
compressedLibs["libflutter.so"] = libFlutter.sha256()
|
||||
|
||||
println("Compressing ${arch.name}/libflutter.so with brotli")
|
||||
exec {
|
||||
commandLine(
|
||||
brotli,
|
||||
"-$compressionLevel",
|
||||
libFlutter.absolutePath,
|
||||
"-o", compressedFlutter.absolutePath
|
||||
)
|
||||
}
|
||||
libFlutter.delete()
|
||||
|
||||
val json = groovy.json.JsonBuilder(compressedLibs)
|
||||
File(arch, "index.so").writeText(json.toString())
|
||||
}
|
||||
|
||||
val topDirL = tempDir.absolutePath.length + 1
|
||||
val zos = ZipOutputStream(output.outputStream())
|
||||
|
||||
val coreCount = Runtime.getRuntime().availableProcessors()
|
||||
val flutterResources = tempDir.walkTopDown().filter{f -> f.absolutePath.contains("flutter_assets")}
|
||||
val pngFiles = tempDir.walkTopDown().filter{f -> f.name.endsWith(".png")}
|
||||
|
||||
val assetIndex = mutableMapOf<String, String>()
|
||||
val indexReadWriteLock = ReentrantReadWriteLock()
|
||||
|
||||
if (compressionLevel == "Z") {
|
||||
if (optipng != null) {
|
||||
val executor = Executors.newFixedThreadPool(coreCount)
|
||||
val futures = mutableListOf<Future<*>>()
|
||||
|
||||
pngFiles.forEach { pngFile ->
|
||||
val cacheFile = File(optipngCacheDir, pngFile.sha256())
|
||||
|
||||
if (cacheFile.exists()) {
|
||||
cacheFile.copyTo(pngFile, true)
|
||||
} else {
|
||||
val future = executor.submit {
|
||||
exec {
|
||||
commandLine(
|
||||
optipng,
|
||||
"-zm", "9",
|
||||
"-zw", "32k",
|
||||
"-o9",
|
||||
pngFile.absolutePath
|
||||
)
|
||||
}
|
||||
|
||||
pngFile.copyTo(cacheFile, true)
|
||||
}
|
||||
|
||||
futures.add(future)
|
||||
}
|
||||
}
|
||||
|
||||
futures.forEach { it.get() }
|
||||
executor.shutdown()
|
||||
}
|
||||
|
||||
val executor = Executors.newFixedThreadPool(coreCount)
|
||||
val futures = mutableListOf<Future<*>>()
|
||||
|
||||
val blacklist = listOf(
|
||||
// "AssetManifest.bin",
|
||||
"AssetManifest.json",
|
||||
"FontManifest.json",
|
||||
"isolate_snapshot_data",
|
||||
"kernel_blob.bin",
|
||||
"NativeAssetsManifest.json",
|
||||
"NOTICES.Z",
|
||||
"vm_snapshot_data",
|
||||
"fonts",
|
||||
"shaders"
|
||||
)
|
||||
|
||||
flutterResources.forEach { f ->
|
||||
val relName = f.absolutePath.substring(topDirL).replace("\\", "/")
|
||||
if (f.isDirectory) return@forEach
|
||||
|
||||
val cacheFileRaw = File(assetCompressionDir, f.sha256()+".r")
|
||||
val cacheFileGz = File(assetCompressionDir, f.sha256()+".gz")
|
||||
val cacheFileBr = File(assetCompressionDir, f.sha256()+".br")
|
||||
|
||||
if (cacheFileRaw.exists() || cacheFileGz.exists() || cacheFileBr.exists()) {
|
||||
if (cacheFileRaw.exists()) {
|
||||
cacheFileRaw.copyTo(f, true)
|
||||
|
||||
indexReadWriteLock.writeLock().lock()
|
||||
assetIndex[relName] = "r"
|
||||
indexReadWriteLock.writeLock().unlock()
|
||||
} else if (cacheFileGz.exists()) {
|
||||
cacheFileGz.copyTo(f, true)
|
||||
|
||||
indexReadWriteLock.writeLock().lock()
|
||||
assetIndex[relName] = "g"
|
||||
indexReadWriteLock.writeLock().unlock()
|
||||
} else {
|
||||
cacheFileBr.copyTo(f, true)
|
||||
|
||||
indexReadWriteLock.writeLock().lock()
|
||||
assetIndex[relName] = "b"
|
||||
indexReadWriteLock.writeLock().unlock()
|
||||
}
|
||||
} else {
|
||||
val future = executor.submit {
|
||||
val brTmp = File(f.absolutePath + ".br.tmp")
|
||||
val gzTmp = File(f.absolutePath + ".gz.tmp")
|
||||
|
||||
var blacklisted = false
|
||||
for (f in blacklist) {
|
||||
if (relName.contains(f)) {
|
||||
blacklisted = true
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!blacklisted) {
|
||||
println("$relName: Testing with brotli")
|
||||
exec {
|
||||
commandLine(
|
||||
brotli,
|
||||
"-$compressionLevel",
|
||||
f.absolutePath,
|
||||
"-o", brTmp.absolutePath
|
||||
)
|
||||
}
|
||||
|
||||
println("$relName: Testing with gzip")
|
||||
ant.invokeMethod(
|
||||
"gzip", mapOf(
|
||||
"src" to f.absolutePath,
|
||||
"destfile" to gzTmp.absolutePath,
|
||||
)
|
||||
)
|
||||
|
||||
println("$brTmp: ${brTmp.length()}")
|
||||
println("$gzTmp: ${gzTmp.length()}")
|
||||
if (f.length() < gzTmp.length() && f.length() < brTmp.length()) {
|
||||
println("$relName: Raw file wins")
|
||||
|
||||
f.copyTo(cacheFileRaw, true)
|
||||
|
||||
indexReadWriteLock.writeLock().lock()
|
||||
assetIndex[relName] = "r"
|
||||
indexReadWriteLock.writeLock().unlock()
|
||||
} else {
|
||||
if (brTmp.length() < gzTmp.length()) {
|
||||
println("$relName: Brotli wins")
|
||||
|
||||
f.delete()
|
||||
brTmp.copyTo(f, true)
|
||||
brTmp.copyTo(cacheFileBr, true)
|
||||
|
||||
indexReadWriteLock.writeLock().lock()
|
||||
assetIndex[relName] = "b"
|
||||
indexReadWriteLock.writeLock().unlock()
|
||||
} else {
|
||||
println("$relName: Gzip wins")
|
||||
|
||||
f.delete()
|
||||
gzTmp.copyTo(f, true)
|
||||
gzTmp.copyTo(cacheFileGz, true)
|
||||
|
||||
indexReadWriteLock.writeLock().lock()
|
||||
assetIndex[relName] = "g"
|
||||
indexReadWriteLock.writeLock().unlock()
|
||||
}
|
||||
}
|
||||
|
||||
brTmp.delete()
|
||||
gzTmp.delete()
|
||||
}
|
||||
}
|
||||
|
||||
futures.add(future)
|
||||
}
|
||||
}
|
||||
|
||||
futures.forEach { it.get() }
|
||||
executor.shutdown()
|
||||
}
|
||||
|
||||
tempDir.walkTopDown().forEach { f ->
|
||||
if (f.absolutePath == tempDir.absolutePath) return@forEach
|
||||
|
||||
var relName = f.absolutePath.substring(topDirL).replace("\\", "/")
|
||||
if (f.isDirectory && !relName.endsWith("/")) relName += "/"
|
||||
|
||||
if (compressionLevel == "Z") {
|
||||
if (relName == "assets/flutter_assets/assets/firka.i") return@forEach
|
||||
}
|
||||
|
||||
println(relName)
|
||||
|
||||
val compress = !relName.endsWith(".so") && !relName.endsWith(".arsc")
|
||||
zos.setMethod(if (compress) { DEFLATED } else { STORED })
|
||||
val entry = ZipEntry(relName)
|
||||
if (!compress) {
|
||||
entry.size = f.length()
|
||||
entry.crc = FileUtils.checksumCRC32(f)
|
||||
}
|
||||
zos.putNextEntry(entry)
|
||||
if (f.isFile) {
|
||||
zos.write(f.readBytes())
|
||||
}
|
||||
zos.closeEntry()
|
||||
}
|
||||
if (compressionLevel == "Z") {
|
||||
zos.setMethod(DEFLATED)
|
||||
zos.putNextEntry(ZipEntry("assets/flutter_assets/assets/firka.i"))
|
||||
|
||||
val indexUncompressed = File(tempDir, "index.json")
|
||||
indexReadWriteLock.readLock().lock()
|
||||
val json = groovy.json.JsonBuilder(assetIndex)
|
||||
indexReadWriteLock.readLock().unlock()
|
||||
indexUncompressed.writeText(json.toString())
|
||||
|
||||
val indexCompressed = File(tempDir, "index.json.br")
|
||||
|
||||
exec {
|
||||
commandLine(
|
||||
brotli,
|
||||
"-$compressionLevel",
|
||||
indexUncompressed.absolutePath,
|
||||
"-o", indexCompressed.absolutePath
|
||||
)
|
||||
}
|
||||
|
||||
zos.write(indexCompressed.readBytes())
|
||||
indexUncompressed.delete()
|
||||
indexCompressed.delete()
|
||||
|
||||
zos.closeEntry()
|
||||
}
|
||||
zos.close()
|
||||
|
||||
tempDir.deleteRecursively()
|
||||
println("APK transformed successfully")
|
||||
}
|
||||
|
||||
fun transformAppBundle() {
|
||||
val buildDir = project.buildDir
|
||||
val bundle = File(buildDir, "outputs/bundle/release/app-release.aab")
|
||||
val bundleTmp = File(buildDir, "outputs/bundle/release/tmp.zip")
|
||||
|
||||
val apks = getApks(false)
|
||||
val apkCount = apks.count { it.name.startsWith("app-") && it.name.endsWith("-release.apk") }
|
||||
|
||||
if (!bundle.exists()) {
|
||||
throw Exception("Bundle not found at: $bundle")
|
||||
}
|
||||
|
||||
if (apkCount < 3) {
|
||||
throw Exception("Excepected 3 apks per abi but only found $apkCount")
|
||||
}
|
||||
|
||||
val aabTempDir = File(project.buildDir, "tmp/aab-transform")
|
||||
aabTempDir.deleteRecursively()
|
||||
aabTempDir.mkdirs()
|
||||
|
||||
val apksUnzipped = File(project.buildDir, "tmp/apks-unzipped")
|
||||
apksUnzipped.deleteRecursively()
|
||||
|
||||
val arm32TempDir = File(apksUnzipped, "armeabi-v7a")
|
||||
arm32TempDir.mkdirs()
|
||||
val arm64TempDir = File(apksUnzipped, "arm64-v8a")
|
||||
arm64TempDir.mkdirs()
|
||||
val x86TempDir = File(apksUnzipped, "x86_64")
|
||||
x86TempDir.mkdirs()
|
||||
|
||||
copy {
|
||||
from(zipTree(bundle))
|
||||
into(aabTempDir)
|
||||
}
|
||||
copy {
|
||||
from(zipTree(apks.first { it.name.contains("armeabi-v7a") }))
|
||||
into(arm32TempDir)
|
||||
}
|
||||
copy {
|
||||
from(zipTree(apks.first { it.name.contains("arm64-v8a") }))
|
||||
into(arm64TempDir)
|
||||
}
|
||||
copy {
|
||||
from(zipTree(apks.first { it.name.contains("x86_64") }))
|
||||
into(x86TempDir)
|
||||
}
|
||||
|
||||
val libs = File(aabTempDir, "base/lib").listFiles()!!
|
||||
|
||||
for (dstLibs in libs) {
|
||||
println("Copying lib: ${dstLibs.name}")
|
||||
val srcDir = File(apksUnzipped, dstLibs.name)
|
||||
if (!srcDir.exists()) {
|
||||
continue
|
||||
}
|
||||
val srcLibs = File(srcDir, "lib/${dstLibs.name}/")
|
||||
|
||||
dstLibs.listFiles()!!.forEach { it.delete() }
|
||||
srcLibs.listFiles()!!.forEach { it.copyTo(File(dstLibs, it.name)) }
|
||||
}
|
||||
|
||||
val zos = ZipOutputStream(bundleTmp.outputStream())
|
||||
val bundleZip = ZipFile(bundle)
|
||||
val bundleEntries = bundleZip.entries()
|
||||
|
||||
val brotli = findToolInPath("brotli")
|
||||
?: throw Exception("Brotli not found in path")
|
||||
val optipng = findToolInPath("optipng")
|
||||
?: throw Exception("Optipng not found in path")
|
||||
|
||||
val indexReadWriteLock = ReentrantReadWriteLock()
|
||||
val assetIndex = mutableMapOf<String, String>()
|
||||
|
||||
while (bundleEntries.hasMoreElements()) {
|
||||
val entry = bundleEntries.nextElement()
|
||||
|
||||
/*
|
||||
if (entry.name == "base/assets/flutter_assets/assets/firka.i") {
|
||||
println("Patching: ${entry.name}")
|
||||
zos.putNextEntry(ZipEntry("assets/flutter_assets/assets/firka.i"))
|
||||
|
||||
val indexUncompressed = File(aabTempDir, "index.json")
|
||||
indexReadWriteLock.readLock().lock()
|
||||
val json = groovy.json.JsonBuilder(assetIndex)
|
||||
indexReadWriteLock.readLock().unlock()
|
||||
indexUncompressed.writeText(json.toString())
|
||||
|
||||
val indexCompressed = File(aabTempDir, "index.json.br")
|
||||
|
||||
exec {
|
||||
commandLine(
|
||||
brotli,
|
||||
"-Z",
|
||||
indexUncompressed.absolutePath,
|
||||
"-o", indexCompressed.absolutePath
|
||||
)
|
||||
}
|
||||
|
||||
zos.write(indexCompressed.readBytes())
|
||||
indexUncompressed.delete()
|
||||
indexCompressed.delete()
|
||||
|
||||
zos.closeEntry()
|
||||
continue
|
||||
}
|
||||
if (entry.name.startsWith("base/lib")) {
|
||||
println("Patching: ${entry.name}")
|
||||
zos.putNextEntry(ZipEntry(entry.name))
|
||||
|
||||
|
||||
|
||||
zos.closeEntry()
|
||||
continue
|
||||
}
|
||||
*/
|
||||
|
||||
println("Adding: ${entry.name}")
|
||||
|
||||
zos.putNextEntry(ZipEntry(entry.name))
|
||||
|
||||
if (!entry.isDirectory) {
|
||||
val data = bundleZip.getInputStream(entry).readAllBytes()
|
||||
zos.write(data)
|
||||
}
|
||||
zos.closeEntry()
|
||||
}
|
||||
bundleZip.close()
|
||||
zos.close()
|
||||
|
||||
bundle.delete()
|
||||
signBundle(bundleTmp, bundle)
|
||||
bundleTmp.delete()
|
||||
|
||||
aabTempDir.deleteRecursively()
|
||||
println("AAB transformed successfully")
|
||||
|
||||
}
|
||||
|
||||
fun File.sha256(): String {
|
||||
val md = MessageDigest.getInstance("SHA-256")
|
||||
val digest = md.digest(this.readBytes())
|
||||
return digest.fold("") { str, it -> str + "%02x".format(it) }
|
||||
}
|
||||
|
||||
fun getApks(debug: Boolean): List<File> {
|
||||
val buildDir = project.buildDir
|
||||
val apkDir = File(buildDir, "outputs/flutter-apk")
|
||||
val apks = apkDir.listFiles()!!
|
||||
val flavor = if (debug) { "debug" } else { "release" }
|
||||
|
||||
return apks
|
||||
.filter { apk -> apk.name.startsWith("app-") && apk.name.endsWith("-$flavor.apk") }
|
||||
.toList()
|
||||
}
|
||||
|
||||
fun getDebugKeystorePath(): String {
|
||||
val userHome = System.getProperty("user.home")
|
||||
val debugKeystore = File(userHome, ".android/debug.keystore")
|
||||
|
||||
if (!debugKeystore.exists()) {
|
||||
throw GradleException("Debug keystore not found at: ${debugKeystore.absolutePath}")
|
||||
}
|
||||
|
||||
return debugKeystore.absolutePath
|
||||
}
|
||||
|
||||
fun getDefaultAndroidSdkPath(): String? {
|
||||
val os = System.getProperty("os.name").lowercase()
|
||||
val userHome = System.getProperty("user.home")
|
||||
|
||||
return when {
|
||||
os.contains("win") ->
|
||||
"$userHome\\AppData\\Local\\Android\\Sdk"
|
||||
os.contains("mac") ->
|
||||
"$userHome/Library/Android/sdk"
|
||||
os.contains("linux") ->
|
||||
"$userHome/Android/Sdk"
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun findToolInPath(toolName: String): String? {
|
||||
val pathEnvironment = System.getenv("PATH")
|
||||
val pathDirs = pathEnvironment.split(File.pathSeparator)
|
||||
|
||||
val executableNames = when {
|
||||
System.getProperty("os.name").lowercase().contains("win") ->
|
||||
listOf("$toolName.exe", toolName)
|
||||
else ->
|
||||
listOf(toolName)
|
||||
}
|
||||
|
||||
for (pathDir in pathDirs) {
|
||||
for (execName in executableNames) {
|
||||
val possibleTool = File(pathDir, execName)
|
||||
if (possibleTool.exists() && possibleTool.canExecute()) {
|
||||
return possibleTool.absolutePath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun findToolInSdkPath(toolName: String): String? {
|
||||
var androidHome : String? = System.getenv("ANDROID_HOME")
|
||||
?: System.getenv("ANDROID_SDK_ROOT")
|
||||
|
||||
if (androidHome == null) androidHome = getDefaultAndroidSdkPath()
|
||||
|
||||
if (androidHome != null) {
|
||||
val buildTools = File(androidHome, "build-tools")
|
||||
if (buildTools.exists()) {
|
||||
val latestVersion = buildTools.listFiles()
|
||||
?.filter { it.isDirectory }
|
||||
?.filter { it.name != "debian" }
|
||||
?.maxByOrNull { it.name }
|
||||
|
||||
if (latestVersion != null) {
|
||||
val toolExec = File(latestVersion, toolName)
|
||||
if (toolExec.exists()) {
|
||||
return toolExec.absolutePath
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!toolName.contains(".exe")) {
|
||||
val exeTool = findToolInSdkPath("$toolName.exe")
|
||||
if (exeTool != null) return exeTool
|
||||
}
|
||||
if (!toolName.contains(".sh")) {
|
||||
val shTool = findToolInSdkPath("$toolName.sh")
|
||||
if (shTool != null) return shTool
|
||||
}
|
||||
if (!toolName.contains(".bat")) {
|
||||
val batTool = findToolInSdkPath("$toolName.bat")
|
||||
if (batTool != null) return batTool
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun signWithDebugKey(input: File, output: File) {
|
||||
val debugKeystore = getDebugKeystorePath()
|
||||
val debugKeystorePassword = "android"
|
||||
val debugKeyAlias = "androiddebugkey"
|
||||
val debugKeyPassword = "android"
|
||||
|
||||
val zipAlign: String = findToolInSdkPath("zipalign")
|
||||
?: throw Exception("Could not find zipalign in ANDROID_SDK")
|
||||
val apksigner: String = findToolInSdkPath("apksigner")
|
||||
?: throw Exception("Could not find zipalign in ANDROID_SDK")
|
||||
|
||||
exec {
|
||||
commandLine(
|
||||
zipAlign,
|
||||
"-v", "4",
|
||||
input.absolutePath,
|
||||
output.absolutePath
|
||||
)
|
||||
}
|
||||
|
||||
exec {
|
||||
commandLine(
|
||||
apksigner, "sign",
|
||||
"--ks", debugKeystore,
|
||||
"--ks-pass", "pass:$debugKeystorePassword",
|
||||
"--ks-key-alias", debugKeyAlias,
|
||||
"--key-pass", "pass:$debugKeyPassword",
|
||||
output.absolutePath
|
||||
)
|
||||
}
|
||||
|
||||
println("APK signed and aligned successfully")
|
||||
}
|
||||
|
||||
fun signWithReleaseKey(input: File, output: File) {
|
||||
val secretsDir = File(projectDir.absolutePath, "../../../secrets/")
|
||||
val propsFile = File(secretsDir, "keystore.properties")
|
||||
|
||||
if (!propsFile.exists()) {
|
||||
throw Exception("Release keystore not found!")
|
||||
}
|
||||
|
||||
val props = loadProperties(propsFile)
|
||||
|
||||
val releaseKeystore = File(secretsDir, props["storeFile"].toString())
|
||||
val releaseKeystorePassword = props["storePassword"] as String
|
||||
val releaseKeyAlias = props["keyAlias"] as String
|
||||
val releaseKeyPassword = props["keyPassword"] as String
|
||||
|
||||
val zipAlign: String = findToolInSdkPath("zipalign")
|
||||
?: throw Exception("Could not find zipalign either in ANDROID_SDK")
|
||||
val apksigner: String = findToolInSdkPath("apksigner")
|
||||
?: throw Exception("Could not find zipalign either in ANDROID_SDK")
|
||||
|
||||
exec {
|
||||
commandLine(
|
||||
zipAlign,
|
||||
"-v", "4",
|
||||
input.absolutePath,
|
||||
output.absolutePath
|
||||
)
|
||||
}
|
||||
|
||||
exec {
|
||||
commandLine(
|
||||
apksigner, "sign",
|
||||
"--ks", releaseKeystore,
|
||||
"--ks-pass", "pass:$releaseKeystorePassword",
|
||||
"--ks-key-alias", releaseKeyAlias,
|
||||
"--key-pass", "pass:$releaseKeyPassword",
|
||||
output.absolutePath
|
||||
)
|
||||
}
|
||||
|
||||
println("APK signed and aligned successfully")
|
||||
}
|
||||
|
||||
fun signBundle(input: File, output: File) {
|
||||
val secretsDir = File(projectDir.absolutePath, "../../../secrets/")
|
||||
val propsFile = File(secretsDir, "keystore.properties")
|
||||
|
||||
if (!propsFile.exists()) {
|
||||
throw Exception("Release keystore not found!")
|
||||
}
|
||||
|
||||
val props = loadProperties(propsFile)
|
||||
|
||||
val releaseKeystore = File(secretsDir, props["storeFile"].toString())
|
||||
val releaseKeystorePassword = props["storePassword"] as String
|
||||
val releaseKeyAlias = props["keyAlias"] as String
|
||||
val releaseKeyPassword = props["keyPassword"] as String
|
||||
|
||||
// val zipAlign: String = findToolInSdkPath("zipalign")
|
||||
// ?: throw Exception("Could not find zipalign in ANDROID_SDK")
|
||||
val jarsigner: String = findToolInPath("jarsigner")
|
||||
?: throw Exception("Could not find jarsigner in PATH")
|
||||
|
||||
/*
|
||||
exec {
|
||||
commandLine(
|
||||
zipAlign,
|
||||
"-v", "4",
|
||||
input.absolutePath,
|
||||
output.absolutePath
|
||||
)
|
||||
}
|
||||
*/
|
||||
input.copyTo(output, true)
|
||||
|
||||
exec {
|
||||
// -keystore $KEYSTORE -storetype $STORETYPE -storepass $STOREPASS -digestalg SHA1 -sigalg SHA256withRSA application.zip $KEYALIAS
|
||||
commandLine(
|
||||
jarsigner,
|
||||
"-verbose",
|
||||
"-sigalg", "SHA256withRSA",
|
||||
"-digestalg", "SHA-256",
|
||||
"-keystore", releaseKeystore,
|
||||
"-storepass", releaseKeystorePassword,
|
||||
output.absolutePath,
|
||||
releaseKeyAlias
|
||||
)
|
||||
}
|
||||
|
||||
println("AAB signed and aligned successfully")
|
||||
}
|
||||
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 7.5 KiB |
18
firka_wear/codegen-lock.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
icons:
|
||||
"flutter_launcher_icons.yaml": "2c1bf9056dfe8db94333143643d2b46308fa332e08de9eda62046a941e83aaaa"
|
||||
"pubspec.yaml": "1948fc1c2c22fbf38f60f1e5b66d71aeefb7637757b26a12619d924c792ed882"
|
||||
"assets/images/logos/colored_logo.png": "ff9c3452b1b0ed07ffa9067fa4cf4dae45dad3e46f5cb6ef4a62ac8c05d8c080"
|
||||
"assets/images/logos/monochrome_logo.png": "188d2b0a64c70323b09bcee721663d6698fb557066f20ddaec97bba6869c1c6c"
|
||||
"assets/images/logos/colored_logo_without_mustache.png": "d11cff9f38985885873bfdd2d84e61f8fab03803eada94d4caac1545ef3685f3"
|
||||
"assets/images/logos/colored_logo_only_mustache.png": "bad6220c11bdfb1dfe04e5173bd2ebedd3999689d4b3a68fc63dc520c96dd33b"
|
||||
l10n:
|
||||
"l10n.yml": "a57bc304cac4a2b0235593586f17f400a5165d67fc9aadeaa11893cfa36ee082"
|
||||
"lib/l10n/app_de.arb": "4be15b38c7a86bae77d5d44eb2172c687c94255716c7103ef16418d9a3aa2856"
|
||||
"lib/l10n/app_en.arb": "6ee8594c5153a1e1fe714a05ea99ed38e63a42aa74faaa1d610c8ba669d7cbbb"
|
||||
"lib/l10n/app_hu.arb": "9acbf3245d9b286c6b7b20d84750313e49e9c09689e1fe60ad5623877cdbf7a6"
|
||||
isar:
|
||||
"lib/data/models/app_settings_model.dart": "2bf4d089ccfcb73edbca5b2d5757e1e698ddde2b8783d212a870aac3157fbb5b"
|
||||
"lib/data/models/generic_cache_model.dart": "dd9979a4f0ba37ce5fd733bf0966088a759b5f356d97ea09c65eefffe8984639"
|
||||
"lib/data/models/homework_cache_model.dart": "911748133c4bcb32bebe40a7c2f6f30d63c030b89a77c6825ec19643d8f8b3c6"
|
||||
"lib/data/models/timetable_cache_model.dart": "078cbc0c5b1e3f0303a56bfe1e55df7669f0b06687ba399ddcae2df2b565d4c7"
|
||||
"lib/data/models/token_model.dart": "3dc6211102c00d8382bfaa929e0ca7dedd7b1771c337f4e96d54e47572e5f6e1"
|
||||
@@ -7,5 +7,3 @@ flutter_launcher_icons:
|
||||
adaptive_icon_foreground: "assets/images/logos/colored_logo_only_mustache.png"
|
||||
adaptive_icon_foreground_inset: 0
|
||||
min_sdk_android: 21
|
||||
ios: true
|
||||
remove_alpha_channel_ios: true
|
||||
|
||||