1
0

Compare commits

..

112 Commits

Author SHA1 Message Date
d3a6ea5acf Merge branch 'release/0.18.3' 2020-06-10 19:18:23 +02:00
674a78b661 Version 0.18.3 2020-06-10 19:18:09 +02:00
5c84c8d5b1 Fix force average calc from two semesters (#870) 2020-06-10 17:28:49 +02:00
522a36d670 Fix message deleting (#875) 2020-06-10 16:26:45 +02:00
2d0cfc3e8e Merge tag '0.18.2' into develop
Version 0.18.2
2020-06-02 17:08:02 +02:00
4b6b722f87 Merge branch 'release/0.18.2' 2020-06-02 17:07:58 +02:00
419675066f Version 0.18.2 2020-06-02 17:07:52 +02:00
191b1ad022 Emulate summaries from grade list when summaries are empty (#855) 2020-06-02 15:51:15 +02:00
792e44a9d0 Fix login button state in student select login fragment (#863) 2020-06-02 15:50:32 +02:00
ff5a47b0df New Crowdin translations (#856) 2020-06-02 15:18:41 +02:00
7bf0acb703 Revert "Bump sonarqube-gradle-plugin from 2.8 to 3.0" (#864)
This reverts commit ab7d30c995.
2020-06-02 15:13:41 +02:00
ba5dbf90d8 Fixes in updating adapter items (#854) 2020-06-02 01:04:41 +02:00
54f41aaa63 Fix too many alarms on samsung devices (#859) 2020-06-02 01:04:02 +02:00
1db42210e8 Bump about_libraries from 8.1.4 to 8.1.6 (#861)
Bumps `about_libraries` from 8.1.4 to 8.1.6.

Updates `aboutlibraries-plugin` from 8.1.4 to 8.1.6
- [Release notes](https://github.com/mikepenz/AboutLibraries/releases)
- [Changelog](https://github.com/mikepenz/AboutLibraries/blob/develop/gradle-release.gradle)
- [Commits](https://github.com/mikepenz/AboutLibraries/compare/v8.1.4...v8.1.6)

Updates `aboutlibraries-core` from 8.1.4 to 8.1.6
- [Release notes](https://github.com/mikepenz/AboutLibraries/releases)
- [Changelog](https://github.com/mikepenz/AboutLibraries/blob/develop/gradle-release.gradle)
- [Commits](https://github.com/mikepenz/AboutLibraries/compare/v8.1.4...v8.1.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-06-02 01:02:16 +02:00
fb554a4a3b Fix capitalization in new message activity (#860) 2020-06-02 01:01:58 +02:00
d8d13c73fb Filter out empty items in grade summary (#857) 2020-06-02 01:01:02 +02:00
5c0160a24d Don't capture click on login student select checkbox (#862) 2020-06-02 00:57:22 +02:00
ab7d30c995 Bump sonarqube-gradle-plugin from 2.8 to 3.0 (#853) 2020-05-30 12:19:52 +00:00
1cfa1f15c0 Bump gradle from 3.6.3 to 4.0.0 (#852) 2020-05-30 11:33:43 +00:00
2149a4db9f Bump about_libraries from 8.1.3 to 8.1.4 (#851) 2020-05-30 11:17:22 +00:00
df57d16d21 Bump dagger from 2.27 to 2.28 (#850) 2020-05-30 11:16:58 +00:00
2ff031005e Fix displaying the feature disabled message in completed lessons (#849) 2020-05-30 13:15:28 +02:00
b9ab85ee55 Merge tag '0.18.1' into develop
Version 0.18.1
2020-05-24 21:20:28 +02:00
064998129e Merge branch 'release/0.18.1' 2020-05-24 21:20:23 +02:00
4044cdd9a5 Version 0.18.1 2020-05-24 21:20:17 +02:00
1ee10a5902 New Crowdin translations (#847) 2020-05-24 21:18:49 +02:00
27b1d076c7 Bump firebase-inappmessaging-ktx from 19.0.6 to 19.0.7 (#844) 2020-05-24 21:03:35 +02:00
c8b32fdb3b Bump firebase-analytics from 17.4.1 to 17.4.2 (#843) 2020-05-24 21:02:42 +02:00
0b4434fdb6 Bump firebase-crashlytics-gradle from 2.1.0 to 2.1.1 (#841) 2020-05-24 20:53:59 +02:00
699fbff082 Fix debug channel (#846) 2020-05-24 20:53:23 +02:00
4c295f2ab4 Bump firebase-messaging from 20.1.7 to 20.2.0 (#839) 2020-05-24 18:42:24 +00:00
dcbaa170db Bump about_libraries from 8.1.2 to 8.1.3 (#845) 2020-05-24 20:36:09 +02:00
c71b533645 Bump firebase-inappmessaging-display-ktx from 19.0.6 to 19.0.7 (#838) 2020-05-24 18:10:47 +00:00
63f2576ff1 Hide advanced login options button (#837) 2020-05-24 20:07:24 +02:00
b744a4182b Open dontkillmyapp.com if no action found in app killer manager (#835) 2020-05-24 20:04:09 +02:00
0c4364609b Show check for updates dialog before report a bug (#834) 2020-05-24 20:03:46 +02:00
3308d7fe6f Wrap long preference titles (#836) 2020-05-24 19:52:01 +02:00
2cdde78c54 Allow access to saturday in timetable and attendance (#833) 2020-05-24 19:48:56 +02:00
428b599be0 Improve firebase logging (#832) 2020-05-24 19:48:14 +02:00
3541ab81b8 Destroy webview in password recover before setting binding to null (#829) 2020-05-24 19:47:20 +02:00
7fa14e5077 Set app_name in build.gradle (#830) 2020-05-24 19:36:40 +02:00
cec1068f2e Fix crash in timetable time left (#826) 2020-05-24 19:34:10 +02:00
f737018548 Add debug statements to get/update methods in grade details adapter (#827) 2020-05-24 19:33:04 +02:00
9c01316178 Fix mark message as read in search mode (#828) 2020-05-24 19:24:01 +02:00
c3a6f8253a Fix grade sorting (#825) 2020-05-24 19:23:35 +02:00
d558c4db66 New Crowdin translations (#817) 2020-05-23 16:34:26 +02:00
722886aaf2 Merge tag '0.18.0' into develop
Version 0.18.0
2020-05-21 00:59:19 +02:00
31902a7667 Merge branch 'release/0.18.0' 2020-05-21 00:59:13 +02:00
4c1c4f8a43 Version 0.18.0 2020-05-21 00:59:05 +02:00
7850412ba9 Fix crash in timetable on api < 21 (#816) 2020-05-20 23:08:32 +02:00
4f0ff5f49c New Crowdin translations (#813) 2020-05-20 22:48:09 +02:00
131ba7dbb1 Add app killer manager to settings (#808) 2020-05-20 16:59:26 +02:00
b95b529015 Add lesson time left display (#550) 2020-05-20 16:06:24 +02:00
29226dd93e Add notification about upcoming lesson (#578) 2020-05-20 15:11:01 +02:00
115da64167 Add search in messages (#804) 2020-05-20 14:12:32 +02:00
6cd1877af7 Fix notifications on android 8.0 (#814) 2020-05-20 12:34:29 +02:00
78a90591fd Bump coil from 0.10.1 to 0.11.0 (#812) 2020-05-16 21:06:45 +00:00
45265d025d Bump appcompat from 1.2.0-beta01 to 1.2.0-rc01 (#811) 2020-05-16 20:52:37 +00:00
9bf5c2dc40 Bump firebase-crashlytics-gradle from 2.0.0 to 2.1.0 (#810) 2020-05-16 20:48:15 +00:00
ee4bdd2a9a Bump firebase-analytics from 17.4.0 to 17.4.1 (#809) 2020-05-16 20:46:11 +00:00
0b75635ad5 Add option to hide/show chart list in grade class stats (#807) 2020-05-16 22:21:14 +02:00
f7b5b9c413 Add fullscreen mode to homework dialog (#806) 2020-05-16 22:06:00 +02:00
52d66ac30b New Crowdin translations (#797) 2020-05-13 11:25:50 +02:00
6ac5c6a0b4 Add widget system theme option (#759) 2020-05-10 12:00:21 +02:00
45fc76a9a5 Update translations (#794) 2020-05-10 11:34:54 +02:00
6d1fa0cf05 Optimize grade average provider (#792) 2020-05-10 10:51:01 +02:00
8eb0c0351b Use view binding instead of kotlin synthetics (#791) 2020-05-10 10:39:10 +02:00
ec80f939f1 Update Crowdin configuration file 2020-05-06 23:10:19 +02:00
70fc51a0b5 Update Crowdin configuration file 2020-05-06 23:07:13 +02:00
bd700a88bf Add nav bar color in night style (#790) 2020-05-03 15:06:49 +02:00
98f2f0e74f Migrate from fabric to firebase crashlytics (#789) 2020-05-01 19:00:42 +02:00
4a3b746d48 Remove flexible adapter (#781) 2020-05-01 17:38:19 +02:00
a1f864b35e Add importantForAutofill to login fields (#788) 2020-05-01 12:54:01 +02:00
17ac3cfd52 Bump firebase-analytics from 17.3.0 to 17.4.0 (#787) 2020-04-30 22:40:38 +00:00
c6c2b1c6a3 Bump coil from 0.9.5 to 0.10.1 (#785) 2020-04-29 14:40:12 +00:00
5fba3d5775 Bump firebase-inappmessaging-ktx from 19.0.5 to 19.0.6 (#782) 2020-04-28 21:25:27 +00:00
6fe62edd63 Bump firebase-inappmessaging-display-ktx from 19.0.5 to 19.0.6 (#786) 2020-04-28 21:04:54 +00:00
87af3da1ad Bump threetenabp from 1.2.3 to 1.2.4 (#783) 2020-04-28 20:57:14 +00:00
155f0cc347 Bump threetenbp from 1.4.3 to 1.4.4 (#784) 2020-04-28 20:56:55 +00:00
2de1ad5334 Merge tag '0.17.4' into develop
Version 0.17.4
2020-04-23 19:24:59 +02:00
763543a16e Merge branch 'release/0.17.4' 2020-04-23 19:24:54 +02:00
acabe90c9f Version 0.17.4 2020-04-23 19:24:49 +02:00
f79da9003a Merge tag '0.17.3' into develop
Version 0.17.3
2020-04-23 16:22:49 +02:00
fc9e558cd6 Merge branch 'release/0.17.3' 2020-04-23 16:22:44 +02:00
68140bd544 Version 0.17.3 2020-04-23 16:22:39 +02:00
2c4c2d1f49 Fix injector of ErrorDialog (#780) 2020-04-23 11:07:18 +02:00
4894086d9d Bump about_libraries from 8.1.1 to 8.1.2 (#779) 2020-04-21 16:21:44 +00:00
1d29ef5fe3 Merge tag '0.17.2' into develop
Version 0.17.2
2020-04-19 23:42:00 +02:00
7fa333cff2 Merge branch 'release/0.17.2' 2020-04-19 23:41:53 +02:00
c1ffc2ae72 Version 0.17.2 2020-04-19 23:37:29 +02:00
9c0e2dc533 Refresh semesters if previous list was downloaded in different m… (#776) 2020-04-19 23:21:59 +02:00
9b18e3669d Fix crash after send message when activity is in background (#777) 2020-04-19 23:21:25 +02:00
366ebc781d Add error message and bug report button to error dialog (#778) 2020-04-19 23:20:55 +02:00
4bd0459155 Don't log common errors to crashlytics (#775) 2020-04-18 23:07:20 +02:00
b19084cb57 Fix visibility of dialog close button when homework content is l… (#774) 2020-04-18 23:06:05 +02:00
69be7ca412 Bump kotlin_version from 1.3.71 to 1.3.72 (#773) 2020-04-17 21:01:41 +00:00
07307b9709 Bump swiperefreshlayout from 1.1.0-beta01 to 1.1.0-rc01 (#770) 2020-04-17 20:59:02 +00:00
ee4a5e56a9 Bump gradle from 3.6.2 to 3.6.3 (#772) 2020-04-17 20:37:38 +00:00
c8f8ec77a9 Bump preference-ktx from 1.1.0 to 1.1.1 (#771) 2020-04-17 20:35:24 +00:00
be46a43427 Merge tag '0.17.1' into develop
Version 0.17.1
2020-04-12 19:20:59 +02:00
183e379223 Merge branch 'release/0.17.1' 2020-04-12 19:20:53 +02:00
2350fc2ddf Version 0.17.1 2020-04-12 19:20:44 +02:00
152f966a66 Bump firebase-inappmessaging-display-ktx from 19.0.4 to 19.0.5 (#767) 2020-04-12 17:19:06 +00:00
85ee7fad1d Bump firebase-inappmessaging-ktx from 19.0.4 to 19.0.5 (#768) 2020-04-12 16:58:23 +00:00
3ac085573f Show all known adfslight registers in register list (#766) 2020-04-12 18:55:16 +02:00
64a19d9627 Bump about_libraries from 8.1.0 to 8.1.1 (#769) 2020-04-12 15:14:08 +00:00
76af623c94 Add Sdk.init(student) call in all remote repositories (#764) 2020-04-12 15:13:35 +02:00
11c285be01 Fix homework dialog size (#765) 2020-04-12 15:12:25 +02:00
a0528496eb Fix crash caused by updating view in not attached fragment (#763) 2020-04-09 23:46:42 +02:00
299345b864 Fix crash in message preview (#762) 2020-04-09 23:30:56 +02:00
0a18fefb1f Use recycler view in homework details dialog (#761) 2020-04-08 18:17:20 +02:00
a26f0ec8c8 Merge tag '0.17.0' into develop
Version 0.17.0
2020-04-05 18:42:42 +02:00
360 changed files with 7110 additions and 5898 deletions

View File

@ -1,3 +1,3 @@
component_depth: 8 component_depth: 10
languages: languages:
- kotlin - kotlin

View File

@ -14,7 +14,7 @@ cache:
branches: branches:
only: only:
- develop - develop
- 0.17.0 - 0.18.3
android: android:
licenses: licenses:
@ -48,20 +48,15 @@ before_script:
script: script:
- ./gradlew dependencies --stacktrace --daemon - ./gradlew dependencies --stacktrace --daemon
- fossa --no-ansi || true - fossa --no-ansi || true
#- ./gradlew lintPlayRelease -x fabricGenerateResourcesPlayRelease --stacktrace --daemon - ./gradlew -Pcoverage testPlayDebugUnitTest --stacktrace --daemon
- ./gradlew -Pcoverage testPlayDebugUnitTest -x fabricGenerateResourcesPlay --stacktrace --daemon
- ./gradlew -Pcoverage createFdroidDebugCoverageReport --stacktrace --daemon - ./gradlew -Pcoverage createFdroidDebugCoverageReport --stacktrace --daemon
- ./gradlew -Pcoverage jacocoTestReport --stacktrace --daemon - ./gradlew -Pcoverage jacocoTestReport --stacktrace --daemon
- if [ -z ${SONAR_HOST+x} ]; then echo "sonar scan skipped"; else
git fetch --unshallow;
./gradlew sonarqube -x test -x lint -x fabricGenerateResourcesPlayRelease -x fabricGenerateResourcesFdroidRelease -Dsonar.host.url=$SONAR_HOST -Dsonar.organization=$SONAR_ORG -Dsonar.login=$SONAR_KEY -Dsonar.branch.name=${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} --stacktrace --daemon;
fi
- | - |
if [ $TRAVIS_TAG ]; then if [ $TRAVIS_TAG ]; then
gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/google-services.json.gpg; gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/google-services.json.gpg;
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/key.p12.gpg; gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/key.p12.gpg;
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg; gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg;
./gradlew publishPlayRelease -PenableCrashlytics --stacktrace; ./gradlew publishPlayRelease -PenableFirebase --stacktrace;
fi fi
after_success: after_success:

View File

@ -1,8 +1,7 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions' apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'io.fabric'
apply plugin: 'com.github.triplet.play' apply plugin: 'com.github.triplet.play'
apply plugin: 'com.mikepenz.aboutlibraries.plugin' apply plugin: 'com.mikepenz.aboutlibraries.plugin'
apply from: 'jacoco.gradle' apply from: 'jacoco.gradle'
@ -18,14 +17,14 @@ android {
testApplicationId "io.github.tests.wulkanowy" testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 17 minSdkVersion 17
targetSdkVersion 29 targetSdkVersion 29
versionCode 54 versionCode 62
versionName "0.17.0" versionName "0.18.3"
multiDexEnabled true multiDexEnabled true
resValue "string", "app_name", "Wulkanowy"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
manifestPlaceholders = [ manifestPlaceholders = [
fabric_api_key : System.getenv("FABRIC_API_KEY") ?: "null", firebase_enabled: project.hasProperty("enableFirebase")
crashlytics_enabled: project.hasProperty("enableCrashlytics")
] ]
javaCompileOptions { javaCompileOptions {
annotationProcessorOptions { annotationProcessorOptions {
@ -52,18 +51,17 @@ android {
buildTypes { buildTypes {
release { release {
buildConfigField "boolean", "CRASHLYTICS_ENABLED", "true"
minifyEnabled true minifyEnabled true
shrinkResources true shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release signingConfig signingConfigs.release
} }
debug { debug {
buildConfigField "boolean", "CRASHLYTICS_ENABLED", project.hasProperty("enableCrashlytics") ? "true" : "false" resValue "string", "app_name", "Wulkanowy DEV " + defaultConfig.versionCode
applicationIdSuffix ".dev" applicationIdSuffix ".dev"
versionNameSuffix "-dev" versionNameSuffix "-dev"
testCoverageEnabled = project.hasProperty('coverage') testCoverageEnabled = project.hasProperty('coverage')
ext.enableCrashlytics = project.hasProperty("enableCrashlytics") ext.enableCrashlytics = project.hasProperty("enableFirebase")
} }
} }
@ -75,11 +73,14 @@ android {
} }
fdroid { fdroid {
buildConfigField "boolean", "CRASHLYTICS_ENABLED", "false"
dimension "platform" dimension "platform"
} }
} }
viewBinding {
enabled = true
}
lintOptions { lintOptions {
disable 'HardwareIds' disable 'HardwareIds'
} }
@ -103,10 +104,6 @@ android {
} }
} }
androidExtensions {
experimental = true
}
play { play {
serviceAccountEmail = System.getenv("PLAY_SERVICE_ACCOUNT_EMAIL") ?: "jan@fakelog.cf" serviceAccountEmail = System.getenv("PLAY_SERVICE_ACCOUNT_EMAIL") ?: "jan@fakelog.cf"
serviceAccountCredentials = file('key.p12') serviceAccountCredentials = file('key.p12')
@ -117,8 +114,7 @@ play {
ext { ext {
work_manager = "2.3.4" work_manager = "2.3.4"
room = "2.2.5" room = "2.2.5"
dagger = "2.27" dagger = "2.28"
// don't update https://github.com/ChuckerTeam/chucker/issues/242
chucker = "3.2.0" chucker = "3.2.0"
mockk = "1.9.2" mockk = "1.9.2"
} }
@ -128,21 +124,21 @@ configurations.all {
} }
dependencies { dependencies {
implementation "io.github.wulkanowy:sdk:0.17.0" implementation "io.github.wulkanowy:sdk:0.18.3"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "androidx.core:core-ktx:1.2.0" implementation "androidx.core:core-ktx:1.2.0"
implementation "androidx.activity:activity-ktx:1.1.0" implementation "androidx.activity:activity-ktx:1.1.0"
implementation "androidx.appcompat:appcompat:1.2.0-beta01" implementation "androidx.appcompat:appcompat:1.2.0-rc01"
implementation "androidx.appcompat:appcompat-resources:1.1.0" implementation "androidx.appcompat:appcompat-resources:1.1.0"
implementation "androidx.fragment:fragment-ktx:1.2.4" implementation "androidx.fragment:fragment-ktx:1.2.4"
implementation "androidx.annotation:annotation:1.1.0" implementation "androidx.annotation:annotation:1.1.0"
implementation "androidx.multidex:multidex:2.0.1" implementation "androidx.multidex:multidex:2.0.1"
implementation "androidx.preference:preference-ktx:1.1.0" implementation "androidx.preference:preference-ktx:1.1.1"
implementation "androidx.recyclerview:recyclerview:1.1.0" implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation "androidx.viewpager:viewpager:1.0.0" implementation "androidx.viewpager:viewpager:1.0.0"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-beta01" implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-rc01"
implementation "androidx.constraintlayout:constraintlayout:1.1.3" implementation "androidx.constraintlayout:constraintlayout:1.1.3"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0" implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
implementation "com.google.android.material:material:1.1.0" implementation "com.google.android.material:material:1.1.0"
@ -167,30 +163,29 @@ dependencies {
implementation "com.squareup.inject:assisted-inject-annotations-dagger2:0.5.2" implementation "com.squareup.inject:assisted-inject-annotations-dagger2:0.5.2"
kapt "com.squareup.inject:assisted-inject-processor-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.aurelhubert:ahbottomnavigation:2.3.4"
implementation "com.ncapdevi:frag-nav:3.3.0" implementation "com.ncapdevi:frag-nav:3.3.0"
implementation "com.github.YarikSOffice:lingver:1.2.1" implementation "com.github.YarikSOffice:lingver:1.2.2"
implementation "com.github.pwittchen:reactivenetwork-rx2:3.0.6" implementation "com.github.pwittchen:reactivenetwork-rx2:3.0.8"
implementation "io.reactivex.rxjava2:rxandroid:2.1.1" implementation "io.reactivex.rxjava2:rxandroid:2.1.1"
implementation "io.reactivex.rxjava2:rxjava:2.2.19" implementation "io.reactivex.rxjava2:rxjava:2.2.19"
implementation "com.google.code.gson:gson:2.8.6" implementation "com.google.code.gson:gson:2.8.6"
implementation "com.jakewharton.threetenabp:threetenabp:1.2.3" implementation "com.jakewharton.threetenabp:threetenabp:1.2.4"
implementation "com.jakewharton.timber:timber:4.7.1" implementation "com.jakewharton.timber:timber:4.7.1"
implementation "at.favre.lib:slf4j-timber:1.0.1" implementation "at.favre.lib:slf4j-timber:1.0.1"
implementation "fr.bipi.treessence:treessence:0.3.2" implementation "fr.bipi.treessence:treessence:0.3.2"
implementation "com.mikepenz:aboutlibraries-core:$about_libraries" implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
implementation 'com.wdullaer:materialdatetimepicker:4.2.3' implementation 'com.wdullaer:materialdatetimepicker:4.2.3'
implementation "io.coil-kt:coil:0.9.5" implementation "io.coil-kt:coil:0.11.0"
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
playImplementation 'com.google.firebase:firebase-analytics:17.3.0' playImplementation 'com.google.firebase:firebase-analytics:17.4.2'
playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx:19.0.4' playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx:19.0.7'
playImplementation "com.google.firebase:firebase-inappmessaging-ktx:19.0.4" playImplementation "com.google.firebase:firebase-inappmessaging-ktx:19.0.7"
playImplementation "com.google.firebase:firebase-messaging:20.1.0" playImplementation 'com.google.firebase:firebase-messaging:20.2.0'
playImplementation "com.crashlytics.sdk.android:crashlytics:2.10.1" playImplementation 'com.google.firebase:firebase-crashlytics:17.0.0'
playImplementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava' playImplementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker" releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
@ -200,7 +195,7 @@ dependencies {
testImplementation "junit:junit:4.13" testImplementation "junit:junit:4.13"
testImplementation "io.mockk:mockk:$mockk" testImplementation "io.mockk:mockk:$mockk"
testImplementation "org.threeten:threetenbp:1.4.3" testImplementation "org.threeten:threetenbp:1.4.4"
testImplementation "org.mockito:mockito-inline:3.3.3" testImplementation "org.mockito:mockito-inline:3.3.3"
androidTestImplementation "androidx.test:core:1.2.0" androidTestImplementation "androidx.test:core:1.2.0"

View File

@ -0,0 +1,29 @@
package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.db.entities.Student
import org.threeten.bp.LocalDateTime
fun getStudent(): Student {
return Student(
email = "test",
password = "test123",
schoolSymbol = "23",
scrapperBaseUrl = "fakelog.cf",
loginType = "AUTO",
isCurrent = true,
studentName = "",
schoolShortName = "",
schoolName = "",
studentId = 0,
classId = 1,
symbol = "",
registrationDate = LocalDateTime.now(),
className = "",
loginMode = "API",
certificateKey = "",
privateKey = "",
mobileBaseUrl = "",
userLoginId = 0,
isParent = false
)
}

View File

@ -24,7 +24,7 @@ class GradeLocalTest {
fun createDb() { fun createDb() {
testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java)
.build() .build()
gradeLocal = GradeLocal(testDb.gradeDao) gradeLocal = GradeLocal(testDb.gradeDao, testDb.gradeSummaryDao)
} }
@After @After
@ -43,7 +43,7 @@ class GradeLocalTest {
val semester = Semester(1, 2, "", 2019, 2, 1, now(), now(), 1, 1) val semester = Semester(1, 2, "", 2019, 2, 1, now(), now(), 1, 1)
val grades = gradeLocal val grades = gradeLocal
.getGrades(semester) .getGradesDetails(semester)
.blockingGet() .blockingGet()
assertEquals(2, grades.size) assertEquals(2, grades.size)

View File

@ -11,6 +11,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Grade
import io.mockk.MockKAnnotations import io.mockk.MockKAnnotations
import io.mockk.every import io.mockk.every
import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.MockK
@ -52,7 +53,7 @@ class GradeRepositoryTest {
fun initApi() { fun initApi() {
MockKAnnotations.init(this) MockKAnnotations.init(this)
testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build() testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build()
gradeLocal = GradeLocal(testDb.gradeDao) gradeLocal = GradeLocal(testDb.gradeDao, testDb.gradeSummaryDao)
gradeRemote = GradeRemote(mockSdk) gradeRemote = GradeRemote(mockSdk)
every { studentMock.registrationDate } returns LocalDateTime.of(2019, 2, 27, 12, 0) every { studentMock.registrationDate } returns LocalDateTime.of(2019, 2, 27, 12, 0)
@ -75,10 +76,10 @@ class GradeRepositoryTest {
createGradeApi(5, 4.0, of(2019, 2, 26), "przed zalogowanie w aplikacji"), createGradeApi(5, 4.0, of(2019, 2, 26), "przed zalogowanie w aplikacji"),
createGradeApi(5, 4.0, of(2019, 2, 27), "Ocena z dnia logowania"), createGradeApi(5, 4.0, of(2019, 2, 27), "Ocena z dnia logowania"),
createGradeApi(5, 4.0, of(2019, 2, 28), "Ocena jeszcze nowsza") createGradeApi(5, 4.0, of(2019, 2, 28), "Ocena jeszcze nowsza")
)) ) to emptyList())
val grades = GradeRepository(settings, gradeLocal, gradeRemote) val grades = GradeRepository(settings, gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true).blockingGet().sortedByDescending { it.date } .getGrades(studentMock, semesterMock, true).blockingGet().first.sortedByDescending { it.date }
assertFalse { grades[0].isRead } assertFalse { grades[0].isRead }
assertFalse { grades[1].isRead } assertFalse { grades[1].isRead }
@ -99,10 +100,10 @@ class GradeRepositoryTest {
createGradeApi(4, 3.0, of(2019, 2, 26), "starszą niż ostatnia lokalnie"), createGradeApi(4, 3.0, of(2019, 2, 26), "starszą niż ostatnia lokalnie"),
createGradeApi(3, 4.0, of(2019, 2, 27), "Ta jest z tego samego dnia co ostatnia lokalnie"), createGradeApi(3, 4.0, of(2019, 2, 27), "Ta jest z tego samego dnia co ostatnia lokalnie"),
createGradeApi(2, 5.0, of(2019, 2, 28), "Ta jest już w ogóle nowa") createGradeApi(2, 5.0, of(2019, 2, 28), "Ta jest już w ogóle nowa")
)) ) to emptyList())
val grades = GradeRepository(settings, gradeLocal, gradeRemote) val grades = GradeRepository(settings, gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true).blockingGet().sortedByDescending { it.date } .getGrades(studentMock, semesterMock, true).blockingGet().first.sortedByDescending { it.date }
assertFalse { grades[0].isRead } assertFalse { grades[0].isRead }
assertFalse { grades[1].isRead } assertFalse { grades[1].isRead }
@ -121,12 +122,12 @@ class GradeRepositoryTest {
every { mockSdk.getGrades(1) } returns Single.just(listOf( every { mockSdk.getGrades(1) } returns Single.just(listOf(
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
)) ) to emptyList())
val grades = GradeRepository(settings, gradeLocal, gradeRemote) val grades = GradeRepository(settings, gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true).blockingGet() .getGrades(studentMock, semesterMock, true).blockingGet()
assertEquals(2, grades.size) assertEquals(2, grades.first.size)
} }
@Test @Test
@ -140,12 +141,12 @@ class GradeRepositoryTest {
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
)) ) to emptyList())
val grades = GradeRepository(settings, gradeLocal, gradeRemote) val grades = GradeRepository(settings, gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true).blockingGet() .getGrades(studentMock, semesterMock, true).blockingGet()
assertEquals(3, grades.size) assertEquals(3, grades.first.size)
} }
@Test @Test
@ -156,12 +157,12 @@ class GradeRepositoryTest {
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
)) ) to emptyList())
val grades = GradeRepository(settings, gradeLocal, gradeRemote) val grades = GradeRepository(settings, gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true).blockingGet() .getGrades(studentMock, semesterMock, true).blockingGet()
assertEquals(3, grades.size) assertEquals(3, grades.first.size)
} }
@Test @Test
@ -171,11 +172,11 @@ class GradeRepositoryTest {
createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
)) ))
every { mockSdk.getGrades(1) } returns Single.just(listOf()) every { mockSdk.getGrades(1) } returns Single.just(emptyList<Grade>() to emptyList())
val grades = GradeRepository(settings, gradeLocal, gradeRemote) val grades = GradeRepository(settings, gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true).blockingGet() .getGrades(studentMock, semesterMock, true).blockingGet()
assertEquals(0, grades.size) assertEquals(0, grades.first.size)
} }
} }

View File

@ -5,13 +5,11 @@ import androidx.room.Room
import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.repositories.getStudent
import io.github.wulkanowy.data.db.entities.Student
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.threeten.bp.LocalDateTime.now
import kotlin.test.assertEquals import kotlin.test.assertEquals
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@ -21,14 +19,13 @@ class StudentLocalTest {
private lateinit var testDb: AppDatabase private lateinit var testDb: AppDatabase
private lateinit var sharedProvider: SharedPrefProvider private val student = getStudent()
@Before @Before
fun createDb() { fun createDb() {
val context = ApplicationProvider.getApplicationContext<Context>() val context = ApplicationProvider.getApplicationContext<Context>()
testDb = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java) testDb = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java)
.build() .build()
sharedProvider = SharedPrefProvider(context.getSharedPreferences("TEST", Context.MODE_PRIVATE))
studentLocal = StudentLocal(testDb.studentDao, context) studentLocal = StudentLocal(testDb.studentDao, context)
} }
@ -39,8 +36,7 @@ class StudentLocalTest {
@Test @Test
fun saveAndReadTest() { fun saveAndReadTest() {
studentLocal.saveStudents(listOf(Student(email = "test", password = "test123", schoolSymbol = "23", scrapperBaseUrl = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolShortName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now(), className = "", loginMode = "API", certificateKey = "", privateKey = "", mobileBaseUrl = "", userLoginId = 0, isParent = false))) studentLocal.saveStudents(listOf(student)).blockingGet()
.blockingGet()
val student = studentLocal.getCurrentStudent(true).blockingGet() val student = studentLocal.getCurrentStudent(true).blockingGet()
assertEquals("23", student.schoolSymbol) assertEquals("23", student.schoolSymbol)

View File

@ -8,11 +8,15 @@ import androidx.test.filters.SdkSuppress
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy
import io.github.wulkanowy.data.repositories.getStudent
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.mockk.MockKAnnotations import io.mockk.MockKAnnotations
import io.mockk.every import io.mockk.every
import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.MockK
import io.mockk.mockk
import io.reactivex.Single import io.reactivex.Single
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -33,9 +37,17 @@ class TimetableRepositoryTest {
.strategy(TestInternetObservingStrategy()) .strategy(TestInternetObservingStrategy())
.build() .build()
@MockK
private lateinit var studentMock: Student
private val student = getStudent()
@MockK @MockK
private lateinit var semesterMock: Semester private lateinit var semesterMock: Semester
@MockK
private lateinit var timetableNotificationSchedulerHelper: TimetableNotificationSchedulerHelper
private lateinit var timetableRemote: TimetableRemote private lateinit var timetableRemote: TimetableRemote
private lateinit var timetableLocal: TimetableLocal private lateinit var timetableLocal: TimetableLocal
@ -49,10 +61,17 @@ class TimetableRepositoryTest {
timetableLocal = TimetableLocal(testDb.timetableDao) timetableLocal = TimetableLocal(testDb.timetableDao)
timetableRemote = TimetableRemote(mockSdk) timetableRemote = TimetableRemote(mockSdk)
every { timetableNotificationSchedulerHelper.scheduleNotifications(any(), any()) } returns mockk()
every { timetableNotificationSchedulerHelper.cancelScheduled(any(), any()) } returns mockk()
every { studentMock.studentId } returns 1
every { studentMock.studentName } returns "Jan Kowalski"
every { semesterMock.studentId } returns 1 every { semesterMock.studentId } returns 1
every { semesterMock.diaryId } returns 2 every { semesterMock.diaryId } returns 2
every { semesterMock.schoolYear } returns 2019 every { semesterMock.schoolYear } returns 2019
every { semesterMock.semesterId } returns 1 every { semesterMock.semesterId } returns 1
every { mockSdk.switchDiary(any(), any()) } returns mockSdk every { mockSdk.switchDiary(any(), any()) } returns mockSdk
} }
@ -77,8 +96,8 @@ class TimetableRepositoryTest {
createTimetableRemote(of(2019, 3, 5, 10, 30), 4, "", "W-F") createTimetableRemote(of(2019, 3, 5, 10, 30), 4, "", "W-F")
)) ))
val lessons = TimetableRepository(settings, timetableLocal, timetableRemote) val lessons = TimetableRepository(settings, timetableLocal, timetableRemote, timetableNotificationSchedulerHelper)
.getTimetable(semesterMock, LocalDate.of(2019, 3, 5), LocalDate.of(2019, 3, 5), true) .getTimetable(student, semesterMock, LocalDate.of(2019, 3, 5), LocalDate.of(2019, 3, 5), true)
.blockingGet() .blockingGet()
assertEquals(4, lessons.size) assertEquals(4, lessons.size)
@ -123,8 +142,8 @@ class TimetableRepositoryTest {
createTimetableRemote(of(2019, 12, 25, 10, 40), 4, "126", "Matematyka", "Paweł Czwartkowski", true) createTimetableRemote(of(2019, 12, 25, 10, 40), 4, "126", "Matematyka", "Paweł Czwartkowski", true)
)) ))
val lessons = TimetableRepository(settings, timetableLocal, timetableRemote) val lessons = TimetableRepository(settings, timetableLocal, timetableRemote, timetableNotificationSchedulerHelper)
.getTimetable(semesterMock, LocalDate.of(2019, 12, 23), LocalDate.of(2019, 12, 25), true) .getTimetable(student, semesterMock, LocalDate.of(2019, 12, 23), LocalDate.of(2019, 12, 25), true)
.blockingGet() .blockingGet()
assertEquals(12, lessons.size) assertEquals(12, lessons.size)

View File

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

View File

@ -5,8 +5,6 @@ package io.github.wulkanowy.utils
import android.content.Context import android.content.Context
import timber.log.Timber import timber.log.Timber
fun initCrashlytics(context: Context, appInfo: AppInfo) {}
open class TimberTreeNoOp : Timber.Tree() { open class TimberTreeNoOp : Timber.Tree() {
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {} override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {}
} }

View File

@ -1,13 +1,18 @@
package io.github.wulkanowy.utils package io.github.wulkanowy.utils
import android.app.Activity
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@Suppress("UNUSED_PARAMETER")
class FirebaseAnalyticsHelper @Inject constructor() { class FirebaseAnalyticsHelper @Inject constructor() {
@Suppress("UNUSED_PARAMETER")
fun logEvent(name: String, vararg params: Pair<String, Any?>) { fun logEvent(name: String, vararg params: Pair<String, Any?>) {
// do nothing // do nothing
} }
fun setCurrentScreen(activity: Activity, name: String?) {
// do nothing
}
} }

View File

@ -18,7 +18,8 @@
android:supportsRtl="false" android:supportsRtl="false"
android:theme="@style/WulkanowyTheme" android:theme="@style/WulkanowyTheme"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute"> tools:ignore="GoogleAppIndexingWarning,UnusedAttribute"
tools:replace="android:supportsRtl,android:allowBackup">
<activity <activity
android:name=".ui.modules.splash.SplashActivity" android:name=".ui.modules.splash.SplashActivity"
android:screenOrientation="portrait" android:screenOrientation="portrait"
@ -39,7 +40,8 @@
android:name=".ui.modules.main.MainActivity" android:name=".ui.modules.main.MainActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:label="@string/main_title" android:label="@string/main_title"
android:theme="@style/WulkanowyTheme.NoActionBar" /> android:theme="@style/WulkanowyTheme.NoActionBar"
android:windowSoftInputMode="adjustPan" />
<activity <activity
android:name=".ui.modules.message.send.SendMessageActivity" android:name=".ui.modules.message.send.SendMessageActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
@ -91,6 +93,8 @@
android:resource="@xml/provider_widget_lucky_number" /> android:resource="@xml/provider_widget_lucky_number" />
</receiver> </receiver>
<receiver android:name=".services.alarm.TimetableNotificationReceiver" />
<provider <provider
android:name="androidx.work.impl.WorkManagerInitializer" android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init" android:authorities="${applicationId}.workmanager-init"
@ -107,12 +111,33 @@
android:resource="@xml/provider_paths" /> android:resource="@xml/provider_paths" />
</provider> </provider>
<!-- workaround for https://github.com/firebase/firebase-android-sdk/issues/473 enabled:false -->
<!-- https://firebase.googleblog.com/2017/03/take-control-of-your-firebase-init-on.html -->
<provider
android:name="com.google.firebase.provider.FirebaseInitProvider"
android:authorities="${applicationId}.firebaseinitprovider"
android:enabled="${firebase_enabled}"
android:exported="false" />
<meta-data <meta-data
android:name="io.fabric.ApiKey" android:name="firebase_analytics_collection_enabled"
android:value="${fabric_api_key}" /> android:value="${firebase_enabled}" />
<meta-data
android:name="google_analytics_adid_collection_enabled"
android:value="${firebase_enabled}" />
<meta-data <meta-data
android:name="firebase_crashlytics_collection_enabled" android:name="firebase_crashlytics_collection_enabled"
android:value="${crashlytics_enabled}" /> android:value="${firebase_enabled}" />
<meta-data
android:name="firebase_messaging_auto_init_enabled"
android:value="${firebase_enabled}" />
<meta-data
android:name="firebase_inapp_messaging_auto_data_collection_enabled"
android:value="${firebase_enabled}" />
<meta-data <meta-data
android:name="com.google.firebase.messaging.default_notification_icon" android:name="com.google.firebase.messaging.default_notification_icon"

View File

@ -10,8 +10,6 @@ import com.jakewharton.threetenabp.AndroidThreeTen
import com.yariksoffice.lingver.Lingver import com.yariksoffice.lingver.Lingver
import dagger.android.AndroidInjector import dagger.android.AndroidInjector
import dagger.android.support.DaggerApplication import dagger.android.support.DaggerApplication
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.utils.Log
import fr.bipi.tressence.file.FileLoggerTree import fr.bipi.tressence.file.FileLoggerTree
import io.github.wulkanowy.di.DaggerAppComponent import io.github.wulkanowy.di.DaggerAppComponent
import io.github.wulkanowy.services.sync.SyncWorkerFactory import io.github.wulkanowy.services.sync.SyncWorkerFactory
@ -21,7 +19,6 @@ import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.CrashlyticsExceptionTree import io.github.wulkanowy.utils.CrashlyticsExceptionTree
import io.github.wulkanowy.utils.CrashlyticsTree import io.github.wulkanowy.utils.CrashlyticsTree
import io.github.wulkanowy.utils.DebugLogTree import io.github.wulkanowy.utils.DebugLogTree
import io.github.wulkanowy.utils.initCrashlytics
import io.reactivex.exceptions.UndeliverableException import io.reactivex.exceptions.UndeliverableException
import io.reactivex.plugins.RxJavaPlugins import io.reactivex.plugins.RxJavaPlugins
import timber.log.Timber import timber.log.Timber
@ -52,12 +49,10 @@ class WulkanowyApp : DaggerApplication(), Configuration.Provider {
themeManager.applyDefaultTheme() themeManager.applyDefaultTheme()
initLogging() initLogging()
initCrashlytics(this, appInfo)
} }
private fun initLogging() { private fun initLogging() {
if (appInfo.isDebug) { if (appInfo.isDebug) {
FlexibleAdapter.enableLogs(Log.Level.DEBUG)
Timber.plant(DebugLogTree()) Timber.plant(DebugLogTree())
Timber.plant(FileLoggerTree.Builder() Timber.plant(FileLoggerTree.Builder()
.withFileName("wulkanowy.%g.log") .withFileName("wulkanowy.%g.log")

View File

@ -1,9 +1,11 @@
package io.github.wulkanowy.data package io.github.wulkanowy.data
import android.app.AlarmManager
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.res.AssetManager import android.content.res.AssetManager
import android.content.res.Resources import android.content.res.Resources
import androidx.core.content.getSystemService
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.strategy.WalledGardenInternetObservingStrategy import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.strategy.WalledGardenInternetObservingStrategy

View File

@ -1,30 +0,0 @@
package io.github.wulkanowy.data
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk
import javax.inject.Inject
class SdkHelper @Inject constructor(private val sdk: Sdk) {
fun init(student: Student) {
sdk.apply {
email = student.email
password = student.password
symbol = student.symbol
schoolSymbol = student.schoolSymbol
studentId = student.studentId
classId = student.classId
if (Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
scrapperBaseUrl = student.scrapperBaseUrl
loginType = Sdk.ScrapperLoginType.valueOf(student.loginType)
}
loginId = student.userLoginId
mode = Sdk.Mode.valueOf(student.loginMode)
mobileBaseUrl = student.mobileBaseUrl
certKey = student.certificateKey
privateKey = student.privateKey
}
}
}

View File

@ -22,6 +22,9 @@ interface StudentDao {
@Query("SELECT * FROM Students WHERE is_current = 1") @Query("SELECT * FROM Students WHERE is_current = 1")
fun loadCurrent(): Maybe<Student> fun loadCurrent(): Maybe<Student>
@Query("SELECT * FROM Students WHERE id = :id")
fun loadById(id: Int): Maybe<Student>
@Query("SELECT * FROM Students") @Query("SELECT * FROM Students")
fun loadAll(): Maybe<List<Student>> fun loadAll(): Maybe<List<Student>>

View File

@ -1,3 +0,0 @@
package io.github.wulkanowy.data.pojos
class AppCreator(val displayName: String, val githubUsername: String)

View File

@ -0,0 +1,3 @@
package io.github.wulkanowy.data.pojos
class Contributor(val displayName: String, val githubUsername: String)

View File

@ -2,18 +2,18 @@ package io.github.wulkanowy.data.repositories.appcreator
import android.content.res.AssetManager import android.content.res.AssetManager
import com.google.gson.Gson import com.google.gson.Gson
import io.github.wulkanowy.data.pojos.AppCreator import io.github.wulkanowy.data.pojos.Contributor
import io.reactivex.Single import io.reactivex.Single
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class AppCreatorRepository @Inject constructor(private val assets: AssetManager) { class AppCreatorRepository @Inject constructor(private val assets: AssetManager) {
fun getAppCreators(): Single<List<AppCreator>> { fun getAppCreators(): Single<List<Contributor>> {
return Single.fromCallable<List<AppCreator>> { return Single.fromCallable<List<Contributor>> {
Gson().fromJson( Gson().fromJson(
assets.open("contributors.json").bufferedReader().use { it.readText() }, assets.open("contributors.json").bufferedReader().use { it.readText() },
Array<AppCreator>::class.java Array<Contributor>::class.java
).toList() ).toList()
} }
} }

View File

@ -2,8 +2,10 @@ package io.github.wulkanowy.data.repositories.attendance
import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Absent import io.github.wulkanowy.sdk.pojo.Absent
import io.github.wulkanowy.utils.init
import io.reactivex.Single import io.reactivex.Single
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import org.threeten.bp.LocalDateTime import org.threeten.bp.LocalDateTime
@ -14,8 +16,9 @@ import javax.inject.Singleton
@Singleton @Singleton
class AttendanceRemote @Inject constructor(private val sdk: Sdk) { class AttendanceRemote @Inject constructor(private val sdk: Sdk) {
fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Attendance>> { fun getAttendance(student: Student, semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Attendance>> {
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getAttendance(startDate, endDate, semester.semesterId) return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getAttendance(startDate, endDate, semester.semesterId)
.map { attendance -> .map { attendance ->
attendance.map { attendance.map {
Attendance( Attendance(
@ -39,8 +42,8 @@ class AttendanceRemote @Inject constructor(private val sdk: Sdk) {
} }
} }
fun excuseAbsence(semester: Semester, absenceList: List<Attendance>, reason: String?): Single<Boolean> { fun excuseAbsence(student: Student, semester: Semester, absenceList: List<Attendance>, reason: String?): Single<Boolean> {
return sdk.switchDiary(semester.diaryId, semester.schoolYear).excuseForAbsence(absenceList.map { attendance -> return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).excuseForAbsence(absenceList.map { attendance ->
Absent( Absent(
date = LocalDateTime.of(attendance.date, LocalTime.of(0, 0)), date = LocalDateTime.of(attendance.date, LocalTime.of(0, 0)),
timeId = attendance.timeId timeId = attendance.timeId

View File

@ -4,7 +4,8 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.utils.friday import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single import io.reactivex.Single
@ -20,29 +21,25 @@ class AttendanceRepository @Inject constructor(
private val remote: AttendanceRemote private val remote: AttendanceRemote
) { ) {
fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean) fun getAttendance(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean): Single<List<Attendance>> {
: Single<List<Attendance>> { return local.getAttendance(semester, start.monday, end.sunday).filter { !forceRefresh }
return Single.fromCallable { startDate.monday to endDate.friday } .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
.flatMap { dates -> if (it) remote.getAttendance(student, semester, start.monday, end.sunday)
local.getAttendance(semester, dates.first, dates.second).filter { !forceRefresh } else Single.error(UnknownHostException())
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap { }.flatMap { newAttendance ->
if (it) remote.getAttendance(semester, dates.first, dates.second) local.getAttendance(semester, start.monday, end.sunday)
else Single.error(UnknownHostException()) .toSingle(emptyList())
}.flatMap { newAttendance -> .doOnSuccess { oldAttendance ->
local.getAttendance(semester, dates.first, dates.second) local.deleteAttendance(oldAttendance.uniqueSubtract(newAttendance))
.toSingle(emptyList()) local.saveAttendance(newAttendance.uniqueSubtract(oldAttendance))
.doOnSuccess { oldAttendance -> }
local.deleteAttendance(oldAttendance.uniqueSubtract(newAttendance)) }.flatMap {
local.saveAttendance(newAttendance.uniqueSubtract(oldAttendance)) local.getAttendance(semester, start.monday, end.sunday)
} .toSingle(emptyList())
}.flatMap { }).map { list -> list.filter { it.date in start..end } }
local.getAttendance(semester, dates.first, dates.second)
.toSingle(emptyList())
}).map { list -> list.filter { it.date in startDate..endDate } }
}
} }
fun excuseForAbsence(semester: Semester, attendanceList: List<Attendance>, reason: String? = null): Single<Boolean> { fun excuseForAbsence(student: Student, semester: Semester, attendanceList: List<Attendance>, reason: String? = null): Single<Boolean> {
return remote.excuseAbsence(semester, attendanceList, reason) return remote.excuseAbsence(student, semester, attendanceList, reason)
} }
} }

View File

@ -2,7 +2,9 @@ package io.github.wulkanowy.data.repositories.attendancesummary
import io.github.wulkanowy.data.db.entities.AttendanceSummary import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
import io.reactivex.Single import io.reactivex.Single
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -10,8 +12,9 @@ import javax.inject.Singleton
@Singleton @Singleton
class AttendanceSummaryRemote @Inject constructor(private val sdk: Sdk) { class AttendanceSummaryRemote @Inject constructor(private val sdk: Sdk) {
fun getAttendanceSummary(semester: Semester, subjectId: Int): Single<List<AttendanceSummary>> { fun getAttendanceSummary(student: Student, semester: Semester, subjectId: Int): Single<List<AttendanceSummary>> {
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getAttendanceSummary(subjectId) return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getAttendanceSummary(subjectId)
.map { attendance -> .map { attendance ->
attendance.map { attendance.map {
AttendanceSummary( AttendanceSummary(

View File

@ -4,6 +4,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.AttendanceSummary import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single import io.reactivex.Single
import java.net.UnknownHostException import java.net.UnknownHostException
@ -17,11 +18,11 @@ class AttendanceSummaryRepository @Inject constructor(
private val remote: AttendanceSummaryRemote private val remote: AttendanceSummaryRemote
) { ) {
fun getAttendanceSummary(semester: Semester, subjectId: Int, forceRefresh: Boolean = false): Single<List<AttendanceSummary>> { fun getAttendanceSummary(student: Student, semester: Semester, subjectId: Int, forceRefresh: Boolean = false): Single<List<AttendanceSummary>> {
return local.getAttendanceSummary(semester, subjectId).filter { !forceRefresh } return local.getAttendanceSummary(semester, subjectId).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap { .flatMap {
if (it) remote.getAttendanceSummary(semester, subjectId) if (it) remote.getAttendanceSummary(student, semester, subjectId)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
}.flatMap { new -> }.flatMap { new ->
local.getAttendanceSummary(semester, subjectId).toSingle(emptyList()) local.getAttendanceSummary(semester, subjectId).toSingle(emptyList())

View File

@ -2,7 +2,9 @@ package io.github.wulkanowy.data.repositories.completedlessons
import io.github.wulkanowy.data.db.entities.CompletedLesson import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
import io.reactivex.Single import io.reactivex.Single
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import javax.inject.Inject import javax.inject.Inject
@ -11,8 +13,9 @@ import javax.inject.Singleton
@Singleton @Singleton
class CompletedLessonsRemote @Inject constructor(private val sdk: Sdk) { class CompletedLessonsRemote @Inject constructor(private val sdk: Sdk) {
fun getCompletedLessons(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<CompletedLesson>> { fun getCompletedLessons(student: Student, semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<CompletedLesson>> {
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getCompletedLessons(startDate, endDate) return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getCompletedLessons(startDate, endDate)
.map { lessons -> .map { lessons ->
lessons.map { lessons.map {
it.absence it.absence

View File

@ -4,7 +4,8 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.CompletedLesson import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.utils.friday import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single import io.reactivex.Single
@ -20,25 +21,22 @@ class CompletedLessonsRepository @Inject constructor(
private val remote: CompletedLessonsRemote private val remote: CompletedLessonsRemote
) { ) {
fun getCompletedLessons(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean = false): Single<List<CompletedLesson>> { fun getCompletedLessons(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): Single<List<CompletedLesson>> {
return Single.fromCallable { startDate.monday to endDate.friday } return local.getCompletedLessons(semester, start.monday, end.sunday).filter { !forceRefresh }
.flatMap { dates -> .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
local.getCompletedLessons(semester, dates.first, dates.second).filter { !forceRefresh } .flatMap {
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) if (it) remote.getCompletedLessons(student, semester, start.monday, end.sunday)
.flatMap { else Single.error(UnknownHostException())
if (it) remote.getCompletedLessons(semester, dates.first, dates.second) }.flatMap { new ->
else Single.error(UnknownHostException()) local.getCompletedLessons(semester, start.monday, end.sunday)
}.flatMap { new -> .toSingle(emptyList())
local.getCompletedLessons(semester, dates.first, dates.second) .doOnSuccess { old ->
.toSingle(emptyList()) local.deleteCompleteLessons(old.uniqueSubtract(new))
.doOnSuccess { old -> local.saveCompletedLessons(new.uniqueSubtract(old))
local.deleteCompleteLessons(old.uniqueSubtract(new)) }
local.saveCompletedLessons(new.uniqueSubtract(old)) }.flatMap {
} local.getCompletedLessons(semester, start.monday, end.sunday)
}.flatMap { .toSingle(emptyList())
local.getCompletedLessons(semester, dates.first, dates.second) }).map { list -> list.filter { it.date in start..end } }
.toSingle(emptyList())
}).map { list -> list.filter { it.date in startDate..endDate } }
}
} }
} }

View File

@ -2,7 +2,9 @@ package io.github.wulkanowy.data.repositories.exam
import io.github.wulkanowy.data.db.entities.Exam import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
import io.reactivex.Single import io.reactivex.Single
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import javax.inject.Inject import javax.inject.Inject
@ -11,8 +13,9 @@ import javax.inject.Singleton
@Singleton @Singleton
class ExamRemote @Inject constructor(private val sdk: Sdk) { class ExamRemote @Inject constructor(private val sdk: Sdk) {
fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Exam>> { fun getExams(student: Student, semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Exam>> {
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getExams(startDate, endDate, semester.semesterId) return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getExams(startDate, endDate, semester.semesterId)
.map { exams -> .map { exams ->
exams.map { exams.map {
Exam( Exam(

View File

@ -4,7 +4,8 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.Exam import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.utils.friday import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single import io.reactivex.Single
@ -20,25 +21,22 @@ class ExamRepository @Inject constructor(
private val remote: ExamRemote private val remote: ExamRemote
) { ) {
fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean = false): Single<List<Exam>> { fun getExams(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): Single<List<Exam>> {
return Single.fromCallable { startDate.monday to endDate.friday } return local.getExams(semester, start.monday, end.sunday).filter { !forceRefresh }
.flatMap { dates -> .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
local.getExams(semester, dates.first, dates.second).filter { !forceRefresh } .flatMap {
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) if (it) remote.getExams(student, semester, start.monday, end.sunday)
.flatMap { else Single.error(UnknownHostException())
if (it) remote.getExams(semester, dates.first, dates.second) }.flatMap { new ->
else Single.error(UnknownHostException()) local.getExams(semester, start.monday, end.sunday)
}.flatMap { new -> .toSingle(emptyList())
local.getExams(semester, dates.first, dates.second) .doOnSuccess { old ->
.toSingle(emptyList()) local.deleteExams(old.uniqueSubtract(new))
.doOnSuccess { old -> local.saveExams(new.uniqueSubtract(old))
local.deleteExams(old.uniqueSubtract(new)) }
local.saveExams(new.uniqueSubtract(old)) }.flatMap {
} local.getExams(semester, start.monday, end.sunday)
}.flatMap { .toSingle(emptyList())
local.getExams(semester, dates.first, dates.second) }).map { list -> list.filter { it.date in start..end } }
.toSingle(emptyList())
}).map { list -> list.filter { it.date in startDate..endDate } }
}
} }
} }

View File

@ -1,14 +1,19 @@
package io.github.wulkanowy.data.repositories.grade package io.github.wulkanowy.data.repositories.grade
import io.github.wulkanowy.data.db.dao.GradeDao import io.github.wulkanowy.data.db.dao.GradeDao
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Maybe import io.reactivex.Maybe
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class GradeLocal @Inject constructor(private val gradeDb: GradeDao) { class GradeLocal @Inject constructor(
private val gradeDb: GradeDao,
private val gradeSummaryDb: GradeSummaryDao
) {
fun saveGrades(grades: List<Grade>) { fun saveGrades(grades: List<Grade>) {
gradeDb.insertAll(grades) gradeDb.insertAll(grades)
@ -22,7 +27,19 @@ class GradeLocal @Inject constructor(private val gradeDb: GradeDao) {
gradeDb.updateAll(grades) gradeDb.updateAll(grades)
} }
fun getGrades(semester: Semester): Maybe<List<Grade>> { fun getGradesDetails(semester: Semester): Maybe<List<Grade>> {
return gradeDb.loadAll(semester.semesterId, semester.studentId).filter { it.isNotEmpty() } return gradeDb.loadAll(semester.semesterId, semester.studentId).filter { it.isNotEmpty() }
} }
fun saveGradesSummary(gradesSummary: List<GradeSummary>) {
gradeSummaryDb.insertAll(gradesSummary)
}
fun deleteGradesSummary(gradesSummary: List<GradeSummary>) {
gradeSummaryDb.deleteAll(gradesSummary)
}
fun getGradesSummary(semester: Semester): Maybe<List<GradeSummary>> {
return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId).filter { it.isNotEmpty() }
}
} }

View File

@ -1,8 +1,11 @@
package io.github.wulkanowy.data.repositories.grade package io.github.wulkanowy.data.repositories.grade
import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
import io.reactivex.Single import io.reactivex.Single
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -10,10 +13,11 @@ import javax.inject.Singleton
@Singleton @Singleton
class GradeRemote @Inject constructor(private val sdk: Sdk) { class GradeRemote @Inject constructor(private val sdk: Sdk) {
fun getGrades(semester: Semester): Single<List<Grade>> { fun getGrades(student: Student, semester: Semester): Single<Pair<List<Grade>, List<GradeSummary>>> {
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getGrades(semester.semesterId) return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.map { grades -> .getGrades(semester.semesterId)
grades.map { .map { (details, summary) ->
details.map {
Grade( Grade(
studentId = semester.studentId, studentId = semester.studentId,
semesterId = semester.semesterId, semesterId = semester.semesterId,
@ -30,6 +34,19 @@ class GradeRemote @Inject constructor(private val sdk: Sdk) {
date = it.date, date = it.date,
teacher = it.teacher teacher = it.teacher
) )
} to summary.map {
GradeSummary(
semesterId = semester.semesterId,
studentId = semester.studentId,
position = 0,
subject = it.name,
predictedGrade = it.predicted,
finalGrade = it.final,
pointsSum = it.pointsSum,
proposedPoints = it.proposedPoints,
finalPoints = it.finalPoints,
average = it.average
)
} }
} }
} }

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.grade
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
@ -19,34 +20,47 @@ class GradeRepository @Inject constructor(
private val remote: GradeRemote private val remote: GradeRemote
) { ) {
fun getGrades(student: Student, semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Single<List<Grade>> { fun getGrades(student: Student, semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Single<Pair<List<Grade>, List<GradeSummary>>> {
return local.getGrades(semester).filter { !forceRefresh } return local.getGradesDetails(semester).flatMap { details ->
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) local.getGradesSummary(semester).map { summary -> details to summary }
.flatMap { }.filter { !forceRefresh }
if (it) remote.getGrades(semester) .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
else Single.error(UnknownHostException()) if (it) remote.getGrades(student, semester)
}.flatMap { new -> else Single.error(UnknownHostException())
local.getGrades(semester).toSingle(emptyList()) }.flatMap { (newDetails, newSummary) ->
.doOnSuccess { old -> local.getGradesDetails(semester).toSingle(emptyList())
val notifyBreakDate = old.maxBy { it.date }?.date ?: student.registrationDate.toLocalDate() .doOnSuccess { old ->
local.deleteGrades(old.uniqueSubtract(new)) val notifyBreakDate = old.maxBy { it.date }?.date ?: student.registrationDate.toLocalDate()
local.saveGrades(new.uniqueSubtract(old) local.deleteGrades(old.uniqueSubtract(newDetails))
.onEach { local.saveGrades(newDetails.uniqueSubtract(old)
if (it.date >= notifyBreakDate) it.apply { .onEach {
isRead = false if (it.date >= notifyBreakDate) it.apply {
if (notify) isNotified = false isRead = false
} if (notify) isNotified = false
}) }
} })
}.flatMap { local.getGrades(semester).toSingle(emptyList()) }) }.flatMap {
local.getGradesSummary(semester).toSingle(emptyList())
.doOnSuccess { old ->
local.deleteGradesSummary(old.uniqueSubtract(newSummary))
local.saveGradesSummary(newSummary.uniqueSubtract(old))
}
}
}.flatMap {
local.getGradesDetails(semester).toSingle(emptyList()).flatMap { details ->
local.getGradesSummary(semester).toSingle(emptyList()).map { summary ->
details to summary
}
}
})
} }
fun getUnreadGrades(semester: Semester): Single<List<Grade>> { fun getUnreadGrades(semester: Semester): Single<List<Grade>> {
return local.getGrades(semester).map { it.filter { grade -> !grade.isRead } }.toSingle(emptyList()) return local.getGradesDetails(semester).map { it.filter { grade -> !grade.isRead } }.toSingle(emptyList())
} }
fun getNotNotifiedGrades(semester: Semester): Single<List<Grade>> { fun getNotNotifiedGrades(semester: Semester): Single<List<Grade>> {
return local.getGrades(semester).map { it.filter { grade -> !grade.isNotified } }.toSingle(emptyList()) return local.getGradesDetails(semester).map { it.filter { grade -> !grade.isNotified } }.toSingle(emptyList())
} }
fun updateGrade(grade: Grade): Completable { fun updateGrade(grade: Grade): Completable {

View File

@ -1,24 +0,0 @@
package io.github.wulkanowy.data.repositories.gradessummary
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Maybe
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class GradeSummaryLocal @Inject constructor(private val gradeSummaryDb: GradeSummaryDao) {
fun saveGradesSummary(gradesSummary: List<GradeSummary>) {
gradeSummaryDb.insertAll(gradesSummary)
}
fun deleteGradesSummary(gradesSummary: List<GradeSummary>) {
gradeSummaryDb.deleteAll(gradesSummary)
}
fun getGradesSummary(semester: Semester): Maybe<List<GradeSummary>> {
return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId).filter { it.isNotEmpty() }
}
}

View File

@ -1,32 +0,0 @@
package io.github.wulkanowy.data.repositories.gradessummary
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.sdk.Sdk
import io.reactivex.Single
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class GradeSummaryRemote @Inject constructor(private val sdk: Sdk) {
fun getGradeSummary(semester: Semester): Single<List<GradeSummary>> {
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getGradesSummary(semester.semesterId)
.map { gradesSummary ->
gradesSummary.map {
GradeSummary(
semesterId = semester.semesterId,
studentId = semester.studentId,
position = 0,
subject = it.name,
predictedGrade = it.predicted,
finalGrade = it.final,
pointsSum = it.pointsSum,
proposedPoints = it.proposedPoints,
finalPoints = it.finalPoints,
average = it.average
)
}
}
}
}

View File

@ -1,34 +0,0 @@
package io.github.wulkanowy.data.repositories.gradessummary
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.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single
import java.net.UnknownHostException
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class GradeSummaryRepository @Inject constructor(
private val settings: InternetObservingSettings,
private val local: GradeSummaryLocal,
private val remote: GradeSummaryRemote
) {
fun getGradesSummary(semester: Semester, forceRefresh: Boolean = false): Single<List<GradeSummary>> {
return local.getGradesSummary(semester).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
if (it) remote.getGradeSummary(semester)
else Single.error(UnknownHostException())
}.flatMap { new ->
local.getGradesSummary(semester).toSingle(emptyList())
.doOnSuccess { old ->
local.deleteGradesSummary(old.uniqueSubtract(new))
local.saveGradesSummary(new.uniqueSubtract(old))
}
}.flatMap { local.getGradesSummary(semester).toSingle(emptyList()) })
}
}

View File

@ -3,7 +3,9 @@ package io.github.wulkanowy.data.repositories.gradestatistics
import io.github.wulkanowy.data.db.entities.GradePointsStatistics import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeStatistics import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
import io.reactivex.Single import io.reactivex.Single
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -11,8 +13,8 @@ import javax.inject.Singleton
@Singleton @Singleton
class GradeStatisticsRemote @Inject constructor(private val sdk: Sdk) { class GradeStatisticsRemote @Inject constructor(private val sdk: Sdk) {
fun getGradeStatistics(semester: Semester, isSemester: Boolean): Single<List<GradeStatistics>> { fun getGradeStatistics(student: Student, semester: Semester, isSemester: Boolean): Single<List<GradeStatistics>> {
return sdk.switchDiary(semester.diaryId, semester.schoolYear).let { return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).let {
if (isSemester) it.getGradesAnnualStatistics(semester.semesterId) if (isSemester) it.getGradesAnnualStatistics(semester.semesterId)
else it.getGradesPartialStatistics(semester.semesterId) else it.getGradesPartialStatistics(semester.semesterId)
}.map { gradeStatistics -> }.map { gradeStatistics ->
@ -29,8 +31,9 @@ class GradeStatisticsRemote @Inject constructor(private val sdk: Sdk) {
} }
} }
fun getGradePointsStatistics(semester: Semester): Single<List<GradePointsStatistics>> { fun getGradePointsStatistics(student: Student, semester: Semester): Single<List<GradePointsStatistics>> {
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getGradesPointsStatistics(semester.semesterId) return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getGradesPointsStatistics(semester.semesterId)
.map { gradePointsStatistics -> .map { gradePointsStatistics ->
gradePointsStatistics.map { gradePointsStatistics.map {
GradePointsStatistics( GradePointsStatistics(

View File

@ -5,6 +5,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.Inter
import io.github.wulkanowy.data.db.entities.GradePointsStatistics import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeStatistics import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.pojos.GradeStatisticsItem import io.github.wulkanowy.data.pojos.GradeStatisticsItem
import io.github.wulkanowy.ui.modules.grade.statistics.ViewType import io.github.wulkanowy.ui.modules.grade.statistics.ViewType
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
@ -20,11 +21,11 @@ class GradeStatisticsRepository @Inject constructor(
private val remote: GradeStatisticsRemote private val remote: GradeStatisticsRemote
) { ) {
fun getGradesStatistics(semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false): Single<List<GradeStatisticsItem>> { fun getGradesStatistics(student: Student, semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false): Single<List<GradeStatisticsItem>> {
return local.getGradesStatistics(semester, isSemester, subjectName).map { it.mapToStatisticItems() }.filter { !forceRefresh } return local.getGradesStatistics(semester, isSemester, subjectName).map { it.mapToStatisticItems() }.filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap { .flatMap {
if (it) remote.getGradeStatistics(semester, isSemester) if (it) remote.getGradeStatistics(student, semester, isSemester)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
}.flatMap { new -> }.flatMap { new ->
local.getGradesStatistics(semester, isSemester).toSingle(emptyList()) local.getGradesStatistics(semester, isSemester).toSingle(emptyList())
@ -35,11 +36,11 @@ class GradeStatisticsRepository @Inject constructor(
}.flatMap { local.getGradesStatistics(semester, isSemester, subjectName).map { it.mapToStatisticItems() }.toSingle(emptyList()) }) }.flatMap { local.getGradesStatistics(semester, isSemester, subjectName).map { it.mapToStatisticItems() }.toSingle(emptyList()) })
} }
fun getGradesPointsStatistics(semester: Semester, subjectName: String, forceRefresh: Boolean): Single<List<GradeStatisticsItem>> { fun getGradesPointsStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean): Single<List<GradeStatisticsItem>> {
return local.getGradesPointsStatistics(semester, subjectName).map { it.mapToStatisticsItem() }.filter { !forceRefresh } return local.getGradesPointsStatistics(semester, subjectName).map { it.mapToStatisticsItem() }.filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap { .flatMap {
if (it) remote.getGradePointsStatistics(semester) if (it) remote.getGradePointsStatistics(student, semester)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
}.flatMap { new -> }.flatMap { new ->
local.getGradesPointsStatistics(semester).toSingle(emptyList()) local.getGradesPointsStatistics(semester).toSingle(emptyList())

View File

@ -2,7 +2,9 @@ package io.github.wulkanowy.data.repositories.homework
import io.github.wulkanowy.data.db.entities.Homework import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
import io.reactivex.Single import io.reactivex.Single
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import javax.inject.Inject import javax.inject.Inject
@ -11,8 +13,9 @@ import javax.inject.Singleton
@Singleton @Singleton
class HomeworkRemote @Inject constructor(private val sdk: Sdk) { class HomeworkRemote @Inject constructor(private val sdk: Sdk) {
fun getHomework(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Homework>> { fun getHomework(student: Student, semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Homework>> {
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getHomework(startDate, endDate) return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getHomework(startDate, endDate)
.map { homework -> .map { homework ->
homework.map { homework.map {
Homework( Homework(

View File

@ -4,7 +4,8 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.Homework import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.utils.friday import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Completable import io.reactivex.Completable
@ -21,12 +22,12 @@ class HomeworkRepository @Inject constructor(
private val remote: HomeworkRemote private val remote: HomeworkRemote
) { ) {
fun getHomework(semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): Single<List<Homework>> { fun getHomework(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): Single<List<Homework>> {
return Single.fromCallable { start.monday to end.friday }.flatMap { (monday, friday) -> return Single.fromCallable { start.monday to end.sunday }.flatMap { (monday, friday) ->
local.getHomework(semester, monday, friday).filter { !forceRefresh } local.getHomework(semester, monday, friday).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap { .flatMap {
if (it) remote.getHomework(semester, monday, friday) if (it) remote.getHomework(student, semester, monday, friday)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
}.flatMap { new -> }.flatMap { new ->
local.getHomework(semester, monday, friday).toSingle(emptyList()) local.getHomework(semester, monday, friday).toSingle(emptyList())

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.luckynumber
import io.github.wulkanowy.data.db.entities.LuckyNumber import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
import io.reactivex.Maybe import io.reactivex.Maybe
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import javax.inject.Inject import javax.inject.Inject
@ -12,7 +13,7 @@ import javax.inject.Singleton
class LuckyNumberRemote @Inject constructor(private val sdk: Sdk) { class LuckyNumberRemote @Inject constructor(private val sdk: Sdk) {
fun getLuckyNumber(student: Student): Maybe<LuckyNumber> { fun getLuckyNumber(student: Student): Maybe<LuckyNumber> {
return sdk.getLuckyNumber(student.schoolShortName).map { return sdk.init(student).getLuckyNumber(student.schoolShortName).map {
LuckyNumber( LuckyNumber(
studentId = student.studentId, studentId = student.studentId,
date = LocalDate.now(), date = LocalDate.now(),

View File

@ -2,7 +2,6 @@ package io.github.wulkanowy.data.repositories.luckynumber
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.SdkHelper
import io.github.wulkanowy.data.db.entities.LuckyNumber import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.reactivex.Completable import io.reactivex.Completable
@ -16,36 +15,33 @@ import javax.inject.Singleton
class LuckyNumberRepository @Inject constructor( class LuckyNumberRepository @Inject constructor(
private val settings: InternetObservingSettings, private val settings: InternetObservingSettings,
private val local: LuckyNumberLocal, private val local: LuckyNumberLocal,
private val remote: LuckyNumberRemote, private val remote: LuckyNumberRemote
private val sdkHelper: SdkHelper
) { ) {
fun getLuckyNumber(student: Student, forceRefresh: Boolean = false, notify: Boolean = false): Maybe<LuckyNumber> { fun getLuckyNumber(student: Student, forceRefresh: Boolean = false, notify: Boolean = false): Maybe<LuckyNumber> {
return Maybe.just(sdkHelper.init(student)).flatMap { return local.getLuckyNumber(student, LocalDate.now()).filter { !forceRefresh }
local.getLuckyNumber(student, LocalDate.now()).filter { !forceRefresh } .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .flatMapMaybe {
.flatMapMaybe { if (it) remote.getLuckyNumber(student)
if (it) remote.getLuckyNumber(student) else Maybe.error(UnknownHostException())
else Maybe.error(UnknownHostException()) }.flatMap { new ->
}.flatMap { new -> local.getLuckyNumber(student, LocalDate.now())
local.getLuckyNumber(student, LocalDate.now()) .doOnSuccess { old ->
.doOnSuccess { old -> if (new != old) {
if (new != old) { local.deleteLuckyNumber(old)
local.deleteLuckyNumber(old)
local.saveLuckyNumber(new.apply {
if (notify) isNotified = false
})
}
}
.doOnComplete {
local.saveLuckyNumber(new.apply { local.saveLuckyNumber(new.apply {
if (notify) isNotified = false if (notify) isNotified = false
}) })
} }
}.flatMap({ local.getLuckyNumber(student, LocalDate.now()) }, { Maybe.error(it) }, }
{ local.getLuckyNumber(student, LocalDate.now()) }) .doOnComplete {
) local.saveLuckyNumber(new.apply {
} if (notify) isNotified = false
})
}
}.flatMap({ local.getLuckyNumber(student, LocalDate.now()) }, { Maybe.error(it) },
{ local.getLuckyNumber(student, LocalDate.now()) })
)
} }
fun getNotNotifiedLuckyNumber(student: Student): Maybe<LuckyNumber> { fun getNotNotifiedLuckyNumber(student: Student): Maybe<LuckyNumber> {

View File

@ -8,6 +8,7 @@ import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Folder import io.github.wulkanowy.sdk.pojo.Folder
import io.github.wulkanowy.sdk.pojo.SentMessage import io.github.wulkanowy.sdk.pojo.SentMessage
import io.github.wulkanowy.utils.init
import io.reactivex.Single import io.reactivex.Single
import org.threeten.bp.LocalDateTime.now import org.threeten.bp.LocalDateTime.now
import javax.inject.Inject import javax.inject.Inject
@ -18,7 +19,7 @@ import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
class MessageRemote @Inject constructor(private val sdk: Sdk) { class MessageRemote @Inject constructor(private val sdk: Sdk) {
fun getMessages(student: Student, semester: Semester, folder: MessageFolder): Single<List<Message>> { fun getMessages(student: Student, semester: Semester, folder: MessageFolder): Single<List<Message>> {
return sdk.getMessages(Folder.valueOf(folder.name), semester.start.atStartOfDay(), semester.end.atStartOfDay()).map { messages -> return sdk.init(student).getMessages(Folder.valueOf(folder.name), semester.start.atStartOfDay(), semester.end.atStartOfDay()).map { messages ->
messages.map { messages.map {
Message( Message(
studentId = student.id.toInt(), studentId = student.id.toInt(),
@ -41,8 +42,8 @@ class MessageRemote @Inject constructor(private val sdk: Sdk) {
} }
} }
fun getMessagesContentDetails(message: Message, markAsRead: Boolean = false): Single<Pair<String, List<MessageAttachment>>> { fun getMessagesContentDetails(student: Student, message: Message, markAsRead: Boolean = false): Single<Pair<String, List<MessageAttachment>>> {
return sdk.getMessageDetails(message.messageId, message.folderId, markAsRead, message.realId).map { details -> return sdk.init(student).getMessageDetails(message.messageId, message.folderId, markAsRead, message.realId).map { details ->
details.content to details.attachments.map { details.content to details.attachments.map {
MessageAttachment( MessageAttachment(
realId = it.id, realId = it.id,
@ -55,8 +56,8 @@ class MessageRemote @Inject constructor(private val sdk: Sdk) {
} }
} }
fun sendMessage(subject: String, content: String, recipients: List<Recipient>): Single<SentMessage> { fun sendMessage(student: Student, subject: String, content: String, recipients: List<Recipient>): Single<SentMessage> {
return sdk.sendMessage( return sdk.init(student).sendMessage(
subject = subject, subject = subject,
content = content, content = content,
recipients = recipients.map { recipients = recipients.map {
@ -73,7 +74,7 @@ class MessageRemote @Inject constructor(private val sdk: Sdk) {
) )
} }
fun deleteMessage(message: Message): Single<Boolean> { fun deleteMessage(student: Student, message: Message): Single<Boolean> {
return sdk.deleteMessages(listOf(Pair(message.realId, message.folderId))) return sdk.init(student).deleteMessages(listOf(message.messageId to message.folderId))
} }
} }

View File

@ -2,7 +2,6 @@ package io.github.wulkanowy.data.repositories.message
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.SdkHelper
import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageWithAttachment import io.github.wulkanowy.data.db.entities.MessageWithAttachment
import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.Recipient
@ -22,60 +21,53 @@ import javax.inject.Singleton
class MessageRepository @Inject constructor( class MessageRepository @Inject constructor(
private val settings: InternetObservingSettings, private val settings: InternetObservingSettings,
private val local: MessageLocal, private val local: MessageLocal,
private val remote: MessageRemote, private val remote: MessageRemote
private val sdkHelper: SdkHelper
) { ) {
fun getMessages(student: Student, semester: Semester, folder: MessageFolder, forceRefresh: Boolean = false, notify: Boolean = false): Single<List<Message>> { fun getMessages(student: Student, semester: Semester, folder: MessageFolder, forceRefresh: Boolean = false, notify: Boolean = false): Single<List<Message>> {
return Single.just(sdkHelper.init(student)) return local.getMessages(student, folder).filter { !forceRefresh }
.flatMap { _ -> .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
local.getMessages(student, folder).filter { !forceRefresh } .flatMap {
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) if (it) remote.getMessages(student, semester, folder)
.flatMap { else Single.error(UnknownHostException())
if (it) remote.getMessages(student, semester, folder) }.flatMap { new ->
else Single.error(UnknownHostException()) local.getMessages(student, folder).toSingle(emptyList())
}.flatMap { new -> .doOnSuccess { old ->
local.getMessages(student, folder).toSingle(emptyList()) local.deleteMessages(old.uniqueSubtract(new))
.doOnSuccess { old -> local.saveMessages(new.uniqueSubtract(old)
local.deleteMessages(old.uniqueSubtract(new)) .onEach {
local.saveMessages(new.uniqueSubtract(old) it.isNotified = !notify
.onEach { })
it.isNotified = !notify }
}) }.flatMap { local.getMessages(student, folder).toSingle(emptyList()) }
} )
}.flatMap { local.getMessages(student, folder).toSingle(emptyList()) }
)
}
} }
fun getMessage(student: Student, message: Message, markAsRead: Boolean = false): Single<MessageWithAttachment> { fun getMessage(student: Student, message: Message, markAsRead: Boolean = false): Single<MessageWithAttachment> {
return Single.just(sdkHelper.init(student)) return local.getMessageWithAttachment(student, message)
.flatMap { _ -> .filter {
local.getMessageWithAttachment(student, message) it.message.content.isNotEmpty().also { status ->
.filter { Timber.d("Message content in db empty: ${!status}")
it.message.content.isNotEmpty().also { status -> } && !it.message.unread
Timber.d("Message content in db empty: ${!status}")
} && !it.message.unread
}
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
if (it) local.getMessageWithAttachment(student, message)
else Single.error(UnknownHostException())
}
.flatMap { dbMessage ->
remote.getMessagesContentDetails(dbMessage.message, markAsRead).doOnSuccess { (downloadedMessage, attachments) ->
local.updateMessages(listOf(dbMessage.message.copy(unread = !markAsRead).apply {
id = dbMessage.message.id
content = content.ifBlank { downloadedMessage }
}))
local.saveMessageAttachments(attachments)
Timber.d("Message ${message.messageId} with blank content: ${dbMessage.message.content.isBlank()}, marked as read")
}
}.flatMap {
local.getMessageWithAttachment(student, message)
}
)
} }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
if (it) local.getMessageWithAttachment(student, message)
else Single.error(UnknownHostException())
}
.flatMap { dbMessage ->
remote.getMessagesContentDetails(student, dbMessage.message, markAsRead).doOnSuccess { (downloadedMessage, attachments) ->
local.updateMessages(listOf(dbMessage.message.copy(unread = !markAsRead).apply {
id = dbMessage.message.id
content = content.ifBlank { downloadedMessage }
}))
local.saveMessageAttachments(attachments)
Timber.d("Message ${message.messageId} with blank content: ${dbMessage.message.content.isBlank()}, marked as read")
}
}.flatMap {
local.getMessageWithAttachment(student, message)
}
)
} }
fun getNotNotifiedMessages(student: Student): Single<List<Message>> { fun getNotNotifiedMessages(student: Student): Single<List<Message>> {
@ -88,18 +80,18 @@ class MessageRepository @Inject constructor(
return Completable.fromCallable { local.updateMessages(messages) } return Completable.fromCallable { local.updateMessages(messages) }
} }
fun sendMessage(subject: String, content: String, recipients: List<Recipient>): Single<SentMessage> { fun sendMessage(student: Student, subject: String, content: String, recipients: List<Recipient>): Single<SentMessage> {
return ReactiveNetwork.checkInternetConnectivity(settings) return ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap { .flatMap {
if (it) remote.sendMessage(subject, content, recipients) if (it) remote.sendMessage(student, subject, content, recipients)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
} }
} }
fun deleteMessage(message: Message): Single<Boolean> { fun deleteMessage(student: Student, message: Message): Single<Boolean> {
return ReactiveNetwork.checkInternetConnectivity(settings) return ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap { .flatMap {
if (it) remote.deleteMessage(message) if (it) remote.deleteMessage(student, message)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
} }
.doOnSuccess { .doOnSuccess {

View File

@ -2,8 +2,10 @@ package io.github.wulkanowy.data.repositories.mobiledevice
import io.github.wulkanowy.data.db.entities.MobileDevice import io.github.wulkanowy.data.db.entities.MobileDevice
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.pojos.MobileDeviceToken import io.github.wulkanowy.data.pojos.MobileDeviceToken
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
import io.reactivex.Single import io.reactivex.Single
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -11,13 +13,14 @@ import javax.inject.Singleton
@Singleton @Singleton
class MobileDeviceRemote @Inject constructor(private val sdk: Sdk) { class MobileDeviceRemote @Inject constructor(private val sdk: Sdk) {
fun getDevices(semester: Semester): Single<List<MobileDevice>> { fun getDevices(student: Student, semester: Semester): Single<List<MobileDevice>> {
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getRegisteredDevices() return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getRegisteredDevices()
.map { devices -> .map { devices ->
devices.map { devices.map {
MobileDevice( MobileDevice(
studentId = semester.studentId, studentId = semester.studentId,
date = it.date, date = it.createDate,
deviceId = it.id, deviceId = it.id,
name = it.name name = it.name
) )
@ -25,12 +28,14 @@ class MobileDeviceRemote @Inject constructor(private val sdk: Sdk) {
} }
} }
fun unregisterDevice(semester: Semester, device: MobileDevice): Single<Boolean> { fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice): Single<Boolean> {
return sdk.switchDiary(semester.diaryId, semester.schoolYear).unregisterDevice(device.deviceId) return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.unregisterDevice(device.deviceId)
} }
fun getToken(semester: Semester): Single<MobileDeviceToken> { fun getToken(student: Student, semester: Semester): Single<MobileDeviceToken> {
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getToken() return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getToken()
.map { .map {
MobileDeviceToken( MobileDeviceToken(
token = it.token, token = it.token,

View File

@ -4,6 +4,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.MobileDevice import io.github.wulkanowy.data.db.entities.MobileDevice
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.pojos.MobileDeviceToken import io.github.wulkanowy.data.pojos.MobileDeviceToken
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single import io.reactivex.Single
@ -18,11 +19,11 @@ class MobileDeviceRepository @Inject constructor(
private val remote: MobileDeviceRemote private val remote: MobileDeviceRemote
) { ) {
fun getDevices(semester: Semester, forceRefresh: Boolean = false): Single<List<MobileDevice>> { fun getDevices(student: Student, semester: Semester, forceRefresh: Boolean = false): Single<List<MobileDevice>> {
return local.getDevices(semester).filter { !forceRefresh } return local.getDevices(semester).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap { .flatMap {
if (it) remote.getDevices(semester) if (it) remote.getDevices(student, semester)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
}.flatMap { new -> }.flatMap { new ->
local.getDevices(semester).toSingle(emptyList()) local.getDevices(semester).toSingle(emptyList())
@ -34,18 +35,18 @@ class MobileDeviceRepository @Inject constructor(
).flatMap { local.getDevices(semester).toSingle(emptyList()) } ).flatMap { local.getDevices(semester).toSingle(emptyList()) }
} }
fun unregisterDevice(semester: Semester, device: MobileDevice): Single<Boolean> { fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice): Single<Boolean> {
return ReactiveNetwork.checkInternetConnectivity(settings) return ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap { .flatMap {
if (it) remote.unregisterDevice(semester, device) if (it) remote.unregisterDevice(student, semester, device)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
} }
} }
fun getToken(semester: Semester): Single<MobileDeviceToken> { fun getToken(student: Student, semester: Semester): Single<MobileDeviceToken> {
return ReactiveNetwork.checkInternetConnectivity(settings) return ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap { .flatMap {
if (it) remote.getToken(semester) if (it) remote.getToken(student, semester)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
} }
} }

View File

@ -2,7 +2,9 @@ package io.github.wulkanowy.data.repositories.note
import io.github.wulkanowy.data.db.entities.Note import io.github.wulkanowy.data.db.entities.Note
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
import io.reactivex.Single import io.reactivex.Single
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -10,8 +12,9 @@ import javax.inject.Singleton
@Singleton @Singleton
class NoteRemote @Inject constructor(private val sdk: Sdk) { class NoteRemote @Inject constructor(private val sdk: Sdk) {
fun getNotes(semester: Semester): Single<List<Note>> { fun getNotes(student: Student, semester: Semester): Single<List<Note>> {
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getNotes(semester.semesterId) return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getNotes(semester.semesterId)
.map { notes -> .map { notes ->
notes.map { notes.map {
Note( Note(

View File

@ -23,7 +23,7 @@ class NoteRepository @Inject constructor(
return local.getNotes(student).filter { !forceRefresh } return local.getNotes(student).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap { .flatMap {
if (it) remote.getNotes(semester) if (it) remote.getNotes(student, semester)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
}.flatMap { new -> }.flatMap { new ->
local.getNotes(student).toSingle(emptyList()) local.getNotes(student).toSingle(emptyList())

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.preferences
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -17,8 +18,8 @@ class PreferencesRepository @Inject constructor(
val isShowPresent: Boolean val isShowPresent: Boolean
get() = getBoolean(R.string.pref_key_attendance_present, R.bool.pref_default_attendance_present) get() = getBoolean(R.string.pref_key_attendance_present, R.bool.pref_default_attendance_present)
val gradeAverageMode: String val gradeAverageMode: GradeAverageMode
get() = getString(R.string.pref_key_grade_average_mode, R.string.pref_default_grade_average_mode) get() = GradeAverageMode.getByValue(getString(R.string.pref_key_grade_average_mode, R.string.pref_default_grade_average_mode))
val gradeAverageForceCalc: Boolean val gradeAverageForceCalc: Boolean
get() = getBoolean(R.string.pref_key_grade_average_force_calc, R.bool.pref_default_grade_average_force_calc) get() = getBoolean(R.string.pref_key_grade_average_force_calc, R.bool.pref_default_grade_average_force_calc)
@ -26,6 +27,9 @@ class PreferencesRepository @Inject constructor(
val isGradeExpandable: Boolean val isGradeExpandable: Boolean
get() = !getBoolean(R.string.pref_key_expand_grade, R.bool.pref_default_expand_grade) get() = !getBoolean(R.string.pref_key_expand_grade, R.bool.pref_default_expand_grade)
val showAllSubjectsOnStatisticsList: Boolean
get() = getBoolean(R.string.pref_key_grade_statistics_list, R.bool.pref_default_grade_statistics_list)
val appThemeKey = context.getString(R.string.pref_key_app_theme) val appThemeKey = context.getString(R.string.pref_key_app_theme)
val appTheme: String val appTheme: String
get() = getString(appThemeKey, R.string.pref_default_app_theme) get() = getString(appThemeKey, R.string.pref_default_app_theme)
@ -52,6 +56,10 @@ class PreferencesRepository @Inject constructor(
val isNotificationsEnable: Boolean val isNotificationsEnable: Boolean
get() = getBoolean(R.string.pref_key_notifications_enable, R.bool.pref_default_notifications_enable) get() = getBoolean(R.string.pref_key_notifications_enable, R.bool.pref_default_notifications_enable)
val isUpcomingLessonsNotificationsEnableKey = context.getString(R.string.pref_key_notifications_upcoming_lessons_enable)
val isUpcomingLessonsNotificationsEnable: Boolean
get() = getBoolean(isUpcomingLessonsNotificationsEnableKey, R.bool.pref_default_notification_upcoming_lessons_enable)
val isDebugNotificationEnableKey = context.getString(R.string.pref_key_notification_debug) val isDebugNotificationEnableKey = context.getString(R.string.pref_key_notification_debug)
val isDebugNotificationEnable: Boolean val isDebugNotificationEnable: Boolean
get() = getBoolean(isDebugNotificationEnableKey, R.bool.pref_default_notification_debug) get() = getBoolean(isDebugNotificationEnableKey, R.bool.pref_default_notification_debug)
@ -68,6 +76,9 @@ class PreferencesRepository @Inject constructor(
val showWholeClassPlan: String val showWholeClassPlan: String
get() = getString(R.string.pref_key_timetable_show_whole_class, R.string.pref_default_timetable_show_whole_class) get() = getString(R.string.pref_key_timetable_show_whole_class, R.string.pref_default_timetable_show_whole_class)
val showTimetableTimers: Boolean
get() = getBoolean(R.string.pref_key_timetable_show_timers, R.bool.pref_default_timetable_show_timers)
private fun getString(id: Int, default: Int) = getString(context.getString(id), default) 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 getString(id: String, default: Int) = sharedPref.getString(id, context.getString(default)) ?: context.getString(default)

View File

@ -3,7 +3,9 @@ package io.github.wulkanowy.data.repositories.recipient
import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
import io.reactivex.Single import io.reactivex.Single
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -12,15 +14,15 @@ import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
@Singleton @Singleton
class RecipientRemote @Inject constructor(private val sdk: Sdk) { class RecipientRemote @Inject constructor(private val sdk: Sdk) {
fun getRecipients(role: Int, unit: ReportingUnit): Single<List<Recipient>> { fun getRecipients(student: Student, role: Int, unit: ReportingUnit): Single<List<Recipient>> {
return sdk.getRecipients(unit.realId, role) return sdk.init(student).getRecipients(unit.realId, role)
.map { recipients -> .map { recipients ->
recipients.map { it.toRecipient() } recipients.map { it.toRecipient() }
} }
} }
fun getMessageRecipients(message: Message): Single<List<Recipient>> { fun getMessageRecipients(student: Student, message: Message): Single<List<Recipient>> {
return sdk.getMessageRecipients(message.messageId, message.senderId) return sdk.init(student).getMessageRecipients(message.messageId, message.senderId)
.map { recipients -> .map { recipients ->
recipients.map { it.toRecipient() } recipients.map { it.toRecipient() }
} }

View File

@ -2,7 +2,6 @@ package io.github.wulkanowy.data.repositories.recipient
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.SdkHelper
import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.data.db.entities.ReportingUnit
@ -17,36 +16,31 @@ import javax.inject.Singleton
class RecipientRepository @Inject constructor( class RecipientRepository @Inject constructor(
private val settings: InternetObservingSettings, private val settings: InternetObservingSettings,
private val local: RecipientLocal, private val local: RecipientLocal,
private val remote: RecipientRemote, private val remote: RecipientRemote
private val sdkHelper: SdkHelper
) { ) {
fun getRecipients(student: Student, role: Int, unit: ReportingUnit, forceRefresh: Boolean = false): Single<List<Recipient>> { fun getRecipients(student: Student, role: Int, unit: ReportingUnit, forceRefresh: Boolean = false): Single<List<Recipient>> {
return Single.just(sdkHelper.init(student)) return local.getRecipients(student, role, unit).filter { !forceRefresh }
.flatMap { _ -> .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
local.getRecipients(student, role, unit).filter { !forceRefresh } .flatMap {
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) if (it) remote.getRecipients(student, role, unit)
.flatMap { else Single.error(UnknownHostException())
if (it) remote.getRecipients(role, unit) }.flatMap { new ->
else Single.error(UnknownHostException()) local.getRecipients(student, role, unit).toSingle(emptyList())
}.flatMap { new -> .doOnSuccess { old ->
local.getRecipients(student, role, unit).toSingle(emptyList()) local.deleteRecipients(old.uniqueSubtract(new))
.doOnSuccess { old -> local.saveRecipients(new.uniqueSubtract(old))
local.deleteRecipients(old.uniqueSubtract(new))
local.saveRecipients(new.uniqueSubtract(old))
}
}.flatMap {
local.getRecipients(student, role, unit).toSingle(emptyList())
} }
) }.flatMap {
} local.getRecipients(student, role, unit).toSingle(emptyList())
}
)
} }
fun getMessageRecipients(student: Student, message: Message): Single<List<Recipient>> { fun getMessageRecipients(student: Student, message: Message): Single<List<Recipient>> {
return Single.just(sdkHelper.init(student)) return ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap { ReactiveNetwork.checkInternetConnectivity(settings) }
.flatMap { .flatMap {
if (it) remote.getMessageRecipients(message) if (it) remote.getMessageRecipients(student, message)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
} }
} }

View File

@ -1,7 +1,9 @@
package io.github.wulkanowy.data.repositories.reportingunit package io.github.wulkanowy.data.repositories.reportingunit
import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
import io.reactivex.Single import io.reactivex.Single
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -9,8 +11,8 @@ import javax.inject.Singleton
@Singleton @Singleton
class ReportingUnitRemote @Inject constructor(private val sdk: Sdk) { class ReportingUnitRemote @Inject constructor(private val sdk: Sdk) {
fun getReportingUnits(): Single<List<ReportingUnit>> { fun getReportingUnits(student: Student): Single<List<ReportingUnit>> {
return sdk.getReportingUnits().map { return sdk.init(student).getReportingUnits().map {
it.map { unit -> it.map { unit ->
ReportingUnit( ReportingUnit(
studentId = sdk.studentId, studentId = sdk.studentId,

View File

@ -2,7 +2,6 @@ package io.github.wulkanowy.data.repositories.reportingunit
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.SdkHelper
import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
@ -16,41 +15,34 @@ import javax.inject.Singleton
class ReportingUnitRepository @Inject constructor( class ReportingUnitRepository @Inject constructor(
private val settings: InternetObservingSettings, private val settings: InternetObservingSettings,
private val local: ReportingUnitLocal, private val local: ReportingUnitLocal,
private val remote: ReportingUnitRemote, private val remote: ReportingUnitRemote
private val sdkHelper: SdkHelper
) { ) {
fun getReportingUnits(student: Student, forceRefresh: Boolean = false): Single<List<ReportingUnit>> { fun getReportingUnits(student: Student, forceRefresh: Boolean = false): Single<List<ReportingUnit>> {
return Single.just(sdkHelper.init(student)) return local.getReportingUnits(student).filter { !forceRefresh }
.flatMap { _ -> .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
local.getReportingUnits(student).filter { !forceRefresh } .flatMap {
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) if (it) remote.getReportingUnits(student)
.flatMap { else Single.error(UnknownHostException())
if (it) remote.getReportingUnits() }.flatMap { new ->
else Single.error(UnknownHostException()) local.getReportingUnits(student).toSingle(emptyList())
}.flatMap { new -> .doOnSuccess { old ->
local.getReportingUnits(student).toSingle(emptyList()) local.deleteReportingUnits(old.uniqueSubtract(new))
.doOnSuccess { old -> local.saveReportingUnits(new.uniqueSubtract(old))
local.deleteReportingUnits(old.uniqueSubtract(new)) }
local.saveReportingUnits(new.uniqueSubtract(old)) }.flatMap { local.getReportingUnits(student).toSingle(emptyList()) }
} )
}.flatMap { local.getReportingUnits(student).toSingle(emptyList()) }
)
}
} }
fun getReportingUnit(student: Student, unitId: Int): Maybe<ReportingUnit> { fun getReportingUnit(student: Student, unitId: Int): Maybe<ReportingUnit> {
return Maybe.just(sdkHelper.init(student)) return local.getReportingUnit(student, unitId)
.flatMap { _ -> .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
local.getReportingUnit(student, unitId) .flatMap {
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) if (it) getReportingUnits(student, true)
.flatMap { else Single.error(UnknownHostException())
if (it) getReportingUnits(student, true) }.flatMapMaybe {
else Single.error(UnknownHostException()) local.getReportingUnit(student, unitId)
}.flatMapMaybe { }
local.getReportingUnit(student, unitId) )
}
)
}
} }
} }

View File

@ -2,14 +2,17 @@ package io.github.wulkanowy.data.repositories.school
import io.github.wulkanowy.data.db.entities.School import io.github.wulkanowy.data.db.entities.School
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
import io.reactivex.Single import io.reactivex.Single
import javax.inject.Inject import javax.inject.Inject
class SchoolRemote @Inject constructor(private val sdk: Sdk) { class SchoolRemote @Inject constructor(private val sdk: Sdk) {
fun getSchoolInfo(semester: Semester): Single<School> { fun getSchoolInfo(student: Student, semester: Semester): Single<School> {
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getSchool() return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getSchool()
.map { .map {
School( School(
studentId = semester.studentId, studentId = semester.studentId,

View File

@ -4,6 +4,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.School
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.reactivex.Maybe import io.reactivex.Maybe
import io.reactivex.Single import io.reactivex.Single
import java.net.UnknownHostException import java.net.UnknownHostException
@ -17,11 +18,11 @@ class SchoolRepository @Inject constructor(
private val remote: SchoolRemote private val remote: SchoolRemote
) { ) {
fun getSchoolInfo(semester: Semester, forceRefresh: Boolean = false): Maybe<School> { fun getSchoolInfo(student: Student, semester: Semester, forceRefresh: Boolean = false): Maybe<School> {
return local.getSchool(semester).filter { !forceRefresh } return local.getSchool(semester).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap { .flatMap {
if (it) remote.getSchoolInfo(semester) if (it) remote.getSchoolInfo(student, semester)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
}.flatMapMaybe { new -> }.flatMapMaybe { new ->
local.getSchool(semester) local.getSchool(semester)

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.semester
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
import io.reactivex.Single import io.reactivex.Single
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -11,7 +12,7 @@ import javax.inject.Singleton
class SemesterRemote @Inject constructor(private val sdk: Sdk) { class SemesterRemote @Inject constructor(private val sdk: Sdk) {
fun getSemesters(student: Student): Single<List<Semester>> { fun getSemesters(student: Student): Single<List<Semester>> {
return sdk.getSemesters().map { semesters -> return sdk.init(student).getSemesters().map { semesters ->
semesters.map { semesters.map {
Semester( Semester(
studentId = student.studentId, studentId = student.studentId,

View File

@ -2,13 +2,12 @@ package io.github.wulkanowy.data.repositories.semester
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.SdkHelper
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.getCurrentOrLast import io.github.wulkanowy.utils.getCurrentOrLast
import io.github.wulkanowy.utils.isCurrent import io.github.wulkanowy.utils.isCurrent
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Maybe
import io.reactivex.Single import io.reactivex.Single
import java.net.UnknownHostException import java.net.UnknownHostException
import javax.inject.Inject import javax.inject.Inject
@ -18,31 +17,28 @@ import javax.inject.Singleton
class SemesterRepository @Inject constructor( class SemesterRepository @Inject constructor(
private val remote: SemesterRemote, private val remote: SemesterRemote,
private val local: SemesterLocal, private val local: SemesterLocal,
private val settings: InternetObservingSettings, private val settings: InternetObservingSettings
private val sdkHelper: SdkHelper
) { ) {
fun getSemesters(student: Student, forceRefresh: Boolean = false, refreshOnNoCurrent: Boolean = false): Single<List<Semester>> { fun getSemesters(student: Student, forceRefresh: Boolean = false, refreshOnNoCurrent: Boolean = false): Single<List<Semester>> {
return Maybe.just(sdkHelper.init(student)) return local.getSemesters(student).filter { !forceRefresh }.filter { semesters ->
.flatMap { when {
local.getSemesters(student).filter { !forceRefresh }.filter { Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API -> semesters.firstOrNull { it.isCurrent }?.diaryId != 0
if (refreshOnNoCurrent) { refreshOnNoCurrent -> semesters.any { semester -> semester.isCurrent }
it.any { semester -> semester.isCurrent } else -> true
} else true
}
} }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) }.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap { .flatMap {
if (it) remote.getSemesters(student) if (it) remote.getSemesters(student)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
}.flatMap { new -> }.flatMap { new ->
if (new.isEmpty()) throw IllegalArgumentException("Empty semester list!") if (new.isEmpty()) throw IllegalArgumentException("Empty semester list!")
local.getSemesters(student).toSingle(emptyList()).doOnSuccess { old -> local.getSemesters(student).toSingle(emptyList()).doOnSuccess { old ->
local.deleteSemesters(old.uniqueSubtract(new)) local.deleteSemesters(old.uniqueSubtract(new))
local.saveSemesters(new.uniqueSubtract(old)) local.saveSemesters(new.uniqueSubtract(old))
} }
}.flatMap { local.getSemesters(student).toSingle(emptyList()) }) }.flatMap { local.getSemesters(student).toSingle(emptyList()) })
} }
fun getCurrentSemester(student: Student, forceRefresh: Boolean = false): Single<Semester> { fun getCurrentSemester(student: Student, forceRefresh: Boolean = false): Single<Semester> {

View File

@ -29,10 +29,18 @@ class StudentLocal @Inject constructor(
fun getStudents(decryptPass: Boolean): Maybe<List<Student>> { fun getStudents(decryptPass: Boolean): Maybe<List<Student>> {
return studentDb.loadAll() return studentDb.loadAll()
.map { list -> list.map { it.apply { if (decryptPass) password = decrypt(password) } } } .map { list -> list.map { it.apply { if (decryptPass && Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password) } } }
.filter { it.isNotEmpty() } .filter { it.isNotEmpty() }
} }
fun getStudentById(id: Int): Maybe<Student> {
return studentDb.loadById(id).map {
it.apply {
if (Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password)
}
}
}
fun getCurrentStudent(decryptPass: Boolean): Maybe<Student> { fun getCurrentStudent(decryptPass: Boolean): Maybe<Student> {
return studentDb.loadCurrent().map { return studentDb.loadCurrent().map {
it.apply { it.apply {

View File

@ -39,7 +39,7 @@ class StudentRemote @Inject constructor(private val sdk: Sdk) {
} }
fun getStudentsMobileApi(token: String, pin: String, symbol: String): Single<List<Student>> { fun getStudentsMobileApi(token: String, pin: String, symbol: String): Single<List<Student>> {
return sdk.getStudentsFromMobileApi(token, pin, symbol).map { mapStudents(it, "", "") } return sdk.getStudentsFromMobileApi(token, pin, symbol, "").map { mapStudents(it, "", "") }
} }
fun getStudentsScrapper(email: String, password: String, scrapperBaseUrl: String, symbol: String): Single<List<Student>> { fun getStudentsScrapper(email: String, password: String, scrapperBaseUrl: String, symbol: String): Single<List<Student>> {
@ -47,6 +47,6 @@ class StudentRemote @Inject constructor(private val sdk: Sdk) {
} }
fun getStudentsHybrid(email: String, password: String, scrapperBaseUrl: String, symbol: String): Single<List<Student>> { fun getStudentsHybrid(email: String, password: String, scrapperBaseUrl: String, symbol: String): Single<List<Student>> {
return sdk.getStudentsHybrid(email, password, scrapperBaseUrl, symbol).map { mapStudents(it, email, password) } return sdk.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol).map { mapStudents(it, email, password) }
} }
} }

View File

@ -47,6 +47,12 @@ class StudentRepository @Inject constructor(
return local.getStudents(decryptPass).toSingle(emptyList()) return local.getStudents(decryptPass).toSingle(emptyList())
} }
fun getStudentById(id: Int): Single<Student> {
return local.getStudentById(id)
.switchIfEmpty(Maybe.error(NoCurrentStudentException()))
.toSingle()
}
fun getCurrentStudent(decryptPass: Boolean = true): Single<Student> { fun getCurrentStudent(decryptPass: Boolean = true): Single<Student> {
return local.getCurrentStudent(decryptPass) return local.getCurrentStudent(decryptPass)
.switchIfEmpty(Maybe.error(NoCurrentStudentException())) .switchIfEmpty(Maybe.error(NoCurrentStudentException()))

View File

@ -12,7 +12,7 @@ class SubjectLocal @Inject constructor(private val subjectDao: SubjectDao) {
fun getSubjects(semester: Semester): Maybe<List<Subject>> { fun getSubjects(semester: Semester): Maybe<List<Subject>> {
return subjectDao.loadAll(semester.diaryId, semester.studentId) return subjectDao.loadAll(semester.diaryId, semester.studentId)
.filter { !it.isEmpty() } .filter { it.isNotEmpty() }
} }
fun saveSubjects(subjects: List<Subject>) { fun saveSubjects(subjects: List<Subject>) {

View File

@ -1,8 +1,10 @@
package io.github.wulkanowy.data.repositories.subject package io.github.wulkanowy.data.repositories.subject
import io.github.wulkanowy.data.db.entities.Semester 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.Subject
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
import io.reactivex.Single import io.reactivex.Single
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -10,8 +12,9 @@ import javax.inject.Singleton
@Singleton @Singleton
class SubjectRemote @Inject constructor(private val sdk: Sdk) { class SubjectRemote @Inject constructor(private val sdk: Sdk) {
fun getSubjects(semester: Semester): Single<List<Subject>> { fun getSubjects(student: Student, semester: Semester): Single<List<Subject>> {
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getSubjects() return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getSubjects()
.map { subjects -> .map { subjects ->
subjects.map { subjects.map {
Subject( Subject(

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.subject
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Subject import io.github.wulkanowy.data.db.entities.Subject
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single import io.reactivex.Single
@ -17,11 +18,11 @@ class SubjectRepository @Inject constructor(
private val remote: SubjectRemote private val remote: SubjectRemote
) { ) {
fun getSubjects(semester: Semester, forceRefresh: Boolean = false): Single<List<Subject>> { fun getSubjects(student: Student, semester: Semester, forceRefresh: Boolean = false): Single<List<Subject>> {
return local.getSubjects(semester).filter { !forceRefresh } return local.getSubjects(semester).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap { .flatMap {
if (it) remote.getSubjects(semester) if (it) remote.getSubjects(student, semester)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
}.flatMap { new -> }.flatMap { new ->
local.getSubjects(semester) local.getSubjects(semester)

View File

@ -1,8 +1,10 @@
package io.github.wulkanowy.data.repositories.teacher package io.github.wulkanowy.data.repositories.teacher
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Teacher import io.github.wulkanowy.data.db.entities.Teacher
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
import io.reactivex.Single import io.reactivex.Single
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -10,8 +12,9 @@ import javax.inject.Singleton
@Singleton @Singleton
class TeacherRemote @Inject constructor(private val sdk: Sdk) { class TeacherRemote @Inject constructor(private val sdk: Sdk) {
fun getTeachers(semester: Semester): Single<List<Teacher>> { fun getTeachers(student: Student, semester: Semester): Single<List<Teacher>> {
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getTeachers(semester.semesterId) return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getTeachers(semester.semesterId)
.map { teachers -> .map { teachers ->
teachers.map { teachers.map {
Teacher( Teacher(

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.teacher
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Teacher import io.github.wulkanowy.data.db.entities.Teacher
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single import io.reactivex.Single
@ -17,11 +18,11 @@ class TeacherRepository @Inject constructor(
private val remote: TeacherRemote private val remote: TeacherRemote
) { ) {
fun getTeachers(semester: Semester, forceRefresh: Boolean = false): Single<List<Teacher>> { fun getTeachers(student: Student, semester: Semester, forceRefresh: Boolean = false): Single<List<Teacher>> {
return local.getTeachers(semester).filter { !forceRefresh } return local.getTeachers(semester).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap { .flatMap {
if (it) remote.getTeachers(semester) if (it) remote.getTeachers(student, semester)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
}.flatMap { new -> }.flatMap { new ->
local.getTeachers(semester).toSingle(emptyList()) local.getTeachers(semester).toSingle(emptyList())

View File

@ -1,8 +1,10 @@
package io.github.wulkanowy.data.repositories.timetable package io.github.wulkanowy.data.repositories.timetable
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
import io.reactivex.Single import io.reactivex.Single
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import javax.inject.Inject import javax.inject.Inject
@ -11,8 +13,9 @@ import javax.inject.Singleton
@Singleton @Singleton
class TimetableRemote @Inject constructor(private val sdk: Sdk) { class TimetableRemote @Inject constructor(private val sdk: Sdk) {
fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Timetable>> { fun getTimetable(student: Student, semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Timetable>> {
return sdk.switchDiary(semester.diaryId, semester.schoolYear).getTimetable(startDate, endDate) return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getTimetable(startDate, endDate)
.map { lessons -> .map { lessons ->
lessons.map { lessons.map {
Timetable( Timetable(

View File

@ -3,8 +3,10 @@ package io.github.wulkanowy.data.repositories.timetable
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.utils.friday import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single import io.reactivex.Single
@ -17,21 +19,22 @@ import javax.inject.Singleton
class TimetableRepository @Inject constructor( class TimetableRepository @Inject constructor(
private val settings: InternetObservingSettings, private val settings: InternetObservingSettings,
private val local: TimetableLocal, private val local: TimetableLocal,
private val remote: TimetableRemote private val remote: TimetableRemote,
private val schedulerHelper: TimetableNotificationSchedulerHelper
) { ) {
fun getTimetable(semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): Single<List<Timetable>> { fun getTimetable(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): Single<List<Timetable>> {
return Single.fromCallable { start.monday to end.friday }.flatMap { (monday, friday) -> return Single.fromCallable { start.monday to end.sunday }.flatMap { (monday, sunday) ->
local.getTimetable(semester, monday, friday).filter { !forceRefresh } local.getTimetable(semester, monday, sunday).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap { .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap {
if (it) remote.getTimetable(semester, monday, friday) if (it) remote.getTimetable(student, semester, monday, sunday)
else Single.error(UnknownHostException()) else Single.error(UnknownHostException())
}.flatMap { new -> }.flatMap { new ->
local.getTimetable(semester, monday, friday) local.getTimetable(semester, monday, sunday)
.toSingle(emptyList()) .toSingle(emptyList())
.doOnSuccess { old -> .doOnSuccess { old ->
local.deleteTimetable(old.uniqueSubtract(new)) local.deleteTimetable(old.uniqueSubtract(new).also { schedulerHelper.cancelScheduled(it) })
local.saveTimetable(new.uniqueSubtract(old).map { item -> local.saveTimetable(new.uniqueSubtract(old).also { schedulerHelper.scheduleNotifications(it, student) }.map { item ->
item.also { new -> item.also { new ->
old.singleOrNull { new.start == it.start }?.let { old -> old.singleOrNull { new.start == it.start }?.let { old ->
return@map new.copy( return@map new.copy(
@ -43,8 +46,8 @@ class TimetableRepository @Inject constructor(
}) })
} }
}.flatMap { }.flatMap {
local.getTimetable(semester, monday, friday).toSingle(emptyList()) local.getTimetable(semester, monday, sunday).toSingle(emptyList())
}).map { list -> list.filter { it.date in start..end } } }).map { list -> list.filter { it.date in start..end }.also { schedulerHelper.scheduleNotifications(it, student) } }
} }
} }
} }

View File

@ -5,8 +5,6 @@ import android.content.Context
import com.yariksoffice.lingver.Lingver import com.yariksoffice.lingver.Lingver
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.WulkanowyApp import io.github.wulkanowy.WulkanowyApp
import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
@ -23,9 +21,6 @@ internal class AppModule {
@Provides @Provides
fun provideSchedulersProvider() = SchedulersProvider() fun provideSchedulersProvider() = SchedulersProvider()
@Provides
fun provideFlexibleAdapter() = FlexibleAdapter<AbstractFlexibleItem<*>>(null, null, true)
@Singleton @Singleton
@Provides @Provides
fun provideAppWidgetManager(context: Context): AppWidgetManager = AppWidgetManager.getInstance(context) fun provideAppWidgetManager(context: Context): AppWidgetManager = AppWidgetManager.getInstance(context)

View File

@ -3,6 +3,8 @@ package io.github.wulkanowy.di
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
import io.github.wulkanowy.di.scopes.PerActivity import io.github.wulkanowy.di.scopes.PerActivity
import io.github.wulkanowy.ui.base.ErrorDialog
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver
import io.github.wulkanowy.ui.modules.login.LoginActivity import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.ui.modules.login.LoginModule import io.github.wulkanowy.ui.modules.login.LoginModule
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity
@ -18,6 +20,9 @@ import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider
@Module @Module
internal abstract class BindingModule { internal abstract class BindingModule {
@ContributesAndroidInjector
abstract fun bindErrorDialog(): ErrorDialog
@PerActivity @PerActivity
@ContributesAndroidInjector @ContributesAndroidInjector
abstract fun bindSplashActivity(): SplashActivity abstract fun bindSplashActivity(): SplashActivity
@ -44,4 +49,7 @@ internal abstract class BindingModule {
@ContributesAndroidInjector @ContributesAndroidInjector
abstract fun bindLuckyNumberWidgetProvider(): LuckyNumberWidgetProvider abstract fun bindLuckyNumberWidgetProvider(): LuckyNumberWidgetProvider
@ContributesAndroidInjector
abstract fun bindTimetableNotificationReceiver(): TimetableNotificationReceiver
} }

View File

@ -1,7 +1,9 @@
package io.github.wulkanowy.services package io.github.wulkanowy.services
import android.app.AlarmManager
import android.content.Context import android.content.Context
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.core.content.getSystemService
import androidx.work.WorkManager import androidx.work.WorkManager
import com.squareup.inject.assisted.dagger2.AssistedModule import com.squareup.inject.assisted.dagger2.AssistedModule
import dagger.Binds import dagger.Binds
@ -15,13 +17,13 @@ import io.github.wulkanowy.services.sync.channels.LuckyNumberChannel
import io.github.wulkanowy.services.sync.channels.NewGradesChannel import io.github.wulkanowy.services.sync.channels.NewGradesChannel
import io.github.wulkanowy.services.sync.channels.NewMessagesChannel import io.github.wulkanowy.services.sync.channels.NewMessagesChannel
import io.github.wulkanowy.services.sync.channels.NewNotesChannel import io.github.wulkanowy.services.sync.channels.NewNotesChannel
import io.github.wulkanowy.services.sync.channels.UpcomingLessonsChannel
import io.github.wulkanowy.services.sync.channels.PushChannel import io.github.wulkanowy.services.sync.channels.PushChannel
import io.github.wulkanowy.services.sync.works.AttendanceSummaryWork import io.github.wulkanowy.services.sync.works.AttendanceSummaryWork
import io.github.wulkanowy.services.sync.works.AttendanceWork import io.github.wulkanowy.services.sync.works.AttendanceWork
import io.github.wulkanowy.services.sync.works.CompletedLessonWork import io.github.wulkanowy.services.sync.works.CompletedLessonWork
import io.github.wulkanowy.services.sync.works.ExamWork import io.github.wulkanowy.services.sync.works.ExamWork
import io.github.wulkanowy.services.sync.works.GradeStatisticsWork import io.github.wulkanowy.services.sync.works.GradeStatisticsWork
import io.github.wulkanowy.services.sync.works.GradeSummaryWork
import io.github.wulkanowy.services.sync.works.GradeWork import io.github.wulkanowy.services.sync.works.GradeWork
import io.github.wulkanowy.services.sync.works.HomeworkWork import io.github.wulkanowy.services.sync.works.HomeworkWork
import io.github.wulkanowy.services.sync.works.LuckyNumberWork import io.github.wulkanowy.services.sync.works.LuckyNumberWork
@ -47,6 +49,10 @@ abstract class ServicesModule {
@Singleton @Singleton
@Provides @Provides
fun provideNotificationManager(context: Context) = NotificationManagerCompat.from(context) fun provideNotificationManager(context: Context) = NotificationManagerCompat.from(context)
@Singleton
@Provides
fun provideAlarmManager(context: Context): AlarmManager = context.getSystemService()!!
} }
@ContributesAndroidInjector @ContributesAndroidInjector
@ -64,10 +70,6 @@ abstract class ServicesModule {
@IntoSet @IntoSet
abstract fun provideAttendanceWork(work: AttendanceWork): Work abstract fun provideAttendanceWork(work: AttendanceWork): Work
@Binds
@IntoSet
abstract fun provideGradeSummaryWork(work: GradeSummaryWork): Work
@Binds @Binds
@IntoSet @IntoSet
abstract fun provideExamWork(work: ExamWork): Work abstract fun provideExamWork(work: ExamWork): Work
@ -131,4 +133,8 @@ abstract class ServicesModule {
@Binds @Binds
@IntoSet @IntoSet
abstract fun providePushChannel(channel: PushChannel): Channel abstract fun providePushChannel(channel: PushChannel): Channel
@Binds
@IntoSet
abstract fun provideUpcomingLessonsChannel(channel: UpcomingLessonsChannel): Channel
} }

View File

@ -0,0 +1,117 @@
package io.github.wulkanowy.services.alarm
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Build.VERSION_CODES.N
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import dagger.android.AndroidInjection
import io.github.wulkanowy.R
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.services.sync.channels.UpcomingLessonsChannel.Companion.CHANNEL_ID
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.getCompatColor
import io.github.wulkanowy.utils.toLocalDateTime
import timber.log.Timber
import javax.inject.Inject
class TimetableNotificationReceiver : BroadcastReceiver() {
@Inject
lateinit var studentRepository: StudentRepository
@Inject
lateinit var schedulers: SchedulersProvider
companion object {
const val NOTIFICATION_TYPE_CURRENT = 1
const val NOTIFICATION_TYPE_UPCOMING = 2
const val NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION = 3
const val NOTIFICATION_ID = "id"
const val STUDENT_NAME = "student_name"
const val STUDENT_ID = "student_id"
const val LESSON_TYPE = "type"
const val LESSON_TITLE = "title"
const val LESSON_ROOM = "room"
const val LESSON_NEXT_TITLE = "next_title"
const val LESSON_NEXT_ROOM = "next_room"
const val LESSON_START = "start_timestamp"
const val LESSON_END = "end_timestamp"
}
@SuppressLint("CheckResult")
override fun onReceive(context: Context, intent: Intent) {
Timber.d("Receiving intent... ${intent.toUri(0)}")
AndroidInjection.inject(this, context)
studentRepository.getCurrentStudent(false)
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
val studentId = intent.getIntExtra(STUDENT_ID, 0)
if (it.studentId == studentId) prepareNotification(context, intent)
else Timber.d("Notification studentId($studentId) differs from current(${it.studentId})")
}, { Timber.e(it) })
}
private fun prepareNotification(context: Context, intent: Intent) {
val type = intent.getIntExtra(LESSON_TYPE, 0)
val notificationId = intent.getIntExtra(NOTIFICATION_ID, MainView.Section.TIMETABLE.id)
if (type == NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION) {
return NotificationManagerCompat.from(context).cancel(notificationId)
}
val studentId = intent.getIntExtra(STUDENT_ID, 0)
val studentName = intent.getStringExtra(STUDENT_NAME)
val subject = intent.getStringExtra(LESSON_TITLE)
val room = intent.getStringExtra(LESSON_ROOM)
val start = intent.getLongExtra(LESSON_START, 0)
val end = intent.getLongExtra(LESSON_END, 0)
val nextSubject = intent.getStringExtra(LESSON_NEXT_TITLE)
val nextRoom = intent.getStringExtra(LESSON_NEXT_ROOM)
Timber.d("TimetableNotification receive: type: $type, subject: $subject, start: ${start.toLocalDateTime()}, student: $studentId")
showNotification(context, notificationId, studentName,
if (type == NOTIFICATION_TYPE_CURRENT) end else start, end - start,
context.getString(if (type == NOTIFICATION_TYPE_CURRENT) R.string.timetable_now else R.string.timetable_next, "($room) $subject".removePrefix("()")),
nextSubject?.let { context.getString(R.string.timetable_later, "($nextRoom) $nextSubject".removePrefix("()")) }
)
}
private fun showNotification(context: Context, notificationId: Int, studentName: String?, countDown: Long, timeout: Long, title: String, next: String?) {
NotificationManagerCompat.from(context).notify(notificationId, NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle(title)
.setContentText(next)
.setAutoCancel(false)
.setOngoing(true)
.setWhen(countDown)
.apply {
if (Build.VERSION.SDK_INT >= N) setUsesChronometer(true)
}
.setTimeoutAfter(timeout)
.setSmallIcon(R.drawable.ic_stat_timetable)
.setColor(context.getCompatColor(R.color.colorPrimary))
.setStyle(NotificationCompat.InboxStyle().also {
it.setSummaryText(studentName)
it.addLine(next)
})
.setContentIntent(PendingIntent.getActivity(context, MainView.Section.TIMETABLE.id,
MainActivity.getStartIntent(context, MainView.Section.TIMETABLE, true), FLAG_UPDATE_CURRENT))
.build()
)
}
}

View File

@ -0,0 +1,109 @@
package io.github.wulkanowy.services.alarm
import android.app.AlarmManager
import android.app.AlarmManager.RTC_WAKEUP
import android.app.PendingIntent
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
import android.content.Context
import android.content.Intent
import androidx.core.app.AlarmManagerCompat
import androidx.core.app.NotificationManagerCompat
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_END
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_NEXT_ROOM
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_NEXT_TITLE
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_ROOM
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_START
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_TITLE
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_TYPE
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.NOTIFICATION_ID
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.NOTIFICATION_TYPE_CURRENT
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.NOTIFICATION_TYPE_UPCOMING
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.STUDENT_ID
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.STUDENT_NAME
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.toTimestamp
import org.threeten.bp.LocalDateTime
import org.threeten.bp.LocalDateTime.now
import timber.log.Timber
import javax.inject.Inject
class TimetableNotificationSchedulerHelper @Inject constructor(
private val context: Context,
private val alarmManager: AlarmManager,
private val preferencesRepository: PreferencesRepository
) {
private fun getRequestCode(time: LocalDateTime, studentId: Int) = (time.toTimestamp() * studentId).toInt()
private fun getUpcomingLessonTime(index: Int, day: List<Timetable>, lesson: Timetable): LocalDateTime {
return day.getOrNull(index - 1)?.end ?: lesson.start.minusMinutes(30)
}
fun cancelScheduled(lessons: List<Timetable>, studentId: Int = 1) {
lessons.sortedBy { it.start }.forEachIndexed { index, lesson ->
val upcomingTime = getUpcomingLessonTime(index, lessons, lesson)
cancelScheduledTo(upcomingTime..lesson.start, getRequestCode(upcomingTime, studentId))
cancelScheduledTo(lesson.start..lesson.end, getRequestCode(lesson.start, studentId))
Timber.d("TimetableNotification canceled: type 1 & 2, subject: ${lesson.subject}, start: ${lesson.start}, student: $studentId")
}
}
private fun cancelScheduledTo(range: ClosedRange<LocalDateTime>, requestCode: Int) {
if (now() in range) cancelNotification()
alarmManager.cancel(PendingIntent.getBroadcast(context, requestCode, Intent(), FLAG_UPDATE_CURRENT))
}
fun cancelNotification() = NotificationManagerCompat.from(context).cancel(MainView.Section.TIMETABLE.id)
fun scheduleNotifications(lessons: List<Timetable>, student: Student) {
if (!preferencesRepository.isUpcomingLessonsNotificationsEnable) return cancelScheduled(lessons, student.studentId)
lessons.groupBy { it.date }
.map { it.value.sortedBy { lesson -> lesson.start } }
.map { it.filter { lesson -> !lesson.canceled && lesson.isStudentPlan } }
.map { day ->
day.forEachIndexed { index, lesson ->
val intent = createIntent(student, lesson, day.getOrNull(index + 1))
if (lesson.start > now()) {
scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_UPCOMING, getUpcomingLessonTime(index, day, lesson))
}
if (lesson.end > now()) {
scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_CURRENT, lesson.start)
if (day.lastIndex == index) {
scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION, lesson.end)
}
}
}
}
}
private fun createIntent(student: Student, lesson: Timetable, nextLesson: Timetable?): Intent {
return Intent(context, TimetableNotificationReceiver::class.java).apply {
putExtra(STUDENT_ID, student.studentId)
putExtra(STUDENT_NAME, student.studentName)
putExtra(LESSON_ROOM, lesson.room)
putExtra(LESSON_START, lesson.start.toTimestamp())
putExtra(LESSON_END, lesson.end.toTimestamp())
putExtra(LESSON_TITLE, lesson.subject)
putExtra(LESSON_NEXT_TITLE, nextLesson?.subject)
putExtra(LESSON_NEXT_ROOM, nextLesson?.room)
}
}
private fun scheduleBroadcast(intent: Intent, studentId: Int, notificationType: Int, time: LocalDateTime) {
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, RTC_WAKEUP, time.toTimestamp(),
PendingIntent.getBroadcast(context, getRequestCode(time, studentId), intent.also {
it.putExtra(NOTIFICATION_ID, MainView.Section.TIMETABLE.id)
it.putExtra(LESSON_TYPE, notificationType)
}, FLAG_UPDATE_CURRENT)
)
Timber.d("TimetableNotification scheduled: type: $notificationType, subject: ${intent.getStringExtra(LESSON_TITLE)}, start: $time, student: $studentId")
}
}

View File

@ -42,7 +42,7 @@ class SyncManager @Inject constructor(
init { init {
if (now().isHolidays) stopSyncWorker() if (now().isHolidays) stopSyncWorker()
if (SDK_INT > O) { if (SDK_INT >= O) {
channels.forEach { it.create() } channels.forEach { it.create() }
notificationManager.deleteNotificationChannel("new_entries_channel") notificationManager.deleteNotificationChannel("new_entries_channel")
} }

View File

@ -1,7 +1,6 @@
package io.github.wulkanowy.services.sync package io.github.wulkanowy.services.sync
import android.content.Context import android.content.Context
import android.os.Build.VERSION_CODES.LOLLIPOP
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.BigTextStyle import androidx.core.app.NotificationCompat.BigTextStyle
import androidx.core.app.NotificationCompat.PRIORITY_DEFAULT import androidx.core.app.NotificationCompat.PRIORITY_DEFAULT
@ -17,9 +16,9 @@ import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.sdk.exception.FeatureDisabledException import io.github.wulkanowy.sdk.exception.FeatureDisabledException
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
import io.github.wulkanowy.services.sync.channels.DebugChannel import io.github.wulkanowy.services.sync.channels.DebugChannel
import io.github.wulkanowy.services.sync.works.Work import io.github.wulkanowy.services.sync.works.Work
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.getCompatColor import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable import io.reactivex.Completable
import io.reactivex.Single import io.reactivex.Single
@ -33,8 +32,7 @@ class SyncWorker @AssistedInject constructor(
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val works: Set<@JvmSuppressWildcards Work>, private val works: Set<@JvmSuppressWildcards Work>,
private val preferencesRepository: PreferencesRepository, private val preferencesRepository: PreferencesRepository,
private val notificationManager: NotificationManagerCompat, private val notificationManager: NotificationManagerCompat
private val appInfo: AppInfo
) : RxWorker(appContext, workerParameters) { ) : RxWorker(appContext, workerParameters) {
override fun createWork(): Single<Result> { override fun createWork(): Single<Result> {
@ -47,6 +45,10 @@ class SyncWorker @AssistedInject constructor(
.flatMapCompletable { semester -> .flatMapCompletable { semester ->
Completable.mergeDelayError(works.map { work -> Completable.mergeDelayError(works.map { work ->
work.create(student, semester) work.create(student, semester)
.onErrorResumeNext {
if (it is FeatureDisabledException || it is FeatureNotAvailableException) Completable.complete()
else Completable.error(it)
}
.doOnSubscribe { Timber.i("${work::class.java.simpleName} is starting") } .doOnSubscribe { Timber.i("${work::class.java.simpleName} is starting") }
.doOnError { Timber.i("${work::class.java.simpleName} result: An exception occurred") } .doOnError { Timber.i("${work::class.java.simpleName} result: An exception occurred") }
.doOnComplete { Timber.i("${work::class.java.simpleName} result: Success") } .doOnComplete { Timber.i("${work::class.java.simpleName} result: Success") }
@ -57,11 +59,11 @@ class SyncWorker @AssistedInject constructor(
.onErrorReturn { .onErrorReturn {
Timber.e(it, "There was an error during synchronization") Timber.e(it, "There was an error during synchronization")
when { when {
it is FeatureDisabledException -> Result.success()
inputData.getBoolean("one_time", false) -> { inputData.getBoolean("one_time", false) -> {
Result.failure(Data.Builder() Result.failure(Data.Builder()
.putString("error", it.toString()) .putString("error", it.toString())
.build()) .build()
)
} }
else -> Result.retry() else -> Result.retry()
} }

View File

@ -22,7 +22,7 @@ class DebugChannel @Inject constructor(
} }
override fun create() { override fun create() {
if (appInfo.isDebug) return if (!appInfo.isDebug) return
notificationManager.createNotificationChannel( notificationManager.createNotificationChannel(
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_debug), IMPORTANCE_DEFAULT) NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_debug), IMPORTANCE_DEFAULT)
.apply { .apply {

View File

@ -0,0 +1,31 @@
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.IMPORTANCE_DEFAULT
import android.content.Context
import androidx.core.app.NotificationManagerCompat
import io.github.wulkanowy.R
import javax.inject.Inject
@TargetApi(26)
class UpcomingLessonsChannel @Inject constructor(
private val notificationManager: NotificationManagerCompat,
private val context: Context
) : Channel {
companion object {
const val CHANNEL_ID = "lesson_channel"
}
override fun create() {
notificationManager.createNotificationChannel(
NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_upcoming_lessons), IMPORTANCE_DEFAULT).apply {
lockscreenVisibility = VISIBILITY_PUBLIC
setShowBadge(false)
enableVibration(false)
}
)
}
}

View File

@ -11,7 +11,7 @@ class AttendanceSummaryWork @Inject constructor(
) : Work { ) : Work {
override fun create(student: Student, semester: Semester): Completable { override fun create(student: Student, semester: Semester): Completable {
return attendanceSummaryRepository.getAttendanceSummary(semester, -1, true).ignoreElement() return attendanceSummaryRepository.getAttendanceSummary(student, semester, -1, true).ignoreElement()
} }
} }

View File

@ -3,7 +3,7 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository
import io.github.wulkanowy.utils.friday import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.monday
import io.reactivex.Completable import io.reactivex.Completable
import org.threeten.bp.LocalDate.now import org.threeten.bp.LocalDate.now
@ -12,7 +12,7 @@ import javax.inject.Inject
class AttendanceWork @Inject constructor(private val attendanceRepository: AttendanceRepository) : Work { class AttendanceWork @Inject constructor(private val attendanceRepository: AttendanceRepository) : Work {
override fun create(student: Student, semester: Semester): Completable { override fun create(student: Student, semester: Semester): Completable {
return attendanceRepository.getAttendance(semester, now().monday, now().friday, true) return attendanceRepository.getAttendance(student, semester, now().monday, now().sunday, true)
.ignoreElement() .ignoreElement()
} }
} }

View File

@ -3,7 +3,7 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.completedlessons.CompletedLessonsRepository import io.github.wulkanowy.data.repositories.completedlessons.CompletedLessonsRepository
import io.github.wulkanowy.utils.friday import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.monday
import io.reactivex.Completable import io.reactivex.Completable
import org.threeten.bp.LocalDate.now import org.threeten.bp.LocalDate.now
@ -14,7 +14,7 @@ class CompletedLessonWork @Inject constructor(
) : Work { ) : Work {
override fun create(student: Student, semester: Semester): Completable { override fun create(student: Student, semester: Semester): Completable {
return completedLessonsRepository.getCompletedLessons(semester, now().monday, now().friday, true) return completedLessonsRepository.getCompletedLessons(student, semester, now().monday, now().sunday, true)
.ignoreElement() .ignoreElement()
} }
} }

View File

@ -3,7 +3,7 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.exam.ExamRepository import io.github.wulkanowy.data.repositories.exam.ExamRepository
import io.github.wulkanowy.utils.friday import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.monday
import io.reactivex.Completable import io.reactivex.Completable
import org.threeten.bp.LocalDate.now import org.threeten.bp.LocalDate.now
@ -12,6 +12,6 @@ import javax.inject.Inject
class ExamWork @Inject constructor(private val examRepository: ExamRepository) : Work { class ExamWork @Inject constructor(private val examRepository: ExamRepository) : Work {
override fun create(student: Student, semester: Semester): Completable { override fun create(student: Student, semester: Semester): Completable {
return examRepository.getExams(semester, now().monday, now().friday, true).ignoreElement() return examRepository.getExams(student, semester, now().monday, now().sunday, true).ignoreElement()
} }
} }

View File

@ -9,7 +9,7 @@ import javax.inject.Inject
class GradeStatisticsWork @Inject constructor(private val gradeStatisticsRepository: GradeStatisticsRepository) : Work { class GradeStatisticsWork @Inject constructor(private val gradeStatisticsRepository: GradeStatisticsRepository) : Work {
override fun create(student: Student, semester: Semester): Completable { override fun create(student: Student, semester: Semester): Completable {
return gradeStatisticsRepository.getGradesStatistics(semester, "Wszystkie", false, true) return gradeStatisticsRepository.getGradesStatistics(student, semester, "Wszystkie", false, true)
.ignoreElement() .ignoreElement()
} }
} }

View File

@ -1,14 +0,0 @@
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.gradessummary.GradeSummaryRepository
import io.reactivex.Completable
import javax.inject.Inject
class GradeSummaryWork @Inject constructor(private val gradeSummaryRepository: GradeSummaryRepository) : Work {
override fun create(student: Student, semester: Semester): Completable {
return gradeSummaryRepository.getGradesSummary(semester, true).ignoreElement()
}
}

View File

@ -58,4 +58,3 @@ class GradeWork @Inject constructor(
) )
} }
} }

View File

@ -3,7 +3,7 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.homework.HomeworkRepository import io.github.wulkanowy.data.repositories.homework.HomeworkRepository
import io.github.wulkanowy.utils.friday import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.monday
import io.reactivex.Completable import io.reactivex.Completable
import org.threeten.bp.LocalDate.now import org.threeten.bp.LocalDate.now
@ -12,6 +12,6 @@ import javax.inject.Inject
class HomeworkWork @Inject constructor(private val homeworkRepository: HomeworkRepository) : Work { class HomeworkWork @Inject constructor(private val homeworkRepository: HomeworkRepository) : Work {
override fun create(student: Student, semester: Semester): Completable { override fun create(student: Student, semester: Semester): Completable {
return homeworkRepository.getHomework(semester, now().monday, now().friday, true).ignoreElement() return homeworkRepository.getHomework(student, semester, now().monday, now().sunday, true).ignoreElement()
} }
} }

View File

@ -9,6 +9,6 @@ import javax.inject.Inject
class TeacherWork @Inject constructor(private val teacherRepository: TeacherRepository) : Work { class TeacherWork @Inject constructor(private val teacherRepository: TeacherRepository) : Work {
override fun create(student: Student, semester: Semester): Completable { override fun create(student: Student, semester: Semester): Completable {
return teacherRepository.getTeachers(semester, true).ignoreElement() return teacherRepository.getTeachers(student, semester, true).ignoreElement()
} }
} }

View File

@ -3,7 +3,7 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.timetable.TimetableRepository import io.github.wulkanowy.data.repositories.timetable.TimetableRepository
import io.github.wulkanowy.utils.friday import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.monday
import io.reactivex.Completable import io.reactivex.Completable
import org.threeten.bp.LocalDate.now import org.threeten.bp.LocalDate.now
@ -12,7 +12,7 @@ import javax.inject.Inject
class TimetableWork @Inject constructor(private val timetableRepository: TimetableRepository) : Work { class TimetableWork @Inject constructor(private val timetableRepository: TimetableRepository) : Work {
override fun create(student: Student, semester: Semester): Completable { override fun create(student: Student, semester: Semester): Completable {
return timetableRepository.getTimetable(semester, now().monday, now().friday, true) return timetableRepository.getTimetable(student, semester, now().monday, now().sunday, true)
.ignoreElement() .ignoreElement()
} }
} }

View File

@ -11,6 +11,7 @@ import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.viewbinding.ViewBinding
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.android.material.snackbar.Snackbar.LENGTH_LONG import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
import dagger.android.AndroidInjection import dagger.android.AndroidInjection
@ -20,10 +21,13 @@ import io.github.wulkanowy.R
import io.github.wulkanowy.ui.modules.login.LoginActivity import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.utils.FragmentLifecycleLogger import io.github.wulkanowy.utils.FragmentLifecycleLogger
import io.github.wulkanowy.utils.getThemeAttrColor import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.lifecycleAwareVariable
import javax.inject.Inject import javax.inject.Inject
abstract class BaseActivity<T : BasePresenter<out BaseView>> : AppCompatActivity(), BaseView, abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
HasAndroidInjector { AppCompatActivity(), BaseView, HasAndroidInjector {
protected var binding: VB by lifecycleAwareVariable()
@Inject @Inject
lateinit var androidInjector: DispatchingAndroidInjector<Any> lateinit var androidInjector: DispatchingAndroidInjector<Any>

View File

@ -1,9 +1,13 @@
package io.github.wulkanowy.ui.base package io.github.wulkanowy.ui.base
import android.widget.Toast import android.widget.Toast
import androidx.viewbinding.ViewBinding
import dagger.android.support.DaggerAppCompatDialogFragment import dagger.android.support.DaggerAppCompatDialogFragment
import io.github.wulkanowy.utils.lifecycleAwareVariable
abstract class BaseDialogFragment : DaggerAppCompatDialogFragment(), BaseView { abstract class BaseDialogFragment<VB : ViewBinding> : DaggerAppCompatDialogFragment(), BaseView {
protected var binding: VB by lifecycleAwareVariable()
override fun showError(text: String, error: Throwable) { override fun showError(text: String, error: Throwable) {
showMessage(text) showMessage(text)
@ -14,11 +18,11 @@ abstract class BaseDialogFragment : DaggerAppCompatDialogFragment(), BaseView {
} }
override fun showExpiredDialog() { override fun showExpiredDialog() {
(activity as? BaseActivity<*>)?.showExpiredDialog() (activity as? BaseActivity<*, *>)?.showExpiredDialog()
} }
override fun openClearLoginView() { override fun openClearLoginView() {
(activity as? BaseActivity<*>)?.openClearLoginView() (activity as? BaseActivity<*, *>)?.openClearLoginView()
} }
override fun showErrorDetailsDialog(error: Throwable) { override fun showErrorDetailsDialog(error: Throwable) {

View File

@ -0,0 +1,58 @@
package io.github.wulkanowy.ui.base
import android.util.DisplayMetrics
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearSmoothScroller
import androidx.recyclerview.widget.RecyclerView
import kotlin.math.max
import kotlin.math.min
abstract class BaseExpandableAdapter<T : RecyclerView.ViewHolder> : RecyclerView.Adapter<T>() {
companion object {
private const val MILLISECONDS_PER_INCH = 100f
private const val AUTO_SCROLL_DELAY = 150L
}
private var recyclerView: RecyclerView? = null
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
this.recyclerView = recyclerView
}
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
super.onDetachedFromRecyclerView(recyclerView)
this.recyclerView = null
}
// original: https://github.com/davideas/FlexibleAdapter/blob/5.1.0/flexible-adapter/src/main/java/eu/davidea/flexibleadapter/FlexibleAdapter.java#L4984-L5011
protected fun scrollToHeaderWithSubItems(position: Int, subItemsCount: Int) {
val layoutManager = recyclerView!!.layoutManager as LinearLayoutManager
val firstVisibleItem = layoutManager.findFirstCompletelyVisibleItemPosition()
val lastVisibleItem = layoutManager.findLastCompletelyVisibleItemPosition()
val itemsToShow = position + subItemsCount - lastVisibleItem
if (itemsToShow > 0) {
val scrollMax: Int = position - firstVisibleItem
val scrollMin = max(0, position + subItemsCount - lastVisibleItem)
val scrollBy = min(scrollMax, scrollMin)
val scrollTo = firstVisibleItem + scrollBy
scrollToPosition(scrollTo)
} else if (position < firstVisibleItem) {
scrollToPosition(position)
}
}
private fun scrollToPosition(position: Int) {
recyclerView?.run {
postDelayed({
layoutManager?.startSmoothScroll(object : LinearSmoothScroller(context) {
override fun getVerticalSnapPreference() = SNAP_TO_START
override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics) = MILLISECONDS_PER_INCH / displayMetrics.densityDpi
}.apply {
targetPosition = position
})
}, AUTO_SCROLL_DELAY)
}
}
}

View File

@ -1,12 +1,18 @@
package io.github.wulkanowy.ui.base package io.github.wulkanowy.ui.base
import android.view.View import android.view.View
import androidx.annotation.LayoutRes
import androidx.viewbinding.ViewBinding
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.android.material.snackbar.Snackbar.LENGTH_LONG import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.utils.lifecycleAwareVariable
abstract class BaseFragment : DaggerFragment(), BaseView { abstract class BaseFragment<VB : ViewBinding>(@LayoutRes layoutId: Int) : DaggerFragment(layoutId),
BaseView {
protected var binding: VB by lifecycleAwareVariable()
protected var messageContainer: View? = null protected var messageContainer: View? = null
@ -16,7 +22,7 @@ abstract class BaseFragment : DaggerFragment(), BaseView {
.setAction(R.string.all_details) { if (isAdded) showErrorDetailsDialog(error) } .setAction(R.string.all_details) { if (isAdded) showErrorDetailsDialog(error) }
.show() .show()
} else { } else {
(activity as? BaseActivity<*>)?.showError(text, error) (activity as? BaseActivity<*, *>)?.showError(text, error)
} }
} }
@ -28,15 +34,15 @@ abstract class BaseFragment : DaggerFragment(), BaseView {
if (messageContainer != null) { if (messageContainer != null) {
Snackbar.make(messageContainer!!, text, LENGTH_LONG).show() Snackbar.make(messageContainer!!, text, LENGTH_LONG).show()
} else { } else {
(activity as? BaseActivity<*>)?.showMessage(text) (activity as? BaseActivity<*, *>)?.showMessage(text)
} }
} }
override fun showExpiredDialog() { override fun showExpiredDialog() {
(activity as? BaseActivity<*>)?.showExpiredDialog() (activity as? BaseActivity<*, *>)?.showExpiredDialog()
} }
override fun openClearLoginView() { override fun openClearLoginView() {
(activity as? BaseActivity<*>)?.openClearLoginView() (activity as? BaseActivity<*, *>)?.openClearLoginView()
} }
} }

View File

@ -6,19 +6,35 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.HorizontalScrollView
import android.widget.Toast import android.widget.Toast
import android.widget.Toast.LENGTH_LONG import android.widget.Toast.LENGTH_LONG
import androidx.appcompat.app.AlertDialog
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.fragment.app.DialogFragment
import io.github.wulkanowy.R import io.github.wulkanowy.R
import kotlinx.android.synthetic.main.dialog_error.* import io.github.wulkanowy.databinding.DialogErrorBinding
import io.github.wulkanowy.sdk.exception.FeatureDisabledException
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
import io.github.wulkanowy.sdk.exception.ServiceUnavailableException
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.getString
import io.github.wulkanowy.utils.openAppInMarket
import io.github.wulkanowy.utils.openEmailClient
import io.github.wulkanowy.utils.openInternetBrowser
import java.io.InterruptedIOException
import java.io.PrintWriter import java.io.PrintWriter
import java.io.StringWriter import java.io.StringWriter
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import javax.inject.Inject
class ErrorDialog : DialogFragment() { class ErrorDialog : BaseDialogFragment<DialogErrorBinding>() {
private lateinit var error: Throwable private lateinit var error: Throwable
@Inject
lateinit var appInfo: AppInfo
companion object { companion object {
private const val ARGUMENT_KEY = "Data" private const val ARGUMENT_KEY = "Data"
@ -38,7 +54,7 @@ class ErrorDialog : DialogFragment() {
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.dialog_error, container, false) return DialogErrorBinding.inflate(inflater).apply { binding = this }.root
} }
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
@ -48,14 +64,56 @@ class ErrorDialog : DialogFragment() {
error.printStackTrace(PrintWriter(this)) error.printStackTrace(PrintWriter(this))
} }
errorDialogContent.text = stringWriter.toString() with(binding) {
errorDialogCopy.setOnClickListener { errorDialogContent.text = stringWriter.toString()
val clip = ClipData.newPlainText("wulkanowy", stringWriter.toString()) with(errorDialogHorizontalScroll) {
activity?.getSystemService<ClipboardManager>()?.setPrimaryClip(clip) post { fullScroll(HorizontalScrollView.FOCUS_LEFT) }
}
errorDialogCopy.setOnClickListener {
val clip = ClipData.newPlainText("wulkanowy", stringWriter.toString())
activity?.getSystemService<ClipboardManager>()?.setPrimaryClip(clip)
Toast.makeText(context, R.string.all_copied, LENGTH_LONG).show() Toast.makeText(context, R.string.all_copied, LENGTH_LONG).show()
}
errorDialogCancel.setOnClickListener { dismiss() }
errorDialogReport.setOnClickListener {
openConfirmDialog { openEmailClient(stringWriter.toString()) }
}
errorDialogMessage.text = resources.getString(error)
errorDialogReport.isEnabled = when (error) {
is UnknownHostException,
is InterruptedIOException,
is SocketTimeoutException,
is ServiceUnavailableException,
is FeatureDisabledException,
is FeatureNotAvailableException -> false
else -> true
}
} }
errorDialogCancel.setOnClickListener { dismiss() } }
private fun openConfirmDialog(callback: () -> Unit) {
AlertDialog.Builder(requireContext())
.setTitle(R.string.dialog_error_check_update)
.setMessage(R.string.dialog_error_check_update_message)
.setNeutralButton(R.string.about_feedback) { _, _ -> callback() }
.setPositiveButton(R.string.dialog_error_check_update) { _, _ ->
requireContext().openAppInMarket(::showMessage)
}
.show()
}
private fun openEmailClient(content: String) {
requireContext().openEmailClient(
chooserTitle = getString(R.string.about_feedback),
email = "wulkanowyinc@gmail.com",
subject = "Zgłoszenie błędu",
body = requireContext().getString(R.string.about_feedback_template,
"${appInfo.systemManufacturer} ${appInfo.systemModel}", appInfo.systemVersion.toString(), appInfo.versionName
) + "\n" + content,
onActivityNotFound = {
requireContext().openInternetBrowser("https://github.com/wulkanowy/wulkanowy/issues", ::showMessage)
}
)
} }
} }

View File

@ -2,17 +2,11 @@ package io.github.wulkanowy.ui.base
import android.content.res.Resources import android.content.res.Resources
import com.chuckerteam.chucker.api.ChuckerCollector import com.chuckerteam.chucker.api.ChuckerCollector
import io.github.wulkanowy.R
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
import io.github.wulkanowy.sdk.exception.BadCredentialsException import io.github.wulkanowy.sdk.exception.BadCredentialsException
import io.github.wulkanowy.sdk.exception.FeatureDisabledException import io.github.wulkanowy.utils.getString
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
import io.github.wulkanowy.sdk.exception.NotLoggedInException
import io.github.wulkanowy.sdk.exception.ServiceUnavailableException
import io.github.wulkanowy.utils.security.ScramblerException import io.github.wulkanowy.utils.security.ScramblerException
import timber.log.Timber import timber.log.Timber
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import javax.inject.Inject import javax.inject.Inject
open class ErrorHandler @Inject constructor(protected val resources: Resources, private val chuckerCollector: ChuckerCollector) { open class ErrorHandler @Inject constructor(protected val resources: Resources, private val chuckerCollector: ChuckerCollector) {
@ -30,18 +24,10 @@ open class ErrorHandler @Inject constructor(protected val resources: Resources,
} }
protected open fun proceed(error: Throwable) { protected open fun proceed(error: Throwable) {
resources.run { when (error) {
when (error) { is ScramblerException, is BadCredentialsException -> onSessionExpired()
is UnknownHostException -> showErrorMessage(getString(R.string.error_no_internet), error) is NoCurrentStudentException -> onNoCurrentStudent()
is SocketTimeoutException -> showErrorMessage(getString(R.string.error_timeout), error) else -> showErrorMessage(resources.getString(error), error)
is NotLoggedInException -> showErrorMessage(getString(R.string.error_login_failed), error)
is ServiceUnavailableException -> showErrorMessage(getString(R.string.error_service_unavailable), error)
is FeatureDisabledException -> showErrorMessage(getString(R.string.error_feature_disabled), error)
is ScramblerException, is BadCredentialsException -> onSessionExpired()
is NoCurrentStudentException -> onNoCurrentStudent()
is FeatureNotAvailableException -> showErrorMessage(getString(R.string.error_feature_not_available), error)
else -> showErrorMessage(getString(R.string.error_unknown), error)
}
} }
} }

View File

@ -0,0 +1,46 @@
package io.github.wulkanowy.ui.base
import android.annotation.SuppressLint
import android.graphics.PorterDuff
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.databinding.ItemAccountBinding
import io.github.wulkanowy.utils.getThemeAttrColor
import javax.inject.Inject
class WidgetConfigureAdapter @Inject constructor() : RecyclerView.Adapter<WidgetConfigureAdapter.ItemViewHolder>() {
var items = emptyList<Pair<Student, Boolean>>()
var onClickListener: (Student) -> Unit = {}
override fun getItemCount() = items.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
ItemAccountBinding.inflate(LayoutInflater.from(parent.context), parent, false)
)
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
val (student, isCurrent) = items[position]
with(holder.binding) {
accountItemName.text = "${student.studentName} ${student.className}"
accountItemSchool.text = student.schoolName
with(accountItemImage) {
val colorImage = if (isCurrent) context.getThemeAttrColor(R.attr.colorPrimary)
else context.getThemeAttrColor(R.attr.colorOnSurface, 153)
setColorFilter(colorImage, PorterDuff.Mode.SRC_IN)
}
root.setOnClickListener { onClickListener(student) }
}
}
class ItemViewHolder(val binding: ItemAccountBinding) : RecyclerView.ViewHolder(binding.root)
}

View File

@ -0,0 +1,72 @@
package io.github.wulkanowy.ui.modules.about
import android.graphics.drawable.Drawable
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.content.res.ResourcesCompat
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.databinding.ItemAboutBinding
import io.github.wulkanowy.databinding.ScrollableHeaderAboutBinding
import javax.inject.Inject
class AboutAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private enum class ViewType(val id: Int) {
ITEM_HEADER(1),
ITEM_ELEMENT(2)
}
var items = emptyList<Triple<String, String, Drawable?>>()
var onClickListener: (name: String) -> Unit = {}
override fun getItemCount() = items.size + 1
override fun getItemViewType(position: Int) = when (position) {
0 -> ViewType.ITEM_HEADER.id
else -> ViewType.ITEM_ELEMENT.id
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return when (viewType) {
ViewType.ITEM_HEADER.id -> HeaderViewHolder(ScrollableHeaderAboutBinding.inflate(inflater, parent, false))
ViewType.ITEM_ELEMENT.id -> ItemViewHolder(ItemAboutBinding.inflate(inflater, parent, false))
else -> throw IllegalStateException()
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is HeaderViewHolder -> bindHeaderViewHolder(holder.binding)
is ItemViewHolder -> bindItemViewHolder(holder.binding, position - 1)
}
}
private fun bindHeaderViewHolder(binding: ScrollableHeaderAboutBinding) {
with(binding.aboutScrollableHeaderIcon) {
setImageDrawable(ResourcesCompat.getDrawableForDensity(
context.resources, context.applicationInfo.icon, 640, null)
)
}
}
private fun bindItemViewHolder(binding: ItemAboutBinding, position: Int) {
val (title, summary, image) = items[position]
with(binding) {
aboutItemImage.setImageDrawable(image)
aboutItemTitle.text = title
aboutItemSummary.text = summary
root.setOnClickListener { onClickListener(title) }
}
}
private class HeaderViewHolder(val binding: ScrollableHeaderAboutBinding) :
RecyclerView.ViewHolder(binding.root)
private class ItemViewHolder(val binding: ItemAboutBinding) :
RecyclerView.ViewHolder(binding.root)
}

View File

@ -2,13 +2,10 @@ package io.github.wulkanowy.ui.modules.about
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import androidx.recyclerview.widget.LinearLayoutManager
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.R
import io.github.wulkanowy.databinding.FragmentAboutBinding
import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.about.contributor.ContributorFragment import io.github.wulkanowy.ui.modules.about.contributor.ContributorFragment
import io.github.wulkanowy.ui.modules.about.license.LicenseFragment import io.github.wulkanowy.ui.modules.about.license.LicenseFragment
@ -17,19 +14,19 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.getCompatDrawable import io.github.wulkanowy.utils.getCompatDrawable
import io.github.wulkanowy.utils.openAppInMarket
import io.github.wulkanowy.utils.openEmailClient import io.github.wulkanowy.utils.openEmailClient
import io.github.wulkanowy.utils.openInternetBrowser import io.github.wulkanowy.utils.openInternetBrowser
import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.fragment_about.*
import javax.inject.Inject import javax.inject.Inject
class AboutFragment : BaseFragment(), AboutView, MainView.TitledView { class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about), AboutView,
MainView.TitledView {
@Inject @Inject
lateinit var presenter: AboutPresenter lateinit var presenter: AboutPresenter
@Inject @Inject
lateinit var aboutAdapter: FlexibleAdapter<AbstractFlexibleItem<*>> lateinit var aboutAdapter: AboutAdapter
@Inject @Inject
lateinit var appInfo: AppInfo lateinit var appInfo: AppInfo
@ -80,34 +77,34 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
fun newInstance() = AboutFragment() fun newInstance() = AboutFragment()
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_about, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding = FragmentAboutBinding.bind(view)
presenter.onAttachView(this) presenter.onAttachView(this)
} }
override fun initView() { override fun initView() {
aboutAdapter.setOnItemClickListener(presenter::onItemSelected) aboutAdapter.onClickListener = presenter::onItemSelected
with(aboutRecycler) { with(binding.aboutRecycler) {
layoutManager = SmoothScrollLinearLayoutManager(context) layoutManager = LinearLayoutManager(context)
adapter = aboutAdapter adapter = aboutAdapter
} }
} }
override fun updateData(header: AboutScrollableHeader, items: List<AboutItem>) { override fun updateData(data: List<Triple<String, String, Drawable?>>) {
with(aboutAdapter) { with(aboutAdapter) {
removeAllScrollableHeaders() items = data
addScrollableHeader(header) notifyDataSetChanged()
updateDataSet(items)
} }
} }
override fun openAppInMarket() {
context?.openAppInMarket(::showMessage)
}
override fun openLogViewer() { override fun openLogViewer() {
if (appInfo.isDebug) (activity as? MainActivity)?.pushView(LogViewerFragment.newInstance()) (activity as? MainActivity)?.pushView(LogViewerFragment.newInstance())
} }
override fun openDiscordInvite() { override fun openDiscordInvite() {
@ -123,7 +120,7 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
chooserTitle = getString(R.string.about_feedback), chooserTitle = getString(R.string.about_feedback),
email = "wulkanowyinc@gmail.com", email = "wulkanowyinc@gmail.com",
subject = "Zgłoszenie błędu", subject = "Zgłoszenie błędu",
body = requireContext().getString(R.string.about_feedback_template, body = getString(R.string.about_feedback_template,
"${appInfo.systemManufacturer} ${appInfo.systemModel}", appInfo.systemVersion.toString(), appInfo.versionName "${appInfo.systemManufacturer} ${appInfo.systemModel}", appInfo.systemVersion.toString(), appInfo.versionName
), ),
onActivityNotFound = { onActivityNotFound = {

View File

@ -1,56 +0,0 @@
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,9 +1,9 @@
package io.github.wulkanowy.ui.modules.about package io.github.wulkanowy.ui.modules.about
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import timber.log.Timber import timber.log.Timber
@ -13,6 +13,7 @@ class AboutPresenter @Inject constructor(
schedulers: SchedulersProvider, schedulers: SchedulersProvider,
errorHandler: ErrorHandler, errorHandler: ErrorHandler,
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val appInfo: AppInfo,
private val analytics: FirebaseAnalyticsHelper private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<AboutView>(errorHandler, studentRepository, schedulers) { ) : BasePresenter<AboutView>(errorHandler, studentRepository, schedulers) {
@ -23,13 +24,13 @@ class AboutPresenter @Inject constructor(
loadData() loadData()
} }
fun onItemSelected(item: AbstractFlexibleItem<*>) { fun onItemSelected(name: String) {
if (item !is AboutItem) return
view?.run { view?.run {
when (item.title) { when (name) {
versionRes?.first -> { versionRes?.first -> {
Timber.i("Opening log viewer") Timber.i("Opening log viewer")
openLogViewer() if (appInfo.isDebug) openLogViewer()
else openAppInMarket()
analytics.logEvent("about_open", "name" to "log_viewer") analytics.logEvent("about_open", "name" to "log_viewer")
} }
feedbackRes?.first -> { feedbackRes?.first -> {
@ -73,15 +74,16 @@ class AboutPresenter @Inject constructor(
private fun loadData() { private fun loadData() {
view?.run { view?.run {
updateData(AboutScrollableHeader(), listOfNotNull( updateData(listOfNotNull(
versionRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, versionRes,
creatorsRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, creatorsRes,
feedbackRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, feedbackRes,
faqRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, faqRes,
discordRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, discordRes,
homepageRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, homepageRes,
licensesRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, licensesRes,
privacyRes?.let { (title, summary, image) -> AboutItem(title, summary, image) })) privacyRes
))
} }
} }
} }

View File

@ -1,41 +0,0 @@
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
}
}

View File

@ -23,7 +23,9 @@ interface AboutView : BaseView {
fun initView() fun initView()
fun updateData(header: AboutScrollableHeader, items: List<AboutItem>) fun updateData(data: List<Triple<String, String, Drawable?>>)
fun openAppInMarket()
fun openLogViewer() fun openLogViewer()

View File

@ -0,0 +1,41 @@
package io.github.wulkanowy.ui.modules.about.contributor
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import coil.api.load
import coil.transform.RoundedCornersTransformation
import io.github.wulkanowy.data.pojos.Contributor
import io.github.wulkanowy.databinding.ItemContributorBinding
import javax.inject.Inject
class ContributorAdapter @Inject constructor() :
RecyclerView.Adapter<ContributorAdapter.ItemViewHolder>() {
var items = emptyList<Contributor>()
var onClickListener: (Contributor) -> Unit = {}
override fun getItemCount() = items.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
ItemContributorBinding.inflate(LayoutInflater.from(parent.context), parent, false)
)
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
val item = items[position]
with(holder.binding) {
creatorItemName.text = item.displayName
creatorItemAvatar.load("https://github.com/${item.githubUsername}.png") {
transformations(RoundedCornersTransformation(8f))
crossfade(true)
}
root.setOnClickListener { onClickListener(item) }
}
}
class ItemViewHolder(val binding: ItemContributorBinding) :
RecyclerView.ViewHolder(binding.root)
}

View File

@ -1,30 +1,27 @@
package io.github.wulkanowy.ui.modules.about.contributor package io.github.wulkanowy.ui.modules.about.contributor
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.View.GONE import android.view.View.GONE
import android.view.View.VISIBLE import android.view.View.VISIBLE
import android.view.ViewGroup import androidx.recyclerview.widget.LinearLayoutManager
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.pojos.Contributor
import io.github.wulkanowy.databinding.FragmentContributorBinding
import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
import io.github.wulkanowy.utils.openInternetBrowser import io.github.wulkanowy.utils.openInternetBrowser
import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.fragment_creator.*
import javax.inject.Inject import javax.inject.Inject
class ContributorFragment : BaseFragment(), ContributorView, MainView.TitledView { class ContributorFragment : BaseFragment<FragmentContributorBinding>(R.layout.fragment_contributor),
ContributorView, MainView.TitledView {
@Inject @Inject
lateinit var presenter: ContributorPresenter lateinit var presenter: ContributorPresenter
@Inject @Inject
lateinit var creatorsAdapter: FlexibleAdapter<AbstractFlexibleItem<*>> lateinit var creatorsAdapter: ContributorAdapter
override val titleStringId get() = R.string.contributors_title override val titleStringId get() = R.string.contributors_title
@ -32,29 +29,27 @@ class ContributorFragment : BaseFragment(), ContributorView, MainView.TitledView
fun newInstance() = ContributorFragment() fun newInstance() = ContributorFragment()
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
return inflater.inflate(R.layout.fragment_creator, container, false) super.onViewCreated(view, savedInstanceState)
} binding = FragmentContributorBinding.bind(view)
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
presenter.onAttachView(this) presenter.onAttachView(this)
} }
override fun initView() { override fun initView() {
with(creatorRecycler) { with(binding.creatorRecycler) {
layoutManager = SmoothScrollLinearLayoutManager(context) layoutManager = LinearLayoutManager(context)
adapter = creatorsAdapter adapter = creatorsAdapter
addItemDecoration(FlexibleItemDecoration(context) addItemDecoration(DividerItemDecoration(context))
.withDefaultDivider()
.withDrawDividerOnLastItem(false))
} }
creatorsAdapter.setOnItemClickListener(presenter::onItemSelected) creatorsAdapter.onClickListener = presenter::onItemSelected
creatorSeeMore.setOnClickListener { presenter.onSeeMoreClick() } binding.creatorSeeMore.setOnClickListener { presenter.onSeeMoreClick() }
} }
override fun updateData(data: List<ContributorItem>) { override fun updateData(data: List<Contributor>) {
creatorsAdapter.updateDataSet(data) with(creatorsAdapter) {
items = data
notifyDataSetChanged()
}
} }
override fun openUserGithubPage(username: String) { override fun openUserGithubPage(username: String) {
@ -66,7 +61,7 @@ class ContributorFragment : BaseFragment(), ContributorView, MainView.TitledView
} }
override fun showProgress(show: Boolean) { override fun showProgress(show: Boolean) {
creatorProgress.visibility = if (show) VISIBLE else GONE binding.creatorProgress.visibility = if (show) VISIBLE else GONE
} }
override fun onDestroyView() { override fun onDestroyView() {

View File

@ -1,51 +0,0 @@
package io.github.wulkanowy.ui.modules.about.contributor
import android.view.View
import coil.api.load
import coil.transform.RoundedCornersTransformation
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 io.github.wulkanowy.data.pojos.AppCreator
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_contributor.*
class ContributorItem(val creator: AppCreator) :
AbstractFlexibleItem<ContributorItem.ViewHolder>() {
override fun getLayoutRes() = R.layout.item_contributor
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) {
creatorItemName.text = creator.displayName
creatorItemAvatar.load("https://github.com/${creator.githubUsername}.png") {
transformations(RoundedCornersTransformation(8f))
crossfade(true)
}
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ContributorItem
if (creator != other.creator) return false
return true
}
override fun hashCode() = creator.hashCode()
class ViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>) : FlexibleViewHolder(view, adapter),
LayoutContainer {
override val containerView: View? get() = contentView
}
}

View File

@ -1,6 +1,6 @@
package io.github.wulkanowy.ui.modules.about.contributor package io.github.wulkanowy.ui.modules.about.contributor
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import io.github.wulkanowy.data.pojos.Contributor
import io.github.wulkanowy.data.repositories.appcreator.AppCreatorRepository import io.github.wulkanowy.data.repositories.appcreator.AppCreatorRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
@ -21,9 +21,8 @@ class ContributorPresenter @Inject constructor(
loadData() loadData()
} }
fun onItemSelected(item: AbstractFlexibleItem<*>) { fun onItemSelected(contributor: Contributor) {
if (item !is ContributorItem) return view?.openUserGithubPage(contributor.githubUsername)
view?.openUserGithubPage(item.creator.githubUsername)
} }
fun onSeeMoreClick() { fun onSeeMoreClick() {
@ -32,7 +31,6 @@ class ContributorPresenter @Inject constructor(
private fun loadData() { private fun loadData() {
disposable.add(appCreatorRepository.getAppCreators() disposable.add(appCreatorRepository.getAppCreators()
.map { it.map { creator -> ContributorItem(creator) } }
.subscribeOn(schedulers.backgroundThread) .subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread) .observeOn(schedulers.mainThread)
.doFinally { view?.showProgress(false) } .doFinally { view?.showProgress(false) }

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