1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2024-09-20 03:49:09 -05:00

Merge branch 'release/0.9.0'

This commit is contained in:
Rafał Borcz 2019-06-03 18:15:41 +02:00
commit 713500d892
141 changed files with 4564 additions and 565 deletions

View File

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

1
.gitignore vendored
View File

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

View File

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

@ -12,9 +12,8 @@ cache:
- $HOME/.gradle/wrapper/ - $HOME/.gradle/wrapper/
#branches: #branches:
# only: # only:
# - master # - develop
# - 0.8.x
android: android:
licenses: licenses:
@ -48,20 +47,20 @@ before_script:
script: script:
- ./gradlew dependencies --stacktrace --daemon - ./gradlew dependencies --stacktrace --daemon
- fossa --no-ansi || true - fossa --no-ansi || true
- ./gradlew lint -x fabricGenerateResourcesRelease --stacktrace --daemon #- ./gradlew lintPlayRelease -x fabricGenerateResourcesPlayRelease --stacktrace --daemon
- ./gradlew test -x fabricGenerateResourcesRelease --stacktrace --daemon - ./gradlew testPlayDebugUnitTest -x fabricGenerateResourcesPlay --stacktrace --daemon
- ./gradlew createDebugCoverageReport --stacktrace --daemon - ./gradlew createPlayDebugCoverageReport --stacktrace --daemon
- ./gradlew jacocoTestReport --stacktrace --daemon - ./gradlew jacocoTestReport --stacktrace --daemon
- if [ -z ${SONAR_HOST+x} ]; then echo "sonar scan skipped"; else - if [ -z ${SONAR_HOST+x} ]; then echo "sonar scan skipped"; else
git fetch --unshallow; 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 fi
- | - |
if [ $TRAVIS_TAG ]; then if [ $TRAVIS_TAG ]; then
gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/google-services.json.gpg; gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/google-services.json.gpg;
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/key.p12.gpg; gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/key.p12.gpg;
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg; gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg;
./gradlew publish -PenableCrashlytics --stacktrace; ./gradlew publishPlayRelease -PenableCrashlytics --stacktrace;
fi fi
after_success: after_success:

View File

@ -1,6 +1,5 @@
# Wulkanowy # 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) [![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) [![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) [![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) [![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) [![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) [![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) [Pobierz wersję beta z Google Play](https://play.google.com/store/apps/details?id=io.github.wulkanowy&amp;utm_source=vcs)

View File

@ -16,8 +16,8 @@ android {
testApplicationId "io.github.tests.wulkanowy" testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 15 minSdkVersion 15
targetSdkVersion 28 targetSdkVersion 28
versionCode 37 versionCode 38
versionName "0.8.4" versionName "0.9.0"
multiDexEnabled true multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
@ -63,6 +63,18 @@ android {
} }
} }
flavorDimensions "platform"
productFlavors {
play {
dimension "platform"
}
fdroid {
buildConfigField "boolean", "CRASHLYTICS_ENABLED", "false"
dimension "platform"
}
}
lintOptions { lintOptions {
disable 'HardwareIds' disable 'HardwareIds'
} }
@ -85,7 +97,7 @@ play {
} }
dependencies { dependencies {
implementation 'io.github.wulkanowy:api:0.8.4' implementation 'io.github.wulkanowy:api:0.9.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.legacy:legacy-support-v4:1.0.0"
@ -94,20 +106,20 @@ dependencies {
implementation "androidx.cardview:cardview:1.0.0" implementation "androidx.cardview:cardview:1.0.0"
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation "com.google.android.material:material:1.1.0-alpha05" implementation "com.google.android.material:material:1.1.0-alpha07"
implementation 'com.github.wulkanowy:MaterialChipsInput:b72fd0ee6f' implementation 'com.github.wulkanowy:MaterialChipsInput:b72fd0ee6f'
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
implementation "androidx.work:work-runtime:2.0.1" implementation "androidx.work:work-runtime:2.0.1"
implementation "androidx.work:work-rxjava2:2.0.1" implementation "androidx.work:work-rxjava2:2.0.1"
implementation "androidx.room:room-runtime:2.1.0-alpha07" implementation "androidx.room:room-runtime:2.1.0-rc01"
implementation "androidx.room:room-rxjava2:2.1.0-alpha07" implementation "androidx.room:room-rxjava2:2.1.0-rc01"
kapt "androidx.room:room-compiler:2.1.0-alpha07" kapt "androidx.room:room-compiler:2.1.0-rc01"
implementation "com.google.dagger:dagger-android-support:2.22.1" implementation "com.google.dagger:dagger-android-support:2.23.1"
kapt "com.google.dagger:dagger-compiler:2.22.1" kapt "com.google.dagger:dagger-compiler:2.23.1"
kapt "com.google.dagger:dagger-android-processor:2.22.1" kapt "com.google.dagger:dagger-android-processor:2.23.1"
implementation 'com.squareup.inject:assisted-inject-annotations-dagger2:0.4.0' implementation 'com.squareup.inject:assisted-inject-annotations-dagger2:0.4.0'
kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.4.0' kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.4.0'
@ -116,21 +128,21 @@ dependencies {
implementation "com.aurelhubert:ahbottomnavigation:2.3.4" 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 'com.github.pwittchen:reactivenetwork-rx2:3.0.3'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation "io.reactivex.rxjava2:rxjava:2.2.8" implementation "io.reactivex.rxjava2:rxjava:2.2.9"
implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.google.code.gson:gson:2.8.5'
implementation "com.jakewharton.threetenabp:threetenabp:1.2.0" implementation "com.jakewharton.threetenabp:threetenabp:1.2.0"
implementation "com.jakewharton.timber:timber:4.7.1" implementation "com.jakewharton.timber:timber:4.7.1"
implementation "at.favre.lib:slf4j-timber:1.0.1" implementation "at.favre.lib:slf4j-timber:1.0.1"
implementation "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.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' playImplementation 'com.google.firebase:firebase-core:16.0.9'
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.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'
@ -139,16 +151,22 @@ dependencies {
testImplementation "junit:junit:4.12" testImplementation "junit:junit:4.12"
testImplementation "io.mockk:mockk:1.9.2" testImplementation "io.mockk:mockk:1.9.2"
testImplementation "org.mockito:mockito-inline:2.27.0" testImplementation 'org.threeten:threetenbp:1.4.0'
testImplementation 'org.threeten:threetenbp:1.3.8' 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:core:1.2.0'
androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.0' androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation "io.mockk:mockk-android:1.9.2" androidTestImplementation "io.mockk:mockk-android:1.9.2"
androidTestImplementation 'org.mockito:mockito-android:2.27.0' androidTestImplementation "androidx.room:room-testing:2.1.0-rc01"
androidTestImplementation "androidx.room:room-testing:2.1.0-alpha07"
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" 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' apply plugin: 'com.google.gms.google-services'

View File

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

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.java.coveragePlugin", "jacoco"
property "sonar.android.lint.report", "build/reports/lint-results.xml" property "sonar.android.lint.report", "build/reports/lint-results.xml"
property "sonar.jacoco.reportPaths", fileTree(dir: project.projectDir, includes: ['**/*.exec', '**/*.ec']) 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 { fun getMigratedRoomDatabase(): AppDatabase {
val database = Room.databaseBuilder(ApplicationProvider.getApplicationContext(), val database = Room.databaseBuilder(ApplicationProvider.getApplicationContext(),
AppDatabase::class.java, dbName) AppDatabase::class.java, dbName)
.addMigrations(Migration12(), Migration13()) .addMigrations(
Migration12(),
Migration13(),
Migration14(),
Migration15()
)
.build() .build()
// close the database and release any stream resources when the test finishes // close the database and release any stream resources when the test finishes
helper.closeWhenFinished(database) 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
class CrashlyticsTree : Timber.Tree() {
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
// do nothing
}
}
fun initCrashlytics(context: Context) {
// 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

@ -4,21 +4,18 @@ import android.content.Context
import androidx.multidex.MultiDex import androidx.multidex.MultiDex
import androidx.work.Configuration import androidx.work.Configuration
import androidx.work.WorkManager import androidx.work.WorkManager
import com.crashlytics.android.Crashlytics
import com.crashlytics.android.core.CrashlyticsCore
import com.jakewharton.threetenabp.AndroidThreeTen import com.jakewharton.threetenabp.AndroidThreeTen
import dagger.android.AndroidInjector import dagger.android.AndroidInjector
import dagger.android.support.DaggerApplication import dagger.android.support.DaggerApplication
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.utils.Log 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.BuildConfig.DEBUG
import io.github.wulkanowy.di.DaggerAppComponent import io.github.wulkanowy.di.DaggerAppComponent
import io.github.wulkanowy.services.sync.SyncWorkerFactory import io.github.wulkanowy.services.sync.SyncWorkerFactory
import io.github.wulkanowy.utils.ActivityLifecycleLogger import io.github.wulkanowy.utils.ActivityLifecycleLogger
import io.github.wulkanowy.utils.CrashlyticsTree import io.github.wulkanowy.utils.CrashlyticsTree
import io.github.wulkanowy.utils.DebugLogTree import io.github.wulkanowy.utils.DebugLogTree
import io.github.wulkanowy.utils.initCrashlytics
import io.reactivex.exceptions.UndeliverableException import io.reactivex.exceptions.UndeliverableException
import io.reactivex.plugins.RxJavaPlugins import io.reactivex.plugins.RxJavaPlugins
import timber.log.Timber import timber.log.Timber
@ -41,7 +38,7 @@ class WulkanowyApp : DaggerApplication() {
WorkManager.initialize(this, Configuration.Builder().setWorkerFactory(workerFactory).build()) WorkManager.initialize(this, Configuration.Builder().setWorkerFactory(workerFactory).build())
RxJavaPlugins.setErrorHandler(::onError) RxJavaPlugins.setErrorHandler(::onError)
initCrashlytics() initCrashlytics(applicationContext)
initLogging() initLogging()
} }
@ -55,12 +52,6 @@ class WulkanowyApp : DaggerApplication() {
registerActivityLifecycleCallbacks(ActivityLifecycleLogger()) 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) { private fun onError(error: Throwable) {
if (error is UndeliverableException && error.cause is IOException || error.cause is InterruptedException) { if (error is UndeliverableException && error.cause is IOException || error.cause is InterruptedException) {
Timber.e(error.cause, "An undeliverable error occurred") Timber.e(error.cause, "An undeliverable error occurred")

View File

@ -132,4 +132,8 @@ internal class RepositoryModule {
@Singleton @Singleton
@Provides @Provides
fun provideRecipientDao(database: AppDatabase) = database.recipientDao 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.HomeworkDao
import io.github.wulkanowy.data.db.dao.LuckyNumberDao import io.github.wulkanowy.data.db.dao.LuckyNumberDao
import io.github.wulkanowy.data.db.dao.MessagesDao 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.NoteDao
import io.github.wulkanowy.data.db.dao.RecipientDao import io.github.wulkanowy.data.db.dao.RecipientDao
import io.github.wulkanowy.data.db.dao.ReportingUnitDao 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.Homework
import io.github.wulkanowy.data.db.entities.LuckyNumber import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Message 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.Note
import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.data.db.entities.ReportingUnit
@ -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.Migration11
import io.github.wulkanowy.data.db.migrations.Migration12 import io.github.wulkanowy.data.db.migrations.Migration12
import io.github.wulkanowy.data.db.migrations.Migration13 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.Migration2
import io.github.wulkanowy.data.db.migrations.Migration3 import io.github.wulkanowy.data.db.migrations.Migration3
import io.github.wulkanowy.data.db.migrations.Migration4 import io.github.wulkanowy.data.db.migrations.Migration4
@ -73,7 +77,8 @@ import javax.inject.Singleton
LuckyNumber::class, LuckyNumber::class,
CompletedLesson::class, CompletedLesson::class,
ReportingUnit::class, ReportingUnit::class,
Recipient::class Recipient::class,
MobileDevice::class
], ],
version = AppDatabase.VERSION_SCHEMA, version = AppDatabase.VERSION_SCHEMA,
exportSchema = true exportSchema = true
@ -82,7 +87,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
companion object { companion object {
const val VERSION_SCHEMA = 13 const val VERSION_SCHEMA = 15
fun newInstance(context: Context): AppDatabase { fun newInstance(context: Context): AppDatabase {
return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database") return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database")
@ -101,7 +106,9 @@ abstract class AppDatabase : RoomDatabase() {
Migration10(), Migration10(),
Migration11(), Migration11(),
Migration12(), Migration12(),
Migration13() Migration13(),
Migration14(),
Migration15()
) )
.build() .build()
} }
@ -140,4 +147,6 @@ abstract class AppDatabase : RoomDatabase() {
abstract val reportingUnitDao: ReportingUnitDao abstract val reportingUnitDao: ReportingUnitDao
abstract val recipientDao: RecipientDao 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") @ColumnInfo(name = "student_id")
val studentId: Int, val studentId: Int,
val position: Int,
val subject: String, val subject: String,
@ColumnInfo(name = "predicted_grade")
val predictedGrade: String, 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) @PrimaryKey(autoGenerate = true)
var id: Long = 0 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( GradeSummary(
semesterId = semester.semesterId, semesterId = semester.semesterId,
studentId = semester.studentId, studentId = semester.studentId,
position = it.order,
subject = it.name, subject = it.name,
predictedGrade = it.predicted, 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,44 @@
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 remote.unregisterDevice(semester, device)
}
fun getToken(semester: Semester): Single<MobileDeviceToken> {
return remote.getToken(semester)
}
}

View File

@ -40,7 +40,7 @@ class PreferencesRepository @Inject constructor(
val servicesOnlyWifiKey: String = context.getString(R.string.pref_key_services_wifi_only) val servicesOnlyWifiKey: String = context.getString(R.string.pref_key_services_wifi_only)
val isServicesOnlyWifi: Boolean val isServicesOnlyWifi: Boolean
get() = sharedPref.getBoolean(servicesOnlyWifiKey, true) get() = sharedPref.getBoolean(servicesOnlyWifiKey, false)
val isNotificationsEnable: Boolean val isNotificationsEnable: Boolean
get() = sharedPref.getBoolean(context.getString(R.string.pref_key_notifications_enable), true) get() = sharedPref.getBoolean(context.getString(R.string.pref_key_notifications_enable), true)

View File

@ -2,7 +2,6 @@ package io.github.wulkanowy.di
import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetManager
import android.content.Context import android.content.Context
import com.google.firebase.analytics.FirebaseAnalytics
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
@ -27,10 +26,6 @@ internal class AppModule {
@Provides @Provides
fun provideFlexibleAdapter() = FlexibleAdapter<AbstractFlexibleItem<*>>(null, null, true) fun provideFlexibleAdapter() = FlexibleAdapter<AbstractFlexibleItem<*>>(null, null, true)
@Singleton
@Provides
fun provideFirebaseAnalytics(context: Context) = FirebaseAnalytics.getInstance(context)
@Singleton @Singleton
@Provides @Provides
fun provideAppWidgetManager(context: Context): AppWidgetManager = AppWidgetManager.getInstance(context) fun provideAppWidgetManager(context: Context): AppWidgetManager = AppWidgetManager.getInstance(context)

View File

@ -1,8 +1,11 @@
package io.github.wulkanowy.ui.base 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.os.Bundle
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
@ -12,10 +15,12 @@ import dagger.android.AndroidInjection
import dagger.android.DispatchingAndroidInjector import dagger.android.DispatchingAndroidInjector
import dagger.android.support.HasSupportFragmentInjector import dagger.android.support.HasSupportFragmentInjector
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.utils.FragmentLifecycleLogger import io.github.wulkanowy.utils.FragmentLifecycleLogger
import javax.inject.Inject import javax.inject.Inject
abstract class BaseActivity : AppCompatActivity(), BaseView, HasSupportFragmentInjector { abstract class BaseActivity<T : BasePresenter<out BaseView>> : AppCompatActivity(), BaseView,
HasSupportFragmentInjector {
@Inject @Inject
lateinit var supportFragmentInjector: DispatchingAndroidInjector<Fragment> lateinit var supportFragmentInjector: DispatchingAndroidInjector<Fragment>
@ -28,6 +33,8 @@ abstract class BaseActivity : AppCompatActivity(), BaseView, HasSupportFragmentI
protected var messageContainer: View? = null protected var messageContainer: View? = null
abstract var presenter: T
public override fun onCreate(savedInstanceState: Bundle?) { public override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this) AndroidInjection.inject(this)
themeManager.applyTheme(this) themeManager.applyTheme(this)
@ -51,9 +58,24 @@ abstract class BaseActivity : AppCompatActivity(), BaseView, HasSupportFragmentI
else Toast.makeText(this, text, Toast.LENGTH_LONG).show() 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() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
invalidateOptionsMenu() invalidateOptionsMenu()
presenter.onDetachView()
} }
override fun supportFragmentInjector() = supportFragmentInjector override fun supportFragmentInjector() = supportFragmentInjector

View File

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

View File

@ -1,8 +1,16 @@
package io.github.wulkanowy.ui.base 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 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() val disposable = CompositeDisposable()
@ -10,7 +18,33 @@ open class BasePresenter<T : BaseView>(private val errorHandler: ErrorHandler) {
open fun onAttachView(view: T) { open fun onAttachView(view: T) {
this.view = view 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() { open fun onDetachView() {

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,9 @@
package io.github.wulkanowy.ui.modules.attendance package io.github.wulkanowy.ui.modules.attendance
import io.github.wulkanowy.data.db.entities.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 val isViewEmpty: Boolean

View File

@ -13,13 +13,13 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R 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.ui.modules.main.MainView
import io.github.wulkanowy.utils.setOnItemSelectedListener import io.github.wulkanowy.utils.setOnItemSelectedListener
import kotlinx.android.synthetic.main.fragment_attendance_summary.* import kotlinx.android.synthetic.main.fragment_attendance_summary.*
import javax.inject.Inject import javax.inject.Inject
class AttendanceSummaryFragment : BaseSessionFragment(), AttendanceSummaryView, MainView.TitledView { class AttendanceSummaryFragment : BaseFragment(), AttendanceSummaryView, MainView.TitledView {
@Inject @Inject
lateinit var presenter: AttendanceSummaryPresenter 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.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.data.repositories.subject.SubjectRepository import io.github.wulkanowy.data.repositories.subject.SubjectRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter 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.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.calculatePercentage import io.github.wulkanowy.utils.calculatePercentage
@ -19,14 +19,14 @@ import java.util.concurrent.TimeUnit.MILLISECONDS
import javax.inject.Inject import javax.inject.Inject
class AttendanceSummaryPresenter @Inject constructor( class AttendanceSummaryPresenter @Inject constructor(
private val errorHandler: SessionErrorHandler, schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val attendanceSummaryRepository: AttendanceSummaryRepository, private val attendanceSummaryRepository: AttendanceSummaryRepository,
private val subjectRepository: SubjectRepository, private val subjectRepository: SubjectRepository,
private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val schedulers: SchedulersProvider,
private val analytics: FirebaseAnalyticsHelper private val analytics: FirebaseAnalyticsHelper
) : BaseSessionPresenter<AttendanceSummaryView>(errorHandler) { ) : BasePresenter<AttendanceSummaryView>(errorHandler, studentRepository, schedulers) {
private var subjects = emptyList<Subject>() private var subjects = emptyList<Subject>()

View File

@ -1,8 +1,8 @@
package io.github.wulkanowy.ui.modules.attendance.summary 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 val isViewEmpty: Boolean

View File

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

View File

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

View File

@ -1,9 +1,9 @@
package io.github.wulkanowy.ui.modules.exam package io.github.wulkanowy.ui.modules.exam
import io.github.wulkanowy.data.db.entities.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 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.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.grade.GradeRepository 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.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.utils.calcAverage import io.github.wulkanowy.utils.calcAverage
import io.github.wulkanowy.utils.changeModifier import io.github.wulkanowy.utils.changeModifier
import io.reactivex.Maybe
import io.reactivex.Single import io.reactivex.Single
import javax.inject.Inject import javax.inject.Inject
class GradeAverageProvider @Inject constructor( class GradeAverageProvider @Inject constructor(
private val preferencesRepository: PreferencesRepository, 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>> { fun getGradeAverage(student: Student, semesters: List<Semester>, selectedSemesterId: Int, forceRefresh: Boolean): Single<Map<String, Double>> {
return when (preferencesRepository.gradeAverageMode) { return when (preferencesRepository.gradeAverageMode) {
"all_year" -> getAllYearAverage(student, semesters, selectedSemesterId, forceRefresh) "all_year" -> getAllYearAverage(student, semesters, selectedSemesterId, forceRefresh)
@ -27,16 +31,19 @@ class GradeAverageProvider @Inject constructor(
val plusModifier = preferencesRepository.gradePlusModifier val plusModifier = preferencesRepository.gradePlusModifier
val minusModifier = preferencesRepository.gradeMinusModifier val minusModifier = preferencesRepository.gradeMinusModifier
return gradeRepository.getGrades(student, selectedSemester, forceRefresh) return getAverageFromGradeSummary(selectedSemester, forceRefresh)
.flatMap { firstGrades -> .switchIfEmpty(gradeRepository.getGrades(student, selectedSemester, forceRefresh)
if (selectedSemester == firstSemester) Single.just(firstGrades) .flatMap { firstGrades ->
else gradeRepository.getGrades(student, firstSemester) if (selectedSemester == firstSemester) Single.just(firstGrades)
.map { secondGrades -> secondGrades + firstGrades } else {
}.map { grades -> gradeRepository.getGrades(student, firstSemester)
grades.map { it.changeModifier(plusModifier, minusModifier) } .map { secondGrades -> secondGrades + firstGrades }
.groupBy { it.subject } }
.mapValues { it.value.calcAverage() } }.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>> { 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 plusModifier = preferencesRepository.gradePlusModifier
val minusModifier = preferencesRepository.gradeMinusModifier val minusModifier = preferencesRepository.gradeMinusModifier
return gradeRepository.getGrades(student, selectedSemester, forceRefresh) return getAverageFromGradeSummary(selectedSemester, forceRefresh)
.map { grades -> .switchIfEmpty(gradeRepository.getGrades(student, selectedSemester, forceRefresh)
grades.map { it.changeModifier(plusModifier, minusModifier) } .map { grades ->
.groupBy { it.subject } grades.map { it.changeModifier(plusModifier, minusModifier) }
.mapValues { it.value.calcAverage() } .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()
} }
} }
} }

View File

@ -11,8 +11,8 @@ import android.view.View.VISIBLE
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import io.github.wulkanowy.R 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.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.details.GradeDetailsFragment
import io.github.wulkanowy.ui.modules.grade.statistics.GradeStatisticsFragment import io.github.wulkanowy.ui.modules.grade.statistics.GradeStatisticsFragment
import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment 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 kotlinx.android.synthetic.main.fragment_grade.*
import javax.inject.Inject import javax.inject.Inject
class GradeFragment : BaseSessionFragment(), GradeView, MainView.MainChildView, MainView.TitledView { class GradeFragment : BaseFragment(), GradeView, MainView.MainChildView, MainView.TitledView {
@Inject @Inject
lateinit var presenter: GradePresenter 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.db.entities.Semester
import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter 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.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class GradePresenter @Inject constructor( class GradePresenter @Inject constructor(
private val errorHandler: SessionErrorHandler, schedulers: SchedulersProvider,
private val schedulers: SchedulersProvider, errorHandler: ErrorHandler,
private val studentRepository: StudentRepository, studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: FirebaseAnalyticsHelper
) : BaseSessionPresenter<GradeView>(errorHandler) { ) : BasePresenter<GradeView>(errorHandler, studentRepository, schedulers) {
var selectedIndex = 0 var selectedIndex = 0
private set private set

View File

@ -1,8 +1,8 @@
package io.github.wulkanowy.ui.modules.grade 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 val currentPageIndex: Int

View File

@ -17,7 +17,7 @@ import eu.davidea.flexibleadapter.items.IExpandable
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Grade 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.GradeFragment
import io.github.wulkanowy.ui.modules.grade.GradeView import io.github.wulkanowy.ui.modules.grade.GradeView
import io.github.wulkanowy.ui.modules.main.MainActivity 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 kotlinx.android.synthetic.main.fragment_grade_details.*
import javax.inject.Inject import javax.inject.Inject
class GradeDetailsFragment : BaseSessionFragment(), GradeDetailsView, GradeView.GradeChildView { class GradeDetailsFragment : BaseFragment(), GradeDetailsView, GradeView.GradeChildView {
@Inject @Inject
lateinit var presenter: GradeDetailsPresenter 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.preferences.PreferencesRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter 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.ui.modules.grade.GradeAverageProvider import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
@ -16,15 +16,15 @@ import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class GradeDetailsPresenter @Inject constructor( class GradeDetailsPresenter @Inject constructor(
private val errorHandler: SessionErrorHandler, schedulers: SchedulersProvider,
private val schedulers: SchedulersProvider, errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val gradeRepository: GradeRepository, private val gradeRepository: GradeRepository,
private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val preferencesRepository: PreferencesRepository, private val preferencesRepository: PreferencesRepository,
private val averageProvider: GradeAverageProvider, private val averageProvider: GradeAverageProvider,
private val analytics: FirebaseAnalyticsHelper private val analytics: FirebaseAnalyticsHelper
) : BaseSessionPresenter<GradeDetailsView>(errorHandler) { ) : BasePresenter<GradeDetailsView>(errorHandler, studentRepository, schedulers) {
private var currentSemesterId = 0 private var currentSemesterId = 0

View File

@ -4,9 +4,9 @@ import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IExpandable import eu.davidea.flexibleadapter.items.IExpandable
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
import io.github.wulkanowy.data.db.entities.Grade 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 val isViewEmpty: Boolean

View File

@ -16,7 +16,7 @@ import com.github.mikephil.charting.data.PieEntry
import com.github.mikephil.charting.formatter.ValueFormatter import com.github.mikephil.charting.formatter.ValueFormatter
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.GradeStatistics 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.GradeFragment
import io.github.wulkanowy.ui.modules.grade.GradeView import io.github.wulkanowy.ui.modules.grade.GradeView
import io.github.wulkanowy.utils.getThemeAttrColor 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 kotlinx.android.synthetic.main.fragment_grade_statistics.*
import javax.inject.Inject import javax.inject.Inject
class GradeStatisticsFragment : BaseSessionFragment(), GradeStatisticsView, GradeView.GradeChildView { class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.GradeChildView {
@Inject @Inject
lateinit var presenter: GradeStatisticsPresenter 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.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.data.repositories.subject.SubjectRepository import io.github.wulkanowy.data.repositories.subject.SubjectRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter 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.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class GradeStatisticsPresenter @Inject constructor( class GradeStatisticsPresenter @Inject constructor(
private val errorHandler: SessionErrorHandler, schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val gradeStatisticsRepository: GradeStatisticsRepository, private val gradeStatisticsRepository: GradeStatisticsRepository,
private val subjectRepository: SubjectRepository, private val subjectRepository: SubjectRepository,
private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val preferencesRepository: PreferencesRepository, private val preferencesRepository: PreferencesRepository,
private val schedulers: SchedulersProvider,
private val analytics: FirebaseAnalyticsHelper private val analytics: FirebaseAnalyticsHelper
) : BaseSessionPresenter<GradeStatisticsView>(errorHandler) { ) : BasePresenter<GradeStatisticsView>(errorHandler, studentRepository, schedulers) {
private var subjects = emptyList<Subject>() private var subjects = emptyList<Subject>()

View File

@ -1,9 +1,9 @@
package io.github.wulkanowy.ui.modules.grade.statistics package io.github.wulkanowy.ui.modules.grade.statistics
import io.github.wulkanowy.data.db.entities.GradeStatistics 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 val isViewEmpty: Boolean

View File

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

View File

@ -1,19 +1,21 @@
package io.github.wulkanowy.ui.modules.grade.summary package io.github.wulkanowy.ui.modules.grade.summary
import android.annotation.SuppressLint
import android.view.View import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.viewholders.FlexibleViewHolder import eu.davidea.viewholders.FlexibleViewHolder
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.GradeSummary
import kotlinx.android.extensions.LayoutContainer import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_grade_summary.* import kotlinx.android.synthetic.main.item_grade_summary.*
class GradeSummaryItem( class GradeSummaryItem(
private val title: String, val summary: GradeSummary,
private val average: String, private val average: String
private val predictedGrade: String,
private val finalGrade: String
) : AbstractFlexibleItem<GradeSummaryItem.ViewHolder>() { ) : AbstractFlexibleItem<GradeSummaryItem.ViewHolder>() {
override fun getLayoutRes() = R.layout.item_grade_summary override fun getLayoutRes() = R.layout.item_grade_summary
@ -22,12 +24,16 @@ class GradeSummaryItem(
return ViewHolder(view, adapter) return ViewHolder(view, adapter)
} }
@SuppressLint("SetTextI18n")
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) { override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.run { holder.run {
gradeSummaryItemTitle.text = title gradeSummaryItemTitle.text = summary.subject
gradeSummaryItemPoints.text = summary.pointsSum
gradeSummaryItemAverage.text = average gradeSummaryItemAverage.text = average
gradeSummaryItemPredicted.text = predictedGrade gradeSummaryItemPredicted.text = "${summary.predictedGrade} ${summary.proposedPoints}".trim()
gradeSummaryItemFinal.text = finalGrade 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 other as GradeSummaryItem
if (average != other.average) return false if (average != other.average) return false
if (title != other.title) return false if (summary != other.summary) return false
if (predictedGrade != other.predictedGrade) return false if (summary.id != other.summary.id) return false
if (finalGrade != other.finalGrade) return false
return true return true
} }
override fun hashCode(): Int { 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 + average.hashCode()
result = 31 * result + predictedGrade.hashCode()
result = 31 * result + finalGrade.hashCode()
return result 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.gradessummary.GradeSummaryRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter 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.ui.modules.grade.GradeAverageProvider import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
@ -16,14 +16,14 @@ import java.util.Locale.FRANCE
import javax.inject.Inject import javax.inject.Inject
class GradeSummaryPresenter @Inject constructor( class GradeSummaryPresenter @Inject constructor(
private val errorHandler: SessionErrorHandler, schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val gradeSummaryRepository: GradeSummaryRepository, private val gradeSummaryRepository: GradeSummaryRepository,
private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val averageProvider: GradeAverageProvider, private val averageProvider: GradeAverageProvider,
private val schedulers: SchedulersProvider,
private val analytics: FirebaseAnalyticsHelper private val analytics: FirebaseAnalyticsHelper
) : BaseSessionPresenter<GradeSummaryView>(errorHandler) { ) : BasePresenter<GradeSummaryView>(errorHandler, studentRepository, schedulers) {
override fun onAttachView(view: GradeSummaryView) { override fun onAttachView(view: GradeSummaryView) {
super.onAttachView(view) super.onAttachView(view)
@ -96,10 +96,8 @@ class GradeSummaryPresenter @Inject constructor(
gradesSummary.filter { !checkEmpty(it, filteredAverages) } gradesSummary.filter { !checkEmpty(it, filteredAverages) }
.map { .map {
GradeSummaryItem( GradeSummaryItem(
title = it.subject, summary = it,
average = formatAverage(filteredAverages.getOrElse(it.subject) { 0.0 }, ""), average = formatAverage(filteredAverages.getOrElse(it.subject) { 0.0 }, "")
predictedGrade = it.predictedGrade,
finalGrade = it.finalGrade
) )
}.let { }.let {
it to GradeSummaryScrollableHeader( it to GradeSummaryScrollableHeader(

View File

@ -1,8 +1,8 @@
package io.github.wulkanowy.ui.modules.grade.summary 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 val isViewEmpty: Boolean

View File

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

View File

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

View File

@ -1,9 +1,9 @@
package io.github.wulkanowy.ui.modules.homework package io.github.wulkanowy.ui.modules.homework
import io.github.wulkanowy.data.db.entities.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() fun initView()

View File

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

View File

@ -1,12 +1,18 @@
package io.github.wulkanowy.ui.modules.login package io.github.wulkanowy.ui.modules.login
import io.github.wulkanowy.data.db.entities.Student 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.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.SchedulersProvider
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject 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) { override fun onAttachView(view: LoginView) {
super.onAttachView(view) super.onAttachView(view)

View File

@ -1,6 +1,5 @@
package io.github.wulkanowy.ui.modules.login.form 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.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
@ -11,12 +10,12 @@ import javax.inject.Inject
import javax.inject.Named import javax.inject.Named
class LoginFormPresenter @Inject constructor( class LoginFormPresenter @Inject constructor(
private val schedulers: SchedulersProvider, schedulers: SchedulersProvider,
private val errorHandler: LoginErrorHandler, studentRepository: StudentRepository,
private val studentRepository: StudentRepository, private val loginErrorHandler: LoginErrorHandler,
private val analytics: FirebaseAnalyticsHelper, private val analytics: FirebaseAnalyticsHelper,
@param:Named("isDebug") private val isDebug: Boolean @param:Named("isDebug") private val isDebug: Boolean
) : BasePresenter<LoginFormView>(errorHandler) { ) : BasePresenter<LoginFormView>(loginErrorHandler, studentRepository, schedulers) {
override fun onAttachView(view: LoginFormView) { override fun onAttachView(view: LoginFormView) {
super.onAttachView(view) super.onAttachView(view)
@ -24,7 +23,7 @@ class LoginFormPresenter @Inject constructor(
initView() initView()
if (isDebug) showVersion() else showPrivacyPolicy() if (isDebug) showVersion() else showPrivacyPolicy()
errorHandler.onBadCredentials = { loginErrorHandler.onBadCredentials = {
setErrorPassIncorrect() setErrorPassIncorrect()
showSoftKeyboard() showSoftKeyboard()
Timber.i("Entered wrong username or password") Timber.i("Entered wrong username or password")
@ -78,12 +77,12 @@ class LoginFormPresenter @Inject constructor(
} }
.subscribe({ .subscribe({
Timber.i("Login result: Success") 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) view?.notifyParentAccountLogged(it)
}, { }, {
Timber.i("Login result: An exception occurred") Timber.i("Login result: An exception occurred")
analytics.logEvent("registration_form", SUCCESS to false, "students" to -1, "endpoint" to endpoint, "error" to it.localizedMessage) analytics.logEvent("registration_form", "success" to false, "students" to -1, "endpoint" to endpoint, "error" to it.localizedMessage.ifEmpty { "No message" })
errorHandler.dispatch(it) loginErrorHandler.dispatch(it)
})) }))
} }

View File

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

View File

@ -1,6 +1,5 @@
package io.github.wulkanowy.ui.modules.login.symbol 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.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
@ -12,11 +11,11 @@ import java.io.Serializable
import javax.inject.Inject import javax.inject.Inject
class LoginSymbolPresenter @Inject constructor( class LoginSymbolPresenter @Inject constructor(
private val studentRepository: StudentRepository, studentRepository: StudentRepository,
private val errorHandler: LoginErrorHandler, schedulers: SchedulersProvider,
private val schedulers: SchedulersProvider, private val loginErrorHandler: LoginErrorHandler,
private val analytics: FirebaseAnalyticsHelper private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<LoginSymbolView>(errorHandler) { ) : BasePresenter<LoginSymbolView>(loginErrorHandler, studentRepository, schedulers) {
var loginData: Triple<String, String, String>? = null var loginData: Triple<String, String, String>? = null
@ -59,7 +58,7 @@ class LoginSymbolPresenter @Inject constructor(
} }
} }
.subscribe({ .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 { view?.apply {
if (it.isEmpty()) { if (it.isEmpty()) {
Timber.i("Login with symbol result: Empty student list") Timber.i("Login with symbol result: Empty student list")
@ -71,8 +70,8 @@ class LoginSymbolPresenter @Inject constructor(
} }
}, { }, {
Timber.i("Login with symbol result: An exception occurred") 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) analytics.logEvent("registration_symbol", "success" to false, "students" to -1, "endpoint" to loginData?.third, "symbol" to symbol, "error" to it.localizedMessage.ifEmpty { "No message" })
errorHandler.dispatch(it) loginErrorHandler.dispatch(it)
})) }))
} }

View File

@ -6,12 +6,12 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.LuckyNumber 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 io.github.wulkanowy.ui.modules.main.MainView
import kotlinx.android.synthetic.main.fragment_lucky_number.* import kotlinx.android.synthetic.main.fragment_lucky_number.*
import javax.inject.Inject import javax.inject.Inject
class LuckyNumberFragment : BaseSessionFragment(), LuckyNumberView, MainView.TitledView { class LuckyNumberFragment : BaseFragment(), LuckyNumberView, MainView.TitledView {
@Inject @Inject
lateinit var presenter: LuckyNumberPresenter 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.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.session.SessionErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class LuckyNumberPresenter @Inject constructor( class LuckyNumberPresenter @Inject constructor(
private val errorHandler: SessionErrorHandler, schedulers: SchedulersProvider,
private val schedulers: SchedulersProvider, errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val luckyNumberRepository: LuckyNumberRepository, private val luckyNumberRepository: LuckyNumberRepository,
private val studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<LuckyNumberView>(errorHandler) { ) : BasePresenter<LuckyNumberView>(errorHandler, studentRepository, schedulers) {
override fun onAttachView(view: LuckyNumberView) { override fun onAttachView(view: LuckyNumberView) {
super.onAttachView(view) super.onAttachView(view)

View File

@ -1,9 +1,9 @@
package io.github.wulkanowy.ui.modules.luckynumber package io.github.wulkanowy.ui.modules.luckynumber
import io.github.wulkanowy.data.db.entities.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() fun initView()

View File

@ -16,13 +16,14 @@ import io.github.wulkanowy.utils.setOnItemClickListener
import kotlinx.android.synthetic.main.activity_widget_configure.* import kotlinx.android.synthetic.main.activity_widget_configure.*
import javax.inject.Inject import javax.inject.Inject
class LuckyNumberWidgetConfigureActivity : BaseActivity(), LuckyNumberWidgetConfigureView { class LuckyNumberWidgetConfigureActivity : BaseActivity<LuckyNumberWidgetConfigurePresenter>(),
LuckyNumberWidgetConfigureView {
@Inject @Inject
lateinit var configureAdapter: FlexibleAdapter<AbstractFlexibleItem<*>> lateinit var configureAdapter: FlexibleAdapter<AbstractFlexibleItem<*>>
@Inject @Inject
lateinit var presenter: LuckyNumberWidgetConfigurePresenter override lateinit var presenter: LuckyNumberWidgetConfigurePresenter
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -69,9 +70,4 @@ class LuckyNumberWidgetConfigureActivity : BaseActivity(), LuckyNumberWidgetConf
override fun openLoginView() { override fun openLoginView() {
startActivity(LoginActivity.getStartIntent(this)) 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 import javax.inject.Inject
class LuckyNumberWidgetConfigurePresenter @Inject constructor( class LuckyNumberWidgetConfigurePresenter @Inject constructor(
private val errorHandler: ErrorHandler, schedulers: SchedulersProvider,
private val schedulers: SchedulersProvider, errorHandler: ErrorHandler,
private val studentRepository: StudentRepository, studentRepository: StudentRepository,
private val sharedPref: SharedPrefHelper private val sharedPref: SharedPrefHelper
) : BasePresenter<LuckyNumberWidgetConfigureView>(errorHandler) { ) : BasePresenter<LuckyNumberWidgetConfigureView>(errorHandler, studentRepository, schedulers) {
private var appWidgetId: Int? = null 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.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment 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.exam.ExamFragment
import io.github.wulkanowy.ui.modules.grade.GradeFragment import io.github.wulkanowy.ui.modules.grade.GradeFragment
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment 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.luckynumber.LuckyNumberFragment
import io.github.wulkanowy.ui.modules.message.MessageFragment import io.github.wulkanowy.ui.modules.message.MessageFragment
import io.github.wulkanowy.ui.modules.more.MoreFragment 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 kotlinx.android.synthetic.main.activity_main.*
import javax.inject.Inject import javax.inject.Inject
class MainActivity : BaseActivity(), MainView { class MainActivity : BaseActivity<MainPresenter>(), MainView {
@Inject @Inject
lateinit var presenter: MainPresenter override lateinit var presenter: MainPresenter
@Inject @Inject
lateinit var navController: FragNavController lateinit var navController: FragNavController
@ -154,15 +152,6 @@ class MainActivity : BaseActivity(), MainView {
navController.showDialogFragment(AccountDialog.newInstance()) 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() { override fun notifyMenuViewReselected() {
(navController.currentStack?.getOrNull(0) as? MainView.MainChildView)?.onFragmentReselected() (navController.currentStack?.getOrNull(0) as? MainView.MainChildView)?.onFragmentReselected()
} }
@ -183,19 +172,9 @@ class MainActivity : BaseActivity(), MainView {
presenter.onBackPressed { super.onBackPressed() } 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) { override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
navController.onSaveInstanceState(outState) navController.onSaveInstanceState(outState)
intent.removeExtra(EXTRA_START_MENU) 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.MessageFragment
import io.github.wulkanowy.ui.modules.message.MessageModule import io.github.wulkanowy.ui.modules.message.MessageModule
import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment 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.more.MoreFragment
import io.github.wulkanowy.ui.modules.note.NoteFragment import io.github.wulkanowy.ui.modules.note.NoteFragment
import io.github.wulkanowy.ui.modules.settings.SettingsFragment import io.github.wulkanowy.ui.modules.settings.SettingsFragment
@ -97,4 +100,12 @@ abstract class MainModule {
@PerFragment @PerFragment
@ContributesAndroidInjector @ContributesAndroidInjector
abstract fun bindAccountDialog(): AccountDialog 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 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.preferences.PreferencesRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.services.sync.SyncManager 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.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import io.reactivex.Completable
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class MainPresenter @Inject constructor( class MainPresenter @Inject constructor(
private val errorHandler: ErrorHandler, schedulers: SchedulersProvider,
private val studentRepository: StudentRepository, errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val prefRepository: PreferencesRepository, private val prefRepository: PreferencesRepository,
private val syncManager: SyncManager, private val syncManager: SyncManager,
private val schedulers: SchedulersProvider,
private val analytics: FirebaseAnalyticsHelper private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<MainView>(errorHandler) { ) : BasePresenter<MainView>(errorHandler, studentRepository, schedulers) {
fun onAttachView(view: MainView, initMenu: MainView.MenuView?) { fun onAttachView(view: MainView, initMenu: MainView.MenuView?) {
super.onAttachView(view) super.onAttachView(view)
@ -34,7 +31,7 @@ class MainPresenter @Inject constructor(
} }
syncManager.startSyncWorker() syncManager.startSyncWorker()
analytics.logEvent(APP_OPEN, DESTINATION to initMenu?.name) analytics.logEvent("app_open", "destination" to initMenu?.name)
} }
fun onViewChange() { fun onViewChange() {
@ -80,28 +77,6 @@ class MainPresenter @Inject constructor(
} == true } == 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> { private fun getProperViewIndexes(initMenu: MainView.MenuView?): Pair<Int, Int> {
return when { return when {
initMenu?.id in 0..3 -> initMenu!!.id to -1 initMenu?.id in 0..3 -> initMenu!!.id to -1

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,9 @@
package io.github.wulkanowy.ui.modules.message.preview package io.github.wulkanowy.ui.modules.message.preview
import io.github.wulkanowy.data.db.entities.Message 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 val noSubjectString: String

View File

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

View File

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

View File

@ -13,7 +13,7 @@ import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.repositories.message.MessageFolder 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.main.MainActivity
import io.github.wulkanowy.ui.modules.message.MessageFragment import io.github.wulkanowy.ui.modules.message.MessageFragment
import io.github.wulkanowy.ui.modules.message.MessageItem 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 kotlinx.android.synthetic.main.fragment_message_tab.*
import javax.inject.Inject import javax.inject.Inject
class MessageTabFragment : BaseSessionFragment(), MessageTabView { class MessageTabFragment : BaseFragment(), MessageTabView {
@Inject @Inject
lateinit var presenter: MessageTabPresenter 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.MessageFolder
import io.github.wulkanowy.data.repositories.message.MessageRepository import io.github.wulkanowy.data.repositories.message.MessageRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.session.BaseSessionPresenter 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.ui.modules.message.MessageItem import io.github.wulkanowy.ui.modules.message.MessageItem
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
@ -14,12 +14,12 @@ import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class MessageTabPresenter @Inject constructor( class MessageTabPresenter @Inject constructor(
private val errorHandler: SessionErrorHandler, schedulers: SchedulersProvider,
private val schedulers: SchedulersProvider, errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val messageRepository: MessageRepository, private val messageRepository: MessageRepository,
private val studentRepository: StudentRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: FirebaseAnalyticsHelper
) : BaseSessionPresenter<MessageTabView>(errorHandler) { ) : BasePresenter<MessageTabView>(errorHandler, studentRepository, schedulers) {
lateinit var folder: MessageFolder lateinit var folder: MessageFolder

View File

@ -1,10 +1,10 @@
package io.github.wulkanowy.ui.modules.message.tab package io.github.wulkanowy.ui.modules.message.tab
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem 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 import io.github.wulkanowy.ui.modules.message.MessageItem
interface MessageTabView : BaseSessionView { interface MessageTabView : BaseView {
val noSubjectString: String 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: (MobileDevice, position: Int) -> Unit = { _, _ -> }
}

View File

@ -0,0 +1,114 @@
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.EmptyViewHelper
import eu.davidea.flexibleadapter.helpers.UndoHelper
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.R
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() {
mobileDevicesRecycler.run {
layoutManager = SmoothScrollLinearLayoutManager(context)
adapter = devicesAdapter
addItemDecoration(FlexibleItemDecoration(context)
.withDefaultDivider()
.withDrawDividerOnLastItem(false)
)
}
EmptyViewHelper.create(devicesAdapter, mobileDevicesEmpty)
mobileDevicesSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
mobileDeviceAddButton.setOnClickListener { presenter.onRegisterDevice() }
devicesAdapter.run {
isPermanentDelete = false
onDeviceUnregisterListener = { device, position ->
val onActionListener = object : UndoHelper.OnActionListener {
override fun onActionConfirmed(action: Int, event: Int) {
presenter.onUnregister(device)
}
override fun onActionCanceled(action: Int, positions: MutableList<Int>?) {
devicesAdapter.restoreDeletedItems()
}
}
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 updateData(data: List<MobileDeviceItem>) {
devicesAdapter.updateDataSet(data)
}
override fun clearData() {
devicesAdapter.clear()
}
override fun hideRefresh() {
mobileDevicesSwipe.isRefreshing = false
}
override fun showProgress(show: Boolean) {
mobileDevicesProgress.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()
}
}

View File

@ -0,0 +1,53 @@
package io.github.wulkanowy.ui.modules.mobiledevice
import android.view.View
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.viewholders.FlexibleViewHolder
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.MobileDevice
import io.github.wulkanowy.utils.toFormattedString
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.item_mobile_device.*
class MobileDeviceItem(val device: MobileDevice) : AbstractFlexibleItem<MobileDeviceItem.ViewHolder>() {
override fun getLayoutRes() = R.layout.item_mobile_device
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<*>>): ViewHolder {
return ViewHolder(view, adapter)
}
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<*>>, holder: ViewHolder, position: Int, payloads: MutableList<Any>?) {
holder.apply {
mobileDeviceItemDate.text = device.date.toFormattedString("dd.MM.yyyy HH:mm:ss")
mobileDeviceItemName.text = device.name
mobileDeviceItemUnregister.setOnClickListener {
(adapter as MobileDeviceAdapter).onDeviceUnregisterListener(device, holder.flexibleAdapterPosition)
}
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as MobileDeviceItem
if (device.id != other.device.id) return false
return true
}
override fun hashCode(): Int {
var result = device.hashCode()
result = 31 * result + device.id.toInt()
return result
}
class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer {
override val containerView: View
get() = contentView
}
}

View File

@ -0,0 +1,12 @@
package io.github.wulkanowy.ui.modules.mobiledevice
import dagger.Module
import dagger.Provides
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
@Module
class MobileDeviceModule {
@Provides
fun provideMobileDeviceFlexibleAdapter() = MobileDeviceAdapter<AbstractFlexibleItem<*>>()
}

View File

@ -0,0 +1,94 @@
package io.github.wulkanowy.ui.modules.mobiledevice
import io.github.wulkanowy.data.db.entities.MobileDevice
import io.github.wulkanowy.data.repositories.mobiledevice.MobileDeviceRepository
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.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import timber.log.Timber
import javax.inject.Inject
class MobileDevicePresenter @Inject constructor(
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val mobileDeviceRepository: MobileDeviceRepository,
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<MobileDeviceView>(errorHandler, studentRepository, schedulers) {
override fun onAttachView(view: MobileDeviceView) {
super.onAttachView(view)
view.initView()
Timber.i("Mobile device view was initialized")
loadData()
}
fun onSwipeRefresh() {
loadData(true)
}
private fun loadData(forceRefresh: Boolean = false) {
Timber.i("Loading mobile devices data started")
disposable.add(studentRepository.getCurrentStudent()
.flatMap { semesterRepository.getCurrentSemester(it) }
.flatMap { mobileDeviceRepository.getDevices(it, forceRefresh) }
.map { items -> items.map { MobileDeviceItem(it) } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
hideRefresh()
showProgress(false)
enableSwipe(true)
}
}.subscribe({
Timber.i("Loading mobile devices result: Success")
view?.run {
updateData(it)
showContent(it.isNotEmpty())
}
analytics.logEvent("load_devices", "items" to it.size, "force_refresh" to forceRefresh)
}) {
Timber.i("Loading mobile devices result: An exception occurred")
errorHandler.dispatch(it)
})
}
fun onRegisterDevice() {
view?.showTokenDialog()
}
fun onUnregister(device: MobileDevice) {
Timber.i("Unregister device started")
disposable.add(studentRepository.getCurrentStudent()
.flatMap { semesterRepository.getCurrentSemester(it) }
.flatMap { semester ->
mobileDeviceRepository.unregisterDevice(semester, device)
.flatMap { mobileDeviceRepository.getDevices(semester, it) }
}
.map { items -> items.map { MobileDeviceItem(it) } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
showProgress(false)
enableSwipe(true)
}
}
.subscribe({
Timber.i("Unregister device result: Success")
view?.run {
updateData(it)
showContent(it.isNotEmpty())
}
}) {
Timber.i("Unregister device result: An exception occurred")
errorHandler.dispatch(it)
}
)
}
}

View File

@ -0,0 +1,24 @@
package io.github.wulkanowy.ui.modules.mobiledevice
import io.github.wulkanowy.ui.base.BaseView
interface MobileDeviceView : BaseView {
val isViewEmpty: Boolean
fun initView()
fun updateData(data: List<MobileDeviceItem>)
fun hideRefresh()
fun clearData()
fun showProgress(show: Boolean)
fun enableSwipe(enable: Boolean)
fun showContent(show: Boolean)
fun showTokenDialog()
}

View File

@ -0,0 +1,89 @@
package io.github.wulkanowy.ui.modules.mobiledevice.token
import android.graphics.BitmapFactory
import android.os.Bundle
import android.util.Base64
import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
import android.widget.Toast
import dagger.android.support.DaggerDialogFragment
import io.github.wulkanowy.R
import io.github.wulkanowy.data.pojos.MobileDeviceToken
import io.github.wulkanowy.data.repositories.mobiledevice.MobileDeviceRemote
import io.github.wulkanowy.ui.base.BaseActivity
import kotlinx.android.synthetic.main.dialog_mobile_device.*
import javax.inject.Inject
class MobileDeviceTokenDialog : DaggerDialogFragment(), MobileDeviceTokenVIew {
@Inject
lateinit var presenter: MobileDeviceTokenPresenter
companion object {
fun newInstance(): MobileDeviceTokenDialog = MobileDeviceTokenDialog()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, 0)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.dialog_mobile_device, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
presenter.onAttachView(this)
}
override fun initView() {
mobileDeviceDialogClose.setOnClickListener { dismiss() }
}
override fun updateData(token: MobileDeviceToken) {
mobileDeviceDialogToken.text = token.token
mobileDeviceDialogSymbol.text = token.symbol
mobileDeviceDialogPin.text = token.pin
mobileDeviceQr.setImageBitmap(Base64.decode(token.qr, Base64.DEFAULT).let {
BitmapFactory.decodeByteArray(it, 0, it.size)
})
}
override fun hideLoading() {
mobileDeviceDialogProgress.visibility = GONE
}
override fun showContent() {
mobileDeviceDialogContent.visibility = VISIBLE
}
override fun closeDialog() {
dismiss()
}
override fun showError(text: String, error: Throwable) {
showMessage(text)
}
override fun showMessage(text: String) {
Toast.makeText(context, text, Toast.LENGTH_LONG).show()
}
override fun showExpiredDialog() {
(activity as? BaseActivity<*>)?.showExpiredDialog()
}
override fun openClearLoginView() {
(activity as? BaseActivity<*>)?.openClearLoginView()
}
override fun onDestroyView() {
presenter.onDetachView()
super.onDestroyView()
}
}

View File

@ -0,0 +1,51 @@
package io.github.wulkanowy.ui.modules.mobiledevice.token
import io.github.wulkanowy.data.repositories.mobiledevice.MobileDeviceRepository
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.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import timber.log.Timber
import javax.inject.Inject
class MobileDeviceTokenPresenter @Inject constructor(
schedulers: SchedulersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val mobileDeviceRepository: MobileDeviceRepository,
private val analytics: FirebaseAnalyticsHelper
) : BasePresenter<MobileDeviceTokenVIew>(errorHandler, studentRepository, schedulers) {
override fun onAttachView(view: MobileDeviceTokenVIew) {
super.onAttachView(view)
view.initView()
Timber.i("Mobile device view was initialized")
loadData()
}
private fun loadData() {
Timber.i("Mobile device registration data started")
disposable.add(studentRepository.getCurrentStudent()
.flatMap { semesterRepository.getCurrentSemester(it) }
.flatMap { mobileDeviceRepository.getToken(it) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally { view?.hideLoading() }
.subscribe({
Timber.i("Mobile device registration result: Success")
view?.run {
updateData(it)
showContent()
}
analytics.logEvent("device_register", "symbol" to it.token.substring(0, 3))
}) {
Timber.i("Mobile device registration result: An exception occurred")
view?.closeDialog()
errorHandler.dispatch(it)
}
)
}
}

View File

@ -0,0 +1,17 @@
package io.github.wulkanowy.ui.modules.mobiledevice.token
import io.github.wulkanowy.data.pojos.MobileDeviceToken
import io.github.wulkanowy.ui.base.BaseView
interface MobileDeviceTokenVIew : BaseView {
fun initView()
fun hideLoading()
fun showContent()
fun closeDialog()
fun updateData(token: MobileDeviceToken)
}

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