Compare commits

..

154 Commits

Author SHA1 Message Date
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
unknown
8a670b3a4c bump 2021-10-03 20:14:02 +02:00
unknown
2917a2e913 mobile 2021-10-03 16:19:45 +02:00
unknown
414c183399 mobile 2021-10-03 15:13:29 +02:00
unknown
428d6ff975 wait for progress animation to finish 2021-10-03 15:04:47 +02:00
unknown
c418a61133 cleanup 2021-10-03 14:20:43 +02:00
unknown
7e83d7b969 sqflite fix 2021-10-03 12:17:32 +02:00
unknown
bdf6cc20c7 linux .desktop file 2021-10-03 01:11:02 +02:00
unknown
da089da22b add linux desktop support 2021-10-03 01:04:45 +02:00
unknown
fa3c0954c4 mobile 2021-10-03 00:44:50 +02:00
unknown
f4843211f5 changelog 2021-10-03 00:42:03 +02:00
unknown
5e1727eadd kreten 2021-10-03 00:28:35 +02:00
unknown
dd103e7474 mobile 2021-10-03 00:28:33 +02:00
unknown
aa61301b17 format weekday on current week 2021-10-03 00:27:58 +02:00
unknown
af360fda53 refactor 2021-10-03 00:27:40 +02:00
unknown
a56453ab9d status 2021-10-02 21:47:41 +02:00
unknown
c9aed14d7c changelog 2021-10-02 15:46:52 +02:00
unknown
0cef766ee6 bump 3.1.0 2021-10-02 15:45:43 +02:00
unknown
c8cd6bf9b8 subject icon fix 2021-10-02 15:44:33 +02:00
unknown
d9e8b4f4ed add tomorrow 2021-10-02 15:35:36 +02:00
unknown
8ab96a32c3 changelog 2021-10-02 00:14:11 +02:00
unknown
22c8a285ab kreten 2021-10-02 00:07:15 +02:00
unknown
d819245e31 bump 3.0.6 2021-09-30 23:09:45 +02:00
unknown
6b55721ec5 mobile 2021-09-30 22:58:57 +02:00
unknown
13062c4a9b kreten 2021-09-30 22:58:50 +02:00
unknown
2188eaf1c6 high refresh rate patch 2021-09-30 22:37:30 +02:00
unknown
922d252c57 mobile 2021-09-30 21:57:55 +02:00
unknown
7a5290efe5 kreten 2021-09-30 21:57:52 +02:00
unknown
d1e6cc1fbb connectivity testing 2021-09-30 21:57:45 +02:00
unknown
b6a8696911 fix network bug 2021-09-30 21:57:21 +02:00
unknown
f7efd65f5e mobile 2021-09-30 19:51:29 +02:00
unknown
9963f65ab2 mobile 2021-09-30 19:16:53 +02:00
unknown
8d8c3a54c2 use default user-agent for config 2021-09-29 18:18:50 +02:00
unknown
c49d93c7b1 mobile 2021-09-26 20:59:04 +02:00
unknown
b0a7ab20d9 changelog 2021-09-26 01:45:10 +02:00
unknown
59a9e0e236 bump 3.0.5 2021-09-26 01:40:29 +02:00
unknown
e83202ba29 cleanup 2021-09-26 01:39:41 +02:00
unknown
d4489dd9d4 kreten 2021-09-26 01:33:36 +02:00
Unknown
debb4cac5b Merge pull request #26 from filc/analytics
Analytics
2021-09-26 01:32:15 +02:00
unknown
acbdf2aecb Merge branch 'master' into analytics 2021-09-26 01:31:39 +02:00
unknown
5d9ea4311a mobile 2021-09-20 12:53:04 +02:00
unknown
8aa24d7a32 mobile 2021-09-20 12:39:07 +02:00
unknown
9fbcef357e error report 2021-09-20 12:38:06 +02:00
unknown
e9423a8535 mobile 2021-09-19 18:47:20 +02:00
unknown
2a7265256d analytics client 2021-09-19 17:56:38 +02:00
unknown
d5ba231fcc fix database migration 2021-09-18 16:30:17 +02:00
unknown
68d92c4462 changelog 2021-09-18 14:27:44 +02:00
unknown
a46f6130cb ios dist fix 2021-09-18 14:25:20 +02:00
unknown
c6021e7a69 version bump 2021-09-18 12:24:56 +02:00
unknown
3ceb40ddb8 mobile 2021-09-18 12:24:38 +02:00
unknown
593292e860 kreten 2021-09-18 12:24:36 +02:00
unknown
ab952daa10 vibration cleanup 2021-09-18 11:57:01 +02:00
unknown
56b6eb3d2b haptics 2021-09-18 11:17:41 +02:00
unknown
26be20ac28 version bump 2021-09-12 20:25:22 +02:00
unknown
afd004b980 migrate users db 2021-09-12 20:24:39 +02:00
unknown
903cbba69a basics 2021-09-12 19:33:26 +02:00
unknown
6eb6a0cf16 kreten 2021-09-12 18:32:43 +02:00
unknown
abf9f9b13d todo 2021-09-11 19:26:53 +02:00
unknown
0471843919 mobile 2021-09-11 19:13:04 +02:00
unknown
bb83dd2137 version bump 2021-09-11 19:11:04 +02:00
unknown
d9b07525a4 mobile 2021-09-11 19:10:40 +02:00
unknown
92334406c5 kreten 2021-09-11 19:10:35 +02:00
unknown
506af7fa71 mobile 2021-09-11 18:41:52 +02:00
unknown
f5be03e5ce add role 2021-09-11 18:40:11 +02:00
unknown
2047784d8f mobile 2021-09-11 17:58:31 +02:00
unknown
037cf1df48 kreten 2021-09-11 17:58:27 +02:00
unknown
8238cc0678 role 2021-09-11 17:55:27 +02:00
unknown
f52a747268 mobile 2021-09-11 16:01:56 +02:00
unknown
c37bdc7aff kreten 2021-09-11 16:01:52 +02:00
unknown
9015ff2d6b mobile 2021-09-11 15:53:47 +02:00
unknown
a801503838 trim 2021-09-11 15:53:37 +02:00
55nknown
b0565ddcd9 mobile 2021-09-06 09:54:22 +02:00
55nknown
d8b6ba9a64 ios 2021-09-06 09:39:45 +02:00
unknown
549570a48c version bump 2021-09-05 19:54:30 +02:00
unknown
930e8d86b8 mobile 2021-09-05 19:36:14 +02:00
unknown
4aff7df96f kreten 2021-09-05 19:36:10 +02:00
unknown
c839ac178a android build fix 2021-09-05 19:35:08 +02:00
unknown
2c7efc0a18 kreten 2021-09-05 18:49:59 +02:00
unknown
5252813721 mobile 2021-09-05 18:48:23 +02:00
unknown
15d3daefed kreten 2021-09-05 18:48:18 +02:00
unknown
0729c13362 mobile 2021-09-05 16:58:33 +02:00
unknown
4bb978ba63 mobile 2021-09-05 16:55:43 +02:00
unknown
d9c071b5ae update gradle version 2021-09-05 16:53:13 +02:00
unknown
6ffe4b6c33 gitignore 2021-09-04 20:29:46 +02:00
unknown
7bd5d8bd5a version bump 2021-09-04 20:24:03 +02:00
unknown
c0ffa5d726 commit 2021-09-04 20:05:48 +02:00
unknown
befb107183 mobile 2021-09-04 12:11:15 +02:00
unknown
e56488094e mobile 2021-09-02 20:15:02 +02:00
unknown
74831c3e56 beta 5 2021-09-02 20:03:00 +02:00
unknown
9d3347d680 mobile 2021-09-02 20:01:13 +02:00
unknown
25100ab7d9 add test school 2021-09-02 18:08:31 +02:00
unknown
b509adcbc9 kreten 2021-09-02 17:00:03 +02:00
unknown
d421ae9031 capitalize fix 2021-09-02 16:59:57 +02:00
unknown
c3b79fcb3f today, yesterday fix 2021-09-02 01:34:32 +02:00
unknown
3cf57ac6d4 version bump 2021-09-02 01:28:06 +02:00
unknown
d72bf84a36 supporters 2021-09-02 01:27:59 +02:00
unknown
638b8c9413 limit text scaling 2021-09-02 01:27:29 +02:00
unknown
d5bdca698b commit 2021-09-02 00:18:58 +02:00
unknown
ea6b2ec73d commit 2021-09-02 00:18:41 +02:00
unknown
7ffb58c9ac art icon appears for geography 2021-09-01 14:11:30 +02:00
unknown
eaf2e2a6c4 vibration 2021-08-31 22:05:59 +02:00
unknown
8cf642fb72 kreten 2021-08-31 21:14:34 +02:00
unknown
47245b4551 bn 2021-08-31 21:14:11 +02:00
unknown
2b25abaf4d fix font scaling 2021-08-31 21:07:26 +02:00
unknown
adef77b5ed Migrating from Manifest defined custom splash screens 2021-08-31 21:05:34 +02:00
Unknown
8cc3107faf Merge pull request #2 from filc/ios
ios
2021-08-31 19:29:55 +02:00
Unknown
3284c5ba5a Create CONTRIBUTING.md 2021-08-31 18:51:54 +02:00
unknown
942a92f1f4 bn 2021-08-31 18:36:35 +02:00
unknown
ae3272e6da App name 2021-08-31 18:11:18 +02:00
unknown
67768b9de9 readme 2021-08-31 17:51:30 +02:00
unknown
aba24c0c2a commti 2021-08-31 17:40:20 +02:00
unknown
c55a5b9fe7 commit 2021-08-31 16:58:59 +02:00
unknown
fee63a2205 ios icons, launch screen 2021-08-31 16:58:41 +02:00
unknown
9bcf9eb391 bn 2021-08-31 16:58:21 +02:00
DarK-rtfm
fa9a61657b pubspec?? 2021-08-31 15:38:00 +02:00
unknown
33e4595e09 bn 2021-08-31 15:27:58 +02:00
unknown
91582c3b37 fix appstore issues 2021-08-31 15:21:21 +02:00
DarK-rtfm
61de95970e pubspec.ignore 2021-08-31 15:17:21 +02:00
unknown
e36347510f remove modules (deprecated) 2021-08-31 14:39:19 +02:00
unknown
e1429dedae ios version 2021-08-31 14:34:16 +02:00
108 changed files with 1642 additions and 1012 deletions

39
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,39 @@
# Contributing Guide
Köszönjük, ha programozással segíted a munkánkat!
A folytatáshoz szükséged lesz egy Linuxot vagy Windowst futtató számítógépre, minimális programozási tapasztalatra és egy kis angoltudásra.
Segít, ha nem csak kicsit tudsz programozni, és ha ismered a Gitet és a GitHubot ;)
## Miben segítsek?
Kérünk, **olyan dologgal járulj hozzá** a Filchez, ami valószínűleg **sok embernek hasznos lesz** majd. Szeretnénk egy minél teljeskörűbb iskolai asszisztenst létrehozni, de az iskolaspecifikus, vagy külön neked hasznos funkciók helye inkább legyen a saját forkod.
Fontos, hogy **mielőtt egy nagy volumenű projektbe belekezdesz, futtasd meg ötletedet a [Discord szerverünkön](https://filcnaplo.hu/discord),** ahol még azelőtt tudunk tanácsot adni, mielőtt sok-sok órát beleöltél volna egy esetleg felesleges dologba.
A legjobban annak örülünk, ha az [Issues](https://github.com/filcnaplo/filcnaplo/issues) oldalról szemezgetsz, **ha lehet, a [priority taggel megjelöltekkel kezdd](https://github.com/filcnaplo/filcnaplo/issues?q=is%3Aissue+is%3Aopen+label%3Apriority),** vagy ha új vagy a Flutterhez, ajánljuk figyelmedbe [ezeket a viszonylag könnyen javítható hibákat](https://github.com/filcnaplo/filcnaplo/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) (ha épp van ilyen).
## Hogyan segítsek?
Nem ígérhetünk itt sem programozás-, sem git-kurzust, de a projektspecifikus dolgokat leírjuk, és segítünk a Flutter feltelepítésében.
A Filc a Google által pár éve létrehozott **[Fluttert](https://flutter.dev/)** használja, aminek nyelve a **[Dart](https://dart.dev/)**. Ha ismered a C#-ot, Javát, C++t, vagy egyéb hasonló nyelvet, **nem fog gondot okozni a használata.** A felhasználói felület létrehozásában az is segíthet, ha foglalkoztál már korábban weboldalakkal.
Ha még nem használtál Fluttert, mindenképp böngészd át a [YouTube csatornájukat](https://www.youtube.com/channel/UCwXdFgeE9KYzlDdR7TG9cMw).
Könnyen tudsz kódot, vagy akár UI-t is tesztelni a [DartPad](https://dartpad.dev/) oldalon.
#### [Segítség a Flutter telepítéséhez és a forráskód futtatásához](/.github/SETUP.md)
Fontos: **Legyél a flutter beta verzióján!** Írd be: `flutter channel beta`
Ha nem értesz a Githez, ajánljuk figyelmedbe [ezt a cikket](https://medium.com/envienta-magyarorsz%C3%A1g/git-%C3%A9s-github-gyorstalpal%C3%B3-f2d78a732deb). Viszont arra kérünk, a Git használatát ne a Filcen próbáld ki először, hozz létre előbb egy saját Repót, és abba tesztelgess. Ha már nagyjából kitapasztaltad, várjuk hozzájárulásodat.
Készíts egy forkot a saját fiókod alá.
A Filc legfrissebb, épp fejlesztés alatt álló verzióját a [dev brancen](https://github.com/filcnaplo/filcnaplo/tree/dev) találod, kérjük ide commitolj, és ide célozd a forkodból a Pull Requested. Írd le benne, mit változtattál, és ha lehet, csatolj képernyőképet is.
Minél gyakrabban készíts minél részletesebben elnevezett commitokat, hogy el tudjunk tájékozódni az általad beküldött kódon.
---
Az általad fejlesztett funkciók mellé a changelogban odakerül GitHub felhasználóneved.
Ha jelentős és rendszeres hozzájáruló vagy, Discordon megkapod a `DEV` rangot.
Ha bárhol elakadtál, keress minket Discordon.
Jó fejlesztést kívánunk!

View File

@@ -5,21 +5,26 @@
#### Nem hivatalos e-napló alkalmazás az eKRÉTA rendszerhez
[![discord](https://img.shields.io/discord/712698455193157643?label=Discord)](http://filcnaplo.hu/discord)   [![Codemagic build status](https://api.codemagic.io/apps/612cc79b35b443d1b2c638ec/612cc79b35b443d1b2c638eb/status_badge.svg)](https://codemagic.io/apps/612cc79b35b443d1b2c638ec/612cc79b35b443d1b2c638eb/latest_build)
[![Downloads](https://img.shields.io/github/downloads-pre/filc/naplo/latest/total?color=%23&label=Downloads&logo=github&sort=semver)](https://github.com/filc/naplo/releases)   [![discord](https://img.shields.io/discord/712698455193157643?label=Discord)](http://filcnaplo.hu/discord)   [![Codemagic build status](https://api.codemagic.io/apps/612cc79b35b443d1b2c638ec/612cc79b35b443d1b2c638eb/status_badge.svg)](https://codemagic.io/apps/612cc79b35b443d1b2c638ec/612cc79b35b443d1b2c638eb/latest_build)
## Versions
- [Latest Stable](https://github.com/filc/naplo/releases/latest): `3.1.1`
- [Experiment 1]() (Tabs): `3.2.0-pre.1`
- [Experiment 2]() (Widgets): `3.2.0-pre.2`
## Setup
### Clone the project:
### Clone the project
```
$ git clone --recursive https://github.com/filc/naplo
$ cd naplo
```sh
git clone --recursive https://github.com/filc/naplo
cd naplo
```
### Run the app:
### Run the app
```
$ cd filcnaplo
$ flutter run
```sh
cd filcnaplo
flutter run
```

9
changelog.md Normal file
View File

@@ -0,0 +1,9 @@
- Mai nap jelzése az órarendben (kis pont alul)
- Jobb frissítés kezelés
- Frissítések megjelenítése a főoldalon
- Jobb link kezelés
- Hosszú szövegek mostmár kiférnek faliújságnál
- Könnyebben látható frissítések
- Grafikon design változások
- Tantárgy icon javítások
- Hibajavítások

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

@@ -9,3 +9,4 @@ GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
.project

View File

@@ -52,6 +52,7 @@ android {
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}
signingConfigs {
@@ -77,6 +78,7 @@ flutter {
}
dependencies {
implementation 'com.android.support:multidex:2.0.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'

View File

@@ -1,8 +1,7 @@
<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">
<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.SplashScreenDrawable" android:resource="@drawable/launch_background" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@@ -10,7 +9,7 @@
</activity>
<meta-data android:name="flutterEmbedding" android:value="2" />
</application>
<!-- Permissions -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.VIBRATE" />

View File

@@ -1,9 +1,9 @@
buildscript {
ext.kotlin_version = '1.3.50'
ext.kotlin_version = '1.6.10'
ext {
compileSdkVersion = 30
targetSdkVersion = 30
compileSdkVersion = 31
targetSdkVersion = 31
appCompatVersion = "1.1.0"
}
@@ -13,7 +13,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.3'
classpath 'com.android.tools.build:gradle:4.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
@@ -33,8 +33,8 @@ subprojects {
afterEvaluate {project ->
if (project.plugins.hasPlugin('android') || project.plugins.hasPlugin('android-library')) {
android {
compileSdkVersion 30
buildToolsVersion '30.0.3'
compileSdkVersion 31
buildToolsVersion '31.0.0'
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

View File

@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"

View File

@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"

41
filcnaplo/ios/Podfile Normal file
View File

@@ -0,0 +1,41 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end

123
filcnaplo/ios/Podfile.lock Normal file
View File

@@ -0,0 +1,123 @@
PODS:
- DKImagePickerController/Core (4.3.2):
- DKImagePickerController/ImageDataManager
- DKImagePickerController/Resource
- DKImagePickerController/ImageDataManager (4.3.2)
- DKImagePickerController/PhotoGallery (4.3.2):
- DKImagePickerController/Core
- DKPhotoGallery
- DKImagePickerController/Resource (4.3.2)
- DKPhotoGallery (0.0.17):
- DKPhotoGallery/Core (= 0.0.17)
- DKPhotoGallery/Model (= 0.0.17)
- DKPhotoGallery/Preview (= 0.0.17)
- DKPhotoGallery/Resource (= 0.0.17)
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Core (0.0.17):
- DKPhotoGallery/Model
- DKPhotoGallery/Preview
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Model (0.0.17):
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Preview (0.0.17):
- DKPhotoGallery/Model
- DKPhotoGallery/Resource
- SDWebImage
- SwiftyGif
- DKPhotoGallery/Resource (0.0.17):
- SDWebImage
- SwiftyGif
- file_picker (0.0.1):
- DKImagePickerController/PhotoGallery
- Flutter
- Flutter (1.0.0)
- flutter_custom_tabs (0.0.1):
- Flutter
- FMDB (2.7.5):
- FMDB/standard (= 2.7.5)
- FMDB/standard (2.7.5)
- open_file (0.0.1):
- Flutter
- package_info_plus (0.4.5):
- Flutter
- path_provider (0.0.1):
- Flutter
- "permission_handler (5.1.0+2)":
- Flutter
- SDWebImage (5.11.1):
- SDWebImage/Core (= 5.11.1)
- SDWebImage/Core (5.11.1)
- share_plus (0.0.1):
- Flutter
- sqflite (0.0.2):
- Flutter
- FMDB (>= 2.7.5)
- SwiftyGif (5.4.0)
- url_launcher (0.0.1):
- Flutter
DEPENDENCIES:
- file_picker (from `.symlinks/plugins/file_picker/ios`)
- Flutter (from `Flutter`)
- flutter_custom_tabs (from `.symlinks/plugins/flutter_custom_tabs/ios`)
- open_file (from `.symlinks/plugins/open_file/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider (from `.symlinks/plugins/path_provider/ios`)
- permission_handler (from `.symlinks/plugins/permission_handler/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- sqflite (from `.symlinks/plugins/sqflite/ios`)
- url_launcher (from `.symlinks/plugins/url_launcher/ios`)
SPEC REPOS:
trunk:
- DKImagePickerController
- DKPhotoGallery
- FMDB
- SDWebImage
- SwiftyGif
EXTERNAL SOURCES:
file_picker:
:path: ".symlinks/plugins/file_picker/ios"
Flutter:
:path: Flutter
flutter_custom_tabs:
:path: ".symlinks/plugins/flutter_custom_tabs/ios"
open_file:
:path: ".symlinks/plugins/open_file/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
path_provider:
:path: ".symlinks/plugins/path_provider/ios"
permission_handler:
:path: ".symlinks/plugins/permission_handler/ios"
share_plus:
:path: ".symlinks/plugins/share_plus/ios"
sqflite:
:path: ".symlinks/plugins/sqflite/ios"
url_launcher:
:path: ".symlinks/plugins/url_launcher/ios"
SPEC CHECKSUMS:
DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
flutter_custom_tabs: 7a10a08686955cb748e5d26e0ae586d30689bf89
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
open_file: 02eb5cb6b21264bd3a696876f5afbfb7ca4f4b7d
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
permission_handler: ccb20a9fad0ee9b1314a52b70b76b473c5f8dab0
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
SwiftyGif: 5d4af95df24caf1c570dbbcb32a3b8a0763bc6d7
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
COCOAPODS: 1.11.0

View File

@@ -8,6 +8,7 @@
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
373A6ECB5FC71FE9D8AF2EDB /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F0ADD56276103500A3016C8 /* Pods_Runner.framework */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
@@ -31,10 +32,13 @@
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
1F0ADD56276103500A3016C8 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
707F8089D970F81C480F73C4 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
80777CF254888CE770D5F909 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -42,6 +46,7 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
98578F0EBCC6D3FF8391AAEB /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -49,12 +54,32 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
373A6ECB5FC71FE9D8AF2EDB /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
6640A963014A9D4F31026053 /* Frameworks */ = {
isa = PBXGroup;
children = (
1F0ADD56276103500A3016C8 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
91FEB6212755D596FFFFEC73 /* Pods */ = {
isa = PBXGroup;
children = (
80777CF254888CE770D5F909 /* Pods-Runner.debug.xcconfig */,
98578F0EBCC6D3FF8391AAEB /* Pods-Runner.release.xcconfig */,
707F8089D970F81C480F73C4 /* Pods-Runner.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
@@ -72,6 +97,8 @@
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
91FEB6212755D596FFFFEC73 /* Pods */,
6640A963014A9D4F31026053 /* Frameworks */,
);
sourceTree = "<group>";
};
@@ -105,12 +132,14 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
779338C8D92BCBC36F75F791 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
71459C0EB905E05018E3D78F /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -183,6 +212,45 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
71459C0EB905E05018E3D78F /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
779338C8D92BCBC36F75F791 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -468,4 +536,4 @@
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
}

View File

@@ -4,4 +4,7 @@
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@@ -1,122 +1,208 @@
{
"images" : [
"images": [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
"size": "60x60",
"expected-size": "180",
"filename": "180.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "iphone",
"scale": "3x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
"size": "40x40",
"expected-size": "80",
"filename": "80.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "iphone",
"scale": "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
"size": "40x40",
"expected-size": "120",
"filename": "120.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "iphone",
"scale": "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
"size": "60x60",
"expected-size": "120",
"filename": "120.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "iphone",
"scale": "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
"size": "57x57",
"expected-size": "57",
"filename": "57.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "iphone",
"scale": "1x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
"size": "29x29",
"expected-size": "58",
"filename": "58.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "iphone",
"scale": "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
"size": "29x29",
"expected-size": "29",
"filename": "29.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "iphone",
"scale": "1x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
"size": "29x29",
"expected-size": "87",
"filename": "87.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "iphone",
"scale": "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
"size": "57x57",
"expected-size": "114",
"filename": "114.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "iphone",
"scale": "2x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
"size": "20x20",
"expected-size": "40",
"filename": "40.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "iphone",
"scale": "2x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
"size": "20x20",
"expected-size": "60",
"filename": "60.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "iphone",
"scale": "3x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
"size": "1024x1024",
"filename": "1024.png",
"expected-size": "1024",
"idiom": "ios-marketing",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"scale": "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
"size": "40x40",
"expected-size": "80",
"filename": "80.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "ipad",
"scale": "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
"size": "72x72",
"expected-size": "72",
"filename": "72.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "ipad",
"scale": "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
"size": "76x76",
"expected-size": "152",
"filename": "152.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "ipad",
"scale": "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
"size": "50x50",
"expected-size": "100",
"filename": "100.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "ipad",
"scale": "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
"size": "29x29",
"expected-size": "58",
"filename": "58.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "ipad",
"scale": "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
"size": "76x76",
"expected-size": "76",
"filename": "76.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "ipad",
"scale": "1x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
"size": "29x29",
"expected-size": "29",
"filename": "29.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "ipad",
"scale": "1x"
},
{
"size": "50x50",
"expected-size": "50",
"filename": "50.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "ipad",
"scale": "1x"
},
{
"size": "72x72",
"expected-size": "144",
"filename": "144.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "ipad",
"scale": "2x"
},
{
"size": "40x40",
"expected-size": "40",
"filename": "40.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "ipad",
"scale": "1x"
},
{
"size": "83.5x83.5",
"expected-size": "167",
"filename": "167.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "ipad",
"scale": "2x"
},
{
"size": "20x20",
"expected-size": "20",
"filename": "20.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "ipad",
"scale": "1x"
},
{
"size": "20x20",
"expected-size": "40",
"filename": "40.png",
"folder": "Assets.xcassets/AppIcon.appiconset/",
"idiom": "ipad",
"scale": "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"info": {
"version": 1,
"author": "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 852 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 B

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -39,6 +39,6 @@
</scenes>
<resources>
<image name="LaunchImage" width="512" height="512"/>
<image name="LaunchBackground" width="1" height="1"/>
<image name="LaunchBackground" width="1080" height="1920"/>
</resources>
</document>

View File

@@ -1,52 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>filcnaplo</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIStatusBarHidden</key>
<false/>
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Filc Napló</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIStatusBarHidden</key>
<false/>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>https</string>
<string>http</string>
</array>
</dict>
<key>NSPhotoLibraryUsageDescription</key>
<string>The app requires the photo library to set a custom profile picture.</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
</dict>
</plist>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>production</string>
</dict>
</plist>

View File

@@ -1,24 +1,44 @@
// ignore_for_file: avoid_print
import 'dart:convert';
import 'package:filcnaplo/models/config.dart';
import 'package:filcnaplo/models/news.dart';
import 'package:filcnaplo/models/release.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/models/supporter.dart';
import 'package:filcnaplo_kreta_api/models/school.dart';
import 'package:http/http.dart' as http;
import 'package:connectivity_plus/connectivity_plus.dart';
class FilcAPI {
static const SCHOOL_LIST = "https://filcnaplo.hu/v2/school_list.json";
static const CONFIG = "https://filcnaplo.hu/v2/config.json";
static const NEWS = "https://filcnaplo.hu/v2/news.json";
static const REPO = "filc/naplo";
static const RELEASES = "https://api.github.com/repos/$REPO/releases";
// Public API
static const schoolList = "https://filcnaplo.hu/v2/school_list.json";
static const news = "https://filcnaplo.hu/v2/news.json";
static const supporters = "https://filcnaplo.hu/v2/supporters.json";
// Private API
static const config = "https://api.filcnaplo.hu/config";
static const reportApi = "https://api.filcnaplo.hu/report";
// Updates
static const repo = "filc/naplo";
static const releases = "https://api.github.com/repos/$repo/releases";
static Future<bool> checkConnectivity() async => (await Connectivity().checkConnectivity()) != ConnectivityResult.none;
static Future<List<School>?> getSchools() async {
try {
http.Response res = await http.get(Uri.parse(SCHOOL_LIST));
http.Response res = await http.get(Uri.parse(schoolList));
if (res.statusCode == 200) {
return (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();
schools.add(School(
city: "Tiszabura",
instituteCode: "supporttest-reni-tiszabura-teszt01",
name: "FILC Éles Reni tiszabura-teszt",
));
return schools;
} else {
throw "HTTP ${res.statusCode}: ${res.body}";
}
@@ -27,15 +47,22 @@ class FilcAPI {
}
}
static Future<Config?> getConfig() async {
static Future<Config?> getConfig(SettingsProvider settings) async {
Map<String, String> headers = {
"x-filc-id": settings.xFilcId,
"user-agent": SettingsProvider.defaultSettings().config.userAgent,
};
try {
http.Response res = await http.get(Uri.parse(CONFIG));
http.Response res = await http.get(Uri.parse(config), headers: headers);
if (res.statusCode == 200) {
return Config.fromJson(jsonDecode(res.body));
} else {
throw "HTTP ${res.statusCode}: ${res.body}";
} else if (res.statusCode == 429) {
res = await http.get(Uri.parse(config));
if (res.statusCode == 200) return Config.fromJson(jsonDecode(res.body));
}
throw "HTTP ${res.statusCode}: ${res.body}";
} catch (error) {
print("ERROR: FilcAPI.getConfig: $error");
}
@@ -43,7 +70,7 @@ class FilcAPI {
static Future<List<News>?> getNews() async {
try {
http.Response res = await http.get(Uri.parse(NEWS));
http.Response res = await http.get(Uri.parse(news));
if (res.statusCode == 200) {
return (jsonDecode(res.body) as List).cast<Map>().map((e) => News.fromJson(e)).toList();
@@ -55,9 +82,23 @@ class FilcAPI {
}
}
static Future<Supporters?> getSupporters() async {
try {
http.Response res = await http.get(Uri.parse(supporters));
if (res.statusCode == 200) {
return Supporters.fromJson(jsonDecode(res.body));
} else {
throw "HTTP ${res.statusCode}: ${res.body}";
}
} catch (error) {
print("ERROR: FilcAPI.getSupporters: $error");
}
}
static Future<List<Release>?> getReleases() async {
try {
http.Response res = await http.get(Uri.parse(RELEASES));
http.Response res = await http.get(Uri.parse(releases));
if (res.statusCode == 200) {
return (jsonDecode(res.body) as List).cast<Map>().map((e) => Release.fromJson(e)).toList();
@@ -70,7 +111,7 @@ class FilcAPI {
}
static Future<http.StreamedResponse?> downloadRelease(Release release) {
if (release.downloads.length > 0) {
if (release.downloads.isNotEmpty) {
try {
var client = http.Client();
var request = http.Request('GET', Uri.parse(release.downloads.first));
@@ -82,4 +123,35 @@ class FilcAPI {
return Future.value(null);
}
static Future<void> sendReport(ErrorReport report) async {
try {
http.Response res = await http.post(Uri.parse(reportApi), body: {
"os": report.os,
"version": report.version,
"error": report.error,
"stack_trace": report.stack,
});
if (res.statusCode != 200) {
throw "HTTP ${res.statusCode}: ${res.body}";
}
} catch (error) {
print("ERROR: FilcAPI.sendReport: $error");
}
}
}
class ErrorReport {
String stack;
String os;
String version;
String error;
ErrorReport({
required this.stack,
required this.os,
required this.version,
required this.error,
});
}

View File

@@ -1,3 +1,6 @@
// ignore_for_file: avoid_print
import 'package:filcnaplo/utils/jwt.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/exam_provider.dart';
@@ -10,10 +13,8 @@ import 'package:filcnaplo/api/providers/user_provider.dart';
import 'package:filcnaplo/api/providers/database_provider.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/models/user.dart';
import 'package:filcnaplo/utils/jwt.dart';
import 'package:filcnaplo_kreta_api/client/api.dart';
import 'package:filcnaplo_kreta_api/client/client.dart';
import 'package:filcnaplo_kreta_api/models/message.dart';
import 'package:filcnaplo_kreta_api/models/student.dart';
import 'package:filcnaplo_kreta_api/models/week.dart';
import 'package:flutter/material.dart';
@@ -65,12 +66,14 @@ Future loginApi({
try {
Provider.of<KretaClient>(context, listen: false).accessToken = res["access_token"];
Map? studentJson = await Provider.of<KretaClient>(context, listen: false).getAPI(KretaAPI.student(instituteCode));
Student student = Student.fromJson(studentJson!);
var user = User(
username: username,
password: password,
instituteCode: instituteCode,
name: JwtUtils.getNameFromJWT(res["access_token"]) ?? "?",
student: Student.fromJson(studentJson!),
name: student.name,
student: student,
role: JwtUtils.getRoleFromJWT(res["access_token"])!,
);
if (onLogin != null) onLogin(user);
@@ -82,14 +85,16 @@ Future loginApi({
// Get user data
try {
await Provider.of<GradeProvider>(context, listen: false).fetch();
await Provider.of<TimetableProvider>(context, listen: false).fetch(week: Week.current());
await Provider.of<ExamProvider>(context, listen: false).fetch();
await Provider.of<HomeworkProvider>(context, listen: false).fetch();
await Provider.of<MessageProvider>(context, listen: false).fetch(type: MessageType.inbox);
await Provider.of<NoteProvider>(context, listen: false).fetch();
await Provider.of<EventProvider>(context, listen: false).fetch();
await Provider.of<AbsenceProvider>(context, listen: false).fetch();
await Future.wait([
Provider.of<GradeProvider>(context, listen: false).fetch(),
Provider.of<TimetableProvider>(context, listen: false).fetch(week: Week.current()),
Provider.of<ExamProvider>(context, listen: false).fetch(),
Provider.of<HomeworkProvider>(context, listen: false).fetch(),
Provider.of<MessageProvider>(context, listen: false).fetchAll(),
Provider.of<NoteProvider>(context, listen: false).fetch(),
Provider.of<EventProvider>(context, listen: false).fetch(),
Provider.of<AbsenceProvider>(context, listen: false).fetch(),
]);
} catch (error) {
print("WARNING: failed to fetch user data: $error");
}

View File

@@ -1,6 +1,9 @@
import 'dart:io';
import 'package:filcnaplo/database/query.dart';
import 'package:filcnaplo/database/store.dart';
import 'package:sqflite/sqflite.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
class DatabaseProvider {
// late Database _database;
@@ -10,8 +13,14 @@ class DatabaseProvider {
late UserDatabaseStore userStore;
Future<void> init() async {
var db = await openDatabase("app.db");
// _database = db;
Database db;
if (Platform.isLinux || Platform.isWindows) {
db = await databaseFactoryFfi.openDatabase("app.db");
} else {
db = await openDatabase("app.db");
}
query = DatabaseQuery(db: db);
store = DatabaseStore(db: db);
userQuery = UserDatabaseQuery(db: db);

View File

@@ -72,10 +72,11 @@ class NewsProvider extends ChangeNotifier {
Provider.of<SettingsProvider>(_context, listen: false).update(_context, newsState: _state);
if (_fresh > 0)
if (_fresh > 0) {
show = true;
else
} else {
show = false;
}
notifyListeners();
}

View File

@@ -0,0 +1,75 @@
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/widgets.dart';
import 'package:http/http.dart' as http;
enum Status { network, maintenance, syncing }
class StatusProvider extends ChangeNotifier {
final List<Status> _stack = [];
double _progress = 0.0;
StatusProvider() {
_handleNetworkChanges();
}
Status? getStatus() => _stack.isNotEmpty ? _stack[0] : null;
// Status progress from 0.0 to 1.0
double get progress => _progress;
void _handleNetworkChanges() {
Connectivity().onConnectivityChanged.listen((event) {
if (event == ConnectivityResult.none) {
if (!_stack.contains(Status.network)) {
_stack.insert(0, Status.network);
notifyListeners();
}
} else {
if (_stack.contains(Status.network)) {
_stack.remove(Status.network);
notifyListeners();
}
}
});
}
void triggerRequest(http.Response res) {
if (res.headers.containsKey("x-maintenance-mode") || res.statusCode == 503) {
if (!_stack.contains(Status.maintenance)) {
_stack.insert(0, Status.maintenance);
notifyListeners();
}
} else {
if (_stack.contains(Status.maintenance)) {
_stack.remove(Status.maintenance);
notifyListeners();
}
}
}
void triggerSync({required int current, required int max}) {
double prev = _progress;
if (!_stack.contains(Status.syncing)) {
_stack.add(Status.syncing);
_progress = 0.0;
notifyListeners();
}
if (max == 0) {
_progress = 0.0;
} else {
_progress = current / max;
}
if (_progress == 1.0) {
notifyListeners();
// Wait for animation
Future.delayed(const Duration(milliseconds: 250), () {
_stack.remove(Status.syncing);
notifyListeners();
});
} else if (progress != prev) {
notifyListeners();
}
}
}

View File

@@ -0,0 +1,69 @@
import 'package:filcnaplo/api/providers/database_provider.dart';
import 'package:filcnaplo/api/providers/status_provider.dart';
import 'package:filcnaplo/api/providers/user_provider.dart';
import 'package:filcnaplo_kreta_api/client/api.dart';
import 'package:filcnaplo_kreta_api/client/client.dart';
import 'package:filcnaplo_kreta_api/models/student.dart';
import 'package:filcnaplo_kreta_api/models/week.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/exam_provider.dart';
import 'package:filcnaplo_kreta_api/providers/grade_provider.dart';
import 'package:filcnaplo_kreta_api/providers/homework_provider.dart';
import 'package:filcnaplo_kreta_api/providers/message_provider.dart';
import 'package:filcnaplo_kreta_api/providers/note_provider.dart';
import 'package:filcnaplo_kreta_api/providers/timetable_provider.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
// Mutex
bool lock = false;
Future<void> syncAll(BuildContext context) {
if (lock) return Future.value();
// Lock
lock = true;
// ignore: avoid_print
print("INFO Syncing all");
UserProvider user = Provider.of<UserProvider>(context, listen: false);
StatusProvider statusProvider = Provider.of<StatusProvider>(context, listen: false);
List<Future<void>> tasks = [];
int taski = 0;
Future<void> _syncStatus(Future<void> future) async {
await future.onError((error, stackTrace) => null);
taski++;
statusProvider.triggerSync(current: taski, max: tasks.length);
}
tasks = [
_syncStatus(Provider.of<GradeProvider>(context, listen: false).fetch()),
_syncStatus(Provider.of<TimetableProvider>(context, listen: false).fetch(week: Week.current())),
_syncStatus(Provider.of<ExamProvider>(context, listen: false).fetch()),
_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<NoteProvider>(context, listen: false).fetch()),
_syncStatus(Provider.of<EventProvider>(context, listen: false).fetch()),
_syncStatus(Provider.of<AbsenceProvider>(context, listen: false).fetch()),
// Sync student
_syncStatus(() async {
if (user.user == null) return;
Map? studentJson = await Provider.of<KretaClient>(context, listen: false).getAPI(KretaAPI.student(user.instituteCode!));
if (studentJson == null) return;
Student student = Student.fromJson(studentJson);
user.user?.name = student.name;
// Store user
await Provider.of<DatabaseProvider>(context, listen: false).store.storeUser(user.user!);
}()),
];
return Future.wait(tasks)
// Unlock
.then((value) => lock = false);
}

View File

@@ -9,7 +9,7 @@ class UpdateProvider extends ChangeNotifier {
// Private
late List<Release> _releases;
bool _available = false;
bool get available => _available && _releases.length > 0;
bool get available => _available && _releases.isNotEmpty;
PackageInfo? _packageInfo;
// Public
@@ -23,6 +23,8 @@ class UpdateProvider extends ChangeNotifier {
PackageInfo.fromPlatform().then((value) => _packageInfo = value);
}
String get currentVersion => _packageInfo?.version ?? "";
Future<void> fetch() async {
if (!Platform.isAndroid) return;
@@ -30,9 +32,10 @@ class UpdateProvider extends ChangeNotifier {
_releases.sort((a, b) => -a.version.compareTo(b.version));
// Check for new releases
if (_releases.length > 0) {
print("INFO: New update: ${releases.first.version}");
_available = _packageInfo != null && _releases.first.version.compareTo(Version.fromString(_packageInfo?.version ?? "")) == 1;
if (_releases.isNotEmpty) {
_available = _packageInfo != null && _releases.first.version.compareTo(Version.fromString(currentVersion)) == 1;
// ignore: avoid_print
if (_available) print("INFO: New update: ${releases.first.version}");
notifyListeners();
}
}

View File

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

View File

@@ -1,6 +1,9 @@
import 'dart:math';
import 'package:filcnaplo/api/client.dart';
import 'package:filcnaplo/api/providers/news_provider.dart';
import 'package:filcnaplo/api/providers/database_provider.dart';
import 'package:filcnaplo/api/providers/status_provider.dart';
import 'package:filcnaplo/models/config.dart';
import 'package:filcnaplo/theme.dart';
import 'package:filcnaplo_kreta_api/client/client.dart';
@@ -29,6 +32,7 @@ import 'package:filcnaplo_kreta_api/providers/timetable_provider.dart';
import 'package:filcnaplo/api/providers/user_provider.dart';
import 'package:filcnaplo/api/providers/update_provider.dart';
import 'package:filcnaplo_mobile_ui/pages/grades/calculator/grade_calculator_provider.dart';
// import 'package:flutter_displaymode/flutter_displaymode.dart';
class App extends StatelessWidget {
final SettingsProvider settings;
@@ -36,25 +40,29 @@ class App extends StatelessWidget {
final DatabaseProvider database;
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
Widget build(BuildContext context) {
setSystemChrome(context);
// Set high refresh mode #28
// if (Platform.isAndroid) FlutterDisplayMode.setHighRefreshRate();
WidgetsBinding.instance?.addPostFrameCallback((_) {
FilcAPI.getConfig().then((Config? config) {
settings.update(context, database: database, config: config ?? Config.fromJson({}));
FilcAPI.getConfig(settings).then((Config? config) {
if (config != null) settings.update(context, database: database, config: config);
});
});
return I18n(
initialLocale: Locale(settings.language, settings.language),
initialLocale: Locale(settings.language, settings.language.toUpperCase()),
child: MultiProvider(
providers: [
ChangeNotifierProvider<SettingsProvider>(create: (_) => settings),
ChangeNotifierProvider<UserProvider>(create: (_) => user),
ChangeNotifierProvider<StatusProvider>(create: (context) => StatusProvider()),
Provider<KretaClient>(create: (context) => KretaClient(context: context, userAgent: settings.config.userAgent)),
Provider<DatabaseProvider>(create: (context) => database),
ChangeNotifierProvider<ThemeModeObserver>(create: (context) => ThemeModeObserver(initialTheme: settings.theme)),
@@ -74,24 +82,47 @@ class App extends StatelessWidget {
ChangeNotifierProvider<GradeCalculatorProvider>(create: (context) => GradeCalculatorProvider(context)),
],
child: Consumer<ThemeModeObserver>(
builder: (context, themeMode, child) => MaterialApp(
title: "Filc Napló",
debugShowCheckedModeBanner: false,
theme: AppTheme.lightTheme(context),
darkTheme: AppTheme.darkTheme(context),
themeMode: themeMode.themeMode,
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
const Locale('en'),
const Locale('hu'),
const Locale('de'),
],
onGenerateRoute: (settings) => rootNavigator(settings),
initialRoute: user.getUsers().length > 0 ? "navigation" : "login"),
builder: (context, themeMode, child) {
return MaterialApp(
builder: (context, child) {
// Limit font size scaling to 1.0
double textScaleFactor = min(MediaQuery.of(context).textScaleFactor, 1.0);
return MediaQuery(
data: MediaQuery.of(context).copyWith(textScaleFactor: textScaleFactor),
child: child ?? Container(),
);
},
title: "Filc Napló",
debugShowCheckedModeBanner: false,
theme: AppTheme.lightTheme(context),
darkTheme: AppTheme.darkTheme(context),
themeMode: themeMode.themeMode,
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
Locale('en', 'EN'),
Locale('hu', 'HU'),
Locale('de', 'DE'),
],
localeListResolutionCallback: (locales, supported) {
Locale locale = const Locale('hu', 'HU');
for (var loc in locales ?? []) {
if (supported.contains(loc)) {
locale = loc;
break;
}
}
return locale;
},
onGenerateRoute: (settings) => rootNavigator(settings),
initialRoute: user.getUsers().isNotEmpty ? "navigation" : "login");
},
),
),
);
@@ -101,15 +132,15 @@ class App extends StatelessWidget {
// if platform == android || platform == ios
switch (route.name) {
case "login_back":
return CupertinoPageRoute(builder: (context) => LoginScreen(back: true));
return CupertinoPageRoute(builder: (context) => const LoginScreen(back: true));
case "login":
return _rootRoute(LoginScreen());
return _rootRoute(const LoginScreen());
case "navigation":
return _rootRoute(Navigation());
return _rootRoute(const NavigationScreen());
case "login_to_navigation":
return loginRoute(Navigation());
return loginRoute(const NavigationScreen());
case "settings":
return settingsRoute(SettingsScreen());
return settingsRoute(const SettingsScreen());
}
// else if platform == windows || ...
}

View File

@@ -1,15 +1,26 @@
// ignore_for_file: avoid_print
import 'dart:io';
import 'package:filcnaplo/database/struct.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:sqflite/sqflite.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
Future<Database> initDB() async {
// await deleteDatabase('app.db'); // for debugging
var db = await openDatabase('app.db');
Database db;
if (Platform.isLinux || Platform.isWindows) {
sqfliteFfiInit();
db = await databaseFactoryFfi.openDatabase("app.db");
} else {
db = await openDatabase("app.db");
}
var settingsDB = await createSettingsTable(db);
// Create table Users
await db.execute("CREATE TABLE IF NOT EXISTS users (id TEXT NOT NULL, name TEXT, username TEXT, password TEXT, institute_code TEXT, student TEXT)");
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)");
@@ -18,7 +29,9 @@ Future<Database> initDB() async {
await db.insert("settings", SettingsProvider.defaultSettings().toMap());
}
await migrateDB(db, settingsDB.struct.keys);
// Migrate Databases
await migrateDB(db, "settings", settingsDB.struct.keys, SettingsProvider.defaultSettings().toMap(), createSettingsTable);
await migrateDB(db, "users", usersDB.struct.keys, {"role": 0}, createUsersTable);
return db;
}
@@ -28,8 +41,9 @@ Future<DatabaseStruct> createSettingsTable(Database db) async {
"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
"vibrate": int, "vibration_strength": int, "ab_weeks": int, "swap_ab_weeks": int,
"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
@@ -38,35 +52,67 @@ Future<DatabaseStruct> createSettingsTable(Database db) async {
return settingsDB;
}
Future<void> migrateDB(Database db, Iterable<String> keys) async {
var settings = (await db.query("settings"))[0];
Future<DatabaseStruct> createUsersTable(Database db) async {
var usersDB = DatabaseStruct(
{"id": String, "name": String, "username": String, "password": String, "institute_code": String, "student": String, "role": int});
bool migrationRequired = keys.any((key) => !settings.containsKey(key) || settings[key] == null);
// Create table Users
await db.execute("CREATE TABLE IF NOT EXISTS users ($usersDB)");
if (migrationRequired) {
var defaultSettings = SettingsProvider.defaultSettings();
var settingsCopy = Map<String, dynamic>.from(settings);
return usersDB;
}
// Delete settings
await db.execute("drop table settings");
Future<void> migrateDB(
Database db,
String table,
Iterable<String> keys,
Map<String, Object?> defaultValues,
Future<DatabaseStruct> Function(Database) create,
) async {
var originalRows = await db.query(table);
// Fill missing columns
keys.forEach((key) {
if (!keys.contains(key)) {
print("debug: dropping $key");
settingsCopy.remove(key);
if (originalRows.isEmpty) {
await db.execute("drop table $table");
await create(db);
return;
}
List<Map<String, dynamic>> migrated = [];
await Future.forEach<Map<String, Object?>>(originalRows, (original) async {
bool migrationRequired = keys.any((key) => !original.containsKey(key) || original[key] == null);
if (migrationRequired) {
print("INFO: Migrating $table");
var copy = Map<String, dynamic>.from(original);
// Fill missing columns
for (var key in keys) {
if (!keys.contains(key)) {
print("DEBUG: dropping $key");
copy.remove(key);
}
if (!original.containsKey(key) || original[key] == null) {
print("DEBUG: migrating $key");
copy[key] = defaultValues[key];
}
}
if (!settings.containsKey(key) || settings[key] == null) {
print("DEBUG: migrating $key");
settingsCopy[key] = defaultSettings.toMap()[key];
}
migrated.add(copy);
}
});
if (migrated.isNotEmpty) {
// Delete table
await db.execute("drop table $table");
// Recreate table
await create(db);
await Future.forEach(migrated, (Map<String, dynamic> copy) async {
await db.insert(table, copy);
});
// Recreate settings
await createSettingsTable(db);
await db.insert("settings", settingsCopy);
print("INFO: Database migrated");
}
}

View File

@@ -1,6 +1,6 @@
import 'dart:convert';
import 'package:filcnaplo/models/user.dart';
import 'package:sqflite/sqflite.dart';
import 'package:sqflite_common/sqlite_api.dart';
// Models
import 'package:filcnaplo/models/settings.dart';
@@ -28,9 +28,9 @@ class DatabaseQuery {
Future<UserProvider> getUsers() async {
var userProvider = UserProvider();
List<Map> usersMap = await db.query("users");
usersMap.forEach((user) {
for (var user in usersMap) {
userProvider.addUser(User.fromMap(user));
});
}
return userProvider;
}
}
@@ -42,7 +42,7 @@ class UserDatabaseQuery {
Future<List<Grade>> getGrades({required String userId}) async {
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?;
if (gradesJson == null) return [];
List<Grade> grades = (jsonDecode(gradesJson) as List).map((e) => Grade.fromJson(e)).toList();
@@ -51,7 +51,7 @@ class UserDatabaseQuery {
Future<List<Lesson>> getLessons({required String userId}) async {
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?;
if (lessonsJson == null) return [];
List<Lesson> lessons = (jsonDecode(lessonsJson) as List).map((e) => Lesson.fromJson(e)).toList();
@@ -60,7 +60,7 @@ class UserDatabaseQuery {
Future<List<Exam>> getExams({required String userId}) async {
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?;
if (examsJson == null) return [];
List<Exam> exams = (jsonDecode(examsJson) as List).map((e) => Exam.fromJson(e)).toList();
@@ -69,7 +69,7 @@ class UserDatabaseQuery {
Future<List<Homework>> getHomework({required String userId}) async {
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?;
if (homeworkJson == null) return [];
List<Homework> homework = (jsonDecode(homeworkJson) as List).map((e) => Homework.fromJson(e)).toList();
@@ -78,7 +78,7 @@ class UserDatabaseQuery {
Future<List<Message>> getMessages({required String userId}) async {
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?;
if (messagesJson == null) return [];
List<Message> messages = (jsonDecode(messagesJson) as List).map((e) => Message.fromJson(e)).toList();
@@ -87,7 +87,7 @@ class UserDatabaseQuery {
Future<List<Note>> getNotes({required String userId}) async {
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?;
if (notesJson == null) return [];
List<Note> notes = (jsonDecode(notesJson) as List).map((e) => Note.fromJson(e)).toList();
@@ -96,7 +96,7 @@ class UserDatabaseQuery {
Future<List<Event>> getEvents({required String userId}) async {
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?;
if (eventsJson == null) return [];
List<Event> events = (jsonDecode(eventsJson) as List).map((e) => Event.fromJson(e)).toList();
@@ -105,7 +105,7 @@ class UserDatabaseQuery {
Future<List<Absence>> getAbsences({required String userId}) async {
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?;
if (absebcesJson == null) return [];
List<Absence> absebces = (jsonDecode(absebcesJson) as List).map((e) => Absence.fromJson(e)).toList();

View File

@@ -1,5 +1,5 @@
import 'dart:convert';
import 'package:sqflite/sqflite.dart';
import 'package:sqflite_common/sqlite_api.dart';
// Models
import 'package:filcnaplo/models/settings.dart';
@@ -24,7 +24,7 @@ class DatabaseStore {
Future<void> storeUser(User user) async {
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]);
} else {
await db.insert("users", user.toMap());

View File

@@ -15,7 +15,7 @@ class DatabaseStruct {
break;
}
return "${name} ${typeName.toUpperCase()}";
return "$name ${typeName.toUpperCase()} ${name == 'id' ? 'NOT NULL' : ''}";
}
@override

View File

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

View File

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

View File

@@ -7,16 +7,16 @@ class SubjectIcon {
static IconData? lookup({Subject? subject, String? subjectName}) {
assert(!(subject == null && subjectName == null));
String name = subject?.name.toLowerCase().specialChars() ?? subjectName ?? "";
String name = (subject?.name ?? subjectName ?? "").toLowerCase().specialChars().trim();
String category = subject?.category.description.toLowerCase().specialChars() ?? "";
// todo: check for categories
if (RegExp("mate(k|matika)").hasMatch(name) || category == "matematika") return Icons.calculate_outlined;
if (RegExp("magyar nyelv|nyelvtan").hasMatch(name)) return Icons.spellcheck_outlined;
if (RegExp("irodalom").hasMatch(name)) return Icons.menu_book_outlined;
if (RegExp("rajz|muvtori|muveszet|kultura").hasMatch(name)) return Icons.palette_outlined;
if (RegExp("tor(i|tenelem)").hasMatch(name)) return Icons.hourglass_empty_outlined;
if (RegExp("foldrajz").hasMatch(name)) return Icons.public_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("^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;
@@ -25,7 +25,7 @@ class SubjectIcon {
if (RegExp("kornyezet|termeszet(tudomany|ismeret)|hon( es nep)?ismeret").hasMatch(name)) return Icons.eco_outlined;
if (RegExp("(hit|erkolcs)tan|vallas|etika").hasMatch(name)) return Icons.favorite_border_outlined;
if (RegExp("penzugy").hasMatch(name)) return Icons.savings_outlined;
if (RegExp("informatika|szoftver|iroda").hasMatch(name)) return Icons.computer_outlined;
if (RegExp("informatika|szoftver|iroda|digitalis").hasMatch(name)) return Icons.computer_outlined;
if (RegExp("prog").hasMatch(name)) return Icons.code_outlined;
if (RegExp("halozat").hasMatch(name)) return Icons.wifi_tethering_outlined;
if (RegExp("szinhaz").hasMatch(name)) return Icons.theater_comedy_outlined;

View File

@@ -7,31 +7,36 @@ import 'package:filcnaplo/helpers/storage_helper.dart';
import 'package:filcnaplo/models/release.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);
// TODO: cleanup old apk files
extension UpdateHelper on Release {
Future<void> install({UpdateCallback? updateCallback}) async {
updateCallback!(-1, UpdateState.preparing);
String downloads = await StorageHelper.downloadsPath();
File apk = File("$downloads/filcnaplo-${version}.apk");
File apk = File("$downloads/filcnaplo-$version.apk");
if (!await apk.exists()) {
updateCallback!(-1, UpdateState.downloading);
updateCallback(-1, UpdateState.downloading);
var bytes = await download(updateCallback: updateCallback);
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);
if (result.type != ResultType.done) {
// ignore: avoid_print
print("ERROR: installUpdate.openFile: " + result.message);
throw result.message;
}
updateCallback(-1, UpdateState.prepare);
updateCallback(-1, UpdateState.none);
}
Future<Uint8List> download({UpdateCallback? updateCallback}) async {

View File

@@ -1,8 +1,8 @@
import 'package:flutter/widgets.dart';
class FilcIcons {
static const IconData home = const FilcIconData(0x41);
static const IconData linux = const FilcIconData(0x42);
static const IconData home = FilcIconData(0x41);
static const IconData linux = FilcIconData(0x42);
}
class FilcIconData extends IconData {

View File

@@ -2,10 +2,12 @@ import 'package:filcnaplo/api/providers/user_provider.dart';
import 'package:filcnaplo/api/providers/database_provider.dart';
import 'package:filcnaplo/database/init.dart';
import 'package:filcnaplo/models/settings.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:filcnaplo/app.dart';
import 'package:flutter/services.dart';
import 'package:filcnaplo_mobile_ui/screens/error_screen.dart';
import 'package:filcnaplo_mobile_ui/screens/error_report_screen.dart';
void main() async {
// Initalize
@@ -39,12 +41,25 @@ class Startup {
}
}
bool errorShown = false;
String lastException = '';
Widget errorBuilder(FlutterErrorDetails details) {
return Builder(builder: (context) {
if (Navigator.of(context).canPop()) Navigator.pop(context);
WidgetsBinding.instance?.addPostFrameCallback((_) {
Navigator.of(context, rootNavigator: true).push(MaterialPageRoute(builder: (ctx) => ErrorScreen(details)));
if (!errorShown && details.exceptionAsString() != lastException) {
errorShown = true;
lastException = details.exceptionAsString();
Navigator.of(context, rootNavigator: true).push(MaterialPageRoute(builder: (context) {
if (kReleaseMode) {
return ErrorReportScreen(details);
} else {
return ErrorScreen(details);
}
})).then((_) => errorShown = false);
}
});
return Container();

View File

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

View File

@@ -6,10 +6,11 @@ import 'package:filcnaplo/theme.dart';
import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:provider/provider.dart';
import 'package:uuid/uuid.dart';
enum Pages { home, grades, timetable, messages, absences }
enum UpdateChannel { stable, beta, dev }
enum VibrationStrength { light, medium, strong }
enum VibrationStrength { off, light, medium, strong }
class SettingsProvider extends ChangeNotifier {
PackageInfo? _packageInfo;
@@ -42,12 +43,12 @@ class SettingsProvider extends ChangeNotifier {
// minutes: times 15
int _notificationPollInterval;
bool _developerMode;
bool _vibrate;
VibrationStrength _vibrationStrength;
bool _ABweeks;
VibrationStrength _vibrate;
bool _abWeeks;
bool _swapABweeks;
UpdateChannel _updateChannel;
Config _config;
String _xFilcId;
SettingsProvider({
required String language,
@@ -62,12 +63,12 @@ class SettingsProvider extends ChangeNotifier {
required int notificationsBitfield,
required bool developerMode,
required int notificationPollInterval,
required bool vibrate,
required VibrationStrength vibrationStrength,
required bool ABweeks,
required VibrationStrength vibrate,
required bool abWeeks,
required bool swapABweeks,
required UpdateChannel updateChannel,
required Config config,
required String xFilcId,
}) : _language = language,
_startPage = startPage,
_rounding = rounding,
@@ -81,11 +82,11 @@ class SettingsProvider extends ChangeNotifier {
_developerMode = developerMode,
_notificationPollInterval = notificationPollInterval,
_vibrate = vibrate,
_vibrationStrength = vibrationStrength,
_ABweeks = ABweeks,
_abWeeks = abWeeks,
_swapABweeks = swapABweeks,
_updateChannel = updateChannel,
_config = config {
_config = config,
_xFilcId = xFilcId {
PackageInfo.fromPlatform().then((PackageInfo packageInfo) {
_packageInfo = packageInfo;
});
@@ -111,12 +112,12 @@ class SettingsProvider extends ChangeNotifier {
notificationsBitfield: map["notifications_bitfield"],
notificationPollInterval: map["notification_poll_interval"],
developerMode: map["developer_mode"] == 1 ? true : false,
vibrate: map["vibrate"] == 1 ? true : false,
vibrationStrength: VibrationStrength.values[map["vibration_strength"]],
ABweeks: map["ab_weeks"] == 1 ? true : false,
vibrate: VibrationStrength.values[map["vibration_strength"]],
abWeeks: map["ab_weeks"] == 1 ? true : false,
swapABweeks: map["swap_ab_weeks"] == 1 ? true : false,
updateChannel: UpdateChannel.values[map["update_channel"]],
config: Config.fromJson(jsonDecode(map["config"] ?? "{}")),
xFilcId: map["x_filc_id"],
);
}
@@ -138,12 +139,12 @@ class SettingsProvider extends ChangeNotifier {
"grade_color4": _gradeColors[3].value,
"grade_color5": _gradeColors[4].value,
"update_channel": _updateChannel.index,
"vibrate": _vibrate ? 1 : 0,
"vibration_strength": _vibrationStrength.index,
"ab_weeks": _ABweeks ? 1 : 0,
"vibration_strength": _vibrate.index,
"ab_weeks": _abWeeks ? 1 : 0,
"swap_ab_weeks": _swapABweeks ? 1 : 0,
"notification_poll_interval": _notificationPollInterval,
"config": jsonEncode(config.json),
"x_filc_id": _xFilcId,
};
}
@@ -167,12 +168,12 @@ class SettingsProvider extends ChangeNotifier {
notificationsBitfield: 255,
developerMode: false,
notificationPollInterval: 1,
vibrate: true,
vibrationStrength: VibrationStrength.medium,
ABweeks: false,
vibrate: VibrationStrength.medium,
abWeeks: false,
swapABweeks: false,
updateChannel: UpdateChannel.stable,
config: Config.fromJson({}),
xFilcId: const Uuid().v4(),
);
}
@@ -189,13 +190,13 @@ class SettingsProvider extends ChangeNotifier {
int get notificationsBitfield => _notificationsBitfield;
bool get developerMode => _developerMode;
int get notificationPollInterval => _notificationPollInterval;
bool get vibrate => _vibrate;
VibrationStrength get vibrationStrength => _vibrationStrength;
bool get ABweeks => _ABweeks;
VibrationStrength get vibrate => _vibrate;
bool get abWeeks => _abWeeks;
bool get swapABweeks => _swapABweeks;
UpdateChannel get updateChannel => _updateChannel;
PackageInfo? get packageInfo => _packageInfo;
Config get config => _config;
String get xFilcId => _xFilcId;
Future<void> update(
BuildContext context, {
@@ -212,12 +213,12 @@ class SettingsProvider extends ChangeNotifier {
int? notificationsBitfield,
bool? developerMode,
int? notificationPollInterval,
bool? vibrate,
VibrationStrength? vibrationStrength,
bool? ABweeks,
VibrationStrength? vibrate,
bool? abWeeks,
bool? swapABweeks,
UpdateChannel? updateChannel,
Config? config,
String? xFilcId,
}) async {
if (language != null && language != _language) _language = language;
if (startPage != null && startPage != _startPage) _startPage = startPage;
@@ -230,16 +231,17 @@ class SettingsProvider extends ChangeNotifier {
if (notificationsEnabled != null && notificationsEnabled != _notificationsEnabled) _notificationsEnabled = notificationsEnabled;
if (notificationsBitfield != null && notificationsBitfield != _notificationsBitfield) _notificationsBitfield = notificationsBitfield;
if (developerMode != null && developerMode != _developerMode) _developerMode = developerMode;
if (notificationPollInterval != null && notificationPollInterval != _notificationPollInterval)
if (notificationPollInterval != null && notificationPollInterval != _notificationPollInterval) {
_notificationPollInterval = notificationPollInterval;
}
if (vibrate != null && vibrate != _vibrate) _vibrate = vibrate;
if (vibrationStrength != null && vibrationStrength != _vibrationStrength) _vibrationStrength = vibrationStrength;
if (ABweeks != null && ABweeks != _ABweeks) _ABweeks = ABweeks;
if (abWeeks != null && abWeeks != _abWeeks) _abWeeks = abWeeks;
if (swapABweeks != null && swapABweeks != _swapABweeks) _swapABweeks = swapABweeks;
if (updateChannel != null && updateChannel != _updateChannel) _updateChannel = updateChannel;
if (config != null && config != _config) _config = config;
if (xFilcId != null && xFilcId != _xFilcId) _xFilcId = xFilcId;
if (database == null) database = Provider.of<DatabaseProvider>(context, listen: false);
database ??= Provider.of<DatabaseProvider>(context, listen: false);
await database.store.storeSettings(this);
notifyListeners();
}

View File

@@ -0,0 +1,38 @@
class Supporter {
String name;
String amount;
String platform;
Supporter(this.name, this.amount, this.platform);
factory Supporter.fromJson(Map json) {
return Supporter(
(json["name"] ?? "").trim(),
json["amount"] ?? "",
json["platform"] ?? "",
);
}
}
class Supporters {
List<Supporter> top;
List<Supporter> all;
int progress;
int max;
Supporters({
required this.top,
required this.all,
required this.progress,
required this.max,
});
factory Supporters.fromJson(Map json) {
return Supporters(
max: (json["progress"] ?? {})["max"] ?? 1,
progress: (json["progress"] ?? {})["value"] ?? 0,
all: ((json["all"] ?? []) as List).cast<Map>().map((e) => Supporter.fromJson(e)).toList(),
top: ((json["top"] ?? []) as List).cast<Map>().map((e) => Supporter.fromJson(e)).toList(),
);
}
}

View File

@@ -3,6 +3,8 @@ import 'package:filcnaplo_kreta_api/client/api.dart';
import 'package:filcnaplo_kreta_api/models/student.dart';
import 'package:uuid/uuid.dart';
enum Role { student, parent }
class User {
late String id;
String username;
@@ -10,6 +12,7 @@ class User {
String instituteCode;
String name;
Student student;
Role role;
User({
String? id,
@@ -18,11 +21,12 @@ class User {
required this.password,
required this.instituteCode,
required this.student,
required this.role,
}) {
if (id != null) {
this.id = id;
} else {
this.id = Uuid().v4();
this.id = const Uuid().v4();
}
}
@@ -32,8 +36,9 @@ class User {
instituteCode: map["institute_code"],
username: map["username"],
password: map["password"],
name: map["name"],
name: map["name"].trim(),
student: Student.fromJson(jsonDecode(map["student"])),
role: Role.values[map["role"] ?? 0],
);
}
@@ -45,9 +50,13 @@ class User {
"institute_code": instituteCode,
"name": name,
"student": jsonEncode(student.json),
"role": role.index,
};
}
@override
String toString() => jsonEncode(toMap());
static Map<String, Object?> loginBody({
required String username,
required String password,
@@ -58,7 +67,7 @@ class User {
"password": password,
"institute_code": instituteCode,
"grant_type": "password",
"client_id": KretaAPI.CLIENT_ID,
"client_id": KretaAPI.clientId,
};
}
}

View File

@@ -1,376 +0,0 @@
// import 'dart:io';
// import 'dart:typed_data';
// import 'package:feather_icons_flutter/feather_icons_flutter.dart';
// import 'package:filcnaplo/data/context/app.dart';
// import 'package:filcnaplo/ui/common/bottom_card.dart';
// import 'package:filcnaplo/utils/colors.dart';
// import 'package:flutter/cupertino.dart';
// import 'package:flutter/material.dart';
// import 'package:filcnaplo/generated/i18n.dart';
// import 'package:flutter_markdown/flutter_markdown.dart';
// import 'package:http/http.dart' as http;
// import 'package:path_provider/path_provider.dart';
// import 'package:open_file/open_file.dart';
// import 'package:filcnaplo/data/context/theme.dart';
// import '../../ui/common/custom_snackbar.dart';
// enum InstallState { update, downloading, saving, installing }
// class AutoUpdater extends StatefulWidget {
// @override
// _AutoUpdaterState createState() => _AutoUpdaterState();
// }
// class _AutoUpdaterState extends State<AutoUpdater> {
// bool buttonPressed = false;
// double progress;
// bool displayProgress = false;
// InstallState installState;
// void downloadCallback(
// double progress, bool displayProgress, InstallState installState) {
// if (mounted) {
// setState(() {
// this.progress = progress;
// this.displayProgress = displayProgress;
// this.installState = installState;
// });
// }
// }
// @override
// Widget build(BuildContext context) {
// if (!buttonPressed) installState = InstallState.update;
// String buttonText;
// switch (installState) {
// case InstallState.update:
// buttonText = I18n.of(context).update;
// break;
// case InstallState.downloading:
// buttonText = I18n.of(context).updateDownloading;
// break;
// case InstallState.saving:
// buttonText = I18n.of(context).updateSaving;
// break;
// case InstallState.installing:
// buttonText = I18n.of(context).updateInstalling;
// break;
// default:
// buttonText = I18n.of(context).error;
// }
// return BottomCard(
// child: Padding(
// padding: EdgeInsets.only(top: 13),
// child: Column(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Expanded(
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Row(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Expanded(
// child: ListTile(
// contentPadding: EdgeInsets.only(left: 8.0),
// title: Text(
// I18n.of(context).updateNewVersion,
// style: TextStyle(
// fontSize: 23,
// fontWeight: FontWeight.bold,
// ),
// ),
// subtitle: Text(
// app.user.sync.release.latestRelease.version,
// style: TextStyle(
// fontWeight: FontWeight.bold,
// fontSize: 18,
// ),
// ),
// ),
// ),
// ClipRRect(
// borderRadius: BorderRadius.circular(12.0),
// child: Image.asset(
// "assets/logo.png",
// width: 60,
// ),
// ),
// ],
// ),
// Padding(
// padding: EdgeInsets.all(8),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Text(
// I18n.of(context).updateChanges + ":",
// style: TextStyle(fontSize: 18),
// ),
// Text(
// I18n.of(context).updateCurrentVersion +
// ": " +
// app.currentAppVersion,
// style:
// TextStyle(color: Colors.white.withAlpha(180)),
// ),
// ],
// ),
// ),
// Expanded(
// child: Padding(
// padding: EdgeInsets.all(8.0),
// child: Builder(builder: (context) {
// try {
// return Markdown(
// shrinkWrap: true,
// data: app.user.sync.release.latestRelease.notes,
// padding: EdgeInsets.all(0),
// physics: BouncingScrollPhysics(),
// styleSheet: MarkdownStyleSheet(
// p: TextStyle(
// fontSize: 15,
// color: app.settings.theme.textTheme
// .bodyText1.color,
// ),
// ),
// );
// } catch (e) {
// print(
// "ERROR: autoUpdater.dart failed to show markdown release notes: " +
// e.toString());
// return Text(
// app.user.sync.release.latestRelease.notes);
// }
// })),
// )
// ],
// ),
// ),
// Stack(
// children: [
// Row(
// mainAxisSize: MainAxisSize.max,
// mainAxisAlignment: MainAxisAlignment.end,
// crossAxisAlignment: CrossAxisAlignment.center,
// children: [
// IconButton(
// icon: Icon(FeatherIcons.helpCircle),
// onPressed: () {
// showDialog(
// context: context,
// builder: (context) => HelpDialog());
// })
// ],
// ),
// Center(
// child: MaterialButton(
// color: ThemeContext.filcGreen,
// elevation: 0,
// highlightElevation: 0,
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(45.0)),
// child: Padding(
// padding: EdgeInsets.symmetric(vertical: 9),
// child: Row(
// mainAxisSize: MainAxisSize.min,
// children: [
// if (displayProgress)
// Container(
// margin: EdgeInsets.only(right: 10),
// height: 19,
// width: 19,
// child: CircularProgressIndicator(
// value: progress,
// valueColor: AlwaysStoppedAnimation<Color>(
// Colors.white),
// strokeWidth: 3.2,
// ),
// ),
// Text(
// buttonText.toUpperCase(),
// style: TextStyle(
// fontSize: 17,
// fontWeight: FontWeight.bold,
// color: Colors.white),
// )
// ],
// ),
// ),
// onPressed: () {
// if (!buttonPressed)
// installUpdate(context, downloadCallback);
// buttonPressed = true;
// },
// ),
// ),
// ],
// ),
// ]),
// ),
// );
// }
// Future installUpdate(BuildContext context, Function updateDisplay) async {
// updateDisplay(null, true, InstallState.downloading);
// String dir = (await getApplicationDocumentsDirectory()).path;
// String latestVersion = app.user.sync.release.latestRelease.version;
// String filename = "filcnaplo-$latestVersion.apk";
// File apk = File("$dir/$filename");
// var httpClient = http.Client();
// var request = new http.Request(
// 'GET', Uri.parse(app.user.sync.release.latestRelease.url));
// var response = httpClient.send(request);
// List<List<int>> chunks = [];
// int downloaded = 0;
// response.asStream().listen((http.StreamedResponse r) {
// r.stream.listen((List<int> chunk) {
// // Display percentage of completion
// updateDisplay(
// downloaded / r.contentLength, true, InstallState.downloading);
// chunks.add(chunk);
// downloaded += chunk.length;
// }, onDone: () async {
// // Display percentage of completion
// updateDisplay(null, true, InstallState.saving);
// // Save the file
// final Uint8List bytes = Uint8List(r.contentLength);
// int offset = 0;
// for (List<int> chunk in chunks) {
// bytes.setRange(offset, offset + chunk.length, chunk);
// offset += chunk.length;
// }
// await apk.writeAsBytes(bytes);
// updateDisplay(null, true, InstallState.installing);
// if (mounted) {
// OpenFile.open(apk.path).then((result) {
// if (result.type != ResultType.done) {
// print("ERROR: installUpdate.openFile: " + result.message);
// ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(
// message: I18n.of(context).error,
// color: Colors.red,
// ));
// }
// Navigator.pop(context);
// });
// }
// });
// });
// }
// }
// class HelpDialog extends StatelessWidget {
// @override
// Widget build(BuildContext context) {
// return Scaffold(
// backgroundColor: Colors.transparent,
// body: Container(
// decoration: BoxDecoration(
// color: app.settings.theme.backgroundColor,
// borderRadius: BorderRadius.circular(4.0),
// ),
// padding: EdgeInsets.symmetric(vertical: 15, horizontal: 10),
// margin: EdgeInsets.all(32.0),
// child: Column(children: [
// Padding(
// padding: EdgeInsets.all(8.0),
// child: Center(
// child: Icon(FeatherIcons.helpCircle),
// ),
// ),
// Expanded(
// child: Markdown(
// shrinkWrap: true,
// data: helpText,
// padding: EdgeInsets.all(0),
// physics: BouncingScrollPhysics(),
// styleSheet: MarkdownStyleSheet(
// p: TextStyle(
// fontSize: 15,
// color: Colors.white,
// ),
// ),
// ),
// ),
// MaterialButton(
// child: Text(I18n.of(context).dialogDone),
// onPressed: () {
// Navigator.of(context).pop();
// })
// ])));
// }
// }
// const String helpText =
// """A **FRISSÍTÉS** gombot megnyomva az app automatikusan letölti a GitHubról a legfrissebb Filc telepítőt.\\
// Ez kb. 50 MB adatforgalommal jár.\\
// A letöltött telepítőt ezután megnyitja az app.
// Telefonmárkától és Android verziótól függően nagyon különböző a telepítés folyamata, de ezekre figyelj:
// - Ha kérdezi a telefon, **engedélyezd a Filctől származó appok telepítését**, majd nyomd meg a vissza gombot.\\
// _(Újabb Android verziók)_
// - Ha szól a telefon hogy a külső appok telepítése biztonsági okokból tiltott, a megjelenő gombbal **ugorj a beállításokba és kapcsold be az Ismeretlen források lehetőséget.**\\
// _(Régi Android verziók)_
// A telepítés után újra megnyílik az app, immár a legfrissebb verzióval. Az indítás során törli a telepítéshez használt .apk fájlokat, így a tárhelyed miatt nem kell aggódnod.
// """;
// class AutoUpdateButton extends StatelessWidget {
// @override
// Widget build(BuildContext context) {
// return Padding(
// padding: EdgeInsets.only(bottom: 5.0),
// child: Container(
// margin: EdgeInsets.symmetric(horizontal: 14.0),
// child: MaterialButton(
// color: textColor(Theme.of(context).backgroundColor).withAlpha(25),
// elevation: 0,
// highlightElevation: 0,
// shape:
// RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
// child: ListTile(
// contentPadding: EdgeInsets.zero,
// leading: Icon(FeatherIcons.download, color: app.settings.appColor),
// title: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Flexible(
// child: Text(
// I18n.of(context).updateAvailable,
// softWrap: false,
// overflow: TextOverflow.fade,
// )),
// Text(
// app.user.sync.release.latestRelease.version,
// style: TextStyle(
// color: app.settings.appColor,
// fontWeight: FontWeight.bold),
// )
// ],
// ),
// ),
// onPressed: () {
// showModalBottomSheet(
// context: context,
// backgroundColor: Colors.transparent,
// builder: (BuildContext context) => AutoUpdater(),
// );
// },
// ),
// ),
// );
// }
// }

View File

@@ -1,106 +0,0 @@
// import 'dart:io';
// import 'package:filcnaplo/data/context/app.dart';
// class ReleaseSync {
// Release latestRelease;
// bool isNew = false;
// Future sync() async {
// if (!Platform.isAndroid) return;
// var releasesJson = await app.user.kreta.getReleases();
// if (!app.settings.preUpdates) {
// for (Map r in releasesJson) {
// if (!r["prerelease"]) {
// latestRelease = Release.fromJson(r);
// break;
// }
// }
// } else {
// // List<Map> releases = [];
// latestRelease = Release.fromJson(releasesJson.first);
// }
// isNew = compareVersions(latestRelease.version, app.currentAppVersion);
// }
// bool compareVersions(String gitHub, String existing) {
// try {
// bool stableGitHub = false;
// List<String> gitHubPartsStrings = gitHub.split(RegExp(r"[.-]"));
// List<int> gitHubParts = [];
// for (String s in gitHubPartsStrings) {
// if (s.startsWith("beta")) {
// s = s.replaceAll("beta", "");
// } else if (s.startsWith("pre")) {
// //! pre versions have lower priority than beta
// s = s.replaceAll("pre", "");
// gitHubParts.add(0);
// } else {
// stableGitHub = true;
// }
// try {
// gitHubParts.add(int.parse(s));
// } catch (e) {
// print("ERROR: ReleaseSync.compareVersions: " + e.toString());
// }
// }
// if (stableGitHub) gitHubParts.add(1000);
// bool stableExisting = false;
// List<String> existingPartsStrings = existing.split(RegExp(r"[.-]"));
// List<int> existingParts = [];
// for (String s in existingPartsStrings) {
// if (s.startsWith("beta")) {
// s = s.replaceAll("beta", "");
// } else if (s.startsWith("pre")) {
// //! pre versions have lower priority than beta
// s = s.replaceAll("pre", "");
// existingParts.add(0);
// } else {
// stableExisting = true;
// }
// try {
// existingParts.add(int.parse(s));
// } catch (e) {
// print("ERROR: ReleaseSync.compareVersions: " + e.toString());
// }
// }
// // what even
// if (stableExisting) existingParts.add(1000);
// int i = 0;
// for (var gitHubPart in gitHubParts) {
// if (gitHubPart > existingParts[i])
// return true;
// else if (existingParts[i] > gitHubPart) return false;
// i++;
// }
// return false;
// } catch (e) {
// print("ERROR: ReleaseSync.compareVersions: " + e.toString());
// return false;
// }
// }
// }
// class Release {
// String version;
// String notes;
// String url;
// bool isExperimental;
// Release(this.version, this.notes, this.url, this.isExperimental);
// factory Release.fromJson(Map json) {
// List<Map> assets = [];
// json["assets"].forEach((json) {
// assets.add(json);
// });
// String url = assets[0]["browser_download_url"];
// return Release(json["tag_name"], json["body"], url, json["prerelease"]);
// }
// }

View File

@@ -1,148 +0,0 @@
// import 'package:filcnaplo/data/context/app.dart';
// import 'package:filcnaplo/data/models/lesson.dart';
// import 'package:filcnaplo/generated/i18n.dart';
// import 'package:filcnaplo/ui/common/custom_snackbar.dart';
// import 'package:filcnaplo/ui/pages/planner/timetable/day.dart';
// import 'package:filcnaplo/utils/format.dart';
// import 'package:filcnaplo/modules/printing/printerDebugScreen.dart';
// import 'package:flutter/material.dart';
// import 'package:flutter/services.dart' show rootBundle;
// import 'package:pdf/pdf.dart';
// import 'package:pdf/widgets.dart' as pw;
// import 'package:printing/printing.dart';
// import 'package:filcnaplo/ui/pages/planner/timetable/builder.dart';
// import 'package:flutter/foundation.dart';
// /*
// Author: daaniiieel
// Name: Timetable Printer (Experimental)
// Description: This module prints out the timetable for the selected user on the
// current week.
// */
// class TimetablePrinter {
// pw.Document build(
// BuildContext context, pw.Document pdf, List<Day> days, int min, int max) {
// List rows = <pw.TableRow>[];
// // build header row
// List<pw.Widget> headerChildren = <pw.Widget>[pw.Container()];
// days.forEach((day) => headerChildren.add(pw.Padding(
// padding: pw.EdgeInsets.all(5),
// child:
// pw.Center(child: pw.Text(weekdayStringShort(context, day.date.weekday))))));
// pw.TableRow headerRow = pw.TableRow(
// children: headerChildren,
// verticalAlignment: pw.TableCellVerticalAlignment.middle);
// rows.add(headerRow);
// // for each row
// for (int i = min; i < max; i++) {
// var children = <pw.Widget>[];
// var row = pw.TableRow(children: children);
// children.add(pw.Padding(
// padding: pw.EdgeInsets.all(5),
// child: pw.Center(child: pw.Text('$i. '))));
// days.forEach((Day day) {
// var lesson = day.lessons.firstWhere(
// (element) => element.lessonIndex != '+'
// ? int.parse(element.lessonIndex) == i
// : false,
// orElse: () => null);
// children.add(lesson != null
// ? pw.Padding(
// padding: pw.EdgeInsets.fromLTRB(5, 10, 5, 5),
// child: pw.Column(children: [
// pw.Text(lesson.name ?? lesson.subject.name),
// pw.Footer(
// leading: pw.Text(lesson.room),
// trailing: pw.Text(monogram(lesson.teacher))),
// ]))
// : pw.Padding(padding: pw.EdgeInsets.all(5)));
// });
// rows.add(row);
// }
// // add timetable to pdf
// pw.Table table = pw.Table(
// children: rows,
// border: pw.TableBorder.all(),
// defaultVerticalAlignment: pw.TableCellVerticalAlignment.middle,
// );
// // header and footer
// pw.Footer footer = pw.Footer(
// trailing: pw.Text('filcnaplo.hu'),
// margin: pw.EdgeInsets.only(top: 12.0),
// );
// String className = app.user.sync.student.student.className;
// pw.Footer header = pw.Footer(
// margin: pw.EdgeInsets.all(5),
// title: pw.Text(className, style: pw.TextStyle(fontSize: 30)),
// );
// pdf.addPage(pw.Page(
// pageFormat: PdfPageFormat.a4
// .landscape, // so the page looks normal both in portrait and landscape
// orientation: pw.PageOrientation.landscape,
// build: (pw.Context context) =>
// pw.Column(children: <pw.Widget>[header, table, footer])));
// return pdf;
// }
// void printPDF(final _scaffoldKey, BuildContext context) {
// // pdf theme (for unicode support)
// rootBundle.load("assets/Roboto-Regular.ttf").then((font) {
// pw.ThemeData myTheme = pw.ThemeData.withFont(base: pw.Font.ttf(font));
// pw.Document pdf = pw.Document(theme: myTheme);
// // sync indicator
// ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(
// message: I18n.of(context).syncTimetable,
// ));
// // get a builder and build current week
// final timetableBuilder = TimetableBuilder();
// timetableBuilder.build(timetableBuilder.getCurrentWeek());
// int minLessonIndex = 1;
// int maxLessonIndex = 1;
// List<Day> weekDays = timetableBuilder.week.days;
// for (Day day in weekDays) {
// for (Lesson lesson in day.lessons) {
// if (lesson.lessonIndex == '+') {
// continue;
// }
// if (int.parse(lesson.lessonIndex) < minLessonIndex) {
// minLessonIndex = int.parse(lesson.lessonIndex);
// }
// if (int.parse(lesson.lessonIndex) > maxLessonIndex) {
// maxLessonIndex = int.parse(lesson.lessonIndex);
// }
// }
// }
// pdf = build(context, pdf, weekDays, minLessonIndex, maxLessonIndex);
// // print pdf
// if (kReleaseMode) {
// Printing.layoutPdf(onLayout: (format) => pdf.save()).then((success) {
// if (success)
// ScaffoldMessenger.of(context).showSnackBar(CustomSnackBar(
// message: I18n.of(context).settingsExportExportTimetableSuccess,
// ));
// });
// } else {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (c) =>
// PrintingDebugScreen((format) => Future.value(pdf.save()))));
// }
// });
// }
// }

View File

@@ -1,17 +0,0 @@
// import 'dart:typed_data';
// import 'package:flutter/material.dart';
// import 'package:printing/printing.dart';
// import 'package:pdf/pdf.dart';
// class PrintingDebugScreen extends StatelessWidget {
// final Future<Uint8List> Function(PdfPageFormat) builder;
// PrintingDebugScreen(
// this.builder,
// );
// @override
// Widget build(BuildContext context) {
// return Scaffold(body: Center(child: PdfPreview(build: this.builder)));
// }
// }

View File

@@ -11,14 +11,14 @@ class AppTheme {
// Light Theme
static ThemeData lightTheme(BuildContext context) {
var lightColors = LightAppColors();
Color accent = accentColorMap[Provider.of<SettingsProvider>(context, listen: false).accentColor] ?? Color(0);
Color accent = accentColorMap[Provider.of<SettingsProvider>(context, listen: false).accentColor] ?? const Color(0x00000000);
return ThemeData(
brightness: Brightness.light,
fontFamily: _fontFamily,
scaffoldBackgroundColor: lightColors.background,
backgroundColor: lightColors.highlight,
primaryColor: lightColors.filc,
dividerColor: Color(0),
dividerColor: const Color(0x00000000),
colorScheme: ColorScheme.fromSwatch(
accentColor: accent,
backgroundColor: lightColors.background,
@@ -37,14 +37,14 @@ class AppTheme {
// Dark Theme
static ThemeData darkTheme(BuildContext context) {
var darkColors = DarkAppColors();
Color accent = accentColorMap[Provider.of<SettingsProvider>(context, listen: false).accentColor] ?? Color(0);
Color accent = accentColorMap[Provider.of<SettingsProvider>(context, listen: false).accentColor] ?? const Color(0x00000000);
return ThemeData(
brightness: Brightness.dark,
fontFamily: _fontFamily,
scaffoldBackgroundColor: darkColors.background,
backgroundColor: darkColors.highlight,
primaryColor: darkColors.filc,
dividerColor: Color(0),
dividerColor: const Color(0x00000000),
colorScheme: ColorScheme.fromSwatch(
accentColor: accent,
backgroundColor: darkColors.background,
@@ -70,7 +70,7 @@ class AppColors {
enum AccentColor { filc, blue, green, lime, yellow, orange, red, pink, purple }
Map<AccentColor, Color> accentColorMap = {
AccentColor.filc: Color(0xff20AC9B),
AccentColor.filc: const Color(0xff20AC9B),
AccentColor.blue: Colors.blue.shade300,
AccentColor.green: Colors.green.shade300,
AccentColor.lime: Colors.lime.shade300,
@@ -82,54 +82,82 @@ Map<AccentColor, Color> accentColorMap = {
};
abstract class ThemeAppColors {
final Color shadow = Color(0);
final Color text = Color(0);
final Color background = Color(0);
final Color highlight = Color(0);
final Color red = Color(0);
final Color orange = Color(0);
final Color yellow = Color(0);
final Color green = Color(0);
final Color filc = Color(0);
final Color teal = Color(0);
final Color blue = Color(0);
final Color indigo = Color(0);
final Color purple = Color(0);
final Color pink = Color(0);
final Color shadow = const Color(0x00000000);
final Color text = const Color(0x00000000);
final Color background = const Color(0x00000000);
final Color highlight = const Color(0x00000000);
final Color red = const Color(0x00000000);
final Color orange = const Color(0x00000000);
final Color yellow = const Color(0x00000000);
final Color green = const Color(0x00000000);
final Color filc = const Color(0x00000000);
final Color teal = const Color(0x00000000);
final Color blue = const Color(0x00000000);
final Color indigo = const Color(0x00000000);
final Color purple = const Color(0x00000000);
final Color pink = const Color(0x00000000);
}
class LightAppColors implements ThemeAppColors {
final shadow = Color(0xffE8E8E8);
@override
final shadow = const Color(0xffE8E8E8);
@override
final text = Colors.black;
final background = Color(0xffF4F9FF);
final highlight = Color(0xffFFFFFF);
final red = Color(0xffFF3B30);
final orange = Color(0xffFF9500);
final yellow = Color(0xffFFCC00);
final green = Color(0xff34C759);
final filc = Color(0xff247665);
final teal = Color(0xff5AC8FA);
final blue = Color(0xff007AFF);
final indigo = Color(0xff5856D6);
final purple = Color(0xffAF52DE);
final pink = Color(0xffFF2D55);
@override
final background = const Color(0xffF4F9FF);
@override
final highlight = const Color(0xffFFFFFF);
@override
final red = const Color(0xffFF3B30);
@override
final orange = const Color(0xffFF9500);
@override
final yellow = const Color(0xffFFCC00);
@override
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 {
final shadow = Color(0);
@override
final shadow = const Color(0x00000000);
@override
final text = Colors.white;
final background = Color(0xff000000);
final highlight = Color(0xff141516);
final red = Color(0xffFF453A);
final orange = Color(0xffFF9F0A);
final yellow = Color(0xffFFD60A);
final green = Color(0xff32D74B);
final filc = Color(0xff29826F);
final teal = Color(0xff64D2FF);
final blue = Color(0xff0A84FF);
final indigo = Color(0xff5E5CE6);
final purple = Color(0xffBF5AF2);
final pink = Color(0xffFF375F);
@override
final background = const Color(0xff000000);
@override
final highlight = const Color(0xff141516);
@override
final red = const Color(0xffFF453A);
@override
final orange = const Color(0xffFF9F0A);
@override
final yellow = const Color(0xffFFD60A);
@override
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 {

View File

@@ -1,3 +1,4 @@
import 'package:filcnaplo_kreta_api/models/week.dart';
import 'package:flutter/widgets.dart';
import 'package:intl/intl.dart';
import 'package:i18n_extension/i18n_widget.dart';
@@ -5,8 +6,7 @@ import 'package:html/parser.dart';
import 'format.i18n.dart';
extension StringFormatUtils on String {
String specialChars() => this
.replaceAll("é", "e")
String specialChars() => replaceAll("é", "e")
.replaceAll("á", "a")
.replaceAll("ó", "o")
.replaceAll("ő", "o")
@@ -16,9 +16,9 @@ extension StringFormatUtils on String {
.replaceAll("ü", "u")
.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) => this.capital()).join(" ");
String capitalize() => split(" ").map((w) => w.capital()).join(" ");
String escapeHtml() {
String htmlString = this;
@@ -32,25 +32,32 @@ extension StringFormatUtils on String {
}
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
if (timeOnly) return DateFormat("HH:mm").format(this);
DateTime now = DateTime.now();
if (this.difference(now).inDays == 0) {
if (this.hour == 0 && this.minute == 0 && this.second == 0) return "Today".i18n;
if (now.year == year && now.month == month && now.day == day) {
if (hour == 0 && minute == 0 && second == 0 || forceToday) return "Today".i18n;
return DateFormat("HH:mm").format(this);
}
if (this.difference(now).inDays == 1) return "Yesterday".i18n;
if (now.year == year && now.month == month && now.subtract(const Duration(days: 1)).day == day) return "Yesterday".i18n;
if (now.year == year && now.month == month && now.add(const Duration(days: 1)).day == day) return "Tomorrow".i18n;
String formatString;
if (this.year == now.year)
formatString = "MMM dd.";
else
formatString = "yy/MM/dd";
if (weekday) formatString += " (EEEE)";
// If date is current week, show only weekday
if (Week.current().start.isBefore(this) && Week.current().end.isAfter(this)) {
formatString = "EEEE";
} else {
if (year == now.year) {
formatString = "MMM dd.";
} else {
formatString = "yy/MM/dd";
} // ex. 21/01/01
return DateFormat(formatString, I18n.of(context).locale.toString()).format(this);
if (weekday) formatString += " (EEEE)"; // ex. (monday)
}
return DateFormat(formatString, I18n.of(context).locale.toString()).format(this).capital();
}
}

View File

@@ -6,14 +6,17 @@ extension Localization on String {
"en_en": {
"Today": "Today",
"Yesterday": "Yesterday",
"Tomorrow": "Tomorrow",
},
"hu_hu": {
"Today": "Ma",
"Yesterday": "Tegnap",
"Tomorrow": "Holnap",
},
"de_de": {
"Today": "Heute",
"Yesterday": "Gestern",
"Tomorrow": "Morgen",
}
};

View File

@@ -1,7 +1,9 @@
import 'dart:convert';
import 'package:filcnaplo/models/user.dart';
class JwtUtils {
static String? getNameFromJWT(String jwt) {
static Map? decodeJwt(String jwt) {
var parts = jwt.split(".");
if (parts.length != 3) return null;
@@ -11,8 +13,28 @@ class JwtUtils {
parts[1] += "=";
}
var payload = utf8.decode(base64Url.decode(parts[1]));
var jwtData = jsonDecode(payload);
return jwtData["name"];
try {
var payload = utf8.decode(base64Url.decode(parts[1]));
return jsonDecode(payload);
} catch (error) {
// ignore: avoid_print
print("ERROR: JwtUtils.decodeJwt: $error");
}
}
static String? getNameFromJWT(String jwt) {
var jwtData = decodeJwt(jwt);
return jwtData?["name"];
}
static Role? getRoleFromJWT(String jwt) {
var jwtData = decodeJwt(jwt);
switch (jwtData?["role"]) {
case "Tanulo":
return Role.student;
case "Gondviselo":
return Role.parent;
}
}
}

1
filcnaplo/linux/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
flutter/ephemeral

View File

@@ -0,0 +1,116 @@
cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)
set(BINARY_NAME "filcnaplo")
set(APPLICATION_ID "hu.filc.filcnaplo")
cmake_policy(SET CMP0063 NEW)
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Root filesystem for cross-building.
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
endif()
# Configure build options.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()
# Compilation settings that should be applied to most targets.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_14)
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
endfunction()
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
# Flutter library and tool build rules.
add_subdirectory(${FLUTTER_MANAGED_DIR})
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
# Application build
add_executable(${BINARY_NAME}
"main.cc"
"my_application.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
)
apply_standard_settings(${BINARY_NAME})
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
add_dependencies(${BINARY_NAME} flutter_assemble)
# Only the install-generated bundle's copy of the executable will launch
# correctly, since the resources must in the right relative locations. To avoid
# people trying to run the unbundled copy, put it in a subdirectory instead of
# the default top-level location.
set_target_properties(${BINARY_NAME}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
)
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
# === Installation ===
# By default, "installing" just makes a relocatable bundle in the build
# directory.
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()
# Start with a clean build bundle directory every time.
install(CODE "
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
" COMPONENT Runtime)
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
if(PLUGIN_BUNDLED_LIBRARIES)
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
# Install the AOT library on non-Debug builds only.
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()

View File

@@ -0,0 +1,8 @@
[Desktop Entry]
Name=Filc Napló
Comment=Nem hivatalos e-napló alkalmazás az eKRÉTA rendszerhez
Exec=filcnaplo
Icon=icon.png
Terminal=false
Type=Application
Categories=Education;

View File

@@ -0,0 +1,87 @@
cmake_minimum_required(VERSION 3.10)
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
# Configuration provided via flutter tool.
include(${EPHEMERAL_DIR}/generated_config.cmake)
# TODO: Move the rest of this into files in ephemeral. See
# https://github.com/flutter/flutter/issues/57146.
# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
# which isn't available in 3.10.
function(list_prepend LIST_NAME PREFIX)
set(NEW_LIST "")
foreach(element ${${LIST_NAME}})
list(APPEND NEW_LIST "${PREFIX}${element}")
endforeach(element)
set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
endfunction()
# === Flutter Library ===
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
# Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
list(APPEND FLUTTER_LIBRARY_HEADERS
"fl_basic_message_channel.h"
"fl_binary_codec.h"
"fl_binary_messenger.h"
"fl_dart_project.h"
"fl_engine.h"
"fl_json_message_codec.h"
"fl_json_method_codec.h"
"fl_message_codec.h"
"fl_method_call.h"
"fl_method_channel.h"
"fl_method_codec.h"
"fl_method_response.h"
"fl_plugin_registrar.h"
"fl_plugin_registry.h"
"fl_standard_message_codec.h"
"fl_standard_method_codec.h"
"fl_string_codec.h"
"fl_value.h"
"fl_view.h"
"flutter_linux.h"
)
list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE
"${EPHEMERAL_DIR}"
)
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
target_link_libraries(flutter INTERFACE
PkgConfig::GTK
PkgConfig::GLIB
PkgConfig::GIO
)
add_dependencies(flutter flutter_assemble)
# === Flutter tool backend ===
# _phony_ is a non-existent file to force this command to run every time,
# since currently there's no way to get a full input/output list from the
# flutter tool.
add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CMAKE_CURRENT_BINARY_DIR}/_phony_
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
)

View File

@@ -0,0 +1,15 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
}

View File

@@ -0,0 +1,15 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter_linux/flutter_linux.h>
// Registers Flutter plugins.
void fl_register_plugins(FlPluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_

View File

@@ -0,0 +1,16 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
url_launcher_linux
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)

BIN
filcnaplo/linux/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Some files were not shown because too many files have changed in this diff Show More