Compare commits
58 Commits
Author | SHA1 | Date | |
---|---|---|---|
800a31f160 | |||
8b83b37b09 | |||
43e95cfdc6 | |||
ae2a697e01 | |||
3f5fbbc71b | |||
206b40ce1b | |||
43c56b5534 | |||
a1076539dc | |||
3c48264539 | |||
539cf2207b | |||
f0e897713c | |||
a448092008 | |||
f5b46707ff | |||
e6247d4428 | |||
d0869b235a | |||
464900d95b | |||
368274239e | |||
ee33197494 | |||
d3ea743707 | |||
976d4b8ce2 | |||
b77fc0d32a | |||
bd3716609e | |||
5b87cc9009 | |||
4f7be8d2cb | |||
8733e7782f | |||
e03b0dfa01 | |||
efafd2094a | |||
1560335749 | |||
5bee155f1e | |||
c9dc9a323f | |||
87e7e00705 | |||
57681b35ea | |||
8fb09d7b7d | |||
168f750863 | |||
3e1acbd3bf | |||
21ef2adcf6 | |||
3f6159e976 | |||
555b5ec112 | |||
60a9bcae46 | |||
eeb1341c1f | |||
c77b50d51b | |||
8644ce32d5 | |||
94506aca52 | |||
eee4e1f4b5 | |||
c1942d012f | |||
fe846b463a | |||
0ea2e68249 | |||
be0445b227 | |||
48249f3093 | |||
8d7110735d | |||
94957850c3 | |||
fa2cfc8427 | |||
3d467c43ba | |||
b76032044d | |||
495b84204c | |||
ea4b299de6 | |||
acb5e2afd4 | |||
50863d6ac2 |
75
.github/workflows/deploy-store.yml
vendored
Normal file
75
.github/workflows/deploy-store.yml
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
name: Deploy to app stores
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [ created ]
|
||||
|
||||
jobs:
|
||||
|
||||
deploy-google-play:
|
||||
name: Deploy to google play
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
environment: google-play
|
||||
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: Decrypt keys
|
||||
env:
|
||||
ENCRYPT_KEY: ${{ secrets.ENCRYPT_KEY }}
|
||||
SERVICES_ENCRYPT_KEY: ${{ secrets.SERVICES_ENCRYPT_KEY }}
|
||||
run: |
|
||||
gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/google-services.json.gpg
|
||||
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/key.p12.gpg
|
||||
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg
|
||||
- name: Upload apk to google play
|
||||
env:
|
||||
PLAY_KEY_ALIAS: ${{ secrets.PLAY_KEY_ALIAS }}
|
||||
PLAY_KEY_PASSWORD: ${{ secrets.PLAY_KEY_PASSWORD }}
|
||||
PLAY_SERVICE_ACCOUNT_EMAIL: ${{ secrets.PLAY_SERVICE_ACCOUNT_EMAIL }}
|
||||
PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }}
|
||||
run: ./gradlew publishPlayRelease -PenableFirebase --stacktrace;
|
||||
|
||||
deploy-app-gallery:
|
||||
name: Deploy to AppGallery
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
environment: app-gallery
|
||||
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: Decrypt keys
|
||||
env:
|
||||
ENCRYPT_KEY: ${{ secrets.ENCRYPT_KEY }}
|
||||
SERVICES_ENCRYPT_KEY: ${{ secrets.SERVICES_ENCRYPT_KEY }}
|
||||
run: |
|
||||
gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/agconnect-services.json.gpg
|
||||
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/key.p12.gpg
|
||||
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg
|
||||
- name: Build HMS version
|
||||
env:
|
||||
PLAY_KEY_ALIAS: ${{ secrets.PLAY_KEY_ALIAS }}
|
||||
PLAY_KEY_PASSWORD: ${{ secrets.PLAY_KEY_PASSWORD }}
|
||||
PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }}
|
||||
run: ./gradlew assembleHmsRelease --stacktrace
|
||||
- name: Upload APK to AppGallery
|
||||
env:
|
||||
AGC_CLIENT_ID: ${{ secrets.AGC_CLIENT_ID }}
|
||||
AGC_CLIENT_SECRET: ${{ secrets.AGC_CLIENT_SECRET }}
|
||||
run: ./gradlew publishHuaweiAppGalleryHmsRelease --stacktrace;
|
144
.github/workflows/deploy-test.yml
vendored
Normal file
144
.github/workflows/deploy-test.yml
vendored
Normal file
@ -0,0 +1,144 @@
|
||||
name: Deploy to app tests
|
||||
|
||||
on:
|
||||
push:
|
||||
# branches: [ develop ]
|
||||
branches: [ '!*' ]
|
||||
pull_request_target:
|
||||
# branches: [ develop ]
|
||||
branches: [ '!*' ]
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
deploy-appcenter:
|
||||
name: App Center
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
environment: app-center
|
||||
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: App Distribution
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
environment: app-distribution
|
||||
if: github.event_name != 'pull_request_target'
|
||||
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
|
42
.github/workflows/test.yml
vendored
42
.github/workflows/test.yml
vendored
@ -1,13 +1,11 @@
|
||||
name: Test and deploy
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ develop ]
|
||||
branches: [ master, develop ]
|
||||
tags: [ '*' ]
|
||||
pull_request:
|
||||
branches: [ develop ]
|
||||
|
||||
workflow_dispatch:
|
||||
branches: [ master, develop ]
|
||||
|
||||
jobs:
|
||||
unit-tests:
|
||||
@ -34,37 +32,3 @@ jobs:
|
||||
- uses: codecov/codecov-action@v1
|
||||
with:
|
||||
flags: unit
|
||||
|
||||
deploy-google-play:
|
||||
name: Deploy to google play
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
environment: google-play
|
||||
needs: [ unit-tests ]
|
||||
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
|
||||
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: Decrypt keys
|
||||
env:
|
||||
ENCRYPT_KEY: ${{ secrets.ENCRYPT_KEY }}
|
||||
SERVICES_ENCRYPT_KEY: ${{ secrets.SERVICES_ENCRYPT_KEY }}
|
||||
run: |
|
||||
gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/google-services.json.gpg
|
||||
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/key.p12.gpg
|
||||
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg
|
||||
- name: Upload apk to google play
|
||||
env:
|
||||
PLAY_KEY_ALIAS: ${{ secrets.PLAY_KEY_ALIAS }}
|
||||
PLAY_KEY_PASSWORD: ${{ secrets.PLAY_KEY_PASSWORD }}
|
||||
PLAY_SERVICE_ACCOUNT_EMAIL: ${{ secrets.PLAY_SERVICE_ACCOUNT_EMAIL }}
|
||||
PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }}
|
||||
run: ./gradlew publishPlayRelease -PenableFirebase --stacktrace;
|
||||
|
@ -32,7 +32,7 @@ Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica
|
||||
* powiadomienia np. o nowej ocenie
|
||||
* obsługa wielu kont wraz z możliwością zmiany nazwy ucznia
|
||||
* ciemny i czarny (AMOLED) motyw
|
||||
* tryb offilne
|
||||
* tryb offline
|
||||
* brak reklam
|
||||
|
||||
## Pobierz
|
||||
|
BIN
app/bitrise.jks.gpg
Normal file
BIN
app/bitrise.jks.gpg
Normal file
Binary file not shown.
@ -4,7 +4,10 @@ apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'dagger.hilt.android.plugin'
|
||||
apply plugin: 'com.google.firebase.crashlytics'
|
||||
apply plugin: 'com.github.triplet.play'
|
||||
apply plugin: 'ru.cian.huawei-publish'
|
||||
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'
|
||||
@ -18,8 +21,8 @@ android {
|
||||
testApplicationId "io.github.tests.wulkanowy"
|
||||
minSdkVersion 17
|
||||
targetSdkVersion 30
|
||||
versionCode 86
|
||||
versionName "1.1.0"
|
||||
versionCode 91
|
||||
versionName "1.1.5"
|
||||
multiDexEnabled true
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
@ -115,6 +118,7 @@ android {
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
useIR = true
|
||||
jvmTarget = "1.8"
|
||||
freeCompilerArgs += ["-Xopt-in=kotlin.RequiresOptIn", "-Xjvm-default=all"]
|
||||
}
|
||||
@ -134,32 +138,43 @@ play {
|
||||
serviceAccountCredentials = file('key.p12')
|
||||
defaultToAppBundles = false
|
||||
track = 'production'
|
||||
updatePriority = 3
|
||||
updatePriority = 1
|
||||
}
|
||||
|
||||
huaweiPublish {
|
||||
instances {
|
||||
hmsRelease {
|
||||
clientId = System.getenv("AGC_CLIENT_ID")
|
||||
clientSecret = System.getenv("AGC_CLIENT_SECRET")
|
||||
buildFormat = "apk"
|
||||
deployType = "draft"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
work_manager = "2.5.0"
|
||||
work_hilt = "1.0.0-alpha03"
|
||||
room = "2.3.0-beta02"
|
||||
work_hilt = "1.0.0-beta01"
|
||||
room = "2.3.0-rc01"
|
||||
chucker = "3.4.0"
|
||||
mockk = "1.10.6"
|
||||
mockk = "1.11.0"
|
||||
moshi = "1.11.0"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "io.github.wulkanowy:sdk:1.1.0"
|
||||
implementation "io.github.wulkanowy:sdk:1.1.5"
|
||||
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3'
|
||||
|
||||
implementation "androidx.core:core-ktx:1.3.2"
|
||||
implementation "androidx.activity:activity-ktx:1.2.0"
|
||||
implementation "androidx.activity:activity-ktx:1.2.2"
|
||||
implementation "androidx.appcompat:appcompat:1.2.0"
|
||||
implementation "androidx.appcompat:appcompat-resources:1.2.0"
|
||||
implementation "androidx.fragment:fragment-ktx:1.3.0"
|
||||
implementation "androidx.annotation:annotation:1.1.0"
|
||||
implementation "androidx.fragment:fragment-ktx:1.3.2"
|
||||
implementation "androidx.annotation:annotation:1.2.0"
|
||||
implementation "androidx.multidex:multidex:2.0.1"
|
||||
|
||||
implementation "androidx.preference:preference-ktx:1.1.1"
|
||||
@ -169,14 +184,14 @@ dependencies {
|
||||
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.1.1"
|
||||
implementation "com.github.wulkanowy:material-chips-input:2.2.0"
|
||||
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
|
||||
implementation 'com.mikhaellopez:circularimageview:4.2.0'
|
||||
|
||||
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.3.1"
|
||||
|
||||
implementation "androidx.room:room-runtime:$room"
|
||||
implementation "androidx.room:room-ktx:$room"
|
||||
@ -203,17 +218,14 @@ dependencies {
|
||||
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
|
||||
implementation 'me.xdrop:fuzzywuzzy:1.3.1'
|
||||
|
||||
playImplementation platform('com.google.firebase:firebase-bom:26.6.0')
|
||||
playImplementation platform('com.google.firebase:firebase-bom:26.7.0')
|
||||
playImplementation 'com.google.firebase:firebase-analytics-ktx'
|
||||
playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx'
|
||||
playImplementation "com.google.firebase:firebase-inappmessaging-ktx"
|
||||
playImplementation 'com.google.firebase:firebase-messaging:'
|
||||
playImplementation 'com.google.firebase:firebase-crashlytics:'
|
||||
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.2.0.301'
|
||||
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.5.1.200'
|
||||
|
||||
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
|
||||
|
||||
@ -239,6 +251,3 @@ dependencies {
|
||||
androidTestImplementation "io.mockk:mockk-android:$mockk"
|
||||
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,33 +1,21 @@
|
||||
# Optimizations
|
||||
-optimizationpasses 5
|
||||
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
|
||||
-dontusemixedcaseclassnames
|
||||
-dontskipnonpubliclibraryclasses
|
||||
-dontskipnonpubliclibraryclassmembers
|
||||
-dontpreverify
|
||||
# General
|
||||
-dontobfuscate
|
||||
-allowaccessmodification
|
||||
-repackageclasses ''
|
||||
-verbose
|
||||
|
||||
|
||||
#Keep all wulkanowy files
|
||||
#Config for wulkanowy
|
||||
-keep class io.github.wulkanowy.** {*;}
|
||||
|
||||
|
||||
#Config for anallitycs
|
||||
-keepattributes *Annotation*
|
||||
#Config for firebase crashlitycs
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
-keep class com.crashlytics.** {*;}
|
||||
-keep public class * extends java.lang.Exception
|
||||
-dontwarn com.crashlytics.**
|
||||
|
||||
|
||||
#Config for OkHttp
|
||||
#Config for Okio and OkHttp
|
||||
-dontwarn javax.annotation.**
|
||||
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
|
||||
-dontwarn org.codehaus.mojo.animal_sniffer.*
|
||||
-dontwarn okhttp3.internal.platform.ConscryptPlatform
|
||||
-dontwarn javax.annotation.**
|
||||
|
||||
|
||||
#Config for MPAndroidChart
|
||||
@ -36,10 +24,3 @@
|
||||
|
||||
#Config for Material Components
|
||||
-keep class com.google.android.material.tabs.** { *; }
|
||||
|
||||
|
||||
#Config for About Libraries
|
||||
-keep class .R
|
||||
-keep class **.R$* {
|
||||
<fields>;
|
||||
}
|
||||
|
@ -9,6 +9,6 @@ import javax.inject.Singleton
|
||||
@Dao
|
||||
interface RecipientDao : BaseDao<Recipient> {
|
||||
|
||||
@Query("SELECT * FROM Recipients WHERE student_id = :userLoginId AND unit_id = :unitId AND role = :role")
|
||||
suspend fun loadAll(userLoginId: Int, unitId: Int, role: Int): List<Recipient>
|
||||
@Query("SELECT * FROM Recipients WHERE student_id = :studentId AND unit_id = :unitId AND role = :role")
|
||||
suspend fun loadAll(studentId: Int, unitId: Int, role: Int): List<Recipient>
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import java.io.Serializable
|
||||
data class Recipient(
|
||||
|
||||
@ColumnInfo(name = "student_id")
|
||||
val userLoginId: Int,
|
||||
val studentId: Int,
|
||||
|
||||
@ColumnInfo(name = "real_id")
|
||||
val realId: String,
|
||||
|
@ -5,7 +5,7 @@ import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
|
||||
|
||||
fun List<SdkRecipient>.mapToEntities(userLoginId: Int) = map {
|
||||
Recipient(
|
||||
userLoginId = userLoginId,
|
||||
studentId = userLoginId,
|
||||
realId = it.id,
|
||||
realName = it.name,
|
||||
name = it.shortName,
|
||||
|
@ -6,7 +6,7 @@ import io.github.wulkanowy.sdk.pojo.ReportingUnit as SdkReportingUnit
|
||||
|
||||
fun List<SdkReportingUnit>.mapToEntities(student: Student) = map {
|
||||
ReportingUnit(
|
||||
studentId = student.studentId,
|
||||
studentId = student.id.toInt(),
|
||||
unitId = it.id,
|
||||
roles = it.roles,
|
||||
senderId = it.senderId,
|
||||
|
@ -2,6 +2,7 @@ package io.github.wulkanowy.data.repositories
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode
|
||||
@ -145,6 +146,10 @@ class PreferencesRepository @Inject constructor(
|
||||
R.bool.pref_default_subjects_without_grades
|
||||
)
|
||||
|
||||
var isKitkatDialogDisabled: Boolean
|
||||
get() = sharedPref.getBoolean("kitkat_dialog_disabled", false)
|
||||
set(value) = sharedPref.edit { putBoolean("kitkat_dialog_disabled", value) }
|
||||
|
||||
private fun getString(id: Int, default: Int) = getString(context.getString(id), default)
|
||||
|
||||
private fun getString(id: String, default: Int) =
|
||||
|
@ -19,22 +19,22 @@ class RecipientRepository @Inject constructor(
|
||||
) {
|
||||
|
||||
suspend fun refreshRecipients(student: Student, unit: ReportingUnit, role: Int) {
|
||||
val new = sdk.init(student).getRecipients(unit.unitId, role).mapToEntities(unit.senderId)
|
||||
val old = recipientDb.loadAll(unit.senderId, unit.unitId, role)
|
||||
val new = sdk.init(student).getRecipients(unit.unitId, role).mapToEntities(unit.studentId)
|
||||
val old = recipientDb.loadAll(unit.studentId, unit.unitId, role)
|
||||
|
||||
recipientDb.deleteAll(old uniqueSubtract new)
|
||||
recipientDb.insertAll(new uniqueSubtract old)
|
||||
}
|
||||
|
||||
suspend fun getRecipients(student: Student, unit: ReportingUnit, role: Int): List<Recipient> {
|
||||
return recipientDb.loadAll(unit.senderId, unit.unitId, role).ifEmpty {
|
||||
return recipientDb.loadAll(unit.studentId, unit.unitId, role).ifEmpty {
|
||||
refreshRecipients(student, unit, role)
|
||||
|
||||
recipientDb.loadAll(unit.senderId, unit.unitId, role)
|
||||
recipientDb.loadAll(unit.studentId, unit.unitId, role)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getMessageRecipients(student: Student, message: Message): List<Recipient> {
|
||||
return sdk.init(student).getMessageRecipients(message.messageId, message.senderId).mapToEntities(student.userLoginId)
|
||||
return sdk.init(student).getMessageRecipients(message.messageId, message.senderId).mapToEntities(student.studentId)
|
||||
}
|
||||
}
|
||||
|
@ -18,25 +18,25 @@ class ReportingUnitRepository @Inject constructor(
|
||||
|
||||
suspend fun refreshReportingUnits(student: Student) {
|
||||
val new = sdk.init(student).getReportingUnits().mapToEntities(student)
|
||||
val old = reportingUnitDb.load(student.studentId)
|
||||
val old = reportingUnitDb.load(student.id.toInt())
|
||||
|
||||
reportingUnitDb.deleteAll(old.uniqueSubtract(new))
|
||||
reportingUnitDb.insertAll(new.uniqueSubtract(old))
|
||||
}
|
||||
|
||||
suspend fun getReportingUnits(student: Student): List<ReportingUnit> {
|
||||
return reportingUnitDb.load(student.studentId).ifEmpty {
|
||||
return reportingUnitDb.load(student.id.toInt()).ifEmpty {
|
||||
refreshReportingUnits(student)
|
||||
|
||||
reportingUnitDb.load(student.studentId)
|
||||
reportingUnitDb.load(student.id.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getReportingUnit(student: Student, unitId: Int): ReportingUnit? {
|
||||
return reportingUnitDb.loadOne(student.studentId, unitId) ?: run {
|
||||
return reportingUnitDb.loadOne(student.id.toInt(), unitId) ?: run {
|
||||
refreshReportingUnits(student)
|
||||
|
||||
return reportingUnitDb.loadOne(student.studentId, unitId)
|
||||
return reportingUnitDb.loadOne(student.id.toInt(), unitId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,25 @@
|
||||
package io.github.wulkanowy.ui.base
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.PorterDuff
|
||||
import android.view.LayoutInflater
|
||||
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.ItemAccountBinding
|
||||
import io.github.wulkanowy.utils.createNameInitialsDrawable
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.nickOrName
|
||||
import javax.inject.Inject
|
||||
|
||||
class WidgetConfigureAdapter @Inject constructor() : RecyclerView.Adapter<WidgetConfigureAdapter.ItemViewHolder>() {
|
||||
class WidgetConfigureAdapter @Inject constructor() :
|
||||
RecyclerView.Adapter<WidgetConfigureAdapter.ItemViewHolder>() {
|
||||
|
||||
var items = emptyList<Pair<Student, Boolean>>()
|
||||
var items = emptyList<StudentWithSemesters>()
|
||||
|
||||
var selectedId = -1L
|
||||
|
||||
var onClickListener: (Student) -> Unit = {}
|
||||
|
||||
@ -26,17 +31,33 @@ class WidgetConfigureAdapter @Inject constructor() : RecyclerView.Adapter<Widget
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
|
||||
val (student, isCurrent) = items[position]
|
||||
val (student, semesters) = items[position]
|
||||
val semester = semesters.maxByOrNull { it.semesterId }
|
||||
val context = holder.binding.root.context
|
||||
val checkBackgroundColor = context.getThemeAttrColor(R.attr.colorSurface)
|
||||
val avatar = context.createNameInitialsDrawable(student.nickOrName, student.avatarColor)
|
||||
val isDuplicatedStudent = items.filter {
|
||||
val studentToCompare = it.student
|
||||
|
||||
studentToCompare.studentId == student.studentId
|
||||
&& studentToCompare.schoolSymbol == student.schoolSymbol
|
||||
&& studentToCompare.symbol == student.symbol
|
||||
}.size > 1
|
||||
|
||||
with(holder.binding) {
|
||||
accountItemName.text = "${student.nickOrName} ${student.className}"
|
||||
accountItemName.text = "${student.nickOrName} ${semester?.diaryName.orEmpty()}"
|
||||
accountItemSchool.text = student.schoolName
|
||||
accountItemImage.setImageDrawable(avatar)
|
||||
|
||||
with(accountItemImage) {
|
||||
val colorImage = if (isCurrent) context.getThemeAttrColor(R.attr.colorPrimary)
|
||||
else context.getThemeAttrColor(R.attr.colorOnSurface, 153)
|
||||
with(accountItemAccountType) {
|
||||
setText(if (student.isParent) R.string.account_type_parent else R.string.account_type_student)
|
||||
isVisible = isDuplicatedStudent
|
||||
}
|
||||
|
||||
setColorFilter(colorImage, PorterDuff.Mode.SRC_IN)
|
||||
with(accountItemCheck) {
|
||||
isVisible = student.id == selectedId
|
||||
borderColor = checkBackgroundColor
|
||||
circleColor = checkBackgroundColor
|
||||
}
|
||||
|
||||
root.setOnClickListener { onClickListener(student) }
|
||||
|
@ -74,9 +74,8 @@ class AccountAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.V
|
||||
studentWithSemesters: StudentWithSemesters
|
||||
) {
|
||||
val context = binding.root.context
|
||||
val student = studentWithSemesters.student
|
||||
val semesters = studentWithSemesters.semesters
|
||||
val diary = semesters.maxByOrNull { it.semesterId }
|
||||
val (student, semesters) = studentWithSemesters
|
||||
val semester = 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)
|
||||
@ -90,7 +89,7 @@ class AccountAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.V
|
||||
}.size > 1 && isAccountQuickDialogMode
|
||||
|
||||
with(binding) {
|
||||
accountItemName.text = "${student.nickOrName} ${diary?.diaryName.orEmpty()}"
|
||||
accountItemName.text = "${student.nickOrName} ${semester?.diaryName.orEmpty()}"
|
||||
accountItemSchool.text = studentWithSemesters.student.schoolName
|
||||
accountItemImage.setImageDrawable(avatar)
|
||||
|
||||
|
@ -192,7 +192,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) {
|
||||
|
@ -190,35 +190,48 @@ 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)
|
||||
showContent(true)
|
||||
updateData(it.data.let { items ->
|
||||
if (prefRepository.isShowPresent) items
|
||||
else items.filter { item -> !item.presence }
|
||||
}.sortedBy { item -> item.number })
|
||||
showEmpty(filteredAttendance.isEmpty())
|
||||
showContent(filteredAttendance.isNotEmpty())
|
||||
updateData(filteredAttendance.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(it.data!!.let { items ->
|
||||
if (prefRepository.isShowPresent) items
|
||||
else items.filter { item -> !item.presence }
|
||||
}.sortedBy { item -> item.number })
|
||||
showEmpty(it.data.isEmpty())
|
||||
updateData(filteredAttendance.sortedBy { item -> item.number })
|
||||
showEmpty(filteredAttendance.isEmpty())
|
||||
showErrorView(false)
|
||||
showContent(it.data.isNotEmpty())
|
||||
showExcuseButton(it.data.any { item -> item.excusable })
|
||||
showContent(filteredAttendance.isNotEmpty())
|
||||
showExcuseButton(filteredAttendance.any { item -> item.excusable })
|
||||
}
|
||||
analytics.logEvent(
|
||||
"load_data",
|
||||
|
@ -68,7 +68,7 @@ class GradeAverageProvider @Inject constructor(
|
||||
forceRefresh: Boolean,
|
||||
averageMode: GradeAverageMode
|
||||
): Flow<Resource<List<GradeSubject>>> {
|
||||
val gradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc
|
||||
val isGradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc
|
||||
val selectedSemester = semesters.single { it.semesterId == semesterId }
|
||||
val firstSemester =
|
||||
semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 }
|
||||
@ -78,8 +78,7 @@ class GradeAverageProvider @Inject constructor(
|
||||
|
||||
if (selectedSemester == firstSemester) return selectedSemesterGradeSubjects
|
||||
|
||||
val firstSemesterGradeSubjects =
|
||||
getGradeSubjects(student, firstSemester, forceRefresh)
|
||||
val firstSemesterGradeSubjects = getGradeSubjects(student, firstSemester, forceRefresh)
|
||||
|
||||
return selectedSemesterGradeSubjects.combine(firstSemesterGradeSubjects) { secondSemesterGradeSubject, firstSemesterGradeSubject ->
|
||||
if (firstSemesterGradeSubject.status == Status.ERROR) {
|
||||
@ -87,9 +86,9 @@ class GradeAverageProvider @Inject constructor(
|
||||
}
|
||||
|
||||
val isAnyVulcanAverageInFirstSemester =
|
||||
firstSemesterGradeSubject.data.orEmpty().any { it.average != .0 }
|
||||
firstSemesterGradeSubject.data.orEmpty().any { it.isVulcanAverage }
|
||||
val isAnyVulcanAverageInSecondSemester =
|
||||
secondSemesterGradeSubject.data.orEmpty().any { it.average != .0 }
|
||||
secondSemesterGradeSubject.data.orEmpty().any { it.isVulcanAverage }
|
||||
|
||||
val updatedData = secondSemesterGradeSubject.data?.map { secondSemesterSubject ->
|
||||
val firstSemesterSubject = firstSemesterGradeSubject.data.orEmpty()
|
||||
@ -99,7 +98,7 @@ class GradeAverageProvider @Inject constructor(
|
||||
calculateAllYearAverage(
|
||||
student = student,
|
||||
isAnyVulcanAverage = isAnyVulcanAverageInFirstSemester || isAnyVulcanAverageInSecondSemester,
|
||||
gradeAverageForceCalc = gradeAverageForceCalc,
|
||||
isGradeAverageForceCalc = isGradeAverageForceCalc,
|
||||
secondSemesterSubject = secondSemesterSubject,
|
||||
firstSemesterSubject = firstSemesterSubject
|
||||
)
|
||||
@ -107,7 +106,7 @@ class GradeAverageProvider @Inject constructor(
|
||||
calculateBothSemestersAverage(
|
||||
student = student,
|
||||
isAnyVulcanAverage = isAnyVulcanAverageInFirstSemester || isAnyVulcanAverageInSecondSemester,
|
||||
gradeAverageForceCalc = gradeAverageForceCalc,
|
||||
isGradeAverageForceCalc = isGradeAverageForceCalc,
|
||||
secondSemesterSubject = secondSemesterSubject,
|
||||
firstSemesterSubject = firstSemesterSubject
|
||||
)
|
||||
@ -121,10 +120,10 @@ class GradeAverageProvider @Inject constructor(
|
||||
private fun calculateAllYearAverage(
|
||||
student: Student,
|
||||
isAnyVulcanAverage: Boolean,
|
||||
gradeAverageForceCalc: Boolean,
|
||||
isGradeAverageForceCalc: Boolean,
|
||||
secondSemesterSubject: GradeSubject,
|
||||
firstSemesterSubject: GradeSubject?
|
||||
) = if (!isAnyVulcanAverage || gradeAverageForceCalc) {
|
||||
) = if (!isAnyVulcanAverage || isGradeAverageForceCalc) {
|
||||
val updatedSecondSemesterGrades =
|
||||
secondSemesterSubject.grades.updateModifiers(student)
|
||||
val updatedFirstSemesterGrades =
|
||||
@ -138,13 +137,13 @@ class GradeAverageProvider @Inject constructor(
|
||||
private fun calculateBothSemestersAverage(
|
||||
student: Student,
|
||||
isAnyVulcanAverage: Boolean,
|
||||
gradeAverageForceCalc: Boolean,
|
||||
isGradeAverageForceCalc: Boolean,
|
||||
secondSemesterSubject: GradeSubject,
|
||||
firstSemesterSubject: GradeSubject?
|
||||
): Double {
|
||||
val divider = if (secondSemesterSubject.grades.any { it.weightValue > .0 }) 2 else 1
|
||||
|
||||
return if (!isAnyVulcanAverage || gradeAverageForceCalc) {
|
||||
return if (!isAnyVulcanAverage || isGradeAverageForceCalc) {
|
||||
val secondSemesterAverage =
|
||||
secondSemesterSubject.grades.updateModifiers(student).calcAverage()
|
||||
val firstSemesterAverage = firstSemesterSubject?.grades?.updateModifiers(student)
|
||||
@ -161,7 +160,7 @@ class GradeAverageProvider @Inject constructor(
|
||||
semester: Semester,
|
||||
forceRefresh: Boolean
|
||||
): Flow<Resource<List<GradeSubject>>> {
|
||||
val gradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc
|
||||
val isGradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc
|
||||
|
||||
return gradeRepository.getGrades(student, semester, forceRefresh = forceRefresh)
|
||||
.map { res ->
|
||||
@ -170,20 +169,21 @@ class GradeAverageProvider @Inject constructor(
|
||||
val allGrades = details.orEmpty().groupBy { it.subject }
|
||||
|
||||
val items = summaries?.emulateEmptySummaries(
|
||||
student,
|
||||
semester,
|
||||
allGrades.toList(),
|
||||
isAnyAverage
|
||||
student = student,
|
||||
semester = semester,
|
||||
grades = allGrades.toList(),
|
||||
calcAverage = isAnyAverage
|
||||
)?.map { summary ->
|
||||
val grades = allGrades[summary.subject].orEmpty()
|
||||
GradeSubject(
|
||||
subject = summary.subject,
|
||||
average = if (!isAnyAverage || gradeAverageForceCalc) {
|
||||
average = if (!isAnyAverage || isGradeAverageForceCalc) {
|
||||
grades.updateModifiers(student).calcAverage()
|
||||
} else summary.average,
|
||||
points = summary.pointsSum,
|
||||
summary = summary,
|
||||
grades = grades
|
||||
grades = grades,
|
||||
isVulcanAverage = isAnyAverage
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ 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
|
||||
@ -121,11 +122,9 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
||||
semesterSwitchMenu?.isVisible = show
|
||||
}
|
||||
|
||||
override fun showSemesterDialog(selectedIndex: Int) {
|
||||
val choices = arrayOf(
|
||||
getString(R.string.grade_semester, 1),
|
||||
getString(R.string.grade_semester, 2)
|
||||
)
|
||||
override fun showSemesterDialog(selectedIndex: Int, semesters: List<Semester>) {
|
||||
val choices = semesters.map { getString(R.string.grade_semester, it.semesterName) }
|
||||
.toTypedArray()
|
||||
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setSingleChoiceItems(choices, selectedIndex) { dialog, which ->
|
||||
|
@ -49,7 +49,9 @@ class GradePresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onSemesterSwitch(): Boolean {
|
||||
if (semesters.isNotEmpty()) view?.showSemesterDialog(selectedIndex - 1)
|
||||
if (semesters.isNotEmpty()) {
|
||||
view?.showSemesterDialog(selectedIndex - 1, semesters.take(2))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -137,11 +139,17 @@ class GradePresenter @Inject constructor(
|
||||
|
||||
private fun loadChild(index: Int, forceRefresh: Boolean = false) {
|
||||
Timber.d("Load grade tab child. Selected semester: $selectedIndex, semesters: ${semesters.joinToString { it.semesterName.toString() }}")
|
||||
semesters.first { it.semesterName == selectedIndex }.semesterId.also {
|
||||
if (forceRefresh || loadedSemesterId[index] != it) {
|
||||
Timber.i("Load grade child view index: $index")
|
||||
view?.notifyChildLoadData(index, it, forceRefresh)
|
||||
}
|
||||
|
||||
val newSelectedSemesterId = try {
|
||||
semesters.first { it.semesterName == selectedIndex }.semesterId
|
||||
} catch (e: NoSuchElementException) {
|
||||
Timber.e(e, "Selected semester no exists")
|
||||
return
|
||||
}
|
||||
|
||||
if (forceRefresh || loadedSemesterId[index] != newSelectedSemesterId) {
|
||||
Timber.i("Load grade child view index: $index")
|
||||
view?.notifyChildLoadData(index, newSelectedSemesterId, forceRefresh)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,5 +8,6 @@ data class GradeSubject(
|
||||
val average: Double,
|
||||
val points: String,
|
||||
val summary: GradeSummary,
|
||||
val grades: List<Grade>
|
||||
val grades: List<Grade>,
|
||||
val isVulcanAverage: Boolean
|
||||
)
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.ui.modules.grade
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface GradeView : BaseView {
|
||||
@ -18,7 +19,7 @@ interface GradeView : BaseView {
|
||||
|
||||
fun showSemesterSwitch(show: Boolean)
|
||||
|
||||
fun showSemesterDialog(selectedIndex: Int)
|
||||
fun showSemesterDialog(selectedIndex: Int, semesters: List<Semester>)
|
||||
|
||||
fun setCurrentSemesterName(semester: Int, schoolYear: Int)
|
||||
|
||||
|
@ -162,7 +162,7 @@ class LoginFormPresenter @Inject constructor(
|
||||
view?.setErrorEmailRequired()
|
||||
isCorrect = false
|
||||
}
|
||||
if ("@" in login && "login" !in host && "email" !in host) {
|
||||
if ("@" in login && "||" !in login && "login" !in host && "email" !in host) {
|
||||
val emailHost = login.substringAfter("@")
|
||||
val emailDomain = URL(host).host
|
||||
if (emailHost != emailDomain) {
|
||||
|
@ -9,6 +9,7 @@ import android.view.View.VISIBLE
|
||||
import android.webkit.JavascriptInterface
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
@ -42,10 +43,12 @@ class LoginRecoverFragment :
|
||||
private lateinit var hostSymbols: Array<String>
|
||||
|
||||
override val recoverHostValue: String
|
||||
get() = hostValues.getOrNull(hostKeys.indexOf(bindingLocal.loginRecoverHost.text.toString())).orEmpty()
|
||||
get() = hostValues.getOrNull(hostKeys.indexOf(bindingLocal.loginRecoverHost.text.toString()))
|
||||
.orEmpty()
|
||||
|
||||
override val formHostSymbol: String
|
||||
get() = hostSymbols.getOrNull(hostKeys.indexOf(bindingLocal.loginRecoverHost.text.toString())).orEmpty()
|
||||
get() = hostSymbols.getOrNull(hostKeys.indexOf(bindingLocal.loginRecoverHost.text.toString()))
|
||||
.orEmpty()
|
||||
|
||||
override val recoverNameValue: String
|
||||
get() = bindingLocal.loginRecoverName.text.toString().trim()
|
||||
@ -82,7 +85,9 @@ class LoginRecoverFragment :
|
||||
|
||||
with(bindingLocal.loginRecoverHost) {
|
||||
setText(hostKeys.getOrNull(0).orEmpty())
|
||||
setAdapter(LoginSymbolAdapter(context, R.layout.support_simple_spinner_dropdown_item, hostKeys))
|
||||
setAdapter(
|
||||
LoginSymbolAdapter(context, R.layout.support_simple_spinner_dropdown_item, hostKeys)
|
||||
)
|
||||
setOnClickListener { if (bindingLocal.loginRecoverFormContainer.visibility == GONE) dismissDropDown() }
|
||||
}
|
||||
}
|
||||
@ -127,6 +132,7 @@ class LoginRecoverFragment :
|
||||
|
||||
override fun showErrorView(show: Boolean) {
|
||||
bindingLocal.loginRecoverError.visibility = if (show) VISIBLE else GONE
|
||||
bindingLocal.loginRecoverErrorDetails.isVisible = true
|
||||
}
|
||||
|
||||
override fun setErrorDetails(message: String) {
|
||||
@ -166,7 +172,7 @@ class LoginRecoverFragment :
|
||||
with(bindingLocal.loginRecoverWebView) {
|
||||
settings.javaScriptEnabled = true
|
||||
webViewClient = object : WebViewClient() {
|
||||
private var recoverWebViewSuccess: Boolean = true
|
||||
private var recoverWebViewSuccess = true
|
||||
|
||||
override fun onPageFinished(view: WebView?, url: String?) {
|
||||
if (recoverWebViewSuccess) {
|
||||
@ -175,10 +181,16 @@ class LoginRecoverFragment :
|
||||
} else {
|
||||
showProgress(false)
|
||||
showErrorView(true)
|
||||
bindingLocal.loginRecoverErrorDetails.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onReceivedError(view: WebView, errorCode: Int, description: String, failingUrl: String) {
|
||||
override fun onReceivedError(
|
||||
view: WebView,
|
||||
errorCode: Int,
|
||||
description: String,
|
||||
failingUrl: String
|
||||
) {
|
||||
recoverWebViewSuccess = false
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import androidx.appcompat.app.AlertDialog
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
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.ActivityWidgetConfigureBinding
|
||||
import io.github.wulkanowy.ui.base.BaseActivity
|
||||
import io.github.wulkanowy.ui.base.WidgetConfigureAdapter
|
||||
@ -38,7 +38,9 @@ class LuckyNumberWidgetConfigureActivity :
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setResult(RESULT_CANCELED)
|
||||
setContentView(ActivityWidgetConfigureBinding.inflate(layoutInflater).apply { binding = this }.root)
|
||||
setContentView(
|
||||
ActivityWidgetConfigureBinding.inflate(layoutInflater).apply { binding = this }.root
|
||||
)
|
||||
|
||||
intent.extras.let {
|
||||
presenter.onAttachView(this, it?.getInt(EXTRA_APPWIDGET_ID))
|
||||
@ -70,8 +72,9 @@ class LuckyNumberWidgetConfigureActivity :
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun updateData(data: List<Pair<Student, Boolean>>) {
|
||||
override fun updateData(data: List<StudentWithSemesters>, selectedStudentId: Long) {
|
||||
with(configureAdapter) {
|
||||
selectedId = selectedStudentId
|
||||
items = data
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
@ -51,16 +51,17 @@ class LuckyNumberWidgetConfigurePresenter @Inject constructor(
|
||||
when (it.status) {
|
||||
Status.LOADING -> Timber.d("Lucky number widget configure students data load")
|
||||
Status.SUCCESS -> {
|
||||
val widgetId = appWidgetId?.let { id -> sharedPref.getLong(getStudentWidgetKey(id), 0) }
|
||||
val selectedStudentId = appWidgetId?.let { id ->
|
||||
sharedPref.getLong(getStudentWidgetKey(id), 0)
|
||||
} ?: -1
|
||||
|
||||
when {
|
||||
it.data!!.isEmpty() -> view?.openLoginView()
|
||||
it.data.size == 1 -> {
|
||||
selectedStudent = it.data.single().student
|
||||
view?.showThemeDialog()
|
||||
}
|
||||
else -> view?.updateData(it.data.map { entity ->
|
||||
entity.student to (entity.student.id == widgetId)
|
||||
})
|
||||
else -> view?.updateData(it.data, selectedStudentId)
|
||||
}
|
||||
}
|
||||
Status.ERROR -> errorHandler.dispatch(it.error!!)
|
||||
|
@ -1,6 +1,6 @@
|
||||
package io.github.wulkanowy.ui.modules.luckynumberwidget
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface LuckyNumberWidgetConfigureView : BaseView {
|
||||
@ -9,7 +9,7 @@ interface LuckyNumberWidgetConfigureView : BaseView {
|
||||
|
||||
fun showThemeDialog()
|
||||
|
||||
fun updateData(data: List<Pair<Student, Boolean>>)
|
||||
fun updateData(data: List<StudentWithSemesters>, selectedStudentId: Long)
|
||||
|
||||
fun updateLuckyNumberWidget(widgetId: Int)
|
||||
|
||||
|
@ -291,7 +291,7 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
): Boolean {
|
||||
val fragment =
|
||||
supportFragmentManager.fragmentFactory.instantiate(classLoader, pref.fragment)
|
||||
navController.pushFragment(fragment)
|
||||
pushView(fragment)
|
||||
return true
|
||||
}
|
||||
|
||||
@ -305,6 +305,8 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
}
|
||||
|
||||
override fun switchMenuView(position: Int) {
|
||||
if (supportFragmentManager.isStateSaved) return
|
||||
|
||||
analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName)
|
||||
navController.switchTab(position)
|
||||
}
|
||||
@ -322,7 +324,7 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
}
|
||||
|
||||
override fun showAccountPicker(studentWithSemesters: List<StudentWithSemesters>) {
|
||||
navController.showDialogFragment(AccountQuickDialog.newInstance(studentWithSemesters))
|
||||
showDialogFragment(AccountQuickDialog.newInstance(studentWithSemesters))
|
||||
}
|
||||
|
||||
override fun showActionBarElevation(show: Boolean) {
|
||||
@ -338,16 +340,31 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
(navController.currentStack?.getOrNull(0) as? MainView.MainChildView)?.onFragmentChanged()
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
fun showDialogFragment(dialog: DialogFragment) {
|
||||
if (supportFragmentManager.isStateSaved) return
|
||||
|
||||
//Deprecated method is used here to avoid fragnav bug
|
||||
if (navController.currentDialogFrag?.fragmentManager == null) {
|
||||
FragNavController::class.java.getDeclaredField("mCurrentDialogFrag").apply {
|
||||
isAccessible = true
|
||||
set(navController, null)
|
||||
}
|
||||
}
|
||||
|
||||
navController.showDialogFragment(dialog)
|
||||
}
|
||||
|
||||
fun pushView(fragment: Fragment) {
|
||||
if (supportFragmentManager.isStateSaved) return
|
||||
|
||||
analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName)
|
||||
navController.pushFragment(fragment)
|
||||
}
|
||||
|
||||
override fun popView(depth: Int) {
|
||||
if (supportFragmentManager.isStateSaved) return
|
||||
|
||||
analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName)
|
||||
navController.safelyPopFragments(depth)
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ class MainPresenter @Inject constructor(
|
||||
|
||||
private fun showCurrentStudentAvatar() {
|
||||
val currentStudent =
|
||||
studentsWitSemesters!!.single { it.student.isCurrent }.student
|
||||
studentsWitSemesters?.singleOrNull { it.student.isCurrent }?.student ?: return
|
||||
|
||||
view?.showStudentAvatar(currentStudent)
|
||||
}
|
||||
|
@ -53,13 +53,14 @@ class SchoolFragment : BaseFragment<FragmentSchoolBinding>(R.layout.fragment_sch
|
||||
|
||||
override fun updateData(data: School) {
|
||||
with(binding) {
|
||||
schoolName.text = data.name
|
||||
schoolAddress.text = data.address.ifBlank { "-" }
|
||||
val noDataString = getString(R.string.all_no_data)
|
||||
schoolName.text = data.name.ifBlank { noDataString }
|
||||
schoolAddress.text = data.address.ifBlank { noDataString }
|
||||
schoolAddressButton.visibility = if (data.address.isNotBlank()) VISIBLE else GONE
|
||||
schoolTelephone.text = data.contact.ifBlank { "-" }
|
||||
schoolTelephone.text = data.contact.ifBlank { noDataString }
|
||||
schoolTelephoneButton.visibility = if (data.contact.isNotBlank()) VISIBLE else GONE
|
||||
schoolHeadmaster.text = data.headmaster
|
||||
schoolPedagogue.text = data.pedagogue
|
||||
schoolHeadmaster.text = data.headmaster.ifBlank { noDataString }
|
||||
schoolPedagogue.text = data.pedagogue.ifBlank { noDataString }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,17 +3,23 @@ package io.github.wulkanowy.ui.modules.splash
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import android.widget.Toast.LENGTH_LONG
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.ui.base.BaseActivity
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.openInternetBrowser
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SplashActivity : BaseActivity<SplashPresenter, ViewBinding>(), SplashView {
|
||||
|
||||
@Inject
|
||||
lateinit var appInfo: AppInfo
|
||||
|
||||
@Inject
|
||||
override lateinit var presenter: SplashPresenter
|
||||
|
||||
@ -40,4 +46,14 @@ class SplashActivity : BaseActivity<SplashPresenter, ViewBinding>(), SplashView
|
||||
override fun showError(text: String, error: Throwable) {
|
||||
Toast.makeText(this, text, LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
override fun showKitkatView() {
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(R.string.drop_kitkat_title)
|
||||
.setMessage(R.string.drop_kitkat_content)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.setNeutralButton(R.string.drop_kitkat_again) { _, _ -> presenter.onNeutralButtonSelected() }
|
||||
.setOnDismissListener { presenter.onKitkatViewDismissed() }
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
package io.github.wulkanowy.ui.modules.splash
|
||||
|
||||
import android.os.Build
|
||||
import io.github.wulkanowy.data.Status
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
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.flowWithResource
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import timber.log.Timber
|
||||
@ -11,25 +14,47 @@ import javax.inject.Inject
|
||||
|
||||
class SplashPresenter @Inject constructor(
|
||||
errorHandler: ErrorHandler,
|
||||
studentRepository: StudentRepository
|
||||
studentRepository: StudentRepository,
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
private val appInfo: AppInfo
|
||||
) : BasePresenter<SplashView>(errorHandler, studentRepository) {
|
||||
|
||||
private var externalUrl: String? = null
|
||||
|
||||
fun onAttachView(view: SplashView, externalUrl: String?) {
|
||||
super.onAttachView(view)
|
||||
this.externalUrl = externalUrl
|
||||
|
||||
if (appInfo.systemVersion < Build.VERSION_CODES.LOLLIPOP && !preferencesRepository.isKitkatDialogDisabled) {
|
||||
view.showKitkatView()
|
||||
} else {
|
||||
loadCorrectDataOrUser()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadCorrectDataOrUser() {
|
||||
if (!externalUrl.isNullOrBlank()) {
|
||||
return view.openExternalUrlAndFinish(externalUrl)
|
||||
view?.openExternalUrlAndFinish(externalUrl!!)
|
||||
return
|
||||
}
|
||||
|
||||
flowWithResource { studentRepository.isCurrentStudentSet() }.onEach {
|
||||
when (it.status) {
|
||||
Status.LOADING -> Timber.d("Is current user set check started")
|
||||
Status.SUCCESS -> with(view) {
|
||||
if (it.data!!) openMainView()
|
||||
else openLoginView()
|
||||
Status.SUCCESS -> {
|
||||
if (it.data!!) view?.openMainView()
|
||||
else view?.openLoginView()
|
||||
}
|
||||
Status.ERROR -> errorHandler.dispatch(it.error!!)
|
||||
}
|
||||
}.launch()
|
||||
}
|
||||
|
||||
fun onKitkatViewDismissed() {
|
||||
loadCorrectDataOrUser()
|
||||
}
|
||||
|
||||
fun onNeutralButtonSelected() {
|
||||
preferencesRepository.isKitkatDialogDisabled = true
|
||||
}
|
||||
}
|
||||
|
@ -9,4 +9,6 @@ interface SplashView : BaseView {
|
||||
fun openMainView()
|
||||
|
||||
fun openExternalUrlAndFinish(url: String)
|
||||
|
||||
fun showKitkatView()
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ class AdditionalLessonsFragment :
|
||||
additionalLessonsSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||
additionalLessonsSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||
additionalLessonsErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||
additionalLessonsErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||
|
||||
additionalLessonsPreviousButton.setOnClickListener { presenter.onPreviousDay() }
|
||||
additionalLessonsNavDate.setOnClickListener { presenter.onPickDate() }
|
||||
|
@ -80,6 +80,10 @@ class AdditionalLessonsPresenter @Inject constructor(
|
||||
loadData(currentDate, true)
|
||||
}
|
||||
|
||||
fun onDetailsClick() {
|
||||
view?.showErrorDetailsDialog(lastError)
|
||||
}
|
||||
|
||||
private fun setBaseDateOnHolidays() {
|
||||
flow {
|
||||
val student = studentRepository.getCurrentStudent()
|
||||
|
@ -12,7 +12,7 @@ import androidx.appcompat.app.AlertDialog
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
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.ActivityWidgetConfigureBinding
|
||||
import io.github.wulkanowy.ui.base.BaseActivity
|
||||
import io.github.wulkanowy.ui.base.WidgetConfigureAdapter
|
||||
@ -37,13 +37,19 @@ class TimetableWidgetConfigureActivity :
|
||||
|
||||
private var dialog: AlertDialog? = null
|
||||
|
||||
override public fun onCreate(savedInstanceState: Bundle?) {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setResult(RESULT_CANCELED)
|
||||
setContentView(ActivityWidgetConfigureBinding.inflate(layoutInflater).apply { binding = this }.root)
|
||||
setContentView(
|
||||
ActivityWidgetConfigureBinding.inflate(layoutInflater).apply { binding = this }.root
|
||||
)
|
||||
|
||||
intent.extras.let {
|
||||
presenter.onAttachView(this, it?.getInt(EXTRA_APPWIDGET_ID), it?.getBoolean(EXTRA_FROM_PROVIDER))
|
||||
presenter.onAttachView(
|
||||
this,
|
||||
it?.getInt(EXTRA_APPWIDGET_ID),
|
||||
it?.getBoolean(EXTRA_FROM_PROVIDER)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,6 +67,7 @@ class TimetableWidgetConfigureActivity :
|
||||
getString(R.string.widget_timetable_theme_light),
|
||||
getString(R.string.widget_timetable_theme_dark)
|
||||
)
|
||||
|
||||
if (appInfo.systemVersion >= Build.VERSION_CODES.Q) items += getString(R.string.widget_timetable_theme_system)
|
||||
|
||||
dialog = AlertDialog.Builder(this, R.style.WulkanowyTheme_WidgetAccountSwitcher)
|
||||
@ -72,8 +79,9 @@ class TimetableWidgetConfigureActivity :
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun updateData(data: List<Pair<Student, Boolean>>) {
|
||||
override fun updateData(data: List<StudentWithSemesters>, selectedStudentId: Long) {
|
||||
with(configureAdapter) {
|
||||
selectedId = selectedStudentId
|
||||
items = data
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
@ -25,7 +25,11 @@ class TimetableWidgetConfigurePresenter @Inject constructor(
|
||||
|
||||
private var selectedStudent: Student? = null
|
||||
|
||||
fun onAttachView(view: TimetableWidgetConfigureView, appWidgetId: Int?, isFromProvider: Boolean?) {
|
||||
fun onAttachView(
|
||||
view: TimetableWidgetConfigureView,
|
||||
appWidgetId: Int?,
|
||||
isFromProvider: Boolean?
|
||||
) {
|
||||
super.onAttachView(view)
|
||||
this.appWidgetId = appWidgetId
|
||||
this.isFromProvider = isFromProvider ?: false
|
||||
@ -56,16 +60,17 @@ class TimetableWidgetConfigurePresenter @Inject constructor(
|
||||
when (it.status) {
|
||||
Status.LOADING -> Timber.d("Timetable widget configure students data load")
|
||||
Status.SUCCESS -> {
|
||||
val widgetId = appWidgetId?.let { id -> sharedPref.getLong(getStudentWidgetKey(id), 0) }
|
||||
val selectedStudentId = appWidgetId?.let { id ->
|
||||
sharedPref.getLong(getStudentWidgetKey(id), 0)
|
||||
} ?: -1
|
||||
|
||||
when {
|
||||
it.data!!.isEmpty() -> view?.openLoginView()
|
||||
it.data.size == 1 && !isFromProvider -> {
|
||||
selectedStudent = it.data.single().student
|
||||
view?.showThemeDialog()
|
||||
}
|
||||
else -> view?.updateData(it.data.map { entity ->
|
||||
entity.student to (entity.student.id == widgetId)
|
||||
})
|
||||
else -> view?.updateData(it.data, selectedStudentId)
|
||||
}
|
||||
}
|
||||
Status.ERROR -> errorHandler.dispatch(it.error!!)
|
||||
|
@ -1,13 +1,13 @@
|
||||
package io.github.wulkanowy.ui.modules.timetablewidget
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface TimetableWidgetConfigureView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<Pair<Student, Boolean>>)
|
||||
fun updateData(data: List<StudentWithSemesters>, selectedStudentId: Long)
|
||||
|
||||
fun updateTimetableWidget(widgetId: Int)
|
||||
|
||||
|
@ -13,6 +13,8 @@ import android.content.Intent
|
||||
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.widget.RemoteViews
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
@ -25,6 +27,8 @@ import io.github.wulkanowy.services.widgets.TimetableWidgetService
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.AnalyticsHelper
|
||||
import io.github.wulkanowy.utils.createNameInitialsDrawable
|
||||
import io.github.wulkanowy.utils.getCompatColor
|
||||
import io.github.wulkanowy.utils.nextOrSameSchoolDay
|
||||
import io.github.wulkanowy.utils.nextSchoolDay
|
||||
import io.github.wulkanowy.utils.nickOrName
|
||||
@ -72,7 +76,8 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() {
|
||||
|
||||
fun getThemeWidgetKey(appWidgetId: Int) = "timetable_widget_theme_$appWidgetId"
|
||||
|
||||
fun getCurrentThemeWidgetKey(appWidgetId: Int) = "timetable_widget_current_theme_$appWidgetId"
|
||||
fun getCurrentThemeWidgetKey(appWidgetId: Int) =
|
||||
"timetable_widget_current_theme_$appWidgetId"
|
||||
}
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
@ -88,21 +93,29 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() {
|
||||
private suspend fun onUpdate(context: Context, intent: Intent) {
|
||||
if (intent.getStringExtra(EXTRA_BUTTON_TYPE) === null) {
|
||||
intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS)?.forEach { appWidgetId ->
|
||||
val student = getStudent(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId)
|
||||
val student =
|
||||
getStudent(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId)
|
||||
updateWidget(context, appWidgetId, now().nextOrSameSchoolDay, student)
|
||||
}
|
||||
} else {
|
||||
val buttonType = intent.getStringExtra(EXTRA_BUTTON_TYPE)
|
||||
val toggledWidgetId = intent.getIntExtra(EXTRA_TOGGLED_WIDGET_ID, 0)
|
||||
val student = getStudent(sharedPref.getLong(getStudentWidgetKey(toggledWidgetId), 0), toggledWidgetId)
|
||||
val savedDate = LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(toggledWidgetId), 0))
|
||||
val student = getStudent(
|
||||
sharedPref.getLong(getStudentWidgetKey(toggledWidgetId), 0),
|
||||
toggledWidgetId
|
||||
)
|
||||
val savedDate =
|
||||
LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(toggledWidgetId), 0))
|
||||
val date = when (buttonType) {
|
||||
BUTTON_RESET -> now().nextOrSameSchoolDay
|
||||
BUTTON_NEXT -> savedDate.nextSchoolDay
|
||||
BUTTON_PREV -> savedDate.previousSchoolDay
|
||||
else -> now().nextOrSameSchoolDay
|
||||
}
|
||||
if (!buttonType.isNullOrBlank()) analytics.logEvent("changed_timetable_widget_day", "button" to buttonType)
|
||||
if (!buttonType.isNullOrBlank()) analytics.logEvent(
|
||||
"changed_timetable_widget_day",
|
||||
"button" to buttonType
|
||||
)
|
||||
updateWidget(context, toggledWidgetId, date, student)
|
||||
}
|
||||
}
|
||||
@ -121,9 +134,15 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() {
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
private fun updateWidget(context: Context, appWidgetId: Int, date: LocalDate, student: Student?) {
|
||||
private fun updateWidget(
|
||||
context: Context,
|
||||
appWidgetId: Int,
|
||||
date: LocalDate,
|
||||
student: Student?
|
||||
) {
|
||||
val savedConfigureTheme = sharedPref.getLong(getThemeWidgetKey(appWidgetId), 0)
|
||||
val isSystemDarkMode = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
|
||||
val isSystemDarkMode =
|
||||
context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
|
||||
var currentTheme = 0L
|
||||
var layoutId = R.layout.widget_timetable
|
||||
|
||||
@ -134,21 +153,28 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() {
|
||||
|
||||
val nextNavIntent = createNavIntent(context, appWidgetId, appWidgetId, BUTTON_NEXT)
|
||||
val prevNavIntent = createNavIntent(context, -appWidgetId, appWidgetId, BUTTON_PREV)
|
||||
val resetNavIntent = createNavIntent(context, Int.MAX_VALUE - appWidgetId, appWidgetId, BUTTON_RESET)
|
||||
val resetNavIntent =
|
||||
createNavIntent(context, Int.MAX_VALUE - appWidgetId, appWidgetId, BUTTON_RESET)
|
||||
val adapterIntent = Intent(context, TimetableWidgetService::class.java)
|
||||
.apply {
|
||||
putExtra(EXTRA_APPWIDGET_ID, appWidgetId)
|
||||
//make Intent unique
|
||||
action = appWidgetId.toString()
|
||||
}
|
||||
val accountIntent = PendingIntent.getActivity(context, -Int.MAX_VALUE + appWidgetId,
|
||||
val accountIntent = PendingIntent.getActivity(
|
||||
context, -Int.MAX_VALUE + appWidgetId,
|
||||
Intent(context, TimetableWidgetConfigureActivity::class.java).apply {
|
||||
addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK)
|
||||
putExtra(EXTRA_APPWIDGET_ID, appWidgetId)
|
||||
putExtra(EXTRA_FROM_PROVIDER, true)
|
||||
}, FLAG_UPDATE_CURRENT)
|
||||
val appIntent = PendingIntent.getActivity(context, MainView.Section.TIMETABLE.id,
|
||||
MainActivity.getStartIntent(context, MainView.Section.TIMETABLE, true), FLAG_UPDATE_CURRENT)
|
||||
}, FLAG_UPDATE_CURRENT
|
||||
)
|
||||
val appIntent = PendingIntent.getActivity(
|
||||
context,
|
||||
MainView.Section.TIMETABLE.id,
|
||||
MainActivity.getStartIntent(context, MainView.Section.TIMETABLE, true),
|
||||
FLAG_UPDATE_CURRENT
|
||||
)
|
||||
|
||||
val remoteView = RemoteViews(context.packageName, layoutId).apply {
|
||||
setEmptyView(R.id.timetableWidgetList, R.id.timetableWidgetEmpty)
|
||||
@ -160,6 +186,11 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() {
|
||||
R.id.timetableWidgetName,
|
||||
student?.nickOrName ?: context.getString(R.string.all_no_data)
|
||||
)
|
||||
|
||||
student?.let {
|
||||
setImageViewBitmap(R.id.timetableWidgetAccount, context.createAvatarBitmap(it))
|
||||
}
|
||||
|
||||
setRemoteAdapter(R.id.timetableWidgetList, adapterIntent)
|
||||
setOnClickPendingIntent(R.id.timetableWidgetNext, nextNavIntent)
|
||||
setOnClickPendingIntent(R.id.timetableWidgetPrev, prevNavIntent)
|
||||
@ -181,13 +212,20 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun createNavIntent(context: Context, code: Int, appWidgetId: Int, buttonType: String): PendingIntent {
|
||||
return PendingIntent.getBroadcast(context, code,
|
||||
private fun createNavIntent(
|
||||
context: Context,
|
||||
code: Int,
|
||||
appWidgetId: Int,
|
||||
buttonType: String
|
||||
): PendingIntent {
|
||||
return PendingIntent.getBroadcast(
|
||||
context, code,
|
||||
Intent(context, TimetableWidgetProvider::class.java).apply {
|
||||
action = ACTION_APPWIDGET_UPDATE
|
||||
putExtra(EXTRA_BUTTON_TYPE, buttonType)
|
||||
putExtra(EXTRA_TOGGLED_WIDGET_ID, appWidgetId)
|
||||
}, FLAG_UPDATE_CURRENT)
|
||||
}, FLAG_UPDATE_CURRENT
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun getStudent(studentId: Long, appWidgetId: Int) = try {
|
||||
@ -208,4 +246,29 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() {
|
||||
}
|
||||
null
|
||||
}
|
||||
|
||||
private fun Context.createAvatarBitmap(student: Student): Bitmap {
|
||||
val avatarColor = if (student.avatarColor == -2937041L) {
|
||||
getCompatColor(R.color.colorPrimaryLight).toLong()
|
||||
} else {
|
||||
student.avatarColor
|
||||
}
|
||||
val avatarDrawable = createNameInitialsDrawable(student.nickOrName, avatarColor, 0.5f)
|
||||
|
||||
val avatarBitmap =
|
||||
if (avatarDrawable.intrinsicWidth <= 0 || avatarDrawable.intrinsicHeight <= 0) {
|
||||
Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
|
||||
} else {
|
||||
Bitmap.createBitmap(
|
||||
avatarDrawable.intrinsicWidth,
|
||||
avatarDrawable.intrinsicHeight,
|
||||
Bitmap.Config.ARGB_8888
|
||||
)
|
||||
}
|
||||
|
||||
val canvas = Canvas(avatarBitmap)
|
||||
avatarDrawable.setBounds(0, 0, canvas.width, canvas.height)
|
||||
avatarDrawable.draw(canvas)
|
||||
return avatarBitmap
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,5 @@
|
||||
Wersja 1.1.0
|
||||
Wersja 1.1.5
|
||||
|
||||
- dodaliśmy wyświetlanie inicjałów imienia ucznia jako awatar widoczny w aplikacji
|
||||
- dodaliśmy historię szczęśliwego numerka
|
||||
- dodaliśmy język słowacki
|
||||
- zmieniliśmy kolor górnego i dolnego paska systemowego lepiej dostosowując je do aplikacji
|
||||
- zmieniliśmy wygląd ustawień dzieląc je na sekcje
|
||||
- naprawiliśmy problem dublujących się czasem ocen
|
||||
- naprawiliśmy kilka innych błędów i poprawiliśmy stabilność aplikacji
|
||||
Naprawiliśmy liczenie średniej wszystkich ocen z całego roku
|
||||
|
||||
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
|
||||
|
@ -25,8 +25,7 @@
|
||||
android:id="@+id/loginRecoverFormContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="16dp"
|
||||
android:visibility="visible">
|
||||
android:paddingBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/loginFormHeader"
|
||||
@ -85,9 +84,9 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:autofillHints="emailAddress"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="textEmailAddress"
|
||||
android:maxLines="1"
|
||||
android:imeOptions="actionDone"
|
||||
tools:targetApi="o" />
|
||||
|
||||
<requestFocus />
|
||||
@ -156,7 +155,7 @@
|
||||
android:orientation="vertical"
|
||||
android:visibility="invisible"
|
||||
tools:ignore="UseCompoundDrawables"
|
||||
tools:visibility="invisible">
|
||||
tools:visibility="visible">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="100dp"
|
||||
@ -175,12 +174,10 @@
|
||||
android:text="@string/error_unknown"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<LinearLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
android:layout_marginTop="16dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/loginRecoverErrorDetails"
|
||||
@ -189,14 +186,21 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:text="@string/all_details" />
|
||||
android:text="@string/all_details"
|
||||
app:layout_constraintEnd_toStartOf="@id/loginRecoverErrorRetry"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/loginRecoverErrorRetry"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/all_retry" />
|
||||
</LinearLayout>
|
||||
android:text="@string/all_retry"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/loginRecoverErrorDetails"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
@ -206,7 +210,7 @@
|
||||
android:fillViewport="true"
|
||||
android:scrollbars="none"
|
||||
android:visibility="invisible"
|
||||
tools:visibility="invisible">
|
||||
tools:visibility="gone">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@ -253,6 +257,5 @@
|
||||
android:layout_marginBottom="30dp"
|
||||
android:text="@android:string/ok" />
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</FrameLayout>
|
||||
|
@ -192,7 +192,7 @@
|
||||
</plurals>
|
||||
<string name="attendance_excuse_dialog_reason">Důvod nepřítomnosti (volitelný)</string>
|
||||
<string name="attendance_excuse_dialog_submit">Poslat</string>
|
||||
<string name="attendance_excuse_success">Nepřítomnost úspěšně omluvena!</string>
|
||||
<string name="attendance_excuse_success">Žádost o omluvu nepřítomnosti byla úspěšně odeslána!</string>
|
||||
<string name="attendance_excuse_no_selection">Musíte vybrat alespoň jednu nepřítomnost!</string>
|
||||
<string name="attendance_excuse_title">Ospravedlnit</string>
|
||||
<!--Attendance summary-->
|
||||
@ -506,6 +506,10 @@
|
||||
<string name="channel_push">Push upozornění</string>
|
||||
<string name="channel_upcoming_lessons">Nadcházející lekce</string>
|
||||
<string name="channel_debug">Ladění</string>
|
||||
<!--Drop kitkat alert dialog strings-->
|
||||
<string name="drop_kitkat_title">Konec podpory</string>
|
||||
<string name="drop_kitkat_content">Ukončujeme podporu pro vaše zařízení. V Wulkanovým už pro něj nebudou žádné nové funkce. Kritické opravy však budeme vydávat až do konce roku 2021, abyste měli čas přejít na novější model</string>
|
||||
<string name="drop_kitkat_again">Nezobrazovat znovu</string>
|
||||
<!--Colors-->
|
||||
<string name="all_black">Černý</string>
|
||||
<string name="all_red">Červený</string>
|
||||
|
@ -176,7 +176,7 @@
|
||||
</plurals>
|
||||
<string name="attendance_excuse_dialog_reason">Abwesenheitsgrund (optional)</string>
|
||||
<string name="attendance_excuse_dialog_submit">Senden</string>
|
||||
<string name="attendance_excuse_success">Abwesenheit erfolgreich entschuldigt!</string>
|
||||
<string name="attendance_excuse_success">Abwesenheitsentschuldigungsanfrage erfolgreich gesendet!</string>
|
||||
<string name="attendance_excuse_no_selection">Sie müssen mindestens eine Abwesenheit auswählen!</string>
|
||||
<string name="attendance_excuse_title">Verzeihung</string>
|
||||
<!--Attendance summary-->
|
||||
@ -466,6 +466,10 @@
|
||||
<string name="channel_push">Push-Benachrichtigungen</string>
|
||||
<string name="channel_upcoming_lessons">Bevorstehende Lektionen</string>
|
||||
<string name="channel_debug">Debuggen</string>
|
||||
<!--Drop kitkat alert dialog strings-->
|
||||
<string name="drop_kitkat_title">Ende der Unterstützung</string>
|
||||
<string name="drop_kitkat_content">Wir beenden die Unterstützung für dein Gerät. Es werden keine neuen Funktionen mehr in Wulkanowy erscheinen. Allerdings werden wir bis Ende 2021 kritische Patches veröffentlichen, so dass du Zeit hast, zu einem neueren Modell zu wechseln</string>
|
||||
<string name="drop_kitkat_again">Nicht mehr fragen</string>
|
||||
<!--Colors-->
|
||||
<string name="all_black">Schwarz</string>
|
||||
<string name="all_red">Rot</string>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!--Activity/Fragment title-->
|
||||
<string name="login_title">Login</string>
|
||||
<string name="login_title">Prisijungti</string>
|
||||
<string name="main_title">Wulkanowy</string>
|
||||
<string name="grade_title">Laipsnis</string>
|
||||
<string name="attendance_title">Attendance</string>
|
||||
@ -192,7 +192,7 @@
|
||||
</plurals>
|
||||
<string name="attendance_excuse_dialog_reason">Absence reason (optional)</string>
|
||||
<string name="attendance_excuse_dialog_submit">Send</string>
|
||||
<string name="attendance_excuse_success">Absence excused successfully!</string>
|
||||
<string name="attendance_excuse_success">Absence excuse request sent successfully!</string>
|
||||
<string name="attendance_excuse_no_selection">You must select at least one absence!</string>
|
||||
<string name="attendance_excuse_title">Excuse</string>
|
||||
<!--Attendance summary-->
|
||||
@ -506,6 +506,10 @@
|
||||
<string name="channel_push">Push notifications</string>
|
||||
<string name="channel_upcoming_lessons">Upcoming lessons</string>
|
||||
<string name="channel_debug">Debug</string>
|
||||
<!--Drop kitkat alert dialog strings-->
|
||||
<string name="drop_kitkat_title">End of support</string>
|
||||
<string name="drop_kitkat_content">We are ending support for your device. No more new features will appear for it in Wulkanowy. However, we will be releasing critical patches until the end of 2021 so you have time to switch to a newer model</string>
|
||||
<string name="drop_kitkat_again">Don\'t show again</string>
|
||||
<!--Colors-->
|
||||
<string name="all_black">Black</string>
|
||||
<string name="all_red">Red</string>
|
||||
|
@ -49,4 +49,12 @@
|
||||
<item name="android:navigationBarColor" tools:targetApi="lollipop">?colorSurface</item>
|
||||
<item name="android:statusBarColor" tools:targetApi="lollipop">@android:color/black</item>
|
||||
</style>
|
||||
|
||||
<style name="WulkanowyTheme.WidgetAccountSwitcher" parent="Theme.MaterialComponents.Dialog">
|
||||
<item name="colorPrimary">@color/colorPrimaryLight</item>
|
||||
<item name="colorSecondary">@color/colorPrimaryLight</item>
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="android:textColor">?android:textColorPrimary</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
@ -192,7 +192,7 @@
|
||||
</plurals>
|
||||
<string name="attendance_excuse_dialog_reason">Powód nieobecności (opcjonalny)</string>
|
||||
<string name="attendance_excuse_dialog_submit">Wyślij</string>
|
||||
<string name="attendance_excuse_success">Usprawiedliwiono pomyślnie!</string>
|
||||
<string name="attendance_excuse_success">Prośba o usprawiedliwienie została pomyślnie wysłana!</string>
|
||||
<string name="attendance_excuse_no_selection">Musisz wybrać co najmniej jedną nieobecność!</string>
|
||||
<string name="attendance_excuse_title">Usprawiedliw</string>
|
||||
<!--Attendance summary-->
|
||||
@ -317,7 +317,7 @@
|
||||
<string name="lucky_number_history_button">Pokaż historię</string>
|
||||
<!--Lucky number history-->
|
||||
<string name="lucky_number_history_title">Historia numerków</string>
|
||||
<string name="lucky_number_history_empty">Brak informacji o szczęśliwych numerach</string>
|
||||
<string name="lucky_number_history_empty">Brak informacji o szczęśliwych numerkach</string>
|
||||
<!--Mobile devices-->
|
||||
<string name="mobile_devices_title">Dostęp mobilny</string>
|
||||
<string name="mobile_devices_no_items">Brak urządzeń</string>
|
||||
@ -506,6 +506,10 @@
|
||||
<string name="channel_push">Powiadomienia push</string>
|
||||
<string name="channel_upcoming_lessons">Nadchodzące lekcje</string>
|
||||
<string name="channel_debug">Debugowanie</string>
|
||||
<!--Drop kitkat alert dialog strings-->
|
||||
<string name="drop_kitkat_title">Koniec wsparcia</string>
|
||||
<string name="drop_kitkat_content">Kończymy wsparcie dla Twojego urządzenia. Nie pojawią się już dla niego żadne nowe funkcje w Wulkanowym. Będziemy jednak wypuszczać krytyczne poprawki do końca 2021 roku, abyś miał czas na przeniesienie się na nowszy model</string>
|
||||
<string name="drop_kitkat_again">Nie pokazuj ponownie</string>
|
||||
<!--Colors-->
|
||||
<string name="all_black">Czarny</string>
|
||||
<string name="all_red">Czerwony</string>
|
||||
|
@ -192,7 +192,7 @@
|
||||
</plurals>
|
||||
<string name="attendance_excuse_dialog_reason">Причина отсутствия (необязательно)</string>
|
||||
<string name="attendance_excuse_dialog_submit">Послать</string>
|
||||
<string name="attendance_excuse_success">Статус отсутствия изменён</string>
|
||||
<string name="attendance_excuse_success">Запрос на освобождение оправдания успешно отправлен!</string>
|
||||
<string name="attendance_excuse_no_selection">Выберите хотя-бы одно отсутствие</string>
|
||||
<string name="attendance_excuse_title">Изменить статус</string>
|
||||
<!--Attendance summary-->
|
||||
@ -506,6 +506,10 @@
|
||||
<string name="channel_push">Показывать push-уведомления</string>
|
||||
<string name="channel_upcoming_lessons">Будущие уроки</string>
|
||||
<string name="channel_debug">Дебаг</string>
|
||||
<!--Drop kitkat alert dialog strings-->
|
||||
<string name="drop_kitkat_title">Конец поддержки</string>
|
||||
<string name="drop_kitkat_content">Поддержка заканчивается на вашем устройстве. В Wulkanowy больше не появятся новые возможности. Однако до конца 2021 года мы выпустим критические патчи, чтобы у вас было время перейти на новую модель</string>
|
||||
<string name="drop_kitkat_again">Не показывать снова</string>
|
||||
<!--Colors-->
|
||||
<string name="all_black">Чёрный</string>
|
||||
<string name="all_red">Красный</string>
|
||||
|
@ -192,7 +192,7 @@
|
||||
</plurals>
|
||||
<string name="attendance_excuse_dialog_reason">Dôvod neprítomnosti (voliteľný)</string>
|
||||
<string name="attendance_excuse_dialog_submit">Poslať</string>
|
||||
<string name="attendance_excuse_success">Neprítomnosť úspešne ospravedlnená!</string>
|
||||
<string name="attendance_excuse_success">Žiadosť o ospravedlnenie neprítomnosti bola úspešne odoslaná!</string>
|
||||
<string name="attendance_excuse_no_selection">Musíte vybrať aspoň jednu neprítomnosť!</string>
|
||||
<string name="attendance_excuse_title">Ospravedlniť</string>
|
||||
<!--Attendance summary-->
|
||||
@ -506,6 +506,10 @@
|
||||
<string name="channel_push">Push upozornenia</string>
|
||||
<string name="channel_upcoming_lessons">Nadchádzajúce lekcie</string>
|
||||
<string name="channel_debug">Ladenie</string>
|
||||
<!--Drop kitkat alert dialog strings-->
|
||||
<string name="drop_kitkat_title">Koniec podpory</string>
|
||||
<string name="drop_kitkat_content">Ukončujeme podporu pre vaše zariadenie. V Wulkanovým už pre neho nebudú žiadne nové funkcie. Kritické opravy však budeme vydávať až do konca roka 2021, aby ste mali čas prejsť na novší model</string>
|
||||
<string name="drop_kitkat_again">Nezobrazovať znovu</string>
|
||||
<!--Colors-->
|
||||
<string name="all_black">Čierny</string>
|
||||
<string name="all_red">Červený</string>
|
||||
|
@ -46,7 +46,7 @@
|
||||
<string name="login_expired_token">Минув термін дії токену</string>
|
||||
<string name="login_invalid_email">Недійсна адреса електронної пошти</string>
|
||||
<string name="login_invalid_login">Використовуйте призначений логін замість електронної пошти</string>
|
||||
<string name="login_invalid_custom_email">Використовуйте призначений логін або електронну адресу в @% 1 $ s</string>
|
||||
<string name="login_invalid_custom_email">Використовуйте призначений логін або електронну адресу в @%1$s</string>
|
||||
<string name="login_invalid_symbol">Неправильний симбвол</string>
|
||||
<string name="login_incorrect_symbol">Студента не знайдено Перевірте символ та обраний варіант реєстру UONET+</string>
|
||||
<string name="login_field_required">Обов\'язкове поле</string>
|
||||
@ -192,7 +192,7 @@
|
||||
</plurals>
|
||||
<string name="attendance_excuse_dialog_reason">Причина відсутності (необов’язково)</string>
|
||||
<string name="attendance_excuse_dialog_submit">Надіслати</string>
|
||||
<string name="attendance_excuse_success">Змінено статус відсутності</string>
|
||||
<string name="attendance_excuse_success">Запит на виправдання відсутності успішно надіслано!</string>
|
||||
<string name="attendance_excuse_no_selection">Оберіть хоча б одну відсутність</string>
|
||||
<string name="attendance_excuse_title">Змінити статус</string>
|
||||
<!--Attendance summary-->
|
||||
@ -506,6 +506,10 @@
|
||||
<string name="channel_push">Показувати push-повідомлення</string>
|
||||
<string name="channel_upcoming_lessons">Наступні уроки</string>
|
||||
<string name="channel_debug">Дебаг</string>
|
||||
<!--Drop kitkat alert dialog strings-->
|
||||
<string name="drop_kitkat_title">Кінець підтримки</string>
|
||||
<string name="drop_kitkat_content">Ми завершуємо підтримку вашого пристрою. Більше нових функцій не з\'явиться у Wulkanowy. Однак ми виробляємо критичні патчі до кінця 2021, тому у вас буде час перейти на новішу модель</string>
|
||||
<string name="drop_kitkat_again">Не показувати знову</string>
|
||||
<!--Colors-->
|
||||
<string name="all_black">Чорний</string>
|
||||
<string name="all_red">Червоний</string>
|
||||
|
@ -194,7 +194,7 @@
|
||||
</plurals>
|
||||
<string name="attendance_excuse_dialog_reason">Absence reason (optional)</string>
|
||||
<string name="attendance_excuse_dialog_submit">Send</string>
|
||||
<string name="attendance_excuse_success">Absence excused successfully!</string>
|
||||
<string name="attendance_excuse_success">Absence excuse request sent successfully!</string>
|
||||
<string name="attendance_excuse_no_selection">You must select at least one absence!</string>
|
||||
<string name="attendance_excuse_title">Excuse</string>
|
||||
|
||||
@ -541,6 +541,12 @@
|
||||
<string name="channel_debug">Debug</string>
|
||||
|
||||
|
||||
<!--Drop kitkat alert dialog strings-->
|
||||
<string name="drop_kitkat_title">End of support</string>
|
||||
<string name="drop_kitkat_content">We are ending support for your device. No more new features will appear for it in Wulkanowy. However, we will be releasing critical patches until the end of 2021 so you have time to switch to a newer model</string>
|
||||
<string name="drop_kitkat_again">Don\'t show again</string>
|
||||
|
||||
|
||||
<!--Colors-->
|
||||
<string name="all_black">Black</string>
|
||||
<string name="all_red">Red</string>
|
||||
@ -554,6 +560,7 @@
|
||||
<string name="all_copied">Copied</string>
|
||||
<string name="all_undo">Undo</string>
|
||||
|
||||
|
||||
<!--Update helper-->
|
||||
<string name="update_download_started">Download of updates has started…</string>
|
||||
<string name="update_download_success">An update has just been downloaded.</string>
|
||||
|
@ -26,7 +26,7 @@
|
||||
<item name="android:windowBackground">@drawable/layer_splash_background</item>
|
||||
</style>
|
||||
|
||||
<style name="WulkanowyTheme.WidgetAccountSwitcher" parent="Theme.MaterialComponents.DayNight.Dialog">
|
||||
<style name="WulkanowyTheme.WidgetAccountSwitcher" parent="Theme.MaterialComponents.Light.Dialog">
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorSecondary">@color/colorPrimary</item>
|
||||
<item name="windowActionBar">false</item>
|
||||
|
@ -54,7 +54,7 @@ class RecipientLocalTest {
|
||||
coEvery { recipientDb.deleteAll(any()) } just Runs
|
||||
|
||||
// execute
|
||||
val res = runBlocking { recipientRepository.getRecipients(student, ReportingUnit(1, 123, "", 4, "", listOf()), 7) }
|
||||
val res = runBlocking { recipientRepository.getRecipients(student, ReportingUnit(4, 123, "", 4, "", listOf()), 7) }
|
||||
|
||||
// verify
|
||||
assertEquals(3, res.size)
|
||||
@ -73,7 +73,7 @@ class RecipientLocalTest {
|
||||
coEvery { recipientDb.deleteAll(any()) } just Runs
|
||||
|
||||
// execute
|
||||
val res = runBlocking { recipientRepository.getRecipients(student, ReportingUnit(1, 123, "", 4, "", listOf()), 7) }
|
||||
val res = runBlocking { recipientRepository.getRecipients(student, ReportingUnit(4, 123, "", 4, "", listOf()), 7) }
|
||||
|
||||
// verify
|
||||
assertEquals(3, res.size)
|
||||
|
@ -440,15 +440,71 @@ class GradeAverageProviderTest {
|
||||
)
|
||||
}
|
||||
|
||||
val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
|
||||
val items = runBlocking {
|
||||
gradeAverageProvider.getGradesDetailsWithAverage(
|
||||
student,
|
||||
semesters[2].semesterId,
|
||||
true
|
||||
).getResult()
|
||||
}
|
||||
|
||||
assertEquals(2, items.size)
|
||||
assertEquals(3.0, items.single { it.subject == "Matematyka" }.average, .0) // (from details): 3,5 + 2,5 → 3,0
|
||||
assertEquals(3.25, items.single { it.subject == "Fizyka" }.average, .0) // (from details): 3,5 + 3,0 → 3,25
|
||||
assertEquals(
|
||||
3.0,
|
||||
items.single { it.subject == "Matematyka" }.average,
|
||||
.0
|
||||
) // (from details): 3,5 + 2,5 → 3,0
|
||||
assertEquals(
|
||||
3.25,
|
||||
items.single { it.subject == "Fizyka" }.average,
|
||||
.0
|
||||
) // (from details): 3,5 + 3,0 → 3,25
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `calc full year average when current is second with load from cache sequence`() {
|
||||
fun `calc all year average`() {
|
||||
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ALL_YEAR
|
||||
every { preferencesRepository.gradeAverageForceCalc } returns false
|
||||
coEvery {
|
||||
gradeRepository.getGrades(
|
||||
student,
|
||||
semesters[1],
|
||||
true
|
||||
)
|
||||
} returns flowWithResource {
|
||||
firstGrades to listOf(
|
||||
getSummary(22, "Matematyka", .0),
|
||||
getSummary(22, "Fizyka", .0)
|
||||
)
|
||||
}
|
||||
coEvery {
|
||||
gradeRepository.getGrades(
|
||||
student,
|
||||
semesters[2],
|
||||
true
|
||||
)
|
||||
} returns flowWithResource {
|
||||
secondGrades to listOf(
|
||||
getSummary(22, "Matematyka", .0),
|
||||
getSummary(22, "Fizyka", .0)
|
||||
)
|
||||
}
|
||||
|
||||
val items = runBlocking {
|
||||
gradeAverageProvider.getGradesDetailsWithAverage(
|
||||
student,
|
||||
semesters[2].semesterId,
|
||||
true
|
||||
).getResult()
|
||||
}
|
||||
|
||||
assertEquals(2, items.size)
|
||||
assertEquals(3.0, items.single { it.subject == "Matematyka" }.average, .0)
|
||||
assertEquals(3.25, items.single { it.subject == "Fizyka" }.average, .0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `force calc full year average when current is second with load from cache sequence`() {
|
||||
every { preferencesRepository.gradeAverageForceCalc } returns true
|
||||
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ALL_YEAR
|
||||
coEvery { semesterRepository.getSemesters(student) } returns semesters
|
||||
@ -490,17 +546,39 @@ class GradeAverageProviderTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `calc both semesters average when no summaries`() {
|
||||
fun `force calc both semesters average when no summaries`() {
|
||||
every { preferencesRepository.gradeAverageForceCalc } returns true
|
||||
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
|
||||
|
||||
coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flowWithResource { firstGrades to emptyList() }
|
||||
coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flowWithResource { secondGrades to emptyList() }
|
||||
coEvery {
|
||||
gradeRepository.getGrades(
|
||||
student,
|
||||
semesters[1],
|
||||
true
|
||||
)
|
||||
} returns flowWithResource { firstGrades to emptyList() }
|
||||
coEvery {
|
||||
gradeRepository.getGrades(
|
||||
student,
|
||||
semesters[2],
|
||||
true
|
||||
)
|
||||
} returns flowWithResource { secondGrades to emptyList() }
|
||||
|
||||
val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
|
||||
val items = runBlocking {
|
||||
gradeAverageProvider.getGradesDetailsWithAverage(
|
||||
student,
|
||||
semesters[2].semesterId,
|
||||
true
|
||||
).getResult()
|
||||
}
|
||||
|
||||
assertEquals(2, items.size)
|
||||
assertEquals(3.0, items.single { it.subject == "Matematyka" }.average, .0) // (from details): 3,5 + 2,5 → 3,0
|
||||
assertEquals(
|
||||
3.0,
|
||||
items.single { it.subject == "Matematyka" }.average,
|
||||
.0
|
||||
) // (from details): 3,5 + 2,5 → 3,0
|
||||
assertEquals(3.25, items.single { it.subject == "Fizyka" }.average, .0) // (from details): 3,5 + 3,0 → 3,25
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,13 @@
|
||||
package io.github.wulkanowy.ui.modules.splash
|
||||
|
||||
import io.github.wulkanowy.MainCoroutineRule
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.verify
|
||||
import org.junit.Before
|
||||
@ -22,6 +25,12 @@ class SplashPresenterTest {
|
||||
@MockK
|
||||
lateinit var studentRepository: StudentRepository
|
||||
|
||||
@MockK
|
||||
lateinit var preferencesRepository: PreferencesRepository
|
||||
|
||||
@MockK
|
||||
lateinit var appInfo: AppInfo
|
||||
|
||||
@MockK(relaxed = true)
|
||||
lateinit var errorHandler: ErrorHandler
|
||||
|
||||
@ -30,19 +39,25 @@ class SplashPresenterTest {
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockKAnnotations.init(this)
|
||||
presenter = SplashPresenter(errorHandler, studentRepository)
|
||||
presenter = SplashPresenter(errorHandler, studentRepository, preferencesRepository, appInfo)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOpenLoginView() {
|
||||
every { appInfo.systemVersion } returns 30
|
||||
every { preferencesRepository.isKitkatDialogDisabled } returns true
|
||||
coEvery { studentRepository.isCurrentStudentSet() } returns false
|
||||
|
||||
presenter.onAttachView(splashView, null)
|
||||
verify { splashView.openLoginView() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMainMainView() {
|
||||
every { appInfo.systemVersion } returns 30
|
||||
every { preferencesRepository.isKitkatDialogDisabled } returns true
|
||||
coEvery { studentRepository.isCurrentStudentSet() } returns true
|
||||
|
||||
presenter.onAttachView(splashView, null)
|
||||
verify { splashView.openMainView() }
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
buildscript {
|
||||
ext {
|
||||
kotlin_version = '1.4.31'
|
||||
kotlin_version = '1.4.32'
|
||||
about_libraries = '8.8.4'
|
||||
hilt_version = "2.33-beta"
|
||||
}
|
||||
@ -13,12 +13,13 @@ buildscript {
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath 'com.android.tools.build:gradle:4.1.2'
|
||||
classpath 'com.android.tools.build:gradle:4.1.3'
|
||||
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
|
||||
classpath 'com.google.gms:google-services:4.3.5'
|
||||
classpath 'com.huawei.agconnect:agcp:1.5.0.300'
|
||||
classpath 'com.huawei.agconnect:agcp:1.5.1.200'
|
||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.5.1'
|
||||
classpath "com.github.triplet.gradle:play-publisher:2.8.0"
|
||||
classpath "ru.cian:huawei-publish-gradle-plugin:1.2.2"
|
||||
classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.1.1"
|
||||
classpath "gradle.plugin.com.star-zero.gradle:githook:1.2.0"
|
||||
classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:$about_libraries"
|
||||
|
Reference in New Issue
Block a user