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