Compare commits

...

70 Commits
0.8.3 ... 0.9.3

Author SHA1 Message Date
08ecbb5341 Version 0.9.3 2019-06-15 01:13:36 +02:00
e38e458386 Fix crash on login when error message is null (#420) 2019-06-15 00:51:54 +02:00
14674b7778 Bump room-testing from 2.1.0-rc01 to 2.1.0 (#417) 2019-06-13 22:33:12 +00:00
c6f0588165 Bump room-compiler from 2.1.0-rc01 to 2.1.0 (#415) 2019-06-13 22:12:23 +00:00
7591af0de1 Bump room-rxjava2 from 2.1.0-rc01 to 2.1.0 (#411) 2019-06-13 21:52:32 +00:00
cbfed09b52 Bump room-runtime from 2.1.0-rc01 to 2.1.0 (#419) 2019-06-13 21:29:47 +00:00
4a026e4a70 Merge tag '0.9.2' into develop
0.9.2 0.9.2
2019-06-08 11:38:42 +02:00
5c185c5eca Merge branch 'release/0.9.2' 2019-06-08 11:38:42 +02:00
0bccbc6011 Version 0.9.2 2019-06-08 11:38:06 +02:00
ed6a0f8cd0 Add StackOverflowError to RxJava's error handler (#408) 2019-06-07 21:38:53 +02:00
58d5e4da0e Fix showing empty view in mobile device view (#407) 2019-06-07 19:03:26 +02:00
ba6fb1a4b9 Fix update of grades in GradeDetailsFragment (#406) 2019-06-07 14:46:11 +02:00
b6b862d4c3 Organize AndroidX libraries (#404) 2019-06-07 14:12:52 +02:00
9b044a19fe Fix typo in service interval (#405) 2019-06-06 22:47:45 +02:00
7485cb2a39 Fix change of worker specs after app update (#402) 2019-06-06 22:32:43 +02:00
75122d0dcd Fix crash TabLayout on Android 4.x in new version of material components (#403) 2019-06-06 19:16:00 +02:00
39af56484b Bump threetenabp from 1.2.0 to 1.2.1 (#401) 2019-06-04 11:23:50 +00:00
05f7e1d115 Merge tag '0.9.1' into develop
Version 0.9.1
2019-06-04 02:44:04 +02:00
9cd5377438 Merge branch 'release/0.9.1' 2019-06-04 02:43:54 +02:00
7a61b233f0 Version 0.9.1 2019-06-04 02:43:50 +02:00
83dbd9874e Add option to force calc average by app (#400) 2019-06-04 02:27:15 +02:00
1d9a49d552 Add githook plugin (#398) 2019-06-04 01:53:20 +02:00
6175081b88 Fix tab layout crash on prelolipop (#399) 2019-06-03 22:47:35 +02:00
116e551186 Merge tag '0.9.0' into develop
0.9.0 0.9.0
2019-06-03 18:15:42 +02:00
713500d892 Merge branch 'release/0.9.0' 2019-06-03 18:15:41 +02:00
dfdbe374c2 Version 0.9.0 2019-06-03 18:15:11 +02:00
b1e3bab5e7 Add F-Droid flavor (#349) 2019-06-03 14:12:48 +02:00
0b2ef367da Change timeout string (#397) 2019-06-03 13:15:21 +02:00
28f27db2b5 Add mobile access managment (#344) 2019-06-03 00:43:54 +02:00
5c70cd8b8c Add session expired dialog after password change (#389) 2019-05-31 15:12:10 +02:00
0f75ff3206 Show grades average from register if exist instead of calculating (#374) 2019-05-31 14:40:53 +02:00
383cab4dae Fix only wifi setting (#394) 2019-05-31 13:46:53 +02:00
e1b0db76c2 Bump material from 1.1.0-alpha06 to 1.1.0-alpha07 (#393) 2019-05-31 11:46:29 +02:00
f10684097c Bump dagger-compiler from 2.23 to 2.23.1 (#391) 2019-05-31 02:02:09 +00:00
35d4342bec Bump dagger-android-processor from 2.23 to 2.23.1 (#392) 2019-05-31 01:42:40 +00:00
068aee215a Bump dagger-android-support from 2.23 to 2.23.1 (#390) 2019-05-31 01:19:26 +00:00
033074c66b Bump room-testing from 2.1.0-beta01 to 2.1.0-rc01 (#377) 2019-05-30 16:46:54 +02:00
0de534cb66 Bump room-rxjava2 from 2.1.0-beta01 to 2.1.0-rc01 (#385) 2019-05-30 16:46:28 +02:00
26e4619dde Bump room-runtime from 2.1.0-beta01 to 2.1.0-rc01 (#388) 2019-05-30 14:06:06 +00:00
cf08d1ff24 Bump mockito-core from 2.28.1 to 2.28.2 (#387) 2019-05-30 13:58:42 +00:00
96c400a0bd Bump runner from 1.1.1 to 1.2.0 (#386) 2019-05-30 13:50:28 +00:00
e238f65dde Bump rxjava from 2.2.8 to 2.2.9 (#381) 2019-05-30 15:22:15 +02:00
ba7966125b Bump dagger-compiler from 2.22.1 to 2.23 (#380) 2019-05-30 12:01:15 +00:00
1536208e45 Bump core from 1.1.0 to 1.2.0 (#383) 2019-05-30 11:46:56 +00:00
24cc07264a Bump dagger-android-support from 2.22.1 to 2.23 (#384) 2019-05-30 11:31:22 +00:00
1ad5232520 Bump mockito-inline from 2.28.1 to 2.28.2 (#382) 2019-05-30 11:29:13 +00:00
07f3333029 Bump mockito-android from 2.28.1 to 2.28.2 (#379) 2019-05-30 11:12:44 +00:00
214afb82a6 Bump junit from 1.1.0 to 1.1.1 (#378) 2019-05-30 11:05:05 +00:00
7a69710261 Bump dagger-android-processor from 2.22.1 to 2.23 (#376) 2019-05-30 10:54:26 +00:00
64d3afbdb3 Bump room-compiler from 2.1.0-beta01 to 2.1.0-rc01 (#375) 2019-05-30 10:53:35 +00:00
387e46f72d Bump mockito-inline from 2.27.5 to 2.28.1 (#372) 2019-05-29 00:16:10 +00:00
89acc2f384 Bump mockito-android from 2.27.5 to 2.28.1 (#373) 2019-05-28 23:59:19 +00:00
4ef1439878 Bump mockito-core from 2.27.2 to 2.28.1 (#371) 2019-05-29 01:57:19 +02:00
6efd170e03 Bump mockito-inline from 2.27.0 to 2.27.5 (#367) 2019-05-28 14:57:39 +00:00
82cd39329a Bump mockito-android from 2.27.0 to 2.27.5 (#354) 2019-05-28 14:22:24 +00:00
b38e0d04e7 Bump threetenbp from 1.3.8 to 1.4.0 (#366) 2019-05-28 14:41:48 +02:00
90b2ffe250 Bump logging-interceptor from 3.12.1 to 3.12.3 (#369)
Bumps logging-interceptor from 3.12.1 to 3.12.3.
2019-05-28 14:41:27 +02:00
33951dff54 Bump crashlytics from 2.9.9 to 2.10.1 (#365) 2019-05-28 11:30:42 +00:00
9ced00c4d7 Bump room-runtime from 2.1.0-alpha07 to 2.1.0-beta01 (#364) 2019-05-28 11:06:56 +00:00
2ac1638911 Bump room-testing from 2.1.0-alpha07 to 2.1.0-beta01 (#363) 2019-05-28 10:48:37 +00:00
463a7e2744 Bump gradle from 1.28.1 to 1.29.0 (#355)
Bumps gradle from 1.28.1 to 1.29.0.
2019-05-28 02:24:36 +02:00
d14382f3b4 Bump firebase-core from 16.0.8 to 16.0.9 (#358)
Bumps firebase-core from 16.0.8 to 16.0.9.
2019-05-28 00:58:09 +02:00
efa0b19cc1 Bump room-rxjava2 from 2.1.0-alpha07 to 2.1.0-beta01 (#357)
Bumps room-rxjava2 from 2.1.0-alpha07 to 2.1.0-beta01.
2019-05-28 00:39:12 +02:00
7cb893a254 Bump play-publisher from 2.2.0 to 2.2.1 (#356)
Bumps play-publisher from 2.2.0 to 2.2.1.
2019-05-28 00:37:06 +02:00
f7371f7b73 Bump room-compiler from 2.1.0-alpha07 to 2.1.0-beta01 (#350)
Bumps room-compiler from 2.1.0-alpha07 to 2.1.0-beta01.
2019-05-27 23:04:12 +02:00
9c626ad517 Bump reactivenetwork-rx2 from 3.0.2 to 3.0.3 (#351)
Bumps [reactivenetwork-rx2](https://github.com/pwittchen/ReactiveNetwork) from 3.0.2 to 3.0.3.
- [Release notes](https://github.com/pwittchen/ReactiveNetwork/releases)
- [Changelog](https://github.com/pwittchen/ReactiveNetwork/blob/RxJava2.x/CHANGELOG.md)
- [Commits](https://github.com/pwittchen/ReactiveNetwork/commits)
2019-05-27 22:58:17 +02:00
9c4c6b0192 Bump material from 1.1.0-alpha05 to 1.1.0-alpha06 (#353)
Bumps [material](https://github.com/material-components/material-components-android) from 1.1.0-alpha05 to 1.1.0-alpha06.
- [Release notes](https://github.com/material-components/material-components-android/releases)
- [Commits](https://github.com/material-components/material-components-android/compare/1.1.0-alpha05...1.1.0-alpha06)
2019-05-27 22:49:16 +02:00
8efc4d750d Bump sonarqube-gradle-plugin from 2.7 to 2.7.1 (#352)
Bumps sonarqube-gradle-plugin from 2.7 to 2.7.1.
2019-05-27 22:41:16 +02:00
e4a6caa13e Version 0.8.4 2019-05-27 18:13:20 +02:00
209e75160a Fix network constraint in background sync (#348) 2019-05-25 14:34:17 +02:00
153 changed files with 4785 additions and 616 deletions

View File

@ -7,7 +7,7 @@ references:
container_config: &container_config
docker:
- image: circleci/android:api-28
- image: circleci/android@sha256:5cdc8626cc6f13efe5ed982cdcdb432b0472f8740fed8743a6461e025ad6cdfc
working_directory: *workspace_root
environment:
environment:
@ -35,7 +35,7 @@ jobs:
command: ./gradlew dependencies --no-daemon --stacktrace --console=plain -PdisablePreDex || true
- run:
name: Initial build
command: ./gradlew build -x test -x lint -x fabricGenerateResourcesRelease -x packageRelease --no-daemon --stacktrace --console=plain -PdisablePreDex
command: ./gradlew build -x test -x lint -x fabricGenerateResourcesFdroidRelease -x fabricGenerateResourcesPlayRelease -x packageRelease --no-daemon --stacktrace --console=plain -PdisablePreDex
- run:
name: Run FOSSA
command: fossa --no-ansi || true
@ -56,7 +56,7 @@ jobs:
<<: *general_cache_key
- run:
name: Run lint
command: ./gradlew lint -x fabricGenerateResourcesRelease --no-daemon --stacktrace --console=plain -PdisablePreDex
command: ./gradlew lint -x fabricGenerateResourcesFdroidRelease -x fabricGenerateResourcesPlayRelease --no-daemon --stacktrace --console=plain -PdisablePreDex
- store_artifacts:
path: ./app/build/reports/
destination: lint_reports/app/
@ -75,7 +75,7 @@ jobs:
<<: *general_cache_key
- run:
name: Run app tests
command: ./gradlew :app:test :app:jacocoTestReport -x fabricGenerateResourcesRelease --no-daemon --stacktrace --console=plain -PdisablePreDex
command: ./gradlew :app:test :app:jacocoTestReport -x fabricGenerateResourcesFdroidRelease -x fabricGenerateResourcesPlayRelease --no-daemon --stacktrace --console=plain -PdisablePreDex
- run:
name: Upload unit code coverage to codecov
command: bash <(curl -s https://codecov.io/bash) -F app
@ -93,6 +93,9 @@ jobs:
<<: *container_config
steps:
- *attach_workspace
- run:
name: Accept licenses
command: yes | sdkmanager --licenses && yes | sdkmanager --update
- run:
name: Setup emulator
command: sdkmanager "system-images;android-19;default;armeabi-v7a" && echo "no" | avdmanager create avd -n test -k "system-images;android-19;default;armeabi-v7a"
@ -113,7 +116,7 @@ jobs:
adb shell input keyevent 82
- run:
name: Run instrumented tests
command: ./gradlew clean createDebugCoverageReport jacocoTestReport --no-daemon --stacktrace --console=plain -PdisablePreDex
command: ./gradlew clean createPlayDebugCoverageReport jacocoTestReport --no-daemon --stacktrace --console=plain -PdisablePreDex
- run:
name: Collect logs from emulator
command: adb logcat -d > ./app/build/reports/logcat_emulator.txt
@ -159,7 +162,7 @@ jobs:
openssl aes-256-cbc -d -in ./app/upload-key-encrypted.jks -k $ENCRYPT_KEY >> ./app/upload-key.jks
- run:
name: Publish release
command: ./gradlew publish --no-daemon --stacktrace --console=plain -PenableCrashlytics -PdisablePreDex
command: ./gradlew publishPlayRelease --no-daemon --stacktrace --console=plain -PenableCrashlytics -PdisablePreDex
workflows:
version: 2

1
.gitignore vendored
View File

@ -48,3 +48,4 @@ app/key.p12
app/upload-key.jks
*.log
.idea/assetWizardSettings.xml
.idea/uiDesigner.xml

View File

@ -12,7 +12,7 @@ build:
script:
- ./gradlew --no-daemon --stacktrace dependencies || true
- ./gradlew --no-daemon --stacktrace assembleDebug
- mv app/build/outputs/apk/debug/app-debug.apk .
- mv app/build/outputs/apk/fdroid/debug/app-fdroid-debug.apk .
artifacts:
name: "${CI_PROJECT_NAME}_${CI_BUILD_REF_NAME}-${CI_BUILD_ID}"
paths:
@ -26,7 +26,7 @@ tests:
- .gradle
policy: pull
script:
- ./gradlew --no-daemon --stacktrace -x fabricGenerateResourcesRelease test
- ./gradlew --no-daemon --stacktrace -x fabricGenerateResourcesFdroidRelease -x fabricGenerateResourcesPlayRelease test
artifacts:
paths:
- app/build/reports/tests
@ -39,7 +39,7 @@ lint:
- .gradle
policy: pull
script:
- ./gradlew --no-daemon --stacktrace -x fabricGenerateResourcesRelease lint
- ./gradlew --no-daemon --stacktrace -x fabricGenerateResourcesFdroidRelease -x fabricGenerateResourcesPlayRelease lint
artifacts:
paths:
- app/build/reports

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>

View File

@ -11,10 +11,10 @@ cache:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
#branches:
# only:
# - master
# - 0.8.x
branches:
only:
- develop
- 0.9.2
android:
licenses:
@ -48,20 +48,20 @@ before_script:
script:
- ./gradlew dependencies --stacktrace --daemon
- fossa --no-ansi || true
- ./gradlew lint -x fabricGenerateResourcesRelease --stacktrace --daemon
- ./gradlew test -x fabricGenerateResourcesRelease --stacktrace --daemon
- ./gradlew createDebugCoverageReport --stacktrace --daemon
#- ./gradlew lintPlayRelease -x fabricGenerateResourcesPlayRelease --stacktrace --daemon
- ./gradlew testPlayDebugUnitTest -x fabricGenerateResourcesPlay --stacktrace --daemon
- ./gradlew createPlayDebugCoverageReport --stacktrace --daemon
- ./gradlew jacocoTestReport --stacktrace --daemon
- if [ -z ${SONAR_HOST+x} ]; then echo "sonar scan skipped"; else
git fetch --unshallow;
./gradlew sonarqube -x test -x lint -x fabricGenerateResourcesRelease -Dsonar.host.url=$SONAR_HOST -Dsonar.organization=$SONAR_ORG -Dsonar.login=$SONAR_KEY -Dsonar.branch.name=${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH} --stacktrace --daemon;
./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
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/upload-key.jks.gpg;
./gradlew publish -PenableCrashlytics --stacktrace;
./gradlew publishPlayRelease -PenableCrashlytics --stacktrace;
fi
after_success:

View File

@ -1,6 +1,5 @@
# Wulkanowy
[![CircleCI](https://img.shields.io/circleci/project/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://circleci.com/gh/wulkanowy/wulkanowy)
[![Travis](https://img.shields.io/travis/com/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://travis-ci.com/wulkanowy/wulkanowy)
[![Bitrise](https://img.shields.io/bitrise/daeff1893f3c8128/master.svg?token=Hjm1ACamk86JDeVVJHOeqQ&style=flat-square)](https://www.bitrise.io/app/daeff1893f3c8128)
[![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy)
@ -8,6 +7,8 @@
[![Sonarcloud](https://sonarcloud.io/api/project_badges/measure?project=io.github.wulkanowy%3Aapp&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=io.github.wulkanowy%3Aapp)
[![FOSSA Status](https://app.fossa.com/api/projects/custom%2B5644%2Fgithub.com%2Fwulkanowy%2Fwulkanowy.svg?type=shield)](https://app.fossa.com/projects/custom%2B5644%2Fgithub.com%2Fwulkanowy%2Fwulkanowy?ref=badge_shield)
[![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr)
[![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg)](https://f-droid.org/packages/io.github.wulkanowy/)
[![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github)](https://github.com/wulkanowy/wulkanowy/releases)
[Pobierz wersję beta z Google Play](https://play.google.com/store/apps/details?id=io.github.wulkanowy&amp;utm_source=vcs)

View File

@ -6,6 +6,7 @@ apply plugin: 'io.fabric'
apply plugin: 'com.github.triplet.play'
apply from: 'jacoco.gradle'
apply from: 'sonarqube.gradle'
apply from: 'hooks.gradle'
android {
compileSdkVersion 28
@ -16,8 +17,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 15
targetSdkVersion 28
versionCode 36
versionName "0.8.3"
versionCode 41
versionName "0.9.3"
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
@ -63,6 +64,18 @@ android {
}
}
flavorDimensions "platform"
productFlavors {
play {
dimension "platform"
}
fdroid {
buildConfigField "boolean", "CRASHLYTICS_ENABLED", "false"
dimension "platform"
}
}
lintOptions {
disable 'HardwareIds'
}
@ -85,70 +98,81 @@ play {
}
dependencies {
implementation 'io.github.wulkanowy:api:0.8.3'
implementation "io.github.wulkanowy:api:0.9.3"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "androidx.legacy:legacy-support-v4:1.0.0"
implementation "androidx.core:core:1.0.2"
implementation "androidx.appcompat:appcompat:1.0.2"
implementation 'androidx.multidex:multidex:2.0.1'
implementation "androidx.fragment:fragment:1.0.0"
implementation "androidx.annotation:annotation:1.1.0"
implementation "androidx.multidex:multidex:2.0.1"
implementation "androidx.cardview:cardview:1.0.0"
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation "com.google.android.material:material:1.1.0-alpha05"
implementation 'com.github.wulkanowy:MaterialChipsInput:b72fd0ee6f'
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
implementation "androidx.recyclerview:recyclerview:1.0.0"
implementation "androidx.viewpager:viewpager:1.0.0"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.0.0"
implementation "com.google.android.material:material:1.1.0-alpha07"
implementation "com.github.wulkanowy:MaterialChipsInput:b72fd0ee6f"
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
implementation "androidx.work:work-runtime:2.0.1"
implementation "androidx.work:work-rxjava2:2.0.1"
implementation "androidx.room:room-runtime:2.1.0-alpha07"
implementation "androidx.room:room-rxjava2:2.1.0-alpha07"
kapt "androidx.room:room-compiler:2.1.0-alpha07"
implementation "androidx.room:room-runtime:2.1.0"
implementation "androidx.room:room-rxjava2:2.1.0"
kapt "androidx.room:room-compiler:2.1.0"
implementation "com.google.dagger:dagger-android-support:2.22.1"
kapt "com.google.dagger:dagger-compiler:2.22.1"
kapt "com.google.dagger:dagger-android-processor:2.22.1"
implementation 'com.squareup.inject:assisted-inject-annotations-dagger2:0.4.0'
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.4.0'
implementation "com.google.dagger:dagger-android-support:2.23.1"
kapt "com.google.dagger:dagger-compiler:2.23.1"
kapt "com.google.dagger:dagger-android-processor:2.23.1"
implementation "com.squareup.inject:assisted-inject-annotations-dagger2:0.4.0"
kapt "com.squareup.inject:assisted-inject-processor-dagger2:0.4.0"
implementation "eu.davidea:flexible-adapter:5.1.0"
implementation "eu.davidea:flexible-adapter-ui:1.0.0"
implementation "com.aurelhubert:ahbottomnavigation:2.3.4"
implementation 'com.ncapdevi:frag-nav:3.2.0'
implementation "com.ncapdevi:frag-nav:3.2.0"
implementation 'com.github.pwittchen:reactivenetwork-rx2:3.0.2'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation "io.reactivex.rxjava2:rxjava:2.2.8"
implementation "com.github.pwittchen:reactivenetwork-rx2:3.0.3"
implementation "io.reactivex.rxjava2:rxandroid:2.1.1"
implementation "io.reactivex.rxjava2:rxjava:2.2.9"
implementation 'com.google.code.gson:gson:2.8.5'
implementation "com.jakewharton.threetenabp:threetenabp:1.2.0"
implementation "com.google.code.gson:gson:2.8.5"
implementation "com.jakewharton.threetenabp:threetenabp:1.2.1"
implementation "com.jakewharton.timber:timber:4.7.1"
implementation "at.favre.lib:slf4j-timber:1.0.1"
implementation "com.squareup.okhttp3:logging-interceptor:3.12.1"
implementation "com.squareup.okhttp3:logging-interceptor:3.12.3"
implementation "com.mikepenz:aboutlibraries:6.2.3"
implementation 'com.takisoft.preferencex:preferencex:1.0.0'
implementation "com.takisoft.preferencex:preferencex:1.0.0"
implementation 'com.google.firebase:firebase-core:16.0.8'
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.9'
playImplementation "com.google.firebase:firebase-core:16.0.9"
playImplementation "com.crashlytics.sdk.android:crashlytics:2.10.1"
releaseImplementation 'fr.o80.chucker:library-no-op:2.0.4'
releaseImplementation "fr.o80.chucker:library-no-op:2.0.4"
debugImplementation 'fr.o80.chucker:library:2.0.4'
debugImplementation "fr.o80.chucker:library:2.0.4"
debugImplementation "com.amitshekhar.android:debug-db:1.0.6"
testImplementation "junit:junit:4.12"
testImplementation "io.mockk:mockk:1.9.2"
testImplementation "org.mockito:mockito-inline:2.27.0"
testImplementation 'org.threeten:threetenbp:1.3.8'
testImplementation "org.threeten:threetenbp:1.4.0"
testImplementation "org.mockito:mockito-core:2.28.2"
testImplementation("org.mockito:mockito-inline:2.28.2") {
exclude group: "org.mockito", module: "mockito-core"
}
androidTestImplementation 'androidx.test:core:1.1.0'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation "androidx.test:core:1.2.0"
androidTestImplementation "androidx.test:runner:1.2.0"
androidTestImplementation "androidx.test.ext:junit:1.1.1"
androidTestImplementation "io.mockk:mockk-android:1.9.2"
androidTestImplementation 'org.mockito:mockito-android:2.27.0'
androidTestImplementation "androidx.room:room-testing:2.1.0-alpha07"
androidTestImplementation "androidx.room:room-testing:2.1.0"
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
androidTestImplementation "org.mockito:mockito-core:2.28.2"
androidTestImplementation("org.mockito:mockito-android:2.28.2") {
exclude group: 'org.mockito', module: 'mockito-core'
}
}
apply plugin: 'com.google.gms.google-services'

10
app/hooks.gradle Normal file
View File

@ -0,0 +1,10 @@
apply plugin: "com.star-zero.gradle.githook"
githook {
failOnMissingHooksDir = false
hooks {
"pre-push" {
shell = "./app/play-publish-lint.sh"
}
}
}

View File

@ -35,11 +35,14 @@ task jacocoTestReport(type: JacocoReport) {
dir: "$buildDir/intermediates/classes/debug",
excludes: excludes
) + fileTree(
dir: "$buildDir/tmp/kotlin-classes/debug",
dir: "$buildDir/tmp/kotlin-classes/playDebug",
excludes: excludes
))
sourceDirectories.setFrom(files("$project.projectDir/src/main/java"))
sourceDirectories.setFrom(files([
"src/main/java",
"src/play/java"
]))
executionData.setFrom(fileTree(
dir: project.projectDir,
includes: ["**/*.exec", "**/*.ec"]

7
app/play-publish-lint.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash -
content=$(cat < "app/src/main/play/release-notes/pl-PL/default.txt") || exit
if [[ "${#content}" -gt 500 ]]; then
echo >&2 "Release notes content has reached the limit of 500 characters"
exit 1
fi

View File

@ -37,3 +37,6 @@
#Config for API
-keep class io.github.wulkanowy.api.** {*;}
#Config for Material Components
-keep class com.google.android.material.tabs.** {*;}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -29,5 +29,6 @@ sonarqube {
property "sonar.java.coveragePlugin", "jacoco"
property "sonar.android.lint.report", "build/reports/lint-results.xml"
property "sonar.jacoco.reportPaths", fileTree(dir: project.projectDir, includes: ['**/*.exec', '**/*.ec'])
property "sonar.coverage.jacoco.xmlReportPaths", "build/reports/jacocoTestReport/jacocoTestReport.xml"
}
}

View File

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

View File

@ -0,0 +1,17 @@
@file:Suppress("UNUSED_PARAMETER")
package io.github.wulkanowy.utils
import android.content.Context
import timber.log.Timber
fun initCrashlytics(context: Context, appInfo: AppInfo) {
// do nothing
}
class CrashlyticsTree : Timber.Tree() {
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
// do nothing
}
}

View File

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

View File

@ -80,7 +80,6 @@
android:name="android.appwidget.provider"
android:resource="@xml/provider_widget_timetable" />
</receiver>
<receiver
android:name=".ui.modules.luckynumberwidget.LuckyNumberWidgetProvider"
android:label="@string/lucky_number_title">

View File

@ -1,24 +1,23 @@
package io.github.wulkanowy
import android.content.Context
import android.util.Log.INFO
import android.util.Log.VERBOSE
import androidx.multidex.MultiDex
import androidx.work.Configuration
import androidx.work.WorkManager
import com.crashlytics.android.Crashlytics
import com.crashlytics.android.core.CrashlyticsCore
import com.jakewharton.threetenabp.AndroidThreeTen
import dagger.android.AndroidInjector
import dagger.android.support.DaggerApplication
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.utils.Log
import io.fabric.sdk.android.Fabric
import io.github.wulkanowy.BuildConfig.CRASHLYTICS_ENABLED
import io.github.wulkanowy.BuildConfig.DEBUG
import io.github.wulkanowy.di.DaggerAppComponent
import io.github.wulkanowy.services.sync.SyncWorkerFactory
import io.github.wulkanowy.utils.ActivityLifecycleLogger
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.CrashlyticsTree
import io.github.wulkanowy.utils.DebugLogTree
import io.github.wulkanowy.utils.initCrashlytics
import io.reactivex.exceptions.UndeliverableException
import io.reactivex.plugins.RxJavaPlugins
import timber.log.Timber
@ -30,6 +29,9 @@ class WulkanowyApp : DaggerApplication() {
@Inject
lateinit var workerFactory: SyncWorkerFactory
@Inject
lateinit var appInfo: AppInfo
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
MultiDex.install(this)
@ -38,15 +40,23 @@ class WulkanowyApp : DaggerApplication() {
override fun onCreate() {
super.onCreate()
AndroidThreeTen.init(this)
WorkManager.initialize(this, Configuration.Builder().setWorkerFactory(workerFactory).build())
RxJavaPlugins.setErrorHandler(::onError)
initCrashlytics()
initWorkManager()
initLogging()
initCrashlytics(this, appInfo)
}
private fun initWorkManager() {
WorkManager.initialize(this,
Configuration.Builder()
.setWorkerFactory(workerFactory)
.setMinimumLoggingLevel(if (appInfo.isDebug) VERBOSE else INFO)
.build())
}
private fun initLogging() {
if (DEBUG) {
if (appInfo.isDebug) {
Timber.plant(DebugLogTree())
FlexibleAdapter.enableLogs(Log.Level.DEBUG)
} else {
@ -55,15 +65,11 @@ class WulkanowyApp : DaggerApplication() {
registerActivityLifecycleCallbacks(ActivityLifecycleLogger())
}
private fun initCrashlytics() {
Fabric.with(Fabric.Builder(this).kits(
Crashlytics.Builder().core(CrashlyticsCore.Builder().disabled(!CRASHLYTICS_ENABLED).build()).build()
).debuggable(DEBUG).build())
}
private fun onError(error: Throwable) {
if (error is UndeliverableException && error.cause is IOException || error.cause is InterruptedException) {
Timber.e(error.cause, "An undeliverable error occurred")
//RxJava's too deep stack traces may cause SOE on older android devices
val cause = error.cause
if (error is UndeliverableException && cause is IOException || cause is InterruptedException || cause is StackOverflowError) {
Timber.e(cause, "An undeliverable error occurred")
} else throw error
}

View File

@ -132,4 +132,8 @@ internal class RepositoryModule {
@Singleton
@Provides
fun provideRecipientDao(database: AppDatabase) = database.recipientDao
@Singleton
@Provides
fun provideMobileDevicesDao(database: AppDatabase) = database.mobileDeviceDao
}

View File

@ -16,6 +16,7 @@ import io.github.wulkanowy.data.db.dao.GradeSummaryDao
import io.github.wulkanowy.data.db.dao.HomeworkDao
import io.github.wulkanowy.data.db.dao.LuckyNumberDao
import io.github.wulkanowy.data.db.dao.MessagesDao
import io.github.wulkanowy.data.db.dao.MobileDeviceDao
import io.github.wulkanowy.data.db.dao.NoteDao
import io.github.wulkanowy.data.db.dao.RecipientDao
import io.github.wulkanowy.data.db.dao.ReportingUnitDao
@ -33,6 +34,7 @@ import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MobileDevice
import io.github.wulkanowy.data.db.entities.Note
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.ReportingUnit
@ -44,6 +46,8 @@ import io.github.wulkanowy.data.db.migrations.Migration10
import io.github.wulkanowy.data.db.migrations.Migration11
import io.github.wulkanowy.data.db.migrations.Migration12
import io.github.wulkanowy.data.db.migrations.Migration13
import io.github.wulkanowy.data.db.migrations.Migration14
import io.github.wulkanowy.data.db.migrations.Migration15
import io.github.wulkanowy.data.db.migrations.Migration2
import io.github.wulkanowy.data.db.migrations.Migration3
import io.github.wulkanowy.data.db.migrations.Migration4
@ -73,7 +77,8 @@ import javax.inject.Singleton
LuckyNumber::class,
CompletedLesson::class,
ReportingUnit::class,
Recipient::class
Recipient::class,
MobileDevice::class
],
version = AppDatabase.VERSION_SCHEMA,
exportSchema = true
@ -82,7 +87,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
const val VERSION_SCHEMA = 13
const val VERSION_SCHEMA = 15
fun newInstance(context: Context): AppDatabase {
return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database")
@ -101,7 +106,9 @@ abstract class AppDatabase : RoomDatabase() {
Migration10(),
Migration11(),
Migration12(),
Migration13()
Migration13(),
Migration14(),
Migration15()
)
.build()
}
@ -140,4 +147,6 @@ abstract class AppDatabase : RoomDatabase() {
abstract val reportingUnitDao: ReportingUnitDao
abstract val recipientDao: RecipientDao
abstract val mobileDeviceDao: MobileDeviceDao
}

View File

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

View File

@ -13,11 +13,26 @@ data class GradeSummary(
@ColumnInfo(name = "student_id")
val studentId: Int,
val position: Int,
val subject: String,
@ColumnInfo(name = "predicted_grade")
val predictedGrade: String,
val finalGrade: String
@ColumnInfo(name = "final_grade")
val finalGrade: String,
@ColumnInfo(name = "proposed_points")
val proposedPoints: String,
@ColumnInfo(name = "final_points")
val finalPoints: String,
@ColumnInfo(name = "points_sum")
val pointsSum: String,
val average: Double
) {
@PrimaryKey(autoGenerate = true)
var id: Long = 0

View File

@ -0,0 +1,25 @@
package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import org.threeten.bp.LocalDateTime
import java.io.Serializable
@Entity(tableName = "MobileDevices")
data class MobileDevice(
@ColumnInfo(name = "student_id")
val studentId: Int,
@ColumnInfo(name = "device_id")
val deviceId: Int,
val name: String,
val date: LocalDateTime
) : Serializable {
@PrimaryKey(autoGenerate = true)
var id: Long = 0
}

View File

@ -0,0 +1,26 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration14 : Migration(13, 14) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS GradesSummary")
database.execSQL("""
CREATE TABLE IF NOT EXISTS GradesSummary (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
semester_id INTEGER NOT NULL,
student_id INTEGER NOT NULL,
position INTEGER NOT NULL,
subject TEXT NOT NULL,
predicted_grade TEXT NOT NULL,
final_grade TEXT NOT NULL,
proposed_points TEXT NOT NULL,
final_points TEXT NOT NULL,
points_sum TEXT NOT NULL,
average REAL NOT NULL
)
""")
}
}

View File

@ -0,0 +1,19 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration15 : Migration(14, 15) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("""
CREATE TABLE IF NOT EXISTS MobileDevices (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
student_id INTEGER NOT NULL,
device_id INTEGER NOT NULL,
name TEXT NOT NULL,
date INTEGER NOT NULL
)
""")
}
}

View File

@ -0,0 +1,12 @@
package io.github.wulkanowy.data.pojos
data class MobileDeviceToken(
val token: String,
val symbol: String,
val pin: String,
val qr: String
)

View File

@ -18,9 +18,14 @@ class GradeSummaryRemote @Inject constructor(private val api: Api) {
GradeSummary(
semesterId = semester.semesterId,
studentId = semester.studentId,
position = it.order,
subject = it.name,
predictedGrade = it.predicted,
finalGrade = it.final
finalGrade = it.final,
pointsSum = it.pointsSum,
proposedPoints = it.proposedPoints,
finalPoints = it.finalPoints,
average = it.average
)
}
}

View File

@ -0,0 +1,25 @@
package io.github.wulkanowy.data.repositories.mobiledevice
import io.github.wulkanowy.data.db.dao.MobileDeviceDao
import io.github.wulkanowy.data.db.entities.MobileDevice
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.reactivex.Maybe
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class MobileDeviceLocal @Inject constructor(private val mobileDb: MobileDeviceDao) {
fun saveDevices(devices: List<MobileDevice>) {
mobileDb.insertAll(devices)
}
fun deleteDevices(devices: List<MobileDevice>) {
mobileDb.deleteAll(devices)
}
fun getDevices(semester: Semester): Maybe<List<MobileDevice>> {
return mobileDb.loadAll(semester.studentId).filter { it.isNotEmpty() }
}
}

View File

@ -0,0 +1,47 @@
package io.github.wulkanowy.data.repositories.mobiledevice
import io.github.wulkanowy.api.Api
import io.github.wulkanowy.data.db.entities.MobileDevice
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.pojos.MobileDeviceToken
import io.github.wulkanowy.utils.toLocalDateTime
import io.reactivex.Single
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class MobileDeviceRemote @Inject constructor(private val api: Api) {
fun getDevices(semester: Semester): Single<List<MobileDevice>> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { api.getRegisteredDevices() }
.map { devices ->
devices.map {
MobileDevice(
studentId = semester.studentId,
date = it.date.toLocalDateTime(),
deviceId = it.id,
name = it.name
)
}
}
}
fun unregisterDevice(semester: Semester, device: MobileDevice): Single<Boolean> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { api.unregisterDevice(device.deviceId) }
}
fun getToken(semester: Semester): Single<MobileDeviceToken> {
return Single.just(api.apply { diaryId = semester.diaryId })
.flatMap { api.getToken() }
.map {
MobileDeviceToken(
token = it.token,
symbol = it.symbol,
pin = it.pin,
qr = it.qrCodeImage
)
}
}
}

View File

@ -0,0 +1,52 @@
package io.github.wulkanowy.data.repositories.mobiledevice
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.MobileDevice
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.pojos.MobileDeviceToken
import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single
import java.net.UnknownHostException
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class MobileDeviceRepository @Inject constructor(
private val settings: InternetObservingSettings,
private val local: MobileDeviceLocal,
private val remote: MobileDeviceRemote
) {
fun getDevices(semester: Semester, forceRefresh: Boolean = false): Single<List<MobileDevice>> {
return local.getDevices(semester).filter { !forceRefresh }
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
if (it) remote.getDevices(semester)
else Single.error(UnknownHostException())
}.flatMap { new ->
local.getDevices(semester).toSingle(emptyList())
.doOnSuccess { old ->
local.deleteDevices(old uniqueSubtract new)
local.saveDevices(new uniqueSubtract old)
}
}
).flatMap { local.getDevices(semester).toSingle(emptyList()) }
}
fun unregisterDevice(semester: Semester, device: MobileDevice): Single<Boolean> {
return ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
if (it) remote.unregisterDevice(semester, device)
else Single.error(UnknownHostException())
}
}
fun getToken(semester: Semester): Single<MobileDeviceToken> {
return ReactiveNetwork.checkInternetConnectivity(settings)
.flatMap {
if (it) remote.getToken(semester)
else Single.error(UnknownHostException())
}
}
}

View File

@ -20,6 +20,9 @@ class PreferencesRepository @Inject constructor(
val gradeAverageMode: String
get() = sharedPref.getString(context.getString(R.string.pref_key_grade_average_mode), "only_one_semester") ?: "only_one_semester"
val gradeAverageForceCalc: Boolean
get() = sharedPref.getBoolean(context.getString(R.string.pref_key_grade_average_force_calc), false)
val isGradeExpandable: Boolean
get() = !sharedPref.getBoolean(context.getString(R.string.pref_key_expand_grade), false)
@ -40,7 +43,7 @@ class PreferencesRepository @Inject constructor(
val servicesOnlyWifiKey: String = context.getString(R.string.pref_key_services_wifi_only)
val isServicesOnlyWifi: Boolean
get() = sharedPref.getBoolean(servicesOnlyWifiKey, true)
get() = sharedPref.getBoolean(servicesOnlyWifiKey, false)
val isNotificationsEnable: Boolean
get() = sharedPref.getBoolean(context.getString(R.string.pref_key_notifications_enable), true)

View File

@ -2,15 +2,13 @@ package io.github.wulkanowy.di
import android.appwidget.AppWidgetManager
import android.content.Context
import com.google.firebase.analytics.FirebaseAnalytics
import dagger.Module
import dagger.Provides
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.BuildConfig.DEBUG
import io.github.wulkanowy.WulkanowyApp
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.SchedulersProvider
import javax.inject.Named
import javax.inject.Singleton
@Module
@ -27,16 +25,11 @@ internal class AppModule {
@Provides
fun provideFlexibleAdapter() = FlexibleAdapter<AbstractFlexibleItem<*>>(null, null, true)
@Singleton
@Provides
fun provideFirebaseAnalytics(context: Context) = FirebaseAnalytics.getInstance(context)
@Singleton
@Provides
fun provideAppWidgetManager(context: Context): AppWidgetManager = AppWidgetManager.getInstance(context)
@Singleton
@Named("isDebug")
@Provides
fun provideIsDebug() = DEBUG
fun provideAppInfo() = AppInfo()
}

View File

@ -6,34 +6,50 @@ import androidx.work.BackoffPolicy.EXPONENTIAL
import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy.KEEP
import androidx.work.ExistingPeriodicWorkPolicy.REPLACE
import androidx.work.NetworkType.METERED
import androidx.work.NetworkType.CONNECTED
import androidx.work.NetworkType.UNMETERED
import androidx.work.PeriodicWorkRequest
import androidx.work.WorkManager
import io.github.wulkanowy.data.db.SharedPrefHelper
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.services.sync.channels.DebugChannel
import io.github.wulkanowy.services.sync.channels.NewEntriesChannel
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.isHolidays
import org.threeten.bp.LocalDate.now
import timber.log.Timber
import java.util.concurrent.TimeUnit.MINUTES
import javax.inject.Inject
import javax.inject.Named
import javax.inject.Singleton
@Singleton
class SyncManager @Inject constructor(
private val workManager: WorkManager,
private val preferencesRepository: PreferencesRepository,
sharedPrefHelper: SharedPrefHelper,
newEntriesChannel: NewEntriesChannel,
debugChannel: DebugChannel,
@Named("isDebug") isDebug: Boolean
appInfo: AppInfo
) {
companion object {
private const val APP_VERSION_CODE_KEY = "app_version_code"
}
init {
if (SDK_INT >= O) newEntriesChannel.create()
if (SDK_INT >= O && isDebug) debugChannel.create()
if (now().isHolidays) stopSyncWorker()
if (SDK_INT > O) {
newEntriesChannel.create()
if (appInfo.isDebug) debugChannel.create()
}
if (sharedPrefHelper.getLong(APP_VERSION_CODE_KEY, -1L) != appInfo.versionCode.toLong()) {
startSyncWorker(true)
sharedPrefHelper.putLong(APP_VERSION_CODE_KEY, appInfo.versionCode.toLong(), true)
}
Timber.i("SyncManager was initialized")
}
@ -43,7 +59,7 @@ class SyncManager @Inject constructor(
PeriodicWorkRequest.Builder(SyncWorker::class.java, preferencesRepository.servicesInterval, MINUTES, 10, MINUTES)
.setBackoffCriteria(EXPONENTIAL, 30, MINUTES)
.setConstraints(Constraints.Builder()
.setRequiredNetworkType(if (preferencesRepository.isServicesOnlyWifi) METERED else UNMETERED)
.setRequiredNetworkType(if (preferencesRepository.isServicesOnlyWifi) UNMETERED else CONNECTED)
.build())
.build())
}

View File

@ -1,8 +1,11 @@
package io.github.wulkanowy.ui.base
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.fragment.app.Fragment
@ -12,10 +15,12 @@ import dagger.android.AndroidInjection
import dagger.android.DispatchingAndroidInjector
import dagger.android.support.HasSupportFragmentInjector
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.utils.FragmentLifecycleLogger
import javax.inject.Inject
abstract class BaseActivity : AppCompatActivity(), BaseView, HasSupportFragmentInjector {
abstract class BaseActivity<T : BasePresenter<out BaseView>> : AppCompatActivity(), BaseView,
HasSupportFragmentInjector {
@Inject
lateinit var supportFragmentInjector: DispatchingAndroidInjector<Fragment>
@ -28,6 +33,8 @@ abstract class BaseActivity : AppCompatActivity(), BaseView, HasSupportFragmentI
protected var messageContainer: View? = null
abstract var presenter: T
public override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
themeManager.applyTheme(this)
@ -51,9 +58,24 @@ abstract class BaseActivity : AppCompatActivity(), BaseView, HasSupportFragmentI
else Toast.makeText(this, text, Toast.LENGTH_LONG).show()
}
override fun showExpiredDialog() {
AlertDialog.Builder(this)
.setTitle(R.string.main_session_expired)
.setMessage(R.string.main_session_relogin)
.setPositiveButton(R.string.main_log_in) { _, _ -> presenter.onExpiredLoginSelected() }
.setNegativeButton(android.R.string.cancel) { _, _ -> }
.show()
}
override fun openClearLoginView() {
startActivity(LoginActivity.getStartIntent(this)
.apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) })
}
override fun onDestroy() {
super.onDestroy()
invalidateOptionsMenu()
presenter.onDetachView()
}
override fun supportFragmentInjector() = supportFragmentInjector

View File

@ -18,7 +18,7 @@ abstract class BaseFragment : DaggerFragment(), BaseView {
}
.show()
} else {
(activity as? BaseActivity)?.showError(text, error)
(activity as? BaseActivity<*>)?.showError(text, error)
}
}
@ -26,7 +26,15 @@ abstract class BaseFragment : DaggerFragment(), BaseView {
if (messageContainer != null) {
Snackbar.make(messageContainer!!, text, LENGTH_LONG).show()
} else {
(activity as? BaseActivity)?.showMessage(text)
(activity as? BaseActivity<*>)?.showMessage(text)
}
}
override fun showExpiredDialog() {
(activity as? BaseActivity<*>)?.showExpiredDialog()
}
override fun openClearLoginView() {
(activity as? BaseActivity<*>)?.openClearLoginView()
}
}

View File

@ -1,8 +1,16 @@
package io.github.wulkanowy.ui.base
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.utils.SchedulersProvider
import io.reactivex.Completable
import io.reactivex.disposables.CompositeDisposable
import timber.log.Timber
open class BasePresenter<T : BaseView>(private val errorHandler: ErrorHandler) {
open class BasePresenter<T : BaseView>(
protected val errorHandler: ErrorHandler,
protected val studentRepository: StudentRepository,
protected val schedulers: SchedulersProvider
) {
val disposable = CompositeDisposable()
@ -10,7 +18,33 @@ open class BasePresenter<T : BaseView>(private val errorHandler: ErrorHandler) {
open fun onAttachView(view: T) {
this.view = view
errorHandler.showErrorMessage = { text, error -> view.showError(text, error) }
errorHandler.apply {
showErrorMessage = view::showError
onSessionExpired = view::showExpiredDialog
onNoCurrentStudent = view::openClearLoginView
}
}
fun onExpiredLoginSelected() {
Timber.i("Attempt to switch the student after the session expires")
disposable.add(studentRepository.getCurrentStudent(false)
.flatMapCompletable { studentRepository.logoutStudent(it) }
.andThen(studentRepository.getSavedStudents(false))
.flatMapCompletable {
if (it.isNotEmpty()) {
Timber.i("Switching current student")
studentRepository.switchStudent(it[0])
} else Completable.complete()
}
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
Timber.i("Switch student result: Open login view")
view?.openClearLoginView()
}, {
Timber.i("Switch student result: An exception occurred")
errorHandler.dispatch(it)
}))
}
open fun onDetachView() {

View File

@ -5,4 +5,8 @@ interface BaseView {
fun showError(text: String, error: Throwable)
fun showMessage(text: String)
fun showExpiredDialog()
fun openClearLoginView()
}

View File

@ -5,7 +5,10 @@ import com.readystatesoftware.chuck.api.ChuckCollector
import io.github.wulkanowy.R
import io.github.wulkanowy.api.interceptor.FeatureDisabledException
import io.github.wulkanowy.api.interceptor.ServiceUnavailableException
import io.github.wulkanowy.api.login.BadCredentialsException
import io.github.wulkanowy.api.login.NotLoggedInException
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
import io.github.wulkanowy.utils.security.ScramblerException
import timber.log.Timber
import java.net.SocketTimeoutException
import java.net.UnknownHostException
@ -15,6 +18,10 @@ open class ErrorHandler @Inject constructor(protected val resources: Resources,
var showErrorMessage: (String, Throwable) -> Unit = { _, _ -> }
var onSessionExpired: () -> Unit = {}
var onNoCurrentStudent: () -> Unit = {}
fun dispatch(error: Throwable) {
chuckCollector.onError(error.javaClass.simpleName, error)
Timber.e(error, "An exception occurred while the Wulkanowy was running")
@ -22,17 +29,23 @@ open class ErrorHandler @Inject constructor(protected val resources: Resources,
}
protected open fun proceed(error: Throwable) {
showErrorMessage((when (error) {
is UnknownHostException -> resources.getString(R.string.error_no_internet)
is SocketTimeoutException -> resources.getString(R.string.error_timeout)
is NotLoggedInException -> resources.getString(R.string.error_login_failed)
is ServiceUnavailableException -> resources.getString(R.string.error_service_unavailable)
is FeatureDisabledException -> resources.getString(R.string.error_feature_disabled)
else -> resources.getString(R.string.error_unknown)
}), error)
resources.run {
when (error) {
is UnknownHostException -> showErrorMessage(getString(R.string.error_no_internet), error)
is SocketTimeoutException -> showErrorMessage(getString(R.string.error_timeout), 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()
else -> showErrorMessage(getString(R.string.error_unknown), error)
}
}
}
open fun clear() {
showErrorMessage = { _, _ -> }
onSessionExpired = {}
onNoCurrentStudent = {}
}
}

View File

@ -1,21 +0,0 @@
package io.github.wulkanowy.ui.base.session
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.ui.modules.main.MainActivity
open class BaseSessionFragment : BaseFragment(), BaseSessionView {
override fun showExpiredDialog() {
(activity as? MainActivity)?.showExpiredDialog()
}
override fun openLoginView() {
activity?.also {
startActivity(LoginActivity.getStartIntent(it)
.apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) })
}
}
}

View File

@ -1,15 +0,0 @@
package io.github.wulkanowy.ui.base.session
import io.github.wulkanowy.ui.base.BasePresenter
open class BaseSessionPresenter<T : BaseSessionView>(private val errorHandler: SessionErrorHandler) :
BasePresenter<T>(errorHandler) {
override fun onAttachView(view: T) {
super.onAttachView(view)
errorHandler.apply {
onDecryptionFail = { view.showExpiredDialog() }
onNoCurrentStudent = { view.openLoginView() }
}
}
}

View File

@ -1,10 +0,0 @@
package io.github.wulkanowy.ui.base.session
import io.github.wulkanowy.ui.base.BaseView
interface BaseSessionView : BaseView {
fun showExpiredDialog()
fun openLoginView()
}

View File

@ -1,32 +0,0 @@
package io.github.wulkanowy.ui.base.session
import android.content.res.Resources
import com.readystatesoftware.chuck.api.ChuckCollector
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.security.ScramblerException
import javax.inject.Inject
open class SessionErrorHandler @Inject constructor(
resources: Resources,
chuckCollector: ChuckCollector
) : ErrorHandler(resources, chuckCollector) {
var onDecryptionFail: () -> Unit = {}
var onNoCurrentStudent: () -> Unit = {}
override fun proceed(error: Throwable) {
when (error) {
is ScramblerException -> onDecryptionFail()
is NoCurrentStudentException -> onNoCurrentStudent()
else -> super.proceed(error)
}
}
override fun clear() {
super.clear()
onDecryptionFail = {}
onNoCurrentStudent = {}
}
}

View File

@ -8,10 +8,10 @@ import android.view.View
import android.view.ViewGroup
import com.mikepenz.aboutlibraries.LibsBuilder
import com.mikepenz.aboutlibraries.LibsFragmentCompat
import io.github.wulkanowy.BuildConfig
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.openInternetBrowser
import io.github.wulkanowy.utils.withOnExtraListener
import javax.inject.Inject
@ -24,6 +24,9 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
@Inject
lateinit var fragmentCompat: LibsFragmentCompat
@Inject
lateinit var appInfo: AppInfo
companion object {
fun newInstance() = AboutFragment()
}
@ -71,9 +74,9 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView {
putExtra(Intent.EXTRA_EMAIL, Array(1) { "wulkanowyinc@gmail.com" })
putExtra(Intent.EXTRA_SUBJECT, "Zgłoszenie błędu")
putExtra(Intent.EXTRA_TEXT, "Tu umieść treść zgłoszenia\n\n" + "-".repeat(40) + "\n" + """
Build: ${BuildConfig.VERSION_CODE}
SDK: ${android.os.Build.VERSION.SDK_INT}
Device: ${android.os.Build.MANUFACTURER} ${android.os.Build.MODEL}
Build: ${appInfo.versionCode}
SDK: ${appInfo.systemVersion}
Device: ${appInfo.systemManufacturer} ${appInfo.systemModel}
""".trimIndent())
}

View File

@ -4,16 +4,20 @@ import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.Libs.SpecialButton.SPECIAL1
import com.mikepenz.aboutlibraries.Libs.SpecialButton.SPECIAL2
import com.mikepenz.aboutlibraries.Libs.SpecialButton.SPECIAL3
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import timber.log.Timber
import javax.inject.Inject
class AboutPresenter @Inject constructor(
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<AboutView>(errorHandler) {
) : BasePresenter<AboutView>(errorHandler, studentRepository, schedulers) {
override fun onAttachView(view: AboutView) {
super.onAttachView(view)

View File

@ -1,7 +1,5 @@
package io.github.wulkanowy.ui.modules.account
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@ -14,6 +12,7 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.dialog_account.*
@ -73,16 +72,17 @@ class AccountDialog : DaggerAppCompatDialogFragment(), AccountView {
}
override fun openLoginView() {
activity?.also {
activity?.let {
startActivity(LoginActivity.getStartIntent(it))
}
}
override fun showExpiredDialog() {
(activity as? BaseActivity<*>)?.showExpiredDialog()
}
override fun openClearLoginView() {
activity?.also {
startActivity(LoginActivity.getStartIntent(it)
.apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) })
}
(activity as? BaseActivity<*>)?.openClearLoginView()
}
override fun showConfirmDialog() {

View File

@ -11,11 +11,11 @@ import timber.log.Timber
import javax.inject.Inject
class AccountPresenter @Inject constructor(
private val errorHandler: ErrorHandler,
private val studentRepository: StudentRepository,
private val syncManager: SyncManager,
private val schedulers: SchedulersProvider
) : BasePresenter<AccountView>(errorHandler) {
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val syncManager: SyncManager
) : BasePresenter<AccountView>(errorHandler, studentRepository, schedulers) {
override fun onAttachView(view: AccountView) {
super.onAttachView(view)

View File

@ -14,8 +14,6 @@ interface AccountView : BaseView {
fun openLoginView()
fun openClearLoginView()
fun recreateMainView()
}

View File

@ -13,7 +13,7 @@ import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.ui.base.session.BaseSessionFragment
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
@ -21,7 +21,7 @@ import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.fragment_attendance.*
import javax.inject.Inject
class AttendanceFragment : BaseSessionFragment(), AttendanceView, MainView.MainChildView, MainView.TitledView {
class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildView, MainView.TitledView {
@Inject
lateinit var presenter: AttendancePresenter

View File

@ -1,20 +1,14 @@
package io.github.wulkanowy.ui.modules.attendance
import com.google.firebase.analytics.FirebaseAnalytics.Param.START_DATE
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.isHolidays
import io.github.wulkanowy.utils.nextSchoolDay
import io.github.wulkanowy.utils.previousOrSameSchoolDay
import io.github.wulkanowy.utils.previousSchoolDay
import io.github.wulkanowy.utils.toFormattedString
import io.github.wulkanowy.utils.*
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalDate.now
import org.threeten.bp.LocalDate.ofEpochDay
@ -23,14 +17,14 @@ import java.util.concurrent.TimeUnit.MILLISECONDS
import javax.inject.Inject
class AttendancePresenter @Inject constructor(
private val errorHandler: SessionErrorHandler,
private val schedulers: SchedulersProvider,
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val attendanceRepository: AttendanceRepository,
private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val prefRepository: PreferencesRepository,
private val analytics: FirebaseAnalyticsHelper
) : BaseSessionPresenter<AttendanceView>(errorHandler) {
) : BasePresenter<AttendanceView>(errorHandler, studentRepository, schedulers) {
lateinit var currentDate: LocalDate
private set
@ -115,7 +109,7 @@ class AttendancePresenter @Inject constructor(
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
}
analytics.logEvent("load_attendance", "items" to it.size, "force_refresh" to forceRefresh, START_DATE to currentDate.toFormattedString("yyyy-MM-dd"))
analytics.logEvent("load_attendance", "items" to it.size, "force_refresh" to forceRefresh)
}) {
Timber.i("Loading attendance result: An exception occurred")
view?.run { showEmpty(isViewEmpty) }

View File

@ -1,9 +1,9 @@
package io.github.wulkanowy.ui.modules.attendance
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.ui.base.session.BaseSessionView
import io.github.wulkanowy.ui.base.BaseView
interface AttendanceView : BaseSessionView {
interface AttendanceView : BaseView {
val isViewEmpty: Boolean

View File

@ -13,13 +13,13 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.session.BaseSessionFragment
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.setOnItemSelectedListener
import kotlinx.android.synthetic.main.fragment_attendance_summary.*
import javax.inject.Inject
class AttendanceSummaryFragment : BaseSessionFragment(), AttendanceSummaryView, MainView.TitledView {
class AttendanceSummaryFragment : BaseFragment(), AttendanceSummaryView, MainView.TitledView {
@Inject
lateinit var presenter: AttendanceSummaryPresenter

View File

@ -6,8 +6,8 @@ import io.github.wulkanowy.data.repositories.attendancesummary.AttendanceSummary
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.data.repositories.subject.SubjectRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.calculatePercentage
@ -19,14 +19,14 @@ import java.util.concurrent.TimeUnit.MILLISECONDS
import javax.inject.Inject
class AttendanceSummaryPresenter @Inject constructor(
private val errorHandler: SessionErrorHandler,
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val attendanceSummaryRepository: AttendanceSummaryRepository,
private val subjectRepository: SubjectRepository,
private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val schedulers: SchedulersProvider,
private val analytics: FirebaseAnalyticsHelper
) : BaseSessionPresenter<AttendanceSummaryView>(errorHandler) {
) : BasePresenter<AttendanceSummaryView>(errorHandler, studentRepository, schedulers) {
private var subjects = emptyList<Subject>()

View File

@ -1,8 +1,8 @@
package io.github.wulkanowy.ui.modules.attendance.summary
import io.github.wulkanowy.ui.base.session.BaseSessionView
import io.github.wulkanowy.ui.base.BaseView
interface AttendanceSummaryView : BaseSessionView {
interface AttendanceSummaryView : BaseView {
val isViewEmpty: Boolean

View File

@ -13,14 +13,14 @@ import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.ui.base.session.BaseSessionFragment
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.fragment_exam.*
import javax.inject.Inject
class ExamFragment : BaseSessionFragment(), ExamView, MainView.MainChildView, MainView.TitledView {
class ExamFragment : BaseFragment(), ExamView, MainView.MainChildView, MainView.TitledView {
@Inject
lateinit var presenter: ExamPresenter

View File

@ -1,13 +1,12 @@
package io.github.wulkanowy.ui.modules.exam
import com.google.firebase.analytics.FirebaseAnalytics.Param.START_DATE
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.repositories.exam.ExamRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.friday
@ -23,13 +22,13 @@ import java.util.concurrent.TimeUnit.MILLISECONDS
import javax.inject.Inject
class ExamPresenter @Inject constructor(
private val errorHandler: SessionErrorHandler,
private val schedulers: SchedulersProvider,
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val examRepository: ExamRepository,
private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val analytics: FirebaseAnalyticsHelper
) : BaseSessionPresenter<ExamView>(errorHandler) {
) : BasePresenter<ExamView>(errorHandler, studentRepository, schedulers) {
lateinit var currentDate: LocalDate
private set
@ -102,7 +101,7 @@ class ExamPresenter @Inject constructor(
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
}
analytics.logEvent("load_exam", "items" to it.size, "force_refresh" to forceRefresh, START_DATE to currentDate.toFormattedString("yyyy-MM-dd"))
analytics.logEvent("load_exam", "items" to it.size, "force_refresh" to forceRefresh)
}) {
Timber.i("Loading exam result: An exception occurred")
view?.run { showEmpty(isViewEmpty) }

View File

@ -1,9 +1,9 @@
package io.github.wulkanowy.ui.modules.exam
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.ui.base.session.BaseSessionView
import io.github.wulkanowy.ui.base.BaseView
interface ExamView : BaseSessionView {
interface ExamView : BaseView {
val isViewEmpty: Boolean

View File

@ -3,16 +3,20 @@ package io.github.wulkanowy.ui.modules.grade
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.grade.GradeRepository
import io.github.wulkanowy.data.repositories.gradessummary.GradeSummaryRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.utils.calcAverage
import io.github.wulkanowy.utils.changeModifier
import io.reactivex.Maybe
import io.reactivex.Single
import javax.inject.Inject
class GradeAverageProvider @Inject constructor(
private val preferencesRepository: PreferencesRepository,
private val gradeRepository: GradeRepository
private val gradeRepository: GradeRepository,
private val gradeSummaryRepository: GradeSummaryRepository
) {
fun getGradeAverage(student: Student, semesters: List<Semester>, selectedSemesterId: Int, forceRefresh: Boolean): Single<Map<String, Double>> {
return when (preferencesRepository.gradeAverageMode) {
"all_year" -> getAllYearAverage(student, semesters, selectedSemesterId, forceRefresh)
@ -27,16 +31,19 @@ class GradeAverageProvider @Inject constructor(
val plusModifier = preferencesRepository.gradePlusModifier
val minusModifier = preferencesRepository.gradeMinusModifier
return gradeRepository.getGrades(student, selectedSemester, forceRefresh)
.flatMap { firstGrades ->
if (selectedSemester == firstSemester) Single.just(firstGrades)
else gradeRepository.getGrades(student, firstSemester)
.map { secondGrades -> secondGrades + firstGrades }
}.map { grades ->
grades.map { it.changeModifier(plusModifier, minusModifier) }
.groupBy { it.subject }
.mapValues { it.value.calcAverage() }
}
return getAverageFromGradeSummary(selectedSemester, forceRefresh)
.switchIfEmpty(gradeRepository.getGrades(student, selectedSemester, forceRefresh)
.flatMap { firstGrades ->
if (selectedSemester == firstSemester) Single.just(firstGrades)
else {
gradeRepository.getGrades(student, firstSemester)
.map { secondGrades -> secondGrades + firstGrades }
}
}.map { grades ->
grades.map { it.changeModifier(plusModifier, minusModifier) }
.groupBy { it.subject }
.mapValues { it.value.calcAverage() }
})
}
private fun getOnlyOneSemesterAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): Single<Map<String, Double>> {
@ -44,11 +51,22 @@ class GradeAverageProvider @Inject constructor(
val plusModifier = preferencesRepository.gradePlusModifier
val minusModifier = preferencesRepository.gradeMinusModifier
return gradeRepository.getGrades(student, selectedSemester, forceRefresh)
.map { grades ->
grades.map { it.changeModifier(plusModifier, minusModifier) }
.groupBy { it.subject }
.mapValues { it.value.calcAverage() }
}
return getAverageFromGradeSummary(selectedSemester, forceRefresh)
.switchIfEmpty(gradeRepository.getGrades(student, selectedSemester, forceRefresh)
.map { grades ->
grades.map { it.changeModifier(plusModifier, minusModifier) }
.groupBy { it.subject }
.mapValues { it.value.calcAverage() }
})
}
private fun getAverageFromGradeSummary(selectedSemester: Semester, forceRefresh: Boolean): Maybe<Map<String, Double>> {
return gradeSummaryRepository.getGradesSummary(selectedSemester, forceRefresh)
.toMaybe()
.flatMap {
if (it.any { summary -> summary.average != .0 }) {
Maybe.just(it.map { summary -> summary.subject to summary.average }.toMap())
} else Maybe.empty()
}.filter { !preferencesRepository.gradeAverageForceCalc }
}
}

View File

@ -11,8 +11,8 @@ import android.view.View.VISIBLE
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter
import io.github.wulkanowy.ui.base.session.BaseSessionFragment
import io.github.wulkanowy.ui.modules.grade.details.GradeDetailsFragment
import io.github.wulkanowy.ui.modules.grade.statistics.GradeStatisticsFragment
import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment
@ -21,7 +21,7 @@ import io.github.wulkanowy.utils.setOnSelectPageListener
import kotlinx.android.synthetic.main.fragment_grade.*
import javax.inject.Inject
class GradeFragment : BaseSessionFragment(), GradeView, MainView.MainChildView, MainView.TitledView {
class GradeFragment : BaseFragment(), GradeView, MainView.MainChildView, MainView.TitledView {
@Inject
lateinit var presenter: GradePresenter

View File

@ -3,20 +3,20 @@ package io.github.wulkanowy.ui.modules.grade
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import timber.log.Timber
import javax.inject.Inject
class GradePresenter @Inject constructor(
private val errorHandler: SessionErrorHandler,
private val schedulers: SchedulersProvider,
private val studentRepository: StudentRepository,
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val analytics: FirebaseAnalyticsHelper
) : BaseSessionPresenter<GradeView>(errorHandler) {
) : BasePresenter<GradeView>(errorHandler, studentRepository, schedulers) {
var selectedIndex = 0
private set

View File

@ -1,8 +1,8 @@
package io.github.wulkanowy.ui.modules.grade
import io.github.wulkanowy.ui.base.session.BaseSessionView
import io.github.wulkanowy.ui.base.BaseView
interface GradeView : BaseSessionView {
interface GradeView : BaseView {
val currentPageIndex: Int

View File

@ -17,7 +17,7 @@ import eu.davidea.flexibleadapter.items.IExpandable
import eu.davidea.flexibleadapter.items.IFlexible
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.ui.base.session.BaseSessionFragment
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.grade.GradeFragment
import io.github.wulkanowy.ui.modules.grade.GradeView
import io.github.wulkanowy.ui.modules.main.MainActivity
@ -25,7 +25,7 @@ import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.fragment_grade_details.*
import javax.inject.Inject
class GradeDetailsFragment : BaseSessionFragment(), GradeDetailsView, GradeView.GradeChildView {
class GradeDetailsFragment : BaseFragment(), GradeDetailsView, GradeView.GradeChildView {
@Inject
lateinit var presenter: GradeDetailsPresenter

View File

@ -6,8 +6,8 @@ import io.github.wulkanowy.data.repositories.grade.GradeRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
@ -16,15 +16,15 @@ import timber.log.Timber
import javax.inject.Inject
class GradeDetailsPresenter @Inject constructor(
private val errorHandler: SessionErrorHandler,
private val schedulers: SchedulersProvider,
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val gradeRepository: GradeRepository,
private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val preferencesRepository: PreferencesRepository,
private val averageProvider: GradeAverageProvider,
private val analytics: FirebaseAnalyticsHelper
) : BaseSessionPresenter<GradeDetailsView>(errorHandler) {
) : BasePresenter<GradeDetailsView>(errorHandler, studentRepository, schedulers) {
private var currentSemesterId = 0
@ -113,7 +113,7 @@ class GradeDetailsPresenter @Inject constructor(
.flatMap { (student, semesters) ->
averageProvider.getGradeAverage(student, semesters, semesterId, forceRefresh)
.flatMap { averages ->
gradeRepository.getGrades(student, semesters.first { semester -> semester.semesterId == semesterId })
gradeRepository.getGrades(student, semesters.first { it.semesterId == semesterId }, forceRefresh)
.map { it.sortedByDescending { grade -> grade.date } }
.map { it.groupBy { grade -> grade.subject }.toSortedMap() }
.map { createGradeItems(it, averages) }

View File

@ -4,9 +4,9 @@ import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IExpandable
import eu.davidea.flexibleadapter.items.IFlexible
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.ui.base.session.BaseSessionView
import io.github.wulkanowy.ui.base.BaseView
interface GradeDetailsView : BaseSessionView {
interface GradeDetailsView : BaseView {
val isViewEmpty: Boolean

View File

@ -16,7 +16,7 @@ import com.github.mikephil.charting.data.PieEntry
import com.github.mikephil.charting.formatter.ValueFormatter
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.ui.base.session.BaseSessionFragment
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.grade.GradeFragment
import io.github.wulkanowy.ui.modules.grade.GradeView
import io.github.wulkanowy.utils.getThemeAttrColor
@ -24,7 +24,7 @@ import io.github.wulkanowy.utils.setOnItemSelectedListener
import kotlinx.android.synthetic.main.fragment_grade_statistics.*
import javax.inject.Inject
class GradeStatisticsFragment : BaseSessionFragment(), GradeStatisticsView, GradeView.GradeChildView {
class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.GradeChildView {
@Inject
lateinit var presenter: GradeStatisticsPresenter

View File

@ -6,23 +6,23 @@ import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.data.repositories.subject.SubjectRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import timber.log.Timber
import javax.inject.Inject
class GradeStatisticsPresenter @Inject constructor(
private val errorHandler: SessionErrorHandler,
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val gradeStatisticsRepository: GradeStatisticsRepository,
private val subjectRepository: SubjectRepository,
private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val preferencesRepository: PreferencesRepository,
private val schedulers: SchedulersProvider,
private val analytics: FirebaseAnalyticsHelper
) : BaseSessionPresenter<GradeStatisticsView>(errorHandler) {
) : BasePresenter<GradeStatisticsView>(errorHandler, studentRepository, schedulers) {
private var subjects = emptyList<Subject>()

View File

@ -1,9 +1,9 @@
package io.github.wulkanowy.ui.modules.grade.statistics
import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.ui.base.session.BaseSessionView
import io.github.wulkanowy.ui.base.BaseView
interface GradeStatisticsView : BaseSessionView {
interface GradeStatisticsView : BaseView {
val isViewEmpty: Boolean

View File

@ -11,13 +11,13 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.session.BaseSessionFragment
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.grade.GradeFragment
import io.github.wulkanowy.ui.modules.grade.GradeView
import kotlinx.android.synthetic.main.fragment_grade_summary.*
import javax.inject.Inject
class GradeSummaryFragment : BaseSessionFragment(), GradeSummaryView, GradeView.GradeChildView {
class GradeSummaryFragment : BaseFragment(), GradeSummaryView, GradeView.GradeChildView {
@Inject
lateinit var presenter: GradeSummaryPresenter

View File

@ -1,19 +1,21 @@
package io.github.wulkanowy.ui.modules.grade.summary
import android.annotation.SuppressLint
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
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.db.entities.GradeSummary
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_grade_summary.*
class GradeSummaryItem(
private val title: String,
private val average: String,
private val predictedGrade: String,
private val finalGrade: String
val summary: GradeSummary,
private val average: String
) : AbstractFlexibleItem<GradeSummaryItem.ViewHolder>() {
override fun getLayoutRes() = R.layout.item_grade_summary
@ -22,12 +24,16 @@ class GradeSummaryItem(
return ViewHolder(view, adapter)
}
@SuppressLint("SetTextI18n")
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.run {
gradeSummaryItemTitle.text = title
gradeSummaryItemTitle.text = summary.subject
gradeSummaryItemPoints.text = summary.pointsSum
gradeSummaryItemAverage.text = average
gradeSummaryItemPredicted.text = predictedGrade
gradeSummaryItemFinal.text = finalGrade
gradeSummaryItemPredicted.text = "${summary.predictedGrade} ${summary.proposedPoints}".trim()
gradeSummaryItemFinal.text = "${summary.finalGrade} ${summary.finalPoints}".trim()
gradeSummaryItemPointsContainer.visibility = if (summary.pointsSum.isBlank()) GONE else VISIBLE
}
}
@ -38,18 +44,16 @@ class GradeSummaryItem(
other as GradeSummaryItem
if (average != other.average) return false
if (title != other.title) return false
if (predictedGrade != other.predictedGrade) return false
if (finalGrade != other.finalGrade) return false
if (summary != other.summary) return false
if (summary.id != other.summary.id) return false
return true
}
override fun hashCode(): Int {
var result = title.hashCode()
var result = summary.hashCode()
result = 31 * result + summary.id.hashCode()
result = 31 * result + average.hashCode()
result = 31 * result + predictedGrade.hashCode()
result = 31 * result + finalGrade.hashCode()
return result
}

View File

@ -4,8 +4,8 @@ import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.repositories.gradessummary.GradeSummaryRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
@ -16,14 +16,14 @@ import java.util.Locale.FRANCE
import javax.inject.Inject
class GradeSummaryPresenter @Inject constructor(
private val errorHandler: SessionErrorHandler,
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val gradeSummaryRepository: GradeSummaryRepository,
private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val averageProvider: GradeAverageProvider,
private val schedulers: SchedulersProvider,
private val analytics: FirebaseAnalyticsHelper
) : BaseSessionPresenter<GradeSummaryView>(errorHandler) {
) : BasePresenter<GradeSummaryView>(errorHandler, studentRepository, schedulers) {
override fun onAttachView(view: GradeSummaryView) {
super.onAttachView(view)
@ -96,10 +96,8 @@ class GradeSummaryPresenter @Inject constructor(
gradesSummary.filter { !checkEmpty(it, filteredAverages) }
.map {
GradeSummaryItem(
title = it.subject,
average = formatAverage(filteredAverages.getOrElse(it.subject) { 0.0 }, ""),
predictedGrade = it.predictedGrade,
finalGrade = it.finalGrade
summary = it,
average = formatAverage(filteredAverages.getOrElse(it.subject) { 0.0 }, "")
)
}.let {
it to GradeSummaryScrollableHeader(

View File

@ -1,8 +1,8 @@
package io.github.wulkanowy.ui.modules.grade.summary
import io.github.wulkanowy.ui.base.session.BaseSessionView
import io.github.wulkanowy.ui.base.BaseView
interface GradeSummaryView : BaseSessionView {
interface GradeSummaryView : BaseView {
val isViewEmpty: Boolean

View File

@ -10,14 +10,14 @@ import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.ui.base.session.BaseSessionFragment
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.fragment_homework.*
import javax.inject.Inject
class HomeworkFragment : BaseSessionFragment(), HomeworkView, MainView.TitledView {
class HomeworkFragment : BaseFragment(), HomeworkView, MainView.TitledView {
@Inject
lateinit var presenter: HomeworkPresenter

View File

@ -1,13 +1,12 @@
package io.github.wulkanowy.ui.modules.homework
import com.google.firebase.analytics.FirebaseAnalytics.Param.START_DATE
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.repositories.homework.HomeworkRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.friday
@ -21,13 +20,13 @@ import java.util.concurrent.TimeUnit
import javax.inject.Inject
class HomeworkPresenter @Inject constructor(
private val errorHandler: SessionErrorHandler,
private val schedulers: SchedulersProvider,
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val homeworkRepository: HomeworkRepository,
private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val analytics: FirebaseAnalyticsHelper
) : BaseSessionPresenter<HomeworkView>(errorHandler) {
) : BasePresenter<HomeworkView>(errorHandler, studentRepository, schedulers) {
lateinit var currentDate: LocalDate
private set
@ -89,7 +88,7 @@ class HomeworkPresenter @Inject constructor(
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
}
analytics.logEvent("load_homework", "items" to it.size, "force_refresh" to forceRefresh, START_DATE to currentDate.toFormattedString("yyyy-MM-dd"))
analytics.logEvent("load_homework", "items" to it.size, "force_refresh" to forceRefresh)
}) {
Timber.i("Loading homework result: An exception occurred")
view?.run { showEmpty(isViewEmpty()) }

View File

@ -1,9 +1,9 @@
package io.github.wulkanowy.ui.modules.homework
import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.ui.base.session.BaseSessionView
import io.github.wulkanowy.ui.base.BaseView
interface HomeworkView : BaseSessionView {
interface HomeworkView : BaseView {
fun initView()

View File

@ -14,10 +14,10 @@ import io.github.wulkanowy.utils.setOnSelectPageListener
import kotlinx.android.synthetic.main.activity_login.*
import javax.inject.Inject
class LoginActivity : BaseActivity(), LoginView {
class LoginActivity : BaseActivity<LoginPresenter>(), LoginView {
@Inject
lateinit var presenter: LoginPresenter
override lateinit var presenter: LoginPresenter
@Inject
lateinit var loginAdapter: BaseFragmentPagerAdapter
@ -81,9 +81,4 @@ class LoginActivity : BaseActivity(), LoginView {
fun onSymbolFragmentAccountLogged(students: List<Student>) {
presenter.onSymbolViewAccountLogged(students)
}
public override fun onDestroy() {
presenter.onDetachView()
super.onDestroy()
}
}

View File

@ -1,12 +1,18 @@
package io.github.wulkanowy.ui.modules.login
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.SchedulersProvider
import timber.log.Timber
import javax.inject.Inject
class LoginPresenter @Inject constructor(errorHandler: ErrorHandler) : BasePresenter<LoginView>(errorHandler) {
class LoginPresenter @Inject constructor(
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository
) : BasePresenter<LoginView>(errorHandler, studentRepository, schedulers) {
override fun onAttachView(view: LoginView) {
super.onAttachView(view)

View File

@ -10,11 +10,11 @@ import android.view.ViewGroup
import android.view.inputmethod.EditorInfo.IME_ACTION_DONE
import android.view.inputmethod.EditorInfo.IME_NULL
import android.widget.ArrayAdapter
import io.github.wulkanowy.BuildConfig.VERSION_NAME
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.hideSoftInput
import io.github.wulkanowy.utils.openInternetBrowser
import io.github.wulkanowy.utils.setOnItemSelectedListener
@ -28,6 +28,9 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
@Inject
lateinit var presenter: LoginFormPresenter
@Inject
lateinit var appInfo: AppInfo
companion object {
fun newInstance() = LoginFormFragment()
}
@ -128,7 +131,7 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
override fun showVersion() {
loginFormVersion.apply {
visibility = VISIBLE
text = "${getString(R.string.app_name)} $VERSION_NAME"
text = "${getString(R.string.app_name)} ${appInfo.versionName}"
}
}

View File

@ -1,30 +1,30 @@
package io.github.wulkanowy.ui.modules.login.form
import com.google.firebase.analytics.FirebaseAnalytics.Param.SUCCESS
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.ifNullOrBlank
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Named
class LoginFormPresenter @Inject constructor(
private val schedulers: SchedulersProvider,
private val errorHandler: LoginErrorHandler,
private val studentRepository: StudentRepository,
schedulers: SchedulersProvider,
studentRepository: StudentRepository,
private val loginErrorHandler: LoginErrorHandler,
private val analytics: FirebaseAnalyticsHelper,
@param:Named("isDebug") private val isDebug: Boolean
) : BasePresenter<LoginFormView>(errorHandler) {
private val appInfo: AppInfo
) : BasePresenter<LoginFormView>(loginErrorHandler, studentRepository, schedulers) {
override fun onAttachView(view: LoginFormView) {
super.onAttachView(view)
view.run {
initView()
if (isDebug) showVersion() else showPrivacyPolicy()
if (appInfo.isDebug) showVersion() else showPrivacyPolicy()
errorHandler.onBadCredentials = {
loginErrorHandler.onBadCredentials = {
setErrorPassIncorrect()
showSoftKeyboard()
Timber.i("Entered wrong username or password")
@ -78,12 +78,12 @@ class LoginFormPresenter @Inject constructor(
}
.subscribe({
Timber.i("Login result: Success")
analytics.logEvent("registration_form", SUCCESS to true, "students" to it.size, "endpoint" to endpoint, "error" to "No error")
analytics.logEvent("registration_form", "success" to true, "students" to it.size, "endpoint" to endpoint, "error" to "No error")
view?.notifyParentAccountLogged(it)
}, {
Timber.i("Login result: An exception occurred")
analytics.logEvent("registration_form", SUCCESS to false, "students" to -1, "endpoint" to endpoint, "error" to it.localizedMessage)
errorHandler.dispatch(it)
analytics.logEvent("registration_form", "success" to false, "students" to -1, "endpoint" to endpoint, "error" to it.message.ifNullOrBlank { "No message" })
loginErrorHandler.dispatch(it)
}))
}

View File

@ -1,6 +1,5 @@
package io.github.wulkanowy.ui.modules.login.studentselect
import com.google.firebase.analytics.FirebaseAnalytics.Param.SUCCESS
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.student.StudentRepository
@ -8,27 +7,28 @@ import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.ifNullOrBlank
import timber.log.Timber
import java.io.Serializable
import javax.inject.Inject
class LoginStudentSelectPresenter @Inject constructor(
private val errorHandler: LoginErrorHandler,
private val studentRepository: StudentRepository,
private val schedulers: SchedulersProvider,
schedulers: SchedulersProvider,
studentRepository: StudentRepository,
private val loginErrorHandler: LoginErrorHandler,
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<LoginStudentSelectView>(errorHandler) {
) : BasePresenter<LoginStudentSelectView>(loginErrorHandler, studentRepository, schedulers) {
var students = emptyList<Student>()
var selectedStudents = mutableListOf<Student>()
private var selectedStudents = mutableListOf<Student>()
fun onAttachView(view: LoginStudentSelectView, students: Serializable?) {
super.onAttachView(view)
view.run {
initView()
enableSignIn(false)
errorHandler.onStudentDuplicate = {
loginErrorHandler.onStudentDuplicate = {
showMessage(it)
Timber.i("The student already registered in the app was selected")
}
@ -78,13 +78,13 @@ class LoginStudentSelectPresenter @Inject constructor(
Timber.i("Registration started")
}
.subscribe({
students.forEach { analytics.logEvent("registration_student_select", SUCCESS to true, "endpoint" to it.endpoint, "symbol" to it.symbol, "error" to "No error") }
students.forEach { analytics.logEvent("registration_student_select", "success" to true, "endpoint" to it.endpoint, "symbol" to it.symbol, "error" to "No error") }
Timber.i("Registration result: Success")
view?.openMainView()
}, { error ->
students.forEach { analytics.logEvent("registration_student_select", SUCCESS to false, "endpoint" to it.endpoint, "symbol" to it.symbol, "error" to error.localizedMessage) }
students.forEach { analytics.logEvent("registration_student_select", "success" to false, "endpoint" to it.endpoint, "symbol" to it.symbol, "error" to error.message.ifNullOrBlank { "No message" }) }
Timber.i("Registration result: An exception occurred ")
errorHandler.dispatch(error)
loginErrorHandler.dispatch(error)
view?.apply {
showProgress(false)
showContent(true)

View File

@ -1,22 +1,22 @@
package io.github.wulkanowy.ui.modules.login.symbol
import com.google.firebase.analytics.FirebaseAnalytics.Param.SUCCESS
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.ifNullOrBlank
import io.reactivex.Single
import timber.log.Timber
import java.io.Serializable
import javax.inject.Inject
class LoginSymbolPresenter @Inject constructor(
private val studentRepository: StudentRepository,
private val errorHandler: LoginErrorHandler,
private val schedulers: SchedulersProvider,
studentRepository: StudentRepository,
schedulers: SchedulersProvider,
private val loginErrorHandler: LoginErrorHandler,
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<LoginSymbolView>(errorHandler) {
) : BasePresenter<LoginSymbolView>(loginErrorHandler, studentRepository, schedulers) {
var loginData: Triple<String, String, String>? = null
@ -59,7 +59,7 @@ class LoginSymbolPresenter @Inject constructor(
}
}
.subscribe({
analytics.logEvent("registration_symbol", SUCCESS to true, "students" to it.size, "endpoint" to loginData?.third, "symbol" to symbol, "error" to "No error")
analytics.logEvent("registration_symbol", "success" to true, "students" to it.size, "endpoint" to loginData?.third, "symbol" to symbol, "error" to "No error")
view?.apply {
if (it.isEmpty()) {
Timber.i("Login with symbol result: Empty student list")
@ -71,8 +71,8 @@ class LoginSymbolPresenter @Inject constructor(
}
}, {
Timber.i("Login with symbol result: An exception occurred")
analytics.logEvent("registration_symbol", SUCCESS to false, "students" to -1, "endpoint" to loginData?.third, "symbol" to symbol, "error" to it.localizedMessage)
errorHandler.dispatch(it)
analytics.logEvent("registration_symbol", "success" to false, "students" to -1, "endpoint" to loginData?.third, "symbol" to symbol, "error" to it.message.ifNullOrBlank { "No message" })
loginErrorHandler.dispatch(it)
}))
}

View File

@ -6,12 +6,12 @@ import android.view.View
import android.view.ViewGroup
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.ui.base.session.BaseSessionFragment
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainView
import kotlinx.android.synthetic.main.fragment_lucky_number.*
import javax.inject.Inject
class LuckyNumberFragment : BaseSessionFragment(), LuckyNumberView, MainView.TitledView {
class LuckyNumberFragment : BaseFragment(), LuckyNumberView, MainView.TitledView {
@Inject
lateinit var presenter: LuckyNumberPresenter

View File

@ -4,20 +4,20 @@ import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import timber.log.Timber
import javax.inject.Inject
class LuckyNumberPresenter @Inject constructor(
private val errorHandler: SessionErrorHandler,
private val schedulers: SchedulersProvider,
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val luckyNumberRepository: LuckyNumberRepository,
private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<LuckyNumberView>(errorHandler) {
) : BasePresenter<LuckyNumberView>(errorHandler, studentRepository, schedulers) {
override fun onAttachView(view: LuckyNumberView) {
super.onAttachView(view)

View File

@ -1,9 +1,9 @@
package io.github.wulkanowy.ui.modules.luckynumber
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.ui.base.session.BaseSessionView
import io.github.wulkanowy.ui.base.BaseView
interface LuckyNumberView : BaseSessionView {
interface LuckyNumberView : BaseView {
fun initView()

View File

@ -16,13 +16,14 @@ import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.activity_widget_configure.*
import javax.inject.Inject
class LuckyNumberWidgetConfigureActivity : BaseActivity(), LuckyNumberWidgetConfigureView {
class LuckyNumberWidgetConfigureActivity : BaseActivity<LuckyNumberWidgetConfigurePresenter>(),
LuckyNumberWidgetConfigureView {
@Inject
lateinit var configureAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
@Inject
lateinit var presenter: LuckyNumberWidgetConfigurePresenter
override lateinit var presenter: LuckyNumberWidgetConfigurePresenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -69,9 +70,4 @@ class LuckyNumberWidgetConfigureActivity : BaseActivity(), LuckyNumberWidgetConf
override fun openLoginView() {
startActivity(LoginActivity.getStartIntent(this))
}
override fun onDestroy() {
super.onDestroy()
presenter.onDetachView()
}
}

View File

@ -11,11 +11,11 @@ import io.github.wulkanowy.utils.SchedulersProvider
import javax.inject.Inject
class LuckyNumberWidgetConfigurePresenter @Inject constructor(
private val errorHandler: ErrorHandler,
private val schedulers: SchedulersProvider,
private val studentRepository: StudentRepository,
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val sharedPref: SharedPrefHelper
) : BasePresenter<LuckyNumberWidgetConfigureView>(errorHandler) {
) : BasePresenter<LuckyNumberWidgetConfigureView>(errorHandler, studentRepository, schedulers) {
private var appWidgetId: Int? = null

View File

@ -7,7 +7,6 @@ import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
@ -22,7 +21,6 @@ import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
import io.github.wulkanowy.ui.modules.exam.ExamFragment
import io.github.wulkanowy.ui.modules.grade.GradeFragment
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
import io.github.wulkanowy.ui.modules.message.MessageFragment
import io.github.wulkanowy.ui.modules.more.MoreFragment
@ -34,10 +32,10 @@ import io.github.wulkanowy.utils.setOnViewChangeListener
import kotlinx.android.synthetic.main.activity_main.*
import javax.inject.Inject
class MainActivity : BaseActivity(), MainView {
class MainActivity : BaseActivity<MainPresenter>(), MainView {
@Inject
lateinit var presenter: MainPresenter
override lateinit var presenter: MainPresenter
@Inject
lateinit var navController: FragNavController
@ -154,15 +152,6 @@ class MainActivity : BaseActivity(), MainView {
navController.showDialogFragment(AccountDialog.newInstance())
}
fun showExpiredDialog() {
AlertDialog.Builder(this)
.setTitle(R.string.main_session_expired)
.setMessage(R.string.main_session_relogin)
.setPositiveButton(R.string.main_log_in) { _, _ -> presenter.onLoginSelected() }
.setNegativeButton(android.R.string.cancel) { _, _ -> }
.show()
}
override fun notifyMenuViewReselected() {
(navController.currentStack?.getOrNull(0) as? MainView.MainChildView)?.onFragmentReselected()
}
@ -183,19 +172,9 @@ class MainActivity : BaseActivity(), MainView {
presenter.onBackPressed { super.onBackPressed() }
}
override fun openLoginView() {
startActivity(LoginActivity.getStartIntent(this)
.apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) })
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
navController.onSaveInstanceState(outState)
intent.removeExtra(EXTRA_START_MENU)
}
override fun onDestroy() {
super.onDestroy()
presenter.onDetachView()
}
}

View File

@ -19,6 +19,9 @@ import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
import io.github.wulkanowy.ui.modules.message.MessageFragment
import io.github.wulkanowy.ui.modules.message.MessageModule
import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment
import io.github.wulkanowy.ui.modules.mobiledevice.token.MobileDeviceTokenDialog
import io.github.wulkanowy.ui.modules.mobiledevice.MobileDeviceFragment
import io.github.wulkanowy.ui.modules.mobiledevice.MobileDeviceModule
import io.github.wulkanowy.ui.modules.more.MoreFragment
import io.github.wulkanowy.ui.modules.note.NoteFragment
import io.github.wulkanowy.ui.modules.settings.SettingsFragment
@ -97,4 +100,12 @@ abstract class MainModule {
@PerFragment
@ContributesAndroidInjector
abstract fun bindAccountDialog(): AccountDialog
@PerFragment
@ContributesAndroidInjector(modules = [MobileDeviceModule::class])
abstract fun bindMobileDevices(): MobileDeviceFragment
@PerFragment
@ContributesAndroidInjector
abstract fun bindMobileDeviceDialog(): MobileDeviceTokenDialog
}

View File

@ -1,7 +1,5 @@
package io.github.wulkanowy.ui.modules.main
import com.google.firebase.analytics.FirebaseAnalytics.Event.APP_OPEN
import com.google.firebase.analytics.FirebaseAnalytics.Param.DESTINATION
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.services.sync.SyncManager
@ -9,18 +7,17 @@ import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.reactivex.Completable
import timber.log.Timber
import javax.inject.Inject
class MainPresenter @Inject constructor(
private val errorHandler: ErrorHandler,
private val studentRepository: StudentRepository,
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val prefRepository: PreferencesRepository,
private val syncManager: SyncManager,
private val schedulers: SchedulersProvider,
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<MainView>(errorHandler) {
) : BasePresenter<MainView>(errorHandler, studentRepository, schedulers) {
fun onAttachView(view: MainView, initMenu: MainView.MenuView?) {
super.onAttachView(view)
@ -34,7 +31,7 @@ class MainPresenter @Inject constructor(
}
syncManager.startSyncWorker()
analytics.logEvent(APP_OPEN, DESTINATION to initMenu?.name)
analytics.logEvent("app_open", "destination" to initMenu?.name)
}
fun onViewChange() {
@ -80,28 +77,6 @@ class MainPresenter @Inject constructor(
} == true
}
fun onLoginSelected() {
Timber.i("Attempt to switch the student after the session expires")
disposable.add(studentRepository.getCurrentStudent(false)
.flatMapCompletable { studentRepository.logoutStudent(it) }
.andThen(studentRepository.getSavedStudents(false))
.flatMapCompletable {
if (it.isNotEmpty()) {
Timber.i("Switching current student")
studentRepository.switchStudent(it[0])
} else Completable.complete()
}
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
Timber.i("Switch student result: Open login view")
view?.openLoginView()
}, {
Timber.i("Switch student result: An exception occurred")
errorHandler.dispatch(it)
}))
}
private fun getProperViewIndexes(initMenu: MainView.MenuView?): Pair<Int, Int> {
return when {
initMenu?.id in 0..3 -> initMenu!!.id to -1

View File

@ -28,8 +28,6 @@ interface MainView : BaseView {
fun popView()
fun openLoginView()
interface MainChildView {
fun onFragmentReselected()

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.ui.modules.message
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.SchedulersProvider
@ -10,9 +11,10 @@ import java.util.concurrent.TimeUnit.MILLISECONDS
import javax.inject.Inject
class MessagePresenter @Inject constructor(
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
private val schedulers: SchedulersProvider
) : BasePresenter<MessageView>(errorHandler) {
studentRepository: StudentRepository
) : BasePresenter<MessageView>(errorHandler, studentRepository, schedulers) {
override fun onAttachView(view: MessageView) {
super.onAttachView(view)

View File

@ -12,7 +12,7 @@ import android.view.View.VISIBLE
import android.view.ViewGroup
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.ui.base.session.BaseSessionFragment
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.modules.message.MessageFragment
@ -20,8 +20,7 @@ import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
import kotlinx.android.synthetic.main.fragment_message_preview.*
import javax.inject.Inject
@SuppressLint("SetTextI18n")
class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainView.TitledView {
class MessagePreviewFragment : BaseFragment(), MessagePreviewView, MainView.TitledView {
@Inject
lateinit var presenter: MessagePreviewPresenter
@ -87,10 +86,12 @@ class MessagePreviewFragment : BaseSessionFragment(), MessagePreviewView, MainVi
messagePreviewSubject.text = subject
}
@SuppressLint("SetTextI18n")
override fun setRecipient(recipient: String) {
messagePreviewAuthor.text = "${getString(R.string.message_to)} $recipient"
}
@SuppressLint("SetTextI18n")
override fun setSender(sender: String) {
messagePreviewAuthor.text = "${getString(R.string.message_from)} $sender"
}

View File

@ -1,11 +1,10 @@
package io.github.wulkanowy.ui.modules.message.preview
import com.google.firebase.analytics.FirebaseAnalytics.Param.START_DATE
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.repositories.message.MessageRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.toFormattedString
@ -13,12 +12,12 @@ import timber.log.Timber
import javax.inject.Inject
class MessagePreviewPresenter @Inject constructor(
private val errorHandler: SessionErrorHandler,
private val schedulers: SchedulersProvider,
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val messageRepository: MessageRepository,
private val studentRepository: StudentRepository,
private val analytics: FirebaseAnalyticsHelper
) : BaseSessionPresenter<MessagePreviewView>(errorHandler) {
) : BasePresenter<MessagePreviewView>(errorHandler, studentRepository, schedulers) {
var messageId = 0L
@ -53,7 +52,7 @@ class MessagePreviewPresenter @Inject constructor(
else setSender(it.sender)
}
}
analytics.logEvent("load_message_preview", START_DATE to message.date.toFormattedString("yyyy.MM.dd"), "length" to message.content?.length)
analytics.logEvent("load_message_preview", "length" to message.content?.length)
}) {
Timber.i("Loading message $id preview result: An exception occurred ")
view?.showMessageError()

View File

@ -1,9 +1,9 @@
package io.github.wulkanowy.ui.modules.message.preview
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.ui.base.session.BaseSessionView
import io.github.wulkanowy.ui.base.BaseView
interface MessagePreviewView : BaseSessionView {
interface MessagePreviewView : BaseView {
val noSubjectString: String

View File

@ -19,13 +19,14 @@ import io.github.wulkanowy.utils.showSoftInput
import kotlinx.android.synthetic.main.activity_send_message.*
import javax.inject.Inject
class SendMessageActivity : BaseActivity(), SendMessageView {
class SendMessageActivity : BaseActivity<SendMessagePresenter>(), SendMessageView {
@Inject
lateinit var presenter: SendMessagePresenter
override lateinit var presenter: SendMessagePresenter
companion object {
private const val EXTRA_MESSAGE = "EXTRA_MESSAGE"
private const val EXTRA_REPLY = "EXTRA_REPLY"
fun getStartIntent(context: Context) = Intent(context, SendMessageActivity::class.java)
@ -126,9 +127,4 @@ class SendMessageActivity : BaseActivity(), SendMessageView {
override fun popView() {
onBackPressed()
}
override fun onDestroy() {
presenter.onDetachView()
super.onDestroy()
}
}

View File

@ -19,16 +19,16 @@ import timber.log.Timber
import javax.inject.Inject
class SendMessagePresenter @Inject constructor(
private val errorHandler: ErrorHandler,
private val schedulers: SchedulersProvider,
private val studentRepository: StudentRepository,
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val messageRepository: MessageRepository,
private val reportingUnitRepository: ReportingUnitRepository,
private val recipientRepository: RecipientRepository,
private val preferencesRepository: PreferencesRepository,
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<SendMessageView>(errorHandler) {
) : BasePresenter<SendMessageView>(errorHandler, studentRepository, schedulers) {
fun onAttachView(view: SendMessageView, message: Message?, reply: Boolean?) {
super.onAttachView(view)

View File

@ -13,7 +13,7 @@ import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R
import io.github.wulkanowy.data.repositories.message.MessageFolder
import io.github.wulkanowy.ui.base.session.BaseSessionFragment
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.message.MessageFragment
import io.github.wulkanowy.ui.modules.message.MessageItem
@ -22,7 +22,7 @@ import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.fragment_message_tab.*
import javax.inject.Inject
class MessageTabFragment : BaseSessionFragment(), MessageTabView {
class MessageTabFragment : BaseFragment(), MessageTabView {
@Inject
lateinit var presenter: MessageTabPresenter

View File

@ -5,8 +5,8 @@ import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.repositories.message.MessageFolder
import io.github.wulkanowy.data.repositories.message.MessageRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.message.MessageItem
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
@ -14,12 +14,12 @@ import timber.log.Timber
import javax.inject.Inject
class MessageTabPresenter @Inject constructor(
private val errorHandler: SessionErrorHandler,
private val schedulers: SchedulersProvider,
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val messageRepository: MessageRepository,
private val studentRepository: StudentRepository,
private val analytics: FirebaseAnalyticsHelper
) : BaseSessionPresenter<MessageTabView>(errorHandler) {
) : BasePresenter<MessageTabView>(errorHandler, studentRepository, schedulers) {
lateinit var folder: MessageFolder

View File

@ -1,10 +1,10 @@
package io.github.wulkanowy.ui.modules.message.tab
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.ui.base.session.BaseSessionView
import io.github.wulkanowy.ui.base.BaseView
import io.github.wulkanowy.ui.modules.message.MessageItem
interface MessageTabView : BaseSessionView {
interface MessageTabView : BaseView {
val noSubjectString: String

View File

@ -0,0 +1,10 @@
package io.github.wulkanowy.ui.modules.mobiledevice
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible
import io.github.wulkanowy.data.db.entities.MobileDevice
class MobileDeviceAdapter<T : IFlexible<*>> : FlexibleAdapter<T>(null, null, true) {
var onDeviceUnregisterListener: (device: MobileDevice, position: Int) -> Unit = { _, _ -> }
}

View File

@ -0,0 +1,124 @@
package io.github.wulkanowy.ui.modules.mobiledevice
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
import eu.davidea.flexibleadapter.common.FlexibleItemDecoration
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.helpers.UndoHelper
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.MobileDevice
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.modules.mobiledevice.token.MobileDeviceTokenDialog
import kotlinx.android.synthetic.main.fragment_mobile_device.*
import javax.inject.Inject
class MobileDeviceFragment : BaseFragment(), MobileDeviceView, MainView.TitledView {
@Inject
lateinit var presenter: MobileDevicePresenter
@Inject
lateinit var devicesAdapter: MobileDeviceAdapter<AbstractFlexibleItem<*>>
companion object {
fun newInstance() = MobileDeviceFragment()
}
override val titleStringId: Int
get() = R.string.mobile_devices_title
override val isViewEmpty: Boolean
get() = devicesAdapter.isEmpty
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_mobile_device, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
messageContainer = mobileDevicesRecycler
presenter.onAttachView(this)
}
override fun initView() {
with(mobileDevicesRecycler) {
layoutManager = SmoothScrollLinearLayoutManager(context)
adapter = devicesAdapter
addItemDecoration(FlexibleItemDecoration(context)
.withDefaultDivider()
.withDrawDividerOnLastItem(false)
)
}
with(devicesAdapter) {
isPermanentDelete = false
onDeviceUnregisterListener = presenter::onUnregisterDevice
}
mobileDevicesSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
mobileDeviceAddButton.setOnClickListener { presenter.onRegisterDevice() }
}
override fun updateData(data: List<MobileDeviceItem>) {
devicesAdapter.updateDataSet(data)
}
override fun restoreDeleteItem() {
devicesAdapter.restoreDeletedItems()
}
override fun clearData() {
devicesAdapter.clear()
}
override fun showUndo(position: Int, device: MobileDevice) {
val onActionListener = object : UndoHelper.OnActionListener {
override fun onActionConfirmed(action: Int, event: Int) {
presenter.onUnregisterConfirmed(device)
}
override fun onActionCanceled(action: Int, positions: MutableList<Int>?) {
presenter.onUnregisterCancelled()
}
}
UndoHelper(devicesAdapter, onActionListener)
.withConsecutive(false)
.withAction(UndoHelper.Action.REMOVE)
.start(listOf(position), mobileDevicesRecycler, R.string.mobile_device_removed, R.string.all_undo, 3000)
}
override fun hideRefresh() {
mobileDevicesSwipe.isRefreshing = false
}
override fun showProgress(show: Boolean) {
mobileDevicesProgress.visibility = if (show) VISIBLE else GONE
}
override fun showEmpty(show: Boolean) {
mobileDevicesEmpty.visibility = if (show) VISIBLE else GONE
}
override fun enableSwipe(enable: Boolean) {
mobileDevicesSwipe.isEnabled = enable
}
override fun showContent(show: Boolean) {
mobileDevicesRecycler.visibility = if (show) VISIBLE else GONE
}
override fun showTokenDialog() {
(activity as? MainActivity)?.showDialogFragment(MobileDeviceTokenDialog.newInstance())
}
override fun onDestroyView() {
presenter.onDetachView()
super.onDestroyView()
}
}

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