==1.0.0==

- removed obsolete deps
- fixed many issues
- improved features
- added:
  * favourites
  * account console
  * push notifications
  * background sync
  * improved settings
This commit is contained in:
Denys Konovalov 2021-12-19 22:20:56 +01:00
parent db6c41a1ce
commit ba75ce8d6d
71 changed files with 6614 additions and 1468 deletions

19
.vscode/launch.json vendored Normal file

@ -0,0 +1,19 @@
{
// Verwendet IntelliSense zum Ermitteln möglicher Attribute.
// Zeigen Sie auf vorhandene Attribute, um die zugehörigen Beschreibungen anzuzeigen.
// Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "app",
"request": "launch",
"type": "dart"
},
{
"name": "app (profile mode)",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
}
]
}

BIN
android.zip Normal file

Binary file not shown.

@ -4,15 +4,21 @@
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="PLATFORM" />
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="Embedded JDK" />
<option name="modules">
<set>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.2/android" />
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shared_preferences-2.0.6/android" />
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
<option value="$USER_HOME$/tools/flutter/.pub-cache/hosted/pub.dartlang.org/background_fetch-1.0.3/android" />
<option value="$USER_HOME$/tools/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_local_notifications-10.0.0-dev.1/android" />
<option value="$USER_HOME$/tools/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.5/android" />
<option value="$USER_HOME$/tools/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences-2.0.8/android" />
<option value="$USER_HOME$/tools/flutter/.pub-cache/hosted/pub.dartlang.org/sqflite-2.0.0+4/android" />
<option value="$USER_HOME$/tools/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_android-6.0.13/android" />
<option value="$USER_HOME$/tools/flutter/.pub-cache/hosted/pub.dartlang.org/webview_flutter_android-2.8.0/android" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />

@ -26,5 +26,55 @@
<option name="name" value="Google3" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="BintrayJCenter" />
<option name="name" value="BintrayJCenter" />
<option name="url" value="https://jcenter.bintray.com/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven2" />
<option name="name" value="maven2" />
<option name="url" value="file:$PROJECT_DIR$/libs" />
</remote-repository>
<remote-repository>
<option name="id" value="maven2" />
<option name="name" value="maven2" />
<option name="url" value="file:$USER_HOME$/tools/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_local_notifications-10.0.0-dev.1/android/libs" />
</remote-repository>
<remote-repository>
<option name="id" value="maven2" />
<option name="name" value="maven2" />
<option name="url" value="file:$USER_HOME$/tools/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.5/android/libs" />
</remote-repository>
<remote-repository>
<option name="id" value="maven2" />
<option name="name" value="maven2" />
<option name="url" value="file:$USER_HOME$/tools/flutter/.pub-cache/hosted/pub.dartlang.org/sqflite-2.0.0+4/android/libs" />
</remote-repository>
<remote-repository>
<option name="id" value="maven2" />
<option name="name" value="maven2" />
<option name="url" value="file:$USER_HOME$/tools/flutter/.pub-cache/hosted/pub.dartlang.org/webview_flutter_android-2.8.0/android/libs" />
</remote-repository>
<remote-repository>
<option name="id" value="maven2" />
<option name="name" value="maven2" />
<option name="url" value="file:$USER_HOME$/tools/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_android-6.0.13/android/libs" />
</remote-repository>
<remote-repository>
<option name="id" value="maven2" />
<option name="name" value="maven2" />
<option name="url" value="file:$USER_HOME$/tools/flutter/.pub-cache/hosted/pub.dartlang.org/background_fetch-1.0.3/android/libs/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven2" />
<option name="name" value="maven2" />
<option name="url" value="file:$PROJECT_DIR$/app/libs" />
</remote-repository>
<remote-repository>
<option name="id" value="maven2" />
<option name="name" value="maven2" />
<option name="url" value="file:$USER_HOME$/tools/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences-2.0.8/android/libs" />
</remote-repository>
</component>
</project>

@ -1,7 +0,0 @@
<component name="libraryTable">
<library name="Flutter Plugins" type="FlutterPluginsLibraryType">
<CLASSES />
<JAVADOC />
<SOURCES />
</library>
</component>

@ -1,9 +1,10 @@
<component name="libraryTable">
<library name="Gradle: androidx.activity:activity:1.0.0@aar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/68dbf8ea01a1dbd974fc87b5753550f0/jetified-activity-1.0.0/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/68dbf8ea01a1dbd974fc87b5753550f0/jetified-activity-1.0.0/res" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/68dbf8ea01a1dbd974fc87b5753550f0/jetified-activity-1.0.0/AndroidManifest.xml" />
<root url="jar://$USER_HOME$/.gradle/caches/transforms-3/7d4bfe2a8fe4cc7cfb1eb08c8f16cfa7/transformed/jetified-activity-1.0.0/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-3/7d4bfe2a8fe4cc7cfb1eb08c8f16cfa7/transformed/jetified-activity-1.0.0/AndroidManifest.xml" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-3/7d4bfe2a8fe4cc7cfb1eb08c8f16cfa7/transformed/jetified-activity-1.0.0/res" />
</CLASSES>
<JAVADOC />
<SOURCES>

@ -1,13 +0,0 @@
<component name="libraryTable">
<library name="Gradle: androidx.annotation:annotation:1.1.0">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/androidx.annotation/annotation/1.1.0/e3a6fb2f40e3a3842e6b7472628ba4ce416ea4c8/annotation-1.1.0.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/androidx.annotation/annotation/1.1.0/408af38ec57369afe3fd6466e1c4bfdd5f15fc92/annotation-1.1.0-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/androidx.annotation/annotation/1.1.0/8b7bdc00eb4d998bfbc76767b098620990f2a805/annotation-1.1.0-sources.jar!/" />
</SOURCES>
</library>
</component>

@ -1,9 +1,10 @@
<component name="libraryTable">
<library name="Gradle: androidx.arch.core:core-runtime:2.0.0@aar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/5f606c30f064eed94124828de37fadc0/core-runtime-2.0.0/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/5f606c30f064eed94124828de37fadc0/core-runtime-2.0.0/res" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/5f606c30f064eed94124828de37fadc0/core-runtime-2.0.0/AndroidManifest.xml" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-3/b15e9b60227c1d77e2391c0767d49728/transformed/core-runtime-2.0.0/res" />
<root url="jar://$USER_HOME$/.gradle/caches/transforms-3/b15e9b60227c1d77e2391c0767d49728/transformed/core-runtime-2.0.0/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-3/b15e9b60227c1d77e2391c0767d49728/transformed/core-runtime-2.0.0/AndroidManifest.xml" />
</CLASSES>
<JAVADOC />
<SOURCES>

@ -1,16 +0,0 @@
<component name="libraryTable">
<library name="Gradle: androidx.core:core:1.1.0@aar">
<ANNOTATIONS>
<root url="jar://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/2e94107f30f39be366a17347c2bf100b/core-1.1.0/annotations.zip!/" />
</ANNOTATIONS>
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/2e94107f30f39be366a17347c2bf100b/core-1.1.0/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/2e94107f30f39be366a17347c2bf100b/core-1.1.0/res" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/2e94107f30f39be366a17347c2bf100b/core-1.1.0/AndroidManifest.xml" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/androidx.core/core/1.1.0/4ae37fad1fe95b42aa47a720908df37ba5d3c85e/core-1.1.0-sources.jar!/" />
</SOURCES>
</library>
</component>

@ -1,9 +1,10 @@
<component name="libraryTable">
<library name="Gradle: androidx.customview:customview:1.0.0@aar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/bc28a09d8d2212ffd5788fe6c1853326/customview-1.0.0/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/bc28a09d8d2212ffd5788fe6c1853326/customview-1.0.0/res" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/bc28a09d8d2212ffd5788fe6c1853326/customview-1.0.0/AndroidManifest.xml" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-3/1329a52a6813b6f962d4a50f002a018d/transformed/customview-1.0.0/res" />
<root url="jar://$USER_HOME$/.gradle/caches/transforms-3/1329a52a6813b6f962d4a50f002a018d/transformed/customview-1.0.0/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-3/1329a52a6813b6f962d4a50f002a018d/transformed/customview-1.0.0/AndroidManifest.xml" />
</CLASSES>
<JAVADOC />
<SOURCES>

@ -1,12 +1,13 @@
<component name="libraryTable">
<library name="Gradle: androidx.fragment:fragment:1.1.0@aar">
<ANNOTATIONS>
<root url="jar://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/2fd9120a6da4df3dbabdd40f6a2d8995/fragment-1.1.0/annotations.zip!/" />
<root url="jar://$USER_HOME$/.gradle/caches/transforms-3/cc4bb60650edfa741e4e3dc70607dcac/transformed/fragment-1.1.0/annotations.zip!/" />
</ANNOTATIONS>
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/2fd9120a6da4df3dbabdd40f6a2d8995/fragment-1.1.0/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/2fd9120a6da4df3dbabdd40f6a2d8995/fragment-1.1.0/res" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/2fd9120a6da4df3dbabdd40f6a2d8995/fragment-1.1.0/AndroidManifest.xml" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-3/cc4bb60650edfa741e4e3dc70607dcac/transformed/fragment-1.1.0/res" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-3/cc4bb60650edfa741e4e3dc70607dcac/transformed/fragment-1.1.0/AndroidManifest.xml" />
<root url="jar://$USER_HOME$/.gradle/caches/transforms-3/cc4bb60650edfa741e4e3dc70607dcac/transformed/fragment-1.1.0/jars/classes.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>

@ -1,9 +1,10 @@
<component name="libraryTable">
<library name="Gradle: androidx.lifecycle:lifecycle-livedata:2.0.0@aar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/fb333fb43575d8a3b51161295454427f/lifecycle-livedata-2.0.0/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/fb333fb43575d8a3b51161295454427f/lifecycle-livedata-2.0.0/res" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/fb333fb43575d8a3b51161295454427f/lifecycle-livedata-2.0.0/AndroidManifest.xml" />
<root url="jar://$USER_HOME$/.gradle/caches/transforms-3/f3888261453da560a886165dfa0f8c7b/transformed/lifecycle-livedata-2.0.0/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-3/f3888261453da560a886165dfa0f8c7b/transformed/lifecycle-livedata-2.0.0/AndroidManifest.xml" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-3/f3888261453da560a886165dfa0f8c7b/transformed/lifecycle-livedata-2.0.0/res" />
</CLASSES>
<JAVADOC />
<SOURCES>

@ -1,9 +1,10 @@
<component name="libraryTable">
<library name="Gradle: androidx.lifecycle:lifecycle-livedata-core:2.0.0@aar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/cad25976877a6ae855ef6821534f58a1/lifecycle-livedata-core-2.0.0/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/cad25976877a6ae855ef6821534f58a1/lifecycle-livedata-core-2.0.0/res" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/cad25976877a6ae855ef6821534f58a1/lifecycle-livedata-core-2.0.0/AndroidManifest.xml" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-3/d804499608d4fe82c9642c020be13091/transformed/lifecycle-livedata-core-2.0.0/res" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-3/d804499608d4fe82c9642c020be13091/transformed/lifecycle-livedata-core-2.0.0/AndroidManifest.xml" />
<root url="jar://$USER_HOME$/.gradle/caches/transforms-3/d804499608d4fe82c9642c020be13091/transformed/lifecycle-livedata-core-2.0.0/jars/classes.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>

@ -1,9 +1,10 @@
<component name="libraryTable">
<library name="Gradle: androidx.lifecycle:lifecycle-runtime:2.2.0@aar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/ca6b214160bd0909b103aa12202ef51c/lifecycle-runtime-2.2.0/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/ca6b214160bd0909b103aa12202ef51c/lifecycle-runtime-2.2.0/res" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/ca6b214160bd0909b103aa12202ef51c/lifecycle-runtime-2.2.0/AndroidManifest.xml" />
<root url="jar://$USER_HOME$/.gradle/caches/transforms-3/b8be27952265c4f463c642420b3d8ef4/transformed/lifecycle-runtime-2.2.0/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-3/b8be27952265c4f463c642420b3d8ef4/transformed/lifecycle-runtime-2.2.0/AndroidManifest.xml" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-3/b8be27952265c4f463c642420b3d8ef4/transformed/lifecycle-runtime-2.2.0/res" />
</CLASSES>
<JAVADOC />
<SOURCES>

@ -1,9 +1,10 @@
<component name="libraryTable">
<library name="Gradle: androidx.lifecycle:lifecycle-viewmodel:2.1.0@aar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/15792e6083b2b5b5a7ffa285d47daa24/lifecycle-viewmodel-2.1.0/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/15792e6083b2b5b5a7ffa285d47daa24/lifecycle-viewmodel-2.1.0/res" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/15792e6083b2b5b5a7ffa285d47daa24/lifecycle-viewmodel-2.1.0/AndroidManifest.xml" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-3/34ecbfc28f9f64b9b10e605849457cf1/transformed/lifecycle-viewmodel-2.1.0/AndroidManifest.xml" />
<root url="jar://$USER_HOME$/.gradle/caches/transforms-3/34ecbfc28f9f64b9b10e605849457cf1/transformed/lifecycle-viewmodel-2.1.0/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-3/34ecbfc28f9f64b9b10e605849457cf1/transformed/lifecycle-viewmodel-2.1.0/res" />
</CLASSES>
<JAVADOC />
<SOURCES>

@ -1,9 +1,10 @@
<component name="libraryTable">
<library name="Gradle: androidx.loader:loader:1.0.0@aar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/d9536c14ffde0b7289f12ba6def6bfa4/loader-1.0.0/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/d9536c14ffde0b7289f12ba6def6bfa4/loader-1.0.0/res" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/d9536c14ffde0b7289f12ba6def6bfa4/loader-1.0.0/AndroidManifest.xml" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-3/024a36b1ca020dc1ffa8d7896e3bf5ff/transformed/loader-1.0.0/res" />
<root url="jar://$USER_HOME$/.gradle/caches/transforms-3/024a36b1ca020dc1ffa8d7896e3bf5ff/transformed/loader-1.0.0/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-3/024a36b1ca020dc1ffa8d7896e3bf5ff/transformed/loader-1.0.0/AndroidManifest.xml" />
</CLASSES>
<JAVADOC />
<SOURCES>

@ -1,9 +1,10 @@
<component name="libraryTable">
<library name="Gradle: androidx.savedstate:savedstate:1.0.0@aar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/2271d874344878e62b9a8e309af0c39d/jetified-savedstate-1.0.0/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/2271d874344878e62b9a8e309af0c39d/jetified-savedstate-1.0.0/res" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/2271d874344878e62b9a8e309af0c39d/jetified-savedstate-1.0.0/AndroidManifest.xml" />
<root url="jar://$USER_HOME$/.gradle/caches/transforms-3/a564c650009ac14109f29da32f460971/transformed/jetified-savedstate-1.0.0/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-3/a564c650009ac14109f29da32f460971/transformed/jetified-savedstate-1.0.0/AndroidManifest.xml" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-3/a564c650009ac14109f29da32f460971/transformed/jetified-savedstate-1.0.0/res" />
</CLASSES>
<JAVADOC />
<SOURCES>

@ -1,13 +0,0 @@
<component name="libraryTable">
<library name="Gradle: androidx.versionedparcelable:versionedparcelable:1.1.0@aar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/96aee308bccd03bc62c306516c23a410/versionedparcelable-1.1.0/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/96aee308bccd03bc62c306516c23a410/versionedparcelable-1.1.0/res" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/96aee308bccd03bc62c306516c23a410/versionedparcelable-1.1.0/AndroidManifest.xml" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/androidx.versionedparcelable/versionedparcelable/1.1.0/d9085927216387af679d18b6f472bc0fc5c7cc81/versionedparcelable-1.1.0-sources.jar!/" />
</SOURCES>
</library>
</component>

@ -1,9 +1,10 @@
<component name="libraryTable">
<library name="Gradle: androidx.viewpager:viewpager:1.0.0@aar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/eb26273f8f7192460456ce62b9bdf9ef/viewpager-1.0.0/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/eb26273f8f7192460456ce62b9bdf9ef/viewpager-1.0.0/res" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-2/files-2.1/eb26273f8f7192460456ce62b9bdf9ef/viewpager-1.0.0/AndroidManifest.xml" />
<root url="jar://$USER_HOME$/.gradle/caches/transforms-3/8552b7074c8a7488b55dc3c476b7f2c3/transformed/viewpager-1.0.0/jars/classes.jar!/" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-3/8552b7074c8a7488b55dc3c476b7f2c3/transformed/viewpager-1.0.0/AndroidManifest.xml" />
<root url="file://$USER_HOME$/.gradle/caches/transforms-3/8552b7074c8a7488b55dc3c476b7f2c3/transformed/viewpager-1.0.0/res" />
</CLASSES>
<JAVADOC />
<SOURCES>

@ -1,9 +0,0 @@
<component name="libraryTable">
<library name="Gradle: io.flutter:arm64_v8a_debug:1.0.0-241c87ad800beeab545ab867354d4683d5bfb6ce">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/io.flutter/arm64_v8a_debug/1.0.0-241c87ad800beeab545ab867354d4683d5bfb6ce/31ce772b217fad9767b2b4c312be45723d5a4aff/arm64_v8a_debug-1.0.0-241c87ad800beeab545ab867354d4683d5bfb6ce.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

@ -1,9 +0,0 @@
<component name="libraryTable">
<library name="Gradle: io.flutter:armeabi_v7a_debug:1.0.0-241c87ad800beeab545ab867354d4683d5bfb6ce">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/io.flutter/armeabi_v7a_debug/1.0.0-241c87ad800beeab545ab867354d4683d5bfb6ce/2451328148067a2ba68f9f8c9cdf9721b03e9bf6/armeabi_v7a_debug-1.0.0-241c87ad800beeab545ab867354d4683d5bfb6ce.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

@ -1,11 +0,0 @@
<component name="libraryTable">
<library name="Gradle: io.flutter:flutter_embedding_debug:1.0.0-241c87ad800beeab545ab867354d4683d5bfb6ce">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/io.flutter/flutter_embedding_debug/1.0.0-241c87ad800beeab545ab867354d4683d5bfb6ce/25be3a5a8b2782d003516239ea5de35152305ff5/flutter_embedding_debug-1.0.0-241c87ad800beeab545ab867354d4683d5bfb6ce.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/io.flutter/flutter_embedding_debug/1.0.0-241c87ad800beeab545ab867354d4683d5bfb6ce/e95f95dfa6111331ce75c3fdd7298c114320748b/flutter_embedding_debug-1.0.0-241c87ad800beeab545ab867354d4683d5bfb6ce-sources.jar!/" />
</SOURCES>
</library>
</component>

@ -1,9 +0,0 @@
<component name="libraryTable">
<library name="Gradle: io.flutter:x86_64_debug:1.0.0-241c87ad800beeab545ab867354d4683d5bfb6ce">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/io.flutter/x86_64_debug/1.0.0-241c87ad800beeab545ab867354d4683d5bfb6ce/128a82a6af9a80d7230ba29ebed9c428e4a1fe9b/x86_64_debug-1.0.0-241c87ad800beeab545ab867354d4683d5bfb6ce.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

@ -1,9 +0,0 @@
<component name="libraryTable">
<library name="Gradle: io.flutter:x86_debug:1.0.0-241c87ad800beeab545ab867354d4683d5bfb6ce">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/io.flutter/x86_debug/1.0.0-241c87ad800beeab545ab867354d4683d5bfb6ce/61fa4ea55499c66fda687ac6d38002af1fac8ab2/x86_debug-1.0.0-241c87ad800beeab545ab867354d4683d5bfb6ce.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

@ -3,7 +3,7 @@
<component name="FrameworkDetectionExcludesConfiguration">
<type id="android" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="JDK" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

@ -4,8 +4,13 @@
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/modules/android.iml" filepath="$PROJECT_DIR$/.idea/modules/android.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/android.app.iml" filepath="$PROJECT_DIR$/.idea/modules/app/android.app.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/-442492029/android.path_provider.iml" filepath="$PROJECT_DIR$/.idea/modules/-442492029/android.path_provider.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/1049519328/android.shared_preferences.iml" filepath="$PROJECT_DIR$/.idea/modules/1049519328/android.shared_preferences.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/-1506358272/android.background_fetch.iml" filepath="$PROJECT_DIR$/.idea/modules/-1506358272/android.background_fetch.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/-731377546/android.flutter_local_notifications.iml" filepath="$PROJECT_DIR$/.idea/modules/-731377546/android.flutter_local_notifications.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/-1978775972/android.sqflite.iml" filepath="$PROJECT_DIR$/.idea/modules/-1978775972/android.sqflite.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/2112108985/android.url_launcher_android.iml" filepath="$PROJECT_DIR$/.idea/modules/2112108985/android.url_launcher_android.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/1177527115/android.webview_flutter_android.iml" filepath="$PROJECT_DIR$/.idea/modules/1177527115/android.webview_flutter_android.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/-1981079475/io.flutter.plugins.pathprovider.android.path_provider.iml" filepath="$PROJECT_DIR$/.idea/modules/-1981079475/io.flutter.plugins.pathprovider.android.path_provider.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/1076383611/io.flutter.plugins.sharedpreferences.android.shared_preferences.iml" filepath="$PROJECT_DIR$/.idea/modules/1076383611/io.flutter.plugins.sharedpreferences.android.shared_preferences.iml" />
</modules>
</component>
</project>

@ -1,96 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":path_provider" external.linked.project.path="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.2/android" external.root.project.path="$MODULE_DIR$/../../.." external.system.id="GRADLE" external.system.module.group="io.flutter.plugins.pathprovider" external.system.module.version="1.0-SNAPSHOT" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle">
<configuration>
<option name="GRADLE_PROJECT_PATH" value=":path_provider" />
<option name="LAST_SUCCESSFUL_SYNC_AGP_VERSION" value="4.1.0" />
<option name="LAST_KNOWN_AGP_VERSION" value="4.1.0" />
</configuration>
</facet>
<facet type="android" name="Android">
<configuration>
<option name="SELECTED_BUILD_VARIANT" value="debug" />
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
<afterSyncTasks>
<task>generateDebugSources</task>
</afterSyncTasks>
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.2/android/src/main/res;file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.2/android/src/debug/res;file://$MODULE_DIR$/../../../../build/path_provider/generated/res/rs/debug;file://$MODULE_DIR$/../../../../build/path_provider/generated/res/resValues/debug" />
<option name="TEST_RES_FOLDERS_RELATIVE_PATH" value="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.2/android/src/androidTest/res;file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.2/android/src/androidTestDebug/res;file://$MODULE_DIR$/../../../../build/path_provider/generated/res/rs/androidTest/debug;file://$MODULE_DIR$/../../../../build/path_provider/generated/res/resValues/androidTest/debug" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
<option name="PROJECT_TYPE" value="1" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/../../../../build/path_provider/intermediates/javac/debug/classes" />
<output-test url="file://$MODULE_DIR$/../../../../build/path_provider/intermediates/javac/debugUnitTest/classes" />
<exclude-output />
<content url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.2">
<sourceFolder url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.2/android/src/main/java" isTestSource="false" />
<sourceFolder url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.2/android/src/test/java" isTestSource="true" />
<excludeFolder url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.2/.dart_tool" />
<excludeFolder url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.2/.pub" />
<excludeFolder url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.2/android/.gradle" />
<excludeFolder url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.2/build" />
<excludeFolder url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.2/example/.dart_tool" />
<excludeFolder url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.2/example/.pub" />
<excludeFolder url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.2/example/build" />
</content>
<content url="file://$MODULE_DIR$/../../../../build/path_provider/generated/aidl_source_output_dir/debug/out" />
<content url="file://$MODULE_DIR$/../../../../build/path_provider/generated/aidl_source_output_dir/debugAndroidTest/out" />
<content url="file://$MODULE_DIR$/../../../../build/path_provider/generated/ap_generated_sources/debug/out">
<sourceFolder url="file://$MODULE_DIR$/../../../../build/path_provider/generated/ap_generated_sources/debug/out" isTestSource="false" generated="true" />
</content>
<content url="file://$MODULE_DIR$/../../../../build/path_provider/generated/ap_generated_sources/debugAndroidTest/out" />
<content url="file://$MODULE_DIR$/../../../../build/path_provider/generated/ap_generated_sources/debugUnitTest/out" />
<content url="file://$MODULE_DIR$/../../../../build/path_provider/generated/renderscript_source_output_dir/debug/out" />
<content url="file://$MODULE_DIR$/../../../../build/path_provider/generated/renderscript_source_output_dir/debugAndroidTest/out" />
<content url="file://$MODULE_DIR$/../../../../build/path_provider/generated/res/resValues/androidTest/debug" />
<content url="file://$MODULE_DIR$/../../../../build/path_provider/generated/res/resValues/debug">
<sourceFolder url="file://$MODULE_DIR$/../../../../build/path_provider/generated/res/resValues/debug" type="java-resource" />
</content>
<content url="file://$MODULE_DIR$/../../../../build/path_provider/generated/res/rs/androidTest/debug" />
<content url="file://$MODULE_DIR$/../../../../build/path_provider/generated/res/rs/debug" />
<content url="file://$MODULE_DIR$/../../../../build/path_provider/generated/source/buildConfig/androidTest/debug" />
<content url="file://$MODULE_DIR$/../../../../build/path_provider/generated/source/buildConfig/debug">
<sourceFolder url="file://$MODULE_DIR$/../../../../build/path_provider/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
</content>
<orderEntry type="jdk" jdkName="Android API 29 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" scope="TEST" name="Gradle: junit:junit:4.12" level="project" />
<orderEntry type="library" scope="TEST" name="Gradle: org.hamcrest:hamcrest-core:1.3" level="project" />
<orderEntry type="library" name="Gradle: io.flutter:flutter_embedding_debug:1.0.0-241c87ad800beeab545ab867354d4683d5bfb6ce" level="project" />
<orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-common-java8:2.2.0" level="project" />
<orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-common:2.2.0" level="project" />
<orderEntry type="library" name="Gradle: androidx.arch.core:core-common:2.1.0" level="project" />
<orderEntry type="library" name="Gradle: androidx.collection:collection:1.1.0" level="project" />
<orderEntry type="library" name="Gradle: androidx.annotation:annotation:1.1.0" level="project" />
<orderEntry type="library" name="Gradle: com.google.guava:guava:28.1-android" level="project" />
<orderEntry type="library" name="Gradle: com.google.guava:failureaccess:1.0.1" level="project" />
<orderEntry type="library" name="Gradle: com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava" level="project" />
<orderEntry type="library" name="Gradle: com.google.code.findbugs:jsr305:3.0.2" level="project" />
<orderEntry type="library" name="Gradle: org.checkerframework:checker-compat-qual:2.5.5" level="project" />
<orderEntry type="library" name="Gradle: com.google.errorprone:error_prone_annotations:2.3.2" level="project" />
<orderEntry type="library" name="Gradle: com.google.j2objc:j2objc-annotations:1.3" level="project" />
<orderEntry type="library" name="Gradle: org.codehaus.mojo:animal-sniffer-annotations:1.18" level="project" />
<orderEntry type="library" name="Gradle: androidx.fragment:fragment:1.1.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.activity:activity:1.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.viewpager:viewpager:1.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.loader:loader:1.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.customview:customview:1.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.core:core:1.1.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-runtime:2.2.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.savedstate:savedstate:1.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-livedata:2.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-livedata-core:2.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.arch.core:core-runtime:2.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.versionedparcelable:versionedparcelable:1.1.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-viewmodel:2.1.0@aar" level="project" />
<orderEntry type="library" name="Dart SDK" level="project" />
</component>
</module>

@ -1,84 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":shared_preferences" external.linked.project.path="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shared_preferences-2.0.6/android" external.root.project.path="$MODULE_DIR$/../../.." external.system.id="GRADLE" external.system.module.group="io.flutter.plugins.sharedpreferences" external.system.module.version="1.0-SNAPSHOT" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle">
<configuration>
<option name="GRADLE_PROJECT_PATH" value=":shared_preferences" />
<option name="LAST_SUCCESSFUL_SYNC_AGP_VERSION" value="4.1.0" />
<option name="LAST_KNOWN_AGP_VERSION" value="4.1.0" />
</configuration>
</facet>
<facet type="android" name="Android">
<configuration>
<option name="SELECTED_BUILD_VARIANT" value="debug" />
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
<afterSyncTasks>
<task>generateDebugSources</task>
</afterSyncTasks>
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shared_preferences-2.0.6/android/src/main/res;file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shared_preferences-2.0.6/android/src/debug/res;file://$MODULE_DIR$/../../../../build/shared_preferences/generated/res/rs/debug;file://$MODULE_DIR$/../../../../build/shared_preferences/generated/res/resValues/debug" />
<option name="TEST_RES_FOLDERS_RELATIVE_PATH" value="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shared_preferences-2.0.6/android/src/androidTest/res;file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shared_preferences-2.0.6/android/src/androidTestDebug/res;file://$MODULE_DIR$/../../../../build/shared_preferences/generated/res/rs/androidTest/debug;file://$MODULE_DIR$/../../../../build/shared_preferences/generated/res/resValues/androidTest/debug" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
<option name="PROJECT_TYPE" value="1" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7">
<output url="file://$MODULE_DIR$/../../../../build/shared_preferences/intermediates/javac/debug/classes" />
<output-test url="file://$MODULE_DIR$/../../../../build/shared_preferences/intermediates/javac/debugUnitTest/classes" />
<exclude-output />
<content url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shared_preferences-2.0.6">
<sourceFolder url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shared_preferences-2.0.6/android/src/main/java" isTestSource="false" />
<excludeFolder url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shared_preferences-2.0.6/.dart_tool" />
<excludeFolder url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shared_preferences-2.0.6/.pub" />
<excludeFolder url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shared_preferences-2.0.6/android/.gradle" />
<excludeFolder url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shared_preferences-2.0.6/build" />
<excludeFolder url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shared_preferences-2.0.6/example/.dart_tool" />
<excludeFolder url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shared_preferences-2.0.6/example/.pub" />
<excludeFolder url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shared_preferences-2.0.6/example/build" />
</content>
<content url="file://$MODULE_DIR$/../../../../build/shared_preferences/generated/aidl_source_output_dir/debug/out" />
<content url="file://$MODULE_DIR$/../../../../build/shared_preferences/generated/aidl_source_output_dir/debugAndroidTest/out" />
<content url="file://$MODULE_DIR$/../../../../build/shared_preferences/generated/ap_generated_sources/debug/out">
<sourceFolder url="file://$MODULE_DIR$/../../../../build/shared_preferences/generated/ap_generated_sources/debug/out" isTestSource="false" generated="true" />
</content>
<content url="file://$MODULE_DIR$/../../../../build/shared_preferences/generated/ap_generated_sources/debugAndroidTest/out" />
<content url="file://$MODULE_DIR$/../../../../build/shared_preferences/generated/ap_generated_sources/debugUnitTest/out" />
<content url="file://$MODULE_DIR$/../../../../build/shared_preferences/generated/renderscript_source_output_dir/debug/out" />
<content url="file://$MODULE_DIR$/../../../../build/shared_preferences/generated/renderscript_source_output_dir/debugAndroidTest/out" />
<content url="file://$MODULE_DIR$/../../../../build/shared_preferences/generated/res/resValues/androidTest/debug" />
<content url="file://$MODULE_DIR$/../../../../build/shared_preferences/generated/res/resValues/debug">
<sourceFolder url="file://$MODULE_DIR$/../../../../build/shared_preferences/generated/res/resValues/debug" type="java-resource" />
</content>
<content url="file://$MODULE_DIR$/../../../../build/shared_preferences/generated/res/rs/androidTest/debug" />
<content url="file://$MODULE_DIR$/../../../../build/shared_preferences/generated/res/rs/debug" />
<content url="file://$MODULE_DIR$/../../../../build/shared_preferences/generated/source/buildConfig/androidTest/debug" />
<content url="file://$MODULE_DIR$/../../../../build/shared_preferences/generated/source/buildConfig/debug">
<sourceFolder url="file://$MODULE_DIR$/../../../../build/shared_preferences/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
</content>
<orderEntry type="jdk" jdkName="Android API 29 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Gradle: io.flutter:flutter_embedding_debug:1.0.0-241c87ad800beeab545ab867354d4683d5bfb6ce" level="project" />
<orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-common-java8:2.2.0" level="project" />
<orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-common:2.2.0" level="project" />
<orderEntry type="library" name="Gradle: androidx.arch.core:core-common:2.1.0" level="project" />
<orderEntry type="library" name="Gradle: androidx.collection:collection:1.1.0" level="project" />
<orderEntry type="library" name="Gradle: androidx.annotation:annotation:1.1.0" level="project" />
<orderEntry type="library" name="Gradle: androidx.fragment:fragment:1.1.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.activity:activity:1.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.viewpager:viewpager:1.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.loader:loader:1.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.customview:customview:1.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.core:core:1.1.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-runtime:2.2.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.savedstate:savedstate:1.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-livedata:2.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-livedata-core:2.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.arch.core:core-runtime:2.0.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.versionedparcelable:versionedparcelable:1.1.0@aar" level="project" />
<orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-viewmodel:2.1.0@aar" level="project" />
</component>
</module>

@ -3,12 +3,11 @@
<component name="FacetManager">
<facet type="java-gradle" name="Java-Gradle">
<configuration>
<option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/../../../build" />
<option name="BUILDABLE" value="false" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<component name="NewModuleRootManager">
<exclude-output />
<content url="file://$MODULE_DIR$/../..">
<excludeFolder url="file://$MODULE_DIR$/../../.gradle" />

File diff suppressed because one or more lines are too long

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
</set>
</option>
</component>
</project>

@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 30
compileSdkVersion rootProject.ext.compileSdkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
@ -45,7 +45,7 @@ android {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "de.cantorgymnasium.meincantor"
minSdkVersion 20
targetSdkVersion 30
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}

@ -1,6 +1,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="de.cantorgymnasium.meincantor">
<uses-permission android:name="android.permission.INTERNET"/>
<application
tools:replace="android:label"
android:label="GCG.MeinCantor"
android:icon="@mipmap/ic_launcher">
<activity
@ -9,9 +12,9 @@
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
android:showWhenLocked="true"
android:turnScreenOn="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
@ -40,5 +43,4 @@
android:name="flutterEmbedding"
android:value="2" />
</application>
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

@ -1,12 +1,17 @@
buildscript {
ext.kotlin_version = '1.3.50'
ext.kotlin_version = '1.3.72'
ext {
compileSdkVersion = 30 // or latest
targetSdkVersion = 30 // or latest
appCompatVersion = "1.1.0" // or latest
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath 'com.android.tools.build:gradle:7.0.4'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
@ -15,6 +20,10 @@ allprojects {
repositories {
google()
mavenCentral()
maven {
// [required] background_fetch
url "${project(':background_fetch').projectDir}/libs"
}
}
}

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip

2132
android/hs_err_pid12837.log Normal file

File diff suppressed because it is too large Load Diff

1732
android/hs_err_pid6124.log Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

@ -0,0 +1,136 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="1025.8879"
height="560.1051"
viewBox="0 0 271.43285 148.19448"
version="1.1"
id="svg5"
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
sodipodi:docname="meincantor-big-dark.svg"
inkscape:export-filename="/home/denyskon/meincantor-big.png"
inkscape:export-xdpi="95.810593"
inkscape:export-ydpi="95.810593"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
units="px"
width="1024px"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="0.45892536"
inkscape:cx="290.89698"
inkscape:cy="459.76975"
inkscape:window-width="1920"
inkscape:window-height="1011"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2">
<rect
x="35.424603"
y="436.05106"
width="722.85211"
height="273.31204"
id="rect3787" />
<rect
id="rect101"
width="1231.0179"
height="630.59302"
x="-576.46198"
y="149.707" />
</defs>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0.24979931,0.24999975)">
<g
id="layer1-6"
transform="matrix(0.21011832,0,0,0.21011832,219.07839,-31.22162)"
inkscape:export-filename="/home/denys/GCG/Moodle/GCG Logo.png"
inkscape:export-xdpi="14.6812"
inkscape:export-ydpi="14.6812"
style="stroke:#1a1a37;stroke-width:2.37961;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
<path
style="fill:#ffbc38;fill-opacity:1;stroke:#1a1a37;stroke-width:2.37961;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path94"
d="m -800.20189,148.59075 c -15.66268,0.005 -30.93737,0.32979 -35.92752,0.97152 -24.97802,3.21209 -46.06623,10.40832 -71.03494,24.24038 -39.97029,22.14261 -74.9122,57.98028 -99.47355,102.02323 -30.8652,55.34705 -42.151,113.50098 -32.8321,169.17882 5.8702,35.07216 16.8579,61.17871 40.68799,96.67317 7.95457,11.84812 11.84213,16.78099 24.46931,31.04978 14.63989,16.54318 27.57386,28.10203 42.20104,37.7145 28.86973,18.97219 57.71137,30.08127 95.58848,36.81738 v 10e-4 c 5.82412,1.03579 11.34034,1.26458 31.88746,1.32448 29.27973,0.0855 41.98501,-0.79986 60.14103,-4.19251 28.56955,-5.3386 57.43662,-16.77149 86.12757,-34.11008 33.44712,-20.21282 61.80706,-48.4681 81.46479,-81.16454 2.61834,-4.35506 4.64965,-8.00301 4.51392,-8.10649 -1.17086,-0.89357 -95.91117,-53.61068 -96.34604,-53.61068 -0.31474,0 -1.29111,1.27902 -2.16939,2.84169 -6.00422,10.68316 -23.40278,29.78738 -34.7033,38.10569 -38.44099,28.29642 -85.40586,35.86808 -129.74092,20.91759 -44.55018,-15.02312 -79.25266,-52.35019 -90.71746,-97.57855 -3.25589,-12.8444 -4.07642,-19.7718 -4.10362,-34.62114 -0.0427,-24.05789 4.0303,-42.064 14.11128,-62.38792 23.94487,-48.27458 71.92399,-78.04898 125.76906,-78.04898 37.71851,0 72.5558,14.34737 98.97588,40.76237 7.22351,7.22214 17.36655,20.0891 21.37491,27.11514 1.14192,2.00157 2.27232,3.55695 2.51201,3.45663 0.23966,-0.1005 21.42683,-11.94867 47.08239,-26.32914 30.23246,-16.94596 46.47755,-26.42693 46.16617,-26.94409 -15.24453,-25.3227 -37.5117,-51.05103 -60.39477,-69.78282 -19.53739,-15.99307 -45.92463,-31.3187 -70.29079,-40.8249 -21.82173,-8.51359 -38.29612,-12.71106 -57.08179,-14.54278 -6.5434,-0.63855 -22.59441,-0.95366 -38.2571,-0.94878 z m 71.03339,248.12387 c -33.16717,0 -60.11718,0.18715 -59.88884,0.41548 0.22842,0.22842 27.12928,15.96042 59.77929,34.95962 32.65006,18.9992 59.59962,34.55902 59.88834,34.57722 0.28884,0.0183 0.52504,-15.7137 0.52504,-34.95962 v -34.9927 z" />
<text
style="font-style:normal;font-weight:400;font-size:499.798px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect101);fill:#ffbc38;fill-opacity:1;stroke:#1a1a37;stroke-width:1.75069;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="text99"
transform="matrix(1.3592448,0,0,1.3592448,227.35348,-182.72846)"
xml:space="preserve"><tspan
x="-576.46289"
y="601.60773"
id="tspan4911"><tspan
style="font-weight:900;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Heavy';shape-inside:url(#rect101)"
id="tspan4909">CG</tspan></tspan></text>
</g>
<g
aria-label="MeinCantor"
transform="matrix(1.2948467,0,0,1.2948467,-47.577624,-464.38522)"
id="text3785"
style="font-size:40px;line-height:1.25;white-space:pre;shape-inside:url(#rect3787);fill:#ffbc3b;fill-opacity:1;stroke:#1a1a37;stroke-width:0.3861461;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:export-filename="/home/denyskon/meincantor/app/assets/images/meincantor-big-dark.png"
inkscape:export-xdpi="95.810593"
inkscape:export-ydpi="95.810593">
<path
d="m 63.463828,444.49745 h -8.32 l -3.88,18.8 -4.16,-18.8 h -8.28 l -2.08,27.72 h 6.4 l 0.48,-10.76 c 0.16,-3.88 0.16,-7.32 -0.12,-11.4 l 4.56,19.24 h 6.2 l 4.28,-19.24 c -0.2,3.44 -0.12,7.44 0.08,11.32 l 0.48,10.84 h 6.44 z"
style="font-weight:bold;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Bold';fill:#ffbc3b;fill-opacity:1;stroke:#1a1a37;stroke-width:0.3861461;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path28345" />
<path
d="m 87.943756,461.17745 c 0,-6.88 -3.72,-10.88 -9.8,-10.88 -6.44,0 -9.92,5.04 -9.92,11.4 0,6.64 3.6,11.2 10.68,11.2 3.44,0 6.16,-1.28 8.28,-2.96 l -2.6,-3.52 c -1.84,1.28 -3.36,1.88 -5.08,1.88 -2.6,0 -4.4,-1.04 -4.8,-4.84 h 13.12 c 0.08,-0.64 0.12,-1.6 0.12,-2.28 z m -6.2,-1.52 h -7.08 c 0.32,-3.84 1.52,-5.16 3.64,-5.16 2.52,0 3.4,2 3.44,4.88 z"
style="font-weight:bold;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Bold';fill:#ffbc3b;fill-opacity:1;stroke:#1a1a37;stroke-width:0.3861461;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path28347" />
<path
d="m 94.703714,439.77745 c -2.2,0 -3.72,1.56 -3.72,3.6 0,2.04 1.52,3.6 3.72,3.6 2.2,0 3.76,-1.56 3.76,-3.6 0,-2.04 -1.56,-3.6 -3.76,-3.6 z m 3.2,11.2 h -6.32 v 21.24 h 6.32 z"
style="font-weight:bold;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Bold';fill:#ffbc3b;fill-opacity:1;stroke:#1a1a37;stroke-width:0.3861461;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path28349" />
<path
d="m 115.3037,450.29745 c -2.64,0 -4.68,1.12 -6.48,3.2 l -0.44,-2.52 h -5.52 v 21.24 h 6.32 v -14.52 c 1.12,-1.76 2.28,-2.76 3.68,-2.76 1.24,0 2,0.6 2,2.84 v 14.44 h 6.32 v -15.52 c 0,-4.04 -2.2,-6.4 -5.88,-6.4 z"
style="font-weight:bold;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Bold';fill:#ffbc3b;fill-opacity:1;stroke:#1a1a37;stroke-width:0.3861461;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path28351" />
<path
d="m 137.26368,443.81745 c -7.12,0 -12.48,5.2 -12.48,14.48 0,9.48 5.08,14.6 12.68,14.6 3.84,0 6.88,-1.72 8.68,-3.56 l -2.96,-3.84 c -1.6,1.24 -3.2,2.32 -5.4,2.32 -3.68,0 -6.12,-2.84 -6.12,-9.52 0,-6.8 2.48,-9.64 6,-9.64 1.92,0 3.48,0.72 4.96,2 l 3.2,-3.8 c -2.28,-1.92 -4.8,-3.04 -8.56,-3.04 z"
style="font-weight:bold;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Bold';fill:#ffbc3b;fill-opacity:1;stroke:#1a1a37;stroke-width:0.3861461;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path28353" />
<path
d="m 165.82364,466.05745 v -8.24 c 0,-4.92 -2.64,-7.52 -8.8,-7.52 -2.4,0 -5.32,0.56 -8,1.56 l 1.44,4.16 c 2.12,-0.72 4.2,-1.12 5.56,-1.12 2.6,0 3.6,0.76 3.6,3.28 v 0.92 h -2.16 c -6.44,0 -9.96,2.52 -9.96,7.16 0,3.88 2.68,6.64 6.92,6.64 2.52,0 4.88,-0.84 6.4,-3.16 1,2.08 2.64,2.84 5.12,3.04 l 1.32,-4.12 c -1,-0.36 -1.44,-0.96 -1.44,-2.6 z m -9.56,2.32 c -1.56,0 -2.48,-1 -2.48,-2.68 0,-2.12 1.4,-3.12 4.28,-3.12 h 1.56 v 3.8 c -0.76,1.24 -1.92,2 -3.36,2 z"
style="font-weight:bold;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Bold';fill:#ffbc3b;fill-opacity:1;stroke:#1a1a37;stroke-width:0.3861461;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path28355" />
<path
d="m 183.30358,450.29745 c -2.64,0 -4.68,1.12 -6.48,3.2 l -0.44,-2.52 h -5.52 v 21.24 h 6.32 v -14.52 c 1.12,-1.76 2.28,-2.76 3.68,-2.76 1.24,0 2,0.6 2,2.84 v 14.44 h 6.32 v -15.52 c 0,-4.04 -2.2,-6.4 -5.88,-6.4 z"
style="font-weight:bold;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Bold';fill:#ffbc3b;fill-opacity:1;stroke:#1a1a37;stroke-width:0.3861461;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path28357" />
<path
d="m 205.14356,467.17745 c -0.84,0.52 -1.56,0.72 -2.28,0.72 -1.32,0 -2,-0.72 -2,-2.76 v -9.76 h 4.2 l 0.68,-4.4 h -4.88 v -5.32 l -6.32,0.72 v 4.6 h -3 v 4.4 h 3 v 9.84 c 0,5 2.28,7.64 6.96,7.68 1.92,0 4.16,-0.56 5.76,-1.68 z"
style="font-weight:bold;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Bold';fill:#ffbc3b;fill-opacity:1;stroke:#1a1a37;stroke-width:0.3861461;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path28359" />
<path
d="m 218.18354,450.29745 c -6.48,0 -10.4,4.48 -10.4,11.28 0,7.16 3.96,11.32 10.4,11.32 6.48,0 10.4,-4.48 10.4,-11.28 0,-7.16 -3.92,-11.32 -10.4,-11.32 z m 0,4.64 c 2.56,0 3.84,1.96 3.84,6.68 0,4.6 -1.28,6.64 -3.84,6.64 -2.56,0 -3.84,-1.96 -3.84,-6.68 0,-4.6 1.28,-6.64 3.84,-6.64 z"
style="font-weight:bold;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Bold';fill:#ffbc3b;fill-opacity:1;stroke:#1a1a37;stroke-width:0.3861461;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path28361" />
<path
d="m 243.86353,450.37745 c -2.44,0 -4.52,1.76 -5.48,4.68 l -0.52,-4.08 h -5.52 v 21.24 h 6.32 v -10.6 c 0.68,-3.2 1.8,-5.12 4.4,-5.12 0.72,0 1.24,0.12 1.92,0.28 l 1,-6.12 c -0.68,-0.2 -1.32,-0.28 -2.12,-0.28 z"
style="font-weight:bold;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Bold';fill:#ffbc3b;fill-opacity:1;stroke:#1a1a37;stroke-width:0.3861461;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path28363" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

@ -0,0 +1,136 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="1025.8879"
height="560.1051"
viewBox="0 0 271.43285 148.19448"
version="1.1"
id="svg5"
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
sodipodi:docname="meincantor-big.svg"
inkscape:export-filename="/home/denyskon/meincantor-big.png"
inkscape:export-xdpi="95.810593"
inkscape:export-ydpi="95.810593"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
units="px"
width="1024px"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="0.45892536"
inkscape:cx="290.89698"
inkscape:cy="459.76975"
inkscape:window-width="1920"
inkscape:window-height="1011"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2">
<rect
x="35.424603"
y="436.05106"
width="722.85211"
height="273.31204"
id="rect3787" />
<rect
id="rect101"
width="1231.0179"
height="630.59302"
x="-576.46198"
y="149.707" />
</defs>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0.24979931,0.24999975)">
<g
id="layer1-6"
transform="matrix(0.21011832,0,0,0.21011832,219.07839,-31.22162)"
inkscape:export-filename="/home/denys/GCG/Moodle/GCG Logo.png"
inkscape:export-xdpi="14.6812"
inkscape:export-ydpi="14.6812"
style="stroke:#1a1a37;stroke-width:2.37961;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
<path
style="fill:#ffbc38;fill-opacity:1;stroke:#1a1a37;stroke-width:2.37961;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path94"
d="m -800.20189,148.59075 c -15.66268,0.005 -30.93737,0.32979 -35.92752,0.97152 -24.97802,3.21209 -46.06623,10.40832 -71.03494,24.24038 -39.97029,22.14261 -74.9122,57.98028 -99.47355,102.02323 -30.8652,55.34705 -42.151,113.50098 -32.8321,169.17882 5.8702,35.07216 16.8579,61.17871 40.68799,96.67317 7.95457,11.84812 11.84213,16.78099 24.46931,31.04978 14.63989,16.54318 27.57386,28.10203 42.20104,37.7145 28.86973,18.97219 57.71137,30.08127 95.58848,36.81738 v 10e-4 c 5.82412,1.03579 11.34034,1.26458 31.88746,1.32448 29.27973,0.0855 41.98501,-0.79986 60.14103,-4.19251 28.56955,-5.3386 57.43662,-16.77149 86.12757,-34.11008 33.44712,-20.21282 61.80706,-48.4681 81.46479,-81.16454 2.61834,-4.35506 4.64965,-8.00301 4.51392,-8.10649 -1.17086,-0.89357 -95.91117,-53.61068 -96.34604,-53.61068 -0.31474,0 -1.29111,1.27902 -2.16939,2.84169 -6.00422,10.68316 -23.40278,29.78738 -34.7033,38.10569 -38.44099,28.29642 -85.40586,35.86808 -129.74092,20.91759 -44.55018,-15.02312 -79.25266,-52.35019 -90.71746,-97.57855 -3.25589,-12.8444 -4.07642,-19.7718 -4.10362,-34.62114 -0.0427,-24.05789 4.0303,-42.064 14.11128,-62.38792 23.94487,-48.27458 71.92399,-78.04898 125.76906,-78.04898 37.71851,0 72.5558,14.34737 98.97588,40.76237 7.22351,7.22214 17.36655,20.0891 21.37491,27.11514 1.14192,2.00157 2.27232,3.55695 2.51201,3.45663 0.23966,-0.1005 21.42683,-11.94867 47.08239,-26.32914 30.23246,-16.94596 46.47755,-26.42693 46.16617,-26.94409 -15.24453,-25.3227 -37.5117,-51.05103 -60.39477,-69.78282 -19.53739,-15.99307 -45.92463,-31.3187 -70.29079,-40.8249 -21.82173,-8.51359 -38.29612,-12.71106 -57.08179,-14.54278 -6.5434,-0.63855 -22.59441,-0.95366 -38.2571,-0.94878 z m 71.03339,248.12387 c -33.16717,0 -60.11718,0.18715 -59.88884,0.41548 0.22842,0.22842 27.12928,15.96042 59.77929,34.95962 32.65006,18.9992 59.59962,34.55902 59.88834,34.57722 0.28884,0.0183 0.52504,-15.7137 0.52504,-34.95962 v -34.9927 z" />
<text
style="font-style:normal;font-weight:400;font-size:499.798px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect101);fill:#ffbc38;fill-opacity:1;stroke:#1a1a37;stroke-width:1.75069;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="text99"
transform="matrix(1.3592448,0,0,1.3592448,227.35348,-182.72846)"
xml:space="preserve"><tspan
x="-576.46289"
y="601.60773"
id="tspan4975"><tspan
style="font-weight:900;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Heavy';shape-inside:url(#rect101)"
id="tspan4973">CG</tspan></tspan></text>
</g>
<g
aria-label="MeinCantor"
transform="matrix(1.2948467,0,0,1.2948467,-47.577624,-464.38522)"
id="text3785"
style="font-size:40px;line-height:1.25;white-space:pre;shape-inside:url(#rect3787);fill:#1a1a37;fill-opacity:1;stroke:none;stroke-width:0.386146;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:export-filename="/home/denyskon/meincantor/app/assets/images/meincantor-big-dark.png"
inkscape:export-xdpi="95.810593"
inkscape:export-ydpi="95.810593">
<path
d="m 63.463828,444.49745 h -8.32 l -3.88,18.8 -4.16,-18.8 h -8.28 l -2.08,27.72 h 6.4 l 0.48,-10.76 c 0.16,-3.88 0.16,-7.32 -0.12,-11.4 l 4.56,19.24 h 6.2 l 4.28,-19.24 c -0.2,3.44 -0.12,7.44 0.08,11.32 l 0.48,10.84 h 6.44 z"
style="font-weight:bold;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Bold';fill:#1a1a37;fill-opacity:1;stroke:none;stroke-width:0.386146;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path28345" />
<path
d="m 87.943756,461.17745 c 0,-6.88 -3.72,-10.88 -9.8,-10.88 -6.44,0 -9.92,5.04 -9.92,11.4 0,6.64 3.6,11.2 10.68,11.2 3.44,0 6.16,-1.28 8.28,-2.96 l -2.6,-3.52 c -1.84,1.28 -3.36,1.88 -5.08,1.88 -2.6,0 -4.4,-1.04 -4.8,-4.84 h 13.12 c 0.08,-0.64 0.12,-1.6 0.12,-2.28 z m -6.2,-1.52 h -7.08 c 0.32,-3.84 1.52,-5.16 3.64,-5.16 2.52,0 3.4,2 3.44,4.88 z"
style="font-weight:bold;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Bold';fill:#1a1a37;fill-opacity:1;stroke:none;stroke-width:0.386146;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path28347" />
<path
d="m 94.703714,439.77745 c -2.2,0 -3.72,1.56 -3.72,3.6 0,2.04 1.52,3.6 3.72,3.6 2.2,0 3.76,-1.56 3.76,-3.6 0,-2.04 -1.56,-3.6 -3.76,-3.6 z m 3.2,11.2 h -6.32 v 21.24 h 6.32 z"
style="font-weight:bold;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Bold';fill:#1a1a37;fill-opacity:1;stroke:none;stroke-width:0.386146;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path28349" />
<path
d="m 115.3037,450.29745 c -2.64,0 -4.68,1.12 -6.48,3.2 l -0.44,-2.52 h -5.52 v 21.24 h 6.32 v -14.52 c 1.12,-1.76 2.28,-2.76 3.68,-2.76 1.24,0 2,0.6 2,2.84 v 14.44 h 6.32 v -15.52 c 0,-4.04 -2.2,-6.4 -5.88,-6.4 z"
style="font-weight:bold;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Bold';fill:#1a1a37;fill-opacity:1;stroke:none;stroke-width:0.386146;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path28351" />
<path
d="m 137.26368,443.81745 c -7.12,0 -12.48,5.2 -12.48,14.48 0,9.48 5.08,14.6 12.68,14.6 3.84,0 6.88,-1.72 8.68,-3.56 l -2.96,-3.84 c -1.6,1.24 -3.2,2.32 -5.4,2.32 -3.68,0 -6.12,-2.84 -6.12,-9.52 0,-6.8 2.48,-9.64 6,-9.64 1.92,0 3.48,0.72 4.96,2 l 3.2,-3.8 c -2.28,-1.92 -4.8,-3.04 -8.56,-3.04 z"
style="font-weight:bold;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Bold';fill:#1a1a37;fill-opacity:1;stroke:none;stroke-width:0.386146;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path28353" />
<path
d="m 165.82364,466.05745 v -8.24 c 0,-4.92 -2.64,-7.52 -8.8,-7.52 -2.4,0 -5.32,0.56 -8,1.56 l 1.44,4.16 c 2.12,-0.72 4.2,-1.12 5.56,-1.12 2.6,0 3.6,0.76 3.6,3.28 v 0.92 h -2.16 c -6.44,0 -9.96,2.52 -9.96,7.16 0,3.88 2.68,6.64 6.92,6.64 2.52,0 4.88,-0.84 6.4,-3.16 1,2.08 2.64,2.84 5.12,3.04 l 1.32,-4.12 c -1,-0.36 -1.44,-0.96 -1.44,-2.6 z m -9.56,2.32 c -1.56,0 -2.48,-1 -2.48,-2.68 0,-2.12 1.4,-3.12 4.28,-3.12 h 1.56 v 3.8 c -0.76,1.24 -1.92,2 -3.36,2 z"
style="font-weight:bold;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Bold';fill:#1a1a37;fill-opacity:1;stroke:none;stroke-width:0.386146;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path28355" />
<path
d="m 183.30358,450.29745 c -2.64,0 -4.68,1.12 -6.48,3.2 l -0.44,-2.52 h -5.52 v 21.24 h 6.32 v -14.52 c 1.12,-1.76 2.28,-2.76 3.68,-2.76 1.24,0 2,0.6 2,2.84 v 14.44 h 6.32 v -15.52 c 0,-4.04 -2.2,-6.4 -5.88,-6.4 z"
style="font-weight:bold;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Bold';fill:#1a1a37;fill-opacity:1;stroke:none;stroke-width:0.386146;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path28357" />
<path
d="m 205.14356,467.17745 c -0.84,0.52 -1.56,0.72 -2.28,0.72 -1.32,0 -2,-0.72 -2,-2.76 v -9.76 h 4.2 l 0.68,-4.4 h -4.88 v -5.32 l -6.32,0.72 v 4.6 h -3 v 4.4 h 3 v 9.84 c 0,5 2.28,7.64 6.96,7.68 1.92,0 4.16,-0.56 5.76,-1.68 z"
style="font-weight:bold;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Bold';fill:#1a1a37;fill-opacity:1;stroke:none;stroke-width:0.386146;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path28359" />
<path
d="m 218.18354,450.29745 c -6.48,0 -10.4,4.48 -10.4,11.28 0,7.16 3.96,11.32 10.4,11.32 6.48,0 10.4,-4.48 10.4,-11.28 0,-7.16 -3.92,-11.32 -10.4,-11.32 z m 0,4.64 c 2.56,0 3.84,1.96 3.84,6.68 0,4.6 -1.28,6.64 -3.84,6.64 -2.56,0 -3.84,-1.96 -3.84,-6.68 0,-4.6 1.28,-6.64 3.84,-6.64 z"
style="font-weight:bold;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Bold';fill:#1a1a37;fill-opacity:1;stroke:none;stroke-width:0.386146;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path28361" />
<path
d="m 243.86353,450.37745 c -2.44,0 -4.52,1.76 -5.48,4.68 l -0.52,-4.08 h -5.52 v 21.24 h 6.32 v -10.6 c 0.68,-3.2 1.8,-5.12 4.4,-5.12 0.72,0 1.24,0.12 1.92,0.28 l 1,-6.12 c -0.68,-0.2 -1.32,-0.28 -2.12,-0.28 z"
style="font-weight:bold;font-family:'Fira Sans';-inkscape-font-specification:'Fira Sans Bold';fill:#1a1a37;fill-opacity:1;stroke:none;stroke-width:0.386146;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path28363" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

@ -1,4 +1,3 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class AppearanceSettings extends StatelessWidget {
@ -13,7 +12,7 @@ class AppearanceSettings extends StatelessWidget {
),
body: ListView(
padding: const EdgeInsets.fromLTRB(5, 5, 5, 5),
children: [],
children: const [],
));
}
}

@ -1,12 +1,29 @@
import 'package:background_fetch/background_fetch.dart';
import 'package:flutter/material.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:meincantor/background_fetch.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../login.dart';
import 'package:meincantor/login.dart';
class DevSettings extends StatelessWidget {
class DevSettings extends StatefulWidget {
const DevSettings({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => _DevSettingsState();
}
class _DevSettingsState extends State<DevSettings> {
int _status = 0;
void _onClickStatus() async {
int status = await BackgroundFetch.status;
print('[BackgroundFetch] status: $status');
setState(() {
_status = status;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
@ -14,41 +31,98 @@ class DevSettings extends StatelessWidget {
title: const Text("Entwickler-Einstellungen"),
centerTitle: true,
),
body: ListView(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 20),
children: [
Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
child: TextField(
decoration: const InputDecoration(
icon: Icon(MdiIcons.keyOutline),
border: OutlineInputBorder(),
labelText: 'MeinCantor API-Schlüssel',
),
onSubmitted: (String value) async {
SharedPreferences prefs =
await SharedPreferences.getInstance();
String apiKey = value;
await prefs.setString('api_key', apiKey);
final snackBar = SnackBar(
content:
Text('Neuer API-Schlüssel gesetzt: $apiKey'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
})),
const Divider(),
Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
child: OutlinedButton(
onPressed: () async {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Login()),
);
},
child: const Text("Benutzerdaten neu laden"),
)
)
],
));
body: LayoutBuilder(builder: (context, constraints) {
double widgetWidth = constraints.maxWidth;
int factor;
if (widgetWidth <= 600) {
factor = 1;
} else if (widgetWidth <= 1400) {
factor = 2;
} else if (widgetWidth <= 2000) {
factor = 3;
} else {
factor = 1;
}
return Center(
heightFactor: 1,
child: Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width / factor,
),
child: ListView(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 20),
children: [
Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
child: TextField(
decoration: const InputDecoration(
icon: Icon(MdiIcons.keyOutline),
border: OutlineInputBorder(),
labelText: 'MeinCantor API-Schlüssel',
),
onSubmitted: (String value) async {
SharedPreferences prefs =
await SharedPreferences.getInstance();
String apiKey = value;
await prefs.setString('api_key', apiKey);
final snackBar = SnackBar(
content: Text(
'Neuer API-Schlüssel gesetzt: $apiKey'));
ScaffoldMessenger.of(context)
.showSnackBar(snackBar);
})),
const Divider(),
Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
child: OutlinedButton(
onPressed: () async {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Login()),
);
},
child: const Text("Benutzerdaten neu laden"),
)),
const Divider(),
Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
child: OutlinedButton(
onPressed: () {
_onClickStatus();
},
child: const Text(
"Status der Hintergrundoperation anzeigen"),
)),
const Divider(),
Padding(
padding: const EdgeInsets.fromLTRB(10, 10, 10, 10),
child: Text(_status.toString())),
const Divider(),
Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
child: OutlinedButton(
onPressed: () async {
await backgroundFetchTimetable();
},
child: const Text(
"Stundenplan im Hintergrund neu laden"),
)),
const Divider(),
Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
child: OutlinedButton(
onPressed: () async {
await backgroundFetchArticles();
},
child: const Text(
"Schülerzeitung im Hintergrund neu laden"),
)),
],
)),
);
}));
}
}

@ -1,6 +1,3 @@
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:meincantor/const.dart';
@ -17,79 +14,136 @@ class InfoSettings extends StatelessWidget {
title: const Text("Informationen"),
centerTitle: true,
),
body: ListView(
padding: const EdgeInsets.fromLTRB(5, 5, 5, 5),
children: [
const ListTile(
leading: Icon(Icons.info_outlined),
title: Text("Version"),
subtitle: Text(version)),
ListTile(
leading: Icon(Icons.person_outlined),
title: Text("Autor"),
subtitle: Text(author),
onTap: () => launch("https://git.cantorgymnasium.de/denyskon"),
),
ListTile(
leading: const Icon(Icons.source_outlined),
title: const Text("Quellcode"),
subtitle: Linkify(
onOpen: (link) async {
if (await canLaunch(link.url)) {
await launch(link.url);
} else {
throw 'Could not launch $link';
}
},
text: "https://git.cantorgymnasium.de/cantortechnik/meincantor-app",
linkStyle: const TextStyle(color: Palette.accent),
body: LayoutBuilder(builder: (context, constraints) {
double widgetWidth = constraints.maxWidth;
int factor;
if (widgetWidth <= 600) {
factor = 1;
} else if (widgetWidth <= 1400) {
factor = 2;
} else if (widgetWidth <= 2000) {
factor = 3;
} else {
factor = 1;
}
return Center(
heightFactor: 1,
child: Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width / factor,
),
),
ListTile(
leading: const Icon(Icons.settings_backup_restore_outlined),
title: const Text("Änderungsverlauf"),
subtitle: const Text("Was ist neu?"),
onTap: () {
showModalBottomSheet<void>(
isScrollControlled: true,
context: context,
builder: (BuildContext context) {
return SizedBox(
height: 400,
child: Column(
children: <Widget>[
AppBar(
title: const Text("Änderungsverlauf"),
),
const Padding(
padding: EdgeInsets.all(10),
child: Text("1.0 --\nErste Release-Version!"),
),
],
child: ListView(
padding: const EdgeInsets.fromLTRB(5, 5, 5, 5),
children: [
const Padding(
padding: EdgeInsets.all(5),
child: ListTile(
leading: Icon(Icons.info_outlined),
title: Text("Version"),
subtitle: Text(version)),
),
Padding(
padding: const EdgeInsets.all(5),
child: ListTile(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0)),
leading: const Icon(Icons.person_outlined),
title: const Text("Autor"),
subtitle: const Text(author),
onTap: () =>
launch("https://git.cantorgymnasium.de/denyskon"),
),
);
},
);
},
),
ListTile(
leading: const Icon(Icons.copyright_outlined),
title: const Text("Lizenzen"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LicensePage(
applicationIcon: Image.asset(
"assets/images/meincantor_r.png",
height: 64,
width: 64),
applicationVersion: version,
)),
);
},
),
],
));
),
Padding(
padding: const EdgeInsets.all(5),
child: ListTile(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0)),
leading: const Icon(Icons.source_outlined),
title: const Text("Quellcode"),
subtitle: Linkify(
onOpen: (link) async {
if (await canLaunch(link.url)) {
await launch(link.url);
} else {
throw 'Could not launch $link';
}
},
text:
"https://git.cantorgymnasium.de/cantortechnik/meincantor-app",
linkStyle: const TextStyle(color: Palette.accent),
),
onTap: () => launch(
"https://git.cantorgymnasium.de/cantortechnik/meincantor-app")),
),
Padding(
padding: const EdgeInsets.all(5),
child: ListTile(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0)),
leading:
const Icon(Icons.settings_backup_restore_outlined),
title: const Text("Änderungsverlauf"),
subtitle: const Text("Was ist neu?"),
onTap: () {
showModalBottomSheet<void>(
isScrollControlled: true,
context: context,
builder: (BuildContext context) {
return SizedBox(
height: 400,
child: Column(
children: <Widget>[
AppBar(
title: const Text("Änderungsverlauf"),
),
const Padding(
padding: EdgeInsets.all(10),
child: Text(
"1.0.0 --\nErste Release-Version!"),
),
],
),
);
},
);
},
),
),
Padding(
padding: const EdgeInsets.all(5),
child: ListTile(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0)),
leading: const Icon(Icons.copyright_outlined),
title: const Text("Lizenzen"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LicensePage(
applicationIcon: Padding(
padding: const EdgeInsets.all(5),
child: MediaQuery.of(context).platformBrightness == Brightness.light
? Image.asset(
"assets/images/meincantor-big.png",
width: 196)
: Image.asset(
"assets/images/meincantor-big-dark.png",
width: 196)
),
applicationVersion: version,
)),
);
},
),
),
],
)),
);
}));
}
}

@ -1,9 +1,9 @@
import 'dart:convert';
import 'package:meincantor/main.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cyclop/cyclop.dart';
import 'package:meincantor/networking.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -12,52 +12,6 @@ import 'package:meincantor/presets/subjects.dart';
import 'package:meincantor/presets/teachers.dart';
class PlanSettings extends StatelessWidget {
const PlanSettings({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Plan"),
centerTitle: true,
),
body: ListView(
padding: const EdgeInsets.fromLTRB(5, 5, 5, 5),
children: [
ListTile(
leading: const Icon(Icons.list_alt_outlined, color: Colors.red),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
title: const Text("Kurse"),
subtitle: const Text("Konfiguration der Kurse (Whitelist)"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const WhitelistSettings()),
);
},
),
ListTile(
leading:
const Icon(Icons.color_lens_outlined, color: Colors.teal),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
title: const Text("Farben"),
subtitle:
const Text("Konfiguration der Farben für die Plankacheln"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const PlanColorSettings()),
);
},
),
],
));
}
}
class WhitelistSettings extends StatefulWidget {
const WhitelistSettings({Key? key}) : super(key: key);
@ -77,7 +31,7 @@ class _WhitelistSettingsState extends State<WhitelistSettings> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Farben"),
title: const Text("Kurse"),
centerTitle: true,
),
body: ListView(
@ -106,11 +60,36 @@ class _WhitelistSettingsState extends State<WhitelistSettings> {
snapshot.data! as List<dynamic>;
final _blacklisted =
_blacklist.contains(id);
return ListTile(
leading: Checkbox(
value:
_blacklisted ? false : true,
onChanged: (state) async {
return Padding(
padding: const EdgeInsets.all(5),
child: ListTile(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(
15.0)),
leading: Checkbox(
value: _blacklisted
? false
: true,
onChanged: (state) async {
SharedPreferences prefs =
await SharedPreferences
.getInstance();
setState(() {
_blacklisted
? _blacklist
.remove(id)
: _blacklist.add(id);
});
prefs.setString("blacklist",
jsonEncode(_blacklist));
},
activeColor: color),
title: Text(
subjects[subject] ?? subject),
subtitle: Text(
teachers[teacher] ?? teacher),
onTap: () async {
SharedPreferences prefs =
await SharedPreferences
.getInstance();
@ -122,23 +101,7 @@ class _WhitelistSettingsState extends State<WhitelistSettings> {
prefs.setString("blacklist",
jsonEncode(_blacklist));
},
activeColor: color),
title: Text(subjects[subject] ?? ""),
subtitle:
Text(teachers[teacher] ?? ""),
onTap: () async {
SharedPreferences prefs =
await SharedPreferences
.getInstance();
setState(() {
_blacklisted
? _blacklist.remove(id)
: _blacklist.add(id);
});
prefs.setString("blacklist",
jsonEncode(_blacklist));
},
);
));
} else {
return const LinearProgressIndicator();
}
@ -187,8 +150,12 @@ Future<Color> buildPlanColors(String lesson) async {
Future<List<dynamic>> buildLessonsList() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String lessonsJson = prefs.getString("lessons")!;
List<dynamic> lessons = jsonDecode(lessonsJson);
String? lessonsJson = prefs.getString("lessons");
if (lessonsJson == null || lessonsJson.isEmpty) {
await fetchLessonList();
lessonsJson = prefs.getString("lessons");
}
List<dynamic> lessons = jsonDecode(lessonsJson!);
return lessons;
}
@ -235,30 +202,37 @@ class _PlanColorSettingsState extends State<PlanColorSettings> {
builder: (context, snapshot) {
if (snapshot.hasData) {
Color color = snapshot.data as Color;
return ListTile(
leading: ColorButton(
key: const Key('c1'),
color: color,
config: const ColorPickerConfig(
enableEyePicker: false),
onSwatchesChanged: (Set<Color> value) {
swatches = value;
},
size: 32,
swatches: swatches,
onColorChanged: (Color value) async {
setState(() {
color = value;
});
SharedPreferences prefs =
await SharedPreferences
.getInstance();
prefs.setInt(
"color$subject", value.value);
}),
title: Text(subjects[subject] ?? ""),
subtitle: Text(teachers[teacher] ?? ""),
);
return Padding(
padding: const EdgeInsets.all(5),
child: ListTile(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(15.0)),
leading: ColorButton(
key: const Key('c1'),
color: color,
config: const ColorPickerConfig(
enableEyePicker: false),
onSwatchesChanged:
(Set<Color> value) {
swatches = value;
},
size: 32,
swatches: swatches,
onColorChanged: (Color value) async {
setState(() {
color = value;
});
SharedPreferences prefs =
await SharedPreferences
.getInstance();
prefs.setInt(
"color$subject", value.value);
}),
title: Text(subjects[subject] ?? subject),
subtitle:
Text(teachers[teacher] ?? teacher),
));
} else {
return (const LinearProgressIndicator());
}

@ -1,4 +1,3 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class ServiceSettings extends StatelessWidget {
@ -13,7 +12,7 @@ class ServiceSettings extends StatelessWidget {
),
body: ListView(
padding: const EdgeInsets.fromLTRB(5, 5, 5, 5),
children: [],
children: const [],
));
}
}

@ -1,14 +1,10 @@
import 'package:cyclop/cyclop.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:meincantor/Settings/Pages/plan_settings.dart';
import 'package:meincantor/networking.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'dart:io' show Platform;
import '../../const.dart';
import 'package:meincantor/const.dart';
import 'package:webviewx/webviewx.dart';
Future<String> getSettingsString(String key) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
@ -30,104 +26,172 @@ class UserSettings extends StatelessWidget {
title: const Text("Benutzereinstellungen"),
centerTitle: true,
),
body: ListView(
padding: const EdgeInsets.fromLTRB(5, 5, 5, 5),
children: [
Padding(
padding: const EdgeInsets.fromLTRB(10, 10, 10, 10),
child: FutureBuilder(
future: Future.sync(() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? user = prefs.getString("user");
if (user == null || user.isEmpty) {
user = "";
}
String? name = prefs.getString("name");
if (name == null || name.isEmpty) {
name = "";
}
Map data = {"user": user, "name": name };
return data;
}),
builder: (context, snapshot) {
if (snapshot.hasData) {
// .svg?text=${(snapshot.data! as Map)['name'][0]}
String url = "$avatarUrl/${(snapshot.data! as Map)['user']}";
return Container(
width: 120.0,
height: 120.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
fit: BoxFit.scaleDown,
image: NetworkImage(url)
)
)
);
} else {
return const CircularProgressIndicator();
}
},
),
),
FutureBuilder(
future: getSettingsString("name"),
builder: (context, snapshot) {
if (snapshot.hasData) {
return TextField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Name',
body: LayoutBuilder(builder: (context, constraints) {
double widgetWidth = constraints.maxWidth;
int factor;
if (widgetWidth <= 600) {
factor = 1;
} else if (widgetWidth <= 1400) {
factor = 2;
} else if (widgetWidth <= 2000) {
factor = 3;
} else {
factor = 1;
}
return Center(
heightFactor: 1,
child: Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width / factor,
),
child: ListView(
padding: const EdgeInsets.fromLTRB(5, 5, 5, 5),
children: [
Padding(
padding: const EdgeInsets.fromLTRB(10, 10, 10, 10),
child: FutureBuilder(
future: Future.sync(() async {
SharedPreferences prefs =
await SharedPreferences.getInstance();
String? user = prefs.getString("user");
if (user == null || user.isEmpty) {
user = "";
}
String? name = prefs.getString("name");
if (name == null || name.isEmpty) {
name = "";
}
Map data = {"user": user, "name": name};
return data;
}),
builder: (context, snapshot) {
if (snapshot.hasData) {
String url =
"$avatarUrl/${(snapshot.data! as Map)['user']}";
return Container(
width: 120.0,
height: 120.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
fit: BoxFit.scaleDown,
image: NetworkImage(url))));
} else {
return const CircularProgressIndicator();
}
},
),
readOnly: true,
controller:
TextEditingController(text: snapshot.data as String),
);
} else {
return (const Center(child: CircularProgressIndicator()));
}
}),
Padding(
padding: const EdgeInsets.fromLTRB(5, 20, 5, 5),
child: buildClassesChooser()),
ListTile(
leading: const Icon(MdiIcons.accountSettingsOutline),
trailing: const Icon(Icons.link, size: 16),
title: const Text("Account-Konsole"),
subtitle: const Text("Konto-Einstellungen öffnen"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AccountConsole()),
);
},
),
],
)
);
),
Padding(
padding: const EdgeInsets.fromLTRB(5, 5, 5, 5),
child: FutureBuilder(
future: getSettingsString("name"),
builder: (context, snapshot) {
if (snapshot.hasData) {
return TextField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Name',
icon: Icon(MdiIcons.passport)),
readOnly: true,
controller: TextEditingController(
text: snapshot.data as String),
);
} else {
return (const Center(
child: CircularProgressIndicator()));
}
}),
),
const Divider(),
Padding(
padding: const EdgeInsets.fromLTRB(5, 5, 5, 5),
child: FutureBuilder(
future: getSettingsString("user"),
builder: (context, snapshot) {
if (snapshot.hasData) {
return TextField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Benutzername',
icon: Icon(MdiIcons.identifier)),
readOnly: true,
controller: TextEditingController(
text: snapshot.data as String),
);
} else {
return (const Center(
child: CircularProgressIndicator()));
}
}),
),
const Divider(),
Padding(
padding: const EdgeInsets.fromLTRB(5, 5, 5, 5),
child: FutureBuilder(
future: getSettingsString("email"),
builder: (context, snapshot) {
if (snapshot.hasData) {
return TextField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'E-Mail-Adresse',
icon: Icon(Icons.email_outlined)),
readOnly: true,
controller: TextEditingController(
text: snapshot.data as String),
);
} else {
return (const Center(
child: CircularProgressIndicator()));
}
}),
),
Padding(
padding: const EdgeInsets.fromLTRB(5, 20, 5, 5),
child: buildClassesChooser()),
ListTile(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0)),
leading: const Icon(MdiIcons.accountSettingsOutline),
trailing: const Icon(Icons.link, size: 16),
title: const Text("Account-Konsole"),
subtitle: const Text("Konto-Einstellungen öffnen"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const AccountConsole()),
);
},
),
],
)),
);
}));
}
}
class AccountConsole extends StatefulWidget {
const AccountConsole({Key? key}) : super(key: key);
@override
AccountConsoleState createState() => AccountConsoleState();
}
class AccountConsoleState extends State<AccountConsole> {
@override
void initState() {
super.initState();
// Enable virtual display.
if (Platform.isAndroid) WebView.platform = AndroidWebView();
}
@override
Widget build(BuildContext context) {
return const WebView(
initialUrl: 'https://mein.cantorgymnasium.de/auth/realms/GCG.MeinCantor/account/',
return WebViewX(
height: MediaQuery.of(context).size.height,
initialContent: 'https://mein.cantorgymnasium.de/auth/realms/GCG.MeinCantor/account/',
initialSourceType: SourceType.url,
javascriptMode: JavascriptMode.unrestricted,
width: MediaQuery.of(context).size.width
);
}
}

@ -1,13 +1,10 @@
import 'package:meincantor/Settings/Pages/appearance_settings.dart';
import 'package:meincantor/Settings/Pages/service_settings.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'Pages/dev_settings.dart';
import 'Pages/info_settings.dart';
import 'Pages/plan_settings.dart';
import 'Pages/user_settings.dart';
import 'package:meincantor/Settings/Pages/dev_settings.dart';
import 'package:meincantor/Settings/Pages/info_settings.dart';
import 'package:meincantor/Settings/Pages/plan_settings.dart';
import 'package:meincantor/Settings/Pages/user_settings.dart';
class Settings extends StatelessWidget {
const Settings({Key? key}) : super(key: key);
@ -22,84 +19,100 @@ class Settings extends StatelessWidget {
body: ListView(
padding: const EdgeInsets.fromLTRB(5, 5, 5, 5),
children: [
ListTile(
leading: const Icon(MdiIcons.accountSettingsOutline,
color: Colors.cyan),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
title: const Text("Benutzer"),
subtitle: const Text("Profilbild, Klasse & mehr"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const UserSettings()),
);
},
Padding(
padding: const EdgeInsets.all(5),
child: ListTile(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0)),
leading: const Icon(MdiIcons.accountSettingsOutline,
color: Colors.cyan),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
title: const Text("Benutzer"),
subtitle: const Text("Profilbild, Klasse & mehr"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const UserSettings()),
);
},
),
),
ListTile(
leading:
const Icon(MdiIcons.timetable, color: Colors.orangeAccent),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
title: const Text("Plan"),
subtitle: const Text("Kurse/Fächer, Farben & mehr"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const PlanSettings()),
);
},
Padding(
padding: const EdgeInsets.all(5),
child: ListTile(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0)),
leading: const Icon(Icons.list_alt_outlined, color: Colors.red),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
title: const Text("Kurse"),
subtitle: const Text("Konfiguration der Kurse"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const WhitelistSettings()),
);
},
),
),
ListTile(
leading: const Icon(Icons.color_lens_outlined,
color: Colors.pinkAccent),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
title: const Text("Aussehen"),
subtitle: const Text("Widgets, Design & mehr"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const AppearanceSettings()),
);
},
Padding(
padding: const EdgeInsets.all(5),
child: ListTile(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0)),
leading:
const Icon(Icons.color_lens_outlined, color: Colors.teal),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
title: const Text("Farben"),
subtitle:
const Text("Konfiguration der Farben für die Plankacheln"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const PlanColorSettings()),
);
},
),
),
ListTile(
leading: const Icon(MdiIcons.server, color: Colors.lightGreen),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
title: const Text("Dienste"),
subtitle: const Text("Konten, Plattformen & mehr"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const ServiceSettings()),
);
},
Padding(
padding: const EdgeInsets.all(5),
child: ListTile(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0)),
leading: const Icon(Icons.developer_mode_outlined,
color: Colors.deepOrangeAccent),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
title: const Text("Entwickleroptionen"),
subtitle: const Text("API, Benutzerdaten & mehr"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DevSettings()),
);
},
),
),
ListTile(
leading: const Icon(Icons.developer_mode_outlined,
color: Colors.deepOrangeAccent),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
title: const Text("Entwickleroptionen"),
subtitle: const Text("API, Benutzerdaten & mehr"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const DevSettings()),
);
},
),
ListTile(
leading:
const Icon(Icons.info_outlined, color: Colors.greenAccent),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
title: const Text("Informationen"),
subtitle: const Text("Version, Lizenzen & mehr"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const InfoSettings()),
);
},
Padding(
padding: const EdgeInsets.all(5),
child: ListTile(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0)),
leading:
const Icon(Icons.info_outlined, color: Colors.greenAccent),
trailing: const Icon(Icons.arrow_forward_ios, size: 16),
title: const Text("Informationen"),
subtitle: const Text("Version, Lizenzen & mehr"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const InfoSettings()),
);
},
),
),
],
));

258
lib/background_fetch.dart Normal file

@ -0,0 +1,258 @@
import 'dart:convert';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:intl/intl.dart';
import 'package:meincantor/timetable.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
import 'package:meincantor/networking.dart';
Future<void> backgroundFetchTimetable() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? todayTimetable = prefs.getString("todayTimetable");
String? tomorrowTimetable = prefs.getString("tomorrowTimetable");
http.Response todayResponse = await fetchClassTimetable(
"/${DateFormat("yyyyMMdd").format(DateTime.now())}", null);
http.Response tomorrowResponse = await fetchClassTimetable(
"/${DateFormat("yyyyMMdd").format(DateTime.now().add(const Duration(days: 1)))}",
null);
if (todayResponse.statusCode == 200) {
if (todayTimetable != todayResponse.body) {
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
const AndroidNotificationDetails
androidPlatformChannelSpecificsTodayPlan = AndroidNotificationDetails(
'de.cantorgymnasium.meincantor.today.plan',
'Vertretungsplan für heute',
channelDescription: '',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker');
const NotificationDetails platformChannelSpecificsTodayPlan =
NotificationDetails(
android: androidPlatformChannelSpecificsTodayPlan);
const AndroidNotificationDetails
androidPlatformChannelSpecificsTodayInfo = AndroidNotificationDetails(
'de.cantorgymnasium.meincantor.today.info',
'Informationen für heute',
channelDescription: '',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker');
const NotificationDetails platformChannelSpecificsTodayInfo =
NotificationDetails(
android: androidPlatformChannelSpecificsTodayInfo);
List<TimetableLesson> lessonsList =
ClassTimetable.fromJson(jsonDecode(todayResponse.body)).timetable;
List<int> changedLessons = [];
for (var element in lessonsList) {
if (element.info.isNotEmpty) {
changedLessons.add(element.count);
}
}
String subtitle;
if (changedLessons.isNotEmpty && changedLessons.length > 1) {
subtitle = "Änderungen in den Stunden ";
for (var i in changedLessons) {
subtitle += "$i";
if (changedLessons.indexOf(i) != changedLessons.length - 1) {
subtitle += ", ";
}
}
} else if (changedLessons.isNotEmpty && changedLessons.length == 1) {
subtitle = "Änderungen in Stunde ${changedLessons[0]}";
} else {
subtitle = "Keine Änderungen im Plan gefunden!";
}
await flutterLocalNotificationsPlugin.show(
0,
'Neuer Vertretungsplan für heute geladen!',
subtitle,
platformChannelSpecificsTodayPlan,
payload: 'item x');
if ((jsonDecode(todayResponse.body)["info"] as String).isNotEmpty) {
await flutterLocalNotificationsPlugin.show(
1,
'Informationen für heute',
(jsonDecode(todayResponse.body)["info"] as String),
platformChannelSpecificsTodayInfo,
payload: 'item x');
}
prefs.setString("todayTimetable", todayResponse.body);
}
}
if (tomorrowResponse.statusCode == 200) {
if (tomorrowTimetable != tomorrowResponse.body) {
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
const AndroidNotificationDetails
androidPlatformChannelSpecificsTomorrowPlan =
AndroidNotificationDetails(
'de.cantorgymnasium.meincantor.tomorrow.plan',
'Vertretungsplan für morgen',
channelDescription: '',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker');
const NotificationDetails platformChannelSpecificsTomorrowPlan =
NotificationDetails(
android: androidPlatformChannelSpecificsTomorrowPlan);
const AndroidNotificationDetails
androidPlatformChannelSpecificsTomorrowInfo =
AndroidNotificationDetails(
'de.cantorgymnasium.meincantor.tomorrow.info',
'Informationen für morgen',
channelDescription: '',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker');
const NotificationDetails platformChannelSpecificsTomorrowInfo =
NotificationDetails(
android: androidPlatformChannelSpecificsTomorrowInfo);
List<TimetableLesson> lessonsList =
ClassTimetable.fromJson(jsonDecode(tomorrowResponse.body)).timetable;
List<int> changedLessons = [];
for (var element in lessonsList) {
if (element.info.isNotEmpty) {
changedLessons.add(element.count);
}
}
String subtitle;
if (changedLessons.isNotEmpty && changedLessons.length > 1) {
subtitle = "Änderungen in den Stunden ";
for (var i in changedLessons) {
subtitle += "$i";
if (changedLessons.indexOf(i) != changedLessons.length - 1) {
subtitle += ", ";
}
}
} else if (changedLessons.isNotEmpty && changedLessons.length == 1) {
subtitle = "Änderungen in Stunde ${changedLessons[0]}";
} else {
subtitle = "Keine Änderungen im Plan gefunden!";
}
await flutterLocalNotificationsPlugin.show(
2,
'Neuer Vertretungsplan für morgen geladen!',
subtitle,
platformChannelSpecificsTomorrowPlan,
payload: 'item x');
if ((jsonDecode(tomorrowResponse.body)["info"] as String).isNotEmpty) {
await flutterLocalNotificationsPlugin.show(
3,
'Informationen für morgen',
(jsonDecode(tomorrowResponse.body)["info"] as String),
platformChannelSpecificsTomorrowInfo,
payload: 'item x');
}
prefs.setString("tomorrowTimetable", tomorrowResponse.body);
}
}
}
Future<void> backgroundFetchArticles() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? articles = prefs.getString("articles");
String? news = prefs.getString("news");
http.Response fetchedArticles = await getArticles();
http.Response fetchedNews = await getNews();
if (fetchedArticles.statusCode == 200) {
if (articles != fetchedArticles.body) {
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
const AndroidNotificationDetails androidPlatformChannelSpecificsSZ =
AndroidNotificationDetails(
'de.cantorgymnasium.meincantor.sz', 'Schülerzeitung',
channelDescription: '',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker');
const NotificationDetails platformChannelSpecificsSZ =
NotificationDetails(android: androidPlatformChannelSpecificsSZ);
String subtitle;
List listFetchedArticles = jsonDecode(fetchedArticles.body)["data"];
if (articles != null && articles.isNotEmpty) {
List listSavedArticles = jsonDecode(articles)["data"];
int diff = listFetchedArticles.length - listSavedArticles.length;
diff == 1
? subtitle = "1 neuer Artikel!"
: diff > 1
? subtitle = "$diff neue Artikel!"
: subtitle = "Fehler beim Ermitteln der Änderungen!";
} else {
int len = listFetchedArticles.length;
len == 1
? subtitle = "1 neuer Artikel!"
: len > 1
? subtitle = "$len neue Artikel!"
: subtitle = "Fehler beim Ermitteln der Änderungen!";
}
await flutterLocalNotificationsPlugin.show(
4,
'Neuer Inhalt von der Schülerzeitung verfügbar!',
subtitle,
platformChannelSpecificsSZ,
payload: 'item x');
prefs.setString("articles", fetchedArticles.body);
}
}
if (fetchedNews.statusCode == 200) {
if (news != fetchedNews.body) {
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
const AndroidNotificationDetails androidPlatformChannelSpecificsNews =
AndroidNotificationDetails(
'de.cantorgymnasium.meincantor.news', 'Aktuelles',
channelDescription: '',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker');
const NotificationDetails platformChannelSpecificsNews =
NotificationDetails(android: androidPlatformChannelSpecificsNews);
String subtitle;
List listFetchedNews = jsonDecode(fetchedNews.body)["data"];
if (news != null && news.isNotEmpty) {
List listSavedNews = jsonDecode(news)["data"];
int diff = listFetchedNews.length - listSavedNews.length;
diff == 1
? subtitle = "1 neuer Artikel!"
: diff > 1
? subtitle = "$diff neue Artikel!"
: subtitle = "Fehler beim Ermitteln der Änderungen!";
} else {
int len = listFetchedNews.length;
len == 1
? subtitle = "1 neuer Artikel!"
: len > 1
? subtitle = "$len neue Artikel!"
: subtitle = "Fehler beim Ermitteln der Änderungen!";
}
await flutterLocalNotificationsPlugin.show(
5,
'Neue Informationen verfügbar!',
subtitle,
platformChannelSpecificsNews,
payload: 'item x');
prefs.setString("news", fetchedNews.body);
}
}
}

@ -3,10 +3,12 @@ import 'dart:convert';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:shared_preferences/shared_preferences.dart';
Future<String> getCachedTimetable(String ext) async {
Future<String> getCachedTimetable(String ext, String? presetClassNum) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String classNum;
if (prefs.getString('class_num') != null) {
if (presetClassNum != null) {
classNum = presetClassNum.replaceAll("/", "_");
} else if (prefs.getString('class_num') != null) {
classNum = prefs.getString('class_num')!.replaceAll("/", "_");
} else {
classNum = '05_1';

File diff suppressed because it is too large Load Diff

@ -3,9 +3,10 @@ import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:http/http.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'networking.dart';
import 'dashboard.dart';
import 'package:meincantor/networking.dart';
import 'package:meincantor/dashboard.dart';
Future<bool> checkKey() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
@ -14,6 +15,7 @@ Future<bool> checkKey() async {
}
class Login extends StatelessWidget {
Login({Key? key}) : super(key: key);
final userController = TextEditingController();
final passwordController = TextEditingController();
@ -39,9 +41,6 @@ class Login extends StatelessWidget {
}
return Scaffold(
appBar: AppBar(
title: const Text("Anmelden"),
),
body: Center(
child: SingleChildScrollView(
child: Padding(
@ -54,8 +53,14 @@ class Login extends StatelessWidget {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset("assets/images/meincantor_r.png",
height: 192, width: 192),
MediaQuery.of(context).platformBrightness ==
Brightness.light
? Image.asset(
"assets/images/meincantor-big.png",
width: 256)
: Image.asset(
"assets/images/meincantor-big-dark.png",
width: 256),
const Divider(),
AutofillGroup(
child: Column(
@ -65,7 +70,7 @@ class Login extends StatelessWidget {
AutofillHints.username
],
decoration: const InputDecoration(
icon: Icon(CupertinoIcons.person),
icon: Icon(MdiIcons.identifier),
border: OutlineInputBorder(),
labelText: 'Benutzername',
),
@ -77,7 +82,7 @@ class Login extends StatelessWidget {
AutofillHints.password
],
decoration: const InputDecoration(
icon: Icon(CupertinoIcons.lock),
icon: Icon(MdiIcons.lock),
border: OutlineInputBorder(),
labelText: 'Passwort',
),
@ -89,9 +94,9 @@ class Login extends StatelessWidget {
const Divider(),
TextField(
decoration: const InputDecoration(
icon: Icon(CupertinoIcons.lock),
icon: Icon(MdiIcons.twoFactorAuthentication),
border: OutlineInputBorder(),
labelText: '2F2-Code (OTP) [falls aktiviert]',
labelText: '2F2-Code',
),
obscureText: true,
controller: otpController,
@ -109,18 +114,21 @@ class Login extends StatelessWidget {
if (loginResponse.statusCode == 200) {
String apiKey = jsonDecode(utf8.decode(
loginResponse.bodyBytes))['token'];
await prefs.setString('api_key', apiKey);
prefs.setString('api_key', apiKey);
dynamic userinfo = jsonDecode(
await getUserInfo(
userController.text,
passwordController.text,
otpController.text,
devIdController.text));
await prefs.setString('user',
prefs.setString('user',
userinfo['preferred_username']);
await prefs.setString(
'name', userinfo['name']);
await prefs.setString(
prefs.setString('user',
userinfo['preferred_username']);
prefs.setString('name', userinfo['name']);
prefs.setString(
'email', userinfo['email']);
prefs.setString(
'class_num',
userinfo['groups'][0]
.replaceAll("_", "/"));

@ -1,32 +1,52 @@
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dashboard.dart';
import 'login.dart';
import 'package:meincantor/dashboard.dart';
import 'package:meincantor/login.dart';
import 'dart:math';
import 'package:background_fetch/background_fetch.dart';
void backgroundFetchHeadlessTask(HeadlessTask task) async {
String taskId = task.taskId;
bool isTimeout = task.timeout;
if (isTimeout) {
// This task has exceeded its allowed running-time.
// You must stop what you're doing and immediately .finish(taskId)
print("[BackgroundFetch] Headless task timed-out: $taskId");
BackgroundFetch.finish(taskId);
return;
}
print('[BackgroundFetch] Headless event received.');
// Do your work here...
BackgroundFetch.finish(taskId);
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(const App());
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
// initialise the plugin. app_icon needs to be a added as a drawable resource to the Android head project
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('app_icon');
final IOSInitializationSettings initializationSettingsIOS =
const IOSInitializationSettings initializationSettingsIOS =
IOSInitializationSettings();
final MacOSInitializationSettings initializationSettingsMacOS =
const MacOSInitializationSettings initializationSettingsMacOS =
MacOSInitializationSettings();
final InitializationSettings initializationSettings = InitializationSettings(
const InitializationSettings initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
macOS: initializationSettingsMacOS);
await flutterLocalNotificationsPlugin.initialize(initializationSettings);
runApp(const App());
BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask);
}
class App extends StatelessWidget {
class App extends StatefulWidget {
const App({Key? key}) : super(key: key);
@override
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
MaterialColor generateMaterialColor(Color color) {
return MaterialColor(color.value, {
50: tintColor(color, 0.5),

@ -1,10 +1,7 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:meincantor/cache_manager.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/painting.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
@ -16,14 +13,16 @@ import 'package:meincantor/login.dart';
import 'package:meincantor/main.dart';
Future<http.Response> getArticles() async {
var uri = Uri.https(szUrl["url"]!, "/articles");
final response = await http.get(uri);
var uri = Uri.https(szUrl["url"]! as String, "/api/articles");
final response =
await http.get(uri, headers: szUrl["headers"]! as Map<String, String>);
return (response);
}
Future<http.Response> getNews() async {
var uri = Uri.https(szUrl["url"]!, "/aktuelles");
final response = await http.get(uri);
var uri = Uri.https(szUrl["url"]! as String, "/api/aktuelles");
final response =
await http.get(uri, headers: szUrl["headers"]! as Map<String, String>);
return (response);
}
@ -50,21 +49,9 @@ Future<String> getUserInfo(
}
}
Future<http.Response> fetchClassTimetable(String ext) async {
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
const AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails('de.cantorgymnasium.meincantor', 'GCG.MeinCantor',
channelDescription: '',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker');
const NotificationDetails platformChannelSpecifics =
NotificationDetails(android: androidPlatformChannelSpecifics);
await flutterLocalNotificationsPlugin.show(
0, 'Neuer Vertretungsplan geladen!', 'Du hast folgende Vertretungen:\nSt. 8 Deutsch Frau Rinke, Raum 203\nSt. 4 Biologie Frau Borchert, Raum 107', platformChannelSpecifics,
payload: 'item x');
Future<http.Response> fetchClassTimetable(String ext, String? classNum) async {
try {
return (http.Response(await getCachedTimetable(ext), 200));
return (http.Response(await getCachedTimetable(ext, classNum), 200));
} on HttpExceptionWithStatus catch (e) {
return http.Response(e.message, e.statusCode);
} on HttpException catch (e) {
@ -72,20 +59,6 @@ Future<http.Response> fetchClassTimetable(String ext) async {
} on SocketException catch (e) {
return http.Response(e.message, 404);
}
/*SharedPreferences prefs = await SharedPreferences.getInstance();
String classNum;
if (prefs.getString('class_num') != null) {
classNum = prefs.getString('class_num')!.replaceAll("/", "_");
} else {
classNum = '05_1';
}
var apiKey = prefs.getString('api_key');
var uri = Uri.https("mein.cantorgymnasium.de", "/api/timetable/$classNum$ext");
var headers = {"x-api-key": "$apiKey"};
final response = http.get(uri, headers: headers).onError((error, stackTrace) {
return (http.Response("", 404));
});
return response;*/
}
fetchLessonList() async {
@ -180,7 +153,7 @@ Widget buildTimetable(Future<http.Response> future, String info) {
Widget buildTodayClassTimetableLesson(int count) {
return FutureBuilder<http.Response>(
future: fetchClassTimetable(
"/${DateFormat("yyyyMMdd").format(DateTime.now())}"),
"/${DateFormat("yyyyMMdd").format(DateTime.now())}", null),
builder: (context, snapshot) {
if (snapshot.hasData) {
int statusCode = snapshot.data!.statusCode;
@ -281,7 +254,7 @@ Widget buildClassesChooser() {
if (statusCode == 200) {
List<String> items = [];
jsonDecode(utf8.decode(snapshot.data!.bodyBytes)).forEach((value) {
items.add(value..toString());
items.add(value.toString());
});
return ClassesChooser(items: items);
} else if (statusCode == 400) {
@ -322,7 +295,6 @@ class ClassesChooser extends StatefulWidget {
class _ClassesChooserState extends State<ClassesChooser> {
final List<String> items;
//final String dropdownValue;
String? dropdownValue;
_ClassesChooserState(this.items);
@ -348,12 +320,15 @@ class _ClassesChooserState extends State<ClassesChooser> {
border: OutlineInputBorder(),
labelText: 'Klasse (05/1, 07/3, 10/2...)',
),
onChanged: (String? newValue) {
setState(() async {
dropdownValue = newValue!;
SharedPreferences prefs = await SharedPreferences.getInstance();
String classNum = newValue;
await prefs.setString('class_num', classNum);
onChanged: (String? newValue) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String classNum = newValue!;
prefs.setString('class_num', classNum);
prefs.remove("lessons");
prefs.remove("todayTImetable");
prefs.remove("tomorrowTimetable");
setState(() {
dropdownValue = newValue;
});
},
items: items.map<DropdownMenuItem<String>>((String value) {

@ -30,162 +30,197 @@ class _NewsState extends State<News> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Aktuelles"),
centerTitle: true,
),
body: FutureBuilder<http.Response>(
future: getNews(),
builder: (context, snapshot) {
if (snapshot.hasData) {
int statusCode = snapshot.data!.statusCode;
if (statusCode == 200) {
String data = utf8.decode(snapshot.data!.bodyBytes);
List articles = jsonDecode(data);
List<Widget> articleTiles = [];
for (var element in articles) {
Color color = Colors.white70;
Widget card = FutureBuilder(
future: getNewsRead(),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<dynamic> readList = snapshot.data! as List<dynamic>;
if (!readList.contains(element["id"])) {
return GestureDetector(
onTap: () async {
SharedPreferences prefs =
await SharedPreferences.getInstance();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Article.fromData(
element["title"],
element["content"],
element["author"],
element["published_at"])
.widget),
);
readList.add(element["id"]);
prefs.setString("newsRead", jsonEncode(readList));
setState(() {
color = Colors.transparent;
});
},
child: Card(
color: color,
child: Padding(
padding:
const EdgeInsets.fromLTRB(10, 10, 10, 10),
child: FutureBuilder(
future: Future.delayed(
const Duration(seconds: 0)),
builder: (context, snapshot) {
if (element["summary"] != null &&
(element["summary"] as String)
.isNotEmpty) {
return ListTile(
title: Text(element["title"],
style: const TextStyle(
fontWeight: FontWeight.bold)),
subtitle: Text(
element["summary"],
overflow: TextOverflow.ellipsis,
maxLines: 2,
softWrap: true,
),
trailing: Text(
"${DateTime.parse(element["published_at"]).day.toString()}.${DateTime.parse(element["published_at"]).month.toString()}.${DateTime.parse(element["published_at"]).year.toString()}"),
);
} else {
return ListTile(
title: Text(element["title"],
style: const TextStyle(
fontWeight: FontWeight.bold)),
trailing: Text(
"${DateTime.parse(element["published_at"]).day.toString()}.${DateTime.parse(element["published_at"]).month.toString()}.${DateTime.parse(element["published_at"]).year.toString()}"),
);
}
},
)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
)),
);
} else {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Article.fromData(
element["title"],
element["content"],
element["author"],
element["published_at"])
.widget),
);
},
child: Card(
child: Padding(
padding:
const EdgeInsets.fromLTRB(10, 10, 10, 10),
child: FutureBuilder(
future: Future.delayed(
const Duration(seconds: 0)),
builder: (context, snapshot) {
if (element["summary"] != null &&
(element["summary"] as String)
.isNotEmpty) {
return ListTile(
title: Text(element["title"],
style: const TextStyle(
fontWeight: FontWeight.bold)),
subtitle: Text(
element["summary"],
overflow: TextOverflow.ellipsis,
maxLines: 2,
softWrap: true,
),
trailing: Text(
"${DateTime.parse(element["published_at"]).day.toString()}.${DateTime.parse(element["published_at"]).month.toString()}.${DateTime.parse(element["published_at"]).year.toString()}"),
);
} else {
return ListTile(
title: Text(element["title"],
style: const TextStyle(
fontWeight: FontWeight.bold)
),
trailing: Text(
"${DateTime.parse(element["published_at"]).day.toString()}.${DateTime.parse(element["published_at"]).month.toString()}.${DateTime.parse(element["published_at"]).year.toString()}"),
);
}
},
)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
)),
);
}
} else {
return const LinearProgressIndicator();
}
},
);
articleTiles.add(card);
}
return ListView(
children: articleTiles.reversed.toList(),
);
} else {
return (const Center(
child: Text("Uups... Irgendwas ist schief gelaufen")));
}
appBar: AppBar(
title: const Text("Aktuelles"),
centerTitle: true,
),
body: LayoutBuilder(builder: (context, constraints) {
double widgetWidth = constraints.maxWidth;
int factor;
if (widgetWidth <= 600) {
factor = 1;
} else if (widgetWidth <= 1400) {
factor = 2;
} else if (widgetWidth <= 2000) {
factor = 3;
} else {
return (const Center(child: CircularProgressIndicator()));
factor = 1;
}
},
),
);
return Center(
heightFactor: 1,
child: Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width / factor,
),
child: FutureBuilder<http.Response>(
future: getNews(),
builder: (context, snapshot) {
if (snapshot.hasData) {
int statusCode = snapshot.data!.statusCode;
if (statusCode == 200) {
String data = utf8.decode(snapshot.data!.bodyBytes);
List articles = jsonDecode(data)["data"];
List<Widget> articleTiles = [];
for (var element in articles) {
int id = element["id"];
element = element["attributes"];
Color color = Colors.white70;
Widget card = FutureBuilder(
future: getNewsRead(),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<dynamic> readList =
snapshot.data! as List<dynamic>;
if (!readList.contains(id)) {
return GestureDetector(
onTap: () async {
SharedPreferences prefs =
await SharedPreferences.getInstance();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
Article.fromData(
element["title"],
element["content"],
element["author"],
element["publish_date"])
.widget),
);
readList.add(id);
prefs.setString(
"newsRead", jsonEncode(readList));
setState(() {
color = Colors.transparent;
});
},
child: Card(
color: color,
child: Padding(
padding: const EdgeInsets.fromLTRB(
10, 10, 10, 10),
child: FutureBuilder(
future: Future.delayed(
const Duration(seconds: 0)),
builder: (context, snapshot) {
if (element["summary"] != null &&
(element["summary"] as String)
.isNotEmpty) {
return ListTile(
title: Text(element["title"],
style: const TextStyle(
fontWeight:
FontWeight.bold)),
subtitle: Text(
element["summary"],
overflow:
TextOverflow.ellipsis,
maxLines: 2,
softWrap: true,
),
trailing: Text(
"${DateTime.parse(element["publish_date"]).day.toString()}.${DateTime.parse(element["publish_date"]).month.toString()}.${DateTime.parse(element["publish_date"]).year.toString()}"),
);
} else {
return ListTile(
title: Text(element["title"],
style: const TextStyle(
fontWeight:
FontWeight.bold)),
trailing: Text(
"${DateTime.parse(element["publish_date"]).day.toString()}.${DateTime.parse(element["publish_date"]).month.toString()}.${DateTime.parse(element["publish_date"]).year.toString()}"),
);
}
},
)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
)),
);
} else {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
Article.fromData(
element["title"],
element["content"],
element["author"],
element["publish_date"])
.widget),
);
},
child: Card(
child: Padding(
padding: const EdgeInsets.fromLTRB(
10, 10, 10, 10),
child: FutureBuilder(
future: Future.delayed(
const Duration(seconds: 0)),
builder: (context, snapshot) {
if (element["summary"] != null &&
(element["summary"] as String)
.isNotEmpty) {
return ListTile(
title: Text(element["title"],
style: const TextStyle(
fontWeight:
FontWeight.bold)),
subtitle: Text(
element["summary"],
overflow:
TextOverflow.ellipsis,
maxLines: 2,
softWrap: true,
),
trailing: Text(
"${DateTime.parse(element["publish_date"]).day.toString()}.${DateTime.parse(element["publish_date"]).month.toString()}.${DateTime.parse(element["publish_date"]).year.toString()}"),
);
} else {
return ListTile(
title: Text(element["title"],
style: const TextStyle(
fontWeight:
FontWeight.bold)),
trailing: Text(
"${DateTime.parse(element["publish_date"]).day.toString()}.${DateTime.parse(element["publish_date"]).month.toString()}.${DateTime.parse(element["publish_date"]).year.toString()}"),
);
}
},
)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
)),
);
}
} else {
return const LinearProgressIndicator();
}
},
);
articleTiles.add(card);
}
return ListView(
children: articleTiles.reversed.toList(),
);
} else {
return (const Center(
child:
Text("Uups... Irgendwas ist schief gelaufen")));
}
} else {
return (const Center(child: CircularProgressIndicator()));
}
},
),
),
);
}));
}
}

@ -1 +0,0 @@

@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
Map colors = {
'Bio': Colors.green,
'bio1': Colors.green,
'bioL1': Colors.green,
'Mat': Colors.indigo,
'matL1': Colors.indigo,
'matL2': Colors.indigo,
@ -10,20 +12,26 @@ Map colors = {
'deu1': Colors.red,
'deu2': Colors.red,
'deu3': Colors.red,
'deuL1': Colors.red,
'Kun': Colors.deepPurple,
'kun1': Colors.deepPurple,
'kun2': Colors.deepPurple,
'kun3': Colors.deepPurple,
'Geo': Colors.brown,
'geo1': Colors.brown,
'Lat': Colors.teal,
'lat1': Colors.teal,
'lat2': Colors.teal,
'lat3': Colors.teal,
'Che': Colors.lightGreen,
'che1': Colors.lightGreen,
'cheL1': Colors.lightGreen,
'Eng': Colors.amber,
'eng1': Colors.amber,
'eng2': Colors.amber,
'eng3': Colors.amber,
'engL1': Colors.amber,
'engL2': Colors.amber,
'Phy': Colors.cyan,
'phy1': Colors.cyan,
'phy2': Colors.cyan,

@ -1,30 +1,63 @@
dynamic subjects = {
'---': '---',
'Bio': 'Biologie',
'bio1': 'Biologie 1',
'bioL1': 'Biologie Leistungskurs 1',
'Mat': 'Mathematik',
'matL1': 'Mathematik Leistungskurs 1',
'matL2': 'Mathematik Leistungskurs 2',
'matL3': 'Mathematik Leistungskurs 3',
'Kun': 'Kunst',
'kun1': 'Kunst 1',
'Mus': 'Musik',
'mus1': 'Musik 1',
'mus2': 'Musik 2',
'Geo': 'Geographie',
'geo1': 'Geographie 1',
'Ges': 'Geschichte',
'ges1': 'Geschichte 1',
'ges2': 'Geschichte 2',
'ges3': 'Geschichte 3',
'Che': 'Chemie',
'che1': 'Chemie 1',
'cheL1': 'Chemie Leistungskurs 1',
'Lat': 'Latein',
'lat1': 'Latein 1',
'Inf': 'Informatik',
'inf1': 'Informatik 1',
'inf2': 'Informatik 2',
'infL1': 'Informatik Leistungskurs 1',
'Eng': 'Englisch',
'eng1': 'Englisch 1',
'eng2': 'Englisch 2',
'engL1': 'Englisch Leistungskurs 1',
'engL2': 'Englisch Leistungskurs 2',
'Frz': 'Französisch',
'frz1': 'Französisch 1',
'frz2': 'Französisch 2',
'frz3': 'Französisch 3',
'Phy': 'Physik',
'phy1': 'Physik 1',
'phyL1': 'Physik Leistungskurs 1',
'phyL2': 'Physik Leistungskurs 2',
'psy1': 'Psychologie 1',
'Spo': 'Sport',
'spo1': 'Sport 1',
'spo2': 'Sport 2',
'spo3': 'Sport 3',
'Deu': 'Deutsch',
'deu1': 'Deutsch 1',
'deu2': 'Deutsch 2',
'deu3': 'Deutsch 3',
'deuL1': 'Deutsch Leistungskurs 1',
'Lme': 'Lernmethoden',
'Eth': 'Ethik',
'eth1': 'Ethik 1',
'EvR': 'Evangelische Religion',
'evr1': 'Evangelische Religion 1',
'Soz': 'Sozialkunde',
'soz1': 'Sozialkunde 1',
'soz2': 'Sozialkunde 2',
'Ast': 'Astronomie',
'Spa': 'Spanisch',
'FK': 'Fachkurs',

@ -1,4 +1,3 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class RoomOverview extends StatelessWidget {

198
lib/saved.dart Normal file

@ -0,0 +1,198 @@
// SizedBox(
// width: 170,
// child: GestureDetector(
// onTap: () async {
// Navigator.push(
// context,
// MaterialPageRoute(builder: (context) => const SB()),
// );
// },
// child: Card(
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(10),
// ),
// child: const Padding(
// padding: EdgeInsets.all(10),
// child: ListTile(
// title: Padding(
// padding: EdgeInsets.fromLTRB(0, 0, 0, 10),
// child: Icon(
// MdiIcons.libraryShelves,
// color: Palette.accent,
// size: 48,
// ),
// ),
// subtitle: Center(
// child: Padding(
// padding: EdgeInsets.fromLTRB(0, 10, 0, 0),
// child: Text(
// 'Schulbibliothek',
// ),
// ),
// ),
// ),
// )),
// ),
// ),
// SizedBox(
// width: 170,
// child: GestureDetector(
// onTap: () async {
// Navigator.push(
// context,
// MaterialPageRoute(builder: (context) => const SC()),
// );
// },
// child: Card(
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(10),
// ),
// child: const Padding(
// padding: EdgeInsets.all(10),
// child: ListTile(
// title: Padding(
// padding: EdgeInsets.fromLTRB(0, 0, 0, 10),
// child: Icon(
// MdiIcons.laptop,
// color: Palette.accent,
// size: 48,
// ),
// ),
// subtitle: Center(
// child: Padding(
// padding: EdgeInsets.fromLTRB(0, 10, 0, 0),
// child: Text(
// 'Schulcomputer',
// ),
// ),
// ),
// ),
// )),
// ),
// ),
// SizedBox(
// width: 170,
// child: GestureDetector(
// onTap: () async {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => const RoomOverview()),
// );
// },
// child: Card(
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(10),
// ),
// child: const Padding(
// padding: EdgeInsets.all(10),
// child: ListTile(
// title: Padding(
// padding: EdgeInsets.fromLTRB(0, 0, 0, 10),
// child: Icon(
// MdiIcons.door,
// color: Palette.accent,
// size: 48,
// ),
// ),
// subtitle: Center(
// child: Padding(
// padding: EdgeInsets.fromLTRB(0, 10, 0, 0),
// child: Text(
// 'Raumübersicht',
// ),
// ),
// ),
// ),
// )),
// ),
// ),
// ListTile(
// leading:
// const Icon(MdiIcons.timetable, color: Colors.orangeAccent),
// trailing: const Icon(Icons.arrow_forward_ios, size: 16),
// title: const Text("Plan"),
// subtitle: const Text("Kurse/Fächer, Farben & mehr"),
// onTap: () {
// Navigator.push(
// context,
// MaterialPageRoute(builder: (context) => const PlanSettings()),
// );
// },
// ),
// ListTile(
// leading: const Icon(Icons.color_lens_outlined,
// color: Colors.pinkAccent),
// trailing: const Icon(Icons.arrow_forward_ios, size: 16),
// title: const Text("Aussehen"),
// subtitle: const Text("Widgets, Design & mehr"),
// onTap: () {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => const AppearanceSettings()),
// );
// },
// ),
// ListTile(
// leading: const Icon(MdiIcons.server, color: Colors.lightGreen),
// trailing: const Icon(Icons.arrow_forward_ios, size: 16),
// title: const Text("Dienste"),
// subtitle: const Text("Konten, Plattformen & mehr"),
// onTap: () {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => const ServiceSettings()),
// );
// },
// ),
// class PlanSettings extends StatelessWidget {
// const PlanSettings({Key? key}) : super(key: key);
// @override
// Widget build(BuildContext context) {
// return Scaffold(
// appBar: AppBar(
// title: const Text("Plan"),
// centerTitle: true,
// ),
// body: ListView(
// padding: const EdgeInsets.fromLTRB(5, 5, 5, 5),
// children: [
// ListTile(
// leading: const Icon(Icons.list_alt_outlined, color: Colors.red),
// trailing: const Icon(Icons.arrow_forward_ios, size: 16),
// title: const Text("Kurse"),
// subtitle: const Text("Konfiguration der Kurse"),
// onTap: () {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => const WhitelistSettings()),
// );
// },
// ),
// ListTile(
// leading:
// const Icon(Icons.color_lens_outlined, color: Colors.teal),
// trailing: const Icon(Icons.arrow_forward_ios, size: 16),
// title: const Text("Farben"),
// subtitle:
// const Text("Konfiguration der Farben für die Plankacheln"),
// onTap: () {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => const PlanColorSettings()),
// );
// },
// ),
// ],
// ));
// }
// }

@ -5,6 +5,8 @@ import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:http/http.dart' as http;
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:markdown/markdown.dart' as md;
Future<List> getSZread() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
@ -30,161 +32,197 @@ class _SZState extends State<SZ> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Schülerzeitung"),
centerTitle: true,
),
body: FutureBuilder<http.Response>(
future: getArticles(),
builder: (context, snapshot) {
if (snapshot.hasData) {
int statusCode = snapshot.data!.statusCode;
if (statusCode == 200) {
String data = utf8.decode(snapshot.data!.bodyBytes);
List articles = jsonDecode(data);
List<Widget> articleTiles = [];
for (var element in articles) {
Color color = Colors.white70;
Widget card = FutureBuilder(
future: getSZread(),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<dynamic> readList = snapshot.data! as List<dynamic>;
if (!readList.contains(element["id"])) {
return GestureDetector(
onTap: () async {
SharedPreferences prefs =
await SharedPreferences.getInstance();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Article.fromData(
element["title"],
element["content"],
element["author"],
element["published_at"])
.widget),
);
readList.add(element["id"]);
prefs.setString("SZread", jsonEncode(readList));
setState(() {
color = Colors.transparent;
});
},
child: Card(
color: color,
child: Padding(
padding:
const EdgeInsets.fromLTRB(10, 10, 10, 10),
child: FutureBuilder(
future: Future.delayed(
const Duration(seconds: 0)),
builder: (context, snapshot) {
if (element["summary"] != null &&
(element["summary"] as String)
.isNotEmpty) {
return ListTile(
title: Text(element["title"],
style: const TextStyle(
fontWeight: FontWeight.bold)),
subtitle: Text(
element["summary"],
overflow: TextOverflow.ellipsis,
maxLines: 2,
softWrap: true,
),
trailing: Text(
"${DateTime.parse(element["published_at"]).day.toString()}.${DateTime.parse(element["published_at"]).month.toString()}.${DateTime.parse(element["published_at"]).year.toString()}"),
);
} else {
return ListTile(
title: Text(element["title"],
style: const TextStyle(
fontWeight: FontWeight.bold)),
trailing: Text(
"${DateTime.parse(element["published_at"]).day.toString()}.${DateTime.parse(element["published_at"]).month.toString()}.${DateTime.parse(element["published_at"]).year.toString()}"),
);
}
},
)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
)),
);
} else {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Article.fromData(
element["title"],
element["content"],
element["author"],
element["published_at"])
.widget),
);
},
child: Card(
child: Padding(
padding:
const EdgeInsets.fromLTRB(10, 10, 10, 10),
child: FutureBuilder(
future: Future.delayed(
const Duration(seconds: 0)),
builder: (context, snapshot) {
if (element["summary"] != null &&
(element["summary"] as String)
.isNotEmpty) {
return ListTile(
title: Text(element["title"],
style: const TextStyle(
fontWeight: FontWeight.bold)),
subtitle: Text(
element["summary"],
overflow: TextOverflow.ellipsis,
maxLines: 2,
softWrap: true,
),
trailing: Text(
"${DateTime.parse(element["published_at"]).day.toString()}.${DateTime.parse(element["published_at"]).month.toString()}.${DateTime.parse(element["published_at"]).year.toString()}"),
);
} else {
return ListTile(
title: Text(element["title"],
style: const TextStyle(
fontWeight: FontWeight.bold)),
trailing: Text(
"${DateTime.parse(element["published_at"]).day.toString()}.${DateTime.parse(element["published_at"]).month.toString()}.${DateTime.parse(element["published_at"]).year.toString()}"),
);
}
},
)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
)),
);
}
} else {
return const LinearProgressIndicator();
}
},
);
articleTiles.add(card);
}
return ListView(
children: articleTiles.reversed.toList(),
);
} else {
return (const Center(
child: Text("Uups... Irgendwas ist schief gelaufen")));
}
appBar: AppBar(
title: const Text("Schülerzeitung"),
centerTitle: true,
),
body: LayoutBuilder(builder: (context, constraints) {
double widgetWidth = constraints.maxWidth;
int factor;
if (widgetWidth <= 600) {
factor = 1;
} else if (widgetWidth <= 1400) {
factor = 2;
} else if (widgetWidth <= 2000) {
factor = 3;
} else {
return (const Center(child: CircularProgressIndicator()));
factor = 1;
}
},
),
);
return Center(
heightFactor: 1,
child: Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width / factor,
),
child: FutureBuilder<http.Response>(
future: getArticles(),
builder: (context, snapshot) {
if (snapshot.hasData) {
int statusCode = snapshot.data!.statusCode;
if (statusCode == 200) {
String data = utf8.decode(snapshot.data!.bodyBytes);
List articles = jsonDecode(data)["data"];
List<Widget> articleTiles = [];
for (var element in articles) {
int id = element["id"];
element = element["attributes"];
Color color = Colors.white70;
Widget card = FutureBuilder(
future: getSZread(),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<dynamic> readList =
snapshot.data! as List<dynamic>;
if (!readList.contains(id)) {
return GestureDetector(
onTap: () async {
SharedPreferences prefs =
await SharedPreferences.getInstance();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
Article.fromData(
element["title"],
element["content"],
element["author"],
element["publish_date"])
.widget),
);
readList.add(id);
prefs.setString(
"SZread", jsonEncode(readList));
setState(() {
color = Colors.transparent;
});
},
child: Card(
color: color,
child: Padding(
padding: const EdgeInsets.fromLTRB(
10, 10, 10, 10),
child: FutureBuilder(
future: Future.delayed(
const Duration(seconds: 0)),
builder: (context, snapshot) {
if (element["summary"] != null &&
(element["summary"] as String)
.isNotEmpty) {
return ListTile(
title: Text(element["title"],
style: const TextStyle(
fontWeight:
FontWeight.bold)),
subtitle: Text(
element["summary"],
overflow:
TextOverflow.ellipsis,
maxLines: 2,
softWrap: true,
),
trailing: Text(
"${DateTime.parse(element["publish_date"]).day.toString()}.${DateTime.parse(element["publish_date"]).month.toString()}.${DateTime.parse(element["publish_date"]).year.toString()}"),
);
} else {
return ListTile(
title: Text(element["title"],
style: const TextStyle(
fontWeight:
FontWeight.bold)),
trailing: Text(
"${DateTime.parse(element["publish_date"]).day.toString()}.${DateTime.parse(element["publish_date"]).month.toString()}.${DateTime.parse(element["publish_date"]).year.toString()}"),
);
}
},
)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
)),
);
} else {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
Article.fromData(
element["title"],
element["content"],
element["author"],
element["publish_date"])
.widget),
);
},
child: Card(
child: Padding(
padding: const EdgeInsets.fromLTRB(
10, 10, 10, 10),
child: FutureBuilder(
future: Future.delayed(
const Duration(seconds: 0)),
builder: (context, snapshot) {
if (element["summary"] != null &&
(element["summary"] as String)
.isNotEmpty) {
return ListTile(
title: Text(element["title"],
style: const TextStyle(
fontWeight:
FontWeight.bold)),
subtitle: Text(
element["summary"],
overflow:
TextOverflow.ellipsis,
maxLines: 2,
softWrap: true,
),
trailing: Text(
"${DateTime.parse(element["publish_date"]).day.toString()}.${DateTime.parse(element["publish_date"]).month.toString()}.${DateTime.parse(element["publish_date"]).year.toString()}"),
);
} else {
return ListTile(
title: Text(element["title"],
style: const TextStyle(
fontWeight:
FontWeight.bold)),
trailing: Text(
"${DateTime.parse(element["publish_date"]).day.toString()}.${DateTime.parse(element["publish_date"]).month.toString()}.${DateTime.parse(element["publish_date"]).year.toString()}"),
);
}
},
)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
)),
);
}
} else {
return const LinearProgressIndicator();
}
},
);
articleTiles.add(card);
}
return ListView(
children: articleTiles.reversed.toList(),
);
} else {
return (const Center(
child:
Text("Uups... Irgendwas ist schief gelaufen")));
}
} else {
return (const Center(child: CircularProgressIndicator()));
}
},
),
),
);
}));
}
}
@ -216,7 +254,14 @@ class Article {
title: Text(
"${DateTime.parse(publishDate).day.toString()}.${DateTime.parse(publishDate).month.toString()}.${DateTime.parse(publishDate).year.toString()}"),
),
MarkdownBody(data: content)
MarkdownBody(
data: content,
onTapLink: (text, url, title) {
launch(url!);
},
extensionSet: md.ExtensionSet.commonMark,
imageDirectory: "https://cms.mein.cantorgymnasium.de",
)
],
)));
}

@ -1,4 +1,3 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class SB extends StatelessWidget {

@ -1,4 +1,3 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class SC extends StatelessWidget {

@ -3,7 +3,6 @@ import 'package:meincantor/presets/subjects.dart';
import 'package:meincantor/presets/colors.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart';
@ -25,8 +24,10 @@ class ClassTimetableBuilder {
onTap: () {
showModalBottomSheet<void>(
isScrollControlled: true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25.0),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(25.0),
topRight: Radius.circular(25.0)),
),
context: context,
builder: (BuildContext context) {
@ -35,6 +36,8 @@ class ClassTimetableBuilder {
child: ListView(
children: <Widget>[
ListTile(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25.0)),
title: const Text("Informationen",
style: TextStyle(fontWeight: FontWeight.bold)),
leading: const Icon(Icons.arrow_back),
@ -90,15 +93,15 @@ class LessonsListBuilder {
title: Text(element.count.toString() + '.' + ' ' + element.name,
style: TextStyle(color: element.fontColor)),
subtitle: Row(children: [
Icon(CupertinoIcons.person, color: element.fontColor),
Icon(Icons.person_outline, color: element.fontColor),
const SizedBox(width: 5),
Text(element.teacher, style: TextStyle(color: element.fontColor)),
const Spacer(),
Icon(CupertinoIcons.home, color: element.fontColor),
Icon(MdiIcons.door, color: element.fontColor),
const SizedBox(width: 5),
Text(element.room, style: TextStyle(color: element.fontColor))
]),
leading: Icon(CupertinoIcons.time, color: element.fontColor)));
leading: Icon(MdiIcons.clockOutline, color: element.fontColor)));
if (element.info != '') {
cardChildren.add(ListTile(
title: Text(element.info,
@ -150,7 +153,7 @@ class TimetableInfo {
}
class ClassTimetable {
final List timetable;
final List<TimetableLesson> timetable;
ClassTimetable({required this.timetable});
factory ClassTimetable.fromJson(Map<String, dynamic> json) {
List<TimetableLesson> lessons = [];
@ -201,11 +204,10 @@ class ClassTimetable {
lessons.add(TimetableLesson(
value['St'],
value["Nr"],
subjects[subject] ?? subject.toString(),
value["Nr"] ?? 0,
subject == '&nbsp;' ? "---" : subjects[subject] ?? subject.toString(),
teachers[teacher] ?? teacher.toString(),
room.toString(),
value['If'].toString(),
lessonColor,
fontColor,
info));
@ -221,10 +223,9 @@ class TimetableLesson {
final String name;
final String teacher;
final String room;
final String comment;
final Future<Color> color;
final Color fontColor;
final String info;
const TimetableLesson(this.count, this.id, this.name, this.teacher, this.room,
this.comment, this.color, this.fontColor, this.info);
this.color, this.fontColor, this.info);
}

@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 0.8.0-dev
version: 1.0.0
environment:
sdk: ">=2.12.0 <3.0.0"
@ -31,7 +31,7 @@ dependencies:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
# cupertino_icons: ^1.0.2
shared_preferences: ^2.0.6
http: ^0.13.3
google_fonts: ^2.1.0
@ -45,7 +45,7 @@ dependencies:
url_launcher: ^6.0.17
flutter_linkify: ^5.0.2
flutter_svg: ^1.0.0
webview_flutter: ^3.0.0
webviewx: ^0.2.1
flutter_local_notifications: ^10.0.0-dev.1
background_fetch: ^1.0.3
@ -83,6 +83,8 @@ flutter:
# To add assets to your application, add an assets section, like this:
assets:
- assets/images/meincantor_r.png
- assets/images/meincantor-big.png
- assets/images/meincantor-big-dark.png
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see