Compare commits
No commits in common. "1.1.2" and "0.24.3" have entirely different histories.
303 changed files with 2052 additions and 18811 deletions
210
.github/workflows/test.yml
vendored
210
.github/workflows/test.yml
vendored
|
@ -10,14 +10,39 @@ on:
|
|||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Pre-build
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: fkirc/skip-duplicate-actions@master
|
||||
- uses: actions/checkout@v2
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}
|
||||
- name: Build
|
||||
run: ./gradlew --build-cache compileFdroidDebugUnitTestKotlin preFdroidDebugAndroidTestBuild dexBuilderFdroidDebugAndroidTest packageFdroidDebug packageFdroidDebugAndroidTest
|
||||
- name: Prepare build cache
|
||||
run: tar -cf prebuild.tar .build-cache .gradle app/build
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: prebuild.tar
|
||||
path: prebuild.tar
|
||||
|
||||
unit-tests:
|
||||
name: Unit tests
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
needs: [ build ]
|
||||
steps:
|
||||
- uses: fkirc/skip-duplicate-actions@master
|
||||
- uses: actions/checkout@v2
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
|
@ -27,6 +52,11 @@ jobs:
|
|||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: prebuild.tar
|
||||
- name: Extract build cache
|
||||
run: tar -xf prebuild.tar
|
||||
- name: Unit tests
|
||||
run: |
|
||||
./gradlew --build-cache -Pcoverage testFdroidDebugUnitTest --stacktrace
|
||||
|
@ -35,12 +65,49 @@ jobs:
|
|||
with:
|
||||
flags: unit
|
||||
|
||||
instrumentation-tests:
|
||||
name: Instrumentation tests
|
||||
runs-on: macOS-latest
|
||||
timeout-minutes: 15
|
||||
needs: [ build ]
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
api-level: [21, 29]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: prebuild.tar
|
||||
- name: Extract build cache
|
||||
run: tar -xf prebuild.tar
|
||||
- name: Instrumentation tests
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: ${{ matrix.api-level }}
|
||||
arch: x86
|
||||
script: |
|
||||
./gradlew --build-cache -Pcoverage connectedFdroidDebugAndroidTest --stacktrace
|
||||
./gradlew --build-cache -Pcoverage jacocoTestReport --stacktrace
|
||||
- uses: codecov/codecov-action@v1
|
||||
with:
|
||||
flags: instrumented,api-${{ matrix.api-level }}
|
||||
|
||||
deploy-google-play:
|
||||
name: Deploy to google play
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
environment: google-play
|
||||
needs: [ unit-tests ]
|
||||
needs: [ build, unit-tests, instrumentation-tests ]
|
||||
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
@ -53,6 +120,11 @@ jobs:
|
|||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: prebuild.tar
|
||||
- name: Extract build cache
|
||||
run: tar -xf prebuild.tar
|
||||
- name: Decrypt keys
|
||||
env:
|
||||
ENCRYPT_KEY: ${{ secrets.ENCRYPT_KEY }}
|
||||
|
@ -68,135 +140,3 @@ jobs:
|
|||
PLAY_SERVICE_ACCOUNT_EMAIL: ${{ secrets.PLAY_SERVICE_ACCOUNT_EMAIL }}
|
||||
PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }}
|
||||
run: ./gradlew publishPlayRelease -PenableFirebase --stacktrace;
|
||||
|
||||
deploy-appcenter:
|
||||
name: Deploy to App Center
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
environment: app-center
|
||||
if: github.ref != 'refs/heads/develop'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}
|
||||
- name: Set run number with offset
|
||||
env:
|
||||
BUILD_NUMBER_OFFSET: ${{ secrets.BUILD_NUMBER_OFFSET }}
|
||||
run: echo "RUN_NUMBER=$((GITHUB_RUN_NUMBER+BUILD_NUMBER_OFFSET))" >> $GITHUB_ENV
|
||||
- name: Prepare build configuration
|
||||
run: |
|
||||
sed -i -e "s#applicationIdSuffix \".dev\"#applicationIdSuffix \".${GITHUB_HEAD_REF//[-.\/]/_}\"#" app/build.gradle
|
||||
sed -i -e "s#.dev\"#.${GITHUB_HEAD_REF//[-.\/]/_}\"#" app/src/debug/google-services.json
|
||||
sed -i -e "s#.dev\"#.${GITHUB_HEAD_REF//[-.\/]/_}\"#" app/src/debug/agconnect-services.json
|
||||
sed -i -e '/versionNameSuffix/d' app/build.gradle
|
||||
- name: Add signing config
|
||||
run: |
|
||||
cat >> app/build.gradle <<EOF
|
||||
android.signingConfigs.debug {
|
||||
storeFile file("bitrise.jks")
|
||||
storePassword System.getenv("BITRISE_KEYSTORE_PASSWORD")
|
||||
keyAlias System.getenv("BITRISE_KEY_ALIAS")
|
||||
keyPassword System.getenv("BITRISE_KEY_PASSWORD")
|
||||
}
|
||||
EOF
|
||||
- name: Decrypt keys
|
||||
env:
|
||||
BITRISE_ENCRYPT_KEY: ${{ secrets.BITRISE_ENCRYPT_KEY }}
|
||||
run: |
|
||||
gpg --yes --batch --passphrase=$BITRISE_ENCRYPT_KEY ./app/bitrise.jks.gpg
|
||||
- name: Bump version
|
||||
uses: chkfung/android-version-actions@v1.1
|
||||
with:
|
||||
gradlePath: app/build.gradle
|
||||
versionCode: ${{ env.RUN_NUMBER }}
|
||||
versionName: ${{ env.RUN_NUMBER }}-${{ github.head_ref }}
|
||||
- name: Build apk
|
||||
env:
|
||||
BITRISE_KEYSTORE_PASSWORD: ${{ secrets.BITRISE_KEYSTORE_PASSWORD }}
|
||||
BITRISE_KEY_ALIAS: ${{ secrets.BITRISE_KEY_ALIAS }}
|
||||
BITRISE_KEY_PASSWORD: ${{ secrets.BITRISE_KEY_PASSWORD }}
|
||||
run: ./gradlew assembleFdroidDebug --stacktrace
|
||||
- name: Upload apk to github artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: wulkanowyDEV-${{ env.RUN_NUMBER }}.apk
|
||||
path: app/build/outputs/apk/fdroid/debug/app-fdroid-debug.apk
|
||||
- name: Deploy to app center
|
||||
uses: wzieba/AppCenter-Github-Action@v1
|
||||
with:
|
||||
appName: wulkanowy/wulkanowy
|
||||
token: ${{ secrets.APP_CENTER_TOKEN }}
|
||||
group: Testers
|
||||
file: app/build/outputs/apk/fdroid/debug/app-fdroid-debug.apk
|
||||
notifyTesters: true
|
||||
debug: true
|
||||
|
||||
deploy-app-distribution:
|
||||
name: Deploy to AppDistribution
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
environment: app-distribution
|
||||
if: github.ref == 'refs/heads/develop'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}
|
||||
- name: Set run number with offset
|
||||
env:
|
||||
BUILD_NUMBER_OFFSET: ${{ secrets.BUILD_NUMBER_OFFSET }}
|
||||
run: echo "RUN_NUMBER=$((GITHUB_RUN_NUMBER+BUILD_NUMBER_OFFSET))" >> $GITHUB_ENV
|
||||
- name: Add signing config
|
||||
run: |
|
||||
cat >> app/build.gradle <<EOF
|
||||
android.signingConfigs.debug {
|
||||
storeFile file("bitrise.jks")
|
||||
storePassword System.getenv("BITRISE_KEYSTORE_PASSWORD")
|
||||
keyAlias System.getenv("BITRISE_KEY_ALIAS")
|
||||
keyPassword System.getenv("BITRISE_KEY_PASSWORD")
|
||||
}
|
||||
EOF
|
||||
- name: Decrypt keys
|
||||
env:
|
||||
BITRISE_ENCRYPT_KEY: ${{ secrets.BITRISE_ENCRYPT_KEY }}
|
||||
BITRISE_SERVICES_ENCRYPT_KEY: ${{ secrets.BITRISE_SERVICES_ENCRYPT_KEY }}
|
||||
run: |
|
||||
gpg --yes --batch --passphrase=$BITRISE_SERVICES_ENCRYPT_KEY ./app/src/debug/google-services.json.gpg
|
||||
gpg --yes --batch --passphrase=$BITRISE_ENCRYPT_KEY ./app/bitrise.jks.gpg
|
||||
- name: Bump version
|
||||
uses: chkfung/android-version-actions@v1.1
|
||||
with:
|
||||
gradlePath: app/build.gradle
|
||||
versionCode: ${{ env.RUN_NUMBER }}
|
||||
versionName: ${{ env.RUN_NUMBER }}
|
||||
- name: Build apk
|
||||
env:
|
||||
BITRISE_KEYSTORE_PASSWORD: ${{ secrets.BITRISE_KEYSTORE_PASSWORD }}
|
||||
BITRISE_KEY_ALIAS: ${{ secrets.BITRISE_KEY_ALIAS }}
|
||||
BITRISE_KEY_PASSWORD: ${{ secrets.BITRISE_KEY_PASSWORD }}
|
||||
run: ./gradlew assemblePlayDebug -PenableFirebase --stacktrace
|
||||
- name: Upload apk to github artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: wulkanowyDEV-${{ env.RUN_NUMBER }}-dev.apk
|
||||
path: app/build/outputs/apk/play/debug/app-play-debug.apk
|
||||
- name: Deploy to app distribution
|
||||
uses: wzieba/Firebase-Distribution-Github-Action@v1
|
||||
with:
|
||||
appId: ${{ secrets.FIREBASE_APP_ID }}
|
||||
token: ${{ secrets.FIREBASE_TOKEN }}
|
||||
groups: discord
|
||||
file: app/build/outputs/apk/play/debug/app-play-debug.apk
|
||||
|
|
15
.idea/codeStyles/Project.xml
generated
15
.idea/codeStyles/Project.xml
generated
|
@ -18,9 +18,18 @@
|
|||
</option>
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
|
||||
<option name="CONTINUATION_INDENT_IN_PARAMETER_LISTS" value="false" />
|
||||
<option name="CONTINUATION_INDENT_IN_ARGUMENT_LISTS" value="false" />
|
||||
<option name="CONTINUATION_INDENT_FOR_EXPRESSION_BODIES" value="false" />
|
||||
<option name="CONTINUATION_INDENT_FOR_CHAINED_CALLS" value="false" />
|
||||
<option name="CONTINUATION_INDENT_IN_SUPERTYPE_LISTS" value="false" />
|
||||
<option name="CONTINUATION_INDENT_IN_IF_CONDITIONS" value="false" />
|
||||
<option name="CONTINUATION_INDENT_IN_ELVIS" value="false" />
|
||||
<option name="WRAP_ELVIS_EXPRESSIONS" value="0" />
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<MarkdownNavigatorCodeStyleSettings>
|
||||
<option name="RIGHT_MARGIN" value="72" />
|
||||
</MarkdownNavigatorCodeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
|
@ -134,11 +143,13 @@
|
|||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
|
||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
||||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="0" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
|
||||
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
|
||||
<option name="EXTENDS_LIST_WRAP" value="1" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
|
|
11
README.en.md
11
README.en.md
|
@ -12,7 +12,7 @@ Unofficial android VULCAN UONET+ register client for both students and their par
|
|||
|
||||
## Features
|
||||
|
||||
* logging in using the email and password
|
||||
* logging in using the email and password OR using token and pin
|
||||
* functions from the register website:
|
||||
* grades
|
||||
* grade statistics
|
||||
|
@ -25,19 +25,15 @@ Unofficial android VULCAN UONET+ register client for both students and their par
|
|||
* homework
|
||||
* notes
|
||||
* lucky number
|
||||
* additional lessons
|
||||
* school conferences
|
||||
* student and school information
|
||||
* calculation of the average independently of school's preferences
|
||||
* notifications, e.g. about a new grade
|
||||
* support for multiple accounts with the ability to rename students
|
||||
* dark and black (AMOLED) theme
|
||||
* offline mode
|
||||
* no ads
|
||||
|
||||
## Download
|
||||
|
||||
You can download the current version from the Google Play, F-Droid or Huawei AppGallery store
|
||||
You can download the current beta version from the Google Play, F-Droid or Huawei AppGallery store
|
||||
|
||||
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
|
||||
alt="Get it on Google Play"
|
||||
|
@ -64,9 +60,6 @@ You can also download a [development version](https://wulkanowy.github.io/#downl
|
|||
|
||||
Please contribute to the project either by creating a PR or submitting an issue on GitHub.
|
||||
|
||||
For people interested in translating the application into different languages, we provide Crowdin
|
||||
https://crowdin.com/project/wulkanowy2
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details
|
||||
|
|
11
README.md
11
README.md
|
@ -12,7 +12,7 @@ Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica
|
|||
|
||||
## Funkcje
|
||||
|
||||
* logowanie za pomocą e-maila i hasła
|
||||
* logowanie za pomocą e-maila i hasła LUB tokena i pinu
|
||||
* funkcje ze strony internetowej dziennika:
|
||||
* oceny
|
||||
* statystyki ocen
|
||||
|
@ -25,19 +25,15 @@ Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica
|
|||
* zadania domowe
|
||||
* uwagi
|
||||
* szczęśliwy numerek
|
||||
* dodatkowe lekcje
|
||||
* zebrania w szkole
|
||||
* informacje o uczniu i szkole
|
||||
* obliczanie średniej niezależnie od preferencji szkoły
|
||||
* powiadomienia np. o nowej ocenie
|
||||
* obsługa wielu kont wraz z możliwością zmiany nazwy ucznia
|
||||
* ciemny i czarny (AMOLED) motyw
|
||||
* tryb offilne
|
||||
* brak reklam
|
||||
|
||||
## Pobierz
|
||||
|
||||
Aktualną wersję możesz pobrać ze sklepu Google Play, F-Droid lub Huawei AppGallery
|
||||
Aktualną wersję beta możesz pobrać ze sklepu Google Play, F-Droid lub Huawei AppGallery
|
||||
|
||||
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
|
||||
alt="Pobierz z Google Play"
|
||||
|
@ -65,9 +61,6 @@ Możesz także pobrać [wersję rozwojową](https://wulkanowy.github.io/#downloa
|
|||
|
||||
Wnieś swój wkład w projekt, tworząc PR lub wysyłając issue na GitHub.
|
||||
|
||||
Dla osób zainteresowanych tłumaczeniem aplikacji na różne języki udostępniamy Crowdina
|
||||
https://crowdin.com/project/wulkanowy2
|
||||
|
||||
## Licencja
|
||||
|
||||
Ten projekt udostępniany jest na licencji Apache License 2.0 - szczegóły w pliku [LICENSE](LICENSE)
|
||||
|
|
BIN
app/bitrise.jks
BIN
app/bitrise.jks
Binary file not shown.
Binary file not shown.
|
@ -5,30 +5,25 @@ apply plugin: 'dagger.hilt.android.plugin'
|
|||
apply plugin: 'com.google.firebase.crashlytics'
|
||||
apply plugin: 'com.github.triplet.play'
|
||||
apply plugin: 'com.mikepenz.aboutlibraries.plugin'
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
apply plugin: 'com.huawei.agconnect'
|
||||
apply from: 'jacoco.gradle'
|
||||
apply from: 'sonarqube.gradle'
|
||||
apply from: 'hooks.gradle'
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion '30.0.3'
|
||||
buildToolsVersion '30.0.2'
|
||||
|
||||
defaultConfig {
|
||||
applicationId "io.github.wulkanowy"
|
||||
testApplicationId "io.github.tests.wulkanowy"
|
||||
minSdkVersion 17
|
||||
targetSdkVersion 30
|
||||
versionCode 88
|
||||
versionName "1.1.2"
|
||||
versionCode 81
|
||||
versionName "0.24.3"
|
||||
multiDexEnabled true
|
||||
resValue "string", "app_name", "Wulkanowy"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
|
||||
resValue "string", "app_name", "Wulkanowy"
|
||||
buildConfigField "long", "BUILD_TIMESTAMP", String.valueOf(System.currentTimeMillis())
|
||||
|
||||
manifestPlaceholders = [
|
||||
firebase_enabled: project.hasProperty("enableFirebase")
|
||||
]
|
||||
|
@ -43,8 +38,7 @@ android {
|
|||
}
|
||||
|
||||
sourceSets {
|
||||
// https://github.com/robolectric/robolectric/issues/3928#issuecomment-395309991
|
||||
debug.assets.srcDirs += files("$projectDir/schemas".toString())
|
||||
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
|
@ -106,10 +100,6 @@ android {
|
|||
disable 'HardwareIds'
|
||||
}
|
||||
|
||||
testOptions.unitTests {
|
||||
includeAndroidResources = true
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
coreLibraryDesugaringEnabled true
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
|
@ -117,7 +107,6 @@ android {
|
|||
}
|
||||
|
||||
kotlinOptions {
|
||||
useIR = true
|
||||
jvmTarget = "1.8"
|
||||
freeCompilerArgs += ["-Xopt-in=kotlin.RequiresOptIn", "-Xjvm-default=all"]
|
||||
}
|
||||
|
@ -136,32 +125,31 @@ play {
|
|||
serviceAccountEmail = System.getenv("PLAY_SERVICE_ACCOUNT_EMAIL") ?: "jan@fakelog.cf"
|
||||
serviceAccountCredentials = file('key.p12')
|
||||
defaultToAppBundles = false
|
||||
track = 'production'
|
||||
track = 'alpha'
|
||||
updatePriority = 5
|
||||
}
|
||||
|
||||
ext {
|
||||
work_manager = "2.5.0"
|
||||
work_hilt = "1.0.0-beta01"
|
||||
room = "2.3.0-beta03"
|
||||
work_manager = "2.4.0"
|
||||
room = "2.2.6"
|
||||
chucker = "3.4.0"
|
||||
mockk = "1.10.6"
|
||||
mockk = "1.10.5"
|
||||
moshi = "1.11.0"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "io.github.wulkanowy:sdk:1.1.2"
|
||||
implementation "io.github.wulkanowy:sdk:0.24.1"
|
||||
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1'
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2'
|
||||
|
||||
implementation "androidx.core:core-ktx:1.3.2"
|
||||
implementation "androidx.activity:activity-ktx:1.2.1"
|
||||
implementation "androidx.activity:activity-ktx:1.1.0"
|
||||
implementation "androidx.appcompat:appcompat:1.2.0"
|
||||
implementation "androidx.appcompat:appcompat-resources:1.2.0"
|
||||
implementation "androidx.fragment:fragment-ktx:1.3.1"
|
||||
implementation "androidx.fragment:fragment-ktx:1.2.5"
|
||||
implementation "androidx.annotation:annotation:1.1.0"
|
||||
implementation "androidx.multidex:multidex:2.0.1"
|
||||
|
||||
|
@ -171,15 +159,15 @@ dependencies {
|
|||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
|
||||
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
|
||||
implementation "com.google.android.material:material:1.3.0"
|
||||
implementation "com.github.wulkanowy:material-chips-input:2.2.0"
|
||||
implementation "com.google.android.material:material:1.2.1"
|
||||
implementation "com.github.wulkanowy:material-chips-input:2.1.1"
|
||||
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
|
||||
implementation 'com.mikhaellopez:circularimageview:4.2.0'
|
||||
implementation "me.zhanghai.android.materialprogressbar:library:1.6.1"
|
||||
|
||||
implementation "androidx.work:work-runtime-ktx:$work_manager"
|
||||
playImplementation "androidx.work:work-gcm:$work_manager"
|
||||
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.0"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
|
||||
|
||||
implementation "androidx.room:room-runtime:$room"
|
||||
implementation "androidx.room:room-ktx:$room"
|
||||
|
@ -187,12 +175,12 @@ dependencies {
|
|||
|
||||
implementation "com.google.dagger:hilt-android:$hilt_version"
|
||||
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
|
||||
implementation "androidx.hilt:hilt-work:$work_hilt"
|
||||
kapt "androidx.hilt:hilt-compiler:$work_hilt"
|
||||
implementation 'androidx.hilt:hilt-work:1.0.0-alpha02'
|
||||
kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02'
|
||||
|
||||
implementation "com.aurelhubert:ahbottomnavigation:2.3.4"
|
||||
implementation "com.ncapdevi:frag-nav:3.3.0"
|
||||
implementation "com.github.YarikSOffice:lingver:1.3.0"
|
||||
implementation "com.github.YarikSOffice:lingver:1.2.2"
|
||||
|
||||
implementation "com.squareup.moshi:moshi:$moshi"
|
||||
implementation "com.squareup.moshi:moshi-adapters:$moshi"
|
||||
|
@ -206,7 +194,7 @@ dependencies {
|
|||
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
|
||||
implementation 'me.xdrop:fuzzywuzzy:1.3.1'
|
||||
|
||||
playImplementation platform('com.google.firebase:firebase-bom:26.7.0')
|
||||
playImplementation platform('com.google.firebase:firebase-bom:26.3.0')
|
||||
playImplementation 'com.google.firebase:firebase-analytics-ktx'
|
||||
playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx'
|
||||
playImplementation "com.google.firebase:firebase-inappmessaging-ktx"
|
||||
|
@ -215,30 +203,25 @@ dependencies {
|
|||
playImplementation 'com.google.android.play:core-ktx:1.8.1'
|
||||
playImplementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
|
||||
|
||||
hmsImplementation 'com.huawei.hms:hianalytics:5.2.0.300'
|
||||
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.5.0.300'
|
||||
hmsImplementation 'com.huawei.hms:hianalytics:5.1.0.301'
|
||||
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.4.2.301'
|
||||
|
||||
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
|
||||
|
||||
debugImplementation "com.github.ChuckerTeam.Chucker:library:$chucker"
|
||||
debugImplementation "com.amitshekhar.android:debug-db:1.0.6"
|
||||
|
||||
testImplementation "junit:junit:4.13.2"
|
||||
testImplementation "junit:junit:4.13.1"
|
||||
testImplementation "io.mockk:mockk:$mockk"
|
||||
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.2'
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
|
||||
testImplementation 'org.robolectric:robolectric:4.5.1'
|
||||
testImplementation "androidx.test:runner:1.3.0"
|
||||
testImplementation "androidx.test.ext:junit:1.1.2"
|
||||
testImplementation "androidx.test:core:1.3.0"
|
||||
testImplementation "androidx.room:room-testing:$room"
|
||||
testImplementation "com.google.dagger:hilt-android-testing:$hilt_version"
|
||||
kaptTest "com.google.dagger:hilt-android-compiler:$hilt_version"
|
||||
|
||||
androidTestImplementation "androidx.test:core:1.3.0"
|
||||
androidTestImplementation "androidx.test:runner:1.3.0"
|
||||
androidTestImplementation "androidx.test.ext:junit:1.1.2"
|
||||
androidTestImplementation "io.mockk:mockk-android:$mockk"
|
||||
androidTestImplementation "androidx.room:room-testing:$room"
|
||||
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
}
|
||||
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
apply plugin: 'com.huawei.agconnect'
|
||||
|
|
29
app/proguard-rules.pro
vendored
29
app/proguard-rules.pro
vendored
|
@ -1,21 +1,33 @@
|
|||
# General
|
||||
# Optimizations
|
||||
-optimizationpasses 5
|
||||
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
|
||||
-dontusemixedcaseclassnames
|
||||
-dontskipnonpubliclibraryclasses
|
||||
-dontskipnonpubliclibraryclassmembers
|
||||
-dontpreverify
|
||||
-dontobfuscate
|
||||
-allowaccessmodification
|
||||
-repackageclasses ''
|
||||
-verbose
|
||||
|
||||
|
||||
#Config for wulkanowy
|
||||
#Keep all wulkanowy files
|
||||
-keep class io.github.wulkanowy.** {*;}
|
||||
|
||||
|
||||
#Config for firebase crashlitycs
|
||||
#Config for anallitycs
|
||||
-keepattributes *Annotation*
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
-keep class com.crashlytics.** {*;}
|
||||
-keep public class * extends java.lang.Exception
|
||||
-dontwarn com.crashlytics.**
|
||||
|
||||
|
||||
#Config for Okio and OkHttp
|
||||
-dontwarn javax.annotation.**
|
||||
#Config for OkHttp
|
||||
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
|
||||
-dontwarn org.codehaus.mojo.animal_sniffer.*
|
||||
-dontwarn okhttp3.internal.platform.ConscryptPlatform
|
||||
-dontwarn javax.annotation.**
|
||||
|
||||
|
||||
#Config for MPAndroidChart
|
||||
|
@ -24,3 +36,10 @@
|
|||
|
||||
#Config for Material Components
|
||||
-keep class com.google.android.material.tabs.** { *; }
|
||||
|
||||
|
||||
#Config for About Libraries
|
||||
-keep class .R
|
||||
-keep class **.R$* {
|
||||
<fields>;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,11 @@
|
|||
package io.github.wulkanowy.data
|
||||
|
||||
import io.github.wulkanowy.utils.DispatchersProvider
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
||||
class TestDispatchersProvider : DispatchersProvider() {
|
||||
|
||||
override val backgroundThread: CoroutineDispatcher
|
||||
get() = Dispatchers.Unconfined
|
||||
}
|
|
@ -9,7 +9,6 @@ import androidx.test.core.app.ApplicationProvider
|
|||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import io.github.wulkanowy.data.db.AppDatabase
|
||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import org.junit.Rule
|
||||
|
||||
abstract class AbstractMigrationTest {
|
||||
|
@ -25,16 +24,12 @@ abstract class AbstractMigrationTest {
|
|||
|
||||
fun getMigratedRoomDatabase(): AppDatabase {
|
||||
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||
val database = Room.databaseBuilder(
|
||||
ApplicationProvider.getApplicationContext(),
|
||||
AppDatabase::class.java,
|
||||
dbName
|
||||
).addMigrations(
|
||||
*AppDatabase.getMigrations(
|
||||
SharedPrefProvider(PreferenceManager.getDefaultSharedPreferences(context)),
|
||||
AppInfo()
|
||||
val database = Room.databaseBuilder(ApplicationProvider.getApplicationContext(),
|
||||
AppDatabase::class.java, dbName)
|
||||
.addMigrations(*AppDatabase.getMigrations(SharedPrefProvider(PreferenceManager
|
||||
.getDefaultSharedPreferences(context)))
|
||||
)
|
||||
).build()
|
||||
.build()
|
||||
// close the database and release any stream resources when the test finishes
|
||||
helper.closeWhenFinished(database)
|
||||
return database
|
|
@ -2,20 +2,14 @@ package io.github.wulkanowy.data.db.migrations
|
|||
|
||||
import android.content.ContentValues
|
||||
import android.database.sqlite.SQLiteDatabase.CONFLICT_FAIL
|
||||
import android.os.Build
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import dagger.hilt.android.testing.HiltTestApplication
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@HiltAndroidTest
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [Build.VERSION_CODES.O_MR1], application = HiltTestApplication::class)
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class Migration12Test : AbstractMigrationTest() {
|
||||
|
||||
@Test
|
|
@ -2,26 +2,17 @@ package io.github.wulkanowy.data.db.migrations
|
|||
|
||||
import android.content.ContentValues
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.os.Build
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import dagger.hilt.android.testing.HiltTestApplication
|
||||
import io.github.wulkanowy.data.db.Converters
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import java.time.LocalDate.of
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@HiltAndroidTest
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [Build.VERSION_CODES.O_MR1], application = HiltTestApplication::class)
|
||||
class Migration13Test : AbstractMigrationTest() {
|
||||
|
||||
@Test
|
|
@ -2,21 +2,12 @@ package io.github.wulkanowy.data.db.migrations
|
|||
|
||||
import android.content.ContentValues
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.os.Build
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import dagger.hilt.android.testing.HiltTestApplication
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import kotlin.random.Random
|
||||
|
||||
@HiltAndroidTest
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [Build.VERSION_CODES.O_MR1], application = HiltTestApplication::class)
|
||||
class Migration27Test : AbstractMigrationTest() {
|
||||
|
||||
@Test
|
|
@ -1,19 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#FFFFFF"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<group
|
||||
android:scaleX="0.92"
|
||||
android:scaleY="0.92"
|
||||
android:translateX="0.96"
|
||||
android:translateY="0.96">
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M3.9512,2A2,2 0,0 0,2 4L2,18A2,2 0,0 0,4 20L10.0996,20C11.3596,21.24 13.09,22 15,22A7,7 0,0 0,15.7988 21.9551L15.7988,19.7832A4.85,4.85 0,0 1,15 19.8496C12.32,19.8496 10.1504,17.68 10.1504,15A4.85,4.85 0,0 1,15 10.1504C17.4677,10.1504 19.4978,11.9912 19.8047,14.375C20.566,14.3758 21.3108,14.5325 21.9922,14.834C21.9491,12.9905 21.2036,11.3226 20,10.0996L20,4A2,2 0,0 0,18 2L4,2A2,2 0,0 0,3.9512 2zM4,5L10,5L10,8L4,8L4,5zM12,5L18,5L18,8L12,8L12,5zM4,10L10.0996,10C9.2596,10.82 8.6291,11.85 8.2891,13L4,13L4,10zM14,12L14,15.6895L15.7988,16.7266L15.7988,14.9922L15.5,14.8203L15.5,12L14,12zM4,15L8,15C8,16.07 8.2399,17.09 8.6699,18L4,18L4,15z" />
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="m17.298,24v-8.1249h2.5c0.7143,0 1.3523,0.1618 1.9141,0.4855 0.5655,0.3199 1.0063,0.7775 1.3225,1.3728 0.3162,0.5915 0.4743,1.2649 0.4743,2.0201v0.3739c0,0.7552 -0.1562,1.4267 -0.4687,2.0145 -0.3088,0.5878 -0.7459,1.0435 -1.3114,1.3672C21.1633,23.8326 20.5253,23.9963 19.8148,24ZM18.9721,17.2311v5.4241h0.8091c0.6548,0 1.1551,-0.2139 1.5011,-0.6417 0.346,-0.4278 0.5227,-1.0398 0.5301,-1.8359v-0.4297c0,-0.8259 -0.1711,-1.4509 -0.5134,-1.875 -0.3423,-0.4278 -0.8426,-0.6417 -1.5011,-0.6417z" />
|
||||
</group>
|
||||
</vector>
|
Binary file not shown.
Before Width: | Height: | Size: 426 B |
Binary file not shown.
Before Width: | Height: | Size: 335 B |
Binary file not shown.
Before Width: | Height: | Size: 519 B |
Binary file not shown.
Before Width: | Height: | Size: 700 B |
|
@ -18,18 +18,6 @@
|
|||
<action android:name="android.intent.action.VIEW" />
|
||||
<data android:scheme="https" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<data android:scheme="mailto" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<data android:scheme="tel" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<data android:scheme="geo" />
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
<application
|
||||
|
@ -56,7 +44,7 @@
|
|||
android:name=".ui.modules.login.LoginActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/login_title"
|
||||
android:theme="@style/WulkanowyTheme.Login"
|
||||
android:theme="@style/WulkanowyTheme.NoActionBar"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
<activity
|
||||
android:name=".ui.modules.main.MainActivity"
|
||||
|
@ -68,7 +56,7 @@
|
|||
android:name=".ui.modules.message.send.SendMessageActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/send_message_title"
|
||||
android:theme="@style/WulkanowyTheme.MessageSend"
|
||||
android:theme="@style/WulkanowyTheme.NoActionBar"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
<activity
|
||||
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"
|
||||
|
|
|
@ -34,17 +34,5 @@
|
|||
{
|
||||
"displayName": "Mateusz Idziejczak",
|
||||
"githubUsername": "Luncenok"
|
||||
},
|
||||
{
|
||||
"displayName": "MRmlik12",
|
||||
"githubUsername": "MRmlik12"
|
||||
},
|
||||
{
|
||||
"displayName": "Damian Czupryn",
|
||||
"githubUsername": "Daxxxis"
|
||||
},
|
||||
{
|
||||
"displayName": "Kamil Studziński",
|
||||
"githubUsername": "studzinskik"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
package io.github.wulkanowy
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.util.Log.DEBUG
|
||||
import android.util.Log.INFO
|
||||
import android.util.Log.VERBOSE
|
||||
import android.webkit.WebView
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.hilt.work.HiltWorkerFactory
|
||||
import androidx.multidex.MultiDex
|
||||
import androidx.work.Configuration
|
||||
|
@ -48,26 +45,24 @@ class WulkanowyApp : Application(), Configuration.Provider {
|
|||
MultiDex.install(this)
|
||||
}
|
||||
|
||||
@SuppressLint("UnsafeOptInUsageWarning")
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
FragmentManager.enableNewStateManager(false)
|
||||
initializeAppLanguage()
|
||||
Lingver.init(this)
|
||||
themeManager.applyDefaultTheme()
|
||||
|
||||
initLogging()
|
||||
fixWebViewLocale()
|
||||
logCurrentLanguage()
|
||||
}
|
||||
|
||||
private fun initLogging() {
|
||||
if (appInfo.isDebug) {
|
||||
Timber.plant(DebugLogTree())
|
||||
Timber.plant(
|
||||
FileLoggerTree.Builder()
|
||||
.withFileName("wulkanowy.%g.log")
|
||||
.withDirName(applicationContext.filesDir.absolutePath)
|
||||
.withFileLimit(10)
|
||||
.withMinPriority(DEBUG)
|
||||
.build()
|
||||
Timber.plant(FileLoggerTree.Builder()
|
||||
.withFileName("wulkanowy.%g.log")
|
||||
.withDirName(applicationContext.filesDir.absolutePath)
|
||||
.withFileLimit(10)
|
||||
.withMinPriority(DEBUG)
|
||||
.build()
|
||||
)
|
||||
} else {
|
||||
Timber.plant(CrashLogExceptionTree())
|
||||
|
@ -76,24 +71,14 @@ class WulkanowyApp : Application(), Configuration.Provider {
|
|||
registerActivityLifecycleCallbacks(ActivityLifecycleLogger())
|
||||
}
|
||||
|
||||
private fun initializeAppLanguage() {
|
||||
Lingver.init(this)
|
||||
|
||||
if (preferencesRepository.appLanguage == "system") {
|
||||
Lingver.getInstance().setFollowSystemLocale(this)
|
||||
analyticsHelper.logEvent("language", "startup" to appInfo.systemLanguage)
|
||||
private fun logCurrentLanguage() {
|
||||
val newLang = if (preferencesRepository.appLanguage == "system") {
|
||||
appInfo.systemLanguage
|
||||
} else {
|
||||
analyticsHelper.logEvent("language", "startup" to preferencesRepository.appLanguage)
|
||||
preferencesRepository.appLanguage
|
||||
}
|
||||
}
|
||||
|
||||
private fun fixWebViewLocale() {
|
||||
//https://stackoverflow.com/questions/40398528/android-webview-language-changes-abruptly-on-android-7-0-and-above
|
||||
try {
|
||||
WebView(this).destroy()
|
||||
} catch (e: Exception) {
|
||||
//Ignore exceptions
|
||||
}
|
||||
analyticsHelper.logEvent("language", "startup" to newLang)
|
||||
}
|
||||
|
||||
override fun getWorkManagerConfiguration() = Configuration.Builder()
|
||||
|
|
|
@ -17,7 +17,6 @@ import io.github.wulkanowy.data.db.AppDatabase
|
|||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import timber.log.Timber
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
@ -34,21 +33,17 @@ internal class RepositoryModule {
|
|||
setSimpleHttpLogger { Timber.d(it) }
|
||||
|
||||
// for debug only
|
||||
addInterceptor(
|
||||
ChuckerInterceptor.Builder(context)
|
||||
.collector(chuckerCollector)
|
||||
.alwaysReadResponseBody(true)
|
||||
.build(), network = true
|
||||
addInterceptor(ChuckerInterceptor.Builder(context)
|
||||
.collector(chuckerCollector)
|
||||
.alwaysReadResponseBody(true)
|
||||
.build(), network = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideChuckerCollector(
|
||||
@ApplicationContext context: Context,
|
||||
prefRepository: PreferencesRepository
|
||||
): ChuckerCollector {
|
||||
fun provideChuckerCollector(@ApplicationContext context: Context, prefRepository: PreferencesRepository): ChuckerCollector {
|
||||
return ChuckerCollector(
|
||||
context = context,
|
||||
showNotification = prefRepository.isDebugNotificationEnable,
|
||||
|
@ -58,11 +53,7 @@ internal class RepositoryModule {
|
|||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideDatabase(
|
||||
@ApplicationContext context: Context,
|
||||
sharedPrefProvider: SharedPrefProvider,
|
||||
appInfo: AppInfo
|
||||
) = AppDatabase.newInstance(context, sharedPrefProvider, appInfo)
|
||||
fun provideDatabase(@ApplicationContext context: Context, sharedPrefProvider: SharedPrefProvider) = AppDatabase.newInstance(context, sharedPrefProvider)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
|
@ -74,8 +65,7 @@ internal class RepositoryModule {
|
|||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideSharedPref(@ApplicationContext context: Context): SharedPreferences =
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
fun provideSharedPref(@ApplicationContext context: Context): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
|
@ -99,8 +89,7 @@ internal class RepositoryModule {
|
|||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideGradeSemesterStatisticsDao(database: AppDatabase) =
|
||||
database.gradeSemesterStatisticsDao
|
||||
fun provideGradeSemesterStatisticsDao(database: AppDatabase) = database.gradeSemesterStatisticsDao
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
|
@ -177,8 +166,4 @@ internal class RepositoryModule {
|
|||
@Singleton
|
||||
@Provides
|
||||
fun provideTimetableAdditionalDao(database: AppDatabase) = database.timetableAdditionalDao
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideStudentInfoDao(database: AppDatabase) = database.studentInfoDao
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import androidx.room.Room
|
|||
import androidx.room.RoomDatabase
|
||||
import androidx.room.RoomDatabase.JournalMode.TRUNCATE
|
||||
import androidx.room.TypeConverters
|
||||
import androidx.room.migration.Migration
|
||||
import io.github.wulkanowy.data.db.dao.AttendanceDao
|
||||
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
|
||||
import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
|
||||
|
@ -27,7 +28,6 @@ import io.github.wulkanowy.data.db.dao.ReportingUnitDao
|
|||
import io.github.wulkanowy.data.db.dao.SchoolDao
|
||||
import io.github.wulkanowy.data.db.dao.SemesterDao
|
||||
import io.github.wulkanowy.data.db.dao.StudentDao
|
||||
import io.github.wulkanowy.data.db.dao.StudentInfoDao
|
||||
import io.github.wulkanowy.data.db.dao.SubjectDao
|
||||
import io.github.wulkanowy.data.db.dao.TeacherDao
|
||||
import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao
|
||||
|
@ -53,7 +53,6 @@ import io.github.wulkanowy.data.db.entities.ReportingUnit
|
|||
import io.github.wulkanowy.data.db.entities.School
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentInfo
|
||||
import io.github.wulkanowy.data.db.entities.Subject
|
||||
import io.github.wulkanowy.data.db.entities.Teacher
|
||||
import io.github.wulkanowy.data.db.entities.Timetable
|
||||
|
@ -81,18 +80,12 @@ import io.github.wulkanowy.data.db.migrations.Migration28
|
|||
import io.github.wulkanowy.data.db.migrations.Migration29
|
||||
import io.github.wulkanowy.data.db.migrations.Migration3
|
||||
import io.github.wulkanowy.data.db.migrations.Migration30
|
||||
import io.github.wulkanowy.data.db.migrations.Migration31
|
||||
import io.github.wulkanowy.data.db.migrations.Migration32
|
||||
import io.github.wulkanowy.data.db.migrations.Migration33
|
||||
import io.github.wulkanowy.data.db.migrations.Migration34
|
||||
import io.github.wulkanowy.data.db.migrations.Migration35
|
||||
import io.github.wulkanowy.data.db.migrations.Migration4
|
||||
import io.github.wulkanowy.data.db.migrations.Migration5
|
||||
import io.github.wulkanowy.data.db.migrations.Migration6
|
||||
import io.github.wulkanowy.data.db.migrations.Migration7
|
||||
import io.github.wulkanowy.data.db.migrations.Migration8
|
||||
import io.github.wulkanowy.data.db.migrations.Migration9
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
|
@ -123,7 +116,6 @@ import javax.inject.Singleton
|
|||
School::class,
|
||||
Conference::class,
|
||||
TimetableAdditional::class,
|
||||
StudentInfo::class,
|
||||
],
|
||||
version = AppDatabase.VERSION_SCHEMA,
|
||||
exportSchema = true
|
||||
|
@ -132,55 +124,50 @@ import javax.inject.Singleton
|
|||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
companion object {
|
||||
const val VERSION_SCHEMA = 35
|
||||
const val VERSION_SCHEMA = 30
|
||||
|
||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
||||
Migration2(),
|
||||
Migration3(),
|
||||
Migration4(),
|
||||
Migration5(),
|
||||
Migration6(),
|
||||
Migration7(),
|
||||
Migration8(),
|
||||
Migration9(),
|
||||
Migration10(),
|
||||
Migration11(),
|
||||
Migration12(),
|
||||
Migration13(),
|
||||
Migration14(),
|
||||
Migration15(),
|
||||
Migration16(),
|
||||
Migration17(),
|
||||
Migration18(),
|
||||
Migration19(sharedPrefProvider),
|
||||
Migration20(),
|
||||
Migration21(),
|
||||
Migration22(),
|
||||
Migration23(),
|
||||
Migration24(),
|
||||
Migration25(),
|
||||
Migration26(),
|
||||
Migration27(),
|
||||
Migration28(),
|
||||
Migration29(),
|
||||
Migration30(),
|
||||
Migration31(),
|
||||
Migration32(),
|
||||
Migration33(),
|
||||
Migration34(),
|
||||
Migration35(appInfo)
|
||||
)
|
||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> {
|
||||
return arrayOf(
|
||||
Migration2(),
|
||||
Migration3(),
|
||||
Migration4(),
|
||||
Migration5(),
|
||||
Migration6(),
|
||||
Migration7(),
|
||||
Migration8(),
|
||||
Migration9(),
|
||||
Migration10(),
|
||||
Migration11(),
|
||||
Migration12(),
|
||||
Migration13(),
|
||||
Migration14(),
|
||||
Migration15(),
|
||||
Migration16(),
|
||||
Migration17(),
|
||||
Migration18(),
|
||||
Migration19(sharedPrefProvider),
|
||||
Migration20(),
|
||||
Migration21(),
|
||||
Migration22(),
|
||||
Migration23(),
|
||||
Migration24(),
|
||||
Migration25(),
|
||||
Migration26(),
|
||||
Migration27(),
|
||||
Migration28(),
|
||||
Migration29(),
|
||||
Migration30(),
|
||||
)
|
||||
}
|
||||
|
||||
fun newInstance(
|
||||
context: Context,
|
||||
sharedPrefProvider: SharedPrefProvider,
|
||||
appInfo: AppInfo
|
||||
) = Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database")
|
||||
.setJournalMode(TRUNCATE)
|
||||
.fallbackToDestructiveMigrationFrom(VERSION_SCHEMA + 1)
|
||||
.fallbackToDestructiveMigrationOnDowngrade()
|
||||
.addMigrations(*getMigrations(sharedPrefProvider, appInfo))
|
||||
.build()
|
||||
fun newInstance(context: Context, sharedPrefProvider: SharedPrefProvider): AppDatabase {
|
||||
return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database")
|
||||
.setJournalMode(TRUNCATE)
|
||||
.fallbackToDestructiveMigrationFrom(VERSION_SCHEMA + 1)
|
||||
.fallbackToDestructiveMigrationOnDowngrade()
|
||||
.addMigrations(*getMigrations(sharedPrefProvider))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
abstract val studentDao: StudentDao
|
||||
|
@ -232,6 +219,4 @@ abstract class AppDatabase : RoomDatabase() {
|
|||
abstract val conferenceDao: ConferenceDao
|
||||
|
||||
abstract val timetableAdditionalDao: TimetableAdditionalDao
|
||||
|
||||
abstract val studentInfoDao: StudentInfoDao
|
||||
}
|
||||
|
|
|
@ -13,7 +13,4 @@ interface LuckyNumberDao : BaseDao<LuckyNumber> {
|
|||
|
||||
@Query("SELECT * FROM LuckyNumbers WHERE student_id = :studentId AND date = :date")
|
||||
fun load(studentId: Int, date: LocalDate): Flow<LuckyNumber?>
|
||||
|
||||
@Query("SELECT * FROM LuckyNumbers WHERE student_id = :studentId AND date >= :start AND date <= :end")
|
||||
fun getAll(studentId: Int, start: LocalDate, end: LocalDate): Flow<List<LuckyNumber>>
|
||||
}
|
||||
|
|
|
@ -6,9 +6,7 @@ import androidx.room.Insert
|
|||
import androidx.room.OnConflictStrategy.ABORT
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import androidx.room.Update
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
@ -22,14 +20,11 @@ interface StudentDao {
|
|||
@Delete
|
||||
suspend fun delete(student: Student)
|
||||
|
||||
@Update(entity = Student::class)
|
||||
suspend fun update(studentNickAndAvatar: StudentNickAndAvatar)
|
||||
|
||||
@Query("SELECT * FROM Students WHERE is_current = 1")
|
||||
suspend fun loadCurrent(): Student?
|
||||
|
||||
@Query("SELECT * FROM Students WHERE id = :id")
|
||||
suspend fun loadById(id: Long): Student?
|
||||
suspend fun loadById(id: Int): Student?
|
||||
|
||||
@Query("SELECT * FROM Students")
|
||||
suspend fun loadAll(): List<Student>
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import io.github.wulkanowy.data.db.entities.StudentInfo
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
@Dao
|
||||
interface StudentInfoDao : BaseDao<StudentInfo> {
|
||||
|
||||
@Query("SELECT * FROM StudentInfo WHERE student_id = :studentId")
|
||||
fun loadStudentInfo(studentId: Int): Flow<StudentInfo?>
|
||||
}
|
|
@ -10,7 +10,7 @@ import java.time.LocalDateTime
|
|||
data class Message(
|
||||
|
||||
@ColumnInfo(name = "student_id")
|
||||
val studentId: Long,
|
||||
val studentId: Int,
|
||||
|
||||
@ColumnInfo(name = "real_id")
|
||||
val realId: Int,
|
||||
|
|
|
@ -7,13 +7,7 @@ import androidx.room.PrimaryKey
|
|||
import java.io.Serializable
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@Entity(
|
||||
tableName = "Students",
|
||||
indices = [Index(
|
||||
value = ["email", "symbol", "student_id", "school_id", "class_id"],
|
||||
unique = true
|
||||
)]
|
||||
)
|
||||
@Entity(tableName = "Students", indices = [Index(value = ["email", "symbol", "student_id", "school_id", "class_id"], unique = true)])
|
||||
data class Student(
|
||||
|
||||
@ColumnInfo(name = "scrapper_base_url")
|
||||
|
@ -58,7 +52,7 @@ data class Student(
|
|||
@ColumnInfo(name = "school_id")
|
||||
val schoolSymbol: String,
|
||||
|
||||
@ColumnInfo(name = "school_short")
|
||||
@ColumnInfo(name ="school_short")
|
||||
val schoolShortName: String,
|
||||
|
||||
@ColumnInfo(name = "school_name")
|
||||
|
@ -79,9 +73,4 @@ data class Student(
|
|||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Long = 0
|
||||
|
||||
var nick = ""
|
||||
|
||||
@ColumnInfo(name = "avatar_color")
|
||||
var avatarColor = 0L
|
||||
}
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.entities
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import io.github.wulkanowy.data.enums.Gender
|
||||
import java.io.Serializable
|
||||
import java.time.LocalDate
|
||||
|
||||
@Entity(tableName = "StudentInfo")
|
||||
data class StudentInfo(
|
||||
|
||||
@ColumnInfo(name = "student_id")
|
||||
val studentId: Int,
|
||||
|
||||
@ColumnInfo(name = "full_name")
|
||||
val fullName: String,
|
||||
|
||||
@ColumnInfo(name = "first_name")
|
||||
val firstName: String,
|
||||
|
||||
@ColumnInfo(name = "second_name")
|
||||
val secondName: String,
|
||||
|
||||
val surname: String,
|
||||
|
||||
@ColumnInfo(name = "birth_date")
|
||||
val birthDate: LocalDate,
|
||||
|
||||
@ColumnInfo(name = "birth_place")
|
||||
val birthPlace: String,
|
||||
|
||||
val gender: Gender,
|
||||
|
||||
@ColumnInfo(name = "has_polish_citizenship")
|
||||
val hasPolishCitizenship: Boolean,
|
||||
|
||||
@ColumnInfo(name = "family_name")
|
||||
val familyName: String,
|
||||
|
||||
@ColumnInfo(name = "parents_names")
|
||||
val parentsNames: String,
|
||||
|
||||
val address: String,
|
||||
|
||||
@ColumnInfo(name = "registered_address")
|
||||
val registeredAddress: String,
|
||||
|
||||
@ColumnInfo(name = "correspondence_address")
|
||||
val correspondenceAddress: String,
|
||||
|
||||
@ColumnInfo(name = "phone_number")
|
||||
val phoneNumber: String,
|
||||
|
||||
@ColumnInfo(name = "cell_phone_number")
|
||||
val cellPhoneNumber: String,
|
||||
|
||||
val email: String,
|
||||
|
||||
@Embedded(prefix = "first_guardian_")
|
||||
val firstGuardian: StudentGuardian?,
|
||||
|
||||
@Embedded(prefix = "second_guardian_")
|
||||
val secondGuardian: StudentGuardian?
|
||||
|
||||
) : Serializable {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Long = 0
|
||||
}
|
||||
|
||||
data class StudentGuardian(
|
||||
|
||||
@ColumnInfo(name = "full_name")
|
||||
val fullName: String,
|
||||
|
||||
val kinship: String,
|
||||
|
||||
val address: String,
|
||||
|
||||
val phones: String,
|
||||
|
||||
val email: String
|
||||
) : Serializable
|
|
@ -1,20 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.entities
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import java.io.Serializable
|
||||
|
||||
@Entity
|
||||
data class StudentNickAndAvatar(
|
||||
|
||||
val nick: String,
|
||||
|
||||
@ColumnInfo(name = "avatar_color")
|
||||
var avatarColor: Long
|
||||
|
||||
) : Serializable {
|
||||
|
||||
@PrimaryKey
|
||||
var id: Long = 0
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration31 : Migration(30, 31) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL(
|
||||
"""CREATE TABLE IF NOT EXISTS StudentInfo (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
student_id INTEGER NOT NULL,
|
||||
full_name TEXT NOT NULL,
|
||||
first_name TEXT NOT NULL,
|
||||
second_name TEXT NOT NULL,
|
||||
surname TEXT NOT NULL,
|
||||
birth_date INTEGER NOT NULL,
|
||||
birth_place TEXT NOT NULL,
|
||||
gender TEXT NOT NULL,
|
||||
has_polish_citizenship INTEGER NOT NULL,
|
||||
family_name TEXT NOT NULL,
|
||||
parents_names TEXT NOT NULL,
|
||||
address TEXT NOT NULL,
|
||||
registered_address TEXT NOT NULL,
|
||||
correspondence_address TEXT NOT NULL,
|
||||
phone_number TEXT NOT NULL,
|
||||
cell_phone_number TEXT NOT NULL,
|
||||
email TEXT NOT NULL,
|
||||
first_guardian_full_name TEXT NOT NULL,
|
||||
first_guardian_kinship TEXT NOT NULL,
|
||||
first_guardian_address TEXT NOT NULL,
|
||||
first_guardian_phones TEXT NOT NULL,
|
||||
first_guardian_email TEXT NOT NULL,
|
||||
second_guardian_full_name TEXT NOT NULL,
|
||||
second_guardian_kinship TEXT NOT NULL,
|
||||
second_guardian_address TEXT NOT NULL,
|
||||
second_guardian_phones TEXT NOT NULL,
|
||||
second_guardian_email TEXT NOT NULL)
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration32 : Migration(31, 32) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE Students ADD COLUMN nick TEXT NOT NULL DEFAULT \"\"")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration33 : Migration(32, 33) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP TABLE IF EXISTS StudentInfo")
|
||||
|
||||
database.execSQL(
|
||||
"""CREATE TABLE IF NOT EXISTS StudentInfo (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
student_id INTEGER NOT NULL,
|
||||
full_name TEXT NOT NULL,
|
||||
first_name TEXT NOT NULL,
|
||||
second_name TEXT NOT NULL,
|
||||
surname TEXT NOT NULL,
|
||||
birth_date INTEGER NOT NULL,
|
||||
birth_place TEXT NOT NULL,
|
||||
gender TEXT NOT NULL,
|
||||
has_polish_citizenship INTEGER NOT NULL,
|
||||
family_name TEXT NOT NULL,
|
||||
parents_names TEXT NOT NULL,
|
||||
address TEXT NOT NULL,
|
||||
registered_address TEXT NOT NULL,
|
||||
correspondence_address TEXT NOT NULL,
|
||||
phone_number TEXT NOT NULL,
|
||||
cell_phone_number TEXT NOT NULL,
|
||||
email TEXT NOT NULL,
|
||||
first_guardian_full_name TEXT,
|
||||
first_guardian_kinship TEXT,
|
||||
first_guardian_address TEXT,
|
||||
first_guardian_phones TEXT,
|
||||
first_guardian_email TEXT,
|
||||
second_guardian_full_name TEXT,
|
||||
second_guardian_kinship TEXT,
|
||||
second_guardian_address TEXT,
|
||||
second_guardian_phones TEXT,
|
||||
second_guardian_email TEXT)
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration34 : Migration(33, 34) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DELETE FROM ReportingUnits")
|
||||
database.execSQL("DELETE FROM Recipients")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.core.database.getLongOrNull
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
|
||||
class Migration35(private val appInfo: AppInfo) : Migration(34, 35) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE Students ADD COLUMN `avatar_color` INTEGER NOT NULL DEFAULT 0")
|
||||
|
||||
val studentsCursor = database.query("SELECT * FROM Students")
|
||||
|
||||
while (studentsCursor.moveToNext()) {
|
||||
val studentId = studentsCursor.getLongOrNull(0)
|
||||
database.execSQL(
|
||||
"""UPDATE Students
|
||||
SET avatar_color = ${appInfo.defaultColorsForAvatar.random()}
|
||||
WHERE id = $studentId"""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
package io.github.wulkanowy.data.enums
|
||||
|
||||
enum class Gender { MALE, FEMALE }
|
|
@ -5,9 +5,10 @@ import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
|||
import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.pojos.GradeStatisticsItem
|
||||
import io.github.wulkanowy.sdk.pojo.GradePointsStatistics as SdkGradePointsStatistics
|
||||
import io.github.wulkanowy.sdk.pojo.GradeStatisticsSemester as SdkGradeStatisticsSemester
|
||||
import io.github.wulkanowy.ui.modules.grade.statistics.ViewType
|
||||
import io.github.wulkanowy.sdk.pojo.GradeStatisticsSubject as SdkGradeStatisticsSubject
|
||||
import io.github.wulkanowy.sdk.pojo.GradeStatisticsSemester as SdkGradeStatisticsSemester
|
||||
import io.github.wulkanowy.sdk.pojo.GradePointsStatistics as SdkGradePointsStatistics
|
||||
|
||||
@JvmName("mapToEntitiesSubject")
|
||||
fun List<SdkGradeStatisticsSubject>.mapToEntities(semester: Semester) = map {
|
||||
|
@ -50,7 +51,7 @@ fun List<SdkGradePointsStatistics>.mapToEntities(semester: Semester) = map {
|
|||
|
||||
fun List<GradePartialStatistics>.mapPartialToStatisticItems() = filterNot { it.classAmounts.isEmpty() }.map {
|
||||
GradeStatisticsItem(
|
||||
type = GradeStatisticsItem.DataType.PARTIAL,
|
||||
type = ViewType.PARTIAL,
|
||||
average = it.classAverage,
|
||||
partial = it,
|
||||
points = null,
|
||||
|
@ -60,7 +61,7 @@ fun List<GradePartialStatistics>.mapPartialToStatisticItems() = filterNot { it.c
|
|||
|
||||
fun List<GradeSemesterStatistics>.mapSemesterToStatisticItems() = filterNot { it.amounts.isEmpty() }.map {
|
||||
GradeStatisticsItem(
|
||||
type = GradeStatisticsItem.DataType.SEMESTER,
|
||||
type = ViewType.SEMESTER,
|
||||
partial = null,
|
||||
points = null,
|
||||
average = "",
|
||||
|
@ -70,7 +71,7 @@ fun List<GradeSemesterStatistics>.mapSemesterToStatisticItems() = filterNot { it
|
|||
|
||||
fun List<GradePointsStatistics>.mapPointsToStatisticsItems() = map {
|
||||
GradeStatisticsItem(
|
||||
type = GradeStatisticsItem.DataType.POINTS,
|
||||
type = ViewType.POINTS,
|
||||
partial = null,
|
||||
semester = null,
|
||||
average = "",
|
||||
|
|
|
@ -4,14 +4,14 @@ import io.github.wulkanowy.data.db.entities.Message
|
|||
import io.github.wulkanowy.data.db.entities.MessageAttachment
|
||||
import io.github.wulkanowy.data.db.entities.Recipient
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
|
||||
import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment
|
||||
import java.time.LocalDateTime
|
||||
import io.github.wulkanowy.sdk.pojo.Message as SdkMessage
|
||||
import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment
|
||||
import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
|
||||
|
||||
fun List<SdkMessage>.mapToEntities(student: Student) = map {
|
||||
Message(
|
||||
studentId = student.id,
|
||||
studentId = student.id.toInt(),
|
||||
realId = it.id ?: 0,
|
||||
messageId = it.messageId ?: 0,
|
||||
sender = it.sender?.name.orEmpty(),
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
package io.github.wulkanowy.data.mappers
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.StudentGuardian
|
||||
import io.github.wulkanowy.data.db.entities.StudentInfo
|
||||
import io.github.wulkanowy.data.enums.Gender
|
||||
import io.github.wulkanowy.sdk.pojo.StudentGuardian as SdkStudentGuardian
|
||||
import io.github.wulkanowy.sdk.pojo.StudentInfo as SdkStudentInfo
|
||||
|
||||
fun SdkStudentInfo.mapToEntity(semester: Semester) = StudentInfo(
|
||||
studentId = semester.studentId,
|
||||
fullName = fullName,
|
||||
firstName = firstName,
|
||||
secondName = secondName,
|
||||
surname = surname,
|
||||
birthDate = birthDate,
|
||||
birthPlace = birthPlace,
|
||||
gender = Gender.valueOf(gender.name),
|
||||
hasPolishCitizenship = hasPolishCitizenship,
|
||||
familyName = familyName,
|
||||
parentsNames = parentsNames,
|
||||
address = address,
|
||||
registeredAddress = registeredAddress,
|
||||
correspondenceAddress = correspondenceAddress,
|
||||
phoneNumber = phoneNumber,
|
||||
cellPhoneNumber = phoneNumber,
|
||||
email = email,
|
||||
firstGuardian = guardianFirst?.mapToEntity(),
|
||||
secondGuardian = guardianSecond?.mapToEntity()
|
||||
)
|
||||
|
||||
fun SdkStudentGuardian.mapToEntity() = StudentGuardian(
|
||||
fullName = fullName,
|
||||
kinship = kinship,
|
||||
address = address,
|
||||
phones = phones,
|
||||
email = email
|
||||
)
|
|
@ -5,7 +5,7 @@ import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
|||
import java.time.LocalDateTime
|
||||
import io.github.wulkanowy.sdk.pojo.Student as SdkStudent
|
||||
|
||||
fun List<SdkStudent>.mapToEntities(password: String = "", colors: List<Long>) = map {
|
||||
fun List<SdkStudent>.mapToEntities(password: String = "") = map {
|
||||
StudentWithSemesters(
|
||||
student = Student(
|
||||
email = it.email,
|
||||
|
@ -28,10 +28,8 @@ fun List<SdkStudent>.mapToEntities(password: String = "", colors: List<Long>) =
|
|||
mobileBaseUrl = it.mobileBaseUrl,
|
||||
privateKey = it.privateKey,
|
||||
certificateKey = it.certificateKey,
|
||||
loginMode = it.loginMode.name,
|
||||
).apply {
|
||||
avatarColor = colors.random()
|
||||
},
|
||||
loginMode = it.loginMode.name
|
||||
),
|
||||
semesters = it.semesters.mapToEntities(it.studentId)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -3,10 +3,11 @@ package io.github.wulkanowy.data.pojos
|
|||
import io.github.wulkanowy.data.db.entities.GradePartialStatistics
|
||||
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
||||
import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
|
||||
import io.github.wulkanowy.ui.modules.grade.statistics.ViewType
|
||||
|
||||
data class GradeStatisticsItem(
|
||||
|
||||
val type: DataType,
|
||||
val type: ViewType,
|
||||
|
||||
val average: String,
|
||||
|
||||
|
@ -15,11 +16,4 @@ data class GradeStatisticsItem(
|
|||
val semester: GradeSemesterStatistics?,
|
||||
|
||||
val points: GradePointsStatistics?
|
||||
|
||||
) {
|
||||
enum class DataType {
|
||||
SEMESTER,
|
||||
PARTIAL,
|
||||
POINTS,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -14,7 +14,6 @@ import io.github.wulkanowy.utils.monday
|
|||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import io.github.wulkanowy.utils.sunday
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import java.time.LocalTime
|
||||
|
@ -28,12 +27,9 @@ class AttendanceRepository @Inject constructor(
|
|||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
||||
private val cacheKey = "attendance"
|
||||
|
||||
fun getAttendance(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) },
|
||||
query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday) },
|
||||
fetch = {
|
||||
|
|
|
@ -10,7 +10,6 @@ import io.github.wulkanowy.utils.getRefreshKey
|
|||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
@ -21,12 +20,9 @@ class AttendanceSummaryRepository @Inject constructor(
|
|||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
||||
private val cacheKey = "attendance_summary"
|
||||
|
||||
fun getAttendanceSummary(student: Student, semester: Semester, subjectId: Int, forceRefresh: Boolean) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) },
|
||||
query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId) },
|
||||
fetch = {
|
||||
|
|
|
@ -12,7 +12,6 @@ import io.github.wulkanowy.utils.monday
|
|||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import io.github.wulkanowy.utils.sunday
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import java.time.LocalDate
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
@ -24,12 +23,9 @@ class CompletedLessonsRepository @Inject constructor(
|
|||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
||||
private val cacheKey = "completed"
|
||||
|
||||
fun getCompletedLessons(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) },
|
||||
query = { completedLessonsDb.loadAll(semester.studentId, semester.diaryId, start.monday, end.sunday) },
|
||||
fetch = {
|
||||
|
|
|
@ -10,7 +10,6 @@ import io.github.wulkanowy.utils.getRefreshKey
|
|||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
@ -21,12 +20,9 @@ class ConferenceRepository @Inject constructor(
|
|||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
||||
private val cacheKey = "conference"
|
||||
|
||||
fun getConferences(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) },
|
||||
query = { conferenceDb.loadAll(semester.diaryId, student.studentId) },
|
||||
fetch = {
|
||||
|
|
|
@ -12,7 +12,6 @@ import io.github.wulkanowy.utils.init
|
|||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import io.github.wulkanowy.utils.startExamsDay
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import java.time.LocalDate
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
@ -24,12 +23,9 @@ class ExamRepository @Inject constructor(
|
|||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
||||
private val cacheKey = "exam"
|
||||
|
||||
fun getExams(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) },
|
||||
query = { examDb.loadAll(semester.diaryId, semester.studentId, start.startExamsDay, start.endExamsDay) },
|
||||
fetch = {
|
||||
|
|
|
@ -16,7 +16,6 @@ import io.github.wulkanowy.utils.uniqueSubtract
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import java.time.LocalDateTime
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
@ -29,20 +28,14 @@ class GradeRepository @Inject constructor(
|
|||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
||||
private val cacheKey = "grade"
|
||||
|
||||
fun getGrades(student: Student, semester: Semester, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = { (details, summaries) ->
|
||||
val isShouldBeRefreshed = refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester))
|
||||
details.isEmpty() || summaries.isEmpty() || forceRefresh || isShouldBeRefreshed
|
||||
},
|
||||
shouldFetch = { (details, summaries) -> details.isEmpty() || summaries.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) },
|
||||
query = {
|
||||
val detailsFlow = gradeDb.loadAll(semester.semesterId, semester.studentId)
|
||||
val summaryFlow = gradeSummaryDb.loadAll(semester.semesterId, semester.studentId)
|
||||
detailsFlow.combine(summaryFlow) { details, summaries -> details to summaries }
|
||||
gradeDb.loadAll(semester.semesterId, semester.studentId).combine(gradeSummaryDb.loadAll(semester.semesterId, semester.studentId)) { details, summaries ->
|
||||
details to summaries
|
||||
}
|
||||
},
|
||||
fetch = {
|
||||
val (details, summary) = sdk.init(student)
|
||||
|
@ -99,27 +92,19 @@ class GradeRepository @Inject constructor(
|
|||
}
|
||||
|
||||
fun getUnreadGrades(semester: Semester): Flow<List<Grade>> {
|
||||
return gradeDb.loadAll(semester.semesterId, semester.studentId).map {
|
||||
it.filter { grade -> !grade.isRead }
|
||||
}
|
||||
return gradeDb.loadAll(semester.semesterId, semester.studentId).map { it.filter { grade -> !grade.isRead } }
|
||||
}
|
||||
|
||||
fun getNotNotifiedGrades(semester: Semester): Flow<List<Grade>> {
|
||||
return gradeDb.loadAll(semester.semesterId, semester.studentId).map {
|
||||
it.filter { grade -> !grade.isNotified }
|
||||
}
|
||||
return gradeDb.loadAll(semester.semesterId, semester.studentId).map { it.filter { grade -> !grade.isNotified } }
|
||||
}
|
||||
|
||||
fun getNotNotifiedPredictedGrades(semester: Semester): Flow<List<GradeSummary>> {
|
||||
return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId).map {
|
||||
it.filter { gradeSummary -> !gradeSummary.isPredictedGradeNotified }
|
||||
}
|
||||
return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId).map { it.filter { gradeSummary -> !gradeSummary.isPredictedGradeNotified } }
|
||||
}
|
||||
|
||||
fun getNotNotifiedFinalGrades(semester: Semester): Flow<List<GradeSummary>> {
|
||||
return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId).map {
|
||||
it.filter { gradeSummary -> !gradeSummary.isFinalGradeNotified }
|
||||
}
|
||||
return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId).map { it.filter { gradeSummary -> !gradeSummary.isFinalGradeNotified } }
|
||||
}
|
||||
|
||||
suspend fun updateGrade(grade: Grade) {
|
||||
|
|
|
@ -17,7 +17,6 @@ import io.github.wulkanowy.utils.getRefreshKey
|
|||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
@ -31,16 +30,11 @@ class GradeStatisticsRepository @Inject constructor(
|
|||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
private val partialMutex = Mutex()
|
||||
private val semesterMutex = Mutex()
|
||||
private val pointsMutex = Mutex()
|
||||
|
||||
private val partialCacheKey = "grade_stats_partial"
|
||||
private val semesterCacheKey = "grade_stats_semester"
|
||||
private val pointsCacheKey = "grade_stats_points"
|
||||
|
||||
fun getGradesPartialStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource(
|
||||
mutex = partialMutex,
|
||||
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(partialCacheKey, semester)) },
|
||||
query = { gradePartialStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
||||
fetch = {
|
||||
|
@ -77,7 +71,6 @@ class GradeStatisticsRepository @Inject constructor(
|
|||
)
|
||||
|
||||
fun getGradesSemesterStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource(
|
||||
mutex = semesterMutex,
|
||||
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(semesterCacheKey, semester)) },
|
||||
query = { gradeSemesterStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
||||
fetch = {
|
||||
|
@ -119,7 +112,6 @@ class GradeStatisticsRepository @Inject constructor(
|
|||
)
|
||||
|
||||
fun getGradesPointsStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource(
|
||||
mutex = pointsMutex,
|
||||
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(pointsCacheKey, semester)) },
|
||||
query = { gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
||||
fetch = {
|
||||
|
|
|
@ -13,7 +13,6 @@ import io.github.wulkanowy.utils.monday
|
|||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import io.github.wulkanowy.utils.sunday
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import java.time.LocalDate
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
@ -25,12 +24,9 @@ class HomeworkRepository @Inject constructor(
|
|||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
||||
private val cacheKey = "homework"
|
||||
|
||||
fun getHomework(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) },
|
||||
query = { homeworkDb.loadAll(semester.semesterId, semester.studentId, start.monday, end.sunday) },
|
||||
fetch = {
|
||||
|
|
|
@ -9,8 +9,6 @@ import io.github.wulkanowy.utils.init
|
|||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDate.now
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
@ -21,10 +19,7 @@ class LuckyNumberRepository @Inject constructor(
|
|||
private val sdk: Sdk
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
||||
fun getLuckyNumber(student: Student, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = { it == null || forceRefresh },
|
||||
query = { luckyNumberDb.load(student.studentId, now()) },
|
||||
fetch = { sdk.init(student).getLuckyNumber(student.schoolShortName)?.mapToEntity(student) },
|
||||
|
@ -38,9 +33,6 @@ class LuckyNumberRepository @Inject constructor(
|
|||
}
|
||||
)
|
||||
|
||||
fun getLuckyNumberHistory(student: Student, start: LocalDate, end: LocalDate) =
|
||||
luckyNumberDb.getAll(student.studentId, start, end)
|
||||
|
||||
suspend fun getNotNotifiedLuckyNumber(student: Student) = luckyNumberDb.load(student.studentId, now()).map {
|
||||
if (it?.isNotified == false) it else null
|
||||
}.first()
|
||||
|
|
|
@ -20,7 +20,6 @@ import io.github.wulkanowy.utils.networkBoundResource
|
|||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import timber.log.Timber
|
||||
import java.time.LocalDateTime.now
|
||||
import javax.inject.Inject
|
||||
|
@ -34,13 +33,10 @@ class MessageRepository @Inject constructor(
|
|||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
||||
private val cacheKey = "message"
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun getMessages(student: Student, semester: Semester, folder: MessageFolder, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, student, folder)) },
|
||||
query = { messagesDb.loadAll(student.id.toInt(), folder.id) },
|
||||
fetch = { sdk.init(student).getMessages(Folder.valueOf(folder.name), now().minusMonths(3), now()).mapToEntities(student) },
|
||||
|
|
|
@ -13,7 +13,6 @@ import io.github.wulkanowy.utils.getRefreshKey
|
|||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
@ -24,12 +23,9 @@ class MobileDeviceRepository @Inject constructor(
|
|||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
||||
private val cacheKey = "devices"
|
||||
|
||||
fun getDevices(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, student)) },
|
||||
query = { mobileDb.loadAll(student.userLoginId.takeIf { it != 0 } ?: student.studentId) },
|
||||
fetch = {
|
||||
|
|
|
@ -13,7 +13,6 @@ import io.github.wulkanowy.utils.networkBoundResource
|
|||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
@ -24,12 +23,9 @@ class NoteRepository @Inject constructor(
|
|||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
||||
private val cacheKey = "note"
|
||||
|
||||
fun getNotes(student: Student, semester: Semester, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) },
|
||||
query = { noteDb.loadAll(student.studentId) },
|
||||
fetch = {
|
||||
|
|
|
@ -18,43 +18,26 @@ class PreferencesRepository @Inject constructor(
|
|||
get() = getString(R.string.pref_key_start_menu, R.string.pref_default_startup).toInt()
|
||||
|
||||
val isShowPresent: Boolean
|
||||
get() = getBoolean(
|
||||
R.string.pref_key_attendance_present,
|
||||
R.bool.pref_default_attendance_present
|
||||
)
|
||||
get() = getBoolean(R.string.pref_key_attendance_present, R.bool.pref_default_attendance_present)
|
||||
|
||||
val gradeAverageMode: GradeAverageMode
|
||||
get() = GradeAverageMode.getByValue(
|
||||
getString(
|
||||
R.string.pref_key_grade_average_mode,
|
||||
R.string.pref_default_grade_average_mode
|
||||
)
|
||||
)
|
||||
get() = GradeAverageMode.getByValue(getString(R.string.pref_key_grade_average_mode, R.string.pref_default_grade_average_mode))
|
||||
|
||||
val gradeAverageForceCalc: Boolean
|
||||
get() = getBoolean(
|
||||
R.string.pref_key_grade_average_force_calc,
|
||||
R.bool.pref_default_grade_average_force_calc
|
||||
)
|
||||
get() = getBoolean(R.string.pref_key_grade_average_force_calc, R.bool.pref_default_grade_average_force_calc)
|
||||
|
||||
val isGradeExpandable: Boolean
|
||||
get() = !getBoolean(R.string.pref_key_expand_grade, R.bool.pref_default_expand_grade)
|
||||
|
||||
val showAllSubjectsOnStatisticsList: Boolean
|
||||
get() = getBoolean(
|
||||
R.string.pref_key_grade_statistics_list,
|
||||
R.bool.pref_default_grade_statistics_list
|
||||
)
|
||||
get() = getBoolean(R.string.pref_key_grade_statistics_list, R.bool.pref_default_grade_statistics_list)
|
||||
|
||||
val appThemeKey = context.getString(R.string.pref_key_app_theme)
|
||||
val appTheme: String
|
||||
get() = getString(appThemeKey, R.string.pref_default_app_theme)
|
||||
|
||||
val gradeColorTheme: String
|
||||
get() = getString(
|
||||
R.string.pref_key_grade_color_scheme,
|
||||
R.string.pref_default_grade_color_scheme
|
||||
)
|
||||
get() = getString(R.string.pref_key_grade_color_scheme, R.string.pref_default_grade_color_scheme)
|
||||
|
||||
val appLanguageKey = context.getString(R.string.pref_key_app_language)
|
||||
val appLanguage
|
||||
|
@ -72,86 +55,50 @@ class PreferencesRepository @Inject constructor(
|
|||
val isServicesOnlyWifi: Boolean
|
||||
get() = getBoolean(servicesOnlyWifiKey, R.bool.pref_default_services_wifi_only)
|
||||
|
||||
val notificationsEnableKey = context.getString(R.string.pref_key_notifications_enable)
|
||||
val isNotificationsEnable: Boolean
|
||||
get() = getBoolean(notificationsEnableKey, R.bool.pref_default_notifications_enable)
|
||||
get() = getBoolean(R.string.pref_key_notifications_enable, R.bool.pref_default_notifications_enable)
|
||||
|
||||
val isUpcomingLessonsNotificationsEnableKey =
|
||||
context.getString(R.string.pref_key_notifications_upcoming_lessons_enable)
|
||||
val isUpcomingLessonsNotificationsEnableKey = context.getString(R.string.pref_key_notifications_upcoming_lessons_enable)
|
||||
val isUpcomingLessonsNotificationsEnable: Boolean
|
||||
get() = getBoolean(
|
||||
isUpcomingLessonsNotificationsEnableKey,
|
||||
R.bool.pref_default_notification_upcoming_lessons_enable
|
||||
)
|
||||
get() = getBoolean(isUpcomingLessonsNotificationsEnableKey, R.bool.pref_default_notification_upcoming_lessons_enable)
|
||||
|
||||
val isDebugNotificationEnableKey = context.getString(R.string.pref_key_notification_debug)
|
||||
val isDebugNotificationEnable: Boolean
|
||||
get() = getBoolean(isDebugNotificationEnableKey, R.bool.pref_default_notification_debug)
|
||||
|
||||
val gradePlusModifier: Double
|
||||
get() = getString(
|
||||
R.string.pref_key_grade_modifier_plus,
|
||||
R.string.pref_default_grade_modifier_plus
|
||||
).toDouble()
|
||||
get() = getString(R.string.pref_key_grade_modifier_plus, R.string.pref_default_grade_modifier_plus).toDouble()
|
||||
|
||||
val gradeMinusModifier: Double
|
||||
get() = getString(
|
||||
R.string.pref_key_grade_modifier_minus,
|
||||
R.string.pref_default_grade_modifier_minus
|
||||
).toDouble()
|
||||
get() = getString(R.string.pref_key_grade_modifier_minus, R.string.pref_default_grade_modifier_minus).toDouble()
|
||||
|
||||
val fillMessageContent: Boolean
|
||||
get() = getBoolean(
|
||||
R.string.pref_key_fill_message_content,
|
||||
R.bool.pref_default_fill_message_content
|
||||
)
|
||||
get() = getBoolean(R.string.pref_key_fill_message_content, R.bool.pref_default_fill_message_content)
|
||||
|
||||
val showGroupsInPlan: Boolean
|
||||
get() = getBoolean(
|
||||
R.string.pref_key_timetable_show_groups,
|
||||
R.bool.pref_default_timetable_show_groups
|
||||
)
|
||||
get() = getBoolean(R.string.pref_key_timetable_show_groups, R.bool.pref_default_timetable_show_groups)
|
||||
|
||||
val showWholeClassPlan: String
|
||||
get() = getString(
|
||||
R.string.pref_key_timetable_show_whole_class,
|
||||
R.string.pref_default_timetable_show_whole_class
|
||||
)
|
||||
get() = getString(R.string.pref_key_timetable_show_whole_class, R.string.pref_default_timetable_show_whole_class)
|
||||
|
||||
val gradeSortingMode: GradeSortingMode
|
||||
get() = GradeSortingMode.getByValue(
|
||||
getString(
|
||||
R.string.pref_key_grade_sorting_mode,
|
||||
R.string.pref_default_grade_sorting_mode
|
||||
)
|
||||
)
|
||||
get() = GradeSortingMode.getByValue(getString(R.string.pref_key_grade_sorting_mode, R.string.pref_default_grade_sorting_mode))
|
||||
|
||||
val showTimetableTimers: Boolean
|
||||
get() = getBoolean(
|
||||
R.string.pref_key_timetable_show_timers,
|
||||
R.bool.pref_default_timetable_show_timers
|
||||
)
|
||||
get() = getBoolean(R.string.pref_key_timetable_show_timers, R.bool.pref_default_timetable_show_timers)
|
||||
|
||||
var isHomeworkFullscreen: Boolean
|
||||
get() = getBoolean(
|
||||
R.string.pref_key_homework_fullscreen,
|
||||
R.bool.pref_default_homework_fullscreen
|
||||
)
|
||||
get() = getBoolean(R.string.pref_key_homework_fullscreen, R.bool.pref_default_homework_fullscreen)
|
||||
set(value) = sharedPref.edit().putBoolean("homework_fullscreen", value).apply()
|
||||
|
||||
val showSubjectsWithoutGrades: Boolean
|
||||
get() = getBoolean(
|
||||
R.string.pref_key_subjects_without_grades,
|
||||
R.bool.pref_default_subjects_without_grades
|
||||
)
|
||||
get() = getBoolean(R.string.pref_key_subjects_without_grades, R.bool.pref_default_subjects_without_grades)
|
||||
|
||||
private fun getString(id: Int, default: Int) = getString(context.getString(id), default)
|
||||
|
||||
private fun getString(id: String, default: Int) =
|
||||
sharedPref.getString(id, context.getString(default)) ?: context.getString(default)
|
||||
private fun getString(id: String, default: Int) = sharedPref.getString(id, context.getString(default)) ?: context.getString(default)
|
||||
|
||||
private fun getBoolean(id: Int, default: Int) = getBoolean(context.getString(id), default)
|
||||
|
||||
private fun getBoolean(id: String, default: Int) =
|
||||
sharedPref.getBoolean(id, context.resources.getBoolean(default))
|
||||
private fun getBoolean(id: String, default: Int) = sharedPref.getBoolean(id, context.resources.getBoolean(default))
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import io.github.wulkanowy.data.mappers.mapToEntity
|
|||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
@ -17,26 +16,16 @@ class SchoolRepository @Inject constructor(
|
|||
private val sdk: Sdk
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
||||
fun getSchoolInfo(student: Student, semester: Semester, forceRefresh: Boolean) =
|
||||
networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = { it == null || forceRefresh },
|
||||
query = { schoolDb.load(semester.studentId, semester.classId) },
|
||||
fetch = {
|
||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).getSchool()
|
||||
.mapToEntity(semester)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
if (old != null && new != old) {
|
||||
with(schoolDb) {
|
||||
deleteAll(listOf(old))
|
||||
insertAll(listOf(new))
|
||||
}
|
||||
} else if (old == null) {
|
||||
schoolDb.insertAll(listOf(new))
|
||||
}
|
||||
fun getSchoolInfo(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource(
|
||||
shouldFetch = { it == null || forceRefresh },
|
||||
query = { schoolDb.load(semester.studentId, semester.classId) },
|
||||
fetch = { sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).getSchool().mapToEntity(semester) },
|
||||
saveFetchResult = { old, new ->
|
||||
if (new != old && old != null) {
|
||||
schoolDb.deleteAll(listOf(old))
|
||||
schoolDb.insertAll(listOf(new))
|
||||
}
|
||||
)
|
||||
schoolDb.insertAll(listOf(new))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.db.dao.StudentInfoDao
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntity
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class StudentInfoRepository @Inject constructor(
|
||||
private val studentInfoDao: StudentInfoDao,
|
||||
private val sdk: Sdk
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
||||
fun getStudentInfo(student: Student, semester: Semester, forceRefresh: Boolean) =
|
||||
networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = { it == null || forceRefresh },
|
||||
query = { studentInfoDao.loadStudentInfo(student.studentId) },
|
||||
fetch = {
|
||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
||||
.getStudentInfo().mapToEntity(semester)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
if (old != null && new != old) {
|
||||
with(studentInfoDao) {
|
||||
deleteAll(listOf(old))
|
||||
insertAll(listOf(new))
|
||||
}
|
||||
} else if (old == null) {
|
||||
studentInfoDao.insertAll(listOf(new))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
|
@ -5,12 +5,10 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
|||
import io.github.wulkanowy.data.db.dao.SemesterDao
|
||||
import io.github.wulkanowy.data.db.dao.StudentDao
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.DispatchersProvider
|
||||
import io.github.wulkanowy.utils.security.decrypt
|
||||
import io.github.wulkanowy.utils.security.encrypt
|
||||
|
@ -24,89 +22,54 @@ class StudentRepository @Inject constructor(
|
|||
private val dispatchers: DispatchersProvider,
|
||||
private val studentDb: StudentDao,
|
||||
private val semesterDb: SemesterDao,
|
||||
private val sdk: Sdk,
|
||||
private val appInfo: AppInfo
|
||||
private val sdk: Sdk
|
||||
) {
|
||||
|
||||
suspend fun isStudentSaved() = getSavedStudents(false).isNotEmpty()
|
||||
suspend fun isStudentSaved(): Boolean = getSavedStudents(false).isNotEmpty()
|
||||
|
||||
suspend fun isCurrentStudentSet() = studentDb.loadCurrent()?.isCurrent ?: false
|
||||
suspend fun isCurrentStudentSet(): Boolean = studentDb.loadCurrent()?.isCurrent ?: false
|
||||
|
||||
suspend fun getStudentsApi(
|
||||
pin: String,
|
||||
symbol: String,
|
||||
token: String
|
||||
): List<StudentWithSemesters> =
|
||||
sdk.getStudentsFromMobileApi(token, pin, symbol, "")
|
||||
.mapToEntities(colors = appInfo.defaultColorsForAvatar)
|
||||
|
||||
suspend fun getStudentsScrapper(
|
||||
email: String,
|
||||
password: String,
|
||||
scrapperBaseUrl: String,
|
||||
symbol: String
|
||||
): List<StudentWithSemesters> =
|
||||
sdk.getStudentsFromScrapper(email, password, scrapperBaseUrl, symbol)
|
||||
.mapToEntities(password, appInfo.defaultColorsForAvatar)
|
||||
|
||||
suspend fun getStudentsHybrid(
|
||||
email: String,
|
||||
password: String,
|
||||
scrapperBaseUrl: String,
|
||||
symbol: String
|
||||
): List<StudentWithSemesters> =
|
||||
sdk.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol)
|
||||
.mapToEntities(password, appInfo.defaultColorsForAvatar)
|
||||
|
||||
suspend fun getSavedStudents(decryptPass: Boolean = true) =
|
||||
studentDb.loadStudentsWithSemesters()
|
||||
.map {
|
||||
it.apply {
|
||||
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
|
||||
student.password = withContext(dispatchers.backgroundThread) {
|
||||
decrypt(student.password)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getStudentById(id: Long, decryptPass: Boolean = true): Student {
|
||||
val student = studentDb.loadById(id) ?: throw NoCurrentStudentException()
|
||||
|
||||
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
|
||||
student.password = withContext(dispatchers.backgroundThread) {
|
||||
decrypt(student.password)
|
||||
}
|
||||
}
|
||||
return student
|
||||
suspend fun getStudentsApi(pin: String, symbol: String, token: String): List<StudentWithSemesters> {
|
||||
return sdk.getStudentsFromMobileApi(token, pin, symbol, "").mapToEntities()
|
||||
}
|
||||
|
||||
suspend fun getCurrentStudent(decryptPass: Boolean = true): Student {
|
||||
val student = studentDb.loadCurrent() ?: throw NoCurrentStudentException()
|
||||
suspend fun getStudentsScrapper(email: String, password: String, scrapperBaseUrl: String, symbol: String): List<StudentWithSemesters> {
|
||||
return sdk.getStudentsFromScrapper(email, password, scrapperBaseUrl, symbol).mapToEntities(password)
|
||||
}
|
||||
|
||||
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
|
||||
student.password = withContext(dispatchers.backgroundThread) {
|
||||
decrypt(student.password)
|
||||
suspend fun getStudentsHybrid(email: String, password: String, scrapperBaseUrl: String, symbol: String): List<StudentWithSemesters> {
|
||||
return sdk.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol).mapToEntities(password)
|
||||
}
|
||||
|
||||
suspend fun getSavedStudents(decryptPass: Boolean = true) = withContext(dispatchers.backgroundThread) {
|
||||
studentDb.loadStudentsWithSemesters().map {
|
||||
it.apply {
|
||||
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) student.password = decrypt(student.password)
|
||||
}
|
||||
}
|
||||
return student
|
||||
}
|
||||
|
||||
suspend fun getStudentById(id: Int) = withContext(dispatchers.backgroundThread) {
|
||||
studentDb.loadById(id)?.apply {
|
||||
if (Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password)
|
||||
}
|
||||
} ?: throw NoCurrentStudentException()
|
||||
|
||||
suspend fun getCurrentStudent(decryptPass: Boolean = true) = withContext(dispatchers.backgroundThread) {
|
||||
studentDb.loadCurrent()?.apply {
|
||||
if (decryptPass && Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password)
|
||||
}
|
||||
} ?: throw NoCurrentStudentException()
|
||||
|
||||
suspend fun saveStudents(studentsWithSemesters: List<StudentWithSemesters>): List<Long> {
|
||||
val semesters = studentsWithSemesters.flatMap { it.semesters }
|
||||
val students = studentsWithSemesters.map { it.student }
|
||||
.map {
|
||||
it.apply {
|
||||
if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.API) {
|
||||
password = withContext(dispatchers.backgroundThread) {
|
||||
encrypt(password, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
semesterDb.insertSemesters(studentsWithSemesters.flatMap { it.semesters })
|
||||
|
||||
semesterDb.insertSemesters(semesters)
|
||||
return studentDb.insertAll(students)
|
||||
return withContext(dispatchers.backgroundThread) {
|
||||
studentDb.insertAll(studentsWithSemesters.map { it.student }.map {
|
||||
if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.API) it.copy(password = encrypt(it.password, context))
|
||||
else it
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun switchStudent(studentWithSemesters: StudentWithSemesters) {
|
||||
|
@ -116,8 +79,7 @@ class StudentRepository @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun logoutStudent(student: Student) = studentDb.delete(student)
|
||||
|
||||
suspend fun updateStudentNickAndAvatar(studentNickAndAvatar: StudentNickAndAvatar) =
|
||||
studentDb.update(studentNickAndAvatar)
|
||||
suspend fun logoutStudent(student: Student) {
|
||||
studentDb.delete(student)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import io.github.wulkanowy.sdk.Sdk
|
|||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
@ -18,10 +17,7 @@ class SubjectRepository @Inject constructor(
|
|||
private val sdk: Sdk
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
||||
fun getSubjects(student: Student, semester: Semester, forceRefresh: Boolean = false) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = { it.isEmpty() || forceRefresh },
|
||||
query = { subjectDao.loadAll(semester.diaryId, semester.studentId) },
|
||||
fetch = {
|
||||
|
|
|
@ -8,7 +8,6 @@ import io.github.wulkanowy.sdk.Sdk
|
|||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
@ -18,10 +17,7 @@ class TeacherRepository @Inject constructor(
|
|||
private val sdk: Sdk
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
||||
fun getTeachers(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = { it.isEmpty() || forceRefresh },
|
||||
query = { teacherDb.loadAll(semester.studentId, semester.classId) },
|
||||
fetch = {
|
||||
|
|
|
@ -18,7 +18,6 @@ import io.github.wulkanowy.utils.sunday
|
|||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import java.time.LocalDate
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
@ -32,12 +31,9 @@ class TimetableRepository @Inject constructor(
|
|||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
||||
private val cacheKey = "timetable"
|
||||
|
||||
fun getTimetable(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean, refreshAdditional: Boolean = false) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = { (timetable, additional) -> timetable.isEmpty() || (additional.isEmpty() && refreshAdditional) || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) },
|
||||
query = {
|
||||
timetableDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)
|
||||
|
|
|
@ -27,7 +27,6 @@ import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companio
|
|||
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.STUDENT_NAME
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.DispatchersProvider
|
||||
import io.github.wulkanowy.utils.nickOrName
|
||||
import io.github.wulkanowy.utils.toTimestamp
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
|
@ -42,23 +41,17 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
|||
private val dispatchersProvider: DispatchersProvider,
|
||||
) {
|
||||
|
||||
private fun getRequestCode(time: LocalDateTime, studentId: Int) =
|
||||
(time.toTimestamp() * studentId).toInt()
|
||||
private fun getRequestCode(time: LocalDateTime, studentId: Int) = (time.toTimestamp() * studentId).toInt()
|
||||
|
||||
private fun getUpcomingLessonTime(
|
||||
index: Int,
|
||||
day: List<Timetable>,
|
||||
lesson: Timetable
|
||||
) = day.getOrNull(index - 1)?.end ?: lesson.start.minusMinutes(30)
|
||||
private fun getUpcomingLessonTime(index: Int, day: List<Timetable>, lesson: Timetable): LocalDateTime {
|
||||
return day.getOrNull(index - 1)?.end ?: lesson.start.minusMinutes(30)
|
||||
}
|
||||
|
||||
suspend fun cancelScheduled(lessons: List<Timetable>, studentId: Int = 1) {
|
||||
withContext(dispatchersProvider.backgroundThread) {
|
||||
lessons.sortedBy { it.start }.forEachIndexed { index, lesson ->
|
||||
val upcomingTime = getUpcomingLessonTime(index, lessons, lesson)
|
||||
cancelScheduledTo(
|
||||
upcomingTime..lesson.start,
|
||||
getRequestCode(upcomingTime, studentId)
|
||||
)
|
||||
cancelScheduledTo(upcomingTime..lesson.start, getRequestCode(upcomingTime, studentId))
|
||||
cancelScheduledTo(lesson.start..lesson.end, getRequestCode(lesson.start, studentId))
|
||||
|
||||
Timber.d("TimetableNotification canceled: type 1 & 2, subject: ${lesson.subject}, start: ${lesson.start}, student: $studentId")
|
||||
|
@ -68,18 +61,13 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
|||
|
||||
private fun cancelScheduledTo(range: ClosedRange<LocalDateTime>, requestCode: Int) {
|
||||
if (now() in range) cancelNotification()
|
||||
alarmManager.cancel(
|
||||
PendingIntent.getBroadcast(context, requestCode, Intent(), FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
alarmManager.cancel(PendingIntent.getBroadcast(context, requestCode, Intent(), FLAG_UPDATE_CURRENT))
|
||||
}
|
||||
|
||||
fun cancelNotification() =
|
||||
NotificationManagerCompat.from(context).cancel(MainView.Section.TIMETABLE.id)
|
||||
fun cancelNotification() = NotificationManagerCompat.from(context).cancel(MainView.Section.TIMETABLE.id)
|
||||
|
||||
suspend fun scheduleNotifications(lessons: List<Timetable>, student: Student) {
|
||||
if (!preferencesRepository.isUpcomingLessonsNotificationsEnable) {
|
||||
return cancelScheduled(lessons, student.studentId)
|
||||
}
|
||||
if (!preferencesRepository.isUpcomingLessonsNotificationsEnable) return cancelScheduled(lessons, student.studentId)
|
||||
|
||||
withContext(dispatchersProvider.backgroundThread) {
|
||||
lessons.groupBy { it.date }
|
||||
|
@ -94,28 +82,13 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
|||
val intent = createIntent(student, lesson, active.getOrNull(index + 1))
|
||||
|
||||
if (lesson.start > now()) {
|
||||
scheduleBroadcast(
|
||||
intent,
|
||||
student.studentId,
|
||||
NOTIFICATION_TYPE_UPCOMING,
|
||||
getUpcomingLessonTime(index, active, lesson)
|
||||
)
|
||||
scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_UPCOMING, getUpcomingLessonTime(index, active, lesson))
|
||||
}
|
||||
|
||||
if (lesson.end > now()) {
|
||||
scheduleBroadcast(
|
||||
intent,
|
||||
student.studentId,
|
||||
NOTIFICATION_TYPE_CURRENT,
|
||||
lesson.start
|
||||
)
|
||||
scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_CURRENT, lesson.start)
|
||||
if (active.lastIndex == index) {
|
||||
scheduleBroadcast(
|
||||
intent,
|
||||
student.studentId,
|
||||
NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION,
|
||||
lesson.end
|
||||
)
|
||||
scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION, lesson.end)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -126,7 +99,7 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
|||
private fun createIntent(student: Student, lesson: Timetable, nextLesson: Timetable?): Intent {
|
||||
return Intent(context, TimetableNotificationReceiver::class.java).apply {
|
||||
putExtra(STUDENT_ID, student.studentId)
|
||||
putExtra(STUDENT_NAME, student.nickOrName)
|
||||
putExtra(STUDENT_NAME, student.studentName)
|
||||
putExtra(LESSON_ROOM, lesson.room)
|
||||
putExtra(LESSON_START, lesson.start.toTimestamp())
|
||||
putExtra(LESSON_END, lesson.end.toTimestamp())
|
||||
|
@ -136,23 +109,13 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun scheduleBroadcast(
|
||||
intent: Intent,
|
||||
studentId: Int,
|
||||
notificationType: Int,
|
||||
time: LocalDateTime
|
||||
) {
|
||||
AlarmManagerCompat.setExactAndAllowWhileIdle(
|
||||
alarmManager, RTC_WAKEUP, time.toTimestamp(),
|
||||
private fun scheduleBroadcast(intent: Intent, studentId: Int, notificationType: Int, time: LocalDateTime) {
|
||||
AlarmManagerCompat.setExactAndAllowWhileIdle(alarmManager, RTC_WAKEUP, time.toTimestamp(),
|
||||
PendingIntent.getBroadcast(context, getRequestCode(time, studentId), intent.also {
|
||||
it.putExtra(NOTIFICATION_ID, MainView.Section.TIMETABLE.id)
|
||||
it.putExtra(LESSON_TYPE, notificationType)
|
||||
}, FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
Timber.d(
|
||||
"TimetableNotification scheduled: type: $notificationType, subject: ${
|
||||
intent.getStringExtra(LESSON_TITLE)
|
||||
}, start: $time, student: $studentId"
|
||||
)
|
||||
Timber.d("TimetableNotification scheduled: type: $notificationType, subject: ${intent.getStringExtra(LESSON_TITLE)}, start: $time, student: $studentId")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,12 +5,11 @@ import androidx.core.app.NotificationCompat
|
|||
import androidx.core.app.NotificationCompat.BigTextStyle
|
||||
import androidx.core.app.NotificationCompat.PRIORITY_DEFAULT
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.hilt.work.HiltWorker
|
||||
import androidx.hilt.Assisted
|
||||
import androidx.hilt.work.WorkerInject
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.Data
|
||||
import androidx.work.WorkerParameters
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.SemesterRepository
|
||||
|
@ -24,8 +23,7 @@ import kotlinx.coroutines.coroutineScope
|
|||
import timber.log.Timber
|
||||
import kotlin.random.Random
|
||||
|
||||
@HiltWorker
|
||||
class SyncWorker @AssistedInject constructor(
|
||||
class SyncWorker @WorkerInject constructor(
|
||||
@Assisted appContext: Context,
|
||||
@Assisted workerParameters: WorkerParameters,
|
||||
private val studentRepository: StudentRepository,
|
||||
|
@ -60,10 +58,9 @@ class SyncWorker @AssistedInject constructor(
|
|||
}
|
||||
val result = when {
|
||||
exceptions.isNotEmpty() && inputData.getBoolean("one_time", false) -> {
|
||||
Result.failure(
|
||||
Data.Builder()
|
||||
.putString("error", exceptions.map { it.stackTraceToString() }.toString())
|
||||
.build()
|
||||
Result.failure(Data.Builder()
|
||||
.putString("error", exceptions.map { it.stackTraceToString() }.toString())
|
||||
.build()
|
||||
)
|
||||
}
|
||||
exceptions.isNotEmpty() -> Result.retry()
|
||||
|
@ -77,16 +74,13 @@ class SyncWorker @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private fun notify(result: Result) {
|
||||
notificationManager.notify(
|
||||
Random.nextInt(Int.MAX_VALUE),
|
||||
NotificationCompat.Builder(applicationContext, DebugChannel.CHANNEL_ID)
|
||||
.setContentTitle("Debug notification")
|
||||
.setSmallIcon(R.drawable.ic_stat_push)
|
||||
.setAutoCancel(true)
|
||||
.setColor(applicationContext.getCompatColor(R.color.colorPrimary))
|
||||
.setStyle(BigTextStyle().bigText("${SyncWorker::class.java.simpleName} result: $result"))
|
||||
.setPriority(PRIORITY_DEFAULT)
|
||||
.build()
|
||||
)
|
||||
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(applicationContext, DebugChannel.CHANNEL_ID)
|
||||
.setContentTitle("Debug notification")
|
||||
.setSmallIcon(R.drawable.ic_stat_push)
|
||||
.setAutoCancel(true)
|
||||
.setColor(applicationContext.getCompatColor(R.color.colorPrimary))
|
||||
.setStyle(BigTextStyle().bigText("${SyncWorker::class.java.simpleName} result: $result"))
|
||||
.setPriority(PRIORITY_DEFAULT)
|
||||
.build())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,6 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
|||
abstract var presenter: T
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
inject()
|
||||
themeManager.applyActivityTheme(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleLogger, true)
|
||||
|
@ -45,9 +44,7 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
|||
|
||||
if (SDK_INT >= LOLLIPOP) {
|
||||
@Suppress("DEPRECATION")
|
||||
setTaskDescription(
|
||||
ActivityManager.TaskDescription(null, null, getThemeAttrColor(R.attr.colorSurface))
|
||||
)
|
||||
setTaskDescription(ActivityManager.TaskDescription(null, null, getThemeAttrColor(R.attr.colorSurface)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,9 +84,4 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
|||
invalidateOptionsMenu()
|
||||
presenter.onDetachView()
|
||||
}
|
||||
|
||||
//https://github.com/google/dagger/releases/tag/dagger-2.33
|
||||
protected open fun inject() {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentPagerAdapter
|
||||
|
||||
//TODO Use ViewPager2
|
||||
class BaseFragmentPagerAdapter(private val fragmentManager: FragmentManager) :
|
||||
FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
|
||||
|
||||
|
|
|
@ -42,8 +42,10 @@ class ErrorDialog : BaseDialogFragment<DialogErrorBinding>() {
|
|||
companion object {
|
||||
private const val ARGUMENT_KEY = "Data"
|
||||
|
||||
fun newInstance(error: Throwable) = ErrorDialog().apply {
|
||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, error) }
|
||||
fun newInstance(error: Throwable): ErrorDialog {
|
||||
return ErrorDialog().apply {
|
||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, error) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,14 +57,12 @@ class ErrorDialog : BaseDialogFragment<DialogErrorBinding>() {
|
|||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
) = DialogErrorBinding.inflate(inflater).apply { binding = this }.root
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return DialogErrorBinding.inflate(inflater).apply { binding = this }.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
val stringWriter = StringWriter().apply {
|
||||
error.printStackTrace(PrintWriter(this))
|
||||
|
@ -114,17 +114,11 @@ class ErrorDialog : BaseDialogFragment<DialogErrorBinding>() {
|
|||
chooserTitle = getString(R.string.about_feedback),
|
||||
email = "wulkanowyinc@gmail.com",
|
||||
subject = "Zgłoszenie błędu",
|
||||
body = requireContext().getString(
|
||||
R.string.about_feedback_template,
|
||||
"${appInfo.systemManufacturer} ${appInfo.systemModel}",
|
||||
appInfo.systemVersion.toString(),
|
||||
"${appInfo.versionName}-${appInfo.buildFlavor}"
|
||||
body = requireContext().getString(R.string.about_feedback_template,
|
||||
"${appInfo.systemManufacturer} ${appInfo.systemModel}", appInfo.systemVersion.toString(), appInfo.versionName
|
||||
) + "\n" + content,
|
||||
onActivityNotFound = {
|
||||
requireContext().openInternetBrowser(
|
||||
"https://github.com/wulkanowy/wulkanowy/issues",
|
||||
::showMessage
|
||||
)
|
||||
requireContext().openInternetBrowser("https://github.com/wulkanowy/wulkanowy/issues", ::showMessage)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,9 +8,6 @@ import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
|
|||
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
@ -20,13 +17,7 @@ class ThemeManager @Inject constructor(private val preferencesRepository: Prefer
|
|||
fun applyActivityTheme(activity: AppCompatActivity) {
|
||||
if (isThemeApplicable(activity)) {
|
||||
applyDefaultTheme()
|
||||
if (preferencesRepository.appTheme == "black") {
|
||||
when (activity) {
|
||||
is MainActivity -> activity.setTheme(R.style.WulkanowyTheme_Black)
|
||||
is LoginActivity -> activity.setTheme(R.style.WulkanowyTheme_Login_Black)
|
||||
is SendMessageActivity -> activity.setTheme(R.style.WulkanowyTheme_MessageSend_Black)
|
||||
}
|
||||
}
|
||||
if (preferencesRepository.appTheme == "black") activity.setTheme(R.style.WulkanowyTheme_Black)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,13 +33,8 @@ class ThemeManager @Inject constructor(private val preferencesRepository: Prefer
|
|||
}
|
||||
|
||||
private fun isThemeApplicable(activity: AppCompatActivity): Boolean {
|
||||
return activity.packageManager
|
||||
.getPackageInfo(activity.packageName, GET_ACTIVITIES)
|
||||
.activities.singleOrNull { it.name == activity::class.java.canonicalName }
|
||||
?.theme.let {
|
||||
it == R.style.WulkanowyTheme_Black || it == R.style.WulkanowyTheme_NoActionBar
|
||||
|| it == R.style.WulkanowyTheme_Login || it == R.style.WulkanowyTheme_Login_Black
|
||||
|| it == R.style.WulkanowyTheme_MessageSend || it == R.style.WulkanowyTheme_MessageSend_Black
|
||||
}
|
||||
return activity.packageManager.getPackageInfo(activity.packageName, GET_ACTIVITIES)
|
||||
.activities.singleOrNull { it.name == activity::class.java.canonicalName }?.theme
|
||||
.let { it == R.style.WulkanowyTheme_Black || it == R.style.WulkanowyTheme_NoActionBar }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ import io.github.wulkanowy.R
|
|||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.databinding.ItemAccountBinding
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.nickOrName
|
||||
import javax.inject.Inject
|
||||
|
||||
class WidgetConfigureAdapter @Inject constructor() : RecyclerView.Adapter<WidgetConfigureAdapter.ItemViewHolder>() {
|
||||
|
@ -29,7 +28,7 @@ class WidgetConfigureAdapter @Inject constructor() : RecyclerView.Adapter<Widget
|
|||
val (student, isCurrent) = items[position]
|
||||
|
||||
with(holder.binding) {
|
||||
accountItemName.text = "${student.nickOrName} ${student.className}"
|
||||
accountItemName.text = "${student.studentName} ${student.className}"
|
||||
accountItemSchool.text = student.schoolName
|
||||
|
||||
with(accountItemImage) {
|
||||
|
|
|
@ -18,8 +18,6 @@ import io.github.wulkanowy.utils.getCompatDrawable
|
|||
import io.github.wulkanowy.utils.openAppInMarket
|
||||
import io.github.wulkanowy.utils.openEmailClient
|
||||
import io.github.wulkanowy.utils.openInternetBrowser
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import io.github.wulkanowy.utils.toLocalDateTime
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
|
@ -37,9 +35,7 @@ class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about
|
|||
|
||||
override val versionRes: Triple<String, String, Drawable?>?
|
||||
get() = context?.run {
|
||||
val buildTimestamp = appInfo.buildTimestamp.toLocalDateTime().toFormattedString("yyyy-MM-dd")
|
||||
val versionSignature = "${appInfo.versionName}-${appInfo.buildFlavor} (${appInfo.versionCode}), $buildTimestamp"
|
||||
Triple(getString(R.string.about_version), versionSignature, getCompatDrawable(R.drawable.ic_all_about))
|
||||
Triple(getString(R.string.about_version), "${appInfo.versionName} (${appInfo.versionCode})", getCompatDrawable(R.drawable.ic_all_about))
|
||||
}
|
||||
|
||||
override val creatorsRes: Triple<String, String, Drawable?>?
|
||||
|
@ -69,11 +65,7 @@ class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about
|
|||
|
||||
override val homepageRes: Triple<String, String, Drawable?>?
|
||||
get() = context?.run {
|
||||
Triple(
|
||||
getString(R.string.about_homepage),
|
||||
getString(R.string.about_homepage_summary),
|
||||
getCompatDrawable(R.drawable.ic_all_home)
|
||||
)
|
||||
Triple(getString(R.string.about_homepage), getString(R.string.about_homepage_summary), getCompatDrawable(R.drawable.ic_about_homepage))
|
||||
}
|
||||
|
||||
override val licensesRes: Triple<String, String, Drawable?>?
|
||||
|
@ -139,17 +131,11 @@ class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about
|
|||
chooserTitle = getString(R.string.about_feedback),
|
||||
email = "wulkanowyinc@gmail.com",
|
||||
subject = "Zgłoszenie błędu",
|
||||
body = getString(
|
||||
R.string.about_feedback_template,
|
||||
"${appInfo.systemManufacturer} ${appInfo.systemModel}",
|
||||
appInfo.systemVersion.toString(),
|
||||
"${appInfo.versionName}-${appInfo.buildFlavor}"
|
||||
body = getString(R.string.about_feedback_template,
|
||||
"${appInfo.systemManufacturer} ${appInfo.systemModel}", appInfo.systemVersion.toString(), appInfo.versionName
|
||||
),
|
||||
onActivityNotFound = {
|
||||
requireContext().openInternetBrowser(
|
||||
"https://github.com/wulkanowy/wulkanowy/issues",
|
||||
::showMessage
|
||||
)
|
||||
requireContext().openInternetBrowser("https://github.com/wulkanowy/wulkanowy/issues", ::showMessage)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@ import android.os.Bundle
|
|||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.text.parseAsHtml
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.mikepenz.aboutlibraries.Libs
|
||||
import com.mikepenz.aboutlibraries.entity.Library
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
@ -26,9 +26,14 @@ class LicenseFragment : BaseFragment<FragmentLicenseBinding>(R.layout.fragment_l
|
|||
@Inject
|
||||
lateinit var licenseAdapter: LicenseAdapter
|
||||
|
||||
private val libs by lazy { Libs(requireContext()) }
|
||||
|
||||
override val titleStringId get() = R.string.license_title
|
||||
|
||||
override val appLibraries by lazy { Libs(requireContext()).libraries }
|
||||
override val appLibraries: ArrayList<Library>?
|
||||
get() = context?.let {
|
||||
libs.prepareLibraries(it, emptyArray(), emptyArray(), autoDetect = true, checkCachedDetection = true, sort = true)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun newInstance() = LicenseFragment()
|
||||
|
@ -58,7 +63,7 @@ class LicenseFragment : BaseFragment<FragmentLicenseBinding>(R.layout.fragment_l
|
|||
|
||||
override fun openLicense(licenseHtml: String) {
|
||||
context?.let {
|
||||
MaterialAlertDialogBuilder(it).apply {
|
||||
AlertDialog.Builder(it).apply {
|
||||
setTitle(R.string.license_dialog_title)
|
||||
setMessage(licenseHtml.parseAsHtml())
|
||||
setPositiveButton(android.R.string.ok) { _, _ -> }
|
||||
|
|
|
@ -5,7 +5,7 @@ import io.github.wulkanowy.ui.base.BaseView
|
|||
|
||||
interface LicenseView : BaseView {
|
||||
|
||||
val appLibraries: List<Library>
|
||||
val appLibraries: ArrayList<Library>?
|
||||
|
||||
fun initView()
|
||||
|
||||
|
|
|
@ -1,25 +1,23 @@
|
|||
package io.github.wulkanowy.ui.modules.account
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.PorterDuff
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.databinding.HeaderAccountBinding
|
||||
import io.github.wulkanowy.databinding.ItemAccountBinding
|
||||
import io.github.wulkanowy.utils.createNameInitialsDrawable
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.nickOrName
|
||||
import javax.inject.Inject
|
||||
|
||||
class AccountAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
var isAccountQuickDialogMode = false
|
||||
|
||||
var items = emptyList<AccountItem<*>>()
|
||||
|
||||
var onClickListener: (StudentWithSemesters) -> Unit = {}
|
||||
|
@ -32,77 +30,56 @@ class AccountAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.V
|
|||
val inflater = LayoutInflater.from(parent.context)
|
||||
|
||||
return when (viewType) {
|
||||
AccountItem.ViewType.HEADER.id -> HeaderViewHolder(
|
||||
HeaderAccountBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
AccountItem.ViewType.ITEM.id -> ItemViewHolder(
|
||||
ItemAccountBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
AccountItem.ViewType.HEADER.id -> HeaderViewHolder(HeaderAccountBinding.inflate(inflater, parent, false))
|
||||
AccountItem.ViewType.ITEM.id -> ItemViewHolder(ItemAccountBinding.inflate(inflater, parent, false))
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is HeaderViewHolder -> bindHeaderViewHolder(
|
||||
holder.binding,
|
||||
items[position].value as Account,
|
||||
position
|
||||
)
|
||||
is ItemViewHolder -> bindItemViewHolder(
|
||||
holder.binding,
|
||||
items[position].value as StudentWithSemesters
|
||||
)
|
||||
is HeaderViewHolder -> bindHeaderViewHolder(holder.binding, items[position].value as Account)
|
||||
is ItemViewHolder -> bindItemViewHolder(holder.binding, items[position].value as StudentWithSemesters)
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindHeaderViewHolder(
|
||||
binding: HeaderAccountBinding,
|
||||
account: Account,
|
||||
position: Int
|
||||
) {
|
||||
private fun bindHeaderViewHolder(binding: HeaderAccountBinding, account: Account) {
|
||||
with(binding) {
|
||||
accountHeaderDivider.visibility = if (position == 0) GONE else VISIBLE
|
||||
accountHeaderEmail.text = account.email
|
||||
accountHeaderType.setText(if (account.isParent) R.string.account_type_parent else R.string.account_type_student)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun bindItemViewHolder(
|
||||
binding: ItemAccountBinding,
|
||||
studentWithSemesters: StudentWithSemesters
|
||||
) {
|
||||
val context = binding.root.context
|
||||
private fun bindItemViewHolder(binding: ItemAccountBinding, studentWithSemesters: StudentWithSemesters) {
|
||||
val student = studentWithSemesters.student
|
||||
val semesters = studentWithSemesters.semesters
|
||||
val diary = semesters.maxByOrNull { it.semesterId }
|
||||
val avatar = context.createNameInitialsDrawable(student.nickOrName, student.avatarColor)
|
||||
val checkBackgroundColor =
|
||||
context.getThemeAttrColor(if (isAccountQuickDialogMode) R.attr.colorBackgroundFloating else R.attr.colorSurface)
|
||||
val isDuplicatedStudent = items.filter {
|
||||
if (it.value !is StudentWithSemesters) return@filter false
|
||||
val studentToCompare = it.value.student
|
||||
|
||||
studentToCompare.studentId == student.studentId
|
||||
&& studentToCompare.schoolSymbol == student.schoolSymbol
|
||||
&& studentToCompare.symbol == student.symbol
|
||||
}.size > 1 && isAccountQuickDialogMode
|
||||
|
||||
with(binding) {
|
||||
accountItemName.text = "${student.nickOrName} ${diary?.diaryName.orEmpty()}"
|
||||
accountItemName.text = "${student.studentName} ${diary?.diaryName.orEmpty()}"
|
||||
accountItemSchool.text = studentWithSemesters.student.schoolName
|
||||
accountItemImage.setImageDrawable(avatar)
|
||||
|
||||
with(accountItemAccountType) {
|
||||
setText(if (student.isParent) R.string.account_type_parent else R.string.account_type_student)
|
||||
isVisible = isDuplicatedStudent
|
||||
with(accountItemLoginMode) {
|
||||
visibility = when (Sdk.Mode.valueOf(student.loginMode)) {
|
||||
Sdk.Mode.API -> {
|
||||
setText(R.string.account_login_mobile_api)
|
||||
VISIBLE
|
||||
}
|
||||
Sdk.Mode.HYBRID -> {
|
||||
setText(R.string.account_login_hybrid)
|
||||
VISIBLE
|
||||
}
|
||||
Sdk.Mode.SCRAPPER -> {
|
||||
GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
with(accountItemCheck) {
|
||||
isVisible = student.isCurrent
|
||||
borderColor = checkBackgroundColor
|
||||
circleColor = checkBackgroundColor
|
||||
with(accountItemImage) {
|
||||
val colorImage = if (student.isCurrent) context.getThemeAttrColor(R.attr.colorPrimary)
|
||||
else context.getThemeAttrColor(R.attr.colorOnSurface, 153)
|
||||
|
||||
setColorFilter(colorImage, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
|
||||
root.setOnClickListener { onClickListener(studentWithSemesters) }
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
package io.github.wulkanowy.ui.modules.account
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import android.widget.Toast.LENGTH_LONG
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.databinding.DialogAccountBinding
|
||||
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AccountDialog : BaseDialogFragment<DialogAccountBinding>(), AccountView {
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: AccountPresenter
|
||||
|
||||
@Inject
|
||||
lateinit var accountAdapter: AccountAdapter
|
||||
|
||||
companion object {
|
||||
fun newInstance() = AccountDialog()
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return DialogAccountBinding.inflate(inflater).apply { binding = this }.root
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
presenter.onAttachView(this)
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
accountAdapter.onClickListener = presenter::onItemSelected
|
||||
|
||||
with(binding) {
|
||||
accountDialogAdd.setOnClickListener { presenter.onAddSelected() }
|
||||
accountDialogRemove.setOnClickListener { presenter.onRemoveSelected() }
|
||||
accountDialogRecycler.apply {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = accountAdapter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateData(data: List<AccountItem<*>>) {
|
||||
with(accountAdapter) {
|
||||
items = data
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun showError(text: String, error: Throwable) {
|
||||
showMessage(text)
|
||||
}
|
||||
|
||||
override fun showMessage(text: String) {
|
||||
Toast.makeText(context, text, LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
override fun dismissView() {
|
||||
dismiss()
|
||||
}
|
||||
|
||||
override fun openLoginView() {
|
||||
activity?.let {
|
||||
startActivity(LoginActivity.getStartIntent(it))
|
||||
}
|
||||
}
|
||||
|
||||
override fun showConfirmDialog() {
|
||||
context?.let {
|
||||
AlertDialog.Builder(it)
|
||||
.setTitle(R.string.account_logout_student)
|
||||
.setMessage(R.string.account_confirm)
|
||||
.setPositiveButton(R.string.account_logout) { _, _ -> presenter.onLogoutConfirm() }
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun recreateMainView() {
|
||||
activity?.recreate()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
presenter.onDetachView()
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
package io.github.wulkanowy.ui.modules.account
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.View
|
||||
import androidx.core.view.get
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.databinding.FragmentAccountBinding
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsFragment
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AccountFragment : BaseFragment<FragmentAccountBinding>(R.layout.fragment_account),
|
||||
AccountView, MainView.TitledView {
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: AccountPresenter
|
||||
|
||||
@Inject
|
||||
lateinit var accountAdapter: AccountAdapter
|
||||
|
||||
companion object {
|
||||
|
||||
fun newInstance() = AccountFragment()
|
||||
}
|
||||
|
||||
override val titleStringId = R.string.account_title
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding = FragmentAccountBinding.bind(view)
|
||||
presenter.onAttachView(this)
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
binding.accountRecycler.apply {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = accountAdapter
|
||||
}
|
||||
|
||||
accountAdapter.onClickListener = presenter::onItemSelected
|
||||
|
||||
binding.accountAdd.setOnClickListener { presenter.onAddSelected() }
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
menu[0].isVisible = false
|
||||
}
|
||||
|
||||
override fun updateData(data: List<AccountItem<*>>) {
|
||||
with(accountAdapter) {
|
||||
items = data
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun openLoginView() {
|
||||
activity?.let {
|
||||
startActivity(LoginActivity.getStartIntent(it))
|
||||
}
|
||||
}
|
||||
|
||||
override fun openAccountDetailsView(studentWithSemesters: StudentWithSemesters) {
|
||||
(activity as? MainActivity)?.pushView(
|
||||
AccountDetailsFragment.newInstance(studentWithSemesters)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -3,8 +3,10 @@ package io.github.wulkanowy.ui.modules.account
|
|||
import io.github.wulkanowy.data.Status
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.services.sync.SyncManager
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.utils.afterLoading
|
||||
import io.github.wulkanowy.utils.flowWithResource
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import timber.log.Timber
|
||||
|
@ -13,12 +15,13 @@ import javax.inject.Inject
|
|||
class AccountPresenter @Inject constructor(
|
||||
errorHandler: ErrorHandler,
|
||||
studentRepository: StudentRepository,
|
||||
private val syncManager: SyncManager
|
||||
) : BasePresenter<AccountView>(errorHandler, studentRepository) {
|
||||
|
||||
override fun onAttachView(view: AccountView) {
|
||||
super.onAttachView(view)
|
||||
view.initView()
|
||||
Timber.i("Account view was initialized")
|
||||
Timber.i("Account dialog view was initialized")
|
||||
loadData()
|
||||
}
|
||||
|
||||
|
@ -27,39 +30,86 @@ class AccountPresenter @Inject constructor(
|
|||
view?.openLoginView()
|
||||
}
|
||||
|
||||
fun onItemSelected(studentWithSemesters: StudentWithSemesters) {
|
||||
view?.openAccountDetailsView(studentWithSemesters)
|
||||
fun onRemoveSelected() {
|
||||
Timber.i("Select remove account")
|
||||
view?.showConfirmDialog()
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
flowWithResource { studentRepository.getSavedStudents(false) }
|
||||
.onEach {
|
||||
when (it.status) {
|
||||
Status.LOADING -> Timber.i("Loading account data started")
|
||||
Status.SUCCESS -> {
|
||||
Timber.i("Loading account result: Success")
|
||||
view?.updateData(createAccountItems(it.data!!))
|
||||
}
|
||||
Status.ERROR -> {
|
||||
Timber.i("Loading account result: An exception occurred")
|
||||
errorHandler.dispatch(it.error!!)
|
||||
fun onLogoutConfirm() {
|
||||
flowWithResource {
|
||||
val student = studentRepository.getCurrentStudent(false)
|
||||
studentRepository.logoutStudent(student)
|
||||
|
||||
val students = studentRepository.getSavedStudents(false)
|
||||
if (students.isNotEmpty()) {
|
||||
studentRepository.switchStudent(students[0])
|
||||
}
|
||||
students
|
||||
}.onEach {
|
||||
when (it.status) {
|
||||
Status.LOADING -> Timber.i("Attempt to logout current user ")
|
||||
Status.SUCCESS -> view?.run {
|
||||
if (it.data!!.isEmpty()) {
|
||||
Timber.i("Logout result: Open login view")
|
||||
syncManager.stopSyncWorker()
|
||||
openClearLoginView()
|
||||
} else {
|
||||
Timber.i("Logout result: Switch to another student")
|
||||
recreateMainView()
|
||||
}
|
||||
}
|
||||
Status.ERROR -> {
|
||||
Timber.i("Logout result: An exception occurred")
|
||||
errorHandler.dispatch(it.error!!)
|
||||
}
|
||||
}
|
||||
.launch("load")
|
||||
}.afterLoading {
|
||||
view?.dismissView()
|
||||
}.launch("logout")
|
||||
}
|
||||
|
||||
fun onItemSelected(studentWithSemesters: StudentWithSemesters) {
|
||||
Timber.i("Select student item ${studentWithSemesters.student.id}")
|
||||
if (studentWithSemesters.student.isCurrent) {
|
||||
view?.dismissView()
|
||||
} else flowWithResource { studentRepository.switchStudent(studentWithSemesters) }.onEach {
|
||||
when (it.status) {
|
||||
Status.LOADING -> Timber.i("Attempt to change a student")
|
||||
Status.SUCCESS -> {
|
||||
Timber.i("Change a student result: Success")
|
||||
view?.recreateMainView()
|
||||
}
|
||||
Status.ERROR -> {
|
||||
Timber.i("Change a student result: An exception occurred")
|
||||
errorHandler.dispatch(it.error!!)
|
||||
}
|
||||
}
|
||||
}.afterLoading {
|
||||
view?.dismissView()
|
||||
}.launch("switch")
|
||||
}
|
||||
|
||||
private fun createAccountItems(items: List<StudentWithSemesters>): List<AccountItem<*>> {
|
||||
return items.groupBy {
|
||||
Account("${it.student.userName} (${it.student.email})", it.student.isParent)
|
||||
}
|
||||
.map { (account, students) ->
|
||||
listOf(
|
||||
AccountItem(account, AccountItem.ViewType.HEADER)
|
||||
) + students.map { student ->
|
||||
AccountItem(student, AccountItem.ViewType.ITEM)
|
||||
return items.groupBy { Account(it.student.email, it.student.isParent) }.map { (account, students) ->
|
||||
listOf(AccountItem(account, AccountItem.ViewType.HEADER)) + students.map { student ->
|
||||
AccountItem(student, AccountItem.ViewType.ITEM)
|
||||
}
|
||||
}.flatten()
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
flowWithResource { studentRepository.getSavedStudents(false) }.onEach {
|
||||
when (it.status) {
|
||||
Status.LOADING -> Timber.i("Loading account data started")
|
||||
Status.SUCCESS -> {
|
||||
Timber.i("Loading account result: Success")
|
||||
view?.updateData(createAccountItems(it.data!!))
|
||||
}
|
||||
Status.ERROR -> {
|
||||
Timber.i("Loading account result: An exception occurred")
|
||||
errorHandler.dispatch(it.error!!)
|
||||
}
|
||||
}
|
||||
.flatten()
|
||||
}.launch()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package io.github.wulkanowy.ui.modules.account
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface AccountView : BaseView {
|
||||
|
@ -9,7 +8,12 @@ interface AccountView : BaseView {
|
|||
|
||||
fun updateData(data: List<AccountItem<*>>)
|
||||
|
||||
fun dismissView()
|
||||
|
||||
fun showConfirmDialog()
|
||||
|
||||
fun openLoginView()
|
||||
|
||||
fun openAccountDetailsView(studentWithSemesters: StudentWithSemesters)
|
||||
fun recreateMainView()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,164 +0,0 @@
|
|||
package io.github.wulkanowy.ui.modules.account.accountdetails
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.view.get
|
||||
import androidx.core.view.isVisible
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.databinding.FragmentAccountDetailsBinding
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.account.accountedit.AccountEditDialog
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoFragment
|
||||
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
|
||||
import io.github.wulkanowy.utils.createNameInitialsDrawable
|
||||
import io.github.wulkanowy.utils.nickOrName
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AccountDetailsFragment :
|
||||
BaseFragment<FragmentAccountDetailsBinding>(R.layout.fragment_account_details),
|
||||
AccountDetailsView, MainView.TitledView {
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: AccountDetailsPresenter
|
||||
|
||||
override val titleStringId = R.string.account_details_title
|
||||
|
||||
companion object {
|
||||
|
||||
private const val ARGUMENT_KEY = "Data"
|
||||
|
||||
fun newInstance(studentWithSemesters: StudentWithSemesters) =
|
||||
AccountDetailsFragment().apply {
|
||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, studentWithSemesters) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding = FragmentAccountDetailsBinding.bind(view)
|
||||
presenter.onAttachView(this, requireArguments()[ARGUMENT_KEY] as StudentWithSemesters)
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
binding.accountDetailsErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||
binding.accountDetailsErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||
binding.accountDetailsLogout.setOnClickListener { presenter.onRemoveSelected() }
|
||||
binding.accountDetailsSelect.setOnClickListener { presenter.onStudentSelect() }
|
||||
|
||||
binding.accountDetailsPersonalData.setOnClickListener {
|
||||
presenter.onStudentInfoSelected(StudentInfoView.Type.PERSONAL)
|
||||
}
|
||||
binding.accountDetailsAddressData.setOnClickListener {
|
||||
presenter.onStudentInfoSelected(StudentInfoView.Type.ADDRESS)
|
||||
}
|
||||
binding.accountDetailsContactData.setOnClickListener {
|
||||
presenter.onStudentInfoSelected(StudentInfoView.Type.CONTACT)
|
||||
}
|
||||
binding.accountDetailsFamilyData.setOnClickListener {
|
||||
presenter.onStudentInfoSelected(StudentInfoView.Type.FAMILY)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
menu[0].isVisible = false
|
||||
inflater.inflate(R.menu.action_menu_account_details, menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return if (item.itemId == R.id.accountDetailsMenuEdit) {
|
||||
presenter.onAccountEditSelected()
|
||||
true
|
||||
} else false
|
||||
}
|
||||
|
||||
override fun showAccountData(student: Student) {
|
||||
with(binding) {
|
||||
accountDetailsCheck.isVisible = student.isCurrent
|
||||
accountDetailsName.text = student.nickOrName
|
||||
accountDetailsSchool.text = student.schoolName
|
||||
accountDetailsAvatar.setImageDrawable(
|
||||
requireContext().createNameInitialsDrawable(
|
||||
student.nickOrName,
|
||||
student.avatarColor
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun enableSelectStudentButton(enable: Boolean) {
|
||||
binding.accountDetailsSelect.isEnabled = enable
|
||||
}
|
||||
|
||||
override fun showAccountEditDetailsDialog(student: Student) {
|
||||
(requireActivity() as MainActivity).showDialogFragment(
|
||||
AccountEditDialog.newInstance(student)
|
||||
)
|
||||
}
|
||||
|
||||
override fun showLogoutConfirmDialog() {
|
||||
context?.let {
|
||||
AlertDialog.Builder(it)
|
||||
.setTitle(R.string.account_logout_student)
|
||||
.setMessage(R.string.account_confirm)
|
||||
.setPositiveButton(R.string.account_logout) { _, _ -> presenter.onLogoutConfirm() }
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun popView() {
|
||||
(requireActivity() as MainActivity).popView(2)
|
||||
}
|
||||
|
||||
override fun recreateMainView() {
|
||||
requireActivity().recreate()
|
||||
}
|
||||
|
||||
override fun openStudentInfoView(
|
||||
infoType: StudentInfoView.Type,
|
||||
studentWithSemesters: StudentWithSemesters
|
||||
) {
|
||||
(requireActivity() as MainActivity).pushView(
|
||||
StudentInfoFragment.newInstance(
|
||||
infoType,
|
||||
studentWithSemesters
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun showErrorView(show: Boolean) {
|
||||
binding.accountDetailsError.visibility = if (show) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
override fun setErrorDetails(message: String) {
|
||||
binding.accountDetailsErrorMessage.text = message
|
||||
}
|
||||
|
||||
override fun showProgress(show: Boolean) {
|
||||
binding.accountDetailsProgress.visibility = if (show) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
override fun showContent(show: Boolean) {
|
||||
binding.accountDetailsContent.visibility = if (show) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
presenter.onDetachView()
|
||||
super.onDestroyView()
|
||||
}
|
||||
}
|
|
@ -1,180 +0,0 @@
|
|||
package io.github.wulkanowy.ui.modules.account.accountdetails
|
||||
|
||||
import io.github.wulkanowy.data.Resource
|
||||
import io.github.wulkanowy.data.Status
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.services.sync.SyncManager
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
|
||||
import io.github.wulkanowy.utils.afterLoading
|
||||
import io.github.wulkanowy.utils.flowWithResource
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class AccountDetailsPresenter @Inject constructor(
|
||||
errorHandler: ErrorHandler,
|
||||
studentRepository: StudentRepository,
|
||||
private val syncManager: SyncManager
|
||||
) : BasePresenter<AccountDetailsView>(errorHandler, studentRepository) {
|
||||
|
||||
private var studentWithSemesters: StudentWithSemesters? = null
|
||||
|
||||
private lateinit var lastError: Throwable
|
||||
|
||||
private var studentId: Long? = null
|
||||
|
||||
fun onAttachView(view: AccountDetailsView, studentWithSemesters: StudentWithSemesters) {
|
||||
super.onAttachView(view)
|
||||
studentId = studentWithSemesters.student.id
|
||||
|
||||
view.initView()
|
||||
errorHandler.showErrorMessage = ::showErrorViewOnError
|
||||
Timber.i("Account details view was initialized")
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun onRetry() {
|
||||
view?.run {
|
||||
showErrorView(false)
|
||||
showProgress(true)
|
||||
}
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun onDetailsClick() {
|
||||
view?.showErrorDetailsDialog(lastError)
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
flowWithResource { studentRepository.getSavedStudents() }
|
||||
.map { studentWithSemesters ->
|
||||
Resource(
|
||||
data = studentWithSemesters.data?.single { it.student.id == studentId },
|
||||
status = studentWithSemesters.status,
|
||||
error = studentWithSemesters.error
|
||||
)
|
||||
}
|
||||
.onEach {
|
||||
when (it.status) {
|
||||
Status.LOADING -> {
|
||||
view?.run {
|
||||
showProgress(true)
|
||||
showContent(false)
|
||||
}
|
||||
Timber.i("Loading account details view started")
|
||||
}
|
||||
Status.SUCCESS -> {
|
||||
Timber.i("Loading account details view result: Success")
|
||||
studentWithSemesters = it.data
|
||||
view?.run {
|
||||
showAccountData(studentWithSemesters!!.student)
|
||||
enableSelectStudentButton(!studentWithSemesters!!.student.isCurrent)
|
||||
showContent(true)
|
||||
showErrorView(false)
|
||||
}
|
||||
}
|
||||
Status.ERROR -> {
|
||||
Timber.i("Loading account details view result: An exception occurred")
|
||||
errorHandler.dispatch(it.error!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
.afterLoading { view?.showProgress(false) }
|
||||
.launch()
|
||||
}
|
||||
|
||||
fun onAccountEditSelected() {
|
||||
studentWithSemesters?.let {
|
||||
view?.showAccountEditDetailsDialog(it.student)
|
||||
}
|
||||
}
|
||||
|
||||
fun onStudentInfoSelected(infoType: StudentInfoView.Type) {
|
||||
studentWithSemesters?.let {
|
||||
view?.openStudentInfoView(infoType, it)
|
||||
}
|
||||
}
|
||||
|
||||
fun onStudentSelect() {
|
||||
if (studentWithSemesters == null) return
|
||||
|
||||
Timber.i("Select student ${studentWithSemesters!!.student.id}")
|
||||
|
||||
flowWithResource { studentRepository.switchStudent(studentWithSemesters!!) }
|
||||
.onEach {
|
||||
when (it.status) {
|
||||
Status.LOADING -> Timber.i("Attempt to change a student")
|
||||
Status.SUCCESS -> {
|
||||
Timber.i("Change a student result: Success")
|
||||
view?.recreateMainView()
|
||||
}
|
||||
Status.ERROR -> {
|
||||
Timber.i("Change a student result: An exception occurred")
|
||||
errorHandler.dispatch(it.error!!)
|
||||
}
|
||||
}
|
||||
}.afterLoading {
|
||||
view?.popView()
|
||||
}.launch("switch")
|
||||
}
|
||||
|
||||
fun onRemoveSelected() {
|
||||
Timber.i("Select remove account")
|
||||
view?.showLogoutConfirmDialog()
|
||||
}
|
||||
|
||||
fun onLogoutConfirm() {
|
||||
if (studentWithSemesters == null) return
|
||||
|
||||
flowWithResource {
|
||||
val studentToLogout = studentWithSemesters!!.student
|
||||
|
||||
studentRepository.logoutStudent(studentToLogout)
|
||||
val students = studentRepository.getSavedStudents(false)
|
||||
|
||||
if (studentToLogout.isCurrent && students.isNotEmpty()) {
|
||||
studentRepository.switchStudent(students[0])
|
||||
}
|
||||
|
||||
return@flowWithResource students
|
||||
}.onEach {
|
||||
when (it.status) {
|
||||
Status.LOADING -> Timber.i("Attempt to logout user")
|
||||
Status.SUCCESS -> view?.run {
|
||||
when {
|
||||
it.data!!.isEmpty() -> {
|
||||
Timber.i("Logout result: Open login view")
|
||||
syncManager.stopSyncWorker()
|
||||
openClearLoginView()
|
||||
}
|
||||
studentWithSemesters!!.student.isCurrent -> {
|
||||
Timber.i("Logout result: Logout student and switch to another")
|
||||
recreateMainView()
|
||||
}
|
||||
else -> Timber.i("Logout result: Logout student")
|
||||
}
|
||||
}
|
||||
Status.ERROR -> {
|
||||
Timber.i("Logout result: An exception occurred")
|
||||
errorHandler.dispatch(it.error!!)
|
||||
}
|
||||
}
|
||||
}.afterLoading {
|
||||
view?.popView()
|
||||
}.launch("logout")
|
||||
}
|
||||
|
||||
private fun showErrorViewOnError(message: String, error: Throwable) {
|
||||
view?.run {
|
||||
lastError = error
|
||||
setErrorDetails(message)
|
||||
showErrorView(true)
|
||||
showContent(false)
|
||||
showProgress(false)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package io.github.wulkanowy.ui.modules.account.accountdetails
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
|
||||
|
||||
interface AccountDetailsView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun showAccountData(student: Student)
|
||||
|
||||
fun showAccountEditDetailsDialog(student: Student)
|
||||
|
||||
fun showLogoutConfirmDialog()
|
||||
|
||||
fun popView()
|
||||
|
||||
fun recreateMainView()
|
||||
|
||||
fun enableSelectStudentButton(enable: Boolean)
|
||||
|
||||
fun openStudentInfoView(
|
||||
infoType: StudentInfoView.Type,
|
||||
studentWithSemesters: StudentWithSemesters
|
||||
)
|
||||
|
||||
fun showErrorView(show: Boolean)
|
||||
|
||||
fun setErrorDetails(message: String)
|
||||
|
||||
fun showProgress(show: Boolean)
|
||||
|
||||
fun showContent(show: Boolean)
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
package io.github.wulkanowy.ui.modules.account.accountedit
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.graphics.drawable.RippleDrawable
|
||||
import android.graphics.drawable.StateListDrawable
|
||||
import android.os.Build
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.databinding.ItemAccountEditColorBinding
|
||||
import javax.inject.Inject
|
||||
|
||||
class AccountEditColorAdapter @Inject constructor() :
|
||||
RecyclerView.Adapter<AccountEditColorAdapter.ViewHolder>() {
|
||||
|
||||
var items = listOf<Int>()
|
||||
|
||||
var selectedColor = 0
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
|
||||
ItemAccountEditColorBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
)
|
||||
|
||||
@SuppressLint("RestrictedApi", "NewApi")
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val item = items[position]
|
||||
|
||||
with(holder.binding) {
|
||||
accountEditItemColor.setImageDrawable(GradientDrawable().apply {
|
||||
shape = GradientDrawable.OVAL
|
||||
setColor(item)
|
||||
})
|
||||
|
||||
accountEditItemColorContainer.foreground = item.createForegroundDrawable()
|
||||
accountEditCheck.isVisible = selectedColor == item
|
||||
|
||||
root.setOnClickListener {
|
||||
val oldSelectedPosition = items.indexOf(selectedColor)
|
||||
selectedColor = item
|
||||
|
||||
notifyItemChanged(oldSelectedPosition)
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Int.createForegroundDrawable(): Drawable =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
val mask = GradientDrawable().apply {
|
||||
shape = GradientDrawable.OVAL
|
||||
setColor(Color.BLACK)
|
||||
}
|
||||
RippleDrawable(ColorStateList.valueOf(this.rippleColor), null, mask)
|
||||
} else {
|
||||
val foreground = StateListDrawable().apply {
|
||||
alpha = 80
|
||||
setEnterFadeDuration(250)
|
||||
setExitFadeDuration(250)
|
||||
}
|
||||
|
||||
val mask = GradientDrawable().apply {
|
||||
shape = GradientDrawable.OVAL
|
||||
setColor(this@createForegroundDrawable.rippleColor)
|
||||
}
|
||||
|
||||
foreground.apply {
|
||||
addState(intArrayOf(android.R.attr.state_pressed), mask)
|
||||
addState(intArrayOf(), ColorDrawable(Color.TRANSPARENT))
|
||||
}
|
||||
}
|
||||
|
||||
private inline val Int.rippleColor: Int
|
||||
get() {
|
||||
val hsv = FloatArray(3)
|
||||
Color.colorToHSV(this, hsv)
|
||||
hsv[2] = hsv[2] * 0.5f
|
||||
return Color.HSVToColor(hsv)
|
||||
}
|
||||
|
||||
class ViewHolder(val binding: ItemAccountEditColorBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
package io.github.wulkanowy.ui.modules.account.accountedit
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.databinding.DialogAccountEditBinding
|
||||
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AccountEditDialog : BaseDialogFragment<DialogAccountEditBinding>(), AccountEditView {
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: AccountEditPresenter
|
||||
|
||||
@Inject
|
||||
lateinit var accountEditColorAdapter: AccountEditColorAdapter
|
||||
|
||||
companion object {
|
||||
|
||||
private const val ARGUMENT_KEY = "student_with_semesters"
|
||||
|
||||
fun newInstance(student: Student) =
|
||||
AccountEditDialog().apply {
|
||||
arguments = Bundle().apply {
|
||||
putSerializable(ARGUMENT_KEY, student)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View = DialogAccountEditBinding.inflate(inflater).apply { binding = this }.root
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
presenter.onAttachView(this, requireArguments()[ARGUMENT_KEY] as Student)
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
with(binding) {
|
||||
accountEditDetailsCancel.setOnClickListener { dismiss() }
|
||||
accountEditDetailsSave.setOnClickListener {
|
||||
presenter.changeStudentNickAndAvatar(
|
||||
binding.accountEditDetailsNickText.text.toString(),
|
||||
accountEditColorAdapter.selectedColor
|
||||
)
|
||||
}
|
||||
|
||||
with(binding.accountEditColors) {
|
||||
layoutManager = GridLayoutManager(context, 4)
|
||||
adapter = accountEditColorAdapter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateSelectedColorData(color: Int) {
|
||||
with(accountEditColorAdapter) {
|
||||
selectedColor = color
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateColorsData(colors: List<Int>) {
|
||||
with(accountEditColorAdapter) {
|
||||
items = colors
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun showCurrentNick(nick: String) {
|
||||
binding.accountEditDetailsNickText.setText(nick)
|
||||
}
|
||||
|
||||
override fun popView() {
|
||||
dismiss()
|
||||
}
|
||||
|
||||
override fun recreateMainView() {
|
||||
activity?.recreate()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
presenter.onDetachView()
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
package io.github.wulkanowy.ui.modules.account.accountedit
|
||||
|
||||
import io.github.wulkanowy.data.Status
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.afterLoading
|
||||
import io.github.wulkanowy.utils.flowWithResource
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class AccountEditPresenter @Inject constructor(
|
||||
private val appInfo: AppInfo,
|
||||
errorHandler: ErrorHandler,
|
||||
studentRepository: StudentRepository
|
||||
) : BasePresenter<AccountEditView>(errorHandler, studentRepository) {
|
||||
|
||||
lateinit var student: Student
|
||||
|
||||
private val colors = appInfo.defaultColorsForAvatar.map { it.toInt() }
|
||||
|
||||
fun onAttachView(view: AccountEditView, student: Student) {
|
||||
super.onAttachView(view)
|
||||
this.student = student
|
||||
|
||||
with(view) {
|
||||
initView()
|
||||
showCurrentNick(student.nick.trim())
|
||||
}
|
||||
Timber.i("Account edit dialog view was initialized")
|
||||
loadData()
|
||||
|
||||
view.updateColorsData(colors)
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
flowWithResource {
|
||||
studentRepository.getStudentById(student.id, false).avatarColor
|
||||
}.onEach { resource ->
|
||||
when (resource.status) {
|
||||
Status.LOADING -> Timber.i("Attempt to load student")
|
||||
Status.SUCCESS -> {
|
||||
view?.updateSelectedColorData(resource.data?.toInt()!!)
|
||||
Timber.i("Attempt to load student: Success")
|
||||
}
|
||||
Status.ERROR -> {
|
||||
Timber.i("Attempt to load student: An exception occurred")
|
||||
errorHandler.dispatch(resource.error!!)
|
||||
}
|
||||
}
|
||||
}.launch("load_data")
|
||||
}
|
||||
|
||||
fun changeStudentNickAndAvatar(nick: String, avatarColor: Int) {
|
||||
flowWithResource {
|
||||
val studentNick =
|
||||
StudentNickAndAvatar(nick = nick.trim(), avatarColor = avatarColor.toLong())
|
||||
.apply { id = student.id }
|
||||
studentRepository.updateStudentNickAndAvatar(studentNick)
|
||||
}.onEach {
|
||||
when (it.status) {
|
||||
Status.LOADING -> Timber.i("Attempt to change a student nick and avatar")
|
||||
Status.SUCCESS -> {
|
||||
Timber.i("Change a student nick and avatar result: Success")
|
||||
view?.recreateMainView()
|
||||
}
|
||||
Status.ERROR -> {
|
||||
Timber.i("Change a student nick and avatar result: An exception occurred")
|
||||
errorHandler.dispatch(it.error!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
.afterLoading { view?.popView() }
|
||||
.launch("update_student")
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package io.github.wulkanowy.ui.modules.account.accountedit
|
||||
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface AccountEditView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun popView()
|
||||
|
||||
fun recreateMainView()
|
||||
|
||||
fun showCurrentNick(nick: String)
|
||||
|
||||
fun updateSelectedColorData(color: Int)
|
||||
|
||||
fun updateColorsData(colors: List<Int>)
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
package io.github.wulkanowy.ui.modules.account.accountquick
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.databinding.DialogAccountQuickBinding
|
||||
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
||||
import io.github.wulkanowy.ui.modules.account.AccountAdapter
|
||||
import io.github.wulkanowy.ui.modules.account.AccountFragment
|
||||
import io.github.wulkanowy.ui.modules.account.AccountItem
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AccountQuickDialog : BaseDialogFragment<DialogAccountQuickBinding>(), AccountQuickView {
|
||||
|
||||
@Inject
|
||||
lateinit var accountAdapter: AccountAdapter
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: AccountQuickPresenter
|
||||
|
||||
companion object {
|
||||
|
||||
private const val STUDENTS_ARGUMENT_KEY = "students"
|
||||
|
||||
fun newInstance(studentsWithSemesters: List<StudentWithSemesters>) =
|
||||
AccountQuickDialog().apply {
|
||||
arguments = Bundle().apply {
|
||||
putSerializable(STUDENTS_ARGUMENT_KEY, studentsWithSemesters.toTypedArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, 0)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
) = DialogAccountQuickBinding.inflate(inflater).apply { binding = this }.root
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
val studentsWithSemesters =
|
||||
(requireArguments()[STUDENTS_ARGUMENT_KEY] as Array<StudentWithSemesters>).toList()
|
||||
|
||||
presenter.onAttachView(this, studentsWithSemesters)
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
binding.accountQuickDialogManger.setOnClickListener { presenter.onManagerSelected() }
|
||||
|
||||
with(accountAdapter) {
|
||||
isAccountQuickDialogMode = true
|
||||
onClickListener = presenter::onStudentSelect
|
||||
}
|
||||
|
||||
with(binding.accountQuickDialogRecycler) {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = accountAdapter
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateData(data: List<AccountItem<*>>) {
|
||||
with(accountAdapter) {
|
||||
items = data
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun popView() {
|
||||
dismiss()
|
||||
}
|
||||
|
||||
override fun recreateMainView() {
|
||||
activity?.recreate()
|
||||
}
|
||||
|
||||
override fun openAccountView() {
|
||||
(requireActivity() as MainActivity).pushView(AccountFragment.newInstance())
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
presenter.onDetachView()
|
||||
super.onDestroyView()
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
package io.github.wulkanowy.ui.modules.account.accountquick
|
||||
|
||||
import io.github.wulkanowy.data.Status
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.ui.modules.account.AccountItem
|
||||
import io.github.wulkanowy.utils.afterLoading
|
||||
import io.github.wulkanowy.utils.flowWithResource
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class AccountQuickPresenter @Inject constructor(
|
||||
errorHandler: ErrorHandler,
|
||||
studentRepository: StudentRepository
|
||||
) : BasePresenter<AccountQuickView>(errorHandler, studentRepository) {
|
||||
|
||||
private lateinit var studentsWithSemesters: List<StudentWithSemesters>
|
||||
|
||||
fun onAttachView(view: AccountQuickView, studentsWithSemesters: List<StudentWithSemesters>) {
|
||||
super.onAttachView(view)
|
||||
this.studentsWithSemesters = studentsWithSemesters
|
||||
|
||||
view.initView()
|
||||
Timber.i("Account quick dialog view was initialized")
|
||||
view.updateData(createAccountItems(studentsWithSemesters))
|
||||
}
|
||||
|
||||
fun onManagerSelected() {
|
||||
view?.run {
|
||||
openAccountView()
|
||||
popView()
|
||||
}
|
||||
}
|
||||
|
||||
fun onStudentSelect(studentWithSemesters: StudentWithSemesters) {
|
||||
Timber.i("Select student ${studentWithSemesters.student.id}")
|
||||
|
||||
if (studentWithSemesters.student.isCurrent) {
|
||||
view?.popView()
|
||||
return
|
||||
}
|
||||
|
||||
flowWithResource { studentRepository.switchStudent(studentWithSemesters) }
|
||||
.onEach {
|
||||
when (it.status) {
|
||||
Status.LOADING -> Timber.i("Attempt to change a student")
|
||||
Status.SUCCESS -> {
|
||||
Timber.i("Change a student result: Success")
|
||||
view?.recreateMainView()
|
||||
}
|
||||
Status.ERROR -> {
|
||||
Timber.i("Change a student result: An exception occurred")
|
||||
errorHandler.dispatch(it.error!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
.afterLoading { view?.popView() }
|
||||
.launch("switch")
|
||||
}
|
||||
|
||||
private fun createAccountItems(items: List<StudentWithSemesters>) = items.map {
|
||||
AccountItem(it, AccountItem.ViewType.ITEM)
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package io.github.wulkanowy.ui.modules.account.accountquick
|
||||
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
import io.github.wulkanowy.ui.modules.account.AccountItem
|
||||
|
||||
interface AccountQuickView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<AccountItem<*>>)
|
||||
|
||||
fun recreateMainView()
|
||||
|
||||
fun popView()
|
||||
|
||||
fun openAccountView()
|
||||
}
|
|
@ -18,11 +18,12 @@ class AttendanceDialog : DialogFragment() {
|
|||
private lateinit var attendance: Attendance
|
||||
|
||||
companion object {
|
||||
|
||||
private const val ARGUMENT_KEY = "Item"
|
||||
|
||||
fun newInstance(exam: Attendance) = AttendanceDialog().apply {
|
||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
|
||||
fun newInstance(exam: Attendance): AttendanceDialog {
|
||||
return AttendanceDialog().apply {
|
||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,14 +35,12 @@ class AttendanceDialog : DialogFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
) = DialogAttendanceBinding.inflate(inflater).apply { binding = this }.root
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return DialogAttendanceBinding.inflate(inflater).apply { binding = this }.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
with(binding) {
|
||||
attendanceDialogSubject.text = attendance.subject
|
||||
|
|
|
@ -26,7 +26,6 @@ import io.github.wulkanowy.ui.modules.main.MainView
|
|||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||
import io.github.wulkanowy.utils.SchooldaysRangeLimiter
|
||||
import io.github.wulkanowy.utils.dpToPx
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import java.time.LocalDate
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -61,7 +60,6 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
|
|||
override val excuseActionMode: Boolean get() = attendanceAdapter.excuseActionMode
|
||||
|
||||
private var actionMode: ActionMode? = null
|
||||
|
||||
private val actionModeCallback = object : ActionMode.Callback {
|
||||
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||
val inflater = mode.menuInflater
|
||||
|
@ -113,8 +111,6 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
|
|||
|
||||
with(binding) {
|
||||
attendanceSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||
attendanceSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||
attendanceSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||
attendanceErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||
attendanceErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||
|
||||
|
@ -192,7 +188,7 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
|
|||
}
|
||||
|
||||
override fun showContent(show: Boolean) {
|
||||
binding.attendanceRecycler.visibility = if (show) VISIBLE else GONE
|
||||
binding. attendanceRecycler.visibility = if (show) VISIBLE else GONE
|
||||
}
|
||||
|
||||
override fun showRefresh(show: Boolean) {
|
||||
|
@ -226,7 +222,6 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
|
|||
setDateRangeLimiter(SchooldaysRangeLimiter())
|
||||
version = DatePickerDialog.Version.VERSION_2
|
||||
scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL
|
||||
vibrate(false)
|
||||
show(this@AttendanceFragment.parentFragmentManager, null)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -190,48 +190,35 @@ class AttendancePresenter @Inject constructor(
|
|||
flowWithResourceIn {
|
||||
val student = studentRepository.getCurrentStudent()
|
||||
val semester = semesterRepository.getCurrentSemester(student)
|
||||
attendanceRepository.getAttendance(
|
||||
student,
|
||||
semester,
|
||||
currentDate,
|
||||
currentDate,
|
||||
forceRefresh
|
||||
)
|
||||
attendanceRepository.getAttendance(student, semester, currentDate, currentDate, forceRefresh)
|
||||
}.onEach {
|
||||
when (it.status) {
|
||||
Status.LOADING -> {
|
||||
view?.showExcuseButton(false)
|
||||
if (!it.data.isNullOrEmpty()) {
|
||||
val filteredAttendance = if (prefRepository.isShowPresent) {
|
||||
it.data
|
||||
} else {
|
||||
it.data.filter { item -> !item.presence }
|
||||
}
|
||||
|
||||
view?.run {
|
||||
enableSwipe(true)
|
||||
showRefresh(true)
|
||||
showProgress(false)
|
||||
showEmpty(filteredAttendance.isEmpty())
|
||||
showContent(filteredAttendance.isNotEmpty())
|
||||
updateData(filteredAttendance.sortedBy { item -> item.number })
|
||||
showContent(true)
|
||||
updateData(it.data.let { items ->
|
||||
if (prefRepository.isShowPresent) items
|
||||
else items.filter { item -> !item.presence }
|
||||
}.sortedBy { item -> item.number })
|
||||
}
|
||||
}
|
||||
}
|
||||
Status.SUCCESS -> {
|
||||
Timber.i("Loading attendance result: Success")
|
||||
val filteredAttendance = if (prefRepository.isShowPresent) {
|
||||
it.data.orEmpty()
|
||||
} else {
|
||||
it.data?.filter { item -> !item.presence }.orEmpty()
|
||||
}
|
||||
|
||||
view?.apply {
|
||||
updateData(filteredAttendance.sortedBy { item -> item.number })
|
||||
showEmpty(filteredAttendance.isEmpty())
|
||||
updateData(it.data!!.let { items ->
|
||||
if (prefRepository.isShowPresent) items
|
||||
else items.filter { item -> !item.presence }
|
||||
}.sortedBy { item -> item.number })
|
||||
showEmpty(it.data.isEmpty())
|
||||
showErrorView(false)
|
||||
showContent(filteredAttendance.isNotEmpty())
|
||||
showExcuseButton(filteredAttendance.any { item -> item.excusable })
|
||||
showContent(it.data.isNotEmpty())
|
||||
showExcuseButton(it.data.any { item -> item.excusable })
|
||||
}
|
||||
analytics.logEvent(
|
||||
"load_data",
|
||||
|
|
|
@ -15,7 +15,6 @@ import io.github.wulkanowy.databinding.FragmentAttendanceSummaryBinding
|
|||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.dpToPx
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.setOnItemSelectedListener
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -57,8 +56,6 @@ class AttendanceSummaryFragment :
|
|||
|
||||
with(binding) {
|
||||
attendanceSummarySwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||
attendanceSummarySwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||
attendanceSummarySwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||
attendanceSummaryErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||
attendanceSummaryErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import io.github.wulkanowy.databinding.FragmentConferenceBinding
|
|||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
|
@ -48,9 +47,7 @@ class ConferenceFragment : BaseFragment<FragmentConferenceBinding>(R.layout.frag
|
|||
}
|
||||
|
||||
with(binding) {
|
||||
conferenceSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||
conferenceSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||
conferenceSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||
conferenceSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
|
||||
conferenceErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||
conferenceErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||
}
|
||||
|
|
|
@ -17,11 +17,12 @@ class ExamDialog : DialogFragment() {
|
|||
private lateinit var exam: Exam
|
||||
|
||||
companion object {
|
||||
|
||||
private const val ARGUMENT_KEY = "Item"
|
||||
|
||||
fun newInstance(exam: Exam) = ExamDialog().apply {
|
||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
|
||||
fun newInstance(exam: Exam): ExamDialog {
|
||||
return ExamDialog().apply {
|
||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,14 +34,12 @@ class ExamDialog : DialogFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
) = DialogExamBinding.inflate(inflater).apply { binding = this }.root
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return DialogExamBinding.inflate(inflater).apply { binding = this }.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
|
||||
with(binding) {
|
||||
examDialogSubjectValue.text = exam.subject
|
||||
|
|
|
@ -15,7 +15,6 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
|
|||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||
import io.github.wulkanowy.utils.dpToPx
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
|
@ -56,8 +55,6 @@ class ExamFragment : BaseFragment<FragmentExamBinding>(R.layout.fragment_exam),
|
|||
|
||||
with(binding) {
|
||||
examSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||
examSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||
examSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||
examErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||
examErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package io.github.wulkanowy.ui.modules.grade
|
||||
|
||||
import io.github.wulkanowy.data.Resource
|
||||
import io.github.wulkanowy.data.Status
|
||||
import io.github.wulkanowy.data.db.entities.Grade
|
||||
import io.github.wulkanowy.data.db.entities.GradeSummary
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
|
@ -34,169 +33,81 @@ class GradeAverageProvider @Inject constructor(
|
|||
|
||||
private val minusModifier get() = preferencesRepository.gradeMinusModifier
|
||||
|
||||
fun getGradesDetailsWithAverage(student: Student, semesterId: Int, forceRefresh: Boolean) =
|
||||
flowWithResourceIn {
|
||||
val semesters = semesterRepository.getSemesters(student)
|
||||
fun getGradesDetailsWithAverage(student: Student, semesterId: Int, forceRefresh: Boolean) = flowWithResourceIn {
|
||||
val semesters = semesterRepository.getSemesters(student)
|
||||
|
||||
when (preferencesRepository.gradeAverageMode) {
|
||||
ONE_SEMESTER -> getGradeSubjects(
|
||||
student = student,
|
||||
semester = semesters.single { it.semesterId == semesterId },
|
||||
forceRefresh = forceRefresh
|
||||
)
|
||||
BOTH_SEMESTERS -> calculateCombinedAverage(
|
||||
student = student,
|
||||
semesters = semesters,
|
||||
semesterId = semesterId,
|
||||
forceRefresh = forceRefresh,
|
||||
averageMode = BOTH_SEMESTERS
|
||||
)
|
||||
ALL_YEAR -> calculateCombinedAverage(
|
||||
student = student,
|
||||
semesters = semesters,
|
||||
semesterId = semesterId,
|
||||
forceRefresh = forceRefresh,
|
||||
averageMode = ALL_YEAR
|
||||
)
|
||||
}
|
||||
}.distinctUntilChanged()
|
||||
when (preferencesRepository.gradeAverageMode) {
|
||||
ONE_SEMESTER -> getSemesterDetailsWithAverage(student, semesters.single { it.semesterId == semesterId }, forceRefresh)
|
||||
BOTH_SEMESTERS -> calculateBothSemestersAverage(student, semesters, semesterId, forceRefresh)
|
||||
ALL_YEAR -> calculateAllYearAverage(student, semesters, semesterId, forceRefresh)
|
||||
}
|
||||
}.distinctUntilChanged()
|
||||
|
||||
private fun calculateCombinedAverage(
|
||||
student: Student,
|
||||
semesters: List<Semester>,
|
||||
semesterId: Int,
|
||||
forceRefresh: Boolean,
|
||||
averageMode: GradeAverageMode
|
||||
): Flow<Resource<List<GradeSubject>>> {
|
||||
val gradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc
|
||||
private fun calculateBothSemestersAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): Flow<Resource<List<GradeDetailsWithAverage>>> {
|
||||
val selectedSemester = semesters.single { it.semesterId == semesterId }
|
||||
val firstSemester =
|
||||
semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 }
|
||||
val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 }
|
||||
|
||||
val selectedSemesterGradeSubjects =
|
||||
getGradeSubjects(student, selectedSemester, forceRefresh)
|
||||
val selectedSemesterDetailsWithAverage = getSemesterDetailsWithAverage(student, selectedSemester, forceRefresh)
|
||||
|
||||
if (selectedSemester == firstSemester) return selectedSemesterGradeSubjects
|
||||
|
||||
val firstSemesterGradeSubjects =
|
||||
getGradeSubjects(student, firstSemester, forceRefresh)
|
||||
|
||||
return selectedSemesterGradeSubjects.combine(firstSemesterGradeSubjects) { secondSemesterGradeSubject, firstSemesterGradeSubject ->
|
||||
if (firstSemesterGradeSubject.status == Status.ERROR) {
|
||||
return@combine firstSemesterGradeSubject
|
||||
return if (selectedSemester == firstSemester) selectedSemesterDetailsWithAverage else {
|
||||
val firstSemesterDetailsWithAverage = getSemesterDetailsWithAverage(student, firstSemester, forceRefresh)
|
||||
selectedSemesterDetailsWithAverage.combine(firstSemesterDetailsWithAverage) { selectedDetails, secondDetails ->
|
||||
val isAnyAverage = selectedDetails.data.orEmpty().any { it.average != .0 }
|
||||
secondDetails.copy(data = selectedDetails.data?.map { selected ->
|
||||
val second = secondDetails.data.orEmpty().singleOrNull { it.subject == selected.subject }
|
||||
selected.copy(average = if (!isAnyAverage || preferencesRepository.gradeAverageForceCalc) {
|
||||
val selectedGrades = selected.grades.updateModifiers(student).calcAverage()
|
||||
(selectedGrades + (second?.grades?.updateModifiers(student)?.calcAverage() ?: selectedGrades)) / 2
|
||||
} else (selected.average + (second?.average ?: selected.average)) / 2)
|
||||
})
|
||||
}
|
||||
|
||||
val isAnyVulcanAverageInFirstSemester =
|
||||
firstSemesterGradeSubject.data.orEmpty().any { it.average != .0 }
|
||||
val isAnyVulcanAverageInSecondSemester =
|
||||
secondSemesterGradeSubject.data.orEmpty().any { it.average != .0 }
|
||||
|
||||
val updatedData = secondSemesterGradeSubject.data?.map { secondSemesterSubject ->
|
||||
val firstSemesterSubject = firstSemesterGradeSubject.data.orEmpty()
|
||||
.singleOrNull { it.subject == secondSemesterSubject.subject }
|
||||
|
||||
val updatedAverage = if (averageMode == ALL_YEAR) {
|
||||
calculateAllYearAverage(
|
||||
student = student,
|
||||
isAnyVulcanAverage = isAnyVulcanAverageInFirstSemester || isAnyVulcanAverageInSecondSemester,
|
||||
gradeAverageForceCalc = gradeAverageForceCalc,
|
||||
secondSemesterSubject = secondSemesterSubject,
|
||||
firstSemesterSubject = firstSemesterSubject
|
||||
)
|
||||
} else {
|
||||
calculateBothSemestersAverage(
|
||||
student = student,
|
||||
isAnyVulcanAverage = isAnyVulcanAverageInFirstSemester || isAnyVulcanAverageInSecondSemester,
|
||||
gradeAverageForceCalc = gradeAverageForceCalc,
|
||||
secondSemesterSubject = secondSemesterSubject,
|
||||
firstSemesterSubject = firstSemesterSubject
|
||||
)
|
||||
}
|
||||
secondSemesterSubject.copy(average = updatedAverage)
|
||||
}
|
||||
secondSemesterGradeSubject.copy(data = updatedData)
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateAllYearAverage(
|
||||
student: Student,
|
||||
isAnyVulcanAverage: Boolean,
|
||||
gradeAverageForceCalc: Boolean,
|
||||
secondSemesterSubject: GradeSubject,
|
||||
firstSemesterSubject: GradeSubject?
|
||||
) = if (!isAnyVulcanAverage || gradeAverageForceCalc) {
|
||||
val updatedSecondSemesterGrades =
|
||||
secondSemesterSubject.grades.updateModifiers(student)
|
||||
val updatedFirstSemesterGrades =
|
||||
firstSemesterSubject?.grades?.updateModifiers(student).orEmpty()
|
||||
private fun calculateAllYearAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): Flow<Resource<List<GradeDetailsWithAverage>>> {
|
||||
val selectedSemester = semesters.single { it.semesterId == semesterId }
|
||||
val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 }
|
||||
|
||||
(updatedSecondSemesterGrades + updatedFirstSemesterGrades).calcAverage()
|
||||
} else {
|
||||
secondSemesterSubject.average
|
||||
}
|
||||
val selectedSemesterDetailsWithAverage = getSemesterDetailsWithAverage(student, selectedSemester, forceRefresh)
|
||||
|
||||
private fun calculateBothSemestersAverage(
|
||||
student: Student,
|
||||
isAnyVulcanAverage: Boolean,
|
||||
gradeAverageForceCalc: Boolean,
|
||||
secondSemesterSubject: GradeSubject,
|
||||
firstSemesterSubject: GradeSubject?
|
||||
): Double {
|
||||
val divider = if (secondSemesterSubject.grades.any { it.weightValue > .0 }) 2 else 1
|
||||
|
||||
return if (!isAnyVulcanAverage || gradeAverageForceCalc) {
|
||||
val secondSemesterAverage =
|
||||
secondSemesterSubject.grades.updateModifiers(student).calcAverage()
|
||||
val firstSemesterAverage = firstSemesterSubject?.grades?.updateModifiers(student)
|
||||
?.calcAverage() ?: secondSemesterAverage
|
||||
|
||||
(secondSemesterAverage + firstSemesterAverage) / divider
|
||||
} else {
|
||||
(secondSemesterSubject.average + (firstSemesterSubject?.average ?: secondSemesterSubject.average)) / divider
|
||||
return if (selectedSemester == firstSemester) selectedSemesterDetailsWithAverage else {
|
||||
val firstSemesterDetailsWithAverage = getSemesterDetailsWithAverage(student, firstSemester, forceRefresh)
|
||||
selectedSemesterDetailsWithAverage.combine(firstSemesterDetailsWithAverage) { selectedDetails, secondDetails ->
|
||||
val isAnyAverage = selectedDetails.data.orEmpty().any { it.average != .0 }
|
||||
secondDetails.copy(data = selectedDetails.data?.map { selected ->
|
||||
val second = secondDetails.data.orEmpty().singleOrNull { it.subject == selected.subject }
|
||||
selected.copy(average = if (!isAnyAverage || preferencesRepository.gradeAverageForceCalc) {
|
||||
(selected.grades.updateModifiers(student) + second?.grades?.updateModifiers(student).orEmpty()).calcAverage()
|
||||
} else selected.average)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getGradeSubjects(
|
||||
student: Student,
|
||||
semester: Semester,
|
||||
forceRefresh: Boolean
|
||||
): Flow<Resource<List<GradeSubject>>> {
|
||||
val gradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc
|
||||
private fun getSemesterDetailsWithAverage(student: Student, semester: Semester, forceRefresh: Boolean): Flow<Resource<List<GradeDetailsWithAverage>>> {
|
||||
return gradeRepository.getGrades(student, semester, forceRefresh = forceRefresh).map { res ->
|
||||
val (details, summaries) = res.data ?: null to null
|
||||
val isAnyAverage = summaries.orEmpty().any { it.average != .0 }
|
||||
val allGrades = details.orEmpty().groupBy { it.subject }
|
||||
|
||||
return gradeRepository.getGrades(student, semester, forceRefresh = forceRefresh)
|
||||
.map { res ->
|
||||
val (details, summaries) = res.data ?: null to null
|
||||
val isAnyAverage = summaries.orEmpty().any { it.average != .0 }
|
||||
val allGrades = details.orEmpty().groupBy { it.subject }
|
||||
|
||||
val items = summaries?.emulateEmptySummaries(
|
||||
student,
|
||||
semester,
|
||||
allGrades.toList(),
|
||||
isAnyAverage
|
||||
)?.map { summary ->
|
||||
val grades = allGrades[summary.subject].orEmpty()
|
||||
GradeSubject(
|
||||
subject = summary.subject,
|
||||
average = if (!isAnyAverage || gradeAverageForceCalc) {
|
||||
grades.updateModifiers(student).calcAverage()
|
||||
} else summary.average,
|
||||
points = summary.pointsSum,
|
||||
summary = summary,
|
||||
grades = grades
|
||||
)
|
||||
}
|
||||
|
||||
Resource(res.status, items, res.error)
|
||||
val items = summaries?.emulateEmptySummaries(student, semester, allGrades.toList(), isAnyAverage)?.map { summary ->
|
||||
val grades = allGrades[summary.subject].orEmpty()
|
||||
GradeDetailsWithAverage(
|
||||
subject = summary.subject,
|
||||
average = if (!isAnyAverage || preferencesRepository.gradeAverageForceCalc) {
|
||||
grades.updateModifiers(student).calcAverage()
|
||||
} else summary.average,
|
||||
points = summary.pointsSum,
|
||||
summary = summary,
|
||||
grades = grades
|
||||
)
|
||||
}
|
||||
|
||||
Resource(res.status, items, res.error)
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<GradeSummary>.emulateEmptySummaries(
|
||||
student: Student,
|
||||
semester: Semester,
|
||||
grades: List<Pair<String, List<Grade>>>,
|
||||
calcAverage: Boolean
|
||||
): List<GradeSummary> {
|
||||
private fun List<GradeSummary>.emulateEmptySummaries(student: Student, semester: Semester, grades: List<Pair<String, List<Grade>>>, calcAverage: Boolean): List<GradeSummary> {
|
||||
if (isNotEmpty() && size > grades.size) return this
|
||||
|
||||
return grades.mapIndexed { i, (subject, details) ->
|
||||
|
|
|
@ -3,7 +3,7 @@ package io.github.wulkanowy.ui.modules.grade
|
|||
import io.github.wulkanowy.data.db.entities.Grade
|
||||
import io.github.wulkanowy.data.db.entities.GradeSummary
|
||||
|
||||
data class GradeSubject(
|
||||
data class GradeDetailsWithAverage(
|
||||
val subject: String,
|
||||
val average: Double,
|
||||
val points: String,
|
|
@ -10,7 +10,6 @@ import android.view.View.VISIBLE
|
|||
import androidx.appcompat.app.AlertDialog
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.databinding.FragmentGradeBinding
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter
|
||||
|
@ -34,6 +33,7 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||
private var semesterSwitchMenu: MenuItem? = null
|
||||
|
||||
companion object {
|
||||
private const val SAVED_SEMESTER_KEY = "CURRENT_SEMESTER"
|
||||
|
||||
fun newInstance() = GradeFragment()
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding = FragmentGradeBinding.bind(view)
|
||||
presenter.onAttachView(this)
|
||||
presenter.onAttachView(this, savedInstanceState?.getInt(SAVED_SEMESTER_KEY))
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
|
@ -64,13 +64,11 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||
override fun initView() {
|
||||
with(pagerAdapter) {
|
||||
containerId = binding.gradeViewPager.id
|
||||
addFragmentsWithTitle(
|
||||
mapOf(
|
||||
GradeDetailsFragment.newInstance() to getString(R.string.all_details),
|
||||
GradeSummaryFragment.newInstance() to getString(R.string.grade_menu_summary),
|
||||
GradeStatisticsFragment.newInstance() to getString(R.string.grade_menu_statistics)
|
||||
)
|
||||
)
|
||||
addFragmentsWithTitle(mapOf(
|
||||
GradeDetailsFragment.newInstance() to getString(R.string.all_details),
|
||||
GradeSummaryFragment.newInstance() to getString(R.string.grade_menu_summary),
|
||||
GradeStatisticsFragment.newInstance() to getString(R.string.grade_menu_statistics)
|
||||
))
|
||||
}
|
||||
|
||||
with(binding.gradeViewPager) {
|
||||
|
@ -122,9 +120,11 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||
semesterSwitchMenu?.isVisible = show
|
||||
}
|
||||
|
||||
override fun showSemesterDialog(selectedIndex: Int, semesters: List<Semester>) {
|
||||
val choices = semesters.map { getString(R.string.grade_semester, it.semesterName) }
|
||||
.toTypedArray()
|
||||
override fun showSemesterDialog(selectedIndex: Int) {
|
||||
val choices = arrayOf(
|
||||
getString(R.string.grade_semester, 1),
|
||||
getString(R.string.grade_semester, 2)
|
||||
)
|
||||
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setSingleChoiceItems(choices, selectedIndex) { dialog, which ->
|
||||
|
@ -138,10 +138,7 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||
|
||||
override fun setCurrentSemesterName(semester: Int, schoolYear: Int) {
|
||||
subtitleString = getString(R.string.grade_subtitle, semester, schoolYear, schoolYear + 1)
|
||||
|
||||
if (isVisible) {
|
||||
(activity as MainView?)?.setViewSubTitle(subtitleString)
|
||||
}
|
||||
(activity as MainView).setViewSubTitle(subtitleString)
|
||||
}
|
||||
|
||||
fun onChildRefresh() {
|
||||
|
@ -153,8 +150,7 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||
}
|
||||
|
||||
override fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean) {
|
||||
(pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)
|
||||
?.onParentLoadData(semesterId, forceRefresh)
|
||||
(pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentLoadData(semesterId, forceRefresh)
|
||||
}
|
||||
|
||||
override fun notifyChildParentReselected(index: Int) {
|
||||
|
@ -165,6 +161,11 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||
(pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentChangeSemester()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putInt(SAVED_SEMESTER_KEY, presenter.selectedIndex)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
presenter.onDetachView()
|
||||
super.onDestroyView()
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue