1
0

Compare commits

..

145 Commits

Author SHA1 Message Date
ff1e794820 Merge branch 'release/0.13.0' 2019-12-07 23:04:26 +01:00
ceba5f7fe8 Version 0.13.0 2019-12-07 23:04:17 +01:00
f570acbed6 Add FAQ link (#611) 2019-12-07 22:06:15 +01:00
2a7a472d90 Bump fragment-ktx from 1.2.0-rc02 to 1.2.0-rc03 (#610) 2019-12-07 16:07:36 +00:00
138fbe5bf5 Bump activity-ktx from 1.1.0-rc02 to 1.1.0-rc03 (#608) 2019-12-07 12:51:40 +00:00
ad99cc75eb Bump gradle from 3.5.2 to 3.5.3 (#609) 2019-12-07 12:51:24 +00:00
f6606e7a4f Bump coordinatorlayout from 1.1.0-rc01 to 1.1.0 (#607) 2019-12-07 12:51:09 +00:00
3690deef1e Bump mockito-android from 3.1.0 to 3.2.0 (#605) 2019-12-02 16:19:20 +00:00
cd1438587d Bump kotlin_version from 1.3.60 to 1.3.61 (#606) 2019-12-02 16:18:56 +00:00
8193a57227 Bump mockito-inline from 3.1.0 to 3.2.0 (#604) 2019-12-02 16:18:27 +00:00
8467f39ad9 Add russian language (#595) 2019-11-29 20:43:37 +01:00
0fc293f47a Bump assisted-inject-processor-dagger2 from 0.5.1 to 0.5.2 (#601) 2019-11-25 17:15:23 +00:00
9b063edb0b Bump rxjava from 2.2.14 to 2.2.15 (#603) 2019-11-25 16:48:56 +00:00
5d852eee87 Bump assisted-inject-annotations-dagger2 from 0.5.1 to 0.5.2 (#602) 2019-11-25 16:48:07 +00:00
54ab408135 Add error view showing on first loading in fragment view (#590) 2019-11-24 17:05:09 +01:00
41aa326f42 Add 0,00 grade modifier (#596) 2019-11-21 17:41:41 +01:00
24f605c71c Bump work_manager from 2.3.0-alpha03 to 2.3.0-beta01 (#600) 2019-11-21 11:00:11 +00:00
5c52dcc74f Bump room from 2.2.1 to 2.2.2 (#599) 2019-11-21 10:31:14 +00:00
667659fbe6 Bump core-ktx from 1.2.0-beta02 to 1.2.0-rc01 (#598) 2019-11-21 10:30:51 +00:00
d467bf096f Bump recyclerview from 1.1.0-rc01 to 1.1.0 (#597) 2019-11-21 10:28:54 +00:00
98d556bcd5 Bump kotlin_version from 1.3.50 to 1.3.60 (#594) 2019-11-18 10:57:50 +01:00
8c730be635 Bump google-services from 4.3.2 to 4.3.3 (#591) 2019-11-18 08:58:17 +00:00
377d24fbb4 Bump sonarqube-gradle-plugin from 2.8 to 2.8.0.1969 (#593) 2019-11-18 08:26:46 +00:00
bde810e031 Fix screens loading state (#589) 2019-11-17 01:07:43 +01:00
2f18d42c86 Update material design components (#587) 2019-11-10 16:45:53 +01:00
c708b0c20e Bump activity-ktx from 1.1.0-rc01 to 1.1.0-rc02 (#586) 2019-11-09 13:35:59 +00:00
ff8d55d4f8 Bump fragment-ktx from 1.2.0-rc01 to 1.2.0-rc02 (#585) 2019-11-09 13:08:02 +00:00
49bf911c84 Bump core-ktx from 1.2.0-beta01 to 1.2.0-beta02 (#584) 2019-11-09 13:07:27 +00:00
9e33ef419f Fix LuckyNumberWidget not showing ThemeDialog (#583) 2019-11-09 13:59:33 +01:00
40e95eac1e Add BaseDao interface (#581) 2019-11-06 23:52:14 +01:00
19e76a0b5d Bump rxjava from 2.2.13 to 2.2.14 (#580) 2019-11-04 19:23:50 +00:00
5feafe3907 Update gradle wrapper (#579) 2019-11-03 15:07:32 +01:00
b7206ed714 Add DatePicker to Timetable and Attendance modules. (#522) 2019-11-03 14:52:35 +01:00
38370d647d Add language change settings (#577) 2019-11-03 12:37:03 +01:00
323bc188b1 Bump assisted-inject-annotations-dagger2 from 0.5.0 to 0.5.1 (#576) 2019-11-01 20:20:09 +00:00
b17356591a Bump assisted-inject-processor-dagger2 from 0.5.0 to 0.5.1 (#575) 2019-11-01 19:50:28 +00:00
a02c444cf5 Merge tag '0.12.0' into develop
0.12.0
2019-10-29 00:25:17 +01:00
87a133beb9 Merge branch 'release/0.12.0' 2019-10-29 00:25:12 +01:00
1f4a208857 Version 0.12.0 2019-10-29 00:25:03 +01:00
a7c472989c Add support for edu.lublin.eu (#571) 2019-10-28 21:10:58 +01:00
747696e386 Bump firebase-core from 17.2.0 to 17.2.1 (#572) 2019-10-28 14:58:10 +00:00
a71a183160 Add school quick actions (#570) 2019-10-27 00:36:39 +02:00
125a010f03 Fix restoring state in exposed dropdown menu (#569) 2019-10-24 22:56:49 +02:00
5c5993cc2a Change FAB to extended FAB in messages (#536) 2019-10-24 18:45:05 +02:00
f234b71932 Bump dagger from 2.24 to 2.25.2 (#564) 2019-10-24 18:24:22 +02:00
497a3391d4 Bump fragment-ktx from 1.2.0-beta02 to 1.2.0-rc01 (#568) 2019-10-24 06:58:42 +00:00
a72c743c6f Bump work_manager from 2.3.0-alpha02 to 2.3.0-alpha03 (#563) 2019-10-24 06:37:24 +00:00
98fdfd001a Bump recyclerview from 1.1.0-beta05 to 1.1.0-rc01 (#566) 2019-10-24 06:17:11 +00:00
994b162ae3 Bump activity-ktx from 1.1.0-beta01 to 1.1.0-rc01 (#567) 2019-10-24 06:16:51 +00:00
90c60f399b Bump coordinatorlayout from 1.1.0-beta01 to 1.1.0-rc01 (#565) 2019-10-24 06:15:58 +00:00
b16b225a1a Bump room from 2.2.0 to 2.2.1 (#562) 2019-10-24 05:55:08 +00:00
7a4cf694ca Add school info (#557)
* Add db layer to school info

* Add base classes

* Add database migration

* Add base view

* Update icon

* Fix textviews height

* Handle error and empty results

* Improve school info look

* Add strings

* Fix action bar elevation in school fragment

* Add missing blank lines

* Reorganize strings

* Make field title first in order

* Make fields views selectable

* Rename SchoolInfo to School
2019-10-21 21:25:15 +02:00
1b492d50fe Bump aboutlibraries from 7.0.3 to 7.0.4 (#559) 2019-10-21 19:23:41 +00:00
d9b5e000f8 Bump gradle from 1.31.1 to 1.31.2 (#560) 2019-10-21 19:23:13 +00:00
7e30524876 Contact info after failed login (#556) 2019-10-20 19:10:32 +02:00
ce9b12eb93 Add system theme setting for Android 10 (#554) 2019-10-16 22:27:16 +02:00
b602657d55 Move default preferences values to separate file (#555) 2019-10-16 20:10:24 +02:00
360dfbcdb5 Update androidx dependencies (#553) 2019-10-15 19:15:28 +02:00
f466497970 Bump recyclerview from 1.1.0-beta04 to 1.1.0-beta05 (#551) 2019-10-11 21:56:19 +00:00
184a7ab200 Bump room from 2.2.0-rc01 to 2.2.0 (#552) 2019-10-11 21:55:28 +00:00
2e5ef7dfa2 Merge tag '0.11.0' into develop
Version 0.11.0
2019-10-07 00:11:59 +02:00
8f617f4ca1 Merge branch 'release/0.11.0' 2019-10-07 00:11:52 +02:00
fe5f96a394 Version 0.11.0 2019-10-07 00:11:33 +02:00
cb9c35d772 Add navigate up to login view (#547) 2019-10-06 22:43:16 +02:00
7cf7977cc6 Bump gson from 2.8.5 to 2.8.6 (#549) 2019-10-06 20:15:53 +00:00
1de747fa35 Add mark as read button enable logic (#534) 2019-10-06 21:25:24 +02:00
93750829d7 Fix duplicate percentage in attendance summary (#548) 2019-10-06 18:21:56 +02:00
798688e7dd Fix multidex in debug variant (#545) 2019-10-05 22:32:55 +02:00
6ab9c1d737 Update mockito to last version from maven central (#546) 2019-10-03 22:47:49 +02:00
b074ce99b7 Bump sonarqube-gradle-plugin from 2.7.1 to 2.8 (#543) 2019-10-03 20:21:43 +00:00
b03fd86be5 Bump gradle from 3.5.0 to 3.5.1 (#542) 2019-10-03 20:08:32 +00:00
3de2f5ff88 Bump githook from 1.1.0 to 1.2.0 (#539) 2019-10-03 19:54:16 +00:00
35adf83154 Bump rxjava from 2.2.12 to 2.2.13 (#538) 2019-10-03 19:43:12 +00:00
0162c8bbee Add better timetable changes display (#513) 2019-10-03 21:13:01 +02:00
736d570f26 Change timetable widget date format (#530) 2019-10-03 15:29:33 +02:00
5b0901e311 Clear Semesters table (#535) 2019-10-03 14:00:07 +02:00
f9474af39e Add points to class grades statistics (#512) 2019-10-03 00:46:08 +02:00
d411d86355 Add click to copy function (#531) 2019-10-02 23:42:38 +02:00
a50c6707cb Update readme badges (#532) 2019-10-01 22:19:28 +02:00
6991c68d3a Bump mockito-core from 3.0.8 to 3.0.11 (#525) 2019-09-30 10:31:07 +00:00
8b1e6f7bd6 Bump play-publisher from 2.4.1 to 2.4.2 (#528) 2019-09-30 10:16:38 +00:00
943fd9c622 Bump logging-interceptor from 3.12.5 to 3.12.6 (#529) 2019-09-30 10:09:57 +00:00
838e2781c0 Bump mockito-inline from 3.0.8 to 3.0.11 (#526) 2019-09-30 10:05:54 +00:00
66cfd7b52c Bump mockito-android from 3.0.8 to 3.0.11 (#527) 2019-09-30 10:04:50 +00:00
a45bc0eef6 Add teachers (#489) 2019-09-29 18:09:53 +02:00
ed7996299e Improve date navigation (#519) 2019-09-28 20:37:14 +02:00
d4b73fb73e Add dark theme for app widgets (#509) 2019-09-25 22:44:55 +02:00
d6ece78eff Bump mockito-android from 3.0.7 to 3.0.8 (#518) 2019-09-22 11:03:41 +00:00
2f44f3c4ba Bump gradle from 1.31.0 to 1.31.1 (#517) 2019-09-22 10:55:34 +00:00
23b49e4b8c Bump mockito-inline from 3.0.7 to 3.0.8 (#516) 2019-09-22 10:50:21 +00:00
5d33cefe1d Bump mockito-core from 3.0.7 to 3.0.8 (#515) 2019-09-22 10:49:39 +00:00
6089df9462 Fix wrong index in form host value (#507) 2019-09-16 23:27:58 +02:00
3ee98e2bd0 Fix privacy link position on small screens (#508) 2019-09-16 23:13:46 +02:00
53a5d02051 Fix attendance_excused_lateness pl typo (#511) 2019-09-16 23:06:37 +02:00
e332fd9cf9 Bump logging-interceptor from 3.12.4 to 3.12.5 (#510) 2019-09-16 08:29:32 +00:00
7232938c12 Update API 28 to API 29 (#506) 2019-09-12 16:36:32 +02:00
fd02f2253b Merge tag '0.10.2' into develop
Version 0.10.2
2019-09-10 13:01:52 +02:00
12046ef0a0 Merge branch 'release/0.10.2' 2019-09-10 13:01:38 +02:00
dfc84b4208 Version 0.10.2 2019-09-10 12:58:27 +02:00
f5f11d5130 Fix login in symbol view (#493) 2019-09-10 12:25:08 +02:00
81ce328abd Bump recyclerview from 1.1.0-beta03 to 1.1.0-beta04 (#504) 2019-09-09 14:05:04 +00:00
867951136a Bump gradle-play-publisher from cdaeb61 to 2.4.0 (#501) 2019-09-09 13:43:55 +00:00
8b41ab27bd Bump mockito-core from 3.0.6 to 3.0.7 (#502) 2019-09-09 09:25:52 +00:00
e542ef003c Bump mockito-android from 3.0.6 to 3.0.7 (#503) 2019-09-09 08:54:03 +00:00
a5f212e6be Bump room from 2.2.0-beta01 to 2.2.0-rc01 (#499) 2019-09-09 08:39:39 +00:00
385a320536 Bump google-services from 4.3.1 to 4.3.2 (#498) 2019-09-09 08:25:51 +00:00
e65000ec2c Bump mockito-inline from 3.0.6 to 3.0.7 (#497) 2019-09-09 08:25:29 +00:00
c87de7b3c2 Bump logging-interceptor from 3.12.3 to 3.12.4 (#496) 2019-09-09 08:23:22 +00:00
bb6023709f Fix grade details header unread info (#494) 2019-09-08 15:10:29 +02:00
e998e54d3e Merge tag '0.10.1' into develop
Version 0.10.1
2019-09-07 02:25:22 +02:00
e269886eae Merge branch 'release/0.10.1' 2019-09-07 02:25:12 +02:00
dabb83c522 Version 0.10.1 2019-09-07 02:23:44 +02:00
6350b72e23 Fix account icon color in widgets (#488) 2019-09-06 18:23:51 +02:00
e4100d940a Fix crashing tab layout on prelolipop (#492) 2019-09-06 13:55:26 +02:00
6575674169 Fix about_feedback_summary typo (#487) 2019-09-05 18:57:37 +02:00
a13aad984c Merge tag '0.10.0' into develop
Version 0.10.0
2019-09-04 22:59:40 +02:00
750fa9a76d Merge branch 'release/0.10.0' 2019-09-04 22:59:26 +02:00
d0ad4d9364 Version 0.10.0 2019-09-04 22:58:53 +02:00
4e0f41dcb2 Change marking current student in accounts dialog (#486) 2019-09-04 21:19:05 +02:00
c3bb489851 Mitigate disappearing teachers in timetable (#485) 2019-09-04 20:45:34 +02:00
e64e6676f3 Fix status bar color on xiaomi (#484) 2019-09-04 13:52:55 +02:00
108440b1ca Reduce grade color saturations (#482) 2019-09-03 22:43:17 +02:00
d54f5ed1aa Bump aboutlibraries from 7.0.2 to 7.0.3 (#480) 2019-09-03 22:39:05 +02:00
0aac6459f3 Fix elevation overlay on pre-lolipop (#475) 2019-09-02 12:56:13 +02:00
bb9ea7eda1 Fix spaming NoCurentStudentException (#476) 2019-09-02 12:38:09 +02:00
455b04f183 Fix empty hosts list after activity recreating (#477) 2019-09-02 10:05:42 +02:00
39fb4f5def Update dependencies (#474) 2019-09-01 13:24:21 +02:00
c60428e29b Add outline icons (#328) 2019-08-30 12:00:34 +02:00
5413ecabb4 Bump firebase-core from 17.1.0 to 17.2.0 (#473) 2019-08-28 17:03:51 +00:00
c6d9dfa0c9 Change app theme (#330) 2019-08-26 20:54:20 +02:00
b0033af048 Bump rxjava from 2.2.11 to 2.2.12 (#472) 2019-08-26 13:11:35 +00:00
405d37e822 Bump play-publisher from 2.2.1 to 2.3.0 (#446) 2019-08-23 18:57:45 +00:00
0d514b7dc9 Bump gradle from 3.4.2 to 3.5.0 (#469) 2019-08-23 18:37:05 +00:00
c3596aa45c Bump reactivenetwork-rx2 from 3.0.4 to 3.0.6 (#468) 2019-08-23 11:50:18 +00:00
d34bdb2ce3 Bump google-services from 4.3.0 to 4.3.1 (#470) 2019-08-23 11:49:51 +00:00
c91870cc04 Bump kotlin_version from 1.3.41 to 1.3.50 (#471) 2019-08-23 11:35:31 +00:00
2f6862967e Bump mockito-core from 3.0.5 to 3.0.6 (#466) 2019-08-20 10:24:11 +00:00
7525395665 Bump mockito-inline from 3.0.5 to 3.0.6 (#465) 2019-08-20 10:07:52 +00:00
657e2b280e Bump mockito-android from 3.0.5 to 3.0.6 (#467) 2019-08-20 10:06:40 +00:00
4ea9be582f Bump mockito-core from 3.0.4 to 3.0.5 (#464) 2019-08-19 09:16:34 +00:00
3dcc8247bf Bump mockito-inline from 3.0.4 to 3.0.5 (#463) 2019-08-19 08:50:35 +00:00
c45b7c04b9 Bump mockito-android from 3.0.4 to 3.0.5 (#462) 2019-08-19 08:42:55 +00:00
33d6ae8afc Bump firebase-core from 17.0.1 to 17.1.0 (#461) 2019-08-19 08:42:26 +00:00
6871606bef Bump work_manager from 2.1.0 to 2.2.0 (#460) 2019-08-17 18:31:53 +02:00
6e266acec9 Merge tag '0.9.4' into develop
0.9.4
2019-08-12 23:18:04 +02:00
452 changed files with 13498 additions and 2926 deletions

View File

@ -98,7 +98,7 @@ jobs:
command: yes | sdkmanager --licenses && yes | sdkmanager --update
- run:
name: Setup emulator
command: sdkmanager "system-images;android-19;default;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-19;default;armeabi-v7a"
command: sdkmanager "system-images;android-22;default;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-22;default;armeabi-v7a"
- run:
name: Launch emulator
command: export LD_LIBRARY_PATH=${ANDROID_HOME}/emulator/lib64:${ANDROID_HOME}/emulator/lib64/qt/lib && emulator64-arm -avd test -noaudio -no-boot-anim -no-window -accel on
@ -116,7 +116,7 @@ jobs:
adb shell input keyevent 82
- run:
name: Run instrumented tests
command: ./gradlew clean createPlayDebugCoverageReport jacocoTestReport --no-daemon --stacktrace --console=plain -PdisablePreDex
command: ./gradlew clean createFdroidDebugCoverageReport jacocoTestReport --no-daemon --stacktrace --console=plain -PdisablePreDex
- run:
name: Collect logs from emulator
command: adb logcat -d > ./app/build/reports/logcat_emulator.txt

3
.gitignore vendored
View File

@ -110,4 +110,5 @@ Thumbs.db
### AndroidStudio Patch ###
!/gradle/wrapper/gradle-wrapper.jar
!/gradle/wrapper/gradle-wrapper.jar
.idea/jarRepositories.xml

View File

@ -2,7 +2,7 @@
<code_scheme name="Project" version="173">
<option name="LINE_SEPARATOR" value="&#10;" />
<AndroidXmlCodeStyleSettings>
<option name="USE_CUSTOM_SETTINGS" value="true" />
<option name="ARRANGEMENT_SETTINGS_MIGRATED_TO_191" value="true" />
</AndroidXmlCodeStyleSettings>
<JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
@ -24,13 +24,7 @@
<MarkdownNavigatorCodeStyleSettings>
<option name="RIGHT_MARGIN" value="72" />
</MarkdownNavigatorCodeStyleSettings>
<XML>
<option name="XML_KEEP_LINE_BREAKS" value="false" />
<option name="XML_ALIGN_ATTRIBUTES" value="false" />
<option name="XML_SPACE_INSIDE_EMPTY_TAG" value="true" />
</XML>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
@ -41,6 +35,7 @@
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
@ -51,6 +46,7 @@
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
@ -62,6 +58,7 @@
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
@ -72,6 +69,7 @@
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
@ -82,6 +80,7 @@
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
@ -92,6 +91,7 @@
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
@ -102,6 +102,7 @@
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
@ -113,6 +114,7 @@
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
@ -124,6 +126,7 @@
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
@ -141,7 +144,6 @@
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
<option name="EXTENDS_LIST_WRAP" value="1" />
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>

View File

@ -3,8 +3,8 @@ jdk: oraclejdk8
env:
global:
- ANDROID_API_LEVEL=28
- ANDROID_BUILD_TOOLS_VERSION=28.0.3
- ANDROID_API_LEVEL=29
- ANDROID_BUILD_TOOLS_VERSION=29.0.2
cache:
directories:
@ -14,7 +14,7 @@ cache:
branches:
only:
- develop
- 0.9.4
- 0.13.0
android:
licenses:
@ -34,12 +34,12 @@ android:
- extra-android-m2repository
- addon-google_apis-google-$ANDROID_API_LEVEL
# Android emulator
- android-19
- sys-img-armeabi-v7a-android-19
- android-22
- sys-img-armeabi-v7a-android-22
before_script:
# Launch emulator before the execution
- echo no | android create avd --force -n test -t android-19 --abi armeabi-v7a
- echo no | android create avd --force -n test -t android-22 --abi armeabi-v7a
- emulator -avd test -no-audio -no-window &
- android-wait-for-emulator
- adb shell input keyevent 82 &
@ -50,7 +50,7 @@ script:
- fossa --no-ansi || true
#- ./gradlew lintPlayRelease -x fabricGenerateResourcesPlayRelease --stacktrace --daemon
- ./gradlew testPlayDebugUnitTest -x fabricGenerateResourcesPlay --stacktrace --daemon
- ./gradlew createPlayDebugCoverageReport --stacktrace --daemon
- ./gradlew createFdroidDebugCoverageReport --stacktrace --daemon
- ./gradlew jacocoTestReport --stacktrace --daemon
- if [ -z ${SONAR_HOST+x} ]; then echo "sonar scan skipped"; else
git fetch --unshallow;

View File

@ -4,8 +4,8 @@
[![Travis](https://img.shields.io/travis/com/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://travis-ci.com/wulkanowy/wulkanowy)
[![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy)
[![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr)
[![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg)](https://f-droid.org/packages/io.github.wulkanowy/)
[![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github)](https://github.com/wulkanowy/wulkanowy/releases)
[![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/)
[![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases)
Unofficial android VULCAN UONET+ register client for student and parent

View File

@ -4,8 +4,8 @@
[![Travis](https://img.shields.io/travis/com/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://travis-ci.com/wulkanowy/wulkanowy)
[![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy)
[![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr)
[![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg)](https://f-droid.org/packages/io.github.wulkanowy/)
[![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github)](https://github.com/wulkanowy/wulkanowy/releases)
[![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/)
[![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases)
Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica

View File

@ -9,16 +9,16 @@ apply from: 'sonarqube.gradle'
apply from: 'hooks.gradle'
android {
compileSdkVersion 28
buildToolsVersion '28.0.3'
compileSdkVersion 29
buildToolsVersion '29.0.2'
defaultConfig {
applicationId "io.github.wulkanowy"
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 16
targetSdkVersion 28
versionCode 42
versionName "0.9.4"
targetSdkVersion 29
versionCode 48
versionName "0.13.0"
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
@ -28,7 +28,10 @@ android {
]
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
arguments = [
"room.schemaLocation": "$projectDir/schemas".toString(),
"room.incremental" : "true"
]
}
}
}
@ -60,11 +63,11 @@ android {
versionNameSuffix "-dev"
testCoverageEnabled = true
ext.enableCrashlytics = project.hasProperty("enableCrashlytics")
multiDexKeepProguard file('proguard-multidex-rules.pro')
}
}
flavorDimensions "platform"
productFlavors {
play {
dimension "platform"
@ -84,6 +87,15 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
packagingOptions {
exclude 'META-INF/library_release.kotlin_module'
exclude 'META-INF/library-core_release.kotlin_module'
}
}
androidExtensions {
@ -98,65 +110,74 @@ play {
}
ext {
work_manager = "2.1.0"
room = "2.1.0"
dagger = "2.24"
work_manager = "2.3.0-beta01"
room = "2.2.2"
dagger = "2.25.2"
chucker = "2.0.4"
mockk = "1.9.2"
mockito_core = "3.0.4"
}
configurations.all {
resolutionStrategy.force "androidx.constraintlayout:constraintlayout:1.1.3"
}
dependencies {
implementation "io.github.wulkanowy:api:0.9.4"
implementation "io.github.wulkanowy:api:0.13.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "androidx.core:core:1.0.2"
implementation "androidx.appcompat:appcompat:1.0.2"
implementation "androidx.fragment:fragment:1.0.0"
implementation "androidx.core:core-ktx:1.2.0-rc01"
implementation "androidx.activity:activity-ktx:1.1.0-rc03"
implementation "androidx.appcompat:appcompat:1.1.0"
implementation "androidx.appcompat:appcompat-resources:1.1.0"
implementation "androidx.fragment:fragment-ktx:1.2.0-rc03"
implementation "androidx.annotation:annotation:1.1.0"
implementation "androidx.multidex:multidex:2.0.1"
implementation "androidx.recyclerview:recyclerview:1.0.0"
implementation "androidx.preference:preference-ktx:1.1.0"
implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation "androidx.viewpager:viewpager:1.0.0"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03"
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.0.0"
implementation "com.google.android.material:material:1.1.0-alpha07"
implementation "com.github.wulkanowy:MaterialChipsInput:b72fd0ee6f"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
implementation "com.google.android.material:material:1.1.0-beta02"
implementation "com.github.wulkanowy:material-chips-input:2.0.1"
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
implementation "me.zhanghai.android.materialprogressbar:library:1.6.1"
implementation "androidx.work:work-runtime:$work_manager"
implementation "androidx.work:work-runtime-ktx:$work_manager"
implementation "androidx.work:work-rxjava2:$work_manager"
implementation "androidx.work:work-gcm:$work_manager"
implementation "androidx.room:room-runtime:$room"
implementation "androidx.room:room-rxjava2:$room"
implementation "androidx.room:room-ktx:$room"
kapt "androidx.room:room-compiler:$room"
implementation "com.google.dagger:dagger-android-support:$dagger"
kapt "com.google.dagger:dagger-compiler:$dagger"
kapt "com.google.dagger:dagger-android-processor:$dagger"
implementation "com.squareup.inject:assisted-inject-annotations-dagger2:0.5.0"
kapt "com.squareup.inject:assisted-inject-processor-dagger2:0.5.0"
implementation "com.squareup.inject:assisted-inject-annotations-dagger2:0.5.2"
kapt "com.squareup.inject:assisted-inject-processor-dagger2:0.5.2"
implementation "eu.davidea:flexible-adapter:5.1.0"
implementation "eu.davidea:flexible-adapter-ui:1.0.0"
implementation "com.aurelhubert:ahbottomnavigation:2.3.4"
implementation "com.ncapdevi:frag-nav:3.3.0"
implementation "com.github.YarikSOffice:lingver:1.1.0"
implementation "com.github.pwittchen:reactivenetwork-rx2:3.0.4"
implementation "com.github.pwittchen:reactivenetwork-rx2:3.0.6"
implementation "io.reactivex.rxjava2:rxandroid:2.1.1"
implementation "io.reactivex.rxjava2:rxjava:2.2.11"
implementation "io.reactivex.rxjava2:rxjava:2.2.15"
implementation "com.google.code.gson:gson:2.8.5"
implementation "com.google.code.gson:gson:2.8.6"
implementation "com.jakewharton.threetenabp:threetenabp:1.2.1"
implementation "com.jakewharton.timber:timber:4.7.1"
implementation "at.favre.lib:slf4j-timber:1.0.1"
implementation "com.squareup.okhttp3:logging-interceptor:3.12.3"
implementation "com.squareup.okhttp3:logging-interceptor:3.12.6"
implementation "com.mikepenz:aboutlibraries:7.0.4"
implementation 'com.wdullaer:materialdatetimepicker:4.2.3'
implementation "com.mikepenz:aboutlibraries:6.2.3"
implementation "com.takisoft.preferencex:preferencex:1.0.0"
playImplementation "com.google.firebase:firebase-core:17.0.1"
playImplementation "com.google.firebase:firebase-core:17.2.1"
playImplementation "com.crashlytics.sdk.android:crashlytics:2.10.1"
releaseImplementation "fr.o80.chucker:library-no-op:$chucker"
@ -167,10 +188,7 @@ dependencies {
testImplementation "junit:junit:4.12"
testImplementation "io.mockk:mockk:$mockk"
testImplementation "org.threeten:threetenbp:1.4.0"
testImplementation "org.mockito:mockito-core:$mockito_core"
testImplementation("org.mockito:mockito-inline:3.0.4") {
exclude group: "org.mockito", module: "mockito-core"
}
testImplementation "org.mockito:mockito-inline:3.2.0"
androidTestImplementation "androidx.test:core:1.2.0"
androidTestImplementation "androidx.test:runner:1.2.0"
@ -178,10 +196,7 @@ dependencies {
androidTestImplementation "io.mockk:mockk-android:$mockk"
androidTestImplementation "androidx.room:room-testing:$room"
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
androidTestImplementation "org.mockito:mockito-core:$mockito_core"
androidTestImplementation("org.mockito:mockito-android:3.0.4") {
exclude group: 'org.mockito', module: 'mockito-core'
}
androidTestImplementation "org.mockito:mockito-android:3.2.0"
}
apply plugin: 'com.google.gms.google-services'

View File

@ -1,7 +1,7 @@
apply plugin: "jacoco"
jacoco {
toolVersion "0.8.3"
toolVersion "0.8.4"
reportsDir = file("$buildDir/reports")
}

View File

@ -1,3 +0,0 @@
-keep class android.support.test.internal** { *; }
-keep class org.junit.** { *; }
-keep public class io.github.wulkanowy** { *; }

View File

@ -11,6 +11,10 @@
-verbose
#Keep all wulkanowy files
-keep class io.github.wulkanowy.** {*;}
#Config for anallitycs
-keepattributes *Annotation*
-keepattributes SourceFile,LineNumberTable
@ -32,11 +36,10 @@
-dontwarn rx.internal.util.**
-dontwarn sun.misc.Unsafe
#Config for MPAndroidChart
-keep class com.github.mikephil.charting.** { *; }
#Config for API
-keep class io.github.wulkanowy.api.** {*;}
#Config for Material Components
-keep class com.google.android.material.tabs.** {*;}
-keep class com.google.android.material.tabs.** { *; }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -22,12 +22,7 @@ abstract class AbstractMigrationTest {
fun getMigratedRoomDatabase(): AppDatabase {
val database = Room.databaseBuilder(ApplicationProvider.getApplicationContext(),
AppDatabase::class.java, dbName)
.addMigrations(
Migration12(),
Migration13(),
Migration14(),
Migration15()
)
.addMigrations(*AppDatabase.getMigrations())
.build()
// close the database and release any stream resources when the test finishes
helper.closeWhenFinished(database)

View File

@ -3,10 +3,14 @@ package io.github.wulkanowy.data.db.migrations
import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import io.github.wulkanowy.data.db.Converters
import io.github.wulkanowy.data.db.entities.Semester
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import org.threeten.bp.LocalDate.now
import org.threeten.bp.LocalDate.of
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class Migration13Test : AbstractMigrationTest() {
@ -97,11 +101,9 @@ class Migration13Test : AbstractMigrationTest() {
close()
}
helper.runMigrationsAndValidate(dbName, 13, true, Migration13())
val db = helper.runMigrationsAndValidate(dbName, 13, true, Migration13())
val db = getMigratedRoomDatabase()
val semesters1 = db.semesterDao.loadAll(1, 5).blockingGet()
val semesters1 = getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 1 AND class_id = 5")
assertTrue { semesters1.single { it.isCurrent }.isCurrent }
semesters1[0].run {
assertFalse(isCurrent)
@ -119,7 +121,7 @@ class Migration13Test : AbstractMigrationTest() {
assertEquals(2, diaryId)
}
db.semesterDao.loadAll(2, 5).blockingGet().let {
getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 2 AND class_id = 5").let {
assertTrue { it.single { it.isCurrent }.isCurrent }
assertEquals(1970, it[0].schoolYear)
assertEquals(of(1970, 1, 1), it[0].end)
@ -130,7 +132,7 @@ class Migration13Test : AbstractMigrationTest() {
assertTrue(it[3].isCurrent)
}
db.semesterDao.loadAll(2, 5).blockingGet().let {
getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 2 AND class_id = 5").let {
assertTrue { it.single { it.isCurrent }.isCurrent }
assertFalse(it[0].isCurrent)
assertFalse(it[1].isCurrent)
@ -139,6 +141,30 @@ class Migration13Test : AbstractMigrationTest() {
}
}
private fun getSemesters(db: SupportSQLiteDatabase, query: String): List<Semester> {
val semesters = mutableListOf<Semester>()
val cursor = db.query(query)
if (cursor.moveToFirst()) {
do {
semesters.add(Semester(
studentId = cursor.getInt(1),
diaryId = cursor.getInt(2),
diaryName = cursor.getString(3),
semesterId = cursor.getInt(4),
semesterName = cursor.getInt(5),
isCurrent = cursor.getInt(6) == 1,
classId = cursor.getInt(7),
unitId = cursor.getInt(8),
schoolYear = cursor.getInt(9),
start = Converters().timestampToDate(cursor.getLong(10))!!,
end = Converters().timestampToDate(cursor.getLong(11))!!
))
} while (cursor.moveToNext())
}
return semesters.toList()
}
private fun createStudent(db: SupportSQLiteDatabase, studentId: Int, schoolName: String = "", classId: Int = -1, schoolId: Int = 123) {
db.insert("Students", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply {
put("endpoint", "https://fakelog.cf")

View File

@ -4,6 +4,7 @@ import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.data.db.entities.Semester
import org.junit.After
@ -24,7 +25,7 @@ class GradeStatisticsLocalTest {
fun createDb() {
testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java)
.build()
gradeStatisticsLocal = GradeStatisticsLocal(testDb.gradeStatistics)
gradeStatisticsLocal = GradeStatisticsLocal(testDb.gradeStatistics, testDb.gradePointsStatistics)
}
@After
@ -63,7 +64,52 @@ class GradeStatisticsLocalTest {
assertEquals(stats[0].subject, "Wszystkie")
}
@Test
fun saveAndRead_points() {
gradeStatisticsLocal.saveGradesPointsStatistics(listOf(
getGradePointsStatistics("Matematyka", 2, 1),
getGradePointsStatistics("Chemia", 2, 1),
getGradePointsStatistics("Fizyka", 1, 2)
))
val stats = gradeStatisticsLocal.getGradesPointsStatistics(
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1),
"Matematyka"
).blockingGet()
with(stats) {
assertEquals(subject, "Matematyka")
assertEquals(others, 5.0)
assertEquals(student, 5.0)
}
}
@Test
fun saveAndRead_subjectEmpty() {
gradeStatisticsLocal.saveGradesPointsStatistics(listOf())
val stats = gradeStatisticsLocal.getGradesPointsStatistics(
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1),
"Matematyka"
).blockingGet()
assertEquals(null, stats)
}
@Test
fun saveAndRead_allEmpty() {
gradeStatisticsLocal.saveGradesPointsStatistics(listOf())
val stats = gradeStatisticsLocal.getGradesPointsStatistics(
Semester(2, 2, "", 2019, 1, 2, true, LocalDate.now(), LocalDate.now(), 1, 1),
"Wszystkie"
).blockingGet()
assertEquals(null, stats)
}
private fun getGradeStatistics(subject: String, studentId: Int, semesterId: Int): GradeStatistics {
return GradeStatistics(studentId, semesterId, subject, 5, 5, false)
}
private fun getGradePointsStatistics(subject: String, studentId: Int, semesterId: Int): GradePointsStatistics {
return GradePointsStatistics(studentId, semesterId, subject, 5.0, 5.0)
}
}

View File

@ -5,7 +5,7 @@ import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.SharedPrefHelper
import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.db.entities.Student
import org.junit.After
import org.junit.Before
@ -21,14 +21,14 @@ class StudentLocalTest {
private lateinit var testDb: AppDatabase
private lateinit var sharedHelper: SharedPrefHelper
private lateinit var sharedProvider: SharedPrefProvider
@Before
fun createDb() {
val context = ApplicationProvider.getApplicationContext<Context>()
testDb = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java)
.build()
sharedHelper = SharedPrefHelper(context.getSharedPreferences("TEST", Context.MODE_PRIVATE))
sharedProvider = SharedPrefProvider(context.getSharedPreferences("TEST", Context.MODE_PRIVATE))
studentLocal = StudentLocal(testDb.studentDao, context)
}

View File

@ -7,7 +7,7 @@ import org.threeten.bp.LocalDateTime.now
import io.github.wulkanowy.api.timetable.Timetable as TimetableRemote
import io.github.wulkanowy.data.db.entities.Timetable as TimetableLocal
fun createTimetableLocal(number: Int, start: LocalDateTime, room: String = "", subject: String = "", teacher: String = ""): TimetableLocal {
fun createTimetableLocal(number: Int, start: LocalDateTime, room: String = "", subject: String = "", teacher: String = "", changes: Boolean = false): TimetableLocal {
return TimetableLocal(
studentId = 1,
diaryId = 2,
@ -23,12 +23,12 @@ fun createTimetableLocal(number: Int, start: LocalDateTime, room: String = "", s
teacher = teacher,
teacherOld = "",
info = "",
changes = false,
changes = changes,
canceled = false
)
}
fun createTimetableRemote(number: Int, start: LocalDateTime, room: String, subject: String = "", teacher: String = ""): TimetableRemote {
fun createTimetableRemote(number: Int, start: LocalDateTime, room: String, subject: String = "", teacher: String = "", changes: Boolean = false): TimetableRemote {
return TimetableRemote(
number = number,
start = start.toDate(),
@ -39,7 +39,7 @@ fun createTimetableRemote(number: Int, start: LocalDateTime, room: String, subje
room = room,
teacher = teacher,
info = "",
changes = false,
changes = changes,
canceled = false
)
}

View File

@ -60,7 +60,7 @@ class TimetableRepositoryTest {
}
@Test
fun copyDetailsToCompletedFromPrevious() {
fun copyRoomToCompletedFromPrevious() {
timetableLocal.saveTimetable(listOf(
createTimetableLocal(1, of(2019, 3, 5, 8, 0), "123", "Przyroda"),
createTimetableLocal(2, of(2019, 3, 5, 8, 50), "321", "Religia"),
@ -83,7 +83,32 @@ class TimetableRepositoryTest {
assertEquals("123", lessons[0].room)
assertEquals("321", lessons[1].room)
assertEquals("213", lessons[2].room)
}
assertEquals("", lessons[3].teacher)
@Test
fun copyTeacherToCompletedFromPrevious() {
timetableLocal.saveTimetable(listOf(
createTimetableLocal(1, of(2019, 3, 5, 8, 0), "123", "Przyroda", "Jan Garnkiewicz", false),
createTimetableLocal(2, of(2019, 3, 5, 8, 50), "321", "Religia", "Paweł Jumper", false),
createTimetableLocal(3, of(2019, 3, 5, 9, 40), "213", "W-F", "", true),
createTimetableLocal(4, of(2019, 3, 5, 10, 30), "213", "W-F", "", false)
))
every { mockApi.getTimetable(any(), any()) } returns Single.just(listOf(
createTimetableRemote(1, of(2019, 3, 5, 8, 0), "", "Przyroda", "", true), // should override local
createTimetableRemote(2, of(2019, 3, 5, 8, 50), "", "Religia", "", false),
createTimetableRemote(3, of(2019, 3, 5, 9, 40), "", "W-F", "Jan Garnkiewicz", false),
createTimetableRemote(4, of(2019, 3, 5, 10, 30), "", "W-F", "Paweł Jumper", false)
))
val lessons = TimetableRepository(settings, timetableLocal, timetableRemote)
.getTimetable(semesterMock, LocalDate.of(2019, 3, 5), LocalDate.of(2019, 3, 5), true)
.blockingGet()
assertEquals(4, lessons.size)
assertEquals("", lessons[0].teacher)
assertEquals("Paweł Jumper", lessons[1].teacher)
assertEquals("Jan Garnkiewicz", lessons[2].teacher)
assertEquals("Paweł Jumper", lessons[3].teacher)
}
}

View File

@ -1,58 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="1926.9231"
android:viewportHeight="1926.9231">
<group android:translateX="462.46155"
android:translateY="462.46155">
<path
android:pathData="M2000,1440.1V2002H1240.1L238.1,1000v0l-120,-120c0.2,-0.2 -0.7,-1.6 -0.6,-1.8 1.6,-3.1 3.4,-6 5,-9.1 0.4,-0.8 0.6,-1.6 0.6,-2.5 9.2,-15.1 20.2,-28.9 31.4,-42.6 0.1,-0.2 0.3,-0.3 0.4,-0.5 5.1,-6.3 10.3,-12.6 15.3,-18.9 1,-1.2 1.9,-2.4 2.9,-3.7 0.2,-0.2 0.3,-0.4 0.5,-0.6 7.6,-9.7 14.8,-19.7 21.2,-30.2 1,-1.7 2,-3.4 2.9,-5.1 0.1,-0.2 0.2,-0.3 0.3,-0.5 6.2,-11.4 11.3,-23.3 17.7,-34.6 4.5,-8 9.3,-15.9 13.8,-23.9 0.1,-0.2 0.2,-0.3 0.3,-0.5 0.8,-1.5 1.6,-3 2.4,-4.4 0.2,-0.3 0.3,-0.6 0.5,-0.9 2,-3.7 3.8,-7.4 5.6,-11.2 6.2,-13.4 10.2,-27.7 14,-41.9 0,-0.2 0.1,-0.3 0.1,-0.5 0.3,-1 0.5,-2 0.8,-2.9 4,-15.3 7.3,-31.4 13.5,-45.9 1.4,-3.3 3,-6.5 4.6,-9.6 0.1,-0.2 0.2,-0.3 0.3,-0.5 5,-9.7 10.6,-19.1 13.5,-29.7 2.6,-9.2 3.5,-19 3.6,-28.8 0,-0.2 0,-0.3 0,-0.5 0.1,-7.2 -0.1,-14.4 -0.4,-21.3 0.9,-5.6 1.9,-11.3 3,-16.9 0.2,-0.9 0.3,-1.7 0.5,-2.6 1.2,-5.9 2.5,-11.7 4.2,-17.4 0,-0.1 0.1,-0.3 0.1,-0.4 0,-0.2 0.1,-0.3 0.1,-0.5 0.2,-2.1 -0.8,-3.7 -2.4,-4.5l7.7,-7.6c1.2,0.9 2.7,1.3 4.3,0.9 12.8,-3.9 24.9,-9.9 36.3,-16.8 2.8,-1.7 5.6,-3.4 8.3,-5.2 0.2,-0.1 0.3,-0.2 0.5,-0.3 7.7,-4.9 15.2,-10.2 22.1,-16 3.1,-2.7 5.9,-5.6 8.3,-8.9 0.1,-0.2 0.2,-0.3 0.4,-0.5 2.3,-3.4 4.2,-7.2 5.3,-11.4 2.4,-9.2 1.9,-19 1.9,-28.4 0,-0.2 0,-0.4 0,-0.6l5.7,-5.6 -0.3,0.1 4.1,1.3 45.3,45 9.6,9.6 14.3,14.3 100.8,100.8 17.2,17.2 16.3,16.3 30.3,-30.3 -192.9,-191.5c0.5,-0.5 1.1,-1.6 1.4,-2.3 0.3,-0.6 0.5,-1.2 0.4,-1.9 0,-0.8 -0.2,-1.6 -0.6,-2.3 -0.1,-0.3 -0.3,-0.7 -0.4,-1 -0.2,-0.4 -0.5,-0.7 -0.9,-1.1l27.8,-27.8 191.1,-191.1c0.7,0.4 1.5,0.6 2.5,0.7 4.1,0 8.2,0.1 11.9,1.9 0.5,0.3 1.3,0.7 2.1,1.1 1.7,2.3 3.5,4.5 5.7,6.2 2.7,2.1 8.2,3 9.4,0.3z"
android:fillColor="#AD2A2A"/>
<path
android:pathData="m616.3,796.7c0.4,1.2 0.5,2.4 0.5,3.7l-5.8,73.3c-0.4,5.2 -5,9.3 -10.6,9.3h-187.2c-4,0 -7.7,-2.3 -9.5,-5.6l-30.9,-57.4c-0.2,-0.3 -0.3,-0.6 -0.4,-0.9l-29.1,-70.5c-1.1,-2.5 -0.9,-5.4 0.3,-7.8l48.3,-94.5c1.2,-2.3 1.4,-5 0.5,-7.4l-23.6,-65.4c-0.8,-2.3 -0.7,-4.9 0.3,-7.1l39.4,-83.9c4,-8.4 17.1,-7.7 19.9,1.1l12.3,38.2 37.5,100.4c1,2.8 3.4,5 6.4,6l80,27.3c3.1,1.1 5.5,3.4 6.5,6.3z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="m386.7,253.5c0,-22.9 14.5,-42.6 35.3,-51.8 -1.5,-2.5 -2.4,-5.2 -2.4,-8.1 0,-11.1 12.8,-20.1 28.5,-20.1 0.4,0 0.7,0 1.1,0 6.5,-10.8 18.9,-18.1 33.1,-18.1 1,0 2,0 3,0.1 1.3,0.1 2.6,-0.5 3.3,-1.5 10.5,-16 37.1,-27.4 68.2,-27.4 11.5,0 22.4,1.6 32.1,4.3 2.3,-3.8 7.4,-6.4 13.3,-6.4 6.7,0 12.4,3.4 14.1,8.1 8.2,-9.5 22.1,-15.7 37.9,-15.7 25.2,0 45.6,15.9 45.6,35.5 0,2.4 -0.3,4.7 -0.9,7 -0.4,1.6 0.5,3.2 2.2,3.8 16.1,5.9 27,17.1 27,29.9 0,14.6 -14.1,27.1 -34,32 -1.6,0.4 -2.6,1.8 -2.6,3.3 0,0.1 0,0.2 0,0.3 0,11.3 -11.2,20.6 -25.6,21.6 0.1,0.5 0.1,1 0.1,1.6 0,21.7 -41.3,39.2 -92.2,39.2 -11,0 -21.5,-0.8 -31.2,-2.3 0,0.2 0,0.3 0,0.5 0,9 -11.6,16.3 -25.8,16.3 -0.8,0 -1.5,0 -2.2,-0.1 1,2.2 1.5,4.5 1.5,6.9 0,14 -17.7,25.3 -39.6,25.3 -2.4,0 -4.7,-0.1 -7,-0.4 -1.8,-0.2 -3.5,0.9 -3.9,2.6 -1.9,7 -8,12.1 -15.3,12.1 -8.8,0 -15.8,-7.5 -15.8,-16.7 0,-4.3 1.5,-8.2 4,-11.1 0.9,-1 1.2,-2.4 0.6,-3.6 -1.4,-2.6 -2,-5.3 -2,-8.2v0c0,-1.6 -1.3,-2.9 -2.9,-3.3 -27.1,-6 -47.5,-28.6 -47.5,-55.6z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="m870.4,883h-124.4c-3.6,0 -6.9,-1.8 -8.9,-4.6l-83.7,-117.7c-0.5,-0.8 -1,-1.6 -1.3,-2.5l-41.5,-121.2c-0.8,-2.4 -2.7,-4.5 -5.1,-5.7l-101.6,-51.3c-2.8,-1.4 -4.8,-4 -5.4,-6.9l-15,-74.4c-0.2,-1.2 -0.7,-2.4 -1.5,-3.5l-34.5,-50.7c-1.9,-2.8 -2.2,-6.2 -0.8,-9.2l21,-44.9c1.6,-3.4 5.1,-5.7 9.1,-5.9l39,-2.3c2.3,-0.1 4.4,-0.9 6.1,-2.3l28.7,-22.3c5.4,-4.2 13.6,-2.4 16.5,3.5l98.9,201.2c0.4,0.9 0.7,1.8 0.9,2.7l12.2,80.9c0.3,1.9 1.1,3.6 2.4,5l197,215.6c5.8,6.4 0.9,16.5 -8.1,16.5z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="m335.8,838.9c0.8,2.6 0.4,5.4 -1,7.7l-19.3,31.3c-1.9,3.1 -5.4,5.1 -9.2,5.1h-180.6c-8.4,0 -13.5,-8.8 -9,-15.4l116.3,-171.3c0.7,-1 1.1,-2.1 1.4,-3.2l53.3,-227.6c0.6,-2.7 2.4,-5 5,-6.4l72.2,-39.6c2.6,-1.4 4.4,-3.7 5,-6.5l9.7,-42.3c2,-8.8 14.6,-10.7 19.6,-3l3.5,5.6c1.5,2.4 1.9,5.4 0.9,8.1l-65.8,190.2c-0.5,1.5 -0.7,3.1 -0.4,4.7l16.4,91c0.3,1.8 0.1,3.7 -0.7,5.4l-39.1,87.8c-0.9,2.1 -1.1,4.4 -0.4,6.5z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="m424.22,657.05 l1580.54,-0.05c0.54,0 0,292 0,292l-1580.54,-0.03c-10.78,0 -19.46,-8.68 -19.46,-19.46l0,-252.99c0,-10.78 8.68,-19.46 19.46,-19.46z"
android:strokeAlpha="0.49803922"
android:strokeLineJoin="round"
android:strokeWidth="4"
android:fillColor="#3f3f3f"
android:strokeColor="#00000000"
android:fillType="nonZero"
android:fillAlpha="1"
android:strokeLineCap="square"/>
<path
android:pathData="m728.71,710.91l0,19.89l-88.7,0l0,59.3l77.04,0L717.05,810l-77.04,0l0,65.39l89.84,0l0,19.9L616.56,895.29L616.56,791.16l-52.35,-52.35 -0.77,-0.18c5.41,6.64 9.65,14.32 12.69,23.09 3.72,10.39 5.62,21.92 5.7,34.6L581.84,809.87c-0.08,12.67 -1.98,24.25 -5.7,34.72 -3.63,10.39 -8.95,19.35 -15.97,26.87 -6.93,7.43 -15.46,13.26 -25.6,17.49 -7.54,3.1 -15.86,5.07 -24.97,5.89 -3.04,0.27 -6.16,0.42 -9.37,0.45L457.53,895.29l0,-6.55l-0.37,6.82 53.8,53.45l415.85,0c0.24,0 0.47,-0.02 0.69,-0.05L1132.91,948.97L953.5,769.55l-58.39,-58.39 -0.28,0.06 -58.03,184.06l-20.4,0l-44.66,-141.34zM783.29,711.07 L826.41,857.52 850.08,777.86zM509.6,711.29c1.52,0.15 3.01,0.33 4.49,0.54 -1.47,-0.21 -2.97,-0.39 -4.49,-0.54zM481.35,730.04l0,146.11l18.88,0c9.97,-0.08 18.59,-1.81 25.85,-5.19 7.27,-3.46 13.26,-8.15 17.99,-14.07 4.82,-5.91 8.36,-12.88 10.64,-20.91 2.37,-8.03 3.59,-16.73 3.68,-26.11l0,-13.81c-0.08,-9.38 -1.31,-18.04 -3.68,-25.98 -2.37,-8.03 -5.91,-14.95 -10.64,-20.78 -4.73,-5.91 -10.73,-10.56 -17.99,-13.94 -7.27,-3.38 -15.88,-5.15 -25.85,-5.32zM560.17,734.86c0.65,0.69 1.26,1.4 1.88,2.11 -0.61,-0.72 -1.23,-1.42 -1.88,-2.11z"
android:strokeAlpha="1"
android:strokeLineJoin="miter"
android:strokeWidth="1.94642854"
android:fillColor="#000000"
android:strokeColor="#00000000"
android:fillAlpha="0.18431373"
android:strokeLineCap="butt"/>
<path
android:pathData="m457.53,895.28l0,-184.51l42.7,0q19.26,0.25 34.34,6.59 15.21,6.21 25.6,17.49 10.52,11.15 15.97,26.86 5.58,15.59 5.7,34.59l0,13.56q-0.13,19.01 -5.7,34.72 -5.45,15.59 -15.97,26.86 -10.39,11.15 -25.6,17.49 -15.08,6.21 -34.34,6.34zM481.35,730.04l0,146.11l18.88,0q14.95,-0.13 25.85,-5.2 10.9,-5.2 17.99,-14.07 7.22,-8.87 10.64,-20.91 3.55,-12.04 3.67,-26.1l0,-13.81q-0.13,-14.07 -3.67,-25.98 -3.55,-12.04 -10.64,-20.78 -7.1,-8.87 -17.99,-13.94 -10.9,-5.07 -25.85,-5.32z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillAlpha="1"/>
<path
android:pathData="M717.05,810L640.01,810l0,65.39l89.84,0l0,19.9l-113.29,0l0,-184.51l112.15,0l0,20.02L640.01,730.8l0,59.31l77.05,0z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillAlpha="1"/>
<path
android:pathData="m826.41,857.52 l43.59,-146.74l24.96,0l-58.16,184.51L816.4,895.28l-58.29,-184.51l25.09,0z"
android:fillColor="#ffffff"
android:strokeColor="#00000000"
android:fillAlpha="1"/>
</group>
</vector>

View File

@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="1926"
android:viewportHeight="1926">
<path
android:fillColor="#ad2a2a"
android:pathData="M2465,1904v561h-759L704,1463l-120,-120 -1,-1 5,-9 1,-3c9,-15 20,-29 31,-43l16,-19 3,-4 21,-30 3,-6c7,-11 12,-23 18,-35l14,-24 3,-4v-1a234,234 0,0 0,20 -53v-1l1,-3c4,-15 7,-31 13,-46l5,-9v-1c5,-10 11,-19 14, -30 2,-9 3,-19 3,-28v-1,-21l3,-17v-3l4,-17h1v-1c0,-2 -1,-4 -3,-4l8,-8 4,1c13,-4 25,-10 37,-17l8,-5 22,-16 9,-9v-1l5, -11c3,-9 2,-19 2,-29l6,-6 4,2 45,45 10,9 14,14 101,101 17,17 16,17 31,-31 -193,-191 1,-2 1,-2 -1,-3v-1l-1,-1 27,-27 192,-192 2,1a27,27 0,0 1,14 3l6,6c2,2 8,3 9,1l1310,1310z" />
<path
android:fillColor="#fff"
android:pathData="M1082,1260v4l-6,73c0,5 -5,9 -10,9H879c-4,0 -8,-2 -10,-5l-31,-58 -29,-71v-8l48,-94c2,-3 2,-5 1, -8l-24,-65c-1,-2 0,-5 1,-7l39,-84c4,-8 17,-8 20,1l12,38 38,101c1,2 3,5 6,6l80,27c3,1 6,3 7,6l45,135zM852,717c0,-23 15,-43 35,-52l-2,-8c0,-11 13,-20 29,-20h1a38,38 0,0 1,36 -18l3,-2c11,-16 37,-27 68,-27 12,0 23,2 32,4 3,-3 8,-6 14,-6s12,3 14,8c8,-9 22,-16 38,-16 25,0 45,16 45,36l-1,7 3,4c16,6 27,17 27,30 0,14 -15,27 -34,32 -2,0 -3,1 -3,3 0,11 -11,21 -26,22v1c0,22 -41,40 -92,40 -11,0 -21,-1 -31,-3v1c0,9 -12,16 -26,16h-2l2,7c0,14 -18,25 -40,25h-7l-4, 2c-2,7 -8,12 -15,12 -9,0 -16,-7 -16,-16 0,-5 1,-8 4,-11l1,-4c-2,-3 -2,-5 -2,-8 0,-2 -2,-3 -3,-3 -27,-6 -48,-29 -48,-56z" />
<path
android:fillColor="#fff"
android:pathData="M1336,1346h-125c-3,0 -6,-1 -8,-4l-84,-118 -1,-2 -42,-122 -5,-5 -102,-52 -5,-6 -15,-75 -2,-3 -34,-51c-2,-3 -2,-6 -1,-9l21,-45c2,-4 5,-6 9,-6l39,-2c3,0 5,-1 6,-3l29,-22c5,-4 14,-2 17,4l98,201 1,2 13,81 2,5 197,216c6,6 1,16 -8,16zM801,1302c1,3 1,6 -1,8l-19,31c-2,3 -5,5 -9,5H591c-8,0 -13,-8 -9,-15l116,-171 2,-3 53, -228c1,-3 3,-5 5,-6l72,-40c3,-1 5,-4 5,-7l10,-42c2,-9 15,-11 20,-3l3,6c2,2 2,5 1,8l-66,190v5l16,91v5l-40,88v6l22,72z" />
<path
android:fillColor="#3f3f3f"
android:pathData="M886.7,1119.5h1580.5c0.6,0 0,292 0,292H886.7a19.4,19.4 0,0 1,-19.5 -19.5v-253c0,-10.8 8.7,-19.5 19.5,-19.5z"
android:strokeWidth="4"
android:strokeAlpha=".5"
android:strokeColor="#000"
android:strokeLineCap="square"
android:strokeLineJoin="round" />
<path
android:fillAlpha=".2"
android:fillColor="#000"
android:pathData="M1191.2,1173.4v19.9h-88.7v59.3h77v19.9h-77v65.4h89.8v19.9L1079,1357.8v-104.2l-52.3,-52.3 -0.8, -0.2a75.1,75.1 0,0 1,12.7 23,104 104,0 0,1 5.7,34.7v13.5c0,12.7 -2,24.3 -5.7,34.8a72.8,72.8 0,0 1,-41.6 44.4,85.5 85.5,0 0,1 -34.3,6.3L920,1357.8v-6.6l-0.4,6.8 53.8,53.5h622L1416,1232l-58.4,-58.4h-0.3l-58,184.1h -20.4l-44.7,-141.3zM1245.8,1173.5l43,146.5 23.7,-79.7zM972.1,1173.8zM943.8,1192.5v146.1h18.9a62,62 0,0 0,25.8 -5.2,50.3 50.3,0 0,0 18,-14c4.9,-6 8.4,-13 10.7,-21 2.3,-8 3.6,-16.7 3.7,-26v-13.9c-0.1,-9.4 -1.4,-18 -3.7,-26 -2.4,-8 -6,-15 -10.7,-20.7a49.3,49.3 0,0 0,-18 -14,63.4 63.4,0 0,0 -25.8,-5.3zM1022.6,1197.3l2,2.1 -2,-2z"
android:strokeWidth="1.9"
android:strokeColor="#000" />
<path
android:fillColor="#fff"
android:pathData="M920,1357.7v-184.5h42.7q19.3,0.3 34.3,6.6 15.2,6.2 25.6,17.5 10.6,11.2 16,26.9 5.6,15.6 5.7, 34.6v13.5q-0.1,19 -5.7,34.7 -5.4,15.6 -16,26.9 -10.4,11.2 -25.6,17.5 -15,6.2 -34.3,6.3zM943.8,1192.5v146.1h18.9q15, -0.1 25.8,-5.2 11,-5.2 18,-14 7.3,-9 10.7,-21 3.5,-12 3.6,-26v-13.9q0,-14 -3.6,-26t-10.7,-20.7q-7,-9 -18,-14 -10.9,-5 -25.8,-5.3zM1179.5,1272.5h-77v65.4h89.8v19.9L1079,1357.8v-184.6h112.2v20h-88.7v59.4h77zM1288.9, 1320l43.6,-146.8h25l-58.2,184.6h-20.4l-58.3,-184.6h25z"
android:strokeWidth="1"
android:strokeColor="#000" />
</vector>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
<background android:drawable="@color/colorPrimary" />
<foreground android:drawable="@drawable/ic_launcher_foreground_dev" />
</adaptive-icon>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
<background android:drawable="@color/colorPrimary" />
<foreground android:drawable="@drawable/ic_launcher_foreground_dev" />
</adaptive-icon>

View File

@ -1,3 +0,0 @@
<resources>
<string name="app_name">Wulkanowy DEV</string>
</resources>

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#D32F2F</color>
</resources>

View File

@ -43,6 +43,7 @@
android:name=".ui.modules.message.send.SendMessageActivity"
android:configChanges="orientation|screenSize"
android:label="@string/send_message_title"
android:windowSoftInputMode="adjustResize"
android:theme="@style/WulkanowyTheme.NoActionBar" />
<activity
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"

View File

@ -5,14 +5,17 @@ import android.util.Log.INFO
import android.util.Log.VERBOSE
import androidx.multidex.MultiDex
import androidx.work.Configuration
import androidx.work.WorkManager
import com.jakewharton.threetenabp.AndroidThreeTen
import com.yariksoffice.lingver.Lingver
import dagger.android.AndroidInjector
import dagger.android.support.DaggerApplication
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.utils.Log
import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.db.SharedPrefProvider.Companion.APP_VERSION_CODE_KEY
import io.github.wulkanowy.di.DaggerAppComponent
import io.github.wulkanowy.services.sync.SyncWorkerFactory
import io.github.wulkanowy.ui.base.ThemeManager
import io.github.wulkanowy.utils.ActivityLifecycleLogger
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.CrashlyticsTree
@ -24,11 +27,17 @@ import timber.log.Timber
import java.io.IOException
import javax.inject.Inject
class WulkanowyApp : DaggerApplication() {
class WulkanowyApp : DaggerApplication(), Configuration.Provider {
@Inject
lateinit var workerFactory: SyncWorkerFactory
@Inject
lateinit var themeManager: ThemeManager
@Inject
lateinit var sharedPrefProvider: SharedPrefProvider
@Inject
lateinit var appInfo: AppInfo
@ -41,20 +50,14 @@ class WulkanowyApp : DaggerApplication() {
super.onCreate()
AndroidThreeTen.init(this)
RxJavaPlugins.setErrorHandler(::onError)
Lingver.init(this)
themeManager.applyDefaultTheme()
migrateSharedPreferences()
initWorkManager()
initLogging()
initCrashlytics(this, appInfo)
}
private fun initWorkManager() {
WorkManager.initialize(this,
Configuration.Builder()
.setWorkerFactory(workerFactory)
.setMinimumLoggingLevel(if (appInfo.isDebug) VERBOSE else INFO)
.build())
}
private fun initLogging() {
if (appInfo.isDebug) {
Timber.plant(DebugLogTree())
@ -65,6 +68,13 @@ class WulkanowyApp : DaggerApplication() {
registerActivityLifecycleCallbacks(ActivityLifecycleLogger())
}
private fun migrateSharedPreferences() {
if (sharedPrefProvider.getLong(APP_VERSION_CODE_KEY, -1) < 48) { // #596
sharedPrefProvider.delete(getString(R.string.pref_key_grade_modifier_plus))
sharedPrefProvider.delete(getString(R.string.pref_key_grade_modifier_minus))
}
}
private fun onError(error: Throwable) {
//RxJava's too deep stack traces may cause SOE on older android devices
val cause = error.cause
@ -76,4 +86,9 @@ class WulkanowyApp : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.factory().create(this)
}
override fun getWorkManagerConfiguration() = Configuration.Builder()
.setWorkerFactory(workerFactory)
.setMinimumLoggingLevel(if (appInfo.isDebug) VERBOSE else INFO)
.build()
}

View File

@ -85,6 +85,10 @@ internal class RepositoryModule {
@Provides
fun provideGradeStatisticsDao(database: AppDatabase) = database.gradeStatistics
@Singleton
@Provides
fun provideGradePointsStatisticsDao(database: AppDatabase) = database.gradePointsStatistics
@Singleton
@Provides
fun provideMessagesDao(database: AppDatabase) = database.messagesDao
@ -136,4 +140,12 @@ internal class RepositoryModule {
@Singleton
@Provides
fun provideMobileDevicesDao(database: AppDatabase) = database.mobileDeviceDao
@Singleton
@Provides
fun provideTeacherDao(database: AppDatabase) = database.teacherDao
@Singleton
@Provides
fun provideSchoolInfoDao(database: AppDatabase) = database.schoolDao
}

View File

@ -6,11 +6,13 @@ import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.RoomDatabase.JournalMode.TRUNCATE
import androidx.room.TypeConverters
import androidx.room.migration.Migration
import io.github.wulkanowy.data.db.dao.AttendanceDao
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
import io.github.wulkanowy.data.db.dao.ExamDao
import io.github.wulkanowy.data.db.dao.GradeDao
import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao
import io.github.wulkanowy.data.db.dao.GradeStatisticsDao
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
import io.github.wulkanowy.data.db.dao.HomeworkDao
@ -20,15 +22,18 @@ import io.github.wulkanowy.data.db.dao.MobileDeviceDao
import io.github.wulkanowy.data.db.dao.NoteDao
import io.github.wulkanowy.data.db.dao.RecipientDao
import io.github.wulkanowy.data.db.dao.ReportingUnitDao
import io.github.wulkanowy.data.db.dao.SchoolDao
import io.github.wulkanowy.data.db.dao.SemesterDao
import io.github.wulkanowy.data.db.dao.StudentDao
import io.github.wulkanowy.data.db.dao.SubjectDao
import io.github.wulkanowy.data.db.dao.TeacherDao
import io.github.wulkanowy.data.db.dao.TimetableDao
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Homework
@ -38,9 +43,11 @@ import io.github.wulkanowy.data.db.entities.MobileDevice
import io.github.wulkanowy.data.db.entities.Note
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.data.db.entities.School
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Subject
import io.github.wulkanowy.data.db.entities.Teacher
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.db.migrations.Migration10
import io.github.wulkanowy.data.db.migrations.Migration11
@ -48,6 +55,9 @@ import io.github.wulkanowy.data.db.migrations.Migration12
import io.github.wulkanowy.data.db.migrations.Migration13
import io.github.wulkanowy.data.db.migrations.Migration14
import io.github.wulkanowy.data.db.migrations.Migration15
import io.github.wulkanowy.data.db.migrations.Migration16
import io.github.wulkanowy.data.db.migrations.Migration17
import io.github.wulkanowy.data.db.migrations.Migration18
import io.github.wulkanowy.data.db.migrations.Migration2
import io.github.wulkanowy.data.db.migrations.Migration3
import io.github.wulkanowy.data.db.migrations.Migration4
@ -70,6 +80,7 @@ import javax.inject.Singleton
Grade::class,
GradeSummary::class,
GradeStatistics::class,
GradePointsStatistics::class,
Message::class,
Note::class,
Homework::class,
@ -78,7 +89,9 @@ import javax.inject.Singleton
CompletedLesson::class,
ReportingUnit::class,
Recipient::class,
MobileDevice::class
MobileDevice::class,
Teacher::class,
School::class
],
version = AppDatabase.VERSION_SCHEMA,
exportSchema = true
@ -87,29 +100,36 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
const val VERSION_SCHEMA = 15
const val VERSION_SCHEMA = 18
fun getMigrations(): Array<Migration> {
return arrayOf(
Migration2(),
Migration3(),
Migration4(),
Migration5(),
Migration6(),
Migration7(),
Migration8(),
Migration9(),
Migration10(),
Migration11(),
Migration12(),
Migration13(),
Migration14(),
Migration15(),
Migration16(),
Migration17(),
Migration18()
)
}
fun newInstance(context: Context): AppDatabase {
return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database")
.setJournalMode(TRUNCATE)
.fallbackToDestructiveMigrationFrom(VERSION_SCHEMA + 1)
.fallbackToDestructiveMigrationOnDowngrade()
.addMigrations(
Migration2(),
Migration3(),
Migration4(),
Migration5(),
Migration6(),
Migration7(),
Migration8(),
Migration9(),
Migration10(),
Migration11(),
Migration12(),
Migration13(),
Migration14(),
Migration15()
)
.addMigrations(*getMigrations())
.build()
}
}
@ -132,6 +152,8 @@ abstract class AppDatabase : RoomDatabase() {
abstract val gradeStatistics: GradeStatisticsDao
abstract val gradePointsStatistics: GradePointsStatisticsDao
abstract val messagesDao: MessagesDao
abstract val noteDao: NoteDao
@ -149,4 +171,8 @@ abstract class AppDatabase : RoomDatabase() {
abstract val recipientDao: RecipientDao
abstract val mobileDeviceDao: MobileDeviceDao
abstract val teacherDao: TeacherDao
abstract val schoolDao: SchoolDao
}

View File

@ -1,18 +1,19 @@
package io.github.wulkanowy.data.db
import android.annotation.SuppressLint
import android.content.SharedPreferences
import androidx.core.content.edit
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
@SuppressLint("ApplySharedPref")
class SharedPrefHelper @Inject constructor(private val sharedPref: SharedPreferences) {
class SharedPrefProvider @Inject constructor(private val sharedPref: SharedPreferences) {
companion object {
const val APP_VERSION_CODE_KEY = "app_version_code"
}
fun putLong(key: String, value: Long, sync: Boolean = false) {
sharedPref.edit().putLong(key, value).apply {
if (sync) commit() else apply()
}
sharedPref.edit(sync) { putLong(key, value) }
}
fun getLong(key: String, defaultValue: Long) = sharedPref.getLong(key, defaultValue)

View File

@ -1,8 +1,6 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Attendance
import io.reactivex.Maybe
@ -11,13 +9,7 @@ import javax.inject.Singleton
@Singleton
@Dao
interface AttendanceDao {
@Insert
fun insertAll(exams: List<Attendance>): List<Long>
@Delete
fun deleteAll(exams: List<Attendance>)
interface AttendanceDao : BaseDao<Attendance> {
@Query("SELECT * FROM Attendance WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<Attendance>>

View File

@ -1,20 +1,12 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.reactivex.Maybe
@Dao
interface AttendanceSummaryDao {
@Insert
fun insertAll(exams: List<AttendanceSummary>): List<Long>
@Delete
fun deleteAll(exams: List<AttendanceSummary>)
interface AttendanceSummaryDao : BaseDao<AttendanceSummary> {
@Query("SELECT * FROM AttendanceSummary WHERE diary_id = :diaryId AND student_id = :studentId AND subject_id = :subjectId")
fun loadAll(diaryId: Int, studentId: Int, subjectId: Int): Maybe<List<AttendanceSummary>>

View File

@ -0,0 +1,17 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Update
interface BaseDao<T> {
@Insert
fun insertAll(items: List<T>): List<Long>
@Update
fun updateAll(items: List<T>)
@Delete
fun deleteAll(items: List<T>)
}

View File

@ -1,8 +1,6 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.reactivex.Maybe
@ -11,13 +9,7 @@ import javax.inject.Singleton
@Singleton
@Dao
interface CompletedLessonsDao {
@Insert
fun insertAll(exams: List<CompletedLesson>)
@Delete
fun deleteAll(exams: List<CompletedLesson>)
interface CompletedLessonsDao : BaseDao<CompletedLesson> {
@Query("SELECT * FROM CompletedLesson WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<CompletedLesson>>

View File

@ -1,8 +1,6 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Exam
import io.reactivex.Maybe
@ -11,13 +9,7 @@ import javax.inject.Singleton
@Singleton
@Dao
interface ExamDao {
@Insert
fun insertAll(exams: List<Exam>): List<Long>
@Delete
fun deleteAll(exams: List<Exam>)
interface ExamDao : BaseDao<Exam> {
@Query("SELECT * FROM Exams WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<Exam>>

View File

@ -1,26 +1,14 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import io.github.wulkanowy.data.db.entities.Grade
import io.reactivex.Maybe
import javax.inject.Singleton
@Singleton
@Dao
interface GradeDao {
@Insert
fun insertAll(grades: List<Grade>)
@Update
fun updateAll(grade: List<Grade>)
@Delete
fun deleteAll(grades: List<Grade>)
interface GradeDao : BaseDao<Grade> {
@Query("SELECT * FROM Grades WHERE semester_id = :semesterId AND student_id = :studentId")
fun loadAll(semesterId: Int, studentId: Int): Maybe<List<Grade>>

View File

@ -0,0 +1,18 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.reactivex.Maybe
import javax.inject.Singleton
@Singleton
@Dao
interface GradePointsStatisticsDao : BaseDao<GradePointsStatistics> {
@Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName")
fun loadSubject(semesterId: Int, studentId: Int, subjectName: String): Maybe<GradePointsStatistics>
@Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId")
fun loadAll(semesterId: Int, studentId: Int): Maybe<List<GradePointsStatistics>>
}

View File

@ -1,8 +1,6 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.reactivex.Maybe
@ -10,13 +8,7 @@ import javax.inject.Singleton
@Singleton
@Dao
interface GradeStatisticsDao {
@Insert
fun insertAll(gradesStatistics: List<GradeStatistics>)
@Delete
fun deleteAll(gradesStatistics: List<GradeStatistics>)
interface GradeStatisticsDao : BaseDao<GradeStatistics> {
@Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName AND is_semester = :isSemester")
fun loadSubject(semesterId: Int, studentId: Int, subjectName: String, isSemester: Boolean): Maybe<List<GradeStatistics>>

View File

@ -1,8 +1,6 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.reactivex.Maybe
@ -10,13 +8,7 @@ import javax.inject.Singleton
@Singleton
@Dao
interface GradeSummaryDao {
@Insert
fun insertAll(gradesSummary: List<GradeSummary>)
@Delete
fun deleteAll(gradesSummary: List<GradeSummary>)
interface GradeSummaryDao : BaseDao<GradeSummary> {
@Query("SELECT * FROM GradesSummary WHERE student_id = :studentId AND semester_id = :semesterId")
fun loadAll(semesterId: Int, studentId: Int): Maybe<List<GradeSummary>>

View File

@ -1,8 +1,6 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Homework
import io.reactivex.Maybe
@ -11,13 +9,7 @@ import javax.inject.Singleton
@Singleton
@Dao
interface HomeworkDao {
@Insert
fun insertAll(homework: List<Homework>)
@Delete
fun deleteAll(homework: List<Homework>)
interface HomeworkDao : BaseDao<Homework> {
@Query("SELECT * FROM Homework WHERE semester_id = :semesterId AND student_id = :studentId AND date >= :from AND date <= :end")
fun loadAll(semesterId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<Homework>>

View File

@ -1,10 +1,7 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.reactivex.Maybe
import org.threeten.bp.LocalDate
@ -12,18 +9,8 @@ import javax.inject.Singleton
@Singleton
@Dao
interface LuckyNumberDao {
@Insert
fun insert(luckyNumber: LuckyNumber)
@Update
fun update(luckyNumber: LuckyNumber)
@Delete
fun delete(luckyNumber: LuckyNumber)
interface LuckyNumberDao : BaseDao<LuckyNumber> {
@Query("SELECT * FROM LuckyNumbers WHERE student_id = :studentId AND date = :date")
fun load(studentId: Int, date: LocalDate): Maybe<LuckyNumber>
}

View File

@ -1,24 +1,12 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import io.github.wulkanowy.data.db.entities.Message
import io.reactivex.Maybe
@Dao
interface MessagesDao {
@Insert
fun insertAll(messages: List<Message>)
@Delete
fun deleteAll(messages: List<Message>)
@Update
fun updateAll(messages: List<Message>)
interface MessagesDao : BaseDao<Message> {
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder AND removed = 0 ORDER BY date DESC")
fun loadAll(studentId: Int, folder: Int): Maybe<List<Message>>

View File

@ -1,20 +1,12 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.MobileDevice
import io.reactivex.Maybe
@Dao
interface MobileDeviceDao {
@Insert
fun insertAll(devices: List<MobileDevice>)
@Delete
fun deleteAll(devices: List<MobileDevice>)
interface MobileDeviceDao : BaseDao<MobileDevice> {
@Query("SELECT * FROM MobileDevices WHERE student_id = :studentId ORDER BY date DESC")
fun loadAll(studentId: Int): Maybe<List<MobileDevice>>

View File

@ -1,28 +1,15 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import io.github.wulkanowy.data.db.entities.Note
import io.reactivex.Maybe
import javax.inject.Singleton
@Singleton
@Dao
interface NoteDao {
@Insert
fun insertAll(notes: List<Note>)
@Update
fun updateAll(notes: List<Note>)
@Delete
fun deleteAll(notes: List<Note>)
interface NoteDao : BaseDao<Note> {
@Query("SELECT * FROM Notes WHERE student_id = :studentId")
fun loadAll(studentId: Int): Maybe<List<Note>>
}

View File

@ -1,8 +1,6 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Recipient
import io.reactivex.Maybe
@ -10,13 +8,7 @@ import javax.inject.Singleton
@Singleton
@Dao
interface RecipientDao {
@Insert
fun insertAll(messages: List<Recipient>)
@Delete
fun deleteAll(messages: List<Recipient>)
interface RecipientDao : BaseDao<Recipient> {
@Query("SELECT * FROM Recipients WHERE student_id = :studentId AND role = :role AND unit_id = :unitId")
fun load(studentId: Int, role: Int, unitId: Int): Maybe<List<Recipient>>

View File

@ -1,8 +1,6 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.reactivex.Maybe
@ -10,13 +8,7 @@ import javax.inject.Singleton
@Singleton
@Dao
interface ReportingUnitDao {
@Insert
fun insertAll(reportingUnits: List<ReportingUnit>)
@Delete
fun deleteAll(reportingUnits: List<ReportingUnit>)
interface ReportingUnitDao : BaseDao<ReportingUnit> {
@Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId")
fun load(studentId: Int): Maybe<List<ReportingUnit>>

View File

@ -0,0 +1,15 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.School
import io.reactivex.Maybe
import javax.inject.Singleton
@Singleton
@Dao
interface SchoolDao : BaseDao<School> {
@Query("SELECT * FROM School WHERE student_id = :studentId AND class_id = :classId")
fun load(studentId: Int, classId: Int): Maybe<School>
}

View File

@ -1,8 +1,6 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Maybe
@ -10,13 +8,7 @@ import javax.inject.Singleton
@Singleton
@Dao
interface SemesterDao {
@Insert
fun insertAll(semester: List<Semester>)
@Delete
fun deleteAll(semester: List<Semester>)
interface SemesterDao : BaseDao<Semester> {
@Query("SELECT * FROM Semesters WHERE student_id = :studentId AND class_id = :classId")
fun loadAll(studentId: Int, classId: Int): Maybe<List<Semester>>

View File

@ -1,20 +1,12 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Subject
import io.reactivex.Maybe
@Dao
interface SubjectDao {
@Insert
fun insertAll(subjects: List<Subject>): List<Long>
@Delete
fun deleteAll(subjects: List<Subject>)
interface SubjectDao : BaseDao<Subject> {
@Query("SELECT * FROM Subjects WHERE diary_id = :diaryId AND student_id = :studentId")
fun loadAll(diaryId: Int, studentId: Int): Maybe<List<Subject>>

View File

@ -0,0 +1,15 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Teacher
import io.reactivex.Maybe
import javax.inject.Singleton
@Singleton
@Dao
interface TeacherDao : BaseDao<Teacher> {
@Query("SELECT * FROM Teachers WHERE student_id = :studentId AND class_id = :classId")
fun loadAll(studentId: Int, classId: Int): Maybe<List<Teacher>>
}

View File

@ -1,8 +1,6 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Timetable
import io.reactivex.Maybe
@ -11,13 +9,7 @@ import javax.inject.Singleton
@Singleton
@Dao
interface TimetableDao {
@Insert
fun insertAll(exams: List<Timetable>): List<Long>
@Delete
fun deleteAll(exams: List<Timetable>)
interface TimetableDao : BaseDao<Timetable> {
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<Timetable>>

View File

@ -0,0 +1,24 @@
package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "GradesPointsStatistics")
data class GradePointsStatistics(
@ColumnInfo(name = "student_id")
val studentId: Int,
@ColumnInfo(name = "semester_id")
val semesterId: Int,
val subject: String,
val others: Double,
val student: Double
) {
@PrimaryKey(autoGenerate = true)
var id: Long = 0
}

View File

@ -0,0 +1,30 @@
package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
@Entity(tableName = "School")
data class School(
@ColumnInfo(name = "student_id")
val studentId: Int,
@ColumnInfo(name = "class_id")
val classId: Int,
val name: String,
val address: String,
val contact: String,
val headmaster: String,
val pedagogue: String
) : Serializable {
@PrimaryKey(autoGenerate = true)
var id: Long = 0
}

View File

@ -0,0 +1,27 @@
package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
@Entity(tableName = "Teachers")
data class Teacher(
@ColumnInfo(name = "student_id")
val studentId: Int,
@ColumnInfo(name = "class_id")
val classId: Int,
val subject: String,
val name: String,
@ColumnInfo(name = "short_name")
val shortName: String
) : Serializable {
@PrimaryKey(autoGenerate = true)
var id: Long = 0
}

View File

@ -0,0 +1,20 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration16 : Migration(15, 16) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("""
CREATE TABLE IF NOT EXISTS Teachers (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
student_id INTEGER NOT NULL,
class_id INTEGER NOT NULL,
subject TEXT NOT NULL,
name TEXT NOT NULL,
short_name TEXT NOT NULL
)
""")
}
}

View File

@ -0,0 +1,29 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration17 : Migration(16, 17) {
override fun migrate(database: SupportSQLiteDatabase) {
createGradesPointsStatisticsTable(database)
truncateSemestersTable(database)
}
private fun createGradesPointsStatisticsTable(database: SupportSQLiteDatabase) {
database.execSQL("""
CREATE TABLE IF NOT EXISTS GradesPointsStatistics(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
student_id INTEGER NOT NULL,
semester_id INTEGER NOT NULL,
subject TEXT NOT NULL,
others REAL NOT NULL,
student REAL NOT NULL
)
""")
}
private fun truncateSemestersTable(database: SupportSQLiteDatabase) {
database.execSQL("DELETE FROM Semesters")
}
}

View File

@ -0,0 +1,22 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration18 : Migration(17, 18) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("""
CREATE TABLE IF NOT EXISTS School (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
student_id INTEGER NOT NULL,
class_id INTEGER NOT NULL,
name TEXT NOT NULL,
address TEXT NOT NULL,
contact TEXT NOT NULL,
headmaster TEXT NOT NULL,
pedagogue TEXT NOT NULL
)
""")
}
}

View File

@ -1,6 +1,8 @@
package io.github.wulkanowy.data.repositories.gradestatistics
import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao
import io.github.wulkanowy.data.db.dao.GradeStatisticsDao
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Maybe
@ -8,27 +10,57 @@ import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class GradeStatisticsLocal @Inject constructor(private val gradeStatisticsDb: GradeStatisticsDao) {
class GradeStatisticsLocal @Inject constructor(
private val gradeStatisticsDb: GradeStatisticsDao,
private val gradePointsStatisticsDb: GradePointsStatisticsDao
) {
fun getGradesStatistics(semester: Semester, isSemester: Boolean): Maybe<List<GradeStatistics>> {
return gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester)
.filter { !it.isEmpty() }
return gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester).filter { it.isNotEmpty() }
}
fun getGradesPointsStatistics(semester: Semester): Maybe<List<GradePointsStatistics>> {
return gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId).filter { it.isNotEmpty() }
}
fun getGradesStatistics(semester: Semester, isSemester: Boolean, subjectName: String): Maybe<List<GradeStatistics>> {
return (if ("Wszystkie" == subjectName) gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester).map { list ->
list.groupBy { it.grade }.map {
GradeStatistics(semester.studentId, semester.semesterId, subjectName, it.key, it.value.fold(0) { acc, e -> acc + e.amount }, false)
return when (subjectName) {
"Wszystkie" -> gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester).map { list ->
list.groupBy { it.grade }.map {
GradeStatistics(semester.studentId, semester.semesterId, subjectName, it.key,
it.value.fold(0) { acc, e -> acc + e.amount }, false)
}
}
else -> gradeStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName, isSemester)
}.filter { it.isNotEmpty() }
}
fun getGradesPointsStatistics(semester: Semester, subjectName: String): Maybe<GradePointsStatistics> {
return when (subjectName) {
"Wszystkie" -> gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId).flatMap { list ->
if (list.isEmpty()) return@flatMap Maybe.empty<GradePointsStatistics>()
Maybe.just(GradePointsStatistics(semester.studentId, semester.semesterId, subjectName,
list.fold(.0) { acc, e -> acc + e.others },
list.fold(.0) { acc, e -> acc + e.student })
)
}
else -> gradePointsStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName)
}
else gradeStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName, isSemester)).filter { !it.isEmpty() }
}
fun saveGradesStatistics(gradesStatistics: List<GradeStatistics>) {
gradeStatisticsDb.insertAll(gradesStatistics)
}
fun saveGradesPointsStatistics(gradePointsStatistics: List<GradePointsStatistics>) {
gradePointsStatisticsDb.insertAll(gradePointsStatistics)
}
fun deleteGradesStatistics(gradesStatistics: List<GradeStatistics>) {
gradeStatisticsDb.deleteAll(gradesStatistics)
}
fun deleteGradesPointsStatistics(gradesPointsStatistics: List<GradePointsStatistics>) {
gradePointsStatisticsDb.deleteAll(gradesPointsStatistics)
}
}

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.data.repositories.gradestatistics
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Single
@ -12,7 +13,10 @@ class GradeStatisticsRemote @Inject constructor(private val api: Api) {
fun getGradeStatistics(semester: Semester, isSemester: Boolean): Single<List<GradeStatistics>> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { it.getGradesStatistics(semester.semesterId, isSemester) }
.flatMap {
if (isSemester) it.getGradesAnnualStatistics(semester.semesterId)
else it.getGradesPartialStatistics(semester.semesterId)
}
.map { gradeStatistics ->
gradeStatistics.map {
GradeStatistics(
@ -26,4 +30,20 @@ class GradeStatisticsRemote @Inject constructor(private val api: Api) {
}
}
}
fun getGradePointsStatistics(semester: Semester): Single<List<GradePointsStatistics>> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { it.getGradesPointsStatistics(semester.semesterId) }
.map { gradePointsStatistics ->
gradePointsStatistics.map {
GradePointsStatistics(
semesterId = semester.semesterId,
studentId = semester.studentId,
subject = it.subject,
others = it.others,
student = it.student
)
}
}
}
}

View File

@ -2,9 +2,11 @@ package io.github.wulkanowy.data.repositories.gradestatistics
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Maybe
import io.reactivex.Single
import java.net.UnknownHostException
import javax.inject.Inject
@ -31,4 +33,19 @@ class GradeStatisticsRepository @Inject constructor(
}
}.flatMap { local.getGradesStatistics(semester, isSemester, subjectName).toSingle(emptyList()) })
}
fun getGradesPointsStatistics(semester: Semester, subjectName: String, forceRefresh: Boolean): Maybe<GradePointsStatistics> {
return local.getGradesPointsStatistics(semester, subjectName).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMapMaybe {
if (it) remote.getGradePointsStatistics(semester).toMaybe()
else Maybe.error(UnknownHostException())
}.flatMap { new ->
local.getGradesPointsStatistics(semester).defaultIfEmpty(emptyList())
.doOnSuccess { old ->
local.deleteGradesPointsStatistics(old.uniqueSubtract(new))
local.saveGradesPointsStatistics(new.uniqueSubtract(old))
}
}.flatMap { local.getGradesPointsStatistics(semester, subjectName) })
}
}

View File

@ -12,15 +12,15 @@ import javax.inject.Singleton
class LuckyNumberLocal @Inject constructor(private val luckyNumberDb: LuckyNumberDao) {
fun saveLuckyNumber(luckyNumber: LuckyNumber) {
luckyNumberDb.insert(luckyNumber)
luckyNumberDb.insertAll(listOf(luckyNumber))
}
fun updateLuckyNumber(luckyNumber: LuckyNumber) {
luckyNumberDb.update(luckyNumber)
luckyNumberDb.updateAll(listOf(luckyNumber))
}
fun deleteLuckyNumber(luckyNumber: LuckyNumber) {
luckyNumberDb.delete(luckyNumber)
luckyNumberDb.deleteAll(listOf(luckyNumber))
}
fun getLuckyNumber(semester: Semester, date: LocalDate): Maybe<LuckyNumber> {

View File

@ -11,7 +11,6 @@ import io.reactivex.Single
import org.threeten.bp.LocalDateTime.now
import javax.inject.Inject
import javax.inject.Singleton
import io.github.wulkanowy.api.messages.Message as ApiMessage
import io.github.wulkanowy.api.messages.Recipient as ApiRecipient
@Singleton

View File

@ -3,7 +3,6 @@ package io.github.wulkanowy.data.repositories.mobiledevice
import io.github.wulkanowy.data.db.dao.MobileDeviceDao
import io.github.wulkanowy.data.db.entities.MobileDevice
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.reactivex.Maybe
import javax.inject.Inject
import javax.inject.Singleton

View File

@ -12,52 +12,64 @@ class PreferencesRepository @Inject constructor(
val context: Context
) {
val startMenuIndex: Int
get() = sharedPref.getString(context.getString(R.string.pref_key_start_menu), "0")?.toIntOrNull() ?: 0
get() = getString(R.string.pref_key_start_menu, R.string.pref_default_startup).toInt()
val isShowPresent: Boolean
get() = sharedPref.getBoolean(context.getString(R.string.pref_key_attendance_present), true)
get() = getBoolean(R.string.pref_key_attendance_present, R.bool.pref_default_attendance_present)
val gradeAverageMode: String
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_average_mode), "only_one_semester") ?: "only_one_semester"
get() = getString(R.string.pref_key_grade_average_mode, R.string.pref_default_grade_average_mode)
val gradeAverageForceCalc: Boolean
get() = sharedPref.getBoolean(context.getString(R.string.pref_key_grade_average_force_calc), false)
get() = getBoolean(R.string.pref_key_grade_average_force_calc, R.bool.pref_default_grade_average_force_calc)
val isGradeExpandable: Boolean
get() = !sharedPref.getBoolean(context.getString(R.string.pref_key_expand_grade), false)
get() = !getBoolean(R.string.pref_key_expand_grade, R.bool.pref_default_expand_grade)
val appThemeKey: String = context.getString(R.string.pref_key_app_theme)
val appThemeKey = context.getString(R.string.pref_key_app_theme)
val appTheme: String
get() = sharedPref.getString(appThemeKey, "light") ?: "light"
get() = getString(appThemeKey, R.string.pref_default_app_theme)
val gradeColorTheme: String
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_color_scheme), "vulcan") ?: "vulcan"
get() = getString(R.string.pref_key_grade_color_scheme, R.string.pref_default_grade_color_scheme)
val serviceEnableKey: String = context.getString(R.string.pref_key_services_enable)
val appLanguageKey = context.getString(R.string.pref_key_app_language)
val appLanguage
get() = getString(appLanguageKey, R.string.pref_default_app_language)
val serviceEnableKey = context.getString(R.string.pref_key_services_enable)
val isServiceEnabled: Boolean
get() = sharedPref.getBoolean(serviceEnableKey, true)
get() = getBoolean(serviceEnableKey, R.bool.pref_default_services_enable)
val servicesIntervalKey: String = context.getString(R.string.pref_key_services_interval)
val servicesIntervalKey = context.getString(R.string.pref_key_services_interval)
val servicesInterval: Long
get() = sharedPref.getString(servicesIntervalKey, "60")?.toLongOrNull() ?: 60
get() = getString(servicesIntervalKey, R.string.pref_default_services_interval).toLong()
val servicesOnlyWifiKey: String = context.getString(R.string.pref_key_services_wifi_only)
val servicesOnlyWifiKey = context.getString(R.string.pref_key_services_wifi_only)
val isServicesOnlyWifi: Boolean
get() = sharedPref.getBoolean(servicesOnlyWifiKey, false)
get() = getBoolean(servicesOnlyWifiKey, R.bool.pref_default_services_wifi_only)
val isNotificationsEnable: Boolean
get() = sharedPref.getBoolean(context.getString(R.string.pref_key_notifications_enable), true)
get() = getBoolean(R.string.pref_key_notifications_enable, R.bool.pref_default_notifications_enable)
val isDebugNotificationEnableKey: String = context.getString(R.string.pref_key_notification_debug)
val isDebugNotificationEnableKey = context.getString(R.string.pref_key_notification_debug)
val isDebugNotificationEnable: Boolean
get() = sharedPref.getBoolean(isDebugNotificationEnableKey, false)
get() = getBoolean(isDebugNotificationEnableKey, R.bool.pref_default_notification_debug)
val gradePlusModifier: Double
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_plus), "0.0")?.toDouble() ?: 0.0
get() = getString(R.string.pref_key_grade_modifier_plus, R.string.pref_default_grade_modifier_plus).toDouble()
val gradeMinusModifier: Double
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_modifier_minus), "0.0")?.toDouble() ?: 0.0
get() = getString(R.string.pref_key_grade_modifier_minus, R.string.pref_default_grade_modifier_minus).toDouble()
val fillMessageContent: Boolean
get() = sharedPref.getBoolean(context.getString(R.string.pref_key_fill_message_content), true)
get() = getBoolean(R.string.pref_key_fill_message_content, R.bool.pref_default_fill_message_content)
private fun getString(id: Int, default: Int) = getString(context.getString(id), default)
private fun getString(id: String, default: Int) = sharedPref.getString(id, context.getString(default)) ?: context.getString(default)
private fun getBoolean(id: Int, default: Int) = getBoolean(context.getString(id), default)
private fun getBoolean(id: String, default: Int) = sharedPref.getBoolean(id, context.resources.getBoolean(default))
}

View File

@ -15,7 +15,7 @@ class RecipientLocal @Inject constructor(private val recipientDb: RecipientDao)
return recipientDb.load(student.studentId, role, unit.realId).filter { !it.isEmpty() }
}
fun saveRecipients(recipients: List<Recipient>) {
fun saveRecipients(recipients: List<Recipient>): List<Long> {
return recipientDb.insertAll(recipients)
}

View File

@ -18,7 +18,7 @@ class ReportingUnitLocal @Inject constructor(private val reportingUnitDb: Report
return reportingUnitDb.loadOne(student.studentId, unitId)
}
fun saveReportingUnits(reportingUnits: List<ReportingUnit>) {
fun saveReportingUnits(reportingUnits: List<ReportingUnit>): List<Long> {
return reportingUnitDb.insertAll(reportingUnits)
}

View File

@ -0,0 +1,22 @@
package io.github.wulkanowy.data.repositories.school
import io.github.wulkanowy.data.db.dao.SchoolDao
import io.github.wulkanowy.data.db.entities.School
import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Maybe
import javax.inject.Inject
class SchoolLocal @Inject constructor(private val schoolDb: SchoolDao) {
fun saveSchool(school: School) {
schoolDb.insertAll(listOf(school))
}
fun deleteSchool(school: School) {
schoolDb.deleteAll(listOf(school))
}
fun getSchool(semester: Semester): Maybe<School> {
return schoolDb.load(semester.studentId, semester.classId)
}
}

View File

@ -0,0 +1,26 @@
package io.github.wulkanowy.data.repositories.school
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.School
import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Single
import javax.inject.Inject
class SchoolRemote @Inject constructor(private val api: Api) {
fun getSchoolInfo(semester: Semester): Single<School> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { it.getSchool() }
.map {
School(
studentId = semester.studentId,
classId = semester.classId,
name = it.name,
address = it.address,
contact = it.contact,
headmaster = it.headmaster,
pedagogue = it.pedagogue
)
}
}
}

View File

@ -0,0 +1,41 @@
package io.github.wulkanowy.data.repositories.school
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.School
import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Maybe
import io.reactivex.Single
import java.net.UnknownHostException
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SchoolRepository @Inject constructor(
private val settings: InternetObservingSettings,
private val local: SchoolLocal,
private val remote: SchoolRemote
) {
fun getSchoolInfo(semester: Semester, forceRefresh: Boolean = false): Maybe<School> {
return local.getSchool(semester).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
if (it) remote.getSchoolInfo(semester)
else Single.error(UnknownHostException())
}.flatMapMaybe { new ->
local.getSchool(semester)
.doOnSuccess { old ->
if (new != old) {
local.deleteSchool(old)
local.saveSchool(new)
}
}
.doOnComplete {
local.saveSchool(new)
}
}.flatMap({ local.getSchool(semester) }, { Maybe.error(it) },
{ local.getSchool(semester) })
)
}
}

View File

@ -24,7 +24,7 @@ class StudentLocal @Inject constructor(
fun getStudents(decryptPass: Boolean): Maybe<List<Student>> {
return studentDb.loadAll()
.map { list -> list.map { it.apply { if (decryptPass) password = decrypt(password) } } }
.filter { !it.isEmpty() }
.filter { it.isNotEmpty() }
}
fun getCurrentStudent(decryptPass: Boolean): Maybe<Student> {

View File

@ -0,0 +1,22 @@
package io.github.wulkanowy.data.repositories.teacher
import io.github.wulkanowy.data.db.dao.TeacherDao
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Teacher
import io.reactivex.Maybe
import javax.inject.Inject
class TeacherLocal @Inject constructor(private val teacherDb: TeacherDao) {
fun saveTeachers(teachers: List<Teacher>) {
teacherDb.insertAll(teachers)
}
fun deleteTeachers(teachers: List<Teacher>) {
teacherDb.deleteAll(teachers)
}
fun getTeachers(semester: Semester): Maybe<List<Teacher>> {
return teacherDb.loadAll(semester.studentId, semester.classId).filter { it.isNotEmpty() }
}
}

View File

@ -0,0 +1,28 @@
package io.github.wulkanowy.data.repositories.teacher
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Teacher
import io.reactivex.Single
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class TeacherRemote @Inject constructor(private val api: Api) {
fun getTeachers(semester: Semester): Single<List<Teacher>> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { it.getTeachers() }
.map { teachers ->
teachers.map {
Teacher(
studentId = semester.studentId,
name = it.name,
subject = it.subject,
shortName = it.short,
classId = semester.classId
)
}
}
}
}

View File

@ -0,0 +1,34 @@
package io.github.wulkanowy.data.repositories.teacher
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Teacher
import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single
import java.net.UnknownHostException
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class TeacherRepository @Inject constructor(
private val settings: InternetObservingSettings,
private val local: TeacherLocal,
private val remote: TeacherRemote
) {
fun getTeachers(semester: Semester, forceRefresh: Boolean = false): Single<List<Teacher>> {
return local.getTeachers(semester).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
if (it) remote.getTeachers(semester)
else Single.error(UnknownHostException())
}.flatMap { new ->
local.getTeachers(semester).toSingle(emptyList())
.doOnSuccess { old ->
local.deleteTeachers(old.uniqueSubtract(new))
local.saveTeachers(new.uniqueSubtract(old))
}
}.flatMap { local.getTeachers(semester).toSingle(emptyList()) })
}
}

View File

@ -32,10 +32,11 @@ class TimetableRepository @Inject constructor(
.doOnSuccess { old ->
local.deleteTimetable(old.uniqueSubtract(new))
local.saveTimetable(new.uniqueSubtract(old).map { item ->
item.apply {
old.singleOrNull { this.start == it.start }?.let {
return@map copy(
room = if (room.isEmpty()) it.room else room
item.also { new ->
old.singleOrNull { new.start == it.start }?.let { old ->
return@map new.copy(
room = if (new.room.isEmpty()) old.room else new.room,
teacher = if (new.teacher.isEmpty() && !new.changes) old.teacher else new.teacher
)
}
}

View File

@ -14,7 +14,7 @@ import javax.inject.Singleton
AppModule::class,
RepositoryModule::class,
ServicesModule::class,
BuilderModule::class])
BindingModule::class])
interface AppComponent : AndroidInjector<WulkanowyApp> {
@Component.Factory

View File

@ -2,6 +2,7 @@ package io.github.wulkanowy.di
import android.appwidget.AppWidgetManager
import android.content.Context
import com.yariksoffice.lingver.Lingver
import dagger.Module
import dagger.Provides
import eu.davidea.flexibleadapter.FlexibleAdapter
@ -32,4 +33,8 @@ internal class AppModule {
@Singleton
@Provides
fun provideAppInfo() = AppInfo()
@Singleton
@Provides
fun provideLingver() = Lingver.getInstance()
}

View File

@ -6,16 +6,17 @@ import io.github.wulkanowy.di.scopes.PerActivity
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.ui.modules.login.LoginModule
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainModule
import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureActivity
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider
@Suppress("unused")
@Module
internal abstract class BuilderModule {
internal abstract class BindingModule {
@PerActivity
@ContributesAndroidInjector

View File

@ -1,8 +1,6 @@
package io.github.wulkanowy.services
import android.app.NotificationManager
import android.content.Context
import android.content.Context.NOTIFICATION_SERVICE
import androidx.core.app.NotificationManagerCompat
import androidx.work.WorkManager
import com.squareup.inject.assisted.dagger2.AssistedModule
@ -23,31 +21,26 @@ import io.github.wulkanowy.services.sync.works.LuckyNumberWork
import io.github.wulkanowy.services.sync.works.MessageWork
import io.github.wulkanowy.services.sync.works.NoteWork
import io.github.wulkanowy.services.sync.works.RecipientWork
import io.github.wulkanowy.services.sync.works.TeacherWork
import io.github.wulkanowy.services.sync.works.TimetableWork
import io.github.wulkanowy.services.sync.works.Work
import io.github.wulkanowy.services.widgets.TimetableWidgetService
import javax.inject.Singleton
@Suppress("unused")
@AssistedModule
@Module(includes = [AssistedInject_ServicesModule::class])
@Module(includes = [AssistedInject_ServicesModule::class, ServicesModule.Static::class])
abstract class ServicesModule {
@Module
companion object {
object Static {
@JvmStatic
@Provides
fun provideWorkManager() = WorkManager.getInstance()
fun provideWorkManager(context: Context) = WorkManager.getInstance(context)
@JvmStatic
@Singleton
@Provides
fun provideNotificationManagerCompat(context: Context) = NotificationManagerCompat.from(context)
@JvmStatic
@Singleton
@Provides
fun provideNotificationManager(context: Context) = context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
fun provideNotificationManager(context: Context) = NotificationManagerCompat.from(context)
}
@ContributesAndroidInjector
@ -81,6 +74,10 @@ abstract class ServicesModule {
@IntoSet
abstract fun provideTimetableWork(work: TimetableWork): Work
@Binds
@IntoSet
abstract fun provideTeacherWork(work: TeacherWork): Work
@Binds
@IntoSet
abstract fun provideLuckyNumberWork(work: LuckyNumberWork): Work

View File

@ -8,9 +8,10 @@ import androidx.work.ExistingPeriodicWorkPolicy.KEEP
import androidx.work.ExistingPeriodicWorkPolicy.REPLACE
import androidx.work.NetworkType.CONNECTED
import androidx.work.NetworkType.UNMETERED
import androidx.work.PeriodicWorkRequest
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import io.github.wulkanowy.data.db.SharedPrefHelper
import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.db.SharedPrefProvider.Companion.APP_VERSION_CODE_KEY
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.services.sync.channels.DebugChannel
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
@ -26,17 +27,12 @@ import javax.inject.Singleton
class SyncManager @Inject constructor(
private val workManager: WorkManager,
private val preferencesRepository: PreferencesRepository,
sharedPrefHelper: SharedPrefHelper,
sharedPrefProvider: SharedPrefProvider,
newEntriesChannel: NewEntriesChannel,
debugChannel: DebugChannel,
appInfo: AppInfo
) {
companion object {
private const val APP_VERSION_CODE_KEY = "app_version_code"
}
init {
if (now().isHolidays) stopSyncWorker()
@ -45,18 +41,18 @@ class SyncManager @Inject constructor(
if (appInfo.isDebug) debugChannel.create()
}
if (sharedPrefHelper.getLong(APP_VERSION_CODE_KEY, -1L) != appInfo.versionCode.toLong()) {
if (sharedPrefProvider.getLong(APP_VERSION_CODE_KEY, -1L) != appInfo.versionCode.toLong()) {
startSyncWorker(true)
sharedPrefHelper.putLong(APP_VERSION_CODE_KEY, appInfo.versionCode.toLong(), true)
sharedPrefProvider.putLong(APP_VERSION_CODE_KEY, appInfo.versionCode.toLong(), true)
}
Timber.i("SyncManager was initialized")
}
fun startSyncWorker(restart: Boolean = false) {
if (preferencesRepository.isServiceEnabled && !now().isHolidays) {
workManager.enqueueUniquePeriodicWork(SyncWorker::class.java.simpleName, if (restart) REPLACE else KEEP,
PeriodicWorkRequest.Builder(SyncWorker::class.java, preferencesRepository.servicesInterval, MINUTES, 10, MINUTES)
PeriodicWorkRequestBuilder<SyncWorker>(preferencesRepository.servicesInterval, MINUTES)
.setInitialDelay(10, MINUTES)
.setBackoffCriteria(EXPONENTIAL, 30, MINUTES)
.setConstraints(Constraints.Builder()
.setRequiredNetworkType(if (preferencesRepository.isServicesOnlyWifi) UNMETERED else CONNECTED)

View File

@ -64,7 +64,7 @@ class SyncWorker @AssistedInject constructor(
private fun notify(result: Result) {
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(applicationContext, DebugChannel.CHANNEL_ID)
.setContentTitle("Debug notification")
.setSmallIcon(R.drawable.ic_more_settings_24dp)
.setSmallIcon(R.drawable.ic_more_settings)
.setAutoCancel(true)
.setColor(applicationContext.getCompatColor(R.color.colorPrimary))
.setStyle(BigTextStyle().bigText("${SyncWorker::class.java.simpleName} result: $result"))

View File

@ -3,15 +3,15 @@ package io.github.wulkanowy.services.sync.channels
import android.annotation.TargetApi
import android.app.Notification.VISIBILITY_PUBLIC
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.NotificationManager.IMPORTANCE_DEFAULT
import android.content.Context
import androidx.core.app.NotificationManagerCompat
import io.github.wulkanowy.R
import javax.inject.Inject
@TargetApi(26)
class DebugChannel @Inject constructor(
private val notificationManager: NotificationManager,
private val notificationManager: NotificationManagerCompat,
private val context: Context
) {
@ -21,8 +21,9 @@ class DebugChannel @Inject constructor(
fun create() {
notificationManager.createNotificationChannel(
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_debug), IMPORTANCE_DEFAULT).apply {
lockscreenVisibility = VISIBILITY_PUBLIC
})
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_debug), IMPORTANCE_DEFAULT)
.apply {
lockscreenVisibility = VISIBILITY_PUBLIC
})
}
}

View File

@ -3,15 +3,15 @@ package io.github.wulkanowy.services.sync.channels
import android.annotation.TargetApi
import android.app.Notification.VISIBILITY_PUBLIC
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.NotificationManager.IMPORTANCE_HIGH
import android.content.Context
import androidx.core.app.NotificationManagerCompat
import io.github.wulkanowy.R
import javax.inject.Inject
@TargetApi(26)
class NewEntriesChannel @Inject constructor(
private val notificationManager: NotificationManager,
private val notificationManager: NotificationManagerCompat,
private val context: Context
) {
@ -21,10 +21,11 @@ class NewEntriesChannel @Inject constructor(
fun create() {
notificationManager.createNotificationChannel(
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_entries), IMPORTANCE_HIGH).apply {
enableLights(true)
enableVibration(true)
lockscreenVisibility = VISIBILITY_PUBLIC
})
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_new_entries), IMPORTANCE_HIGH)
.apply {
enableLights(true)
enableVibration(true)
lockscreenVisibility = VISIBILITY_PUBLIC
})
}
}

View File

@ -15,7 +15,7 @@ import io.github.wulkanowy.data.repositories.grade.GradeRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView.MenuView
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable
import javax.inject.Inject
@ -41,14 +41,14 @@ class GradeWork @Inject constructor(
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID)
.setContentTitle(context.resources.getQuantityString(R.plurals.grade_new_items, grades.size, grades.size))
.setContentText(context.resources.getQuantityString(R.plurals.grade_notify_new_items, grades.size, grades.size))
.setSmallIcon(R.drawable.ic_stat_notify_grade)
.setSmallIcon(R.drawable.ic_stat_grade)
.setAutoCancel(true)
.setPriority(PRIORITY_HIGH)
.setDefaults(DEFAULT_ALL)
.setColor(context.getCompatColor(R.color.colorPrimary))
.setContentIntent(
PendingIntent.getActivity(context, MenuView.GRADE.id,
MainActivity.getStartIntent(context, MenuView.GRADE, true), FLAG_UPDATE_CURRENT))
PendingIntent.getActivity(context, MainView.Section.GRADE.id,
MainActivity.getStartIntent(context, MainView.Section.GRADE, true), FLAG_UPDATE_CURRENT))
.setStyle(NotificationCompat.InboxStyle().run {
setSummaryText(context.resources.getQuantityString(R.plurals.grade_number_item, grades.size, grades.size))
grades.forEach { addLine("${it.subject}: ${it.entry}") }

View File

@ -15,7 +15,7 @@ import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView.MenuView
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable
import javax.inject.Inject
@ -41,14 +41,14 @@ class LuckyNumberWork @Inject constructor(
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID)
.setContentTitle(context.getString(R.string.lucky_number_notify_new_item_title))
.setContentText(context.getString(R.string.lucky_number_notify_new_item, luckyNumber.luckyNumber))
.setSmallIcon(R.drawable.ic_stat_notify_lucky_number)
.setSmallIcon(R.drawable.ic_stat_luckynumber)
.setAutoCancel(true)
.setDefaults(DEFAULT_ALL)
.setPriority(PRIORITY_HIGH)
.setColor(context.getCompatColor(R.color.colorPrimary))
.setContentIntent(
PendingIntent.getActivity(context, MenuView.MESSAGE.id,
MainActivity.getStartIntent(context, MenuView.LUCKY_NUMBER, true), FLAG_UPDATE_CURRENT))
PendingIntent.getActivity(context, MainView.Section.MESSAGE.id,
MainActivity.getStartIntent(context, MainView.Section.LUCKY_NUMBER, true), FLAG_UPDATE_CURRENT))
.build())
}
}

View File

@ -16,7 +16,7 @@ import io.github.wulkanowy.data.repositories.message.MessageRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView.MenuView
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable
import javax.inject.Inject
@ -42,14 +42,14 @@ class MessageWork @Inject constructor(
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID)
.setContentTitle(context.resources.getQuantityString(R.plurals.message_new_items, messages.size, messages.size))
.setContentText(context.resources.getQuantityString(R.plurals.message_notify_new_items, messages.size, messages.size))
.setSmallIcon(R.drawable.ic_stat_notify_message)
.setSmallIcon(R.drawable.ic_stat_message)
.setAutoCancel(true)
.setDefaults(DEFAULT_ALL)
.setPriority(PRIORITY_HIGH)
.setColor(context.getCompatColor(R.color.colorPrimary))
.setContentIntent(
PendingIntent.getActivity(context, MenuView.MESSAGE.id,
MainActivity.getStartIntent(context, MenuView.MESSAGE, true), FLAG_UPDATE_CURRENT)
PendingIntent.getActivity(context, MainView.Section.MESSAGE.id,
MainActivity.getStartIntent(context, MainView.Section.MESSAGE, true), FLAG_UPDATE_CURRENT)
)
.setStyle(NotificationCompat.InboxStyle().run {
setSummaryText(context.resources.getQuantityString(R.plurals.message_number_item, messages.size, messages.size))

View File

@ -15,7 +15,7 @@ import io.github.wulkanowy.data.repositories.note.NoteRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView.MenuView
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable
import javax.inject.Inject
@ -41,14 +41,14 @@ class NoteWork @Inject constructor(
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewEntriesChannel.CHANNEL_ID)
.setContentTitle(context.resources.getQuantityString(R.plurals.note_new_items, notes.size, notes.size))
.setContentText(context.resources.getQuantityString(R.plurals.note_notify_new_items, notes.size, notes.size))
.setSmallIcon(R.drawable.ic_stat_notify_note)
.setSmallIcon(R.drawable.ic_stat_note)
.setAutoCancel(true)
.setDefaults(DEFAULT_ALL)
.setPriority(PRIORITY_HIGH)
.setColor(context.getCompatColor(R.color.colorPrimary))
.setContentIntent(
PendingIntent.getActivity(context, MenuView.NOTE.id,
MainActivity.getStartIntent(context, MenuView.NOTE, true), FLAG_UPDATE_CURRENT))
PendingIntent.getActivity(context, MainView.Section.NOTE.id,
MainActivity.getStartIntent(context, MainView.Section.NOTE, true), FLAG_UPDATE_CURRENT))
.setStyle(NotificationCompat.InboxStyle().run {
setSummaryText(context.resources.getQuantityString(R.plurals.note_number_item, notes.size, notes.size))
notes.forEach { addLine("${it.teacher}: ${it.category}") }

View File

@ -0,0 +1,14 @@
package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.teacher.TeacherRepository
import io.reactivex.Completable
import javax.inject.Inject
class TeacherWork @Inject constructor(private val teacherRepository: TeacherRepository) : Work {
override fun create(student: Student, semester: Semester): Completable {
return teacherRepository.getTeachers(semester, true).ignoreElement()
}
}

View File

@ -3,7 +3,7 @@ package io.github.wulkanowy.services.widgets
import android.content.Intent
import android.widget.RemoteViewsService
import dagger.android.AndroidInjection
import io.github.wulkanowy.data.db.SharedPrefHelper
import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.data.repositories.timetable.TimetableRepository
@ -23,7 +23,7 @@ class TimetableWidgetService : RemoteViewsService() {
lateinit var semesterRepo: SemesterRepository
@Inject
lateinit var sharedPref: SharedPrefHelper
lateinit var sharedPref: SharedPrefProvider
@Inject
lateinit var schedulers: SchedulersProvider

View File

@ -1,5 +1,8 @@
package io.github.wulkanowy.ui.base
import android.app.ActivityManager
import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES.LOLLIPOP
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.os.Bundle
@ -16,9 +19,11 @@ import dagger.android.HasAndroidInjector
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.utils.FragmentLifecycleLogger
import io.github.wulkanowy.utils.getThemeAttrColor
import javax.inject.Inject
abstract class BaseActivity<T : BasePresenter<out BaseView>> : AppCompatActivity(), BaseView, HasAndroidInjector {
abstract class BaseActivity<T : BasePresenter<out BaseView>> : AppCompatActivity(), BaseView,
HasAndroidInjector {
@Inject
lateinit var androidInjector: DispatchingAndroidInjector<Any>
@ -35,22 +40,29 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>> : AppCompatActivity
public override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
themeManager.applyTheme(this)
themeManager.applyActivityTheme(this)
super.onCreate(savedInstanceState)
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleLogger, true)
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
if (SDK_INT >= LOLLIPOP) {
@Suppress("DEPRECATION")
setTaskDescription(ActivityManager.TaskDescription(null, null, getThemeAttrColor(R.attr.colorSurface)))
}
}
override fun showError(text: String, error: Throwable) {
if (messageContainer != null) {
Snackbar.make(messageContainer!!, text, LENGTH_LONG)
.setAction(R.string.all_details) {
ErrorDialog.newInstance(error).show(supportFragmentManager, error.toString())
}
.setAction(R.string.all_details) { showErrorDetailsDialog(error) }
.show()
} else showMessage(text)
}
override fun showErrorDetailsDialog(error: Throwable) {
ErrorDialog.newInstance(error).show(supportFragmentManager, error.toString())
}
override fun showMessage(text: String) {
if (messageContainer != null) Snackbar.make(messageContainer!!, text, LENGTH_LONG).show()
else Toast.makeText(this, text, Toast.LENGTH_LONG).show()

View File

@ -0,0 +1,27 @@
package io.github.wulkanowy.ui.base
import android.widget.Toast
import dagger.android.support.DaggerAppCompatDialogFragment
abstract class BaseDialogFragment : DaggerAppCompatDialogFragment(), BaseView {
override fun showError(text: String, error: Throwable) {
showMessage(text)
}
override fun showMessage(text: String) {
Toast.makeText(context, text, Toast.LENGTH_LONG).show()
}
override fun showExpiredDialog() {
(activity as? BaseActivity<*>)?.showExpiredDialog()
}
override fun openClearLoginView() {
(activity as? BaseActivity<*>)?.openClearLoginView()
}
override fun showErrorDetailsDialog(error: Throwable) {
ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
}
}

View File

@ -13,15 +13,17 @@ abstract class BaseFragment : DaggerFragment(), BaseView {
override fun showError(text: String, error: Throwable) {
if (messageContainer != null) {
Snackbar.make(messageContainer!!, text, LENGTH_LONG)
.setAction(R.string.all_details) {
if (isAdded) ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
}
.setAction(R.string.all_details) { if (isAdded) showErrorDetailsDialog(error) }
.show()
} else {
(activity as? BaseActivity<*>)?.showError(text, error)
}
}
override fun showErrorDetailsDialog(error: Throwable) {
ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
}
override fun showMessage(text: String) {
if (messageContainer != null) {
Snackbar.make(messageContainer!!, text, LENGTH_LONG).show()

View File

@ -4,14 +4,15 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
class BaseFragmentPagerAdapter(private val fragmentManager: FragmentManager) : FragmentPagerAdapter(fragmentManager) {
class BaseFragmentPagerAdapter(private val fragmentManager: FragmentManager) :
FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
private val pages = mutableMapOf<Fragment, String?>()
var containerId = 0
fun getFragmentInstance(position: Int): Fragment? {
if (containerId == 0) throw IllegalArgumentException("Container id is 0")
require(containerId != 0) { "Container id is 0" }
return fragmentManager.findFragmentByTag("android:switcher:$containerId:$position")
}

View File

@ -9,4 +9,6 @@ interface BaseView {
fun showExpiredDialog()
fun openClearLoginView()
fun showErrorDetailsDialog(error: Throwable)
}

View File

@ -2,13 +2,13 @@ package io.github.wulkanowy.ui.base
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context.CLIPBOARD_SERVICE
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import androidx.core.content.getSystemService
import androidx.fragment.app.DialogFragment
import io.github.wulkanowy.R
import kotlinx.android.synthetic.main.dialog_error.*
@ -43,16 +43,17 @@ class ErrorDialog : DialogFragment() {
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
StringWriter().let { writer ->
error.printStackTrace(PrintWriter(writer))
errorDialogContent.text = writer.toString()
errorDialogCopy.setOnClickListener {
ClipData.newPlainText("wulkanowyError", writer.toString()).let { clip ->
(activity?.getSystemService(CLIPBOARD_SERVICE) as? ClipboardManager)?.primaryClip = clip
}
Toast.makeText(context, R.string.all_copied, LENGTH_LONG).show()
}
val stringWriter = StringWriter().apply {
error.printStackTrace(PrintWriter(this))
}
errorDialogContent.text = stringWriter.toString()
errorDialogCopy.setOnClickListener {
val clip = ClipData.newPlainText("wulkanowy", stringWriter.toString())
activity?.getSystemService<ClipboardManager>()?.setPrimaryClip(clip)
Toast.makeText(context, R.string.all_copied, LENGTH_LONG).show()
}
errorDialogCancel.setOnClickListener { dismiss() }
}

View File

@ -2,29 +2,36 @@ package io.github.wulkanowy.ui.base
import android.content.pm.PackageManager.GET_ACTIVITIES
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
import io.github.wulkanowy.R
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class ThemeManager @Inject constructor(private val preferencesRepository: PreferencesRepository) {
fun applyTheme(activity: AppCompatActivity) {
fun applyActivityTheme(activity: AppCompatActivity) {
if (isThemeApplicable(activity)) {
activity.delegate.apply {
when (preferencesRepository.appTheme) {
"light" -> setLocalNightMode(MODE_NIGHT_NO)
"dark" -> setLocalNightMode(MODE_NIGHT_YES)
"black" -> {
setLocalNightMode(MODE_NIGHT_YES)
activity.setTheme(R.style.WulkanowyTheme_Black)
}
}
}
applyDefaultTheme()
if (preferencesRepository.appTheme == "black") activity.setTheme(R.style.WulkanowyTheme_Black)
}
}
fun applyDefaultTheme() {
AppCompatDelegate.setDefaultNightMode(
when (val theme = preferencesRepository.appTheme) {
"light" -> MODE_NIGHT_NO
"dark", "black" -> MODE_NIGHT_YES
"system" -> MODE_NIGHT_FOLLOW_SYSTEM
else -> throw IllegalArgumentException("Wrong theme: $theme")
}
)
}
private fun isThemeApplicable(activity: AppCompatActivity): Boolean {
return activity.packageManager.getPackageInfo(activity.packageName, GET_ACTIVITIES)
.activities.singleOrNull { it.name == activity::class.java.canonicalName }?.theme

View File

@ -1,19 +1,29 @@
package io.github.wulkanowy.ui.modules.about
import android.content.Intent
import android.content.Intent.ACTION_SENDTO
import android.content.Intent.EXTRA_EMAIL
import android.content.Intent.EXTRA_SUBJECT
import android.content.Intent.EXTRA_TEXT
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.mikepenz.aboutlibraries.LibsBuilder
import com.mikepenz.aboutlibraries.LibsFragmentCompat
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.about.license.LicenseFragment
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.getCompatDrawable
import io.github.wulkanowy.utils.openInternetBrowser
import io.github.wulkanowy.utils.withOnExtraListener
import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.fragment_about.*
import javax.inject.Inject
class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
@ -22,63 +32,99 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
lateinit var presenter: AboutPresenter
@Inject
lateinit var fragmentCompat: LibsFragmentCompat
lateinit var aboutAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
@Inject
lateinit var appInfo: AppInfo
override val versionRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_version), "${appInfo.versionName} (${appInfo.versionCode})", getCompatDrawable(R.drawable.ic_all_about))
}
override val feedbackRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_feedback), getString(R.string.about_feedback_summary), getCompatDrawable(R.drawable.ic_about_feedback))
}
override val faqRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_faq), getString(R.string.about_faq_summary), getCompatDrawable(R.drawable.ic_about_faq))
}
override val discordRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_discord), getString(R.string.about_discord_summary), getCompatDrawable(R.drawable.ic_about_discord))
}
override val homepageRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_homepage), getString(R.string.about_homepage_summary), getCompatDrawable(R.drawable.ic_about_homepage))
}
override val licensesRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_licenses), getString(R.string.about_licenses_summary), getCompatDrawable(R.drawable.ic_about_licenses))
}
override val privacyRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_privacy), getString(R.string.about_privacy_summary), getCompatDrawable(R.drawable.ic_about_privacy))
}
override val titleStringId get() = R.string.about_title
companion object {
fun newInstance() = AboutFragment()
}
override val titleStringId: Int
get() = R.string.about_title
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
presenter.onAttachView(this)
return Bundle().apply {
putSerializable("data", LibsBuilder()
.withAboutAppName(getString(R.string.app_name))
.withAboutVersionShown(true)
.withAboutIconShown(true)
.withLicenseShown(true)
.withAboutSpecial1(getString(R.string.about_discord_invite))
.withAboutSpecial2(getString(R.string.about_homepage))
.withAboutSpecial3(getString(R.string.about_feedback))
.withFields(R.string::class.java.fields)
.withCheckCachedDetection(false)
.withExcludedLibraries("fastadapter", "AndroidIconics", "Jsoup", "Retrofit", "okio",
"Butterknife", "CircleImageView")
.withOnExtraListener { presenter.onExtraSelect(it) })
}.let {
fragmentCompat.onCreateView(inflater.context, inflater, container, savedInstanceState, it)
}
return inflater.inflate(R.layout.fragment_about, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
fragmentCompat.onViewCreated(view, savedInstanceState)
presenter.onAttachView(this)
}
override fun openDiscordInviteView() {
override fun initView() {
aboutAdapter.setOnItemClickListener(presenter::onItemSelected)
with(aboutRecycler) {
layoutManager = SmoothScrollLinearLayoutManager(context)
adapter = aboutAdapter
}
}
override fun updateData(header: AboutScrollableHeader, items: List<AboutItem>) {
with(aboutAdapter) {
removeAllScrollableHeaders()
addScrollableHeader(header)
updateDataSet(items)
}
}
override fun openDiscordInvite() {
context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage)
}
override fun openHomepageWebView() {
override fun openHomepage() {
context?.openInternetBrowser("https://wulkanowy.github.io/", ::showMessage)
}
override fun openEmailClientView() {
val intent = Intent(Intent.ACTION_SENDTO).apply {
data = Uri.parse("mailto:")
putExtra(Intent.EXTRA_EMAIL, Array(1) { "wulkanowyinc@gmail.com" })
putExtra(Intent.EXTRA_SUBJECT, "Zgłoszenie błędu")
putExtra(Intent.EXTRA_TEXT, "Tu umieść treść zgłoszenia\n\n" + "-".repeat(40) + "\n" + """
Build: ${appInfo.versionCode}
SDK: ${appInfo.systemVersion}
Device: ${appInfo.systemManufacturer} ${appInfo.systemModel}
""".trimIndent())
}
override fun openEmailClient() {
val intent = Intent(ACTION_SENDTO)
.apply {
data = Uri.parse("mailto:")
putExtra(EXTRA_EMAIL, arrayOf("wulkanowyinc@gmail.com"))
putExtra(EXTRA_SUBJECT, "Zgłoszenie błędu")
putExtra(EXTRA_TEXT, "Tu umieść treść zgłoszenia\n\n${"-".repeat(40)}\n " +
"""
Build: ${appInfo.versionCode}
SDK: ${appInfo.systemVersion}
Device: ${appInfo.systemManufacturer} ${appInfo.systemModel}
""".trimIndent())
}
context?.let {
if (intent.resolveActivity(it.packageManager) != null) {
@ -89,8 +135,19 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
}
}
override fun openFaqPage() {
context?.openInternetBrowser("https://wulkanowy.github.io/czesto-zadawane-pytania", ::showMessage)
}
override fun openLicenses() {
(activity as? MainActivity)?.pushView(LicenseFragment.newInstance())
}
override fun openPrivacyPolicy() {
context?.openInternetBrowser("https://wulkanowy.github.io/polityka-prywatnosci.html", ::showMessage)
}
override fun onDestroyView() {
fragmentCompat.onDestroyView()
presenter.onDetachView()
super.onDestroyView()
}

View File

@ -0,0 +1,56 @@
package io.github.wulkanowy.ui.modules.about
import android.graphics.drawable.Drawable
import android.view.View
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.viewholders.FlexibleViewHolder
import io.github.wulkanowy.R
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_about.*
class AboutItem(
val title: String,
private val summary: String,
private val image: Drawable?
) : AbstractFlexibleItem<AboutItem.ViewHolder>() {
override fun getLayoutRes() = R.layout.item_about
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) = ViewHolder(view, adapter)
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
with(holder) {
aboutItemImage.setImageDrawable(image)
aboutItemTitle.text = title
aboutItemSummary.text = summary
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as AboutItem
if (title != other.title) return false
if (summary != other.summary) return false
if (image != other.image) return false
return true
}
override fun hashCode(): Int {
var result = title.hashCode()
result = 31 * result + summary.hashCode()
result = 31 * result + (image?.hashCode() ?: 0)
return result
}
class ViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) : FlexibleViewHolder(view, adapter),
LayoutContainer {
override val containerView: View? get() = contentView
}
}

View File

@ -1,14 +0,0 @@
package io.github.wulkanowy.ui.modules.about
import com.mikepenz.aboutlibraries.LibsFragmentCompat
import dagger.Module
import dagger.Provides
import io.github.wulkanowy.di.scopes.PerFragment
@Module
class AboutModule {
@PerFragment
@Provides
fun provideLibsFragmentCompat() = LibsFragmentCompat()
}

View File

@ -1,9 +1,6 @@
package io.github.wulkanowy.ui.modules.about
import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.Libs.SpecialButton.SPECIAL1
import com.mikepenz.aboutlibraries.Libs.SpecialButton.SPECIAL2
import com.mikepenz.aboutlibraries.Libs.SpecialButton.SPECIAL3
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
@ -21,28 +18,59 @@ class AboutPresenter @Inject constructor(
override fun onAttachView(view: AboutView) {
super.onAttachView(view)
view.initView()
Timber.i("About view was initialized")
loadData()
}
fun onExtraSelect(type: Libs.SpecialButton?) {
fun onItemSelected(item: AbstractFlexibleItem<*>) {
if (item !is AboutItem) return
view?.run {
when (type) {
SPECIAL1 -> {
Timber.i("Opening discord invide page")
analytics.logEvent("open_page", "name" to "discord")
openDiscordInviteView()
}
SPECIAL2 -> {
Timber.i("Opening home page")
analytics.logEvent("open_page", "name" to "home")
openHomepageWebView()
}
SPECIAL3 -> {
when (item.title) {
feedbackRes?.first -> {
Timber.i("Opening email client")
analytics.logEvent("open_page", "name" to "email")
openEmailClientView()
openEmailClient()
analytics.logEvent("about_open", "name" to "feedback")
}
faqRes?.first -> {
Timber.i("Opening faq page")
openFaqPage()
analytics.logEvent("about_open", "name" to "faq")
}
discordRes?.first -> {
Timber.i("Opening discord")
openDiscordInvite()
analytics.logEvent("about_open", "name" to "discord")
}
homepageRes?.first -> {
Timber.i("Opening homepage")
openHomepage()
analytics.logEvent("about_open", "name" to "homepage")
}
licensesRes?.first -> {
Timber.i("Opening licenses view")
openLicenses()
analytics.logEvent("about_open", "name" to "licenses")
}
privacyRes?.first -> {
Timber.i("Opening privacy page ")
openPrivacyPolicy()
analytics.logEvent("about_open", "name" to "privacy")
}
}
}
}
private fun loadData() {
view?.run {
updateData(AboutScrollableHeader(), listOfNotNull(
versionRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
feedbackRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
faqRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
discordRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
homepageRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
licensesRes?.let { (title, summary, image) -> AboutItem(title, summary, image) },
privacyRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }))
}
}
}

View File

@ -0,0 +1,41 @@
package io.github.wulkanowy.ui.modules.about
import android.view.View
import androidx.core.content.res.ResourcesCompat
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.viewholders.FlexibleViewHolder
import io.github.wulkanowy.R
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.scrollable_header_about.*
class AboutScrollableHeader : AbstractFlexibleItem<AboutScrollableHeader.ViewHolder>() {
override fun getLayoutRes() = R.layout.scrollable_header_about
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) = ViewHolder(view, adapter)
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
with(holder) {
val context = contentView.context
val drawable = ResourcesCompat.getDrawableForDensity(context.resources, context.applicationInfo.icon, 640, null)
aboutScrollableHeaderIcon.setImageDrawable(drawable)
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
return true
}
override fun hashCode() = javaClass.hashCode()
class ViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) : FlexibleViewHolder(view, adapter),
LayoutContainer {
override val containerView: View? get() = contentView
}
}

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