Compare commits

...

84 Commits
3.1.0 ... 3.3.2

Author SHA1 Message Date
55nknown
cac21a4849 new version 2022-09-05 21:02:21 +02:00
55nknown
4c558157e8 open_file dep git 2022-09-05 21:00:42 +02:00
55nknown
043b669737 implement nonce v2 2022-09-05 21:00:31 +02:00
55nknown
7b9ec6de2e flplug_linux 2022-09-05 17:26:00 +02:00
55nknown
b14821901c vnbmp 2022-08-17 21:28:44 +02:00
55nknown
cc05524bea changelog 2022-06-30 03:15:34 +02:00
55nknown
b3d791a4c3 disable m3 2022-06-30 02:10:34 +02:00
55nknown
9525d7d1df clean up 2022-06-30 01:28:42 +02:00
55nknown
bc040185d0 update pods 2022-06-30 01:21:31 +02:00
55nknown
65e98bf8a8 update ios project 2022-06-30 00:45:53 +02:00
unknown
1a0558485e Create dependabot.yml 2022-06-23 06:15:28 -07:00
unknown
8f7c46d2d4 new version 2022-05-10 18:16:32 +02:00
Brúnó Salomon
4f3d44dfed fix black bar (#107) 2022-05-10 18:00:37 +02:00
unknown
922e8984f8 ios fix try 1 2022-05-02 22:52:30 +02:00
unknown
ee475f8ee8 version bump 2022-05-02 22:09:08 +02:00
unknown
3c431cbce1 Livecardrework (#104) 2022-05-02 22:07:06 +02:00
unknown
708c411339 changelog 2022-03-16 14:26:19 +01:00
unknown
1e9247652a build fix 2022-03-16 12:33:45 +01:00
unknown
551ed6ebdb Merge branch 'master' of ssh://github.com/filc/naplo 2022-03-16 11:51:38 +01:00
unknown
c86ac68007 changelog 2022-03-16 11:51:26 +01:00
DarK-rtfm
d309f11f19 fix graph interval 2022-03-14 08:25:30 +01:00
unknown
e4acd4f872 changelog 2022-03-01 17:43:46 +01:00
unknown
3ee91e7543 settings class avg graph 2022-02-28 17:31:03 +01:00
unknown
f147ae328e changelog 2022-02-28 15:52:40 +01:00
unknown
aab1f605d4 run release script 2022-02-28 15:37:04 +01:00
Brúnó Salomon
c71a6d9468 dev: add version in launch config 2022-02-25 18:52:58 +01:00
unknown
165f836d93 kreten 2022-02-04 16:09:07 +01:00
unknown
6356206291 mobile 2022-02-04 16:09:01 +01:00
unknown
bf8ca49f98 fix build script 2022-02-01 18:37:34 +01:00
unknown
0fca636311 compile time version definition 2022-02-01 18:33:13 +01:00
Brúnó Salomon
8673a9e42a chore: version + changelog 2022-01-22 21:55:49 +01:00
Brúnó Salomon
0390d0df39 feat(quick-actions): add localization 2022-01-17 16:50:32 +01:00
Brúnó Salomon
4ec2f74fee chore(quick-actions): add ios icons 2022-01-17 16:45:55 +01:00
ezyyeah
e2078db34b Merge branch 'master' of https://github.com/filc/naplo 2022-01-16 23:08:29 +01:00
ezyyeah
1f39bdc301 quick actions 2022-01-16 23:08:16 +01:00
unknown
029e841d7b version bump 2022-01-16 21:26:55 +01:00
unknown
6a16f93884 version bump 2022-01-16 21:07:08 +01:00
unknown
cfc0229f09 changelog 2022-01-16 21:03:45 +01:00
unknown
a68dd759d8 homework attachments 2022-01-16 18:15:26 +01:00
unknown
e7d0e3805d Merge branch 'master' of ssh://github.com/filc/naplo 2022-01-16 14:08:07 +01:00
unknown
d617d9ef47 changelog 2022-01-16 14:07:05 +01:00
Unknown
2564224a6e Update changelog.md 2022-01-15 01:52:03 +01:00
unknown
5c2b690bc8 mobile 2022-01-15 01:50:42 +01:00
unknown
ff72d146c0 version bump 2022-01-14 23:50:11 +01:00
unknown
088b6e4580 mobile 2022-01-14 23:44:47 +01:00
Unknown
66793607e8 Update README.md 2022-01-14 23:19:04 +01:00
unknown
c91e792c6c changelog 2022-01-14 23:09:50 +01:00
unknown
16d0bd4163 mobile 2022-01-14 23:09:38 +01:00
Unknown
e559338483 Update README.md 2022-01-08 18:28:47 +01:00
unknown
f2c29aa81b 3.2.0 2022-01-08 15:42:49 +01:00
unknown
e44a3b7330 mobile 2022-01-07 07:05:54 +01:00
unknown
64606311bf mobile 2022-01-07 06:49:17 +01:00
unknown
43c4d2e454 mobile 2022-01-05 16:11:10 +01:00
unknown
9b579ad196 cleanup 2022-01-05 16:10:21 +01:00
unknown
425a4aaa91 cleanup 2022-01-05 15:39:24 +01:00
unknown
506c04aea9 build fix 2022-01-05 13:47:53 +01:00
unknown
cd18cfb220 mobile 2022-01-05 13:03:02 +01:00
unknown
f071e59e7a kreten 2022-01-05 13:02:57 +01:00
unknown
49f3a447b3 desktop 2022-01-05 13:02:52 +01:00
unknown
339dbea1ef flutter linting 2022-01-05 13:02:22 +01:00
unknown
79f6ef4c50 kreta 2022-01-05 12:32:26 +01:00
unknown
ae18fbab2e mobile 2022-01-05 12:32:18 +01:00
unknown
9003e13aa1 updater update 2022-01-05 12:30:43 +01:00
unknown
e58f4859b4 changelog 2022-01-05 12:30:07 +01:00
unknown
be809c6aaf Merge branch 'master' of ssh://github.com/filc/naplo 2022-01-05 12:18:48 +01:00
Brúnó Salomon
a6b012035b mobile 2021-12-30 17:26:43 +01:00
unknown
a0ef512c55 changelog 2021-12-03 23:02:12 +01:00
unknown
4e1c1be6e4 better updates 2021-12-03 23:00:13 +01:00
unknown
92a93941ab changelog 2021-12-03 22:08:27 +01:00
Unknown
ece621455c Update README.md 2021-11-20 15:55:44 +01:00
Unknown
0787f97a6f Update README.md 2021-11-17 22:52:29 +01:00
unknown
c74975e3bc kreten 2021-11-07 11:26:29 +01:00
unknown
4b669b0069 mobile 2021-11-07 11:26:21 +01:00
unknown
d24286b61c mobile 2021-11-07 10:35:57 +01:00
unknown
40f3d2159a revert build fixes, downgrade permission_handler (sdk 30) 2021-11-02 22:10:35 +01:00
unknown
8df77b5c06 3.1.1 changelog 2021-11-02 21:30:49 +01:00
unknown
fd88b5ee55 build fix (gradle ver) 2021-11-02 21:05:11 +01:00
unknown
ceaafb4897 build fix 2021-11-02 20:28:12 +01:00
unknown
6617882892 build fix 2021-11-02 19:57:56 +01:00
unknown
1b16b748d4 bump android sdk version 2021-11-02 19:57:28 +01:00
unknown
7014255b89 bump 3.1.1 2021-11-02 16:12:34 +01:00
unknown
d86f2fffe5 kreten 2021-11-02 16:07:38 +01:00
unknown
de1b0444b6 kreten 2021-11-02 14:12:33 +01:00
unknown
bd1a8358f6 mobile 2021-11-02 14:12:23 +01:00
73 changed files with 1028 additions and 429 deletions

11
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "pub" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"

4
.gitignore vendored
View File

@@ -1,5 +1,7 @@
# See https://www.dartlang.org/guides/libraries/private-files # See https://www.dartlang.org/guides/libraries/private-files
termek.txt
# Files and directories created by pub # Files and directories created by pub
.dart_tool/ .dart_tool/
.packages .packages
@@ -19,3 +21,5 @@ doc/api/
*.js_ *.js_
*.js.deps *.js.deps
*.js.map *.js.map
*.txt

5
.vscode/launch.json vendored
View File

@@ -8,7 +8,10 @@
"name": "filcnaplo", "name": "filcnaplo",
"cwd": "filcnaplo", "cwd": "filcnaplo",
"request": "launch", "request": "launch",
"type": "dart" "type": "dart",
"toolArgs": [
"--dart-define=APPVER=$(cat pubspec.yaml | grep version: | cut -d' ' -f2 | cut -d+ -f1)"
]
} }
] ]
} }

View File

@@ -9,16 +9,16 @@
## Setup ## Setup
### Clone the project: ### Clone the project
``` ```sh
$ git clone --recursive https://github.com/filc/naplo git clone --recursive https://github.com/filc/naplo
$ cd naplo cd naplo
``` ```
### Run the app: ### Run the app
``` ```sh
$ cd filcnaplo cd filcnaplo
$ flutter run flutter run
``` ```

View File

@@ -1,8 +1,3 @@
- Elmaradt és helyettesített órák (főoldal) What's new:
- Feljegyzések (főoldal)
- Faliújság (főoldal)
- Házi feladatok (főoldal)
- Dolgozatok (főoldal)
- Állapot jelző a képernyő alján
- Hét napjai dátumoknál (Például ha okt. 3. van és kaptál egy jegyet okt. 1-én akkor azt fogja kiírni, hogy Péntek és nem a hónapot, napot)
- Hibajavítások - Hibajavítások

View File

@@ -8,7 +8,6 @@
.buildlog/ .buildlog/
.history .history
.svn/ .svn/
build.sh
# IntelliJ related # IntelliJ related
*.iml *.iml

View File

@@ -0,0 +1,29 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View File

@@ -1,6 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="hu.filc.naplo"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="hu.filc.naplo">
<application android:label="Filc Napló" android:icon="@mipmap/ic_launcher" android:requestLegacyExternalStorage="true"> <application android:label="Filc Napló" android:icon="@mipmap/ic_launcher" android:requestLegacyExternalStorage="true">
<activity android:name=".MainActivity" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize"> <activity android:exported="true" android:name=".MainActivity" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize">
<meta-data android:name="io.flutter.embedding.android.NormalTheme" android:resource="@style/NormalTheme" /> <meta-data android:name="io.flutter.embedding.android.NormalTheme" android:resource="@style/NormalTheme" />
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -1,9 +1,9 @@
buildscript { buildscript {
ext.kotlin_version = '1.3.50' ext.kotlin_version = '1.6.10'
ext { ext {
compileSdkVersion = 30 compileSdkVersion = 31
targetSdkVersion = 30 targetSdkVersion = 31
appCompatVersion = "1.1.0" appCompatVersion = "1.1.0"
} }
@@ -33,8 +33,8 @@ subprojects {
afterEvaluate {project -> afterEvaluate {project ->
if (project.plugins.hasPlugin('android') || project.plugins.hasPlugin('android-library')) { if (project.plugins.hasPlugin('android') || project.plugins.hasPlugin('android-library')) {
android { android {
compileSdkVersion 30 compileSdkVersion 31
buildToolsVersion '30.0.3' buildToolsVersion '31.0.0'
} }
} }
} }

Binary file not shown.

View File

@@ -1,5 +0,0 @@
<svg width="20" height="22" viewBox="0 0 20 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M1 8L10 1L19 8V19C19 19.5304 18.7893 20.0391 18.4142 20.4142C18.0391 20.7893 17.5304 21 17 21H3C2.46957 21 1.96086 20.7893 1.58579 20.4142C1.21071 20.0391 1 19.5304 1 19V8Z"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>

Before

Width:  |  Height:  |  Size: 384 B

View File

@@ -1,3 +0,0 @@
<svg width="288" height="288" viewBox="0 0 288 288" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M159.653 207.26C136.598 217.973 115.171 217.858 100.771 216.821C83.6064 215.583 69.7824 210.888 63.72 206.77C59.976 204.236 54.8928 205.215 52.3584 208.959C49.824 212.703 50.8032 217.786 54.5472 220.32C64.3104 226.93 81.5616 231.84 99.5904 233.136C102.643 233.367 105.998 233.496 109.613 233.496C125.309 233.496 145.238 231.999 166.55 222.092C170.64 220.176 172.426 215.324 170.51 211.22C168.61 207.13 163.742 205.344 159.653 207.26ZM250.574 195.135C251.352 124.057 258.667 -10.2371 122.616 0.620487C-11.7072 11.4348 23.904 153.332 21.9024 200.852C20.1312 225.994 11.7936 256.723 0 288H36.3024C40.0464 274.752 42.7824 261.634 43.9632 249.149C46.152 250.675 48.4992 252.144 51.0048 253.555C55.0656 255.96 58.5648 259.143 62.2656 262.512C70.9056 270.389 80.712 279.331 99.864 280.44C101.146 280.512 102.442 280.555 103.709 280.555C123.091 280.555 136.339 272.074 146.995 265.263C152.093 261.994 156.499 259.171 160.646 257.818C172.44 254.131 182.736 248.17 190.426 240.595C191.635 239.415 192.744 238.191 193.795 236.952C198.086 252.663 203.962 270.36 210.47 288H288C269.366 259.258 250.171 231.077 250.574 195.135ZM34.9056 156.471V156.457C33.5664 133.258 44.6688 113.761 59.688 112.882C74.7072 112.004 87.9696 130.105 89.3088 153.289C89.3088 153.303 89.3088 153.303 89.3088 153.303C89.3808 154.556 89.4096 155.794 89.4096 157.018C84.6576 158.213 80.352 159.956 76.5072 161.986C76.4928 161.813 76.4784 161.641 76.464 161.468V161.453C75.1824 148.292 68.1552 138.356 60.7536 139.249C53.3664 140.156 48.4128 151.561 49.7088 164.722C50.2704 170.468 51.912 175.594 54.216 179.482C53.64 179.929 52.0272 181.109 50.1696 182.477C48.7728 183.5 47.0592 184.738 45.0144 186.25C39.4272 178.906 35.5968 168.365 34.9056 156.471ZM187.099 213.797C186.566 226.051 170.525 237.586 155.722 242.208L155.635 242.237C149.472 244.239 143.986 247.752 138.168 251.467C128.405 257.717 118.296 264.183 103.709 264.183C102.744 264.183 101.765 264.154 100.8 264.096C87.4368 263.319 81.1872 257.631 73.2816 250.416C69.1056 246.615 64.7856 242.669 59.2272 239.415L59.0976 239.343C47.088 232.56 39.6288 224.136 39.1536 216.778C38.9232 213.135 40.5504 209.967 43.992 207.389C51.48 201.773 56.5056 198.101 59.8176 195.668C63.504 192.975 64.6272 192.168 65.448 191.376C66.0384 190.829 66.672 190.21 67.3632 189.533C74.232 182.852 85.7376 171.663 103.406 171.663C114.206 171.663 126.158 175.825 138.888 184.004C144.878 187.906 150.106 189.706 156.715 191.996C161.251 193.565 166.406 195.336 173.304 198.288L173.419 198.346C179.842 200.981 187.445 205.805 187.099 213.783V213.797ZM183.557 184.997C182.318 184.378 181.022 183.788 179.683 183.226C173.462 180.576 168.48 178.762 164.347 177.322C166.637 172.858 168.048 167.285 168.178 161.223C168.494 146.492 161.064 134.525 151.589 134.511C142.099 134.482 134.165 146.405 133.848 161.137C133.834 161.626 133.834 162.101 133.848 162.577C128.002 159.898 122.256 157.94 116.64 156.745C116.611 156.183 116.582 155.636 116.568 155.074V155.06C116.021 128.233 132.494 106.014 153.346 105.452C174.197 104.89 191.549 126.174 192.096 153.015V153.029C192.341 165.169 189.101 176.329 183.557 184.997Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 3.2 KiB

19
filcnaplo/build.sh Normal file
View File

@@ -0,0 +1,19 @@
#!/bin/fish
# With build number
function get_version_bn
cat pubspec.yaml | grep version: | cut -d' ' -f2
end
function get_version
cat pubspec.yaml | grep version: | cut -d' ' -f2 | cut -d+ -f1
end
if test -e /mnt/enc/keys/filc3.properties
set -x ANDROID_SIGNING /mnt/enc/keys/filc3.properties
end
flutter build apk --release --dart-define=APPVER=(get_version) --no-tree-shake-icons
cp -v "build/app/outputs/flutter-apk/app-release.apk" ~/"Desktop/hu.filc.naplo_"(get_version_bn).apk
notify-send "Flutter" "Apk build done."

View File

@@ -21,6 +21,6 @@
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.0</string> <string>1.0</string>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>9.0</string> <string>11.0</string>
</dict> </dict>
</plist> </plist>

View File

@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project # Uncomment this line to define a global platform for your project
# platform :ios, '9.0' # platform :ios, '11.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency. # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true' ENV['COCOAPODS_DISABLE_STATS'] = 'true'
@@ -32,6 +32,8 @@ target 'Runner' do
use_modular_headers! use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
pod 'DKImagePickerController/PhotoGallery', :git => 'https://github.com/zhangao0086/DKImagePickerController.git'
end end
post_install do |installer| post_install do |installer|

View File

@@ -1,12 +1,15 @@
PODS: PODS:
- DKImagePickerController/Core (4.3.2): - connectivity_plus (0.0.1):
- Flutter
- ReachabilitySwift
- DKImagePickerController/Core (4.3.4):
- DKImagePickerController/ImageDataManager - DKImagePickerController/ImageDataManager
- DKImagePickerController/Resource - DKImagePickerController/Resource
- DKImagePickerController/ImageDataManager (4.3.2) - DKImagePickerController/ImageDataManager (4.3.4)
- DKImagePickerController/PhotoGallery (4.3.2): - DKImagePickerController/PhotoGallery (4.3.4):
- DKImagePickerController/Core - DKImagePickerController/Core
- DKPhotoGallery - DKPhotoGallery
- DKImagePickerController/Resource (4.3.2) - DKImagePickerController/Resource (4.3.4)
- DKPhotoGallery (0.0.17): - DKPhotoGallery (0.0.17):
- DKPhotoGallery/Core (= 0.0.17) - DKPhotoGallery/Core (= 0.0.17)
- DKPhotoGallery/Model (= 0.0.17) - DKPhotoGallery/Model (= 0.0.17)
@@ -41,45 +44,52 @@ PODS:
- FMDB/standard (2.7.5) - FMDB/standard (2.7.5)
- open_file (0.0.1): - open_file (0.0.1):
- Flutter - Flutter
- package_info_plus (0.4.5): - path_provider_ios (0.0.1):
- Flutter - Flutter
- path_provider (0.0.1): - permission_handler_apple (9.0.4):
- Flutter - Flutter
- "permission_handler (5.1.0+2)": - quick_actions_ios (0.0.1):
- Flutter - Flutter
- SDWebImage (5.11.1): - ReachabilitySwift (5.0.0)
- SDWebImage/Core (= 5.11.1) - SDWebImage (5.13.2):
- SDWebImage/Core (5.11.1) - SDWebImage/Core (= 5.13.2)
- SDWebImage/Core (5.13.2)
- share_plus (0.0.1): - share_plus (0.0.1):
- Flutter - Flutter
- sqflite (0.0.2): - sqflite (0.0.2):
- Flutter - Flutter
- FMDB (>= 2.7.5) - FMDB (>= 2.7.5)
- SwiftyGif (5.4.0) - SwiftyGif (5.4.3)
- url_launcher (0.0.1): - url_launcher_ios (0.0.1):
- Flutter - Flutter
DEPENDENCIES: DEPENDENCIES:
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- DKImagePickerController/PhotoGallery (from `https://github.com/zhangao0086/DKImagePickerController.git`)
- file_picker (from `.symlinks/plugins/file_picker/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`)
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
- flutter_custom_tabs (from `.symlinks/plugins/flutter_custom_tabs/ios`) - flutter_custom_tabs (from `.symlinks/plugins/flutter_custom_tabs/ios`)
- open_file (from `.symlinks/plugins/open_file/ios`) - open_file (from `.symlinks/plugins/open_file/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
- path_provider (from `.symlinks/plugins/path_provider/ios`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- permission_handler (from `.symlinks/plugins/permission_handler/ios`) - quick_actions_ios (from `.symlinks/plugins/quick_actions_ios/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`)
- sqflite (from `.symlinks/plugins/sqflite/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`)
- url_launcher (from `.symlinks/plugins/url_launcher/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
SPEC REPOS: SPEC REPOS:
trunk: trunk:
- DKImagePickerController
- DKPhotoGallery - DKPhotoGallery
- FMDB - FMDB
- ReachabilitySwift
- SDWebImage - SDWebImage
- SwiftyGif - SwiftyGif
EXTERNAL SOURCES: EXTERNAL SOURCES:
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
DKImagePickerController:
:git: https://github.com/zhangao0086/DKImagePickerController.git
file_picker: file_picker:
:path: ".symlinks/plugins/file_picker/ios" :path: ".symlinks/plugins/file_picker/ios"
Flutter: Flutter:
@@ -88,36 +98,43 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_custom_tabs/ios" :path: ".symlinks/plugins/flutter_custom_tabs/ios"
open_file: open_file:
:path: ".symlinks/plugins/open_file/ios" :path: ".symlinks/plugins/open_file/ios"
package_info_plus: path_provider_ios:
:path: ".symlinks/plugins/package_info_plus/ios" :path: ".symlinks/plugins/path_provider_ios/ios"
path_provider: permission_handler_apple:
:path: ".symlinks/plugins/path_provider/ios" :path: ".symlinks/plugins/permission_handler_apple/ios"
permission_handler: quick_actions_ios:
:path: ".symlinks/plugins/permission_handler/ios" :path: ".symlinks/plugins/quick_actions_ios/ios"
share_plus: share_plus:
:path: ".symlinks/plugins/share_plus/ios" :path: ".symlinks/plugins/share_plus/ios"
sqflite: sqflite:
:path: ".symlinks/plugins/sqflite/ios" :path: ".symlinks/plugins/sqflite/ios"
url_launcher: url_launcher_ios:
:path: ".symlinks/plugins/url_launcher/ios" :path: ".symlinks/plugins/url_launcher_ios/ios"
CHECKOUT OPTIONS:
DKImagePickerController:
:commit: a727e44718a67e300089174e5591166045815ba4
:git: https://github.com/zhangao0086/DKImagePickerController.git
SPEC CHECKSUMS: SPEC CHECKSUMS:
DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1 file_picker: 817ab1d8cd2da9d2da412a417162deee3500fc95
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_custom_tabs: 7a10a08686955cb748e5d26e0ae586d30689bf89 flutter_custom_tabs: 7a10a08686955cb748e5d26e0ae586d30689bf89
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
open_file: 02eb5cb6b21264bd3a696876f5afbfb7ca4f4b7d open_file: 02eb5cb6b21264bd3a696876f5afbfb7ca4f4b7d
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce
permission_handler: ccb20a9fad0ee9b1314a52b70b76b473c5f8dab0 quick_actions_ios: 5ec8f5f1ae81512ac803fe10c197ebb875767a9e
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
SDWebImage: 72f86271a6f3139cc7e4a89220946489d4b9a866
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68 share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
SwiftyGif: 5d4af95df24caf1c570dbbcb32a3b8a0763bc6d7 SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de
PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c PODFILE CHECKSUM: 862f939bb7e5390bdb8b2534eb81a9457ea9fbdc
COCOAPODS: 1.11.0 COCOAPODS: 1.11.3

View File

@@ -3,7 +3,7 @@
archiveVersion = 1; archiveVersion = 1;
classes = { classes = {
}; };
objectVersion = 46; objectVersion = 51;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
@@ -76,7 +76,6 @@
98578F0EBCC6D3FF8391AAEB /* Pods-Runner.release.xcconfig */, 98578F0EBCC6D3FF8391AAEB /* Pods-Runner.release.xcconfig */,
707F8089D970F81C480F73C4 /* Pods-Runner.profile.xcconfig */, 707F8089D970F81C480F73C4 /* Pods-Runner.profile.xcconfig */,
); );
name = Pods;
path = Pods; path = Pods;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@@ -156,7 +155,7 @@
97C146E61CF9000F007C117D /* Project object */ = { 97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastUpgradeCheck = 1020; LastUpgradeCheck = 1300;
ORGANIZATIONNAME = ""; ORGANIZATIONNAME = "";
TargetAttributes = { TargetAttributes = {
97C146ED1CF9000F007C117D = { 97C146ED1CF9000F007C117D = {
@@ -322,6 +321,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -340,7 +340,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
@@ -358,7 +358,10 @@
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = hu.filc.naplo; PRODUCT_BUNDLE_IDENTIFIER = hu.filc.naplo;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -390,6 +393,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -414,7 +418,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@@ -445,6 +449,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -463,10 +468,11 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
@@ -482,7 +488,10 @@
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = hu.filc.naplo; PRODUCT_BUNDLE_IDENTIFIER = hu.filc.naplo;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -501,7 +510,10 @@
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = hu.filc.naplo; PRODUCT_BUNDLE_IDENTIFIER = hu.filc.naplo;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1020" LastUpgradeVersion = "1300"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
@@ -27,8 +27,6 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion> <MacroExpansion>
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
@@ -38,8 +36,8 @@
ReferencedContainer = "container:Runner.xcodeproj"> ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
<AdditionalOptions> <Testables>
</AdditionalOptions> </Testables>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug" buildConfiguration = "Debug"
@@ -61,8 +59,6 @@
ReferencedContainer = "container:Runner.xcodeproj"> ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Profile" buildConfiguration = "Profile"

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "icon.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 570 B

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "icon.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "icon.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 B

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "icon.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 B

View File

@@ -52,5 +52,7 @@
<string>The app requires the photo library to set a custom profile picture.</string> <string>The app requires the photo library to set a custom profile picture.</string>
<key>ITSAppUsesNonExemptEncryption</key> <key>ITSAppUsesNonExemptEncryption</key>
<false/> <false/>
</dict> <key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
</dict>
</plist> </plist>

View File

@@ -1,4 +1,7 @@
// ignore_for_file: avoid_print
import 'dart:convert'; import 'dart:convert';
import 'dart:developer';
import 'package:filcnaplo/models/config.dart'; import 'package:filcnaplo/models/config.dart';
import 'package:filcnaplo/models/news.dart'; import 'package:filcnaplo/models/news.dart';
@@ -11,23 +14,23 @@ import 'package:connectivity_plus/connectivity_plus.dart';
class FilcAPI { class FilcAPI {
// Public API // Public API
static const SCHOOL_LIST = "https://filcnaplo.hu/v2/school_list.json"; static const schoolList = "https://filcnaplo.hu/v2/school_list.json";
static const NEWS = "https://filcnaplo.hu/v2/news.json"; static const news = "https://filcnaplo.hu/v2/news.json";
static const SUPPORTERS = "https://filcnaplo.hu/v2/supporters.json"; static const supporters = "https://filcnaplo.hu/v2/supporters.json";
// Private API // Private API
static const CONFIG = "https://api.filcnaplo.hu/config"; static const config = "https://api.filcnaplo.hu/config";
static const REPORT = "https://api.filcnaplo.hu/report"; static const reportApi = "https://api.filcnaplo.hu/report";
// Updates // Updates
static const REPO = "filc/naplo"; static const repo = "filc/naplo";
static const RELEASES = "https://api.github.com/repos/$REPO/releases"; static const releases = "https://api.github.com/repos/$repo/releases";
static Future<bool> checkConnectivity() async => (await Connectivity().checkConnectivity()) != ConnectivityResult.none; static Future<bool> checkConnectivity() async => (await Connectivity().checkConnectivity()) != ConnectivityResult.none;
static Future<List<School>?> getSchools() async { static Future<List<School>?> getSchools() async {
try { try {
http.Response res = await http.get(Uri.parse(SCHOOL_LIST)); http.Response res = await http.get(Uri.parse(schoolList));
if (res.statusCode == 200) { if (res.statusCode == 200) {
List<School> schools = (jsonDecode(res.body) as List).cast<Map>().map((json) => School.fromJson(json)).toList(); List<School> schools = (jsonDecode(res.body) as List).cast<Map>().map((json) => School.fromJson(json)).toList();
@@ -43,32 +46,39 @@ class FilcAPI {
} catch (error) { } catch (error) {
print("ERROR: FilcAPI.getSchools: $error"); print("ERROR: FilcAPI.getSchools: $error");
} }
return null;
} }
static Future<Config?> getConfig(SettingsProvider settings) async { static Future<Config?> getConfig(SettingsProvider settings) async {
final userAgent = SettingsProvider.defaultSettings().config.userAgent;
Map<String, String> headers = { Map<String, String> headers = {
"x-filc-id": settings.xFilcId, "x-filc-id": settings.xFilcId,
"user-agent": SettingsProvider.defaultSettings().config.userAgent, "user-agent": userAgent,
}; };
log("[CONFIG] x-filc-id: \"${settings.xFilcId}\"");
log("[CONFIG] user-agent: \"$userAgent\"");
try { try {
http.Response res = await http.get(Uri.parse(CONFIG), headers: headers); http.Response res = await http.get(Uri.parse(config), headers: headers);
if (res.statusCode == 200) { if (res.statusCode == 200) {
return Config.fromJson(jsonDecode(res.body)); return Config.fromJson(jsonDecode(res.body));
} else if (res.statusCode == 429) { } else if (res.statusCode == 429) {
res = await http.get(Uri.parse(CONFIG)); res = await http.get(Uri.parse(config));
if (res.statusCode == 200) return Config.fromJson(jsonDecode(res.body)); if (res.statusCode == 200) return Config.fromJson(jsonDecode(res.body));
} }
throw "HTTP ${res.statusCode}: ${res.body}"; throw "HTTP ${res.statusCode}: ${res.body}";
} catch (error) { } catch (error) {
print("ERROR: FilcAPI.getConfig: $error"); print("ERROR: FilcAPI.getConfig: $error");
} }
return null;
} }
static Future<List<News>?> getNews() async { static Future<List<News>?> getNews() async {
try { try {
http.Response res = await http.get(Uri.parse(NEWS)); http.Response res = await http.get(Uri.parse(news));
if (res.statusCode == 200) { if (res.statusCode == 200) {
return (jsonDecode(res.body) as List).cast<Map>().map((e) => News.fromJson(e)).toList(); return (jsonDecode(res.body) as List).cast<Map>().map((e) => News.fromJson(e)).toList();
@@ -78,11 +88,12 @@ class FilcAPI {
} catch (error) { } catch (error) {
print("ERROR: FilcAPI.getNews: $error"); print("ERROR: FilcAPI.getNews: $error");
} }
return null;
} }
static Future<Supporters?> getSupporters() async { static Future<Supporters?> getSupporters() async {
try { try {
http.Response res = await http.get(Uri.parse(SUPPORTERS)); http.Response res = await http.get(Uri.parse(supporters));
if (res.statusCode == 200) { if (res.statusCode == 200) {
return Supporters.fromJson(jsonDecode(res.body)); return Supporters.fromJson(jsonDecode(res.body));
@@ -92,11 +103,12 @@ class FilcAPI {
} catch (error) { } catch (error) {
print("ERROR: FilcAPI.getSupporters: $error"); print("ERROR: FilcAPI.getSupporters: $error");
} }
return null;
} }
static Future<List<Release>?> getReleases() async { static Future<List<Release>?> getReleases() async {
try { try {
http.Response res = await http.get(Uri.parse(RELEASES)); http.Response res = await http.get(Uri.parse(releases));
if (res.statusCode == 200) { if (res.statusCode == 200) {
return (jsonDecode(res.body) as List).cast<Map>().map((e) => Release.fromJson(e)).toList(); return (jsonDecode(res.body) as List).cast<Map>().map((e) => Release.fromJson(e)).toList();
@@ -106,10 +118,11 @@ class FilcAPI {
} catch (error) { } catch (error) {
print("ERROR: FilcAPI.getReleases: $error"); print("ERROR: FilcAPI.getReleases: $error");
} }
return null;
} }
static Future<http.StreamedResponse?> downloadRelease(Release release) { static Future<http.StreamedResponse?> downloadRelease(Release release) {
if (release.downloads.length > 0) { if (release.downloads.isNotEmpty) {
try { try {
var client = http.Client(); var client = http.Client();
var request = http.Request('GET', Uri.parse(release.downloads.first)); var request = http.Request('GET', Uri.parse(release.downloads.first));
@@ -124,7 +137,7 @@ class FilcAPI {
static Future<void> sendReport(ErrorReport report) async { static Future<void> sendReport(ErrorReport report) async {
try { try {
http.Response res = await http.post(Uri.parse(REPORT), body: { http.Response res = await http.post(Uri.parse(reportApi), body: {
"os": report.os, "os": report.os,
"version": report.version, "version": report.version,
"error": report.error, "error": report.error,

View File

@@ -1,3 +1,5 @@
// ignore_for_file: avoid_print, use_build_context_synchronously
import 'package:filcnaplo/utils/jwt.dart'; import 'package:filcnaplo/utils/jwt.dart';
import 'package:filcnaplo_kreta_api/providers/absence_provider.dart'; import 'package:filcnaplo_kreta_api/providers/absence_provider.dart';
import 'package:filcnaplo_kreta_api/providers/event_provider.dart'; import 'package:filcnaplo_kreta_api/providers/event_provider.dart';
@@ -22,8 +24,8 @@ import 'package:filcnaplo/api/nonce.dart';
enum LoginState { missingFields, invalidGrant, failed, normal, inProgress, success } enum LoginState { missingFields, invalidGrant, failed, normal, inProgress, success }
Nonce getNonce(BuildContext context, String nonce, String username, String instituteCode) { Nonce getNonce(BuildContext context, String nonce, String username, String instituteCode) {
Nonce nonceEncoder = Nonce(key: [53, 75, 109, 112, 109, 103, 100, 53, 102, 74], nonce: nonce); Nonce nonceEncoder = Nonce(key: [98, 97, 83, 115, 120, 79, 119, 108, 85, 49, 106, 77], nonce: nonce);
nonceEncoder.encode(username.toLowerCase() + instituteCode.toLowerCase() + nonce); nonceEncoder.encode(instituteCode.toUpperCase() + nonce + username.toUpperCase());
return nonceEncoder; return nonceEncoder;
} }

View File

@@ -19,7 +19,7 @@ class Nonce {
return { return {
"X-Authorizationpolicy-Nonce": nonce, "X-Authorizationpolicy-Nonce": nonce,
"X-Authorizationpolicy-Key": encoded ?? "", "X-Authorizationpolicy-Key": encoded ?? "",
"X-Authorizationpolicy-Version": "v1", "X-Authorizationpolicy-Version": "v2",
}; };
} }
} }

View File

@@ -3,6 +3,7 @@ import 'dart:io';
import 'package:filcnaplo/database/query.dart'; import 'package:filcnaplo/database/query.dart';
import 'package:filcnaplo/database/store.dart'; import 'package:filcnaplo/database/store.dart';
import 'package:sqflite/sqflite.dart'; import 'package:sqflite/sqflite.dart';
// ignore: depend_on_referenced_packages
import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart';
class DatabaseProvider { class DatabaseProvider {

View File

@@ -1,3 +1,5 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:math'; import 'dart:math';
import 'package:filcnaplo/api/client.dart'; import 'package:filcnaplo/api/client.dart';
@@ -72,10 +74,11 @@ class NewsProvider extends ChangeNotifier {
Provider.of<SettingsProvider>(_context, listen: false).update(_context, newsState: _state); Provider.of<SettingsProvider>(_context, listen: false).update(_context, newsState: _state);
if (_fresh > 0) if (_fresh > 0) {
show = true; show = true;
else } else {
show = false; show = false;
}
notifyListeners(); notifyListeners();
} }

View File

@@ -5,14 +5,14 @@ import 'package:http/http.dart' as http;
enum Status { network, maintenance, syncing } enum Status { network, maintenance, syncing }
class StatusProvider extends ChangeNotifier { class StatusProvider extends ChangeNotifier {
List<Status> _stack = []; final List<Status> _stack = [];
double _progress = 0.0; double _progress = 0.0;
StatusProvider() { StatusProvider() {
_handleNetworkChanges(); _handleNetworkChanges();
} }
Status? getStatus() => _stack.length > 0 ? _stack[0] : null; Status? getStatus() => _stack.isNotEmpty ? _stack[0] : null;
// Status progress from 0.0 to 1.0 // Status progress from 0.0 to 1.0
double get progress => _progress; double get progress => _progress;
@@ -64,10 +64,12 @@ class StatusProvider extends ChangeNotifier {
if (_progress == 1.0) { if (_progress == 1.0) {
notifyListeners(); notifyListeners();
// Wait for animation // Wait for animation
Future.delayed(Duration(milliseconds: 250), () { Future.delayed(const Duration(milliseconds: 250), () {
_stack.remove(Status.syncing); _stack.remove(Status.syncing);
notifyListeners(); notifyListeners();
}); });
} else if (progress != prev) notifyListeners(); } else if (progress != prev) {
notifyListeners();
}
} }
} }

View File

@@ -1,3 +1,5 @@
// ignore_for_file: use_build_context_synchronously
import 'package:filcnaplo/api/providers/database_provider.dart'; import 'package:filcnaplo/api/providers/database_provider.dart';
import 'package:filcnaplo/api/providers/status_provider.dart'; import 'package:filcnaplo/api/providers/status_provider.dart';
import 'package:filcnaplo/api/providers/user_provider.dart'; import 'package:filcnaplo/api/providers/user_provider.dart';
@@ -24,6 +26,7 @@ Future<void> syncAll(BuildContext context) {
// Lock // Lock
lock = true; lock = true;
// ignore: avoid_print
print("INFO Syncing all"); print("INFO Syncing all");
UserProvider user = Provider.of<UserProvider>(context, listen: false); UserProvider user = Provider.of<UserProvider>(context, listen: false);
@@ -32,24 +35,24 @@ Future<void> syncAll(BuildContext context) {
List<Future<void>> tasks = []; List<Future<void>> tasks = [];
int taski = 0; int taski = 0;
Future<void> _syncStatus(Future<void> future) async { Future<void> syncStatus(Future<void> future) async {
await future.onError((error, stackTrace) => null); await future.onError((error, stackTrace) => null);
taski++; taski++;
statusProvider.triggerSync(current: taski, max: tasks.length); statusProvider.triggerSync(current: taski, max: tasks.length);
} }
tasks = [ tasks = [
_syncStatus(Provider.of<GradeProvider>(context, listen: false).fetch()), syncStatus(Provider.of<GradeProvider>(context, listen: false).fetch()),
_syncStatus(Provider.of<TimetableProvider>(context, listen: false).fetch(week: Week.current())), syncStatus(Provider.of<TimetableProvider>(context, listen: false).fetch(week: Week.current())),
_syncStatus(Provider.of<ExamProvider>(context, listen: false).fetch()), syncStatus(Provider.of<ExamProvider>(context, listen: false).fetch()),
_syncStatus(Provider.of<HomeworkProvider>(context, listen: false).fetch(from: DateTime.now().subtract(Duration(days: 30)))), syncStatus(Provider.of<HomeworkProvider>(context, listen: false).fetch(from: DateTime.now().subtract(const Duration(days: 30)))),
_syncStatus(Provider.of<MessageProvider>(context, listen: false).fetchAll()), syncStatus(Provider.of<MessageProvider>(context, listen: false).fetchAll()),
_syncStatus(Provider.of<NoteProvider>(context, listen: false).fetch()), syncStatus(Provider.of<NoteProvider>(context, listen: false).fetch()),
_syncStatus(Provider.of<EventProvider>(context, listen: false).fetch()), syncStatus(Provider.of<EventProvider>(context, listen: false).fetch()),
_syncStatus(Provider.of<AbsenceProvider>(context, listen: false).fetch()), syncStatus(Provider.of<AbsenceProvider>(context, listen: false).fetch()),
// Sync student // Sync student
_syncStatus(() async { syncStatus(() async {
if (user.user == null) return; if (user.user == null) return;
Map? studentJson = await Provider.of<KretaClient>(context, listen: false).getAPI(KretaAPI.student(user.instituteCode!)); Map? studentJson = await Provider.of<KretaClient>(context, listen: false).getAPI(KretaAPI.student(user.instituteCode!));
if (studentJson == null) return; if (studentJson == null) return;

View File

@@ -3,14 +3,12 @@ import 'dart:io';
import 'package:filcnaplo/api/client.dart'; import 'package:filcnaplo/api/client.dart';
import 'package:filcnaplo/models/release.dart'; import 'package:filcnaplo/models/release.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';
class UpdateProvider extends ChangeNotifier { class UpdateProvider extends ChangeNotifier {
// Private // Private
late List<Release> _releases; late List<Release> _releases;
bool _available = false; bool _available = false;
bool get available => _available && _releases.length > 0; bool get available => _available && _releases.isNotEmpty;
PackageInfo? _packageInfo;
// Public // Public
List<Release> get releases => _releases; List<Release> get releases => _releases;
@@ -20,9 +18,10 @@ class UpdateProvider extends ChangeNotifier {
required BuildContext context, required BuildContext context,
}) { }) {
_releases = List.castFrom(initialReleases); _releases = List.castFrom(initialReleases);
PackageInfo.fromPlatform().then((value) => _packageInfo = value);
} }
static const currentVersion = String.fromEnvironment("APPVER", defaultValue: "1.0");
Future<void> fetch() async { Future<void> fetch() async {
if (!Platform.isAndroid) return; if (!Platform.isAndroid) return;
@@ -30,8 +29,9 @@ class UpdateProvider extends ChangeNotifier {
_releases.sort((a, b) => -a.version.compareTo(b.version)); _releases.sort((a, b) => -a.version.compareTo(b.version));
// Check for new releases // Check for new releases
if (_releases.length > 0) { if (_releases.isNotEmpty) {
_available = _packageInfo != null && _releases.first.version.compareTo(Version.fromString(_packageInfo?.version ?? "")) == 1; _available = _releases.first.version.compareTo(Version.fromString(currentVersion)) == 1;
// ignore: avoid_print
if (_available) print("INFO: New update: ${releases.first.version}"); if (_available) print("INFO: New update: ${releases.first.version}");
notifyListeners(); notifyListeners();
} }

View File

@@ -3,7 +3,7 @@ import 'package:filcnaplo_kreta_api/models/student.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
class UserProvider with ChangeNotifier { class UserProvider with ChangeNotifier {
Map<String, User> _users = {}; final Map<String, User> _users = {};
String? _selectedUserId; String? _selectedUserId;
User? get user => _users[_selectedUserId]; User? get user => _users[_selectedUserId];
@@ -23,7 +23,9 @@ class UserProvider with ChangeNotifier {
void addUser(User user) { void addUser(User user) {
_users[user.id] = user; _users[user.id] = user;
print("DEBUG: Added User: ${user.id} ${user.name}"); if (kDebugMode) {
print("DEBUG: Added User: ${user.id}");
}
} }
void removeUser(String userId) { void removeUser(String userId) {

View File

@@ -1,6 +1,7 @@
import 'dart:io'; import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:dynamic_color/dynamic_color.dart';
import 'package:filcnaplo/api/client.dart'; import 'package:filcnaplo/api/client.dart';
import 'package:filcnaplo/api/providers/news_provider.dart'; import 'package:filcnaplo/api/providers/news_provider.dart';
import 'package:filcnaplo/api/providers/database_provider.dart'; import 'package:filcnaplo/api/providers/database_provider.dart';
@@ -18,6 +19,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:i18n_extension/i18n_widget.dart'; import 'package:i18n_extension/i18n_widget.dart';
import 'package:material_color_utilities/palettes/core_palette.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
// Providers // Providers
@@ -41,7 +43,7 @@ class App extends StatelessWidget {
final DatabaseProvider database; final DatabaseProvider database;
App({Key? key, required this.database, required this.settings, required this.user}) : super(key: key) { App({Key? key, required this.database, required this.settings, required this.user}) : super(key: key) {
if (user.getUsers().length > 0) user.setUser(user.getUsers().first.id); if (user.getUsers().isNotEmpty) user.setUser(user.getUsers().first.id);
} }
@override @override
@@ -51,12 +53,14 @@ class App extends StatelessWidget {
// Set high refresh mode #28 // Set high refresh mode #28
if (Platform.isAndroid) FlutterDisplayMode.setHighRefreshRate(); if (Platform.isAndroid) FlutterDisplayMode.setHighRefreshRate();
WidgetsBinding.instance?.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
FilcAPI.getConfig(settings).then((Config? config) { FilcAPI.getConfig(settings).then((Config? config) {
if (config != null) settings.update(context, database: database, config: config); if (config != null) settings.update(context, database: database, config: config);
}); });
}); });
CorePalette? corePalette;
return I18n( return I18n(
initialLocale: Locale(settings.language, settings.language.toUpperCase()), initialLocale: Locale(settings.language, settings.language.toUpperCase()),
child: MultiProvider( child: MultiProvider(
@@ -84,6 +88,10 @@ class App extends StatelessWidget {
], ],
child: Consumer<ThemeModeObserver>( child: Consumer<ThemeModeObserver>(
builder: (context, themeMode, child) { builder: (context, themeMode, child) {
return FutureBuilder<CorePalette?>(
future: DynamicColorPlugin.getCorePalette(),
builder: (context, snapshot) {
corePalette = snapshot.data;
return MaterialApp( return MaterialApp(
builder: (context, child) { builder: (context, child) {
// Limit font size scaling to 1.0 // Limit font size scaling to 1.0
@@ -96,21 +104,21 @@ class App extends StatelessWidget {
}, },
title: "Filc Napló", title: "Filc Napló",
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
theme: AppTheme.lightTheme(context), theme: AppTheme.lightTheme(context, palette: corePalette),
darkTheme: AppTheme.darkTheme(context), darkTheme: AppTheme.darkTheme(context, palette: corePalette),
themeMode: themeMode.themeMode, themeMode: themeMode.themeMode,
localizationsDelegates: [ localizationsDelegates: const [
GlobalMaterialLocalizations.delegate, GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate, GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate, GlobalCupertinoLocalizations.delegate,
], ],
supportedLocales: [ supportedLocales: const [
const Locale('en', 'EN'), Locale('en', 'EN'),
const Locale('hu', 'HU'), Locale('hu', 'HU'),
const Locale('de', 'DE'), Locale('de', 'DE'),
], ],
localeListResolutionCallback: (locales, supported) { localeListResolutionCallback: (locales, supported) {
Locale locale = Locale('hu', 'HU'); Locale locale = const Locale('hu', 'HU');
for (var loc in locales ?? []) { for (var loc in locales ?? []) {
if (supported.contains(loc)) { if (supported.contains(loc)) {
@@ -122,7 +130,10 @@ class App extends StatelessWidget {
return locale; return locale;
}, },
onGenerateRoute: (settings) => rootNavigator(settings), onGenerateRoute: (settings) => rootNavigator(settings),
initialRoute: user.getUsers().length > 0 ? "navigation" : "login"); initialRoute: user.getUsers().isNotEmpty ? "navigation" : "login",
);
},
);
}, },
), ),
), ),
@@ -133,16 +144,17 @@ class App extends StatelessWidget {
// if platform == android || platform == ios // if platform == android || platform == ios
switch (route.name) { switch (route.name) {
case "login_back": case "login_back":
return CupertinoPageRoute(builder: (context) => LoginScreen(back: true)); return CupertinoPageRoute(builder: (context) => const LoginScreen(back: true));
case "login": case "login":
return _rootRoute(LoginScreen()); return _rootRoute(const LoginScreen());
case "navigation": case "navigation":
return _rootRoute(NavigationScreen()); return _rootRoute(const NavigationScreen());
case "login_to_navigation": case "login_to_navigation":
return loginRoute(NavigationScreen()); return loginRoute(const NavigationScreen());
case "settings": case "settings":
return settingsRoute(SettingsScreen()); return settingsRoute(const SettingsScreen());
} }
return null;
// else if platform == windows || ... // else if platform == windows || ...
} }

View File

@@ -1,10 +1,31 @@
// ignore_for_file: avoid_print
import 'dart:io'; import 'dart:io';
import 'package:filcnaplo/database/struct.dart'; import 'package:filcnaplo/database/struct.dart';
import 'package:filcnaplo/models/settings.dart'; import 'package:filcnaplo/models/settings.dart';
import 'package:sqflite/sqflite.dart'; import 'package:sqflite/sqflite.dart';
// ignore: depend_on_referenced_packages
import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart';
const settingsDB = DatabaseStruct("settings", {
"language": String, "start_page": int, "rounding": int, "theme": int, "accent_color": int, "news": int, "news_state": int, "developer_mode": int,
"update_channel": int, "config": String, // general
"grade_color1": int, "grade_color2": int, "grade_color3": int, "grade_color4": int, "grade_color5": int, // grade colors
"vibration_strength": int, "ab_weeks": int, "swap_ab_weeks": int,
"notifications": int, "notifications_bitfield": int, "notification_poll_interval": int, // notifications
"x_filc_id": String, "graph_class_avg": int, "presentation_mode": int, "bell_delay": int, "bell_delay_enabled": int,
});
const usersDB = DatabaseStruct(
"users", {"id": String, "name": String, "username": String, "password": String, "institute_code": String, "student": String, "role": int});
const userDataDB = DatabaseStruct("user_data", {
"id": String, "grades": String, "timetable": String, "exams": String, "homework": String, "messages": String, "notes": String,
"events": String, "absences": String, "group_averages": String,
// "subject_lesson_count": String, // non kreta data
});
Future<void> createTable(Database db, DatabaseStruct struct) => db.execute("CREATE TABLE IF NOT EXISTS ${struct.table} ($struct)");
Future<Database> initDB() async { Future<Database> initDB() async {
Database db; Database db;
@@ -15,12 +36,9 @@ Future<Database> initDB() async {
db = await openDatabase("app.db"); db = await openDatabase("app.db");
} }
var settingsDB = await createSettingsTable(db); await createTable(db, settingsDB);
await createTable(db, usersDB);
// Create table Users await createTable(db, userDataDB);
var usersDB = await createUsersTable(db);
await db.execute("CREATE TABLE IF NOT EXISTS user_data ("
"id TEXT NOT NULL, grades TEXT, timetable TEXT, exams TEXT, homework TEXT, messages TEXT, notes TEXT, events TEXT, absences TEXT)");
if ((await db.rawQuery("SELECT COUNT(*) FROM settings"))[0].values.first == 0) { if ((await db.rawQuery("SELECT COUNT(*) FROM settings"))[0].values.first == 0) {
// Set default values for table Settings // Set default values for table Settings
@@ -28,87 +46,81 @@ Future<Database> initDB() async {
} }
// Migrate Databases // Migrate Databases
await migrateDB(db, "settings", settingsDB.struct.keys, SettingsProvider.defaultSettings().toMap(), createSettingsTable); try {
await migrateDB(db, "users", usersDB.struct.keys, {"role": 0}, createUsersTable); await migrateDB(
db,
struct: settingsDB,
defaultValues: SettingsProvider.defaultSettings().toMap(),
);
await migrateDB(
db,
struct: usersDB,
defaultValues: {"role": 0},
);
await migrateDB(db, struct: userDataDB, defaultValues: {
"grades": "[]", "timetable": "[]", "exams": "[]", "homework": "[]", "messages": "[]", "notes": "[]", "events": "[]", "absences": "[]",
"group_averages": "[]",
// "subject_lesson_count": "{}", // non kreta data
});
} catch (error) {
print("ERROR: migrateDB: $error");
}
return db; return db;
} }
Future<DatabaseStruct> createSettingsTable(Database db) async {
var settingsDB = DatabaseStruct({
"language": String, "start_page": int, "rounding": int, "theme": int, "accent_color": int, "news": int, "news_state": int, "developer_mode": int,
"update_channel": int, "config": String, // general
"grade_color1": int, "grade_color2": int, "grade_color3": int, "grade_color4": int, "grade_color5": int, // grade colors
"vibration_strength": int, "ab_weeks": int, "swap_ab_weeks": int,
"notifications": int, "notifications_bitfield": int, "notification_poll_interval": int, // notifications
"x_filc_id": String,
});
// Create table Settings
await db.execute("CREATE TABLE IF NOT EXISTS settings ($settingsDB)");
return settingsDB;
}
Future<DatabaseStruct> createUsersTable(Database db) async {
var usersDB = DatabaseStruct(
{"id": String, "name": String, "username": String, "password": String, "institute_code": String, "student": String, "role": int});
// Create table Users
await db.execute("CREATE TABLE IF NOT EXISTS users ($usersDB)");
return usersDB;
}
Future<void> migrateDB( Future<void> migrateDB(
Database db, Database db, {
String table, required DatabaseStruct struct,
Iterable<String> keys, required Map<String, Object?> defaultValues,
Map<String, Object?> defaultValues, }) async {
Future<DatabaseStruct> Function(Database) create, var originalRows = await db.query(struct.table);
) async {
var originalRows = await db.query(table);
if (originalRows.length == 0) { if (originalRows.isEmpty) {
await db.execute("drop table $table"); await db.execute("drop table ${struct.table}");
await create(db); await createTable(db, struct);
return; return;
} }
List<Map<String, dynamic>> migrated = []; List<Map<String, dynamic>> migrated = [];
// go through each row and add missing keys or delete non existing keys
await Future.forEach<Map<String, Object?>>(originalRows, (original) async { await Future.forEach<Map<String, Object?>>(originalRows, (original) async {
bool migrationRequired = keys.any((key) => !original.containsKey(key) || original[key] == null); bool migrationRequired = struct.struct.keys.any((key) => !original.containsKey(key) || original[key] == null) ||
original.keys.any((key) => !struct.struct.containsKey(key));
if (migrationRequired) { if (migrationRequired) {
print("INFO: Migrating $table"); print("INFO: Migrating ${struct.table}");
var copy = Map<String, dynamic>.from(original); var copy = Map<String, Object?>.from(original);
// Fill missing columns // Fill missing columns
keys.forEach((key) { for (var key in struct.struct.keys) {
if (!keys.contains(key)) {
print("DEBUG: dropping $key");
copy.remove(key);
}
if (!original.containsKey(key) || original[key] == null) { if (!original.containsKey(key) || original[key] == null) {
print("DEBUG: migrating $key"); print("DEBUG: migrating $key");
copy[key] = defaultValues[key]; copy[key] = defaultValues[key];
} }
}); }
for (var key in original.keys) {
if (!struct.struct.keys.contains(key)) {
print("DEBUG: dropping $key");
copy.remove(key);
}
}
migrated.add(copy); migrated.add(copy);
} }
}); });
if (migrated.length > 0) { // replace the old table with the migrated one
if (migrated.isNotEmpty) {
// Delete table // Delete table
await db.execute("drop table $table"); await db.execute("drop table ${struct.table}");
// Recreate table // Recreate table
await create(db); await createTable(db, struct);
await Future.forEach(migrated, (Map<String, dynamic> copy) async { await Future.forEach(migrated, (Map<String, Object?> copy) async {
await db.insert(table, copy); await db.insert(struct.table, copy);
}); });
print("INFO: Database migrated"); print("INFO: Database migrated");

View File

@@ -1,5 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'package:filcnaplo/models/subject_lesson_count.dart';
import 'package:filcnaplo/models/user.dart'; import 'package:filcnaplo/models/user.dart';
// ignore: depend_on_referenced_packages
import 'package:sqflite_common/sqlite_api.dart'; import 'package:sqflite_common/sqlite_api.dart';
// Models // Models
@@ -13,6 +15,7 @@ import 'package:filcnaplo_kreta_api/models/message.dart';
import 'package:filcnaplo_kreta_api/models/note.dart'; import 'package:filcnaplo_kreta_api/models/note.dart';
import 'package:filcnaplo_kreta_api/models/event.dart'; import 'package:filcnaplo_kreta_api/models/event.dart';
import 'package:filcnaplo_kreta_api/models/absence.dart'; import 'package:filcnaplo_kreta_api/models/absence.dart';
import 'package:filcnaplo_kreta_api/models/group_average.dart';
class DatabaseQuery { class DatabaseQuery {
DatabaseQuery({required this.db}); DatabaseQuery({required this.db});
@@ -28,9 +31,9 @@ class DatabaseQuery {
Future<UserProvider> getUsers() async { Future<UserProvider> getUsers() async {
var userProvider = UserProvider(); var userProvider = UserProvider();
List<Map> usersMap = await db.query("users"); List<Map> usersMap = await db.query("users");
usersMap.forEach((user) { for (var user in usersMap) {
userProvider.addUser(User.fromMap(user)); userProvider.addUser(User.fromMap(user));
}); }
return userProvider; return userProvider;
} }
} }
@@ -42,7 +45,7 @@ class UserDatabaseQuery {
Future<List<Grade>> getGrades({required String userId}) async { Future<List<Grade>> getGrades({required String userId}) async {
List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]); List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]);
if (userData.length == 0) return []; if (userData.isEmpty) return [];
String? gradesJson = userData.elementAt(0)["grades"] as String?; String? gradesJson = userData.elementAt(0)["grades"] as String?;
if (gradesJson == null) return []; if (gradesJson == null) return [];
List<Grade> grades = (jsonDecode(gradesJson) as List).map((e) => Grade.fromJson(e)).toList(); List<Grade> grades = (jsonDecode(gradesJson) as List).map((e) => Grade.fromJson(e)).toList();
@@ -51,7 +54,7 @@ class UserDatabaseQuery {
Future<List<Lesson>> getLessons({required String userId}) async { Future<List<Lesson>> getLessons({required String userId}) async {
List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]); List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]);
if (userData.length == 0) return []; if (userData.isEmpty) return [];
String? lessonsJson = userData.elementAt(0)["timetable"] as String?; String? lessonsJson = userData.elementAt(0)["timetable"] as String?;
if (lessonsJson == null) return []; if (lessonsJson == null) return [];
List<Lesson> lessons = (jsonDecode(lessonsJson) as List).map((e) => Lesson.fromJson(e)).toList(); List<Lesson> lessons = (jsonDecode(lessonsJson) as List).map((e) => Lesson.fromJson(e)).toList();
@@ -60,7 +63,7 @@ class UserDatabaseQuery {
Future<List<Exam>> getExams({required String userId}) async { Future<List<Exam>> getExams({required String userId}) async {
List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]); List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]);
if (userData.length == 0) return []; if (userData.isEmpty) return [];
String? examsJson = userData.elementAt(0)["exams"] as String?; String? examsJson = userData.elementAt(0)["exams"] as String?;
if (examsJson == null) return []; if (examsJson == null) return [];
List<Exam> exams = (jsonDecode(examsJson) as List).map((e) => Exam.fromJson(e)).toList(); List<Exam> exams = (jsonDecode(examsJson) as List).map((e) => Exam.fromJson(e)).toList();
@@ -69,7 +72,7 @@ class UserDatabaseQuery {
Future<List<Homework>> getHomework({required String userId}) async { Future<List<Homework>> getHomework({required String userId}) async {
List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]); List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]);
if (userData.length == 0) return []; if (userData.isEmpty) return [];
String? homeworkJson = userData.elementAt(0)["homework"] as String?; String? homeworkJson = userData.elementAt(0)["homework"] as String?;
if (homeworkJson == null) return []; if (homeworkJson == null) return [];
List<Homework> homework = (jsonDecode(homeworkJson) as List).map((e) => Homework.fromJson(e)).toList(); List<Homework> homework = (jsonDecode(homeworkJson) as List).map((e) => Homework.fromJson(e)).toList();
@@ -78,7 +81,7 @@ class UserDatabaseQuery {
Future<List<Message>> getMessages({required String userId}) async { Future<List<Message>> getMessages({required String userId}) async {
List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]); List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]);
if (userData.length == 0) return []; if (userData.isEmpty) return [];
String? messagesJson = userData.elementAt(0)["messages"] as String?; String? messagesJson = userData.elementAt(0)["messages"] as String?;
if (messagesJson == null) return []; if (messagesJson == null) return [];
List<Message> messages = (jsonDecode(messagesJson) as List).map((e) => Message.fromJson(e)).toList(); List<Message> messages = (jsonDecode(messagesJson) as List).map((e) => Message.fromJson(e)).toList();
@@ -87,7 +90,7 @@ class UserDatabaseQuery {
Future<List<Note>> getNotes({required String userId}) async { Future<List<Note>> getNotes({required String userId}) async {
List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]); List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]);
if (userData.length == 0) return []; if (userData.isEmpty) return [];
String? notesJson = userData.elementAt(0)["notes"] as String?; String? notesJson = userData.elementAt(0)["notes"] as String?;
if (notesJson == null) return []; if (notesJson == null) return [];
List<Note> notes = (jsonDecode(notesJson) as List).map((e) => Note.fromJson(e)).toList(); List<Note> notes = (jsonDecode(notesJson) as List).map((e) => Note.fromJson(e)).toList();
@@ -96,7 +99,7 @@ class UserDatabaseQuery {
Future<List<Event>> getEvents({required String userId}) async { Future<List<Event>> getEvents({required String userId}) async {
List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]); List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]);
if (userData.length == 0) return []; if (userData.isEmpty) return [];
String? eventsJson = userData.elementAt(0)["events"] as String?; String? eventsJson = userData.elementAt(0)["events"] as String?;
if (eventsJson == null) return []; if (eventsJson == null) return [];
List<Event> events = (jsonDecode(eventsJson) as List).map((e) => Event.fromJson(e)).toList(); List<Event> events = (jsonDecode(eventsJson) as List).map((e) => Event.fromJson(e)).toList();
@@ -105,10 +108,28 @@ class UserDatabaseQuery {
Future<List<Absence>> getAbsences({required String userId}) async { Future<List<Absence>> getAbsences({required String userId}) async {
List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]); List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]);
if (userData.length == 0) return []; if (userData.isEmpty) return [];
String? absebcesJson = userData.elementAt(0)["absences"] as String?; String? absencesJson = userData.elementAt(0)["absences"] as String?;
if (absebcesJson == null) return []; if (absencesJson == null) return [];
List<Absence> absebces = (jsonDecode(absebcesJson) as List).map((e) => Absence.fromJson(e)).toList(); List<Absence> absences = (jsonDecode(absencesJson) as List).map((e) => Absence.fromJson(e)).toList();
return absebces; return absences;
}
Future<List<GroupAverage>> getGroupAverages({required String userId}) async {
List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]);
if (userData.isEmpty) return [];
String? groupAveragesJson = userData.elementAt(0)["group_averages"] as String?;
if (groupAveragesJson == null) return [];
List<GroupAverage> groupAverages = (jsonDecode(groupAveragesJson) as List).map((e) => GroupAverage.fromJson(e)).toList();
return groupAverages;
}
Future<SubjectLessonCount> getSubjectLessonCount({required String userId}) async {
List<Map> userData = await db.query("user_data", where: "id = ?", whereArgs: [userId]);
if (userData.isEmpty) return SubjectLessonCount.fromMap({});
String? lessonCountJson = userData.elementAt(0)["subject_lesson_count"] as String?;
if (lessonCountJson == null) return SubjectLessonCount.fromMap({});
SubjectLessonCount lessonCount = SubjectLessonCount.fromMap(jsonDecode(lessonCountJson) as Map);
return lessonCount;
} }
} }

View File

@@ -1,4 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'package:filcnaplo/models/subject_lesson_count.dart';
// ignore: depend_on_referenced_packages
import 'package:sqflite_common/sqlite_api.dart'; import 'package:sqflite_common/sqlite_api.dart';
// Models // Models
@@ -12,6 +14,7 @@ import 'package:filcnaplo_kreta_api/models/message.dart';
import 'package:filcnaplo_kreta_api/models/note.dart'; import 'package:filcnaplo_kreta_api/models/note.dart';
import 'package:filcnaplo_kreta_api/models/event.dart'; import 'package:filcnaplo_kreta_api/models/event.dart';
import 'package:filcnaplo_kreta_api/models/absence.dart'; import 'package:filcnaplo_kreta_api/models/absence.dart';
import 'package:filcnaplo_kreta_api/models/group_average.dart';
class DatabaseStore { class DatabaseStore {
DatabaseStore({required this.db}); DatabaseStore({required this.db});
@@ -24,7 +27,7 @@ class DatabaseStore {
Future<void> storeUser(User user) async { Future<void> storeUser(User user) async {
List userRes = await db.query("users", where: "id = ?", whereArgs: [user.id]); List userRes = await db.query("users", where: "id = ?", whereArgs: [user.id]);
if (userRes.length > 0) { if (userRes.isNotEmpty) {
await db.update("users", user.toMap(), where: "id = ?", whereArgs: [user.id]); await db.update("users", user.toMap(), where: "id = ?", whereArgs: [user.id]);
} else { } else {
await db.insert("users", user.toMap()); await db.insert("users", user.toMap());
@@ -43,43 +46,54 @@ class UserDatabaseStore {
final Database db; final Database db;
Future storeGrades(List<Grade> grades, {required String userId}) async { Future<void> storeGrades(List<Grade> grades, {required String userId}) async {
String gradesJson = jsonEncode(grades.map((e) => e.json).toList()); String gradesJson = jsonEncode(grades.map((e) => e.json).toList());
await db.update("user_data", {"grades": gradesJson}, where: "id = ?", whereArgs: [userId]); await db.update("user_data", {"grades": gradesJson}, where: "id = ?", whereArgs: [userId]);
} }
Future storeLessons(List<Lesson> lessons, {required String userId}) async { Future<void> storeLessons(List<Lesson> lessons, {required String userId}) async {
String lessonsJson = jsonEncode(lessons.map((e) => e.json).toList()); String lessonsJson = jsonEncode(lessons.map((e) => e.json).toList());
await db.update("user_data", {"timetable": lessonsJson}, where: "id = ?", whereArgs: [userId]); await db.update("user_data", {"timetable": lessonsJson}, where: "id = ?", whereArgs: [userId]);
} }
Future storeExams(List<Exam> exams, {required String userId}) async { Future<void> storeExams(List<Exam> exams, {required String userId}) async {
String examsJson = jsonEncode(exams.map((e) => e.json).toList()); String examsJson = jsonEncode(exams.map((e) => e.json).toList());
await db.update("user_data", {"exams": examsJson}, where: "id = ?", whereArgs: [userId]); await db.update("user_data", {"exams": examsJson}, where: "id = ?", whereArgs: [userId]);
} }
Future storeHomework(List<Homework> homework, {required String userId}) async { Future<void> storeHomework(List<Homework> homework, {required String userId}) async {
String homeworkJson = jsonEncode(homework.map((e) => e.json).toList()); String homeworkJson = jsonEncode(homework.map((e) => e.json).toList());
await db.update("user_data", {"homework": homeworkJson}, where: "id = ?", whereArgs: [userId]); await db.update("user_data", {"homework": homeworkJson}, where: "id = ?", whereArgs: [userId]);
} }
Future storeMessages(List<Message> messages, {required String userId}) async { Future<void> storeMessages(List<Message> messages, {required String userId}) async {
String messagesJson = jsonEncode(messages.map((e) => e.json).toList()); String messagesJson = jsonEncode(messages.map((e) => e.json).toList());
await db.update("user_data", {"messages": messagesJson}, where: "id = ?", whereArgs: [userId]); await db.update("user_data", {"messages": messagesJson}, where: "id = ?", whereArgs: [userId]);
} }
Future storeNotes(List<Note> notes, {required String userId}) async { Future<void> storeNotes(List<Note> notes, {required String userId}) async {
String notesJson = jsonEncode(notes.map((e) => e.json).toList()); String notesJson = jsonEncode(notes.map((e) => e.json).toList());
await db.update("user_data", {"notes": notesJson}, where: "id = ?", whereArgs: [userId]); await db.update("user_data", {"notes": notesJson}, where: "id = ?", whereArgs: [userId]);
} }
Future storeEvents(List<Event> events, {required String userId}) async { Future<void> storeEvents(List<Event> events, {required String userId}) async {
String eventsJson = jsonEncode(events.map((e) => e.json).toList()); String eventsJson = jsonEncode(events.map((e) => e.json).toList());
await db.update("user_data", {"events": eventsJson}, where: "id = ?", whereArgs: [userId]); await db.update("user_data", {"events": eventsJson}, where: "id = ?", whereArgs: [userId]);
} }
Future storeAbsences(List<Absence> absences, {required String userId}) async { Future<void> storeAbsences(List<Absence> absences, {required String userId}) async {
String absencesJson = jsonEncode(absences.map((e) => e.json).toList()); String absencesJson = jsonEncode(absences.map((e) => e.json).toList());
await db.update("user_data", {"absences": absencesJson}, where: "id = ?", whereArgs: [userId]); await db.update("user_data", {"absences": absencesJson}, where: "id = ?", whereArgs: [userId]);
} }
Future<void> storeGroupAverages(List<GroupAverage> groupAverages, {required String userId}) async {
String groupAveragesJson = jsonEncode(groupAverages.map((e) => e.json).toList());
await db.update("user_data", {"group_averages": groupAveragesJson}, where: "id = ?", whereArgs: [userId]);
}
Future<void> storeSubjectLessonCount(SubjectLessonCount lessonCount, {required String userId}) async {
String lessonCountJson = jsonEncode(lessonCount.toMap());
await db.update("user_data", {"subject_lesson_count": lessonCountJson}, where: "id = ?", whereArgs: [userId]);
}
} }

View File

@@ -1,7 +1,8 @@
class DatabaseStruct { class DatabaseStruct {
final String table;
final Map<String, dynamic> struct; final Map<String, dynamic> struct;
DatabaseStruct(this.struct); const DatabaseStruct(this.table, this.struct);
String _toDBfield(String name, dynamic type) { String _toDBfield(String name, dynamic type) {
String typeName = ""; String typeName = "";
@@ -15,7 +16,7 @@ class DatabaseStruct {
break; break;
} }
return "${name} ${typeName.toUpperCase()} ${name == 'id' ? 'NOT NULL' : ''}"; return "$name ${typeName.toUpperCase()} ${name == 'id' ? 'NOT NULL' : ''}";
} }
@override @override

View File

@@ -1,9 +1,13 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:io'; import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:filcnaplo/api/providers/user_provider.dart';
import 'package:filcnaplo/helpers/storage_helper.dart'; import 'package:filcnaplo/helpers/storage_helper.dart';
import 'package:filcnaplo_kreta_api/client/client.dart'; import 'package:filcnaplo_kreta_api/client/client.dart';
import 'package:filcnaplo_kreta_api/models/attachment.dart'; import 'package:filcnaplo_kreta_api/models/attachment.dart';
import 'package:filcnaplo_kreta_api/models/homework.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:open_file/open_file.dart'; import 'package:open_file/open_file.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@@ -28,3 +32,25 @@ extension AttachmentHelper on Attachment {
return result.type == ResultType.done; return result.type == ResultType.done;
} }
} }
extension HomeworkAttachmentHelper on HomeworkAttachment {
Future<String> download(BuildContext context, {bool overwrite = false}) async {
String downloads = await StorageHelper.downloadsPath();
if (!overwrite && await File("$downloads/$name").exists()) return "$downloads/$name";
String url = downloadUrl(Provider.of<UserProvider>(context, listen: false).instituteCode ?? "");
Uint8List data = await Provider.of<KretaClient>(context, listen: false).getAPI(url, rawResponse: true);
if (!await StorageHelper.write("$downloads/$name", data)) return "";
return "$downloads/$name";
}
Future<bool> open(BuildContext context) async {
String downloads = await StorageHelper.downloadsPath();
if (!await File("$downloads/$name").exists()) await download(context);
var result = await OpenFile.open("$downloads/$name");
return result.type == ResultType.done;
}
}

View File

@@ -6,14 +6,15 @@ class AverageHelper {
List<String> ignoreInFinal = ["5,SzorgalomErtek", "4,MagatartasErtek"]; List<String> ignoreInFinal = ["5,SzorgalomErtek", "4,MagatartasErtek"];
if (finalAvg) if (finalAvg) {
grades.removeWhere((e) => grades.removeWhere((e) =>
(e.value.value == 0) || (e.value.value == 0) ||
(ignoreInFinal.contains(e.gradeType?.id))); (ignoreInFinal.contains(e.gradeType?.id)));
}
grades.forEach((e) { for (var e in grades) {
average += e.value.value * ((finalAvg ? 100 : e.value.weight) / 100); average += e.value.value * ((finalAvg ? 100 : e.value.weight) / 100);
}); }
average = average / average = average /
grades grades

View File

@@ -0,0 +1,36 @@
import 'package:flutter/cupertino.dart';
import 'package:quick_actions/quick_actions.dart';
import 'package:filcnaplo_mobile_ui/common/screens.i18n.dart';
const QuickActions quickActions = QuickActions();
void setupQuickActions() {
quickActions.setShortcutItems(<ShortcutItem>[
ShortcutItem(type: 'action_grades', localizedTitle: 'grades'.i18n, icon: 'ic_grades'),
ShortcutItem(type: 'action_timetable', localizedTitle: 'timetable'.i18n, icon: 'ic_timetable'),
ShortcutItem(type: 'action_messages', localizedTitle: 'messages'.i18n, icon: 'ic_messages'),
ShortcutItem(type: 'action_absences', localizedTitle: 'absences'.i18n, icon: 'ic_absences')
]);
}
void handleQuickActions(BuildContext context, void Function(String) callback) {
quickActions.initialize((shortcutType) {
switch (shortcutType) {
case 'action_home':
callback("home");
break;
case 'action_grades':
callback("grades");
break;
case 'action_timetable':
callback("timetable");
break;
case 'action_messages':
callback("messages");
break;
case 'action_absences':
callback("absences");
break;
}
});
}

View File

@@ -1,3 +1,5 @@
// ignore_for_file: avoid_print
import 'dart:io'; import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';

View File

@@ -7,7 +7,7 @@ class SubjectIcon {
static IconData? lookup({Subject? subject, String? subjectName}) { static IconData? lookup({Subject? subject, String? subjectName}) {
assert(!(subject == null && subjectName == null)); assert(!(subject == null && subjectName == null));
String name = subject?.name.toLowerCase().specialChars().trim() ?? subjectName ?? ""; String name = (subject?.name ?? subjectName ?? "").toLowerCase().specialChars().trim();
String category = subject?.category.description.toLowerCase().specialChars() ?? ""; String category = subject?.category.description.toLowerCase().specialChars() ?? "";
// todo: check for categories // todo: check for categories
@@ -16,7 +16,7 @@ class SubjectIcon {
if (RegExp("irodalom").hasMatch(name)) return Icons.menu_book_outlined; if (RegExp("irodalom").hasMatch(name)) return Icons.menu_book_outlined;
if (RegExp("tor(i|tenelem)").hasMatch(name)) return Icons.hourglass_empty_outlined; if (RegExp("tor(i|tenelem)").hasMatch(name)) return Icons.hourglass_empty_outlined;
if (RegExp("foldrajz").hasMatch(name)) return Icons.public_outlined; if (RegExp("foldrajz").hasMatch(name)) return Icons.public_outlined;
if (RegExp("rajz|muvtori|muveszet").hasMatch(name)) return Icons.palette_outlined; if (RegExp("rajz|muvtori|muveszet|vizualis").hasMatch(name)) return Icons.palette_outlined;
if (RegExp("fizika").hasMatch(name)) return Icons.emoji_objects_outlined; if (RegExp("fizika").hasMatch(name)) return Icons.emoji_objects_outlined;
if (RegExp("^enek|zene|szolfezs|zongora|korus").hasMatch(name)) return Icons.music_note_outlined; if (RegExp("^enek|zene|szolfezs|zongora|korus").hasMatch(name)) return Icons.music_note_outlined;
if (RegExp("^tes(i|tneveles)|sport").hasMatch(name)) return Icons.sports_soccer_outlined; if (RegExp("^tes(i|tneveles)|sport").hasMatch(name)) return Icons.sports_soccer_outlined;
@@ -35,7 +35,7 @@ class SubjectIcon {
if (RegExp("technika").hasMatch(name)) return Icons.build_outlined; if (RegExp("technika").hasMatch(name)) return Icons.build_outlined;
if (RegExp("tanc").hasMatch(name)) return Icons.speaker_outlined; if (RegExp("tanc").hasMatch(name)) return Icons.speaker_outlined;
if (RegExp("filozofia").hasMatch(name)) return Icons.psychology_outlined; if (RegExp("filozofia").hasMatch(name)) return Icons.psychology_outlined;
if (RegExp("osztaly(fonoki|kozosseg)").hasMatch(name)) return Icons.groups_outlined; if (RegExp("osztaly(fonoki|kozosseg)").hasMatch(name) || name == "ofo") return Icons.groups_outlined;
if (RegExp("gazdasag").hasMatch(name)) return Icons.account_balance_outlined; if (RegExp("gazdasag").hasMatch(name)) return Icons.account_balance_outlined;
if (RegExp("szorgalom").hasMatch(name)) return Icons.verified_outlined; if (RegExp("szorgalom").hasMatch(name)) return Icons.verified_outlined;
if (RegExp("magatartas").hasMatch(name)) return Icons.emoji_people_outlined; if (RegExp("magatartas").hasMatch(name)) return Icons.emoji_people_outlined;

View File

@@ -7,33 +7,36 @@ import 'package:filcnaplo/helpers/storage_helper.dart';
import 'package:filcnaplo/models/release.dart'; import 'package:filcnaplo/models/release.dart';
import 'package:open_file/open_file.dart'; import 'package:open_file/open_file.dart';
enum UpdateState { prepare, downloading, installing } enum UpdateState { none, preparing, downloading, installing }
typedef UpdateCallback = Function(double progress, UpdateState state); typedef UpdateCallback = Function(double progress, UpdateState state);
// TODO: cleanup old apk files // TODO: cleanup old apk files
extension UpdateHelper on Release { extension UpdateHelper on Release {
Future<void> install({UpdateCallback? updateCallback}) async { Future<void> install({UpdateCallback? updateCallback}) async {
updateCallback!(-1, UpdateState.preparing);
String downloads = await StorageHelper.downloadsPath(); String downloads = await StorageHelper.downloadsPath();
File apk = File("$downloads/filcnaplo-${version}.apk"); File apk = File("$downloads/filcnaplo-$version.apk");
if (!await apk.exists()) { if (!await apk.exists()) {
updateCallback!(-1, UpdateState.downloading); updateCallback(-1, UpdateState.downloading);
var bytes = await download(updateCallback: updateCallback); var bytes = await download(updateCallback: updateCallback);
if (!await StorageHelper.write(apk.path, bytes)) throw "failed to write apk: permission denied"; if (!await StorageHelper.write(apk.path, bytes)) throw "failed to write apk: permission denied";
} }
updateCallback!(-1, UpdateState.installing); updateCallback(-1, UpdateState.installing);
var result = await OpenFile.open(apk.path); var result = await OpenFile.open(apk.path);
if (result.type != ResultType.done) { if (result.type != ResultType.done) {
print("ERROR: installUpdate.openFile: " + result.message); // ignore: avoid_print
print("ERROR: installUpdate.openFile: ${result.message}");
throw result.message; throw result.message;
} }
updateCallback(-1, UpdateState.prepare); updateCallback(-1, UpdateState.none);
} }
Future<Uint8List> download({UpdateCallback? updateCallback}) async { Future<Uint8List> download({UpdateCallback? updateCallback}) async {

View File

@@ -1,10 +1,19 @@
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
class FilcIcons { class FilcIcons {
static const IconData home = const FilcIconData(0x41); FilcIcons._();
static const IconData linux = const FilcIconData(0x42);
}
class FilcIconData extends IconData { static const iconFontFamily = 'FilcIcons';
const FilcIconData(int codePoint) : super(codePoint, fontFamily: "FilcIcons");
/// home
static const IconData home = IconData(0x00, fontFamily: iconFontFamily);
/// linux
static const IconData linux = IconData(0x01, fontFamily: iconFontFamily);
/// upstairs
static const IconData upstairs = IconData(0x02, fontFamily: iconFontFamily);
/// downstairs
static const IconData downstairs = IconData(0x03, fontFamily: iconFontFamily);
} }

View File

@@ -48,15 +48,16 @@ Widget errorBuilder(FlutterErrorDetails details) {
return Builder(builder: (context) { return Builder(builder: (context) {
if (Navigator.of(context).canPop()) Navigator.pop(context); if (Navigator.of(context).canPop()) Navigator.pop(context);
WidgetsBinding.instance?.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
if (!errorShown && details.exceptionAsString() != lastException) { if (!errorShown && details.exceptionAsString() != lastException) {
errorShown = true; errorShown = true;
lastException = details.exceptionAsString(); lastException = details.exceptionAsString();
Navigator.of(context, rootNavigator: true).push(MaterialPageRoute(builder: (context) { Navigator.of(context, rootNavigator: true).push(MaterialPageRoute(builder: (context) {
if (kReleaseMode) if (kReleaseMode) {
return ErrorReportScreen(details); return ErrorReportScreen(details);
else } else {
return ErrorScreen(details); return ErrorScreen(details);
}
})).then((_) => errorShown = false); })).then((_) => errorShown = false);
} }
}); });

View File

@@ -1,15 +1,11 @@
import 'dart:io'; import 'dart:io';
import 'package:package_info_plus/package_info_plus.dart';
class Config { class Config {
String _userAgent; String _userAgent;
String? _version;
Map? json; Map? json;
static const String _version = String.fromEnvironment("APPVER", defaultValue: "2.2.0");
Config({required String userAgent, this.json}) : _userAgent = userAgent { Config({required String userAgent, this.json}) : _userAgent = userAgent;
PackageInfo.fromPlatform().then((value) => _version = value.version);
}
factory Config.fromJson(Map json) { factory Config.fromJson(Map json) {
return Config( return Config(
@@ -18,7 +14,7 @@ class Config {
); );
} }
String get userAgent => _userAgent.replaceAll("\$0", _version ?? "0").replaceAll("\$1", platform).replaceAll("\$2", "0"); String get userAgent => _userAgent.replaceAll("\$0", _version).replaceAll("\$1", platform).replaceAll("\$2", "0");
static String get platform { static String get platform {
if (Platform.isAndroid) { if (Platform.isAndroid) {

View File

@@ -58,11 +58,12 @@ class Version {
// check for valid prerelease name // check for valid prerelease name
if (p[0] != "") { if (p[0] != "") {
if (prereleases.contains(p[0].toLowerCase().trim())) if (prereleases.contains(p[0].toLowerCase().trim())) {
pre = p[0]; pre = p[0];
else } else {
throw "invalid prerelease name: ${p[0]}"; throw "invalid prerelease name: ${p[0]}";
} }
}
// core // core
p = string.split("."); p = string.split(".");
@@ -74,6 +75,7 @@ class Version {
return Version(x, y, z, prerelease: pre, prever: prev); return Version(x, y, z, prerelease: pre, prever: prev);
} catch (error) { } catch (error) {
// ignore: avoid_print
print("WARNING: Failed to parse version ($o): $error"); print("WARNING: Failed to parse version ($o): $error");
return Version.zero; return Version.zero;
} }
@@ -125,5 +127,8 @@ class Version {
} }
static const zero = Version(0, 0, 0); static const zero = Version(0, 0, 0);
static const List<String> prereleases = ["dev", "pre", "alpha", "beta", "rc"]; static const List<String> prereleases = ["dev", "pre", "alpha", "beta", "rc", "nightly", "test"];
@override
int get hashCode => toString().hashCode;
} }

View File

@@ -1,20 +1,20 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:developer';
import 'package:filcnaplo/api/providers/database_provider.dart'; import 'package:filcnaplo/api/providers/database_provider.dart';
import 'package:filcnaplo/models/config.dart'; import 'package:filcnaplo/models/config.dart';
import 'package:filcnaplo/theme.dart'; import 'package:filcnaplo/theme.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
enum Pages { home, grades, timetable, messages, absences } enum Pages { home, grades, timetable, messages, absences }
enum UpdateChannel { stable, beta, dev } enum UpdateChannel { stable, beta, dev }
enum VibrationStrength { off, light, medium, strong } enum VibrationStrength { off, light, medium, strong }
class SettingsProvider extends ChangeNotifier { class SettingsProvider extends ChangeNotifier {
PackageInfo? _packageInfo;
// en_en, hu_hu, de_de // en_en, hu_hu, de_de
String _language; String _language;
Pages _startPage; Pages _startPage;
@@ -44,11 +44,16 @@ class SettingsProvider extends ChangeNotifier {
int _notificationPollInterval; int _notificationPollInterval;
bool _developerMode; bool _developerMode;
VibrationStrength _vibrate; VibrationStrength _vibrate;
bool _ABweeks; bool _abWeeks;
bool _swapABweeks; bool _swapABweeks;
UpdateChannel _updateChannel; UpdateChannel _updateChannel;
Config _config; Config _config;
String _xFilcId; String _xFilcId;
bool _graphClassAvg;
bool _goodStudent;
bool _presentationMode;
bool _bellDelayEnabled;
int _bellDelay;
SettingsProvider({ SettingsProvider({
required String language, required String language,
@@ -64,11 +69,16 @@ class SettingsProvider extends ChangeNotifier {
required bool developerMode, required bool developerMode,
required int notificationPollInterval, required int notificationPollInterval,
required VibrationStrength vibrate, required VibrationStrength vibrate,
required bool ABweeks, required bool abWeeks,
required bool swapABweeks, required bool swapABweeks,
required UpdateChannel updateChannel, required UpdateChannel updateChannel,
required Config config, required Config config,
required String xFilcId, required String xFilcId,
required bool graphClassAvg,
required bool goodStudent,
required bool presentationMode,
required bool bellDelayEnabled,
required int bellDelay,
}) : _language = language, }) : _language = language,
_startPage = startPage, _startPage = startPage,
_rounding = rounding, _rounding = rounding,
@@ -82,17 +92,26 @@ class SettingsProvider extends ChangeNotifier {
_developerMode = developerMode, _developerMode = developerMode,
_notificationPollInterval = notificationPollInterval, _notificationPollInterval = notificationPollInterval,
_vibrate = vibrate, _vibrate = vibrate,
_ABweeks = ABweeks, _abWeeks = abWeeks,
_swapABweeks = swapABweeks, _swapABweeks = swapABweeks,
_updateChannel = updateChannel, _updateChannel = updateChannel,
_config = config, _config = config,
_xFilcId = xFilcId { _xFilcId = xFilcId,
PackageInfo.fromPlatform().then((PackageInfo packageInfo) { _graphClassAvg = graphClassAvg,
_packageInfo = packageInfo; _goodStudent = goodStudent,
}); _presentationMode = presentationMode,
} _bellDelayEnabled = bellDelayEnabled,
_bellDelay = bellDelay;
factory SettingsProvider.fromMap(Map map) { factory SettingsProvider.fromMap(Map map) {
Map<String, Object?>? configMap;
try {
configMap = jsonDecode(map["config"] ?? "{}");
} catch (e) {
log("[ERROR] SettingsProvider.fromMap: $e");
}
return SettingsProvider( return SettingsProvider(
language: map["language"], language: map["language"],
startPage: Pages.values[map["start_page"]], startPage: Pages.values[map["start_page"]],
@@ -106,18 +125,23 @@ class SettingsProvider extends ChangeNotifier {
Color(map["grade_color4"]), Color(map["grade_color4"]),
Color(map["grade_color5"]), Color(map["grade_color5"]),
], ],
newsEnabled: map["news"] == 1 ? true : false, newsEnabled: map["news"] == 1,
newsState: map["news_state"], newsState: map["news_state"],
notificationsEnabled: map["notifications"] == 1 ? true : false, notificationsEnabled: map["notifications"] == 1,
notificationsBitfield: map["notifications_bitfield"], notificationsBitfield: map["notifications_bitfield"],
notificationPollInterval: map["notification_poll_interval"], notificationPollInterval: map["notification_poll_interval"],
developerMode: map["developer_mode"] == 1 ? true : false, developerMode: map["developer_mode"] == 1,
vibrate: VibrationStrength.values[map["vibration_strength"]], vibrate: VibrationStrength.values[map["vibration_strength"]],
ABweeks: map["ab_weeks"] == 1 ? true : false, abWeeks: map["ab_weeks"] == 1,
swapABweeks: map["swap_ab_weeks"] == 1 ? true : false, swapABweeks: map["swap_ab_weeks"] == 1,
updateChannel: UpdateChannel.values[map["update_channel"]], updateChannel: UpdateChannel.values[map["update_channel"]],
config: Config.fromJson(jsonDecode(map["config"] ?? "{}")), config: Config.fromJson(configMap ?? {}),
xFilcId: map["x_filc_id"], xFilcId: map["x_filc_id"],
graphClassAvg: map["graph_class_avg"] == 1,
goodStudent: false,
presentationMode: map["presentation_mode"] == 1,
bellDelayEnabled: map["bell_delay_enabled"] == 1,
bellDelay: map["bell_delay"],
); );
} }
@@ -140,11 +164,15 @@ class SettingsProvider extends ChangeNotifier {
"grade_color5": _gradeColors[4].value, "grade_color5": _gradeColors[4].value,
"update_channel": _updateChannel.index, "update_channel": _updateChannel.index,
"vibration_strength": _vibrate.index, "vibration_strength": _vibrate.index,
"ab_weeks": _ABweeks ? 1 : 0, "ab_weeks": _abWeeks ? 1 : 0,
"swap_ab_weeks": _swapABweeks ? 1 : 0, "swap_ab_weeks": _swapABweeks ? 1 : 0,
"notification_poll_interval": _notificationPollInterval, "notification_poll_interval": _notificationPollInterval,
"config": jsonEncode(config.json), "config": jsonEncode(config.json),
"x_filc_id": _xFilcId, "x_filc_id": _xFilcId,
"graph_class_avg": _graphClassAvg ? 1 : 0,
"presentation_mode": _presentationMode ? 1 : 0,
"bell_delay_enabled": _bellDelayEnabled ? 1 : 0,
"bell_delay": _bellDelay,
}; };
} }
@@ -169,11 +197,16 @@ class SettingsProvider extends ChangeNotifier {
developerMode: false, developerMode: false,
notificationPollInterval: 1, notificationPollInterval: 1,
vibrate: VibrationStrength.medium, vibrate: VibrationStrength.medium,
ABweeks: false, abWeeks: false,
swapABweeks: false, swapABweeks: false,
updateChannel: UpdateChannel.stable, updateChannel: UpdateChannel.stable,
config: Config.fromJson({}), config: Config.fromJson({}),
xFilcId: Uuid().v4(), xFilcId: const Uuid().v4(),
graphClassAvg: false,
goodStudent: false,
presentationMode: false,
bellDelayEnabled: false,
bellDelay: 0,
); );
} }
@@ -191,16 +224,21 @@ class SettingsProvider extends ChangeNotifier {
bool get developerMode => _developerMode; bool get developerMode => _developerMode;
int get notificationPollInterval => _notificationPollInterval; int get notificationPollInterval => _notificationPollInterval;
VibrationStrength get vibrate => _vibrate; VibrationStrength get vibrate => _vibrate;
bool get ABweeks => _ABweeks; bool get abWeeks => _abWeeks;
bool get swapABweeks => _swapABweeks; bool get swapABweeks => _swapABweeks;
UpdateChannel get updateChannel => _updateChannel; UpdateChannel get updateChannel => _updateChannel;
PackageInfo? get packageInfo => _packageInfo;
Config get config => _config; Config get config => _config;
String get xFilcId => _xFilcId; String get xFilcId => _xFilcId;
bool get graphClassAvg => _graphClassAvg;
bool get goodStudent => _goodStudent;
bool get presentationMode => _presentationMode;
bool get bellDelayEnabled => _bellDelayEnabled;
int get bellDelay => _bellDelay;
Future<void> update( Future<void> update(
BuildContext context, { BuildContext context, {
DatabaseProvider? database, DatabaseProvider? database,
bool store = true,
String? language, String? language,
Pages? startPage, Pages? startPage,
int? rounding, int? rounding,
@@ -214,11 +252,16 @@ class SettingsProvider extends ChangeNotifier {
bool? developerMode, bool? developerMode,
int? notificationPollInterval, int? notificationPollInterval,
VibrationStrength? vibrate, VibrationStrength? vibrate,
bool? ABweeks, bool? abWeeks,
bool? swapABweeks, bool? swapABweeks,
UpdateChannel? updateChannel, UpdateChannel? updateChannel,
Config? config, Config? config,
String? xFilcId, String? xFilcId,
bool? graphClassAvg,
bool? goodStudent,
bool? presentationMode,
bool? bellDelayEnabled,
int? bellDelay,
}) async { }) async {
if (language != null && language != _language) _language = language; if (language != null && language != _language) _language = language;
if (startPage != null && startPage != _startPage) _startPage = startPage; if (startPage != null && startPage != _startPage) _startPage = startPage;
@@ -231,17 +274,23 @@ class SettingsProvider extends ChangeNotifier {
if (notificationsEnabled != null && notificationsEnabled != _notificationsEnabled) _notificationsEnabled = notificationsEnabled; if (notificationsEnabled != null && notificationsEnabled != _notificationsEnabled) _notificationsEnabled = notificationsEnabled;
if (notificationsBitfield != null && notificationsBitfield != _notificationsBitfield) _notificationsBitfield = notificationsBitfield; if (notificationsBitfield != null && notificationsBitfield != _notificationsBitfield) _notificationsBitfield = notificationsBitfield;
if (developerMode != null && developerMode != _developerMode) _developerMode = developerMode; if (developerMode != null && developerMode != _developerMode) _developerMode = developerMode;
if (notificationPollInterval != null && notificationPollInterval != _notificationPollInterval) if (notificationPollInterval != null && notificationPollInterval != _notificationPollInterval) {
_notificationPollInterval = notificationPollInterval; _notificationPollInterval = notificationPollInterval;
}
if (vibrate != null && vibrate != _vibrate) _vibrate = vibrate; if (vibrate != null && vibrate != _vibrate) _vibrate = vibrate;
if (ABweeks != null && ABweeks != _ABweeks) _ABweeks = ABweeks; if (abWeeks != null && abWeeks != _abWeeks) _abWeeks = abWeeks;
if (swapABweeks != null && swapABweeks != _swapABweeks) _swapABweeks = swapABweeks; if (swapABweeks != null && swapABweeks != _swapABweeks) _swapABweeks = swapABweeks;
if (updateChannel != null && updateChannel != _updateChannel) _updateChannel = updateChannel; if (updateChannel != null && updateChannel != _updateChannel) _updateChannel = updateChannel;
if (config != null && config != _config) _config = config; if (config != null && config != _config) _config = config;
if (xFilcId != null && xFilcId != _xFilcId) _xFilcId = xFilcId; if (xFilcId != null && xFilcId != _xFilcId) _xFilcId = xFilcId;
if (graphClassAvg != null && graphClassAvg != _graphClassAvg) _graphClassAvg = graphClassAvg;
if (goodStudent != null) _goodStudent = goodStudent;
if (presentationMode != null && presentationMode != _presentationMode) _presentationMode = presentationMode;
if (bellDelay != null && bellDelay != _bellDelay) _bellDelay = bellDelay;
if (bellDelayEnabled != null && bellDelayEnabled != _bellDelayEnabled) _bellDelayEnabled = bellDelayEnabled;
if (database == null) database = Provider.of<DatabaseProvider>(context, listen: false); database ??= Provider.of<DatabaseProvider>(context, listen: false);
await database.store.storeSettings(this); if (store) await database.store.storeSettings(this);
notifyListeners(); notifyListeners();
} }
} }

View File

@@ -0,0 +1,31 @@
import 'package:filcnaplo_kreta_api/models/category.dart';
import 'package:filcnaplo_kreta_api/models/subject.dart';
enum SubjectLessonCountUpdateState { ready, updating }
class SubjectLessonCount {
DateTime lastUpdated;
Map<Subject, int> subjects;
SubjectLessonCountUpdateState state;
SubjectLessonCount({required this.lastUpdated, required this.subjects, this.state = SubjectLessonCountUpdateState.ready});
factory SubjectLessonCount.fromMap(Map json) {
return SubjectLessonCount(
lastUpdated: DateTime.fromMillisecondsSinceEpoch(json["last_updated"] ?? 0),
subjects: ((json["subjects"] as Map?) ?? {}).map(
(key, value) => MapEntry(
Subject(id: key, name: "", category: Category.fromJson({})),
value,
),
),
);
}
Map toMap() {
return {
"last_updated": lastUpdated.millisecondsSinceEpoch,
"subjects": subjects.map((key, value) => MapEntry(key.id, value)),
};
}
}

View File

@@ -26,7 +26,7 @@ class User {
if (id != null) { if (id != null) {
this.id = id; this.id = id;
} else { } else {
this.id = Uuid().v4(); this.id = const Uuid().v4();
} }
} }
@@ -67,7 +67,7 @@ class User {
"password": password, "password": password,
"institute_code": instituteCode, "institute_code": instituteCode,
"grant_type": "password", "grant_type": "password",
"client_id": KretaAPI.CLIENT_ID, "client_id": KretaAPI.clientId,
}; };
} }
} }

View File

@@ -1,5 +1,6 @@
import 'package:filcnaplo/models/settings.dart'; import 'package:filcnaplo/models/settings.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:material_color_utilities/material_color_utilities.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class AppTheme { class AppTheme {
@@ -8,56 +9,118 @@ class AppTheme {
static const String _fontFamily = "Montserrat"; static const String _fontFamily = "Montserrat";
static Color? _paletteAccentLight(CorePalette? palette) => palette != null ? Color(palette.primary.get(70)) : null;
static Color? _paletteHighlightLight(CorePalette? palette) => palette != null ? Color(palette.neutral.get(100)) : null;
static Color? _paletteBackgroundLight(CorePalette? palette) => palette != null ? Color(palette.neutral.get(95)) : null;
static Color? _paletteAccentDark(CorePalette? palette) => palette != null ? Color(palette.primary.get(80)) : null;
static Color? _paletteBackgroundDark(CorePalette? palette) => palette != null ? Color(palette.neutralVariant.get(10)) : null;
static Color? _paletteHighlightDark(CorePalette? palette) => palette != null ? Color(palette.neutralVariant.get(20)) : null;
// Light Theme // Light Theme
static ThemeData lightTheme(BuildContext context) { static ThemeData lightTheme(BuildContext context, {CorePalette? palette}) {
var lightColors = LightAppColors(); var lightColors = LightAppColors();
Color accent = accentColorMap[Provider.of<SettingsProvider>(context, listen: false).accentColor] ?? Color(0); AccentColor accentColor = Provider.of<SettingsProvider>(context, listen: false).accentColor;
Color accent = accentColorMap[accentColor] ?? const Color(0x00000000);
if (accentColor == AccentColor.adaptive) {
if (palette != null) accent = _paletteAccentLight(palette)!;
} else {
palette = null;
}
return ThemeData( return ThemeData(
brightness: Brightness.light, brightness: Brightness.light,
useMaterial3: false,
fontFamily: _fontFamily, fontFamily: _fontFamily,
scaffoldBackgroundColor: lightColors.background, scaffoldBackgroundColor: _paletteBackgroundLight(palette) ?? lightColors.background,
backgroundColor: lightColors.highlight, backgroundColor: _paletteHighlightLight(palette) ?? lightColors.highlight,
primaryColor: lightColors.filc, primaryColor: lightColors.filc,
dividerColor: Color(0), dividerColor: const Color(0x00000000),
colorScheme: ColorScheme.fromSwatch( colorScheme: ColorScheme.fromSwatch(
accentColor: accent, accentColor: accent,
backgroundColor: lightColors.background, backgroundColor: _paletteBackgroundLight(palette) ?? lightColors.background,
brightness: Brightness.light, brightness: Brightness.light,
cardColor: lightColors.highlight, cardColor: _paletteHighlightLight(palette) ?? lightColors.highlight,
errorColor: lightColors.red, errorColor: lightColors.red,
primaryColorDark: lightColors.filc, primaryColorDark: lightColors.filc,
primarySwatch: Colors.teal, primarySwatch: Colors.teal,
), ),
shadowColor: lightColors.shadow, shadowColor: lightColors.shadow,
appBarTheme: AppBarTheme(backgroundColor: lightColors.background), appBarTheme: AppBarTheme(backgroundColor: _paletteBackgroundLight(palette) ?? lightColors.background),
indicatorColor: accent, indicatorColor: accent,
iconTheme: IconThemeData(color: lightColors.text.withOpacity(.75))); iconTheme: IconThemeData(color: lightColors.text.withOpacity(.75)),
navigationBarTheme: NavigationBarThemeData(
indicatorColor: accent.withOpacity(accentColor == AccentColor.adaptive ? 0.4 : 0.8),
iconTheme: MaterialStateProperty.all(IconThemeData(color: lightColors.text)),
backgroundColor: _paletteHighlightLight(palette) ?? lightColors.highlight,
labelTextStyle: MaterialStateProperty.all(TextStyle(
fontSize: 13.0,
fontWeight: FontWeight.w500,
color: lightColors.text.withOpacity(0.8),
)),
labelBehavior: NavigationDestinationLabelBehavior.alwaysShow,
height: 76.0,
),
sliderTheme: SliderThemeData(
inactiveTrackColor: accent.withOpacity(.3),
),
progressIndicatorTheme: ProgressIndicatorThemeData(color: accent),
expansionTileTheme: ExpansionTileThemeData(iconColor: accent),
);
} }
// Dark Theme // Dark Theme
static ThemeData darkTheme(BuildContext context) { static ThemeData darkTheme(BuildContext context, {CorePalette? palette}) {
var darkColors = DarkAppColors(); var darkColors = DarkAppColors();
Color accent = accentColorMap[Provider.of<SettingsProvider>(context, listen: false).accentColor] ?? Color(0); AccentColor accentColor = Provider.of<SettingsProvider>(context, listen: false).accentColor;
Color accent = accentColorMap[accentColor] ?? const Color(0x00000000);
if (accentColor == AccentColor.adaptive) {
if (palette != null) accent = _paletteAccentDark(palette)!;
} else {
palette = null;
}
return ThemeData( return ThemeData(
brightness: Brightness.dark, brightness: Brightness.dark,
useMaterial3: false,
fontFamily: _fontFamily, fontFamily: _fontFamily,
scaffoldBackgroundColor: darkColors.background, scaffoldBackgroundColor: _paletteBackgroundDark(palette) ?? darkColors.background,
backgroundColor: darkColors.highlight, backgroundColor: _paletteHighlightDark(palette) ?? darkColors.highlight,
primaryColor: darkColors.filc, primaryColor: darkColors.filc,
dividerColor: Color(0), dividerColor: const Color(0x00000000),
colorScheme: ColorScheme.fromSwatch( colorScheme: ColorScheme.fromSwatch(
accentColor: accent, accentColor: accent,
backgroundColor: darkColors.background, backgroundColor: _paletteBackgroundDark(palette) ?? darkColors.background,
brightness: Brightness.dark, brightness: Brightness.dark,
cardColor: darkColors.highlight, cardColor: _paletteHighlightDark(palette) ?? darkColors.highlight,
errorColor: darkColors.red, errorColor: darkColors.red,
primaryColorDark: darkColors.filc, primaryColorDark: darkColors.filc,
primarySwatch: Colors.teal, primarySwatch: Colors.teal,
), ),
shadowColor: darkColors.shadow, shadowColor: darkColors.shadow,
appBarTheme: AppBarTheme(backgroundColor: darkColors.background), appBarTheme: AppBarTheme(backgroundColor: _paletteBackgroundDark(palette) ?? darkColors.background),
indicatorColor: accent, indicatorColor: accent,
iconTheme: IconThemeData(color: darkColors.text.withOpacity(.75))); iconTheme: IconThemeData(color: darkColors.text.withOpacity(.75)),
navigationBarTheme: NavigationBarThemeData(
indicatorColor: accent.withOpacity(accentColor == AccentColor.adaptive ? 0.4 : 0.8),
iconTheme: MaterialStateProperty.all(IconThemeData(color: darkColors.text)),
backgroundColor: _paletteHighlightDark(palette) ?? darkColors.highlight,
labelTextStyle: MaterialStateProperty.all(TextStyle(
fontSize: 13.0,
fontWeight: FontWeight.w500,
color: darkColors.text.withOpacity(0.8),
)),
labelBehavior: NavigationDestinationLabelBehavior.alwaysShow,
height: 76.0,
),
sliderTheme: SliderThemeData(
inactiveTrackColor: accent.withOpacity(.3),
),
progressIndicatorTheme: ProgressIndicatorThemeData(color: accent),
expansionTileTheme: ExpansionTileThemeData(iconColor: accent),
);
} }
} }
@@ -67,69 +130,98 @@ class AppColors {
} }
} }
enum AccentColor { filc, blue, green, lime, yellow, orange, red, pink, purple } enum AccentColor { filc, blue, green, lime, yellow, orange, red, pink, purple, adaptive }
Map<AccentColor, Color> accentColorMap = { Map<AccentColor, Color> accentColorMap = {
AccentColor.filc: Color(0xff20AC9B), AccentColor.filc: const Color(0xff20AC9B),
AccentColor.blue: Colors.blue.shade300, AccentColor.blue: Colors.blue.shade300,
AccentColor.green: Colors.green.shade300, AccentColor.green: Colors.green.shade400,
AccentColor.lime: Colors.lime.shade300, AccentColor.lime: Colors.lightGreen.shade400,
AccentColor.yellow: Colors.yellow.shade300, AccentColor.yellow: Colors.orange.shade300,
AccentColor.orange: Colors.deepOrange.shade300, AccentColor.orange: Colors.deepOrange.shade300,
AccentColor.red: Colors.red.shade300, AccentColor.red: Colors.red.shade300,
AccentColor.pink: Colors.pink.shade300, AccentColor.pink: Colors.pink.shade300,
AccentColor.purple: Colors.purple.shade300, AccentColor.purple: Colors.purple.shade300,
AccentColor.adaptive: const Color(0xff20AC9B),
}; };
abstract class ThemeAppColors { abstract class ThemeAppColors {
final Color shadow = Color(0); final Color shadow = const Color(0x00000000);
final Color text = Color(0); final Color text = const Color(0x00000000);
final Color background = Color(0); final Color background = const Color(0x00000000);
final Color highlight = Color(0); final Color highlight = const Color(0x00000000);
final Color red = Color(0); final Color red = const Color(0x00000000);
final Color orange = Color(0); final Color orange = const Color(0x00000000);
final Color yellow = Color(0); final Color yellow = const Color(0x00000000);
final Color green = Color(0); final Color green = const Color(0x00000000);
final Color filc = Color(0); final Color filc = const Color(0x00000000);
final Color teal = Color(0); final Color teal = const Color(0x00000000);
final Color blue = Color(0); final Color blue = const Color(0x00000000);
final Color indigo = Color(0); final Color indigo = const Color(0x00000000);
final Color purple = Color(0); final Color purple = const Color(0x00000000);
final Color pink = Color(0); final Color pink = const Color(0x00000000);
} }
class LightAppColors implements ThemeAppColors { class LightAppColors implements ThemeAppColors {
final shadow = Color(0xffE8E8E8); @override
final shadow = const Color(0xffE8E8E8);
@override
final text = Colors.black; final text = Colors.black;
final background = Color(0xffF4F9FF); @override
final highlight = Color(0xffFFFFFF); final background = const Color(0xffF4F9FF);
final red = Color(0xffFF3B30); @override
final orange = Color(0xffFF9500); final highlight = const Color(0xffFFFFFF);
final yellow = Color(0xffFFCC00); @override
final green = Color(0xff34C759); final red = const Color(0xffFF3B30);
final filc = Color(0xff247665); @override
final teal = Color(0xff5AC8FA); final orange = const Color(0xffFF9500);
final blue = Color(0xff007AFF); @override
final indigo = Color(0xff5856D6); final yellow = const Color(0xffFFCC00);
final purple = Color(0xffAF52DE); @override
final pink = Color(0xffFF2D55); final green = const Color(0xff34C759);
@override
final filc = const Color(0xff247665);
@override
final teal = const Color(0xff5AC8FA);
@override
final blue = const Color(0xff007AFF);
@override
final indigo = const Color(0xff5856D6);
@override
final purple = const Color(0xffAF52DE);
@override
final pink = const Color(0xffFF2D55);
} }
class DarkAppColors implements ThemeAppColors { class DarkAppColors implements ThemeAppColors {
final shadow = Color(0); @override
final shadow = const Color(0x00000000);
@override
final text = Colors.white; final text = Colors.white;
final background = Color(0xff000000); @override
final highlight = Color(0xff141516); final background = const Color(0xff000000);
final red = Color(0xffFF453A); @override
final orange = Color(0xffFF9F0A); final highlight = const Color(0xff141516);
final yellow = Color(0xffFFD60A); @override
final green = Color(0xff32D74B); final red = const Color(0xffFF453A);
final filc = Color(0xff29826F); @override
final teal = Color(0xff64D2FF); final orange = const Color(0xffFF9F0A);
final blue = Color(0xff0A84FF); @override
final indigo = Color(0xff5E5CE6); final yellow = const Color(0xffFFD60A);
final purple = Color(0xffBF5AF2); @override
final pink = Color(0xffFF375F); final green = const Color(0xff32D74B);
@override
final filc = const Color(0xff29826F);
@override
final teal = const Color(0xff64D2FF);
@override
final blue = const Color(0xff0A84FF);
@override
final indigo = const Color(0xff5E5CE6);
@override
final purple = const Color(0xffBF5AF2);
@override
final pink = const Color(0xffFF375F);
} }
class ThemeModeObserver extends ChangeNotifier { class ThemeModeObserver extends ChangeNotifier {

View File

@@ -6,8 +6,7 @@ import 'package:html/parser.dart';
import 'format.i18n.dart'; import 'format.i18n.dart';
extension StringFormatUtils on String { extension StringFormatUtils on String {
String specialChars() => this String specialChars() => replaceAll("é", "e")
.replaceAll("é", "e")
.replaceAll("á", "a") .replaceAll("á", "a")
.replaceAll("ó", "o") .replaceAll("ó", "o")
.replaceAll("ő", "o") .replaceAll("ő", "o")
@@ -17,9 +16,9 @@ extension StringFormatUtils on String {
.replaceAll("ü", "u") .replaceAll("ü", "u")
.replaceAll("í", "i"); .replaceAll("í", "i");
String capital() => this.length > 0 ? this[0].toUpperCase() + this.substring(1) : ""; String capital() => isNotEmpty ? this[0].toUpperCase() + substring(1) : "";
String capitalize() => this.split(" ").map((w) => w.capital()).join(" "); String capitalize() => split(" ").map((w) => w.capital()).join(" ");
String escapeHtml() { String escapeHtml() {
String htmlString = this; String htmlString = this;
@@ -33,28 +32,29 @@ extension StringFormatUtils on String {
} }
extension DateFormatUtils on DateTime { extension DateFormatUtils on DateTime {
String format(BuildContext context, {bool timeOnly = false, bool weekday = false}) { String format(BuildContext context, {bool timeOnly = false, bool forceToday = false, bool weekday = false}) {
// Time only // Time only
if (timeOnly) return DateFormat("HH:mm").format(this); if (timeOnly) return DateFormat("HH:mm").format(this);
DateTime now = DateTime.now(); DateTime now = DateTime.now();
if (now.year == this.year && now.month == this.month && now.day == this.day) { if (now.year == year && now.month == month && now.day == day) {
if (this.hour == 0 && this.minute == 0 && this.second == 0) return "Today".i18n; if (hour == 0 && minute == 0 && second == 0 || forceToday) return "Today".i18n;
return DateFormat("HH:mm").format(this); return DateFormat("HH:mm").format(this);
} }
if (now.year == this.year && now.month == this.month && now.subtract(Duration(days: 1)).day == this.day) return "Yesterday".i18n; if (now.year == year && now.month == month && now.subtract(const Duration(days: 1)).day == day) return "Yesterday".i18n;
if (now.year == this.year && now.month == this.month && now.add(Duration(days: 1)).day == this.day) return "Tomorrow".i18n; if (now.year == year && now.month == month && now.add(const Duration(days: 1)).day == day) return "Tomorrow".i18n;
String formatString; String formatString;
// If date is current week, show only weekday // If date is current week, show only weekday
if (Week.current().start.isBefore(this) && Week.current().end.isAfter(this)) if (Week.current().start.isBefore(this) && Week.current().end.isAfter(this)) {
formatString = "EEEE"; // ex. monday formatString = "EEEE";
else { } else {
if (this.year == now.year) if (year == now.year) {
formatString = "MMM dd."; // ex. Jan. 01. formatString = "MMM dd.";
else } else {
formatString = "yy/MM/dd"; // ex. 21/01/01 formatString = "yy/MM/dd";
} // ex. 21/01/01
if (weekday) formatString += " (EEEE)"; // ex. (monday) if (weekday) formatString += " (EEEE)"; // ex. (monday)
} }

View File

@@ -17,8 +17,10 @@ class JwtUtils {
var payload = utf8.decode(base64Url.decode(parts[1])); var payload = utf8.decode(base64Url.decode(parts[1]));
return jsonDecode(payload); return jsonDecode(payload);
} catch (error) { } catch (error) {
// ignore: avoid_print
print("ERROR: JwtUtils.decodeJwt: $error"); print("ERROR: JwtUtils.decodeJwt: $error");
} }
return null;
} }
static String? getNameFromJWT(String jwt) { static String? getNameFromJWT(String jwt) {
@@ -35,5 +37,6 @@ class JwtUtils {
case "Gondviselo": case "Gondviselo":
return Role.parent; return Role.parent;
} }
return null;
} }
} }

View File

@@ -0,0 +1,37 @@
import 'dart:developer';
import 'package:filcnaplo_kreta_api/models/absence.dart';
import 'package:filcnaplo_kreta_api/models/lesson.dart';
import 'package:filcnaplo_kreta_api/models/week.dart';
import 'package:filcnaplo_kreta_api/providers/timetable_provider.dart';
import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart';
class ReverseSearch {
static Future<Lesson?> getLessonByAbsence(Absence absence, BuildContext context) async {
final timetableProvider = Provider.of<TimetableProvider>(context, listen: false);
List<Lesson> lessons = [];
try {
await timetableProvider.fetch(week: Week.fromDate(absence.date), db: false);
} catch (e) {
log("[ERROR] getLessonByAbsence: $e");
}
lessons = timetableProvider.lessons;
// Find absence lesson in timetable
Lesson lesson = lessons.firstWhere(
(l) => _sameDate(l.date, absence.date) && l.subject.id == absence.subject.id && l.lessonIndex == absence.lessonIndex.toString(),
orElse: () => Lesson.fromJson({'isEmpty': true}),
);
if (lesson.isEmpty) {
return null;
} else {
return lesson;
}
}
// difference.inDays is not reliable
static bool _sameDate(DateTime a, DateTime b) => (a.year == b.year && a.month == b.month && a.day == b.day);
}

View File

@@ -6,9 +6,13 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <dynamic_color/dynamic_color_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h> #include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) { void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) dynamic_color_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin");
dynamic_color_plugin_register_with_registrar(dynamic_color_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);

View File

@@ -3,9 +3,13 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
dynamic_color
url_launcher_linux url_launcher_linux
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES) set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST}) foreach(plugin ${FLUTTER_PLUGIN_LIST})
@@ -14,3 +18,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST})
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>) list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin) endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)

View File

@@ -3,10 +3,10 @@ description: "Nem hivatalos e-napló alkalmazás az e-Kréta rendszerhez"
homepage: https://filcnaplo.hu homepage: https://filcnaplo.hu
publish_to: "none" publish_to: "none"
version: 3.1.0+138 version: 3.3.2+166
environment: environment:
sdk: ">=2.12.0 <3.0.0" sdk: ">=2.16.0-80.1.beta <3.0.0"
dependencies: dependencies:
flutter: flutter:
@@ -30,17 +30,25 @@ dependencies:
http: ^0.13.3 http: ^0.13.3
uuid: ^3.0.4 uuid: ^3.0.4
html: ^0.15.0 html: ^0.15.0
open_file: ^3.2.1 open_file:
git:
url: https://github.com/crazecoder/open_file
ref: master
path_provider: ^2.0.2 path_provider: ^2.0.2
permission_handler: ^8.1.4+2 permission_handler: ^9.2.0
share_plus: ^2.1.4 share_plus: ^4.0.4
package_info_plus: ^1.0.6 connectivity_plus: ^2.0.2
connectivity_plus: ^1.1.0 flutter_displaymode: ^0.4.0
flutter_displaymode: ^0.3.2 quick_actions: ^0.6.0
implicitly_animated_reorderable_list: ^0.4.2
dynamic_color: ^1.2.2
material_color_utilities: ^0.1.3
crypto: ^3.0.2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^2.0.1
# flutter_launcher_icons: ^0.9.0 # flutter_launcher_icons: ^0.9.0
# flutter_native_splash: ^1.2.0 # flutter_native_splash: ^1.2.0
sqflite_common_ffi: ^2.0.0+3 sqflite_common_ffi: ^2.0.0+3

12
filcnaplo/run.sh Normal file
View File

@@ -0,0 +1,12 @@
#!/bin/fish
function get_version
cat pubspec.yaml | grep version: | cut -d' ' -f2 | cut -d+ -f1
end
if test -e /mnt/enc/keys/filc3.properties
set -x ANDROID_SIGNING /mnt/enc/keys/filc3.properties
end
flutter run --debug --dart-define=APPVER=(get_version)

12
filcnaplo/run_release.sh Normal file
View File

@@ -0,0 +1,12 @@
#!/bin/fish
function get_version
cat pubspec.yaml | grep version: | cut -d' ' -f2 | cut -d+ -f1
end
if test -e /mnt/enc/keys/filc3.properties
set -x ANDROID_SIGNING /mnt/enc/keys/filc3.properties
end
flutter run --release --dart-define=APPVER=(get_version)

6
fix-d8dx.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/sh
cd $ANDROID_SDK/build-tools/31.0.0 &&
mv -v d8 dx &&
cd lib &&
mv -v d8.jar dx.jar