forked from github/wulkanowy-mirror
Compare commits
5 Commits
develop
...
feature/at
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1591d494ab | ||
![]() |
1af44cf60a | ||
![]() |
28c234a8fd | ||
![]() |
f8c9122686 | ||
![]() |
7effb7aca2 |
@ -162,7 +162,7 @@ jobs:
|
|||||||
openssl aes-256-cbc -d -in ./app/upload-key-encrypted.jks -k $ENCRYPT_KEY >> ./app/upload-key.jks
|
openssl aes-256-cbc -d -in ./app/upload-key-encrypted.jks -k $ENCRYPT_KEY >> ./app/upload-key.jks
|
||||||
- run:
|
- run:
|
||||||
name: Publish release
|
name: Publish release
|
||||||
command: ./gradlew publishPlayRelease --no-daemon --stacktrace --console=plain -PdisablePreDex
|
command: ./gradlew publishPlayRelease --no-daemon --stacktrace --console=plain -PenableCrashlytics -PdisablePreDex
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[*]
|
[*]
|
||||||
charset=utf-8
|
charset=utf-8
|
||||||
end_of_line=lf
|
end_of_line=lf
|
||||||
insert_final_newline=Advanced
|
insert_final_newline=true
|
||||||
indent_style=space
|
indent_style=space
|
||||||
indent_size=4
|
indent_size=4
|
||||||
|
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
changelog:
|
|
||||||
exclude:
|
|
||||||
labels:
|
|
||||||
- "release ignore"
|
|
||||||
categories:
|
|
||||||
- title: breaking changes
|
|
||||||
labels:
|
|
||||||
- major
|
|
||||||
- title: new features
|
|
||||||
labels:
|
|
||||||
- minor
|
|
||||||
- fr:approved
|
|
||||||
- title: translation updates
|
|
||||||
labels:
|
|
||||||
- translation
|
|
||||||
- title: features
|
|
||||||
labels:
|
|
||||||
- "*"
|
|
@ -1,84 +0,0 @@
|
|||||||
name: Generate APK
|
|
||||||
|
|
||||||
env:
|
|
||||||
main_project_module: app
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types:
|
|
||||||
- closed
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
env:
|
|
||||||
RUNNER_TOOL_CACHE: /toolcache
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout the repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set current date as env variable
|
|
||||||
run: echo "date_today=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Set repository name as env variable
|
|
||||||
run: echo "repository_name=$(echo '${{ gitea.repository }}' | awk -F '/' '{print $2}')" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Set up JDK
|
|
||||||
uses: actions/setup-java@v3
|
|
||||||
with:
|
|
||||||
distribution: 'zulu'
|
|
||||||
java-version: '17'
|
|
||||||
cache: 'gradle'
|
|
||||||
|
|
||||||
- name: Set up Go environment
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: '1.22'
|
|
||||||
|
|
||||||
- name: Get hash of Gradle files
|
|
||||||
uses: https://gitea.com/actions/go-hashfiles@v0.0.1
|
|
||||||
id: get-hash
|
|
||||||
with:
|
|
||||||
patterns: |-
|
|
||||||
**/*.gradle*
|
|
||||||
|
|
||||||
- name: Cache Gradle
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.gradle/caches
|
|
||||||
~/.gradle/wrapper
|
|
||||||
key: gradle-${{ runner.os }}-${{ steps.get-hash.outputs.hash }}
|
|
||||||
|
|
||||||
- name: Get app version
|
|
||||||
id: get_version
|
|
||||||
run: echo "VERSION_NAME=$(grep -m1 "versionName" app/build.gradle | awk '{print $2}' | tr -d \'\'\"\')" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Change wrapper permissions
|
|
||||||
run: chmod +x ./gradlew
|
|
||||||
|
|
||||||
- name: Setup Android SDK
|
|
||||||
uses: android-actions/setup-android@v3
|
|
||||||
|
|
||||||
- name: Build debug APK
|
|
||||||
run: ./gradlew assembleDebug
|
|
||||||
|
|
||||||
- name: Upload artifacts
|
|
||||||
uses: actions/upload-artifact@v3 # not v4 because of GHES
|
|
||||||
with:
|
|
||||||
name: wulkanowy_mod_debug_builds
|
|
||||||
path: |
|
|
||||||
app/build/outputs/**/*-debug.apk
|
|
||||||
|
|
||||||
- name: Create release
|
|
||||||
uses: akkuman/gitea-release-action@v1
|
|
||||||
env:
|
|
||||||
NODE_OPTIONS: '--experimental-fetch'
|
|
||||||
with:
|
|
||||||
files: |
|
|
||||||
app/build/outputs/**/*-debug.apk
|
|
||||||
|
|
||||||
name: Release ${{ env.VERSION_NAME }} (${{ env.date_today }})
|
|
||||||
tag_name: v${{ env.VERSION_NAME }}
|
|
79
.github/workflows/deploy-store.yml
vendored
Normal file
79
.github/workflows/deploy-store.yml
vendored
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
name: Deploy release
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [ created ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
deploy-google-play:
|
||||||
|
name: Google Play
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
environment: google-play
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: 'zulu'
|
||||||
|
java-version: 17
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
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/upload-key.jks.gpg
|
||||||
|
- name: Upload apk to google play
|
||||||
|
env:
|
||||||
|
PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }}
|
||||||
|
PLAY_KEY_ALIAS: ${{ secrets.PLAY_KEY_ALIAS }}
|
||||||
|
PLAY_KEY_PASSWORD: ${{ secrets.PLAY_KEY_PASSWORD }}
|
||||||
|
ANDROID_PUBLISHER_CREDENTIALS: ${{ secrets.ANDROID_PUBLISHER_CREDENTIALS }}
|
||||||
|
ADMOB_PROJECT_ID: ${{ secrets.ADMOB_PROJECT_ID }}
|
||||||
|
SINGLE_SUPPORT_AD_ID: ${{ secrets.SINGLE_SUPPORT_AD_ID }}
|
||||||
|
DASHBOARD_TILE_AD_ID: ${{ secrets.DASHBOARD_TILE_AD_ID }}
|
||||||
|
SET_BUILD_TIMESTAMP: ${{ secrets.SET_BUILD_TIMESTAMP }}
|
||||||
|
run: ./gradlew publishPlayReleaseApps -PenableFirebase --stacktrace;
|
||||||
|
|
||||||
|
deploy-app-gallery:
|
||||||
|
name: AppGallery
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
environment: app-gallery
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: 'zulu'
|
||||||
|
java-version: 17
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
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/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_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }}
|
||||||
|
PLAY_KEY_ALIAS: ${{ secrets.PLAY_KEY_ALIAS }}
|
||||||
|
PLAY_KEY_PASSWORD: ${{ secrets.PLAY_KEY_PASSWORD }}
|
||||||
|
SET_BUILD_TIMESTAMP: ${{ secrets.SET_BUILD_TIMESTAMP }}
|
||||||
|
run: ./gradlew bundleHmsRelease --stacktrace && ./gradlew publishHuaweiAppGalleryHmsRelease --stacktrace
|
146
.github/workflows/deploy-test.yml
vendored
Normal file
146
.github/workflows/deploy-test.yml
vendored
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
name: Deploy DEV
|
||||||
|
|
||||||
|
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@v3
|
||||||
|
- uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: 'zulu'
|
||||||
|
java-version: 17
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
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@v3
|
||||||
|
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@v3
|
||||||
|
- uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: 'zulu'
|
||||||
|
java-version: 17
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
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@v3
|
||||||
|
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
|
90
.github/workflows/test.yml
vendored
Normal file
90
.github/workflows/test.yml
vendored
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
name: Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- develop
|
||||||
|
- 'hotfix/**'
|
||||||
|
tags: [ '*' ]
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
tests-fdroid:
|
||||||
|
name: F-Droid
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
steps:
|
||||||
|
- uses: fkirc/skip-duplicate-actions@master
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: gradle/wrapper-validation-action@v1
|
||||||
|
- uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: 'zulu'
|
||||||
|
java-version: 17
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
|
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}
|
||||||
|
- name: Unit tests
|
||||||
|
run: |
|
||||||
|
./gradlew testFdroidDebugUnitTest --stacktrace
|
||||||
|
./gradlew jacocoTestReport --stacktrace
|
||||||
|
- uses: codecov/codecov-action@v3
|
||||||
|
with:
|
||||||
|
flags: unit
|
||||||
|
|
||||||
|
tests-play:
|
||||||
|
name: Play
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
steps:
|
||||||
|
- uses: fkirc/skip-duplicate-actions@master
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: gradle/wrapper-validation-action@v1
|
||||||
|
- uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: 'zulu'
|
||||||
|
java-version: 17
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
|
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}
|
||||||
|
- name: Unit tests
|
||||||
|
run: |
|
||||||
|
./gradlew testPlayDebugUnitTest --stacktrace
|
||||||
|
./gradlew jacocoTestReport --stacktrace
|
||||||
|
- uses: codecov/codecov-action@v3
|
||||||
|
with:
|
||||||
|
flags: unit
|
||||||
|
|
||||||
|
tests-hms:
|
||||||
|
name: HMS
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
steps:
|
||||||
|
- uses: fkirc/skip-duplicate-actions@master
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: gradle/wrapper-validation-action@v1
|
||||||
|
- uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: 'zulu'
|
||||||
|
java-version: 17
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
|
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}
|
||||||
|
- name: Unit tests
|
||||||
|
run: |
|
||||||
|
./gradlew testHmsDebugUnitTest --stacktrace
|
||||||
|
./gradlew jacocoTestReport --stacktrace
|
||||||
|
- uses: codecov/codecov-action@v3
|
||||||
|
with:
|
||||||
|
flags: unit
|
16
.gitignore
vendored
16
.gitignore
vendored
@ -67,10 +67,6 @@ captures/
|
|||||||
.idea/discord.xml
|
.idea/discord.xml
|
||||||
.idea/migrations.xml
|
.idea/migrations.xml
|
||||||
.idea/androidTestResultsUserPreferences.xml
|
.idea/androidTestResultsUserPreferences.xml
|
||||||
.idea/copilot
|
|
||||||
.idea/deploymentTargetDropDown.xml
|
|
||||||
.idea/deploymentTargetSelector.xml
|
|
||||||
.idea/kotlinc.xml
|
|
||||||
|
|
||||||
# Keystore files
|
# Keystore files
|
||||||
*.jks
|
*.jks
|
||||||
@ -117,14 +113,12 @@ Thumbs.db
|
|||||||
*.ear
|
*.ear
|
||||||
|
|
||||||
### AndroidStudio Patch ###
|
### AndroidStudio Patch ###
|
||||||
|
|
||||||
!/gradle/wrapper/gradle-wrapper.jar
|
!/gradle/wrapper/gradle-wrapper.jar
|
||||||
.idea/jarRepositories.xml
|
.idea/jarRepositories.xml
|
||||||
|
|
||||||
### Services config files
|
|
||||||
agconnect-services.json
|
|
||||||
agconnect-credentials.json
|
|
||||||
google-services.json
|
|
||||||
!app/google-services.json
|
|
||||||
|
|
||||||
|
app/src/release/agconnect-services.json
|
||||||
.idea/appInsightsSettings.xml
|
app/src/release/agconnect-credentials.json
|
||||||
|
.idea/deploymentTargetDropDown.xml
|
||||||
|
.idea/kotlinc.xml
|
||||||
|
@ -61,7 +61,7 @@ script:
|
|||||||
gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/agconnect-services.json.gpg;
|
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/key.p12.gpg;
|
||||||
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg;
|
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg;
|
||||||
./gradlew publishPlayRelease --stacktrace;
|
./gradlew publishPlayRelease -PenableFirebase --stacktrace;
|
||||||
fi
|
fi
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
|
88
README.cs.md
88
README.cs.md
@ -1,33 +1,73 @@
|
|||||||
Česká verze / [Deutsche Version](README.de.md) / [English version](README.en.md) / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md)
|
Česká verze / [Deutsche Version](README.de.md) / [English version](README.en.md) / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md)
|
||||||
|
|
||||||
# Wulkanowy MOD
|
# Wulkanowy
|
||||||
|
|
||||||
## Funkce:
|
[](https://github.com/wulkanowy/wulkanowy/actions)
|
||||||
* skrýt známky
|
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
||||||
* Skrýt jednotlivé záznamy o docházce.
|
[](https://discord.gg/vccAQBr)
|
||||||
* Skrýt komentáře.
|
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||||
* falešná docházka %
|
[](https://github.com/wulkanowy/wulkanowy/releases)
|
||||||
|
[](https://translate.wulkanowy.net.pl)
|
||||||
|
|
||||||
Chcete-li se dostat na skrytý panel:
|
Neoficiální klient deníku VULCAN UONET+ pro žáka a rodiče
|
||||||
1. Přejděte na kartu „Další“.
|
|
||||||
2. Přejděte na panel „Nastavení“.
|
|
||||||
3. Přejděte na panel „O aplikaci“.
|
|
||||||
4. Klikněte 5x na logo aplikace
|
|
||||||
5. Přejděte na domovskou obrazovku
|
|
||||||
6. Přejděte do nastavení
|
|
||||||
7. Zadejte „tajná nastavení“
|
|
||||||
|
|
||||||
# Instalace
|
## Funkce
|
||||||
|
|
||||||
| Název souboru | Přizpůsobeno |
|
* přihlášení pomocí emailu a hesla
|
||||||
| ---------------- | ----------------- |
|
* funkce z webové stránky deníku:
|
||||||
| `*-fdroid-*.apk` | F-Droid |
|
* známky
|
||||||
| `*-hms-*.apk` | Huawei AppGallery |
|
* statistiky známek
|
||||||
| `*-play-*.apk` | Play Store |
|
* frekvence
|
||||||
|
* procento frekvence
|
||||||
|
* zkoušky
|
||||||
|
* plán lekce
|
||||||
|
* dokončené lekce
|
||||||
|
* zprávy
|
||||||
|
* domácí úkoly
|
||||||
|
* poznámky
|
||||||
|
* šťastné číslo
|
||||||
|
* další lekce
|
||||||
|
* školní setkání
|
||||||
|
* informace o žáku a škole
|
||||||
|
* výpočet průměru nezávisle na preferencích školy
|
||||||
|
* upozornění, např. o nových známkách
|
||||||
|
* podpora více účtů s možností přejmenování žáků
|
||||||
|
* tmavý a černý (AMOLED) motiv
|
||||||
|
* offline režim
|
||||||
|
* volitelné reklamy na podporu projektu
|
||||||
|
|
||||||
Stáhněte si vybranou verzi z [releases](https://git.sador.me/sadorowo/wulkanowy-mod/releases).
|
## Stáhnout
|
||||||
Doporučujeme stáhnout nejnovější dostupnou verzi.
|
|
||||||
|
|
||||||
# O projektu Wulkanowy
|
Aktuální verzi si můžete stáhnout z Google Play, F-Droid nebo Huawei AppGallery
|
||||||
|
|
||||||
Chcete si přečíst více o projektu Wulkanowy? [Klikněte sem](https://github.com/wulkanowy/wulkanowy)
|
[<img src="https://play.google.com/intl/cs-CZ/badges/images/generic/cs_badge_web_generic.png"
|
||||||
|
alt="Nyní na Google Play"
|
||||||
|
height="80">](https://play.google.com/store/apps/details?id=io.github.wulkanowy)
|
||||||
|
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
||||||
|
alt="Stáhnout s F-Droid"
|
||||||
|
height="80">](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||||
|
[<img src="https://i.imgur.com/baTGiDP.png"
|
||||||
|
alt="Objevuj v AppGallery"
|
||||||
|
height="80">](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=)
|
||||||
|
|
||||||
|
Můžete si také stáhnout [vývojovou verzi](https://wulkanowy.github.io/#download), která zahrnuje nové funkce připravované pro příští vydání
|
||||||
|
|
||||||
|
## Postaveno s pomocí
|
||||||
|
|
||||||
|
|
||||||
|
* [Wulkanowy SDK](https://github.com/wulkanowy/sdk)
|
||||||
|
* [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html)
|
||||||
|
* [Hilt](https://dagger.dev/hilt/)
|
||||||
|
* [Room](https://developer.android.com/topic/libraries/architecture/room)
|
||||||
|
* [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager)
|
||||||
|
|
||||||
|
## Spolupráce
|
||||||
|
|
||||||
|
Přispějte do projektu vytvořením PR nebo odesláním issue na GitHub.
|
||||||
|
|
||||||
|
Pro zájemce o překlad aplikace do různých jazyků poskytujeme Crowdin:
|
||||||
|
https://crowdin.com/project/wulkanowy2
|
||||||
|
|
||||||
|
## Licence
|
||||||
|
|
||||||
|
Tento projekt je licencován pod licencí Apache License 2.0 - podrobnosti v souboru [LICENSE](LICENSE)
|
||||||
|
88
README.de.md
88
README.de.md
@ -1,33 +1,73 @@
|
|||||||
[Česká verze](README.cs.md) / Deutsche Version / [English version](README.en.md) / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md)
|
[Česká verze](README.cs.md) / Deutsche Version / [English version](README.en.md) / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md)
|
||||||
|
|
||||||
# Wulkanowy MOD
|
# Wulkanowy
|
||||||
|
|
||||||
## Funktionen:
|
[](https://github.com/wulkanowy/wulkanowy/actions)
|
||||||
* Noten ausblenden
|
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
||||||
* Individuelle Anwesenheitslisten ausblenden.
|
[](https://discord.gg/vccAQBr)
|
||||||
* Kommentare ausblenden.
|
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||||
* Anwesenheit fälschen %
|
[](https://github.com/wulkanowy/wulkanowy/releases)
|
||||||
|
[](https://translate.wulkanowy.net.pl)
|
||||||
|
|
||||||
So gelangen Sie zum ausgeblendeten Bereich:
|
Inoffizieller Android VULCAN UONET+ Registrierungsclient für Schüler und ihre Eltern
|
||||||
1. Gehen Sie zur Registerkarte „Mehr“.
|
|
||||||
2. Gehen Sie zum Bereich „Einstellungen“.
|
|
||||||
3. Gehen Sie zum Bereich „Über die Anwendung“.
|
|
||||||
4. Klicken Sie fünfmal auf das Anwendungslogo
|
|
||||||
5. Gehen Sie zum Startbildschirm
|
|
||||||
6. Gehen Sie zu den Einstellungen
|
|
||||||
7. Geben Sie „Geheime Einstellungen“ ein
|
|
||||||
|
|
||||||
# Installation
|
## Merkmale
|
||||||
|
|
||||||
| Dateiname | Angepasst an |
|
* Einloggen mit E-Mail und Passwort
|
||||||
| ---------------- | ----------------- |
|
* Funktionen von der Registerwebsite:
|
||||||
| `*-fdroid-*.apk` | F-Droid |
|
* Noten
|
||||||
| `*-hms-*.apk` | Huawei AppGallery |
|
* Notenstatistik
|
||||||
| `*-play-*.apk` | Play Store |
|
* Anwesenheit
|
||||||
|
* Prozentsatz der Anwesenheit
|
||||||
|
* Prüfungen
|
||||||
|
* Stundenplan
|
||||||
|
* abgeschlossene Unterrichtsstunden
|
||||||
|
* Nachrichten
|
||||||
|
* Hausaufgaben
|
||||||
|
* Anmerkungen
|
||||||
|
* Glückszahl
|
||||||
|
* Zusätzliche Lektionen
|
||||||
|
* Schulkonferenzen
|
||||||
|
* Schüler- und Schulinformationen
|
||||||
|
* Berechnung des Durchschnitts unabhängig von den Präferenzen der Schule
|
||||||
|
* Benachrichtigungen, z. B. über eine neue Note
|
||||||
|
* Unterstützung für mehrere Konten mit der Möglichkeit, den Namen des Schülers zu ändern
|
||||||
|
* dunkles und schwarzes (AMOLED) Thema
|
||||||
|
* Offline-Modus
|
||||||
|
* optionale Werbungen, die es uns ermöglichen das Projekt zu unterstützen
|
||||||
|
|
||||||
Laden Sie die ausgewählte Version von [hier](https://git.sador.me/sadorowo/wulkanowy-mod/releases) herunter.
|
## Herunterladen
|
||||||
Wir empfehlen, die neueste verfügbare Version herunterzuladen.
|
|
||||||
|
|
||||||
# Über das Wulkanowy-Projekt
|
Die aktuelle Version können Sie von der Google Play, F-Droid oder Huawei AppGallery store herunterladen
|
||||||
|
|
||||||
Möchten Sie mehr über das Wulkanowy-Projekt lesen? [Hier klicken](https://github.com/wulkanowy/wulkanowy)
|
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
|
||||||
|
alt="Get it on Google Play"
|
||||||
|
height="80">](https://play.google.com/store/apps/details?id=io.github.wulkanowy)
|
||||||
|
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
||||||
|
alt="Get it on F-Droid"
|
||||||
|
height="80">](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||||
|
[<img src="appgallery_badge.png"
|
||||||
|
alt="Explore it on AppGallery"
|
||||||
|
height="80">](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=)
|
||||||
|
|
||||||
|
Sie können auch eine [Entwicklungsversion herunterladen](https://wulkanowy.github.io/#download) die beinhaltet neue Funktionen, die für die nächste Version vorbereitet werden
|
||||||
|
|
||||||
|
## Gebaut mit
|
||||||
|
|
||||||
|
|
||||||
|
* [Wulkanowy SDK](https://github.com/wulkanowy/sdk)
|
||||||
|
* [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html)
|
||||||
|
* [Hilt](https://dagger.dev/hilt/)
|
||||||
|
* [Room](https://developer.android.com/topic/libraries/architecture/room)
|
||||||
|
* [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager)
|
||||||
|
|
||||||
|
## Beitragen
|
||||||
|
|
||||||
|
Bitte tragen Sie zum Projekt bei, indem Sie entweder eine PR erstellen oder ein Issue auf GitHub einreichen.
|
||||||
|
|
||||||
|
Für Personen, die daran interessiert sind, die Anwendung in verschiedene Sprachen zu übersetzen, bieten wir Crowdin
|
||||||
|
https://crowdin.com/project/wulkanowy2
|
||||||
|
|
||||||
|
## Lizenz
|
||||||
|
|
||||||
|
Dieses Projekt ist unter der Apache License 2.0 lizenziert - siehe die [LIZENZ](LICENSE) Datei für Details
|
||||||
|
88
README.en.md
88
README.en.md
@ -1,33 +1,73 @@
|
|||||||
[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / English version / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md)
|
[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / English version / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md)
|
||||||
|
|
||||||
# Wulkanowy MOD
|
# Wulkanowy
|
||||||
|
|
||||||
## Functions:
|
[](https://github.com/wulkanowy/wulkanowy/actions)
|
||||||
* hide grades
|
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
||||||
* hide individual attendance entries
|
[](https://discord.gg/vccAQBr)
|
||||||
* hide comments
|
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||||
* fake attendance %.
|
[](https://github.com/wulkanowy/wulkanowy/releases)
|
||||||
|
[](https://translate.wulkanowy.net.pl)
|
||||||
|
|
||||||
To get to the hidden panel:
|
Unofficial android VULCAN UONET+ register client for both students and their parents
|
||||||
1. Go to the "More" tab
|
|
||||||
2. Go to the "Settings" panel
|
|
||||||
3. Go to the "About application" panel
|
|
||||||
4. Click on the application logo 5 times
|
|
||||||
5. Go to the home screen
|
|
||||||
6. Go to settings
|
|
||||||
7. Enter "secret settings"
|
|
||||||
|
|
||||||
# Installation
|
## Features
|
||||||
|
|
||||||
| File name | Adapted to |
|
* logging in using the email and password
|
||||||
| ---------------- | ----------------- |
|
* functions from the register website:
|
||||||
| `*-fdroid-*.apk` | F-Droid |
|
* grades
|
||||||
| `*-hms-*.apk` | Huawei AppGallery |
|
* grade statistics
|
||||||
| `*-play-*.apk` | Play Store |
|
* attendance
|
||||||
|
* percentage of attendance
|
||||||
|
* exams
|
||||||
|
* timetable
|
||||||
|
* completed lessons
|
||||||
|
* messages
|
||||||
|
* homework
|
||||||
|
* notes
|
||||||
|
* lucky number
|
||||||
|
* additional lessons
|
||||||
|
* school conferences
|
||||||
|
* student and school information
|
||||||
|
* calculation of the average independently of school's preferences
|
||||||
|
* notifications, e.g. about a new grade
|
||||||
|
* support for multiple accounts with the ability to rename students
|
||||||
|
* dark and black (AMOLED) theme
|
||||||
|
* offline mode
|
||||||
|
* optional ads which allow to support the project
|
||||||
|
|
||||||
Download application from [releases](https://git.sador.me/sadorowo/wulkanowy-mod/releases).
|
## Download
|
||||||
We recommend downloading the latest available version.
|
|
||||||
|
|
||||||
# About the Wulkanowy project
|
You can download the current version from the Google Play, F-Droid or Huawei AppGallery store
|
||||||
|
|
||||||
Want to read more about the Wulkanowy project? [Click here](https://github.com/wulkanowy/wulkanowy)
|
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
|
||||||
|
alt="Get it on Google Play"
|
||||||
|
height="80">](https://play.google.com/store/apps/details?id=io.github.wulkanowy)
|
||||||
|
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
||||||
|
alt="Get it on F-Droid"
|
||||||
|
height="80">](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||||
|
[<img src="appgallery_badge.png"
|
||||||
|
alt="Explore it on AppGallery"
|
||||||
|
height="80">](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=)
|
||||||
|
|
||||||
|
You can also download a [development version](https://wulkanowy.github.io/#download) that includes new features being prepared for the next release
|
||||||
|
|
||||||
|
## Built With
|
||||||
|
|
||||||
|
|
||||||
|
* [Wulkanowy SDK](https://github.com/wulkanowy/sdk)
|
||||||
|
* [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html)
|
||||||
|
* [Hilt](https://dagger.dev/hilt/)
|
||||||
|
* [Room](https://developer.android.com/topic/libraries/architecture/room)
|
||||||
|
* [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager)
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Please contribute to the project either by creating a PR or submitting an issue on GitHub.
|
||||||
|
|
||||||
|
For people interested in translating the application into different languages, we provide Crowdin
|
||||||
|
https://crowdin.com/project/wulkanowy2
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details
|
||||||
|
89
README.md
89
README.md
@ -1,33 +1,74 @@
|
|||||||
[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / [English version](README.en.md) / Polska wersja / [Slovenská verzia](README.sk.md)
|
[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / [English version](README.en.md) / Polska wersja / [Slovenská verzia](README.sk.md)
|
||||||
|
|
||||||
# Wulkanowy MOD
|
# Wulkanowy
|
||||||
|
|
||||||
## Funkcje:
|
[](https://github.com/wulkanowy/wulkanowy/actions)
|
||||||
* ukryj oceny
|
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
||||||
* ukryj poszczególne wpisy frekwencji
|
[](https://discord.gg/vccAQBr)
|
||||||
* ukryj uwagi
|
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||||
* sfałszuj % frekwencji
|
[](https://github.com/wulkanowy/wulkanowy/releases)
|
||||||
|
[](https://translate.wulkanowy.net.pl)
|
||||||
|
|
||||||
Aby dostać się do ukrytego panelu:
|
Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica
|
||||||
1. Przejdź do karty "Więcej"
|
|
||||||
2. Przejdź do panelu "Ustawienia"
|
|
||||||
3. Przejdź do panelu "O aplikacji"
|
|
||||||
4. Kliknij 5 razy w logo aplikacji
|
|
||||||
5. Przejdź na ekran główny
|
|
||||||
6. Wejdź w ustawienia
|
|
||||||
7. Wejdź w "sekretne ustawienia"
|
|
||||||
|
|
||||||
# Instalacja
|
## Funkcje
|
||||||
|
|
||||||
| Nazwa pliku | Przystosowana do |
|
* logowanie za pomocą e-maila i hasła
|
||||||
| ---------------- | ----------------- |
|
* funkcje ze strony internetowej dziennika:
|
||||||
| `*-fdroid-*.apk` | F-Droid |
|
* oceny
|
||||||
| `*-hms-*.apk` | Huawei AppGallery |
|
* statystyki ocen
|
||||||
| `*-play-*.apk` | Sklep Play |
|
* frekwencja
|
||||||
|
* procent frekwencji
|
||||||
|
* sprawdziany
|
||||||
|
* plan lekcji
|
||||||
|
* lekcje zrealizowane
|
||||||
|
* wiadomości
|
||||||
|
* zadania domowe
|
||||||
|
* uwagi
|
||||||
|
* szczęśliwy numerek
|
||||||
|
* dodatkowe lekcje
|
||||||
|
* zebrania w szkole
|
||||||
|
* informacje o uczniu i szkole
|
||||||
|
* obliczanie średniej niezależnie od preferencji szkoły
|
||||||
|
* powiadomienia np. o nowej ocenie
|
||||||
|
* obsługa wielu kont wraz z możliwością zmiany nazwy ucznia
|
||||||
|
* ciemny i czarny (AMOLED) motyw
|
||||||
|
* tryb offline
|
||||||
|
* opcjonalne reklamy umożliwiające wsparcie projektu
|
||||||
|
|
||||||
Pobierz wybraną wersję z [wydań](https://git.sador.me/sadorowo/wulkanowy-mod/releases).
|
## Pobierz
|
||||||
Zalecamy pobranie najnowszej dostępnej wersji.
|
|
||||||
|
|
||||||
# O projekcie Wulkanowy
|
Aktualną wersję możesz pobrać ze sklepu Google Play, F-Droid lub Huawei AppGallery
|
||||||
|
|
||||||
Chcesz poczytać więcej o projekcie Wulkanowy? [Kliknij tutaj](https://github.com/wulkanowy/wulkanowy)
|
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
|
||||||
|
alt="Pobierz z Google Play"
|
||||||
|
height="80">](https://play.google.com/store/apps/details?id=io.github.wulkanowy)
|
||||||
|
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
||||||
|
alt="Pobierz z F-Droid"
|
||||||
|
height="80">](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||||
|
[<img src="appgallery_badge.png"
|
||||||
|
alt="Odkrywaj w AppGallery"
|
||||||
|
height="80">](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=)
|
||||||
|
|
||||||
|
|
||||||
|
Możesz także pobrać [wersję rozwojową](https://wulkanowy.github.io/#download), która zawiera nowe funkcje przygotowywane do następnego wydania
|
||||||
|
|
||||||
|
|
||||||
|
## Zbudowana za pomocą
|
||||||
|
|
||||||
|
* [Wulkanowy SDK](https://github.com/wulkanowy/sdk)
|
||||||
|
* [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html)
|
||||||
|
* [Hilt](https://dagger.dev/hilt/)
|
||||||
|
* [Room](https://developer.android.com/topic/libraries/architecture/room)
|
||||||
|
* [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager)
|
||||||
|
|
||||||
|
## Współpraca
|
||||||
|
|
||||||
|
Wnieś swój wkład w projekt, tworząc PR lub wysyłając issue na GitHub.
|
||||||
|
|
||||||
|
Dla osób zainteresowanych tłumaczeniem aplikacji na różne języki udostępniamy Crowdina
|
||||||
|
https://crowdin.com/project/wulkanowy2
|
||||||
|
|
||||||
|
## Licencja
|
||||||
|
|
||||||
|
Ten projekt udostępniany jest na licencji Apache License 2.0 - szczegóły w pliku [LICENSE](LICENSE)
|
||||||
|
88
README.sk.md
88
README.sk.md
@ -1,33 +1,73 @@
|
|||||||
[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / [English version](README.en.md) / [Polska wersja](README.md) / Slovenská verzia
|
[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / [English version](README.en.md) / [Polska wersja](README.md) / Slovenská verzia
|
||||||
|
|
||||||
# Wulkanowy MOD
|
# Wulkanowy
|
||||||
|
|
||||||
## Funkcie:
|
[](https://github.com/wulkanowy/wulkanowy/actions)
|
||||||
* skryť známky
|
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
||||||
* Skryť individuálne záznamy o dochádzke.
|
[](https://discord.gg/vccAQBr)
|
||||||
* Skryť komentáre.
|
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||||
* falošná dochádzka %
|
[](https://github.com/wulkanowy/wulkanowy/releases)
|
||||||
|
[](https://translate.wulkanowy.net.pl)
|
||||||
|
|
||||||
Ak chcete prejsť na skrytý panel:
|
Neoficiálny klient denníka VULCAN UONET+ pre žiaka a rodičov
|
||||||
1. Prejdite na kartu „Viac“.
|
|
||||||
2. Prejdite na panel „Nastavenia“.
|
|
||||||
3. Prejdite na panel „O aplikácii“.
|
|
||||||
4. Kliknite 5-krát na logo aplikácie
|
|
||||||
5. Prejdite na domovskú obrazovku
|
|
||||||
6. Prejdite do nastavení
|
|
||||||
7. Zadajte „tajné nastavenia“
|
|
||||||
|
|
||||||
# Inštalácia
|
## Funkcie
|
||||||
|
|
||||||
| Názov súboru | Prispôsobené |
|
* prihlásenie pomocou emailu a hesla
|
||||||
| ---------------- | ----------------- |
|
* funkcie z webovej stránky denníka:
|
||||||
| `*-fdroid-*.apk` | F-Droid |
|
* známky
|
||||||
| `*-hms-*.apk` | Huawei AppGallery |
|
* štatistiky známok
|
||||||
| `*-play-*.apk` | Play Store |
|
* frekvencia
|
||||||
|
* percento frekvencie
|
||||||
|
* skúšky
|
||||||
|
* plán lekcie
|
||||||
|
* dokončené lekcie
|
||||||
|
* správy
|
||||||
|
* domáce úlohy
|
||||||
|
* poznámky
|
||||||
|
* šťastné číslo
|
||||||
|
* ďalšie lekcie
|
||||||
|
* školské stretnutie
|
||||||
|
* informácie o žiakovi a škole
|
||||||
|
* výpočet priemeru nezávisle od preferencií školy
|
||||||
|
* upozornenia, napr. o nových známkach
|
||||||
|
* podpora viacerých účtov s možnosťou premenovania žiakov
|
||||||
|
* tmavý a čierny (AMOLED) motív
|
||||||
|
* offline režim
|
||||||
|
* voliteľné reklamy na podporu projektu
|
||||||
|
|
||||||
Stiahnite si vybranú verziu z [releases](https://git.sador.me/sadorowo/wulkanowy-mod/releases).
|
## Stiahnuť
|
||||||
Odporúčame stiahnuť najnovšiu dostupnú verziu.
|
|
||||||
|
|
||||||
# O projekte Wulkanowy
|
Aktuálnu verziu si môžete stiahnuť z Google Play, F-Droid alebo Huawei AppGallery
|
||||||
|
|
||||||
Chcete si prečítať viac o projekte Wulkanowy? [Kliknite sem](https://github.com/wulkanowy/wulkanowy)
|
[<img src="https://play.google.com/intl/sk/badges/images/generic/sk_badge_web_generic.png"
|
||||||
|
alt="Nyní na Google Play"
|
||||||
|
height="80">](https://play.google.com/store/apps/details?id=io.github.wulkanowy)
|
||||||
|
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
||||||
|
alt="Stiahnuť s F-Droid"
|
||||||
|
height="80">](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||||
|
[<img src="https://i.imgur.com/sX8UyAw.png"
|
||||||
|
alt="Objavíte v AppGallery"
|
||||||
|
height="80">](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=)
|
||||||
|
|
||||||
|
Môžete si tiež stiahnuť [vývojovú verziu](https://wulkanowy.github.io/#download), ktorá zahrňuje nové funkcie pripravované pre budúce vydanie
|
||||||
|
|
||||||
|
## Postavené s pomocou
|
||||||
|
|
||||||
|
|
||||||
|
* [Wulkanowy SDK](https://github.com/wulkanowy/sdk)
|
||||||
|
* [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html)
|
||||||
|
* [Hilt](https://dagger.dev/hilt/)
|
||||||
|
* [Room](https://developer.android.com/topic/libraries/architecture/room)
|
||||||
|
* [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager)
|
||||||
|
|
||||||
|
## Spolupráca
|
||||||
|
|
||||||
|
Prispejte do projektu vytvorením PR alebo odoslaním issue na GitHub.
|
||||||
|
|
||||||
|
Pre záujemcov o preklad aplikácie do rôznych jazykov poskytujeme Crowdin:
|
||||||
|
https://crowdin.com/project/wulkanowy2
|
||||||
|
|
||||||
|
## Licencia
|
||||||
|
|
||||||
|
Tento projekt je licencovaný pod licenciou Apache License 2.0 - podrobnosti v súbore [LICENSE](LICENSE)
|
||||||
|
@ -27,12 +27,15 @@ android {
|
|||||||
testApplicationId "io.github.tests.wulkanowy"
|
testApplicationId "io.github.tests.wulkanowy"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 34
|
targetSdkVersion 34
|
||||||
versionCode 173
|
versionCode 148
|
||||||
versionName "2.6.13"
|
versionName "2.4.2"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
resValue "string", "app_name", "Wulkanowy"
|
resValue "string", "app_name", "Wulkanowy"
|
||||||
manifestPlaceholders = [admob_project_id: ""]
|
manifestPlaceholders = [
|
||||||
|
firebase_enabled: project.hasProperty("enableFirebase"),
|
||||||
|
admob_project_id: ""
|
||||||
|
]
|
||||||
|
|
||||||
buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "null"
|
buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "null"
|
||||||
buildConfigField "String", "DASHBOARD_TILE_AD_ID", "null"
|
buildConfigField "String", "DASHBOARD_TILE_AD_ID", "null"
|
||||||
@ -62,8 +65,8 @@ android {
|
|||||||
release {
|
release {
|
||||||
minifyEnabled true
|
minifyEnabled true
|
||||||
shrinkResources true
|
shrinkResources true
|
||||||
// proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
// signingConfig signingConfigs.release
|
signingConfig signingConfigs.release
|
||||||
buildConfigField "String", "MESSAGES_BASE_URL", "\"https://messages.wulkanowy.net.pl\""
|
buildConfigField "String", "MESSAGES_BASE_URL", "\"https://messages.wulkanowy.net.pl\""
|
||||||
buildConfigField "String", "SCHOOLS_BASE_URL", '"https://schools.wulkanowy.net.pl"'
|
buildConfigField "String", "SCHOOLS_BASE_URL", '"https://schools.wulkanowy.net.pl"'
|
||||||
}
|
}
|
||||||
@ -73,6 +76,7 @@ android {
|
|||||||
resValue "string", "app_name", "Wulkanowy DEV"
|
resValue "string", "app_name", "Wulkanowy DEV"
|
||||||
applicationIdSuffix ".dev"
|
applicationIdSuffix ".dev"
|
||||||
versionNameSuffix "-dev"
|
versionNameSuffix "-dev"
|
||||||
|
ext.enableCrashlytics = project.hasProperty("enableFirebase")
|
||||||
buildConfigField "String", "MESSAGES_BASE_URL", "\"https://messages.wulkanowy.net.pl\""
|
buildConfigField "String", "MESSAGES_BASE_URL", "\"https://messages.wulkanowy.net.pl\""
|
||||||
buildConfigField "String", "SCHOOLS_BASE_URL", '"https://schools.wulkanowy.net.pl"'
|
buildConfigField "String", "SCHOOLS_BASE_URL", '"https://schools.wulkanowy.net.pl"'
|
||||||
}
|
}
|
||||||
@ -160,7 +164,7 @@ play {
|
|||||||
defaultToAppBundles = false
|
defaultToAppBundles = false
|
||||||
track = 'production'
|
track = 'production'
|
||||||
releaseStatus = ReleaseStatus.IN_PROGRESS
|
releaseStatus = ReleaseStatus.IN_PROGRESS
|
||||||
userFraction = 0.1d
|
userFraction = 0.99d
|
||||||
updatePriority = 2
|
updatePriority = 2
|
||||||
enabled.set(false)
|
enabled.set(false)
|
||||||
}
|
}
|
||||||
@ -186,30 +190,28 @@ ext {
|
|||||||
android_hilt = "1.2.0"
|
android_hilt = "1.2.0"
|
||||||
room = "2.6.1"
|
room = "2.6.1"
|
||||||
chucker = "4.0.0"
|
chucker = "4.0.0"
|
||||||
mockk = "1.13.10"
|
mockk = "1.13.9"
|
||||||
coroutines = "1.8.1"
|
coroutines = "1.8.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'io.github.wulkanowy:sdk:2.6.11'
|
implementation 'io.github.wulkanowy:sdk:2.4.2-SNAPSHOT'
|
||||||
|
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
|
||||||
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3"
|
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-guava:$coroutines"
|
|
||||||
|
|
||||||
implementation 'androidx.core:core-ktx:1.13.1'
|
implementation 'androidx.core:core-ktx:1.12.0'
|
||||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||||
implementation "androidx.activity:activity-ktx:1.9.0"
|
implementation "androidx.activity:activity-ktx:1.8.2"
|
||||||
implementation "androidx.appcompat:appcompat:1.6.1"
|
implementation "androidx.appcompat:appcompat:1.6.1"
|
||||||
implementation "androidx.fragment:fragment-ktx:1.7.0"
|
implementation "androidx.fragment:fragment-ktx:1.6.2"
|
||||||
implementation "androidx.annotation:annotation:1.7.1"
|
implementation "androidx.annotation:annotation:1.7.1"
|
||||||
implementation "androidx.javascriptengine:javascriptengine:1.0.0-beta01"
|
|
||||||
|
|
||||||
implementation "androidx.preference:preference-ktx:1.2.1"
|
implementation "androidx.preference:preference-ktx:1.2.1"
|
||||||
implementation "androidx.recyclerview:recyclerview:1.3.2"
|
implementation "androidx.recyclerview:recyclerview:1.3.2"
|
||||||
implementation "androidx.viewpager2:viewpager2:1.1.0-rc01"
|
implementation "androidx.viewpager2:viewpager2:1.1.0-beta02"
|
||||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
||||||
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
|
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
|
||||||
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
|
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
|
||||||
@ -235,7 +237,7 @@ dependencies {
|
|||||||
implementation 'com.github.ncapdevi:FragNav:3.3.0'
|
implementation 'com.github.ncapdevi:FragNav:3.3.0'
|
||||||
implementation "com.github.YarikSOffice:lingver:1.3.0"
|
implementation "com.github.YarikSOffice:lingver:1.3.0"
|
||||||
|
|
||||||
implementation 'com.squareup.retrofit2:retrofit:2.11.0'
|
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||||
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0"
|
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0"
|
||||||
implementation "com.squareup.okhttp3:logging-interceptor:4.12.0"
|
implementation "com.squareup.okhttp3:logging-interceptor:4.12.0"
|
||||||
implementation "com.squareup.okhttp3:okhttp-urlconnection:4.12.0"
|
implementation "com.squareup.okhttp3:okhttp-urlconnection:4.12.0"
|
||||||
@ -248,9 +250,9 @@ dependencies {
|
|||||||
implementation "io.github.wulkanowy:AppKillerManager:3.0.1"
|
implementation "io.github.wulkanowy:AppKillerManager:3.0.1"
|
||||||
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
|
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
|
||||||
implementation 'com.fredporciuncula:flow-preferences:1.9.1'
|
implementation 'com.fredporciuncula:flow-preferences:1.9.1'
|
||||||
implementation 'org.apache.commons:commons-text:1.12.0'
|
implementation 'org.apache.commons:commons-text:1.11.0'
|
||||||
|
|
||||||
playImplementation platform('com.google.firebase:firebase-bom:33.0.0')
|
playImplementation platform('com.google.firebase:firebase-bom:32.7.2')
|
||||||
playImplementation 'com.google.firebase:firebase-analytics'
|
playImplementation 'com.google.firebase:firebase-analytics'
|
||||||
playImplementation 'com.google.firebase:firebase-messaging'
|
playImplementation 'com.google.firebase:firebase-messaging'
|
||||||
playImplementation 'com.google.firebase:firebase-crashlytics:'
|
playImplementation 'com.google.firebase:firebase-crashlytics:'
|
||||||
@ -276,7 +278,7 @@ dependencies {
|
|||||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines"
|
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines"
|
||||||
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||||
|
|
||||||
testImplementation 'org.robolectric:robolectric:4.12.1'
|
testImplementation 'org.robolectric:robolectric:4.11.1'
|
||||||
testImplementation "androidx.test:runner:1.5.2"
|
testImplementation "androidx.test:runner:1.5.2"
|
||||||
testImplementation "androidx.test.ext:junit:1.1.5"
|
testImplementation "androidx.test.ext:junit:1.1.5"
|
||||||
testImplementation "androidx.test:core:1.5.0"
|
testImplementation "androidx.test:core:1.5.0"
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
#!/bin/bash -
|
#!/bin/bash -
|
||||||
|
|
||||||
content=$(cat < "app/src/main/play/release-notes/pl-PL/default.txt") || exit
|
content=$(cat < "app/src/main/play/release-notes/pl-PL/default.txt") || exit
|
||||||
content2=echo "$content" | dos2unix
|
if [[ "${#content}" -gt 500 ]]; then
|
||||||
if [[ "${#content2}" -gt 500 ]]; then
|
|
||||||
echo >&2 "Release notes content has reached the limit of 500 characters"
|
echo >&2 "Release notes content has reached the limit of 500 characters"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
92
app/src/debug/agconnect-services.json
Normal file
92
app/src/debug/agconnect-services.json
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
{
|
||||||
|
"agcgw": {
|
||||||
|
"backurl": "connect-dre.hispace.hicloud.com",
|
||||||
|
"url": "connect-dre.dbankcloud.cn",
|
||||||
|
"websocketbackurl": "connect-ws-dre.hispace.dbankcloud.com",
|
||||||
|
"websocketurl": "connect-ws-dre.hispace.dbankcloud.cn"
|
||||||
|
},
|
||||||
|
"agcgw_all": {
|
||||||
|
"CN": "connect-drcn.dbankcloud.cn",
|
||||||
|
"CN_back": "connect-drcn.hispace.hicloud.com",
|
||||||
|
"DE": "connect-dre.dbankcloud.cn",
|
||||||
|
"DE_back": "connect-dre.hispace.hicloud.com",
|
||||||
|
"RU": "connect-drru.hispace.dbankcloud.ru",
|
||||||
|
"RU_back": "connect-drru.hispace.dbankcloud.cn",
|
||||||
|
"SG": "connect-dra.dbankcloud.cn",
|
||||||
|
"SG_back": "connect-dra.hispace.hicloud.com"
|
||||||
|
},
|
||||||
|
"websocketgw_all": {
|
||||||
|
"CN": "connect-ws-drcn.hispace.dbankcloud.cn",
|
||||||
|
"CN_back": "connect-ws-drcn.hispace.dbankcloud.com",
|
||||||
|
"DE": "connect-ws-dre.hispace.dbankcloud.cn",
|
||||||
|
"DE_back": "connect-ws-dre.hispace.dbankcloud.com",
|
||||||
|
"RU": "connect-ws-drru.hispace.dbankcloud.ru",
|
||||||
|
"RU_back": "connect-ws-drru.hispace.dbankcloud.cn",
|
||||||
|
"SG": "connect-ws-dra.hispace.dbankcloud.cn",
|
||||||
|
"SG_back": "connect-ws-dra.hispace.dbankcloud.com"
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"cp_id": "890048000024105546",
|
||||||
|
"product_id": "736430079244736562",
|
||||||
|
"client_id": "514530959291319360",
|
||||||
|
"client_secret": "C42522DBF17D3D4BBE9D9C1783A54484B7E6844B388B7A67502D36A633A4186B",
|
||||||
|
"project_id": "736430079244736562",
|
||||||
|
"app_id": "106552551",
|
||||||
|
"api_key": "CgB6e3x9BUNiq+r8ebCHNojjjYsMT4pJSjjNDOkm9owtBb6rVI6LjnASoZBRxbjjhObcrV5gANo99fI/eKZDTbWS",
|
||||||
|
"package_name": "io.github.wulkanowy.dev"
|
||||||
|
},
|
||||||
|
"oauth_client": {
|
||||||
|
"client_id": "106552551",
|
||||||
|
"client_type": 1
|
||||||
|
},
|
||||||
|
"app_info": {
|
||||||
|
"app_id": "106552551",
|
||||||
|
"package_name": "io.github.wulkanowy.dev"
|
||||||
|
},
|
||||||
|
"service": {
|
||||||
|
"analytics": {
|
||||||
|
"collector_url": "datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn",
|
||||||
|
"collector_url_ru": "datacollector-drru.dt.dbankcloud.ru,datacollector-drru.dt.hicloud.com",
|
||||||
|
"collector_url_sg": "datacollector-dra.dt.hicloud.com,datacollector-dra.dt.dbankcloud.cn",
|
||||||
|
"collector_url_de": "datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn",
|
||||||
|
"collector_url_cn": "datacollector-drcn.dt.hicloud.com,datacollector-drcn.dt.dbankcloud.cn",
|
||||||
|
"resource_id": "p1",
|
||||||
|
"channel_id": ""
|
||||||
|
},
|
||||||
|
"search":{
|
||||||
|
"url":"https://search-dre.cloud.huawei.com"
|
||||||
|
},
|
||||||
|
"cloudstorage": {
|
||||||
|
"storage_url_sg_back": "https://agc-storage-dra.cloud.huawei.asia",
|
||||||
|
"storage_url_ru_back": "https://agc-storage-drru.cloud.huawei.ru",
|
||||||
|
"storage_url_ru": "https://agc-storage-drru.cloud.huawei.ru",
|
||||||
|
"storage_url_de_back": "https://agc-storage-dre.cloud.huawei.eu",
|
||||||
|
"storage_url_de": "https://ops-dre.agcstorage.link",
|
||||||
|
"storage_url": "https://agc-storage-drcn.platform.dbankcloud.cn",
|
||||||
|
"storage_url_sg": "https://ops-dra.agcstorage.link",
|
||||||
|
"storage_url_cn_back": "https://agc-storage-drcn.cloud.huawei.com.cn",
|
||||||
|
"storage_url_cn": "https://agc-storage-drcn.platform.dbankcloud.cn"
|
||||||
|
},
|
||||||
|
"ml": {
|
||||||
|
"mlservice_url": "ml-api-dre.ai.dbankcloud.com,ml-api-dre.ai.dbankcloud.cn"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"region": "DE",
|
||||||
|
"configuration_version": "3.0",
|
||||||
|
"appInfos": [
|
||||||
|
{
|
||||||
|
"package_name": "io.github.wulkanowy.dev",
|
||||||
|
"client": {
|
||||||
|
"app_id": "106552551"
|
||||||
|
},
|
||||||
|
"app_info": {
|
||||||
|
"package_name": "io.github.wulkanowy.dev",
|
||||||
|
"app_id": "106552551"
|
||||||
|
},
|
||||||
|
"oauth_client": {
|
||||||
|
"client_type": 1,
|
||||||
|
"client_id": "106552551"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -36,37 +36,6 @@
|
|||||||
"status": 2
|
"status": 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"client_info": {
|
|
||||||
"mobilesdk_app_id": "1:1091101852179:android:b558a25f65d088b1",
|
|
||||||
"android_client_info": {
|
|
||||||
"package_name": "io.github.wulkanowy"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"oauth_client": [
|
|
||||||
{
|
|
||||||
"client_id": "",
|
|
||||||
"client_type": 3
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"api_key": [
|
|
||||||
{
|
|
||||||
"current_key": ""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"services": {
|
|
||||||
"analytics_service": {
|
|
||||||
"status": 1
|
|
||||||
},
|
|
||||||
"appinvite_service": {
|
|
||||||
"status": 1,
|
|
||||||
"other_platform_oauth_client": []
|
|
||||||
},
|
|
||||||
"ads_service": {
|
|
||||||
"status": 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"configuration_version": "1"
|
"configuration_version": "1"
|
@ -3,8 +3,6 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:installLocation="internalOnly">
|
android:installLocation="internalOnly">
|
||||||
|
|
||||||
<uses-sdk tools:overrideLibrary="androidx.javascriptengine" />
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
@ -44,16 +42,16 @@
|
|||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:networkSecurityConfig="@xml/network_security_config"
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
android:resizeableActivity="true"
|
|
||||||
android:supportsRtl="false"
|
android:supportsRtl="false"
|
||||||
android:theme="@style/WulkanowyTheme"
|
android:theme="@style/WulkanowyTheme"
|
||||||
|
android:resizeableActivity="true"
|
||||||
tools:ignore="DataExtractionRules,UnusedAttribute">
|
tools:ignore="DataExtractionRules,UnusedAttribute">
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.modules.splash.SplashActivity"
|
android:name=".ui.modules.splash.SplashActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:theme="@style/WulkanowyTheme.SplashScreen"
|
android:theme="@style/WulkanowyTheme.SplashScreen"
|
||||||
tools:ignore="DiscouragedApi,LockedOrientationActivity">
|
tools:ignore="LockedOrientationActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
@ -157,9 +155,33 @@
|
|||||||
android:resource="@xml/provider_paths" />
|
android:resource="@xml/provider_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
|
<!-- workaround for https://github.com/firebase/firebase-android-sdk/issues/473 enabled:false -->
|
||||||
|
<!-- https://firebase.googleblog.com/2017/03/take-control-of-your-firebase-init-on.html -->
|
||||||
|
<provider
|
||||||
|
android:name="com.google.firebase.provider.FirebaseInitProvider"
|
||||||
|
android:authorities="${applicationId}.firebaseinitprovider"
|
||||||
|
android:enabled="${firebase_enabled}"
|
||||||
|
android:exported="false"
|
||||||
|
tools:ignore="MissingClass" />
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="install_channel"
|
android:name="install_channel"
|
||||||
android:value="${install_channel}" />
|
android:value="${install_channel}" />
|
||||||
|
<meta-data
|
||||||
|
android:name="firebase_analytics_collection_enabled"
|
||||||
|
android:value="${firebase_enabled}" />
|
||||||
|
<meta-data
|
||||||
|
android:name="google_analytics_adid_collection_enabled"
|
||||||
|
android:value="${firebase_enabled}" />
|
||||||
|
<meta-data
|
||||||
|
android:name="firebase_crashlytics_collection_enabled"
|
||||||
|
android:value="${firebase_enabled}" />
|
||||||
|
<meta-data
|
||||||
|
android:name="firebase_messaging_auto_init_enabled"
|
||||||
|
android:value="${firebase_enabled}" />
|
||||||
|
<meta-data
|
||||||
|
android:name="firebase_inapp_messaging_auto_data_collection_enabled"
|
||||||
|
android:value="${firebase_enabled}" />
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.firebase.messaging.default_notification_icon"
|
android:name="com.google.firebase.messaging.default_notification_icon"
|
||||||
android:resource="@drawable/ic_stat_all" />
|
android:resource="@drawable/ic_stat_all" />
|
||||||
|
@ -13,18 +13,22 @@ import dagger.Provides
|
|||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
import io.github.wulkanowy.data.api.services.SchoolsService
|
import io.github.wulkanowy.data.api.AdminMessageService
|
||||||
import io.github.wulkanowy.data.api.services.WulkanowyService
|
import io.github.wulkanowy.data.api.SchoolsService
|
||||||
import io.github.wulkanowy.data.db.AppDatabase
|
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.utils.AppInfo
|
import io.github.wulkanowy.utils.AppInfo
|
||||||
|
import io.github.wulkanowy.utils.RemoteConfigHelper
|
||||||
|
import io.github.wulkanowy.utils.WebkitCookieManagerProxy
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.logging.HttpLoggingInterceptor
|
import okhttp3.logging.HttpLoggingInterceptor
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import retrofit2.create
|
import retrofit2.create
|
||||||
|
import timber.log.Timber
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@ -32,6 +36,20 @@ import javax.inject.Singleton
|
|||||||
@InstallIn(SingletonComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
internal class DataModule {
|
internal class DataModule {
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Provides
|
||||||
|
fun provideSdk(chuckerInterceptor: ChuckerInterceptor, remoteConfig: RemoteConfigHelper) =
|
||||||
|
Sdk().apply {
|
||||||
|
androidVersion = android.os.Build.VERSION.RELEASE
|
||||||
|
buildTag = android.os.Build.MODEL
|
||||||
|
userAgentTemplate = remoteConfig.userAgentTemplate
|
||||||
|
setSimpleHttpLogger { Timber.d(it) }
|
||||||
|
setAdditionalCookieManager(WebkitCookieManagerProxy())
|
||||||
|
|
||||||
|
// for debug only
|
||||||
|
addInterceptor(chuckerInterceptor, network = true)
|
||||||
|
}
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideChuckerCollector(
|
fun provideChuckerCollector(
|
||||||
@ -71,7 +89,7 @@ internal class DataModule {
|
|||||||
okHttpClient: OkHttpClient,
|
okHttpClient: OkHttpClient,
|
||||||
json: Json,
|
json: Json,
|
||||||
appInfo: AppInfo
|
appInfo: AppInfo
|
||||||
): WulkanowyService = Retrofit.Builder()
|
): AdminMessageService = Retrofit.Builder()
|
||||||
.baseUrl(appInfo.messagesBaseUrl)
|
.baseUrl(appInfo.messagesBaseUrl)
|
||||||
.client(okHttpClient)
|
.client(okHttpClient)
|
||||||
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
|
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
|
||||||
|
@ -1,17 +1,11 @@
|
|||||||
package io.github.wulkanowy.data
|
package io.github.wulkanowy.data
|
||||||
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
||||||
import kotlinx.coroutines.FlowPreview
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.flow.combine
|
|
||||||
import kotlinx.coroutines.flow.debounce
|
|
||||||
import kotlinx.coroutines.flow.emitAll
|
import kotlinx.coroutines.flow.emitAll
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.flow.filterNot
|
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.flatMapLatest
|
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
@ -20,39 +14,16 @@ import kotlinx.coroutines.flow.takeWhile
|
|||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import kotlin.time.Duration
|
|
||||||
import kotlin.time.Duration.Companion.seconds
|
|
||||||
|
|
||||||
sealed interface Resource<out T> {
|
sealed class Resource<T> {
|
||||||
/**
|
|
||||||
* The initial value of a resource flow. Indicates no data that is currently available to be shown,
|
open class Loading<T> : Resource<T>()
|
||||||
* however with the expectation that the state will transition to another one soon.
|
|
||||||
*/
|
|
||||||
open class Loading<T> : Resource<T>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A semi-loading state with some data available to be displayed (usually cached data loaded from
|
|
||||||
* the database). Still not the target state and it's expected to transition into another one soon.
|
|
||||||
*/
|
|
||||||
data class Intermediate<T>(val data: T) : Loading<T>()
|
data class Intermediate<T>(val data: T) : Loading<T>()
|
||||||
|
|
||||||
/**
|
data class Success<T>(val data: T) : Resource<T>()
|
||||||
* The happy-path target state. Data can either be:
|
|
||||||
* - loaded from the database - while it may seem like this case is already handled by the
|
|
||||||
* Intermediate state, the difference here is semantic. Cached data is returned as Intermediate
|
|
||||||
* when there's a API request in progress (or soon expected to be), however when there is no
|
|
||||||
* intention of immediately querying the API, the cached data is returned as a Success.
|
|
||||||
* - fetched from the API.
|
|
||||||
*/
|
|
||||||
data class Success<T>(val data: T) : Resource<T>
|
|
||||||
|
|
||||||
/**
|
data class Error<T>(val error: Throwable) : Resource<T>()
|
||||||
* Something bad happened and we were unable to get the requested data. This can be caused by
|
|
||||||
* a database error, a network error, or really just any other error. Upon receiving this state
|
|
||||||
* the UI can either: display a full screen error, or, when it has received any data previously,
|
|
||||||
* display a snack bar informing of the problem.
|
|
||||||
*/
|
|
||||||
data class Error<T>(val error: Throwable) : Resource<T>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val <T> Resource<T>.dataOrNull: T?
|
val <T> Resource<T>.dataOrNull: T?
|
||||||
@ -93,22 +64,6 @@ fun <T, U> Resource<T>.mapData(block: (T) -> U) = when (this) {
|
|||||||
is Resource.Error -> Resource.Error(this.error)
|
is Resource.Error -> Resource.Error(this.error)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Injects another flow into this flow's resource data.
|
|
||||||
*/
|
|
||||||
inline fun <T1, T2, R> Flow<Resource<T1>>.combineWithResourceData(
|
|
||||||
flow: Flow<T2>,
|
|
||||||
crossinline block: suspend (T1, T2) -> R
|
|
||||||
): Flow<Resource<R>> =
|
|
||||||
combine(flow) { resource, inject ->
|
|
||||||
when (resource) {
|
|
||||||
is Resource.Success -> Resource.Success(block(resource.data, inject))
|
|
||||||
is Resource.Intermediate -> Resource.Intermediate(block(resource.data, inject))
|
|
||||||
is Resource.Loading -> Resource.Loading()
|
|
||||||
is Resource.Error -> Resource.Error(resource.error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> Flow<Resource<T>>.logResourceStatus(name: String, showData: Boolean = false) = onEach {
|
fun <T> Flow<Resource<T>>.logResourceStatus(name: String, showData: Boolean = false) = onEach {
|
||||||
val description = when (it) {
|
val description = when (it) {
|
||||||
is Resource.Intermediate -> "intermediate data received" + if (showData) " (data: `${it.data}`)" else ""
|
is Resource.Intermediate -> "intermediate data received" + if (showData) " (data: `${it.data}`)" else ""
|
||||||
@ -119,29 +74,8 @@ fun <T> Flow<Resource<T>>.logResourceStatus(name: String, showData: Boolean = fa
|
|||||||
Timber.i("$name: $description")
|
Timber.i("$name: $description")
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <T, U> Flow<Resource<T>>.mapResourceData(crossinline block: suspend (T) -> U) = map {
|
fun <T, U> Flow<Resource<T>>.mapResourceData(block: (T) -> U) = map {
|
||||||
when (it) {
|
it.mapData(block)
|
||||||
is Resource.Success -> Resource.Success(block(it.data))
|
|
||||||
is Resource.Intermediate -> Resource.Intermediate(block(it.data))
|
|
||||||
is Resource.Loading -> Resource.Loading()
|
|
||||||
is Resource.Error -> Resource.Error(it.error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
|
||||||
fun <T, U> Flow<Resource<T>>.flatMapResourceData(
|
|
||||||
inheritIntermediate: Boolean = true, block: suspend (T) -> Flow<Resource<U>>
|
|
||||||
) = flatMapLatest {
|
|
||||||
when (it) {
|
|
||||||
is Resource.Success -> block(it.data)
|
|
||||||
is Resource.Intermediate -> block(it.data).map { newRes ->
|
|
||||||
if (inheritIntermediate && newRes is Resource.Success) Resource.Intermediate(newRes.data)
|
|
||||||
else newRes
|
|
||||||
}
|
|
||||||
|
|
||||||
is Resource.Loading -> flowOf(Resource.Loading())
|
|
||||||
is Resource.Error -> flowOf(Resource.Error(it.error))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> Flow<Resource<T>>.onResourceData(block: suspend (T) -> Unit) = onEach {
|
fun <T> Flow<Resource<T>>.onResourceData(block: suspend (T) -> Unit) = onEach {
|
||||||
@ -171,13 +105,13 @@ fun <T> Flow<Resource<T>>.onResourceSuccess(block: suspend (T) -> Unit) = onEach
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> Flow<Resource<T>>.onResourceError(block: suspend (Throwable) -> Unit) = onEach {
|
fun <T> Flow<Resource<T>>.onResourceError(block: (Throwable) -> Unit) = onEach {
|
||||||
if (it is Resource.Error) {
|
if (it is Resource.Error) {
|
||||||
block(it.error)
|
block(it.error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> Flow<Resource<T>>.onResourceNotLoading(block: suspend () -> Unit) = onEach {
|
fun <T> Flow<Resource<T>>.onResourceNotLoading(block: () -> Unit) = onEach {
|
||||||
if (it !is Resource.Loading) {
|
if (it !is Resource.Loading) {
|
||||||
block()
|
block()
|
||||||
}
|
}
|
||||||
@ -187,99 +121,70 @@ suspend fun <T> Flow<Resource<T>>.toFirstResult() = filter { it !is Resource.Loa
|
|||||||
|
|
||||||
suspend fun <T> Flow<Resource<T>>.waitForResult() = takeWhile { it is Resource.Loading }.collect()
|
suspend fun <T> Flow<Resource<T>>.waitForResult() = takeWhile { it is Resource.Loading }.collect()
|
||||||
|
|
||||||
// Can cause excessive amounts of `Resource.Intermediate` to be emitted. Unless that is desired,
|
inline fun <ResultType, RequestType> networkBoundResource(
|
||||||
// use `debounceIntermediates` to alleviate this behavior.
|
|
||||||
inline fun <reified T> combineResourceFlows(flows: Iterable<Flow<Resource<T>>>): Flow<Resource<List<T>>> =
|
|
||||||
combine(flows) { items ->
|
|
||||||
var isIntermediate = false
|
|
||||||
val data = mutableListOf<T>()
|
|
||||||
for (item in items) {
|
|
||||||
when (item) {
|
|
||||||
is Resource.Success -> data.add(item.data)
|
|
||||||
is Resource.Intermediate -> {
|
|
||||||
isIntermediate = true
|
|
||||||
data.add(item.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
is Resource.Loading -> return@combine Resource.Loading()
|
|
||||||
is Resource.Error -> continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (data.isEmpty()) {
|
|
||||||
// All items have to be errors for this to happen, so just return the first one.
|
|
||||||
// mapData is functionally useless and exists only to satisfy the type checker
|
|
||||||
items.first().mapData { listOf(it) }
|
|
||||||
} else if (isIntermediate) {
|
|
||||||
Resource.Intermediate(data)
|
|
||||||
} else {
|
|
||||||
Resource.Success(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(FlowPreview::class)
|
|
||||||
fun <T> Flow<Resource<T>>.debounceIntermediates(timeout: Duration = 5.seconds) = flow {
|
|
||||||
var wasIntermediate = false
|
|
||||||
|
|
||||||
emitAll(this@debounceIntermediates.debounce {
|
|
||||||
if (it is Resource.Intermediate) {
|
|
||||||
if (!wasIntermediate) {
|
|
||||||
wasIntermediate = true
|
|
||||||
Duration.ZERO
|
|
||||||
} else {
|
|
||||||
timeout
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
wasIntermediate = false
|
|
||||||
Duration.ZERO
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline fun <OutputType, ApiType> networkBoundResource(
|
|
||||||
mutex: Mutex = Mutex(),
|
mutex: Mutex = Mutex(),
|
||||||
crossinline isResultEmpty: (OutputType) -> Boolean,
|
showSavedOnLoading: Boolean = true,
|
||||||
crossinline query: () -> Flow<OutputType>,
|
crossinline isResultEmpty: (ResultType) -> Boolean,
|
||||||
crossinline fetch: suspend () -> ApiType,
|
crossinline query: () -> Flow<ResultType>,
|
||||||
crossinline saveFetchResult: suspend (old: OutputType, new: ApiType) -> Unit,
|
crossinline fetch: suspend (ResultType) -> RequestType,
|
||||||
crossinline shouldFetch: (OutputType) -> Boolean = { true },
|
crossinline saveFetchResult: suspend (old: ResultType, new: RequestType) -> Unit,
|
||||||
crossinline filterResult: (OutputType) -> OutputType = { it }
|
crossinline onFetchFailed: (Throwable) -> Unit = { },
|
||||||
) = networkBoundResource(
|
crossinline shouldFetch: (ResultType) -> Boolean = { true },
|
||||||
mutex = mutex,
|
crossinline filterResult: (ResultType) -> ResultType = { it }
|
||||||
isResultEmpty = isResultEmpty,
|
|
||||||
query = query,
|
|
||||||
fetch = fetch,
|
|
||||||
saveFetchResult = saveFetchResult,
|
|
||||||
shouldFetch = shouldFetch,
|
|
||||||
mapResult = filterResult
|
|
||||||
)
|
|
||||||
|
|
||||||
@JvmName("networkBoundResourceWithMap")
|
|
||||||
inline fun <DatabaseType, ApiType, OutputType> networkBoundResource(
|
|
||||||
mutex: Mutex = Mutex(),
|
|
||||||
crossinline isResultEmpty: (OutputType) -> Boolean,
|
|
||||||
crossinline query: () -> Flow<DatabaseType>,
|
|
||||||
crossinline fetch: suspend () -> ApiType,
|
|
||||||
crossinline saveFetchResult: suspend (old: DatabaseType, new: ApiType) -> Unit,
|
|
||||||
crossinline shouldFetch: (DatabaseType) -> Boolean = { true },
|
|
||||||
crossinline mapResult: (DatabaseType) -> OutputType,
|
|
||||||
) = flow {
|
) = flow {
|
||||||
emit(Resource.Loading())
|
emit(Resource.Loading())
|
||||||
|
|
||||||
val data = query().first()
|
val data = query().first()
|
||||||
if (shouldFetch(data)) {
|
emitAll(if (shouldFetch(data)) {
|
||||||
emit(Resource.Intermediate(data))
|
val filteredResult = filterResult(data)
|
||||||
|
|
||||||
|
if (showSavedOnLoading && !isResultEmpty(filteredResult)) {
|
||||||
|
emit(Resource.Intermediate(filteredResult))
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val newData = fetch()
|
val newData = fetch(data)
|
||||||
mutex.withLock { saveFetchResult(query().first(), newData) }
|
mutex.withLock { saveFetchResult(query().first(), newData) }
|
||||||
|
query().map { Resource.Success(filterResult(it)) }
|
||||||
} catch (throwable: Throwable) {
|
} catch (throwable: Throwable) {
|
||||||
emit(Resource.Error(throwable))
|
onFetchFailed(throwable)
|
||||||
return@flow
|
flowOf(Resource.Error(throwable))
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
query().map { Resource.Success(filterResult(it)) }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
emitAll(query().map { Resource.Success(it) })
|
@JvmName("networkBoundResourceWithMap")
|
||||||
|
inline fun <ResultType, RequestType, T> networkBoundResource(
|
||||||
|
mutex: Mutex = Mutex(),
|
||||||
|
showSavedOnLoading: Boolean = true,
|
||||||
|
crossinline isResultEmpty: (T) -> Boolean,
|
||||||
|
crossinline query: () -> Flow<ResultType>,
|
||||||
|
crossinline fetch: suspend (ResultType) -> RequestType,
|
||||||
|
crossinline saveFetchResult: suspend (old: ResultType, new: RequestType) -> Unit,
|
||||||
|
crossinline onFetchFailed: (Throwable) -> Unit = { },
|
||||||
|
crossinline shouldFetch: (ResultType) -> Boolean = { true },
|
||||||
|
crossinline mapResult: (ResultType) -> T,
|
||||||
|
) = flow {
|
||||||
|
emit(Resource.Loading())
|
||||||
|
|
||||||
|
val data = query().first()
|
||||||
|
emitAll(if (shouldFetch(data)) {
|
||||||
|
val mappedResult = mapResult(data)
|
||||||
|
|
||||||
|
if (showSavedOnLoading && !isResultEmpty(mappedResult)) {
|
||||||
|
emit(Resource.Intermediate(mappedResult))
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
val newData = fetch(data)
|
||||||
|
mutex.withLock { saveFetchResult(query().first(), newData) }
|
||||||
|
query().map { Resource.Success(mapResult(it)) }
|
||||||
|
} catch (throwable: Throwable) {
|
||||||
|
onFetchFailed(throwable)
|
||||||
|
flowOf(Resource.Error(throwable))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
query().map { Resource.Success(mapResult(it)) }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
.mapResourceData { mapResult(it) }
|
|
||||||
.filterNot { it is Resource.Intermediate && isResultEmpty(it.data) }
|
|
||||||
|
@ -1,171 +0,0 @@
|
|||||||
package io.github.wulkanowy.data
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.Build
|
|
||||||
import androidx.javascriptengine.JavaScriptSandbox
|
|
||||||
import com.chuckerteam.chucker.api.ChuckerInterceptor
|
|
||||||
import com.google.common.util.concurrent.ListenableFuture
|
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
||||||
import io.github.wulkanowy.data.db.dao.StudentDao
|
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
|
||||||
import io.github.wulkanowy.data.db.entities.StudentIsEduOne
|
|
||||||
import io.github.wulkanowy.data.repositories.WulkanowyRepository
|
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
|
||||||
import io.github.wulkanowy.sdk.scrapper.EvaluateHandler
|
|
||||||
import io.github.wulkanowy.utils.RemoteConfigHelper
|
|
||||||
import io.github.wulkanowy.utils.WebkitCookieManagerProxy
|
|
||||||
import kotlinx.coroutines.guava.await
|
|
||||||
import kotlinx.coroutines.sync.Mutex
|
|
||||||
import kotlinx.coroutines.sync.withLock
|
|
||||||
import timber.log.Timber
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class WulkanowySdkFactory @Inject constructor(
|
|
||||||
@ApplicationContext private val context: Context,
|
|
||||||
private val chuckerInterceptor: ChuckerInterceptor,
|
|
||||||
private val remoteConfig: RemoteConfigHelper,
|
|
||||||
private val webkitCookieManagerProxy: WebkitCookieManagerProxy,
|
|
||||||
private val studentDb: StudentDao,
|
|
||||||
private val wulkanowyRepository: WulkanowyRepository,
|
|
||||||
) {
|
|
||||||
|
|
||||||
private val eduOneMutex = Mutex()
|
|
||||||
private val migrationFailedStudentIds = mutableSetOf<Long>()
|
|
||||||
private val sandbox: ListenableFuture<JavaScriptSandbox>? =
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && JavaScriptSandbox.isSupported())
|
|
||||||
JavaScriptSandbox.createConnectedInstanceAsync(context)
|
|
||||||
else null
|
|
||||||
|
|
||||||
private val sdk = Sdk().apply {
|
|
||||||
androidVersion = Build.VERSION.RELEASE
|
|
||||||
buildTag = Build.MODEL
|
|
||||||
userAgentTemplate = remoteConfig.userAgentTemplate
|
|
||||||
setSimpleHttpLogger { Timber.d(it) }
|
|
||||||
setAdditionalCookieManager(webkitCookieManagerProxy)
|
|
||||||
|
|
||||||
// for debug only
|
|
||||||
addInterceptor(chuckerInterceptor, network = true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createBase() = sdk
|
|
||||||
|
|
||||||
suspend fun create(): Sdk {
|
|
||||||
val mapping = wulkanowyRepository.getMapping()
|
|
||||||
|
|
||||||
return createBase().apply {
|
|
||||||
if (mapping != null) {
|
|
||||||
endpointsMapping = mapping.endpoints
|
|
||||||
vTokenMapping = mapping.vTokens
|
|
||||||
vHeaders = mapping.vHeaders
|
|
||||||
vParamsEvaluation = createIsolate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun createIsolate(): suspend () -> EvaluateHandler {
|
|
||||||
return {
|
|
||||||
val isolate = sandbox?.await()?.createIsolate()
|
|
||||||
object : EvaluateHandler {
|
|
||||||
override suspend fun evaluate(code: String): String? {
|
|
||||||
return isolate?.evaluateJavaScriptAsync(code)?.await()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
isolate?.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun create(student: Student, semester: Semester? = null): Sdk {
|
|
||||||
val overrideIsEduOne = checkEduOneAndMigrateIfNecessary(student)
|
|
||||||
return buildSdk(student, semester, overrideIsEduOne)
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun buildSdk(
|
|
||||||
student: Student,
|
|
||||||
semester: Semester?,
|
|
||||||
isStudentEduOne: Boolean
|
|
||||||
): Sdk {
|
|
||||||
return create().apply {
|
|
||||||
email = student.email
|
|
||||||
password = student.password
|
|
||||||
symbol = student.symbol
|
|
||||||
schoolSymbol = student.schoolSymbol
|
|
||||||
studentId = student.studentId
|
|
||||||
classId = student.classId
|
|
||||||
emptyCookieJarInterceptor = true
|
|
||||||
isEduOne = isStudentEduOne
|
|
||||||
|
|
||||||
if (Sdk.Mode.valueOf(student.loginMode) == Sdk.Mode.HEBE) {
|
|
||||||
mobileBaseUrl = student.mobileBaseUrl
|
|
||||||
} else {
|
|
||||||
scrapperBaseUrl = student.scrapperBaseUrl
|
|
||||||
domainSuffix = student.scrapperDomainSuffix
|
|
||||||
loginType = Sdk.ScrapperLoginType.valueOf(student.loginType)
|
|
||||||
}
|
|
||||||
|
|
||||||
mode = Sdk.Mode.valueOf(student.loginMode)
|
|
||||||
mobileBaseUrl = student.mobileBaseUrl
|
|
||||||
keyId = student.certificateKey
|
|
||||||
privatePem = student.privateKey
|
|
||||||
|
|
||||||
if (semester != null) {
|
|
||||||
diaryId = semester.diaryId
|
|
||||||
kindergartenDiaryId = semester.kindergartenDiaryId
|
|
||||||
schoolYear = semester.schoolYear
|
|
||||||
unitId = semester.unitId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun checkEduOneAndMigrateIfNecessary(student: Student): Boolean {
|
|
||||||
if (student.isEduOne != null) return student.isEduOne
|
|
||||||
|
|
||||||
if (student.id in migrationFailedStudentIds) {
|
|
||||||
Timber.i("Migration eduOne: skipping because of previous failure")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
eduOneMutex.withLock {
|
|
||||||
if (student.id in migrationFailedStudentIds) {
|
|
||||||
Timber.i("Migration eduOne: skipping because of previous failure")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
val studentFromDatabase = studentDb.loadById(student.id)
|
|
||||||
if (studentFromDatabase?.isEduOne != null) {
|
|
||||||
Timber.i("Migration eduOne: already done")
|
|
||||||
return studentFromDatabase.isEduOne
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.i("Migration eduOne: flag missing. Running migration...")
|
|
||||||
val initializedSdk = buildSdk(
|
|
||||||
student = student,
|
|
||||||
semester = null,
|
|
||||||
isStudentEduOne = false, // doesn't matter
|
|
||||||
)
|
|
||||||
val newCurrentStudent = runCatching { initializedSdk.getCurrentStudent() }
|
|
||||||
.onFailure { Timber.e(it, "Migration eduOne: can't get current student") }
|
|
||||||
.getOrNull()
|
|
||||||
|
|
||||||
if (newCurrentStudent == null) {
|
|
||||||
Timber.i("Migration eduOne: failed, so skipping")
|
|
||||||
migrationFailedStudentIds.add(student.id)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.i("Migration eduOne: success. New isEduOne flag: ${newCurrentStudent.isEduOne}")
|
|
||||||
|
|
||||||
val studentIsEduOne = StudentIsEduOne(
|
|
||||||
id = student.id,
|
|
||||||
isEduOne = newCurrentStudent.isEduOne
|
|
||||||
)
|
|
||||||
studentDb.update(studentIsEduOne)
|
|
||||||
return newCurrentStudent.isEduOne
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +1,12 @@
|
|||||||
package io.github.wulkanowy.data.api.services
|
package io.github.wulkanowy.data.api
|
||||||
|
|
||||||
import io.github.wulkanowy.data.api.models.Mapping
|
|
||||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
interface WulkanowyService {
|
interface AdminMessageService {
|
||||||
|
|
||||||
@GET("/v1.json")
|
@GET("/v1.json")
|
||||||
suspend fun getAdminMessages(): List<AdminMessage>
|
suspend fun getAdminMessages(): List<AdminMessage>
|
||||||
|
|
||||||
@GET("/mapping2.json")
|
|
||||||
suspend fun getMapping(): Mapping
|
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package io.github.wulkanowy.data.api.services
|
package io.github.wulkanowy.data.api
|
||||||
|
|
||||||
import io.github.wulkanowy.data.pojos.IntegrityRequest
|
import io.github.wulkanowy.data.pojos.IntegrityRequest
|
||||||
import io.github.wulkanowy.data.pojos.LoginEvent
|
import io.github.wulkanowy.data.pojos.LoginEvent
|
@ -1,20 +0,0 @@
|
|||||||
package io.github.wulkanowy.data.api.models
|
|
||||||
|
|
||||||
import kotlinx.serialization.SerialName
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class Mapping(
|
|
||||||
|
|
||||||
@SerialName("endpoints")
|
|
||||||
val endpoints: Map<String, Map<String, Map<String, String>>>,
|
|
||||||
|
|
||||||
@SerialName("vTokens")
|
|
||||||
val vTokens: Map<String, Map<String, Map<String, String>>>,
|
|
||||||
|
|
||||||
@SerialName("vTokenScheme")
|
|
||||||
val vTokenScheme: Map<String, Map<String, String>> = emptyMap(),
|
|
||||||
|
|
||||||
@SerialName("vHeaders")
|
|
||||||
val vHeaders: Map<String, Map<String, Map<String, String>>> = emptyMap(),
|
|
||||||
)
|
|
@ -120,7 +120,6 @@ import io.github.wulkanowy.data.db.migrations.Migration55
|
|||||||
import io.github.wulkanowy.data.db.migrations.Migration57
|
import io.github.wulkanowy.data.db.migrations.Migration57
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration58
|
import io.github.wulkanowy.data.db.migrations.Migration58
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration6
|
import io.github.wulkanowy.data.db.migrations.Migration6
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration63
|
|
||||||
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
|
||||||
@ -174,10 +173,6 @@ import javax.inject.Singleton
|
|||||||
AutoMigration(from = 57, to = 58, spec = Migration58::class),
|
AutoMigration(from = 57, to = 58, spec = Migration58::class),
|
||||||
AutoMigration(from = 58, to = 59),
|
AutoMigration(from = 58, to = 59),
|
||||||
AutoMigration(from = 59, to = 60),
|
AutoMigration(from = 59, to = 60),
|
||||||
AutoMigration(from = 60, to = 61),
|
|
||||||
AutoMigration(from = 61, to = 62),
|
|
||||||
AutoMigration(from = 62, to = 63, spec = Migration63::class),
|
|
||||||
AutoMigration(from = 63, to = 64),
|
|
||||||
],
|
],
|
||||||
version = AppDatabase.VERSION_SCHEMA,
|
version = AppDatabase.VERSION_SCHEMA,
|
||||||
exportSchema = true
|
exportSchema = true
|
||||||
@ -186,7 +181,7 @@ import javax.inject.Singleton
|
|||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val VERSION_SCHEMA = 64
|
const val VERSION_SCHEMA = 60
|
||||||
|
|
||||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
||||||
Migration2(),
|
Migration2(),
|
||||||
|
@ -2,14 +2,24 @@ package io.github.wulkanowy.data.db.dao
|
|||||||
|
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
|
import androidx.room.Transaction
|
||||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Dao
|
@Dao
|
||||||
interface AdminMessageDao : BaseDao<AdminMessage> {
|
abstract class AdminMessageDao : BaseDao<AdminMessage> {
|
||||||
|
|
||||||
@Query("SELECT * FROM AdminMessages")
|
@Query("SELECT * FROM AdminMessages")
|
||||||
fun loadAll(): Flow<List<AdminMessage>>
|
abstract fun loadAll(): Flow<List<AdminMessage>>
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
open suspend fun removeOldAndSaveNew(
|
||||||
|
oldMessages: List<AdminMessage>,
|
||||||
|
newMessages: List<AdminMessage>
|
||||||
|
) {
|
||||||
|
deleteAll(oldMessages)
|
||||||
|
insertAll(newMessages)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package io.github.wulkanowy.data.db.dao
|
|||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import io.github.wulkanowy.data.db.entities.Attendance
|
import io.github.wulkanowy.data.db.entities.Attendance
|
||||||
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
@ -3,7 +3,6 @@ package io.github.wulkanowy.data.db.dao
|
|||||||
import androidx.room.Delete
|
import androidx.room.Delete
|
||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
import androidx.room.OnConflictStrategy
|
import androidx.room.OnConflictStrategy
|
||||||
import androidx.room.Transaction
|
|
||||||
import androidx.room.Update
|
import androidx.room.Update
|
||||||
|
|
||||||
interface BaseDao<T> {
|
interface BaseDao<T> {
|
||||||
@ -16,10 +15,4 @@ interface BaseDao<T> {
|
|||||||
|
|
||||||
@Delete
|
@Delete
|
||||||
suspend fun deleteAll(items: List<T>)
|
suspend fun deleteAll(items: List<T>)
|
||||||
|
|
||||||
@Transaction
|
|
||||||
suspend fun removeOldAndSaveNew(oldItems: List<T>, newItems: List<T>) {
|
|
||||||
deleteAll(oldItems)
|
|
||||||
insertAll(newItems)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,4 @@ interface GradeDao : BaseDao<Grade> {
|
|||||||
|
|
||||||
@Query("SELECT * FROM Grades WHERE semester_id = :semesterId AND student_id = :studentId")
|
@Query("SELECT * FROM Grades WHERE semester_id = :semesterId AND student_id = :studentId")
|
||||||
fun loadAll(semesterId: Int, studentId: Int): Flow<List<Grade>>
|
fun loadAll(semesterId: Int, studentId: Int): Flow<List<Grade>>
|
||||||
|
|
||||||
@Query("SELECT * FROM Grades WHERE semester_id = :semesterId AND student_id = :studentId " +
|
|
||||||
"AND entry NOT IN(:censoredEntries)")
|
|
||||||
fun loadAllCensored(semesterId: Int, studentId: Int, censoredEntries: Array<String>): Flow<List<Grade>>
|
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,6 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
@Dao
|
@Dao
|
||||||
interface MobileDeviceDao : BaseDao<MobileDevice> {
|
interface MobileDeviceDao : BaseDao<MobileDevice> {
|
||||||
|
|
||||||
@Query("SELECT * FROM MobileDevices WHERE user_login_id = :studentId ORDER BY date DESC")
|
@Query("SELECT * FROM MobileDevices WHERE user_login_id = :userLoginId ORDER BY date DESC")
|
||||||
fun loadAll(studentId: Int): Flow<List<MobileDevice>>
|
fun loadAll(userLoginId: Int): Flow<List<MobileDevice>>
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
@Dao
|
@Dao
|
||||||
interface NoteDao : BaseDao<Note> {
|
interface NoteDao : BaseDao<Note> {
|
||||||
|
|
||||||
@Query("SELECT * FROM Notes WHERE student_id = :studentId")
|
@Query("SELECT * FROM Notes WHERE student_id = :studentId")
|
||||||
fun loadAll(studentId: Int): Flow<List<Note>>
|
fun loadAll(studentId: Int): Flow<List<Note>>
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,6 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
interface SchoolAnnouncementDao : BaseDao<SchoolAnnouncement> {
|
interface SchoolAnnouncementDao : BaseDao<SchoolAnnouncement> {
|
||||||
|
|
||||||
@Query("SELECT * FROM SchoolAnnouncements WHERE user_login_id = :studentId ORDER BY date DESC")
|
@Query("SELECT * FROM SchoolAnnouncements WHERE user_login_id = :userLoginId ORDER BY date DESC")
|
||||||
fun loadAll(studentId: Int): Flow<List<SchoolAnnouncement>>
|
fun loadAll(userLoginId: Int): Flow<List<SchoolAnnouncement>>
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,6 @@ interface SemesterDao : BaseDao<Semester> {
|
|||||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
suspend fun insertSemesters(items: List<Semester>): List<Long>
|
suspend fun insertSemesters(items: List<Semester>): List<Long>
|
||||||
|
|
||||||
@Query("SELECT * FROM Semesters WHERE (student_id = :studentId AND class_id = :classId) OR (student_id = :studentId AND class_id = 0)")
|
@Query("SELECT * FROM Semesters WHERE student_id = :studentId AND class_id = :classId")
|
||||||
suspend fun loadAll(studentId: Int, classId: Int): List<Semester>
|
suspend fun loadAll(studentId: Int, classId: Int): List<Semester>
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,6 @@ import androidx.room.Transaction
|
|||||||
import androidx.room.Update
|
import androidx.room.Update
|
||||||
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.StudentIsAuthorized
|
|
||||||
import io.github.wulkanowy.data.db.entities.StudentIsEduOne
|
|
||||||
import io.github.wulkanowy.data.db.entities.StudentName
|
import io.github.wulkanowy.data.db.entities.StudentName
|
||||||
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -25,12 +23,6 @@ abstract class StudentDao {
|
|||||||
@Delete
|
@Delete
|
||||||
abstract suspend fun delete(student: Student)
|
abstract suspend fun delete(student: Student)
|
||||||
|
|
||||||
@Update(entity = Student::class)
|
|
||||||
abstract suspend fun update(studentIsAuthorized: StudentIsAuthorized)
|
|
||||||
|
|
||||||
@Update(entity = Student::class)
|
|
||||||
abstract suspend fun update(studentIsEduOne: StudentIsEduOne)
|
|
||||||
|
|
||||||
@Update(entity = Student::class)
|
@Update(entity = Student::class)
|
||||||
abstract suspend fun update(studentNickAndAvatar: StudentNickAndAvatar)
|
abstract suspend fun update(studentNickAndAvatar: StudentNickAndAvatar)
|
||||||
|
|
||||||
@ -47,11 +39,11 @@ abstract class StudentDao {
|
|||||||
abstract suspend fun loadAll(): List<Student>
|
abstract suspend fun loadAll(): List<Student>
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT * FROM Students JOIN Semesters ON (Students.student_id = Semesters.student_id AND Students.class_id = Semesters.class_id) OR (Students.student_id = Semesters.student_id AND Semesters.class_id = 0)")
|
@Query("SELECT * FROM Students JOIN Semesters ON Students.student_id = Semesters.student_id AND Students.class_id = Semesters.class_id")
|
||||||
abstract suspend fun loadStudentsWithSemesters(): Map<Student, List<Semester>>
|
abstract suspend fun loadStudentsWithSemesters(): Map<Student, List<Semester>>
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT * FROM Students JOIN Semesters ON (Students.student_id = Semesters.student_id AND Students.class_id = Semesters.class_id) OR (Students.student_id = Semesters.student_id AND Semesters.class_id = 0) WHERE Students.id = :id")
|
@Query("SELECT * FROM Students JOIN Semesters ON Students.student_id = Semesters.student_id AND Students.class_id = Semesters.class_id WHERE Students.id = :id")
|
||||||
abstract suspend fun loadStudentWithSemestersById(id: Long): Map<Student, List<Semester>>
|
abstract suspend fun loadStudentWithSemestersById(id: Long): Map<Student, List<Semester>>
|
||||||
|
|
||||||
@Query("UPDATE Students SET is_current = 1 WHERE id = :id")
|
@Query("UPDATE Students SET is_current = 1 WHERE id = :id")
|
||||||
|
@ -4,8 +4,6 @@ import androidx.room.ColumnInfo
|
|||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import io.github.wulkanowy.data.enums.MessageType
|
import io.github.wulkanowy.data.enums.MessageType
|
||||||
import io.github.wulkanowy.data.serializers.SafeMessageTypeEnumListSerializer
|
|
||||||
import kotlinx.serialization.SerialName
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -36,8 +34,6 @@ data class AdminMessage(
|
|||||||
|
|
||||||
val priority: String,
|
val priority: String,
|
||||||
|
|
||||||
@SerialName("messageTypes")
|
|
||||||
@Serializable(with = SafeMessageTypeEnumListSerializer::class)
|
|
||||||
@ColumnInfo(name = "types", defaultValue = "[]")
|
@ColumnInfo(name = "types", defaultValue = "[]")
|
||||||
val types: List<MessageType> = emptyList(),
|
val types: List<MessageType> = emptyList(),
|
||||||
|
|
||||||
|
@ -33,13 +33,7 @@ data class GradeSummary(
|
|||||||
@ColumnInfo(name = "points_sum")
|
@ColumnInfo(name = "points_sum")
|
||||||
val pointsSum: String,
|
val pointsSum: String,
|
||||||
|
|
||||||
@ColumnInfo(name = "points_sum_all_year")
|
val average: Double
|
||||||
val pointsSumAllYear: String?,
|
|
||||||
|
|
||||||
val average: Double,
|
|
||||||
|
|
||||||
@ColumnInfo(name = "average_all_year")
|
|
||||||
val averageAllYear: Double? = null,
|
|
||||||
) {
|
) {
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
var id: Long = 0
|
var id: Long = 0
|
||||||
|
@ -9,8 +9,8 @@ import java.time.Instant
|
|||||||
@Entity(tableName = "MobileDevices")
|
@Entity(tableName = "MobileDevices")
|
||||||
data class MobileDevice(
|
data class MobileDevice(
|
||||||
|
|
||||||
@ColumnInfo(name = "user_login_id") // todo: change column name
|
@ColumnInfo(name = "user_login_id")
|
||||||
val studentId: Int,
|
val userLoginId: Int,
|
||||||
|
|
||||||
@ColumnInfo(name = "device_id")
|
@ColumnInfo(name = "device_id")
|
||||||
val deviceId: Int,
|
val deviceId: Int,
|
||||||
|
@ -9,16 +9,14 @@ import java.time.LocalDate
|
|||||||
@Entity(tableName = "SchoolAnnouncements")
|
@Entity(tableName = "SchoolAnnouncements")
|
||||||
data class SchoolAnnouncement(
|
data class SchoolAnnouncement(
|
||||||
|
|
||||||
@ColumnInfo(name = "user_login_id") // todo: change column name
|
@ColumnInfo(name = "user_login_id")
|
||||||
val studentId: Int,
|
val userLoginId: Int,
|
||||||
|
|
||||||
val date: LocalDate,
|
val date: LocalDate,
|
||||||
|
|
||||||
val subject: String,
|
val subject: String,
|
||||||
|
|
||||||
val content: String,
|
val content: String
|
||||||
|
|
||||||
val author: String? = null,
|
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
@ -49,7 +49,6 @@ data class Student(
|
|||||||
@ColumnInfo(name = "student_id")
|
@ColumnInfo(name = "student_id")
|
||||||
val studentId: Int,
|
val studentId: Int,
|
||||||
|
|
||||||
@Deprecated("not available in VULCAN anymore")
|
|
||||||
@ColumnInfo(name = "user_login_id")
|
@ColumnInfo(name = "user_login_id")
|
||||||
val userLoginId: Int,
|
val userLoginId: Int,
|
||||||
|
|
||||||
@ -79,13 +78,6 @@ data class Student(
|
|||||||
|
|
||||||
@ColumnInfo(name = "registration_date")
|
@ColumnInfo(name = "registration_date")
|
||||||
val registrationDate: Instant,
|
val registrationDate: Instant,
|
||||||
|
|
||||||
@ColumnInfo(name = "is_authorized", defaultValue = "0")
|
|
||||||
val isAuthorized: Boolean,
|
|
||||||
|
|
||||||
@ColumnInfo(name = "is_edu_one", defaultValue = "NULL")
|
|
||||||
val isEduOne: Boolean?,
|
|
||||||
|
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
@ -96,22 +88,3 @@ data class Student(
|
|||||||
@ColumnInfo(name = "avatar_color")
|
@ColumnInfo(name = "avatar_color")
|
||||||
var avatarColor = 0L
|
var avatarColor = 0L
|
||||||
}
|
}
|
||||||
|
|
||||||
@Entity
|
|
||||||
data class StudentIsAuthorized(
|
|
||||||
|
|
||||||
@PrimaryKey
|
|
||||||
var id: Long,
|
|
||||||
|
|
||||||
@ColumnInfo(name = "is_authorized", defaultValue = "NULL")
|
|
||||||
val isAuthorized: Boolean?,
|
|
||||||
) : Serializable
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
data class StudentIsEduOne(
|
|
||||||
@PrimaryKey
|
|
||||||
var id: Long,
|
|
||||||
|
|
||||||
@ColumnInfo(name = "is_edu_one", defaultValue = "NULL")
|
|
||||||
val isEduOne: Boolean?,
|
|
||||||
) : Serializable
|
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
package io.github.wulkanowy.data.db.migrations
|
|
||||||
|
|
||||||
import androidx.room.migration.AutoMigrationSpec
|
|
||||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
|
||||||
|
|
||||||
class Migration63 : AutoMigrationSpec {
|
|
||||||
|
|
||||||
override fun onPostMigrate(db: SupportSQLiteDatabase) {
|
|
||||||
db.execSQL("UPDATE Students SET is_edu_one = NULL WHERE is_edu_one = 0")
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,6 +7,6 @@ enum class AppTheme(val value: String) {
|
|||||||
BLACK("black");
|
BLACK("black");
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun getByValue(value: String) = entries.find { it.value == value } ?: LIGHT
|
fun getByValue(value: String) = values().find { it.value == value } ?: LIGHT
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,13 +0,0 @@
|
|||||||
package io.github.wulkanowy.data.enums
|
|
||||||
|
|
||||||
enum class AttendanceCalculatorSortingMode(private val value: String) {
|
|
||||||
ALPHABETIC("alphabetic"),
|
|
||||||
ATTENDANCE("attendance_percentage"),
|
|
||||||
LESSON_BALANCE("lesson_balance");
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun getByValue(value: String) =
|
|
||||||
AttendanceCalculatorSortingMode.values()
|
|
||||||
.find { it.value == value } ?: ALPHABETIC
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,6 +8,6 @@ enum class GradeColorTheme(val value: String) : Serializable {
|
|||||||
GRADE_COLOR("grade_color");
|
GRADE_COLOR("grade_color");
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun getByValue(value: String) = entries.find { it.value == value } ?: VULCAN
|
fun getByValue(value: String) = values().find { it.value == value } ?: VULCAN
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,6 +6,6 @@ enum class GradeExpandMode(val value: String) {
|
|||||||
ALWAYS_EXPANDED("always");
|
ALWAYS_EXPANDED("always");
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun getByValue(value: String) = entries.find { it.value == value } ?: ONE
|
fun getByValue(value: String) = values().find { it.value == value } ?: ONE
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,6 +6,6 @@ enum class GradeSortingMode(val value: String) {
|
|||||||
AVERAGE("average");
|
AVERAGE("average");
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun getByValue(value: String) = entries.find { it.value == value } ?: ALPHABETIC
|
fun getByValue(value: String) = values().find { it.value == value } ?: ALPHABETIC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,6 @@ enum class MessageType {
|
|||||||
GENERAL_MESSAGE,
|
GENERAL_MESSAGE,
|
||||||
DASHBOARD_MESSAGE,
|
DASHBOARD_MESSAGE,
|
||||||
LOGIN_MESSAGE,
|
LOGIN_MESSAGE,
|
||||||
LOGIN_STUDENT_SELECT_MESSAGE,
|
|
||||||
LOGIN_SYMBOL_MESSAGE,
|
|
||||||
PASS_RESET_MESSAGE,
|
PASS_RESET_MESSAGE,
|
||||||
ERROR_OVERRIDE,
|
ERROR_OVERRIDE,
|
||||||
}
|
}
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
package io.github.wulkanowy.data.enums
|
|
||||||
|
|
||||||
enum class ShowAdditionalLessonsMode(val value: String) {
|
|
||||||
NONE("none"),
|
|
||||||
INLINE("inline"),
|
|
||||||
BELOW("below");
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun getByValue(value: String) = entries.find { it.value == value } ?: INLINE
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,6 +6,6 @@ enum class TimetableMode(val value: String) {
|
|||||||
SMALL_OTHER_GROUP("small");
|
SMALL_OTHER_GROUP("small");
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun getByValue(value: String) = entries.find { it.value == value } ?: ONLY_CURRENT_GROUP
|
fun getByValue(value: String) = values().find { it.value == value } ?: ONLY_CURRENT_GROUP
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,26 +3,12 @@ package io.github.wulkanowy.data.mappers
|
|||||||
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.sdk.pojo.DirectorInformation as SdkDirectorInformation
|
import io.github.wulkanowy.sdk.pojo.DirectorInformation as SdkDirectorInformation
|
||||||
import io.github.wulkanowy.sdk.pojo.LastAnnouncement as SdkLastAnnouncement
|
|
||||||
|
|
||||||
@JvmName("mapDirectorInformationToEntities")
|
|
||||||
fun List<SdkDirectorInformation>.mapToEntities(student: Student) = map {
|
fun List<SdkDirectorInformation>.mapToEntities(student: Student) = map {
|
||||||
SchoolAnnouncement(
|
SchoolAnnouncement(
|
||||||
studentId = student.studentId,
|
userLoginId = student.userLoginId,
|
||||||
date = it.date,
|
date = it.date,
|
||||||
subject = it.subject,
|
subject = it.subject,
|
||||||
content = it.content,
|
content = it.content,
|
||||||
author = null,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmName("mapLastAnnouncementsToEntities")
|
|
||||||
fun List<SdkLastAnnouncement>.mapToEntities(student: Student) = map {
|
|
||||||
SchoolAnnouncement(
|
|
||||||
studentId = student.studentId,
|
|
||||||
date = it.date,
|
|
||||||
subject = it.subject,
|
|
||||||
content = it.content,
|
|
||||||
author = it.author,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -37,11 +37,9 @@ fun List<SdkGradeSummary>.mapToEntities(semester: Semester) = map {
|
|||||||
predictedGrade = it.predicted,
|
predictedGrade = it.predicted,
|
||||||
finalGrade = it.final,
|
finalGrade = it.final,
|
||||||
pointsSum = it.pointsSum,
|
pointsSum = it.pointsSum,
|
||||||
pointsSumAllYear = it.pointsSumAllYear,
|
|
||||||
proposedPoints = it.proposedPoints,
|
proposedPoints = it.proposedPoints,
|
||||||
finalPoints = it.finalPoints,
|
finalPoints = it.finalPoints,
|
||||||
average = it.average,
|
average = it.average
|
||||||
averageAllYear = it.averageAllYear,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import io.github.wulkanowy.sdk.pojo.Token as SdkToken
|
|||||||
|
|
||||||
fun List<SdkDevice>.mapToEntities(student: Student) = map {
|
fun List<SdkDevice>.mapToEntities(student: Student) = map {
|
||||||
MobileDevice(
|
MobileDevice(
|
||||||
studentId = student.studentId,
|
userLoginId = student.userLoginId,
|
||||||
date = it.createDate.toInstant(),
|
date = it.createDate.toInstant(),
|
||||||
deviceId = it.id,
|
deviceId = it.id,
|
||||||
name = it.name
|
name = it.name
|
||||||
|
@ -34,19 +34,17 @@ fun SdkRegisterUser.mapToPojo(password: String?) = RegisterUser(
|
|||||||
error = it.error,
|
error = it.error,
|
||||||
students = it.subjects
|
students = it.subjects
|
||||||
.filterIsInstance<SdkRegisterStudent>()
|
.filterIsInstance<SdkRegisterStudent>()
|
||||||
.map { registerStudent ->
|
.map { registerSubject ->
|
||||||
RegisterStudent(
|
RegisterStudent(
|
||||||
studentId = registerStudent.studentId,
|
studentId = registerSubject.studentId,
|
||||||
studentName = registerStudent.studentName,
|
studentName = registerSubject.studentName,
|
||||||
studentSecondName = registerStudent.studentSecondName,
|
studentSecondName = registerSubject.studentSecondName,
|
||||||
studentSurname = registerStudent.studentSurname,
|
studentSurname = registerSubject.studentSurname,
|
||||||
className = registerStudent.className,
|
className = registerSubject.className,
|
||||||
classId = registerStudent.classId,
|
classId = registerSubject.classId,
|
||||||
isParent = registerStudent.isParent,
|
isParent = registerSubject.isParent,
|
||||||
isAuthorized = registerStudent.isAuthorized,
|
semesters = registerSubject.semesters
|
||||||
isEduOne = registerStudent.isEduOne,
|
.mapToEntities(registerSubject.studentId),
|
||||||
semesters = registerStudent.semesters
|
|
||||||
.mapToEntities(registerStudent.studentId),
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -86,8 +84,6 @@ fun RegisterStudent.mapToStudentWithSemesters(
|
|||||||
password = user.password.orEmpty(),
|
password = user.password.orEmpty(),
|
||||||
isCurrent = false,
|
isCurrent = false,
|
||||||
registrationDate = Instant.now(),
|
registrationDate = Instant.now(),
|
||||||
isAuthorized = this.isAuthorized,
|
|
||||||
isEduOne = this.isEduOne,
|
|
||||||
).apply {
|
).apply {
|
||||||
avatarColor = colors.random()
|
avatarColor = colors.random()
|
||||||
},
|
},
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
package io.github.wulkanowy.data.pojos
|
|
||||||
|
|
||||||
data class AttendanceData(
|
|
||||||
val subjectName: String,
|
|
||||||
val lessonBalance: Int,
|
|
||||||
val presences: Int,
|
|
||||||
val absences: Int,
|
|
||||||
) {
|
|
||||||
val total: Int
|
|
||||||
get() = presences + absences
|
|
||||||
|
|
||||||
val presencePercentage: Double
|
|
||||||
get() = if (total == 0) 0.0 else (presences.toDouble() / total) * 100
|
|
||||||
}
|
|
@ -45,6 +45,4 @@ data class RegisterStudent(
|
|||||||
val classId: Int,
|
val classId: Int,
|
||||||
val isParent: Boolean,
|
val isParent: Boolean,
|
||||||
val semesters: List<Semester>,
|
val semesters: List<Semester>,
|
||||||
val isAuthorized: Boolean,
|
|
||||||
val isEduOne: Boolean
|
|
||||||
) : java.io.Serializable
|
) : java.io.Serializable
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.Resource
|
||||||
|
import io.github.wulkanowy.data.api.AdminMessageService
|
||||||
|
import io.github.wulkanowy.data.db.dao.AdminMessageDao
|
||||||
|
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||||
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class AdminMessageRepository @Inject constructor(
|
||||||
|
private val adminMessageService: AdminMessageService,
|
||||||
|
private val adminMessageDao: AdminMessageDao,
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val saveFetchResultMutex = Mutex()
|
||||||
|
|
||||||
|
fun getAdminMessages(): Flow<Resource<List<AdminMessage>>> =
|
||||||
|
networkBoundResource(
|
||||||
|
mutex = saveFetchResultMutex,
|
||||||
|
isResultEmpty = { false },
|
||||||
|
query = { adminMessageDao.loadAll() },
|
||||||
|
fetch = { adminMessageService.getAdminMessages() },
|
||||||
|
shouldFetch = { true },
|
||||||
|
saveFetchResult = { oldItems, newItems ->
|
||||||
|
adminMessageDao.removeOldAndSaveNew(oldItems, newItems)
|
||||||
|
},
|
||||||
|
showSavedOnLoading = false,
|
||||||
|
)
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
|
||||||
import io.github.wulkanowy.data.db.dao.AttendanceDao
|
import io.github.wulkanowy.data.db.dao.AttendanceDao
|
||||||
import io.github.wulkanowy.data.db.dao.TimetableDao
|
import io.github.wulkanowy.data.db.dao.TimetableDao
|
||||||
import io.github.wulkanowy.data.db.entities.Attendance
|
import io.github.wulkanowy.data.db.entities.Attendance
|
||||||
@ -8,16 +7,16 @@ 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.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.sdk.pojo.Absent
|
import io.github.wulkanowy.sdk.pojo.Absent
|
||||||
import io.github.wulkanowy.sdk.pojo.Attendance as SdkAttendance
|
|
||||||
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
|
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
|
import io.github.wulkanowy.utils.init
|
||||||
import io.github.wulkanowy.utils.monday
|
import io.github.wulkanowy.utils.monday
|
||||||
import io.github.wulkanowy.utils.sunday
|
import io.github.wulkanowy.utils.sunday
|
||||||
|
import io.github.wulkanowy.utils.switchSemester
|
||||||
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.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
@ -29,73 +28,14 @@ import javax.inject.Singleton
|
|||||||
class AttendanceRepository @Inject constructor(
|
class AttendanceRepository @Inject constructor(
|
||||||
private val attendanceDb: AttendanceDao,
|
private val attendanceDb: AttendanceDao,
|
||||||
private val timetableDb: TimetableDao,
|
private val timetableDb: TimetableDao,
|
||||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
private val sdk: Sdk,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
private val preferencesRepository: PreferencesRepository
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val saveFetchResultMutex = Mutex()
|
private val saveFetchResultMutex = Mutex()
|
||||||
|
|
||||||
private val cacheKey = "attendance"
|
private val cacheKey = "attendance"
|
||||||
|
|
||||||
private fun filterAttendance(
|
|
||||||
hiddenAttendanceTiles: List<DashboardItem.HiddenAttendanceTile>,
|
|
||||||
attendanceItem: Attendance
|
|
||||||
): Boolean {
|
|
||||||
return when {
|
|
||||||
attendanceItem.absence && attendanceItem.excused && hiddenAttendanceTiles.contains(
|
|
||||||
DashboardItem.HiddenAttendanceTile.EXCUSED_ABSENCE
|
|
||||||
) -> false
|
|
||||||
|
|
||||||
attendanceItem.absence && !attendanceItem.excused && hiddenAttendanceTiles.contains(
|
|
||||||
DashboardItem.HiddenAttendanceTile.UNEXCUSED_ABSENCE
|
|
||||||
) -> false
|
|
||||||
|
|
||||||
attendanceItem.lateness && attendanceItem.excused && hiddenAttendanceTiles.contains(
|
|
||||||
DashboardItem.HiddenAttendanceTile.EXCUSED_LATENESS
|
|
||||||
) -> false
|
|
||||||
|
|
||||||
attendanceItem.lateness && !attendanceItem.excused && hiddenAttendanceTiles.contains(
|
|
||||||
DashboardItem.HiddenAttendanceTile.UNEXCUSED_LATENESS
|
|
||||||
) -> false
|
|
||||||
|
|
||||||
attendanceItem.exemption && hiddenAttendanceTiles.contains(DashboardItem.HiddenAttendanceTile.EXEMPTION) -> false
|
|
||||||
attendanceItem.deleted && hiddenAttendanceTiles.contains(DashboardItem.HiddenAttendanceTile.DELETED) -> false
|
|
||||||
attendanceItem.presence && hiddenAttendanceTiles.contains(DashboardItem.HiddenAttendanceTile.PRESENT) -> false
|
|
||||||
|
|
||||||
else -> !hiddenAttendanceTiles.contains(DashboardItem.HiddenAttendanceTile.UNKNOWN)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun filterAttendance(
|
|
||||||
hiddenAttendanceTiles: List<DashboardItem.HiddenAttendanceTile>,
|
|
||||||
attendanceItem: SdkAttendance
|
|
||||||
): Boolean {
|
|
||||||
return when {
|
|
||||||
attendanceItem.absence && attendanceItem.excused && hiddenAttendanceTiles.contains(
|
|
||||||
DashboardItem.HiddenAttendanceTile.EXCUSED_ABSENCE
|
|
||||||
) -> false
|
|
||||||
|
|
||||||
attendanceItem.absence && !attendanceItem.excused && hiddenAttendanceTiles.contains(
|
|
||||||
DashboardItem.HiddenAttendanceTile.UNEXCUSED_ABSENCE
|
|
||||||
) -> false
|
|
||||||
|
|
||||||
attendanceItem.lateness && attendanceItem.excused && hiddenAttendanceTiles.contains(
|
|
||||||
DashboardItem.HiddenAttendanceTile.EXCUSED_LATENESS
|
|
||||||
) -> false
|
|
||||||
|
|
||||||
attendanceItem.lateness && !attendanceItem.excused && hiddenAttendanceTiles.contains(
|
|
||||||
DashboardItem.HiddenAttendanceTile.UNEXCUSED_LATENESS
|
|
||||||
) -> false
|
|
||||||
|
|
||||||
attendanceItem.exemption && hiddenAttendanceTiles.contains(DashboardItem.HiddenAttendanceTile.EXEMPTION) -> false
|
|
||||||
attendanceItem.deleted && hiddenAttendanceTiles.contains(DashboardItem.HiddenAttendanceTile.DELETED) -> false
|
|
||||||
attendanceItem.presence && hiddenAttendanceTiles.contains(DashboardItem.HiddenAttendanceTile.PRESENT) -> false
|
|
||||||
|
|
||||||
else -> !hiddenAttendanceTiles.contains(DashboardItem.HiddenAttendanceTile.UNKNOWN)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getAttendance(
|
fun getAttendance(
|
||||||
student: Student,
|
student: Student,
|
||||||
semester: Semester,
|
semester: Semester,
|
||||||
@ -113,34 +53,24 @@ class AttendanceRepository @Inject constructor(
|
|||||||
it.isEmpty() || forceRefresh || isExpired
|
it.isEmpty() || forceRefresh || isExpired
|
||||||
},
|
},
|
||||||
query = {
|
query = {
|
||||||
val hiddenAttendanceItems = preferencesRepository.hiddenAttendanceItems
|
attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)
|
||||||
|
|
||||||
attendanceDb
|
|
||||||
.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)
|
|
||||||
.map {
|
|
||||||
it.filter { item -> filterAttendance(hiddenAttendanceItems, item) }
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
val hiddenAttendanceItems = preferencesRepository.hiddenAttendanceItems
|
|
||||||
|
|
||||||
val lessons = timetableDb.load(
|
val lessons = timetableDb.load(
|
||||||
semester.diaryId, semester.studentId, start.monday, end.sunday
|
semester.diaryId, semester.studentId, start.monday, end.sunday
|
||||||
)
|
)
|
||||||
|
sdk.init(student)
|
||||||
wulkanowySdkFactory.create(student, semester)
|
.switchSemester(semester)
|
||||||
.getAttendance(start.monday, end.sunday)
|
.getAttendance(start.monday, end.sunday)
|
||||||
.filter { item -> filterAttendance(hiddenAttendanceItems, item) }
|
|
||||||
.mapToEntities(semester, lessons)
|
.mapToEntities(semester, lessons)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
|
attendanceDb.deleteAll(old uniqueSubtract new)
|
||||||
val attendanceToAdd = (new uniqueSubtract old).map { newAttendance ->
|
val attendanceToAdd = (new uniqueSubtract old).map { newAttendance ->
|
||||||
newAttendance.apply { if (notify) isNotified = false }
|
newAttendance.apply { if (notify) isNotified = false }
|
||||||
}
|
}
|
||||||
attendanceDb.removeOldAndSaveNew(
|
attendanceDb.insertAll(attendanceToAdd)
|
||||||
oldItems = old uniqueSubtract new,
|
|
||||||
newItems = attendanceToAdd,
|
|
||||||
)
|
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
||||||
},
|
},
|
||||||
filterResult = { it.filter { item -> item.date in start..end } }
|
filterResult = { it.filter { item -> item.date in start..end } }
|
||||||
@ -151,19 +81,14 @@ class AttendanceRepository @Inject constructor(
|
|||||||
start: LocalDate,
|
start: LocalDate,
|
||||||
end: LocalDate
|
end: LocalDate
|
||||||
): Flow<List<Attendance>> {
|
): Flow<List<Attendance>> {
|
||||||
val hiddenAttendanceItems = preferencesRepository.hiddenAttendanceItems
|
return attendanceDb.loadAll(semester.diaryId, semester.studentId, start, end)
|
||||||
|
|
||||||
return attendanceDb
|
|
||||||
.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)
|
|
||||||
.map {
|
|
||||||
it.filter { item -> filterAttendance(hiddenAttendanceItems, item) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateTimetable(timetable: List<Attendance>) {
|
suspend fun updateTimetable(timetable: List<Attendance>) {
|
||||||
return attendanceDb.updateAll(timetable)
|
return attendanceDb.updateAll(timetable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmName("excuseForAbsenceLessons")
|
||||||
suspend fun excuseForAbsence(
|
suspend fun excuseForAbsence(
|
||||||
student: Student,
|
student: Student,
|
||||||
semester: Semester,
|
semester: Semester,
|
||||||
@ -176,7 +101,26 @@ class AttendanceRepository @Inject constructor(
|
|||||||
timeId = attendance.timeId
|
timeId = attendance.timeId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
wulkanowySdkFactory.create(student, semester)
|
sdk.init(student)
|
||||||
|
.switchSemester(semester)
|
||||||
|
.excuseForAbsence(items, reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmName("excuseForAbsenceDays")
|
||||||
|
suspend fun excuseForAbsence(
|
||||||
|
student: Student,
|
||||||
|
semester: Semester,
|
||||||
|
days: List<LocalDate>,
|
||||||
|
reason: String? = null
|
||||||
|
) {
|
||||||
|
val items = days.map { day ->
|
||||||
|
Absent(
|
||||||
|
date = LocalDateTime.of(day, LocalTime.of(0, 0)),
|
||||||
|
timeId = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
sdk.init(student)
|
||||||
|
.switchSemester(semester)
|
||||||
.excuseForAbsence(items, reason)
|
.excuseForAbsence(items, reason)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import androidx.room.withTransaction
|
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
|
||||||
import io.github.wulkanowy.data.db.AppDatabase
|
|
||||||
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
|
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
|
||||||
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.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
|
import io.github.wulkanowy.utils.init
|
||||||
|
import io.github.wulkanowy.utils.switchSemester
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -18,9 +18,8 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class AttendanceSummaryRepository @Inject constructor(
|
class AttendanceSummaryRepository @Inject constructor(
|
||||||
private val attendanceDb: AttendanceSummaryDao,
|
private val attendanceDb: AttendanceSummaryDao,
|
||||||
|
private val sdk: Sdk,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
private val appDatabase: AppDatabase,
|
|
||||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val saveFetchResultMutex = Mutex()
|
private val saveFetchResultMutex = Mutex()
|
||||||
@ -41,15 +40,14 @@ class AttendanceSummaryRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId) },
|
query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
wulkanowySdkFactory.create(student, semester)
|
sdk.init(student)
|
||||||
|
.switchSemester(semester)
|
||||||
.getAttendanceSummary(subjectId)
|
.getAttendanceSummary(subjectId)
|
||||||
.mapToEntities(semester, subjectId)
|
.mapToEntities(semester, subjectId)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
appDatabase.withTransaction {
|
|
||||||
attendanceDb.deleteAll(old uniqueSubtract new)
|
attendanceDb.deleteAll(old uniqueSubtract new)
|
||||||
attendanceDb.insertAll(new uniqueSubtract old)
|
attendanceDb.insertAll(new uniqueSubtract old)
|
||||||
}
|
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
|
||||||
import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
|
import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
|
||||||
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.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.*
|
||||||
import io.github.wulkanowy.utils.monday
|
|
||||||
import io.github.wulkanowy.utils.sunday
|
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -19,7 +15,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class CompletedLessonsRepository @Inject constructor(
|
class CompletedLessonsRepository @Inject constructor(
|
||||||
private val completedLessonsDb: CompletedLessonsDao,
|
private val completedLessonsDb: CompletedLessonsDao,
|
||||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
private val sdk: Sdk,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -51,15 +47,14 @@ class CompletedLessonsRepository @Inject constructor(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
wulkanowySdkFactory.create(student, semester)
|
sdk.init(student)
|
||||||
|
.switchSemester(semester)
|
||||||
.getCompletedLessons(start.monday, end.sunday)
|
.getCompletedLessons(start.monday, end.sunday)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
completedLessonsDb.removeOldAndSaveNew(
|
completedLessonsDb.deleteAll(old uniqueSubtract new)
|
||||||
oldItems = old uniqueSubtract new,
|
completedLessonsDb.insertAll(new uniqueSubtract old)
|
||||||
newItems = new uniqueSubtract old,
|
|
||||||
)
|
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
||||||
},
|
},
|
||||||
filterResult = { it.filter { item -> item.date in start..end } }
|
filterResult = { it.filter { item -> item.date in start..end } }
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
|
||||||
import io.github.wulkanowy.data.db.dao.ConferenceDao
|
import io.github.wulkanowy.data.db.dao.ConferenceDao
|
||||||
import io.github.wulkanowy.data.db.entities.Conference
|
import io.github.wulkanowy.data.db.entities.Conference
|
||||||
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.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
|
import io.github.wulkanowy.utils.init
|
||||||
|
import io.github.wulkanowy.utils.switchSemester
|
||||||
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.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
@ -19,7 +21,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class ConferenceRepository @Inject constructor(
|
class ConferenceRepository @Inject constructor(
|
||||||
private val conferenceDb: ConferenceDao,
|
private val conferenceDb: ConferenceDao,
|
||||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
private val sdk: Sdk,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -44,18 +46,19 @@ class ConferenceRepository @Inject constructor(
|
|||||||
conferenceDb.loadAll(semester.diaryId, student.studentId, startDate)
|
conferenceDb.loadAll(semester.diaryId, student.studentId, startDate)
|
||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
wulkanowySdkFactory.create(student, semester)
|
sdk.init(student)
|
||||||
|
.switchSemester(semester)
|
||||||
.getConferences()
|
.getConferences()
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
.filter { it.date >= startDate }
|
.filter { it.date >= startDate }
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
conferenceDb.removeOldAndSaveNew(
|
val conferencesToSave = (new uniqueSubtract old).onEach {
|
||||||
oldItems = old uniqueSubtract new,
|
|
||||||
newItems = (new uniqueSubtract old).onEach {
|
|
||||||
if (notify) it.isNotified = false
|
if (notify) it.isNotified = false
|
||||||
},
|
}
|
||||||
)
|
|
||||||
|
conferenceDb.deleteAll(old uniqueSubtract new)
|
||||||
|
conferenceDb.insertAll(conferencesToSave)
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
|
||||||
import io.github.wulkanowy.data.db.dao.ExamDao
|
import io.github.wulkanowy.data.db.dao.ExamDao
|
||||||
import io.github.wulkanowy.data.db.entities.Exam
|
import io.github.wulkanowy.data.db.entities.Exam
|
||||||
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.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.endExamsDay
|
import io.github.wulkanowy.utils.endExamsDay
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
|
import io.github.wulkanowy.utils.init
|
||||||
import io.github.wulkanowy.utils.startExamsDay
|
import io.github.wulkanowy.utils.startExamsDay
|
||||||
|
import io.github.wulkanowy.utils.switchSemester
|
||||||
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.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
@ -21,7 +23,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class ExamRepository @Inject constructor(
|
class ExamRepository @Inject constructor(
|
||||||
private val examDb: ExamDao,
|
private val examDb: ExamDao,
|
||||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
private val sdk: Sdk,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -54,17 +56,18 @@ class ExamRepository @Inject constructor(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
wulkanowySdkFactory.create(student, semester)
|
sdk.init(student)
|
||||||
|
.switchSemester(semester)
|
||||||
.getExams(start.startExamsDay, start.endExamsDay)
|
.getExams(start.startExamsDay, start.endExamsDay)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
examDb.removeOldAndSaveNew(
|
val examsToSave = (new uniqueSubtract old).onEach {
|
||||||
oldItems = old uniqueSubtract new,
|
|
||||||
newItems = (new uniqueSubtract old).onEach {
|
|
||||||
if (notify) it.isNotified = false
|
if (notify) it.isNotified = false
|
||||||
},
|
}
|
||||||
)
|
|
||||||
|
examDb.deleteAll(old uniqueSubtract new)
|
||||||
|
examDb.insertAll(examsToSave)
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
||||||
},
|
},
|
||||||
filterResult = { it.filter { item -> item.date in start..end } }
|
filterResult = { it.filter { item -> item.date in start..end } }
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
|
||||||
import io.github.wulkanowy.data.db.dao.GradeDao
|
import io.github.wulkanowy.data.db.dao.GradeDao
|
||||||
import io.github.wulkanowy.data.db.dao.GradeDescriptiveDao
|
import io.github.wulkanowy.data.db.dao.GradeDescriptiveDao
|
||||||
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
|
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
|
||||||
@ -11,15 +10,17 @@ 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.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
|
import io.github.wulkanowy.utils.init
|
||||||
|
import io.github.wulkanowy.utils.switchSemester
|
||||||
import io.github.wulkanowy.utils.toLocalDate
|
import io.github.wulkanowy.utils.toLocalDate
|
||||||
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.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import timber.log.Timber
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -29,20 +30,12 @@ class GradeRepository @Inject constructor(
|
|||||||
private val gradeDb: GradeDao,
|
private val gradeDb: GradeDao,
|
||||||
private val gradeSummaryDb: GradeSummaryDao,
|
private val gradeSummaryDb: GradeSummaryDao,
|
||||||
private val gradeDescriptiveDb: GradeDescriptiveDao,
|
private val gradeDescriptiveDb: GradeDescriptiveDao,
|
||||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
private val sdk: Sdk,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
private val preferencesRepository: PreferencesRepository
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val saveFetchResultMutex = Mutex()
|
private val saveFetchResultMutex = Mutex()
|
||||||
|
|
||||||
private fun loadGrades(semesterId: Int, studentId: Int): Flow<List<Grade>> {
|
|
||||||
val hiddenGrades = preferencesRepository.hiddenGrades
|
|
||||||
|
|
||||||
Timber.i("Load grades for semester $semesterId student $studentId")
|
|
||||||
return gradeDb.loadAllCensored(semesterId, studentId, hiddenGrades.toTypedArray())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getGrades(
|
fun getGrades(
|
||||||
student: Student,
|
student: Student,
|
||||||
semester: Semester,
|
semester: Semester,
|
||||||
@ -70,15 +63,12 @@ class GradeRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
val hiddenGrades = preferencesRepository.hiddenGrades
|
val (details, summary, descriptive) = sdk.init(student)
|
||||||
|
.switchSemester(semester)
|
||||||
val (details, summary, descriptive) = wulkanowySdkFactory.create(student, semester)
|
|
||||||
.getGrades(semester.semesterId)
|
.getGrades(semester.semesterId)
|
||||||
|
|
||||||
val censoredDetails = details.filterNot { it.entry in hiddenGrades }
|
|
||||||
|
|
||||||
Triple(
|
Triple(
|
||||||
censoredDetails.mapToEntities(semester),
|
details.mapToEntities(semester),
|
||||||
summary.mapToEntities(semester),
|
summary.mapToEntities(semester),
|
||||||
descriptive.mapToEntities(semester)
|
descriptive.mapToEntities(semester)
|
||||||
)
|
)
|
||||||
@ -97,12 +87,10 @@ class GradeRepository @Inject constructor(
|
|||||||
new: List<GradeDescriptive>,
|
new: List<GradeDescriptive>,
|
||||||
notify: Boolean
|
notify: Boolean
|
||||||
) {
|
) {
|
||||||
gradeDescriptiveDb.removeOldAndSaveNew(
|
gradeDescriptiveDb.deleteAll(old uniqueSubtract new)
|
||||||
oldItems = old uniqueSubtract new,
|
gradeDescriptiveDb.insertAll((new uniqueSubtract old).onEach {
|
||||||
newItems = (new uniqueSubtract old).onEach {
|
|
||||||
if (notify) it.isNotified = false
|
if (notify) it.isNotified = false
|
||||||
},
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun refreshGradeDetails(
|
private suspend fun refreshGradeDetails(
|
||||||
@ -113,16 +101,13 @@ class GradeRepository @Inject constructor(
|
|||||||
) {
|
) {
|
||||||
val notifyBreakDate = oldGrades.maxByOrNull { it.date }?.date
|
val notifyBreakDate = oldGrades.maxByOrNull { it.date }?.date
|
||||||
?: student.registrationDate.toLocalDate()
|
?: student.registrationDate.toLocalDate()
|
||||||
|
gradeDb.deleteAll(oldGrades uniqueSubtract newDetails)
|
||||||
gradeDb.removeOldAndSaveNew(
|
gradeDb.insertAll((newDetails uniqueSubtract oldGrades).onEach {
|
||||||
oldItems = oldGrades uniqueSubtract newDetails,
|
|
||||||
newItems = (newDetails uniqueSubtract oldGrades).onEach {
|
|
||||||
if (it.date >= notifyBreakDate) it.apply {
|
if (it.date >= notifyBreakDate) it.apply {
|
||||||
isRead = false
|
isRead = false
|
||||||
if (notify) isNotified = false
|
if (notify) isNotified = false
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun refreshGradeSummaries(
|
private suspend fun refreshGradeSummaries(
|
||||||
@ -130,23 +115,9 @@ class GradeRepository @Inject constructor(
|
|||||||
newSummary: List<GradeSummary>,
|
newSummary: List<GradeSummary>,
|
||||||
notify: Boolean
|
notify: Boolean
|
||||||
) {
|
) {
|
||||||
gradeSummaryDb.removeOldAndSaveNew(
|
gradeSummaryDb.deleteAll(oldSummaries uniqueSubtract newSummary)
|
||||||
oldItems = oldSummaries uniqueSubtract newSummary,
|
gradeSummaryDb.insertAll((newSummary uniqueSubtract oldSummaries).onEach { summary ->
|
||||||
newItems = (newSummary uniqueSubtract oldSummaries).onEach { summary ->
|
val oldSummary = oldSummaries.find { old -> old.subject == summary.subject }
|
||||||
getGradeSummaryWithUpdatedNotificationState(
|
|
||||||
summary = summary,
|
|
||||||
oldSummary = oldSummaries.find { it.subject == summary.subject },
|
|
||||||
notify = notify,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getGradeSummaryWithUpdatedNotificationState(
|
|
||||||
summary: GradeSummary,
|
|
||||||
oldSummary: GradeSummary?,
|
|
||||||
notify: Boolean,
|
|
||||||
) {
|
|
||||||
summary.isPredictedGradeNotified = when {
|
summary.isPredictedGradeNotified = when {
|
||||||
summary.predictedGrade.isEmpty() -> true
|
summary.predictedGrade.isEmpty() -> true
|
||||||
notify && oldSummary?.predictedGrade != summary.predictedGrade -> false
|
notify && oldSummary?.predictedGrade != summary.predictedGrade -> false
|
||||||
@ -157,6 +128,7 @@ class GradeRepository @Inject constructor(
|
|||||||
notify && oldSummary?.finalGrade != summary.finalGrade -> false
|
notify && oldSummary?.finalGrade != summary.finalGrade -> false
|
||||||
else -> true
|
else -> true
|
||||||
}
|
}
|
||||||
|
|
||||||
summary.predictedGradeLastChange = when {
|
summary.predictedGradeLastChange = when {
|
||||||
oldSummary == null -> Instant.now()
|
oldSummary == null -> Instant.now()
|
||||||
summary.predictedGrade != oldSummary.predictedGrade -> Instant.now()
|
summary.predictedGrade != oldSummary.predictedGrade -> Instant.now()
|
||||||
@ -167,16 +139,17 @@ class GradeRepository @Inject constructor(
|
|||||||
summary.finalGrade != oldSummary.finalGrade -> Instant.now()
|
summary.finalGrade != oldSummary.finalGrade -> Instant.now()
|
||||||
else -> oldSummary.finalGradeLastChange
|
else -> oldSummary.finalGradeLastChange
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getUnreadGrades(semester: Semester): Flow<List<Grade>> {
|
fun getUnreadGrades(semester: Semester): Flow<List<Grade>> {
|
||||||
return loadGrades(semester.semesterId, semester.studentId).map {
|
return gradeDb.loadAll(semester.semesterId, semester.studentId).map {
|
||||||
it.filter { grade -> !grade.isRead }
|
it.filter { grade -> !grade.isRead }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getGradesFromDatabase(semester: Semester): Flow<List<Grade>> {
|
fun getGradesFromDatabase(semester: Semester): Flow<List<Grade>> {
|
||||||
return loadGrades(semester.semesterId, semester.studentId)
|
return gradeDb.loadAll(semester.semesterId, semester.studentId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getGradesPredictedFromDatabase(semester: Semester): Flow<List<GradeSummary>> {
|
fun getGradesPredictedFromDatabase(semester: Semester): Flow<List<GradeSummary>> {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
|
||||||
import io.github.wulkanowy.data.db.dao.GradePartialStatisticsDao
|
import io.github.wulkanowy.data.db.dao.GradePartialStatisticsDao
|
||||||
import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao
|
import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao
|
||||||
import io.github.wulkanowy.data.db.dao.GradeSemesterStatisticsDao
|
import io.github.wulkanowy.data.db.dao.GradeSemesterStatisticsDao
|
||||||
@ -13,11 +12,14 @@ import io.github.wulkanowy.data.mappers.mapPointsToStatisticsItems
|
|||||||
import io.github.wulkanowy.data.mappers.mapSemesterToStatisticItems
|
import io.github.wulkanowy.data.mappers.mapSemesterToStatisticItems
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
|
import io.github.wulkanowy.utils.init
|
||||||
|
import io.github.wulkanowy.utils.switchSemester
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import java.util.Locale
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@ -26,7 +28,7 @@ class GradeStatisticsRepository @Inject constructor(
|
|||||||
private val gradePartialStatisticsDb: GradePartialStatisticsDao,
|
private val gradePartialStatisticsDb: GradePartialStatisticsDao,
|
||||||
private val gradePointsStatisticsDb: GradePointsStatisticsDao,
|
private val gradePointsStatisticsDb: GradePointsStatisticsDao,
|
||||||
private val gradeSemesterStatisticsDb: GradeSemesterStatisticsDao,
|
private val gradeSemesterStatisticsDb: GradeSemesterStatisticsDao,
|
||||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
private val sdk: Sdk,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -54,15 +56,14 @@ class GradeStatisticsRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { gradePartialStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
query = { gradePartialStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
wulkanowySdkFactory.create(student, semester)
|
sdk.init(student)
|
||||||
|
.switchSemester(semester)
|
||||||
.getGradesPartialStatistics(semester.semesterId)
|
.getGradesPartialStatistics(semester.semesterId)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
gradePartialStatisticsDb.removeOldAndSaveNew(
|
gradePartialStatisticsDb.deleteAll(old uniqueSubtract new)
|
||||||
oldItems = old uniqueSubtract new,
|
gradePartialStatisticsDb.insertAll(new uniqueSubtract old)
|
||||||
newItems = new uniqueSubtract old,
|
|
||||||
)
|
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(partialCacheKey, semester))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(partialCacheKey, semester))
|
||||||
},
|
},
|
||||||
mapResult = { items ->
|
mapResult = { items ->
|
||||||
@ -79,7 +80,6 @@ class GradeStatisticsRepository @Inject constructor(
|
|||||||
)
|
)
|
||||||
listOf(summaryItem) + items
|
listOf(summaryItem) + items
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> items.filter { it.subject == subjectName }
|
else -> items.filter { it.subject == subjectName }
|
||||||
}.mapPartialToStatisticItems()
|
}.mapPartialToStatisticItems()
|
||||||
}
|
}
|
||||||
@ -101,15 +101,14 @@ class GradeStatisticsRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { gradeSemesterStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
query = { gradeSemesterStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
wulkanowySdkFactory.create(student, semester)
|
sdk.init(student)
|
||||||
|
.switchSemester(semester)
|
||||||
.getGradesSemesterStatistics(semester.semesterId)
|
.getGradesSemesterStatistics(semester.semesterId)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
gradeSemesterStatisticsDb.removeOldAndSaveNew(
|
gradeSemesterStatisticsDb.deleteAll(old uniqueSubtract new)
|
||||||
oldItems = old uniqueSubtract new,
|
gradeSemesterStatisticsDb.insertAll(new uniqueSubtract old)
|
||||||
newItems = new uniqueSubtract old,
|
|
||||||
)
|
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(semesterCacheKey, semester))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(semesterCacheKey, semester))
|
||||||
},
|
},
|
||||||
mapResult = { items ->
|
mapResult = { items ->
|
||||||
@ -139,7 +138,6 @@ class GradeStatisticsRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
listOf(summaryItem) + itemsWithAverage
|
listOf(summaryItem) + itemsWithAverage
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> itemsWithAverage.filter { it.subject == subjectName }
|
else -> itemsWithAverage.filter { it.subject == subjectName }
|
||||||
}.mapSemesterToStatisticItems()
|
}.mapSemesterToStatisticItems()
|
||||||
}
|
}
|
||||||
@ -159,15 +157,14 @@ class GradeStatisticsRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
query = { gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
wulkanowySdkFactory.create(student, semester)
|
sdk.init(student)
|
||||||
|
.switchSemester(semester)
|
||||||
.getGradesPointsStatistics(semester.semesterId)
|
.getGradesPointsStatistics(semester.semesterId)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
gradePointsStatisticsDb.removeOldAndSaveNew(
|
gradePointsStatisticsDb.deleteAll(old uniqueSubtract new)
|
||||||
oldItems = old uniqueSubtract new,
|
gradePointsStatisticsDb.insertAll(new uniqueSubtract old)
|
||||||
newItems = new uniqueSubtract old,
|
|
||||||
)
|
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(pointsCacheKey, semester))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(pointsCacheKey, semester))
|
||||||
},
|
},
|
||||||
mapResult = { items ->
|
mapResult = { items ->
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
|
||||||
import io.github.wulkanowy.data.db.dao.HomeworkDao
|
import io.github.wulkanowy.data.db.dao.HomeworkDao
|
||||||
import io.github.wulkanowy.data.db.entities.Homework
|
import io.github.wulkanowy.data.db.entities.Homework
|
||||||
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.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
|
import io.github.wulkanowy.utils.init
|
||||||
import io.github.wulkanowy.utils.monday
|
import io.github.wulkanowy.utils.monday
|
||||||
import io.github.wulkanowy.utils.sunday
|
import io.github.wulkanowy.utils.sunday
|
||||||
|
import io.github.wulkanowy.utils.switchSemester
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
@ -20,7 +22,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class HomeworkRepository @Inject constructor(
|
class HomeworkRepository @Inject constructor(
|
||||||
private val homeworkDb: HomeworkDao,
|
private val homeworkDb: HomeworkDao,
|
||||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
private val sdk: Sdk,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -53,19 +55,20 @@ class HomeworkRepository @Inject constructor(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
wulkanowySdkFactory.create(student, semester)
|
sdk.init(student)
|
||||||
|
.switchSemester(semester)
|
||||||
.getHomework(start.monday, end.sunday)
|
.getHomework(start.monday, end.sunday)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
|
val homeWorkToSave = (new uniqueSubtract old).onEach {
|
||||||
|
if (notify) it.isNotified = false
|
||||||
|
}
|
||||||
val filteredOld = old.filterNot { it.isAddedByUser }
|
val filteredOld = old.filterNot { it.isAddedByUser }
|
||||||
|
|
||||||
homeworkDb.removeOldAndSaveNew(
|
homeworkDb.deleteAll(filteredOld uniqueSubtract new)
|
||||||
oldItems = filteredOld uniqueSubtract new,
|
homeworkDb.insertAll(homeWorkToSave)
|
||||||
newItems = (new uniqueSubtract old).onEach {
|
|
||||||
if (notify) it.isNotified = false
|
|
||||||
},
|
|
||||||
)
|
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
|
||||||
import io.github.wulkanowy.data.db.dao.LuckyNumberDao
|
import io.github.wulkanowy.data.db.dao.LuckyNumberDao
|
||||||
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntity
|
import io.github.wulkanowy.data.mappers.mapToEntity
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AppWidgetUpdater
|
import io.github.wulkanowy.utils.init
|
||||||
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 kotlinx.coroutines.sync.Mutex
|
||||||
@ -19,8 +18,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class LuckyNumberRepository @Inject constructor(
|
class LuckyNumberRepository @Inject constructor(
|
||||||
private val luckyNumberDb: LuckyNumberDao,
|
private val luckyNumberDb: LuckyNumberDao,
|
||||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
private val sdk: Sdk
|
||||||
private val appWidgetUpdater: AppWidgetUpdater,
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val saveFetchResultMutex = Mutex()
|
private val saveFetchResultMutex = Mutex()
|
||||||
@ -29,28 +27,23 @@ class LuckyNumberRepository @Inject constructor(
|
|||||||
student: Student,
|
student: Student,
|
||||||
forceRefresh: Boolean,
|
forceRefresh: Boolean,
|
||||||
notify: Boolean = false,
|
notify: Boolean = false,
|
||||||
isFromAppWidget: Boolean = false
|
|
||||||
) = networkBoundResource(
|
) = networkBoundResource(
|
||||||
mutex = saveFetchResultMutex,
|
mutex = saveFetchResultMutex,
|
||||||
isResultEmpty = { it == null },
|
isResultEmpty = { it == null },
|
||||||
shouldFetch = { it == null || forceRefresh },
|
shouldFetch = { it == null || forceRefresh },
|
||||||
query = { luckyNumberDb.load(student.studentId, now()) },
|
query = { luckyNumberDb.load(student.studentId, now()) },
|
||||||
fetch = {
|
fetch = {
|
||||||
wulkanowySdkFactory.create(student)
|
sdk.init(student).getLuckyNumber(student.schoolShortName)?.mapToEntity(student)
|
||||||
.getLuckyNumber(student.schoolShortName)
|
|
||||||
?.mapToEntity(student)
|
|
||||||
},
|
},
|
||||||
saveFetchResult = { oldLuckyNumber, newLuckyNumber ->
|
saveFetchResult = { oldLuckyNumber, newLuckyNumber ->
|
||||||
newLuckyNumber ?: return@networkBoundResource
|
newLuckyNumber ?: return@networkBoundResource
|
||||||
|
|
||||||
if (newLuckyNumber != oldLuckyNumber) {
|
if (newLuckyNumber != oldLuckyNumber) {
|
||||||
luckyNumberDb.removeOldAndSaveNew(
|
val updatedLuckNumberList =
|
||||||
oldItems = listOfNotNull(oldLuckyNumber),
|
listOf(newLuckyNumber.apply { if (notify) isNotified = false })
|
||||||
newItems = listOf(newLuckyNumber.apply { if (notify) isNotified = false }),
|
|
||||||
)
|
oldLuckyNumber?.let { luckyNumberDb.deleteAll(listOfNotNull(it)) }
|
||||||
if (!isFromAppWidget) {
|
luckyNumberDb.insertAll(updatedLuckNumberList)
|
||||||
appWidgetUpdater.updateAllAppWidgetsByProvider(LuckyNumberWidgetProvider::class)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -4,7 +4,6 @@ import android.content.Context
|
|||||||
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.data.Resource
|
import io.github.wulkanowy.data.Resource
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
|
||||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||||
import io.github.wulkanowy.data.db.dao.MailboxDao
|
import io.github.wulkanowy.data.db.dao.MailboxDao
|
||||||
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
|
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
|
||||||
@ -30,9 +29,11 @@ import io.github.wulkanowy.data.pojos.MessageDraft
|
|||||||
import io.github.wulkanowy.data.toFirstResult
|
import io.github.wulkanowy.data.toFirstResult
|
||||||
import io.github.wulkanowy.data.waitForResult
|
import io.github.wulkanowy.data.waitForResult
|
||||||
import io.github.wulkanowy.domain.messages.GetMailboxByStudentUseCase
|
import io.github.wulkanowy.domain.messages.GetMailboxByStudentUseCase
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.sdk.pojo.Folder
|
import io.github.wulkanowy.sdk.pojo.Folder
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
|
import io.github.wulkanowy.utils.init
|
||||||
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.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
@ -47,7 +48,7 @@ class MessageRepository @Inject constructor(
|
|||||||
private val messagesDb: MessagesDao,
|
private val messagesDb: MessagesDao,
|
||||||
private val mutedMessageSendersDao: MutedMessageSendersDao,
|
private val mutedMessageSendersDao: MutedMessageSendersDao,
|
||||||
private val messageAttachmentDao: MessageAttachmentDao,
|
private val messageAttachmentDao: MessageAttachmentDao,
|
||||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
private val sdk: Sdk,
|
||||||
@ApplicationContext private val context: Context,
|
@ApplicationContext private val context: Context,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
private val sharedPrefProvider: SharedPrefProvider,
|
private val sharedPrefProvider: SharedPrefProvider,
|
||||||
@ -81,26 +82,19 @@ class MessageRepository @Inject constructor(
|
|||||||
} else messagesDb.loadMessagesWithMutedAuthor(mailbox.globalKey, folder.id)
|
} else messagesDb.loadMessagesWithMutedAuthor(mailbox.globalKey, folder.id)
|
||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
wulkanowySdkFactory.create(student)
|
sdk.init(student).getMessages(
|
||||||
.getMessages(
|
|
||||||
folder = Folder.valueOf(folder.name),
|
folder = Folder.valueOf(folder.name),
|
||||||
mailboxKey = mailbox?.globalKey,
|
mailboxKey = mailbox?.globalKey,
|
||||||
)
|
).mapToEntities(student, mailbox, mailboxDao.loadAll(student.email))
|
||||||
.mapToEntities(
|
|
||||||
student = student,
|
|
||||||
mailbox = mailbox,
|
|
||||||
allMailboxes = mailboxDao.loadAll(student.email)
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
saveFetchResult = { oldWithAuthors, new ->
|
saveFetchResult = { oldWithAuthors, new ->
|
||||||
val old = oldWithAuthors.map { it.message }
|
val old = oldWithAuthors.map { it.message }
|
||||||
messagesDb.removeOldAndSaveNew(
|
messagesDb.deleteAll(old uniqueSubtract new)
|
||||||
oldItems = old uniqueSubtract new,
|
messagesDb.insertAll((new uniqueSubtract old).onEach {
|
||||||
newItems = (new uniqueSubtract old).onEach {
|
|
||||||
val muted = isMuted(it.correspondents)
|
val muted = isMuted(it.correspondents)
|
||||||
it.isNotified = !notify || muted
|
it.isNotified = !notify || muted
|
||||||
},
|
})
|
||||||
)
|
|
||||||
refreshHelper.updateLastRefreshTimestamp(
|
refreshHelper.updateLastRefreshTimestamp(
|
||||||
getRefreshKey(messagesCacheKey, mailbox, folder)
|
getRefreshKey(messagesCacheKey, mailbox, folder)
|
||||||
)
|
)
|
||||||
@ -120,9 +114,8 @@ class MessageRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { messagesDb.loadMessageWithAttachment(message.messageGlobalKey) },
|
query = { messagesDb.loadMessageWithAttachment(message.messageGlobalKey) },
|
||||||
fetch = {
|
fetch = {
|
||||||
wulkanowySdkFactory.create(student)
|
sdk.init(student).getMessageDetails(
|
||||||
.getMessageDetails(
|
messageKey = it!!.message.messageGlobalKey,
|
||||||
messageKey = message.messageGlobalKey,
|
|
||||||
markAsRead = message.unread && markAsRead,
|
markAsRead = message.unread && markAsRead,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -165,8 +158,7 @@ class MessageRepository @Inject constructor(
|
|||||||
recipients: List<Recipient>,
|
recipients: List<Recipient>,
|
||||||
mailbox: Mailbox,
|
mailbox: Mailbox,
|
||||||
) {
|
) {
|
||||||
wulkanowySdkFactory.create(student)
|
sdk.init(student).sendMessage(
|
||||||
.sendMessage(
|
|
||||||
subject = subject,
|
subject = subject,
|
||||||
content = content,
|
content = content,
|
||||||
recipients = recipients.mapFromEntities(),
|
recipients = recipients.mapFromEntities(),
|
||||||
@ -176,8 +168,9 @@ class MessageRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun restoreMessages(student: Student, mailbox: Mailbox?, messages: List<Message>) {
|
suspend fun restoreMessages(student: Student, mailbox: Mailbox?, messages: List<Message>) {
|
||||||
wulkanowySdkFactory.create(student)
|
sdk.init(student).restoreMessages(
|
||||||
.restoreMessages(messages = messages.map { it.messageGlobalKey })
|
messages = messages.map { it.messageGlobalKey },
|
||||||
|
)
|
||||||
|
|
||||||
refreshFolders(student, mailbox)
|
refreshFolders(student, mailbox)
|
||||||
}
|
}
|
||||||
@ -188,8 +181,7 @@ class MessageRepository @Inject constructor(
|
|||||||
|
|
||||||
suspend fun deleteMessages(student: Student, messages: List<Message>) {
|
suspend fun deleteMessages(student: Student, messages: List<Message>) {
|
||||||
val firstMessage = messages.first()
|
val firstMessage = messages.first()
|
||||||
wulkanowySdkFactory.create(student)
|
sdk.init(student).deleteMessages(
|
||||||
.deleteMessages(
|
|
||||||
messages = messages.map { it.messageGlobalKey },
|
messages = messages.map { it.messageGlobalKey },
|
||||||
removeForever = firstMessage.folderId == TRASHED.id,
|
removeForever = firstMessage.folderId == TRASHED.id,
|
||||||
)
|
)
|
||||||
@ -237,9 +229,7 @@ class MessageRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { mailboxDao.loadAll(student.email, student.symbol, student.schoolSymbol) },
|
query = { mailboxDao.loadAll(student.email, student.symbol, student.schoolSymbol) },
|
||||||
fetch = {
|
fetch = {
|
||||||
wulkanowySdkFactory.create(student)
|
sdk.init(student).getMailboxes().mapToEntities(student)
|
||||||
.getMailboxes()
|
|
||||||
.mapToEntities(student)
|
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
mailboxDao.deleteAll(old uniqueSubtract new)
|
mailboxDao.deleteAll(old uniqueSubtract new)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
|
||||||
import io.github.wulkanowy.data.db.dao.MobileDeviceDao
|
import io.github.wulkanowy.data.db.dao.MobileDeviceDao
|
||||||
import io.github.wulkanowy.data.db.entities.MobileDevice
|
import io.github.wulkanowy.data.db.entities.MobileDevice
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
@ -9,8 +8,11 @@ import io.github.wulkanowy.data.mappers.mapToEntities
|
|||||||
import io.github.wulkanowy.data.mappers.mapToMobileDeviceToken
|
import io.github.wulkanowy.data.mappers.mapToMobileDeviceToken
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.data.pojos.MobileDeviceToken
|
import io.github.wulkanowy.data.pojos.MobileDeviceToken
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
|
import io.github.wulkanowy.utils.init
|
||||||
|
import io.github.wulkanowy.utils.switchSemester
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -19,7 +21,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class MobileDeviceRepository @Inject constructor(
|
class MobileDeviceRepository @Inject constructor(
|
||||||
private val mobileDb: MobileDeviceDao,
|
private val mobileDb: MobileDeviceDao,
|
||||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
private val sdk: Sdk,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -38,30 +40,32 @@ class MobileDeviceRepository @Inject constructor(
|
|||||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
|
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
|
||||||
it.isEmpty() || forceRefresh || isExpired
|
it.isEmpty() || forceRefresh || isExpired
|
||||||
},
|
},
|
||||||
query = { mobileDb.loadAll(student.studentId) },
|
query = { mobileDb.loadAll(student.userLoginId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
wulkanowySdkFactory.create(student, semester)
|
sdk.init(student)
|
||||||
|
.switchSemester(semester)
|
||||||
.getRegisteredDevices()
|
.getRegisteredDevices()
|
||||||
.mapToEntities(student)
|
.mapToEntities(student)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
mobileDb.removeOldAndSaveNew(
|
mobileDb.deleteAll(old uniqueSubtract new)
|
||||||
oldItems = old uniqueSubtract new,
|
mobileDb.insertAll(new uniqueSubtract old)
|
||||||
newItems = new uniqueSubtract old,
|
|
||||||
)
|
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice) {
|
suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice) {
|
||||||
wulkanowySdkFactory.create(student, semester)
|
sdk.init(student)
|
||||||
|
.switchSemester(semester)
|
||||||
.unregisterDevice(device.deviceId)
|
.unregisterDevice(device.deviceId)
|
||||||
|
|
||||||
mobileDb.deleteAll(listOf(device))
|
mobileDb.deleteAll(listOf(device))
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getToken(student: Student, semester: Semester): MobileDeviceToken {
|
suspend fun getToken(student: Student, semester: Semester): MobileDeviceToken {
|
||||||
return wulkanowySdkFactory.create(student, semester)
|
return sdk.init(student)
|
||||||
|
.switchSemester(semester)
|
||||||
.getToken()
|
.getToken()
|
||||||
.mapToMobileDeviceToken()
|
.mapToMobileDeviceToken()
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,13 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
|
||||||
import io.github.wulkanowy.data.db.dao.NoteDao
|
import io.github.wulkanowy.data.db.dao.NoteDao
|
||||||
import io.github.wulkanowy.data.db.entities.Note
|
import io.github.wulkanowy.data.db.entities.Note
|
||||||
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.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.*
|
||||||
import io.github.wulkanowy.utils.toLocalDate
|
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -19,9 +16,8 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class NoteRepository @Inject constructor(
|
class NoteRepository @Inject constructor(
|
||||||
private val noteDb: NoteDao,
|
private val noteDb: NoteDao,
|
||||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
private val sdk: Sdk,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
private val preferencesRepository: PreferencesRepository
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val saveFetchResultMutex = Mutex()
|
private val saveFetchResultMutex = Mutex()
|
||||||
@ -40,29 +36,24 @@ class NoteRepository @Inject constructor(
|
|||||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||||
getRefreshKey(cacheKey, semester)
|
getRefreshKey(cacheKey, semester)
|
||||||
)
|
)
|
||||||
|
|
||||||
it.isEmpty() || forceRefresh || isExpired
|
it.isEmpty() || forceRefresh || isExpired
|
||||||
},
|
},
|
||||||
query = { noteDb.loadAll(student.studentId) },
|
query = { noteDb.loadAll(student.studentId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
val showNotes = preferencesRepository.showNotes
|
sdk.init(student)
|
||||||
|
.switchSemester(semester)
|
||||||
wulkanowySdkFactory.create(student, semester)
|
|
||||||
.getNotes()
|
.getNotes()
|
||||||
.filter { showNotes }
|
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
val notesToAdd = (new uniqueSubtract old).onEach {
|
noteDb.deleteAll(old uniqueSubtract new)
|
||||||
|
noteDb.insertAll((new uniqueSubtract old).onEach {
|
||||||
if (it.date >= student.registrationDate.toLocalDate()) it.apply {
|
if (it.date >= student.registrationDate.toLocalDate()) it.apply {
|
||||||
isRead = false
|
isRead = false
|
||||||
if (notify) isNotified = false
|
if (notify) isNotified = false
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
noteDb.removeOldAndSaveNew(
|
|
||||||
oldItems = old uniqueSubtract new,
|
|
||||||
newItems = notesToAdd,
|
|
||||||
)
|
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -7,16 +7,12 @@ import androidx.core.content.edit
|
|||||||
import com.fredporciuncula.flow.preferences.FlowSharedPreferences
|
import com.fredporciuncula.flow.preferences.FlowSharedPreferences
|
||||||
import com.fredporciuncula.flow.preferences.Preference
|
import com.fredporciuncula.flow.preferences.Preference
|
||||||
import com.fredporciuncula.flow.preferences.Serializer
|
import com.fredporciuncula.flow.preferences.Serializer
|
||||||
import com.fredporciuncula.flow.preferences.map
|
|
||||||
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.data.api.models.Mapping
|
|
||||||
import io.github.wulkanowy.data.enums.AppTheme
|
import io.github.wulkanowy.data.enums.AppTheme
|
||||||
import io.github.wulkanowy.data.enums.AttendanceCalculatorSortingMode
|
|
||||||
import io.github.wulkanowy.data.enums.GradeColorTheme
|
import io.github.wulkanowy.data.enums.GradeColorTheme
|
||||||
import io.github.wulkanowy.data.enums.GradeExpandMode
|
import io.github.wulkanowy.data.enums.GradeExpandMode
|
||||||
import io.github.wulkanowy.data.enums.GradeSortingMode
|
import io.github.wulkanowy.data.enums.GradeSortingMode
|
||||||
import io.github.wulkanowy.data.enums.ShowAdditionalLessonsMode
|
|
||||||
import io.github.wulkanowy.data.enums.TimetableGapsMode
|
import io.github.wulkanowy.data.enums.TimetableGapsMode
|
||||||
import io.github.wulkanowy.data.enums.TimetableMode
|
import io.github.wulkanowy.data.enums.TimetableMode
|
||||||
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
|
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
|
||||||
@ -26,7 +22,6 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import timber.log.Timber
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -39,7 +34,6 @@ class PreferencesRepository @Inject constructor(
|
|||||||
private val flowSharedPref: FlowSharedPreferences,
|
private val flowSharedPref: FlowSharedPreferences,
|
||||||
private val json: Json,
|
private val json: Json,
|
||||||
) {
|
) {
|
||||||
private val NO_ATTENDANCE_VALUE = -1.0
|
|
||||||
|
|
||||||
val isShowPresent: Boolean
|
val isShowPresent: Boolean
|
||||||
get() = getBoolean(
|
get() = getBoolean(
|
||||||
@ -47,27 +41,6 @@ class PreferencesRepository @Inject constructor(
|
|||||||
R.bool.pref_default_attendance_present
|
R.bool.pref_default_attendance_present
|
||||||
)
|
)
|
||||||
|
|
||||||
val targetAttendanceFlow: Flow<Int>
|
|
||||||
get() = flowSharedPref.getInt(
|
|
||||||
context.getString(R.string.pref_key_attendance_target),
|
|
||||||
context.resources.getInteger(R.integer.pref_default_attendance_target)
|
|
||||||
).asFlow()
|
|
||||||
|
|
||||||
val attendanceCalculatorSortingModeFlow: Flow<AttendanceCalculatorSortingMode>
|
|
||||||
get() = flowSharedPref.getString(
|
|
||||||
context.getString(R.string.pref_key_attendance_calculator_sorting_mode),
|
|
||||||
context.resources.getString(R.string.pref_default_attendance_calculator_sorting_mode)
|
|
||||||
).asFlow().map(AttendanceCalculatorSortingMode::getByValue)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subjects are empty when they don't have any attendances (total = 0, attendances = 0, absences = 0).
|
|
||||||
*/
|
|
||||||
val attendanceCalculatorShowEmptySubjects: Flow<Boolean>
|
|
||||||
get() = flowSharedPref.getBoolean(
|
|
||||||
context.getString(R.string.pref_key_attendance_calculator_show_empty_subjects),
|
|
||||||
context.resources.getBoolean(R.bool.pref_default_attendance_calculator_show_empty_subjects)
|
|
||||||
).asFlow()
|
|
||||||
|
|
||||||
private val gradeAverageModePref: Preference<GradeAverageMode>
|
private val gradeAverageModePref: Preference<GradeAverageMode>
|
||||||
get() = getObjectFlow(
|
get() = getObjectFlow(
|
||||||
R.string.pref_key_grade_average_mode,
|
R.string.pref_key_grade_average_mode,
|
||||||
@ -218,12 +191,6 @@ class PreferencesRepository @Inject constructor(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val showAdditionalLessonsInPlan: ShowAdditionalLessonsMode
|
|
||||||
get() = getString(
|
|
||||||
R.string.pref_key_timetable_show_additional_lessons,
|
|
||||||
R.string.pref_default_timetable_show_additional_lessons
|
|
||||||
).let { ShowAdditionalLessonsMode.getByValue(it) }
|
|
||||||
|
|
||||||
val gradeSortingMode: GradeSortingMode
|
val gradeSortingMode: GradeSortingMode
|
||||||
get() = GradeSortingMode.getByValue(
|
get() = GradeSortingMode.getByValue(
|
||||||
getString(
|
getString(
|
||||||
@ -305,60 +272,6 @@ class PreferencesRepository @Inject constructor(
|
|||||||
selectedDashboardTilesPreference.set(filteredValue)
|
selectedDashboardTilesPreference.set(filteredValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
var attendancePercentage: Double?
|
|
||||||
get() = attendancePercentagePreference.get().takeIf { it != NO_ATTENDANCE_VALUE }
|
|
||||||
set(value) = attendancePercentagePreference.set(value ?: NO_ATTENDANCE_VALUE)
|
|
||||||
|
|
||||||
var hiddenAttendanceItems: List<DashboardItem.HiddenAttendanceTile>
|
|
||||||
get() = hiddenAttendanceItemsPreference.get().toList()
|
|
||||||
set(value) = hiddenAttendanceItemsPreference.set(value.toSet())
|
|
||||||
|
|
||||||
var hiddenGrades: List<String>
|
|
||||||
get() = hiddenGradesPreference.get().toList()
|
|
||||||
set(value) = hiddenGradesPreference.set(value.toSet())
|
|
||||||
|
|
||||||
var showNotes: Boolean
|
|
||||||
get() = showNotesPreference.get()
|
|
||||||
set(value) = showNotesPreference.set(value)
|
|
||||||
|
|
||||||
var developerMode: Boolean
|
|
||||||
get() = developerModePreference.get()
|
|
||||||
set(value) = developerModePreference.set(value)
|
|
||||||
|
|
||||||
private val developerModePreference: Preference<Boolean>
|
|
||||||
get() = flowSharedPref.getBoolean(
|
|
||||||
context.getString(R.string.pref_key_developer_mode),
|
|
||||||
context.resources.getBoolean(R.bool.pref_default_developer_mode)
|
|
||||||
)
|
|
||||||
|
|
||||||
private val hiddenGradesPreference: Preference<Set<String>>
|
|
||||||
get() {
|
|
||||||
val defaultSet = context.resources.getStringArray(R.array.pref_default_hidden_grades).toSet()
|
|
||||||
val prefKey = "hidden_grades"
|
|
||||||
|
|
||||||
return flowSharedPref.getStringSet(prefKey, defaultSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val showNotesPreference: Preference<Boolean>
|
|
||||||
get() = flowSharedPref.getBoolean(
|
|
||||||
context.getString(R.string.pref_key_show_notes),
|
|
||||||
context.resources.getBoolean(R.bool.pref_default_show_notes)
|
|
||||||
)
|
|
||||||
|
|
||||||
private val hiddenAttendanceItemsPreference: Preference<Set<DashboardItem.HiddenAttendanceTile>>
|
|
||||||
get() {
|
|
||||||
val defaultSet =
|
|
||||||
context.resources.getStringArray(R.array.pref_default_hidden_attendance_items).toSet()
|
|
||||||
val prefKey = "attendance_items"
|
|
||||||
|
|
||||||
return flowSharedPref
|
|
||||||
.getStringSet(prefKey, defaultSet)
|
|
||||||
.map(
|
|
||||||
mapper = { it -> it.map { DashboardItem.HiddenAttendanceTile.valueOf(it) }.toSet() },
|
|
||||||
reverse = { it -> it.map { it.name }.toSet() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val selectedDashboardTilesPreference: Preference<Set<String>>
|
private val selectedDashboardTilesPreference: Preference<Set<String>>
|
||||||
get() {
|
get() {
|
||||||
val defaultSet =
|
val defaultSet =
|
||||||
@ -368,19 +281,6 @@ class PreferencesRepository @Inject constructor(
|
|||||||
return flowSharedPref.getStringSet(prefKey, defaultSet)
|
return flowSharedPref.getStringSet(prefKey, defaultSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val attendancePercentagePreference: Preference<Double>
|
|
||||||
get() {
|
|
||||||
val prefKey = context.getString(R.string.pref_key_attendance_percentage)
|
|
||||||
val defaultValue = context.resources.getString(R.string.pref_default_attendance_percentage)
|
|
||||||
|
|
||||||
return flowSharedPref
|
|
||||||
.getString(prefKey, defaultValue)
|
|
||||||
.map(
|
|
||||||
mapper = { it.toDoubleOrNull() ?: NO_ATTENDANCE_VALUE },
|
|
||||||
reverse = { it.toString() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
var dismissedAdminMessageIds: List<Int>
|
var dismissedAdminMessageIds: List<Int>
|
||||||
get() = sharedPref.getStringSet(PREF_KEY_ADMIN_DISMISSED_MESSAGE_IDS, emptySet())
|
get() = sharedPref.getStringSet(PREF_KEY_ADMIN_DISMISSED_MESSAGE_IDS, emptySet())
|
||||||
.orEmpty()
|
.orEmpty()
|
||||||
@ -446,15 +346,6 @@ class PreferencesRepository @Inject constructor(
|
|||||||
get() = sharedPref.getString(PREF_KEY_INSTALLATION_ID, null).orEmpty()
|
get() = sharedPref.getString(PREF_KEY_INSTALLATION_ID, null).orEmpty()
|
||||||
private set(value) = sharedPref.edit { putString(PREF_KEY_INSTALLATION_ID, value) }
|
private set(value) = sharedPref.edit { putString(PREF_KEY_INSTALLATION_ID, value) }
|
||||||
|
|
||||||
var mapping: Mapping?
|
|
||||||
get() {
|
|
||||||
val value = sharedPref.getString("mapping", null)
|
|
||||||
return value?.let { json.decodeFromString(it) }
|
|
||||||
}
|
|
||||||
set(value) = sharedPref.edit(commit = true) {
|
|
||||||
putString("mapping", value?.let { json.encodeToString(it) })
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (installationId.isEmpty()) {
|
if (installationId.isEmpty()) {
|
||||||
installationId = UUID.randomUUID().toString()
|
installationId = UUID.randomUUID().toString()
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
|
||||||
import io.github.wulkanowy.data.db.dao.RecipientDao
|
import io.github.wulkanowy.data.db.dao.RecipientDao
|
||||||
import io.github.wulkanowy.data.db.entities.Mailbox
|
import io.github.wulkanowy.data.db.entities.*
|
||||||
import io.github.wulkanowy.data.db.entities.MailboxType
|
|
||||||
import io.github.wulkanowy.data.db.entities.Message
|
|
||||||
import io.github.wulkanowy.data.db.entities.Recipient
|
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
|
import io.github.wulkanowy.utils.init
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -17,22 +14,19 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class RecipientRepository @Inject constructor(
|
class RecipientRepository @Inject constructor(
|
||||||
private val recipientDb: RecipientDao,
|
private val recipientDb: RecipientDao,
|
||||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
private val sdk: Sdk,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val cacheKey = "recipient"
|
private val cacheKey = "recipient"
|
||||||
|
|
||||||
suspend fun refreshRecipients(student: Student, mailbox: Mailbox, type: MailboxType) {
|
suspend fun refreshRecipients(student: Student, mailbox: Mailbox, type: MailboxType) {
|
||||||
val new = wulkanowySdkFactory.create(student)
|
val new = sdk.init(student).getRecipients(mailbox.globalKey)
|
||||||
.getRecipients(mailbox.globalKey)
|
|
||||||
.mapToEntities(mailbox.globalKey)
|
.mapToEntities(mailbox.globalKey)
|
||||||
val old = recipientDb.loadAll(type, mailbox.globalKey)
|
val old = recipientDb.loadAll(type, mailbox.globalKey)
|
||||||
|
|
||||||
recipientDb.removeOldAndSaveNew(
|
recipientDb.deleteAll(old uniqueSubtract new)
|
||||||
oldItems = old uniqueSubtract new,
|
recipientDb.insertAll(new uniqueSubtract old)
|
||||||
newItems = new uniqueSubtract old,
|
|
||||||
)
|
|
||||||
|
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
|
||||||
}
|
}
|
||||||
@ -60,7 +54,7 @@ class RecipientRepository @Inject constructor(
|
|||||||
): List<Recipient> {
|
): List<Recipient> {
|
||||||
mailbox ?: return emptyList()
|
mailbox ?: return emptyList()
|
||||||
|
|
||||||
return wulkanowySdkFactory.create(student)
|
return sdk.init(student)
|
||||||
.getMessageReplayDetails(message.messageGlobalKey)
|
.getMessageReplayDetails(message.messageGlobalKey)
|
||||||
.sender
|
.sender
|
||||||
.let(::listOf)
|
.let(::listOf)
|
||||||
|
@ -1,23 +1,17 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class RecoverRepository @Inject constructor(
|
class RecoverRepository @Inject constructor(private val sdk: Sdk) {
|
||||||
private val wulkanowySdkFactory: WulkanowySdkFactory
|
|
||||||
) {
|
|
||||||
|
|
||||||
suspend fun getReCaptchaSiteKey(host: String, symbol: String): Pair<String, String> =
|
suspend fun getReCaptchaSiteKey(host: String, symbol: String): Pair<String, String> {
|
||||||
wulkanowySdkFactory.create()
|
return sdk.getPasswordResetCaptchaCode(host, symbol)
|
||||||
.getPasswordResetCaptchaCode(host, symbol)
|
}
|
||||||
|
|
||||||
suspend fun sendRecoverRequest(
|
suspend fun sendRecoverRequest(
|
||||||
url: String,
|
url: String, symbol: String, email: String, reCaptchaResponse: String
|
||||||
symbol: String,
|
): String = sdk.sendPasswordResetRequest(url, symbol, email, reCaptchaResponse)
|
||||||
email: String,
|
|
||||||
reCaptchaResponse: String
|
|
||||||
): String = wulkanowySdkFactory.create()
|
|
||||||
.sendPasswordResetRequest(url, symbol, email, reCaptchaResponse)
|
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
|
||||||
import io.github.wulkanowy.data.db.dao.SchoolAnnouncementDao
|
import io.github.wulkanowy.data.db.dao.SchoolAnnouncementDao
|
||||||
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
|
import io.github.wulkanowy.utils.init
|
||||||
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.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
@ -17,7 +18,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class SchoolAnnouncementRepository @Inject constructor(
|
class SchoolAnnouncementRepository @Inject constructor(
|
||||||
private val schoolAnnouncementDb: SchoolAnnouncementDao,
|
private val schoolAnnouncementDb: SchoolAnnouncementDao,
|
||||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
private val sdk: Sdk,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -37,27 +38,26 @@ class SchoolAnnouncementRepository @Inject constructor(
|
|||||||
it.isEmpty() || forceRefresh || isExpired
|
it.isEmpty() || forceRefresh || isExpired
|
||||||
},
|
},
|
||||||
query = {
|
query = {
|
||||||
schoolAnnouncementDb.loadAll(student.studentId)
|
schoolAnnouncementDb.loadAll(student.userLoginId)
|
||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
val sdk = wulkanowySdkFactory.create(student)
|
sdk.init(student)
|
||||||
val lastAnnouncements = sdk.getLastAnnouncements().mapToEntities(student)
|
.getDirectorInformation()
|
||||||
val directorInformation = sdk.getDirectorInformation().mapToEntities(student)
|
.mapToEntities(student)
|
||||||
lastAnnouncements + directorInformation
|
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
schoolAnnouncementDb.removeOldAndSaveNew(
|
val schoolAnnouncementsToSave = (new uniqueSubtract old).onEach {
|
||||||
oldItems = old uniqueSubtract new,
|
|
||||||
newItems = (new uniqueSubtract old).onEach {
|
|
||||||
if (notify) it.isNotified = false
|
if (notify) it.isNotified = false
|
||||||
},
|
}
|
||||||
)
|
|
||||||
|
schoolAnnouncementDb.deleteAll(old uniqueSubtract new)
|
||||||
|
schoolAnnouncementDb.insertAll(schoolAnnouncementsToSave)
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
fun getSchoolAnnouncementFromDatabase(student: Student): Flow<List<SchoolAnnouncement>> {
|
fun getSchoolAnnouncementFromDatabase(student: Student): Flow<List<SchoolAnnouncement>> {
|
||||||
return schoolAnnouncementDb.loadAll(student.studentId)
|
return schoolAnnouncementDb.loadAll(student.userLoginId)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateSchoolAnnouncement(schoolAnnouncement: List<SchoolAnnouncement>) =
|
suspend fun updateSchoolAnnouncement(schoolAnnouncement: List<SchoolAnnouncement>) =
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
|
||||||
import io.github.wulkanowy.data.db.dao.SchoolDao
|
import io.github.wulkanowy.data.db.dao.SchoolDao
|
||||||
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.mappers.mapToEntity
|
import io.github.wulkanowy.data.mappers.mapToEntity
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
|
import io.github.wulkanowy.utils.init
|
||||||
|
import io.github.wulkanowy.utils.switchSemester
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -15,7 +17,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class SchoolRepository @Inject constructor(
|
class SchoolRepository @Inject constructor(
|
||||||
private val schoolDb: SchoolDao,
|
private val schoolDb: SchoolDao,
|
||||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
private val sdk: Sdk,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -38,16 +40,17 @@ class SchoolRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { schoolDb.load(semester.studentId, semester.classId) },
|
query = { schoolDb.load(semester.studentId, semester.classId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
wulkanowySdkFactory.create(student, semester)
|
sdk.init(student)
|
||||||
|
.switchSemester(semester)
|
||||||
.getSchool()
|
.getSchool()
|
||||||
.mapToEntity(semester)
|
.mapToEntity(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
if (old != null && new != old) {
|
if (old != null && new != old) {
|
||||||
schoolDb.removeOldAndSaveNew(
|
with(schoolDb) {
|
||||||
oldItems = listOf(old),
|
deleteAll(listOf(old))
|
||||||
newItems = listOf(new)
|
insertAll(listOf(new))
|
||||||
)
|
}
|
||||||
} else if (old == null) {
|
} else if (old == null) {
|
||||||
schoolDb.insertAll(listOf(new))
|
schoolDb.insertAll(listOf(new))
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
import io.github.wulkanowy.data.api.SchoolsService
|
||||||
import io.github.wulkanowy.data.api.services.SchoolsService
|
|
||||||
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.StudentWithSemesters
|
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||||
import io.github.wulkanowy.data.pojos.IntegrityRequest
|
import io.github.wulkanowy.data.pojos.IntegrityRequest
|
||||||
import io.github.wulkanowy.data.pojos.LoginEvent
|
import io.github.wulkanowy.data.pojos.LoginEvent
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||||
import io.github.wulkanowy.utils.IntegrityHelper
|
import io.github.wulkanowy.utils.IntegrityHelper
|
||||||
import io.github.wulkanowy.utils.getCurrentOrLast
|
import io.github.wulkanowy.utils.getCurrentOrLast
|
||||||
|
import io.github.wulkanowy.utils.init
|
||||||
|
import io.github.wulkanowy.utils.switchSemester
|
||||||
import kotlinx.coroutines.withTimeout
|
import kotlinx.coroutines.withTimeout
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
@ -21,7 +23,7 @@ import kotlin.time.Duration.Companion.seconds
|
|||||||
class SchoolsRepository @Inject constructor(
|
class SchoolsRepository @Inject constructor(
|
||||||
private val integrityHelper: IntegrityHelper,
|
private val integrityHelper: IntegrityHelper,
|
||||||
private val schoolsService: SchoolsService,
|
private val schoolsService: SchoolsService,
|
||||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
private val sdk: Sdk,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun logSchoolLogin(loginData: LoginData, students: List<StudentWithSemesters>) {
|
suspend fun logSchoolLogin(loginData: LoginData, students: List<StudentWithSemesters>) {
|
||||||
@ -38,9 +40,10 @@ class SchoolsRepository @Inject constructor(
|
|||||||
private suspend fun logLogin(loginData: LoginData, student: Student, semester: Semester) {
|
private suspend fun logLogin(loginData: LoginData, student: Student, semester: Semester) {
|
||||||
val requestId = UUID.randomUUID().toString()
|
val requestId = UUID.randomUUID().toString()
|
||||||
val token = integrityHelper.getIntegrityToken(requestId) ?: return
|
val token = integrityHelper.getIntegrityToken(requestId) ?: return
|
||||||
val updatedStudent = student.copy(password = loginData.password)
|
|
||||||
|
|
||||||
val schoolInfo = wulkanowySdkFactory.create(updatedStudent, semester)
|
val schoolInfo = sdk
|
||||||
|
.init(student.copy(password = loginData.password))
|
||||||
|
.switchSemester(semester)
|
||||||
.getSchool()
|
.getSchool()
|
||||||
|
|
||||||
schoolsService.logLoginEvent(
|
schoolsService.logLoginEvent(
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
|
||||||
import io.github.wulkanowy.data.db.dao.SemesterDao
|
import io.github.wulkanowy.data.db.dao.SemesterDao
|
||||||
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.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.DispatchersProvider
|
import io.github.wulkanowy.utils.*
|
||||||
import io.github.wulkanowy.utils.getCurrentOrLast
|
|
||||||
import io.github.wulkanowy.utils.isCurrent
|
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -18,8 +14,8 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class SemesterRepository @Inject constructor(
|
class SemesterRepository @Inject constructor(
|
||||||
private val semesterDb: SemesterDao,
|
private val semesterDb: SemesterDao,
|
||||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
private val sdk: Sdk,
|
||||||
private val dispatchers: DispatchersProvider,
|
private val dispatchers: DispatchersProvider
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun getSemesters(
|
suspend fun getSemesters(
|
||||||
@ -49,7 +45,6 @@ class SemesterRepository @Inject constructor(
|
|||||||
0 == it.diaryId && 0 == it.kindergartenDiaryId
|
0 == it.diaryId && 0 == it.kindergartenDiaryId
|
||||||
} == true
|
} == true
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,20 +55,12 @@ class SemesterRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun refreshSemesters(student: Student) {
|
private suspend fun refreshSemesters(student: Student) {
|
||||||
val new = wulkanowySdkFactory.create(student)
|
val new = sdk.init(student).getSemesters().mapToEntities(student.studentId)
|
||||||
.getSemesters()
|
if (new.isEmpty()) return Timber.i("Empty semester list!")
|
||||||
.mapToEntities(student.studentId)
|
|
||||||
|
|
||||||
if (new.isEmpty()) {
|
|
||||||
Timber.i("Empty semester list from SDK!")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val old = semesterDb.loadAll(student.studentId, student.classId)
|
val old = semesterDb.loadAll(student.studentId, student.classId)
|
||||||
semesterDb.removeOldAndSaveNew(
|
semesterDb.deleteAll(old.uniqueSubtract(new))
|
||||||
oldItems = old uniqueSubtract new,
|
semesterDb.insertSemesters(new.uniqueSubtract(old))
|
||||||
newItems = new uniqueSubtract old,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) =
|
suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) =
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
|
||||||
import io.github.wulkanowy.data.db.dao.StudentInfoDao
|
import io.github.wulkanowy.data.db.dao.StudentInfoDao
|
||||||
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.mappers.mapToEntity
|
import io.github.wulkanowy.data.mappers.mapToEntity
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
|
import io.github.wulkanowy.utils.init
|
||||||
|
import io.github.wulkanowy.utils.switchSemester
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -13,7 +15,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class StudentInfoRepository @Inject constructor(
|
class StudentInfoRepository @Inject constructor(
|
||||||
private val studentInfoDao: StudentInfoDao,
|
private val studentInfoDao: StudentInfoDao,
|
||||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
private val sdk: Sdk
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val saveFetchResultMutex = Mutex()
|
private val saveFetchResultMutex = Mutex()
|
||||||
@ -28,16 +30,16 @@ class StudentInfoRepository @Inject constructor(
|
|||||||
shouldFetch = { it == null || forceRefresh },
|
shouldFetch = { it == null || forceRefresh },
|
||||||
query = { studentInfoDao.loadStudentInfo(student.studentId) },
|
query = { studentInfoDao.loadStudentInfo(student.studentId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
wulkanowySdkFactory.create(student, semester)
|
sdk.init(student)
|
||||||
.getStudentInfo()
|
.switchSemester(semester)
|
||||||
.mapToEntity(semester)
|
.getStudentInfo().mapToEntity(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
if (old != null && new != old) {
|
if (old != null && new != old) {
|
||||||
studentInfoDao.removeOldAndSaveNew(
|
with(studentInfoDao) {
|
||||||
oldItems = listOf(old),
|
deleteAll(listOf(old))
|
||||||
newItems = listOf(new),
|
insertAll(listOf(new))
|
||||||
)
|
}
|
||||||
} else if (old == null) {
|
} else if (old == null) {
|
||||||
studentInfoDao.insertAll(listOf(new))
|
studentInfoDao.insertAll(listOf(new))
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,23 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import androidx.room.withTransaction
|
import androidx.room.withTransaction
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
|
||||||
import io.github.wulkanowy.data.db.AppDatabase
|
import io.github.wulkanowy.data.db.AppDatabase
|
||||||
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.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.StudentIsAuthorized
|
|
||||||
import io.github.wulkanowy.data.db.entities.StudentName
|
import io.github.wulkanowy.data.db.entities.StudentName
|
||||||
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
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.mapToPojo
|
import io.github.wulkanowy.data.mappers.mapToPojo
|
||||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.DispatchersProvider
|
import io.github.wulkanowy.utils.DispatchersProvider
|
||||||
|
import io.github.wulkanowy.utils.init
|
||||||
import io.github.wulkanowy.utils.security.Scrambler
|
import io.github.wulkanowy.utils.security.Scrambler
|
||||||
|
import io.github.wulkanowy.utils.switchSemester
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@ -28,7 +26,7 @@ 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 wulkanowySdkFactory: WulkanowySdkFactory,
|
private val sdk: Sdk,
|
||||||
private val appDatabase: AppDatabase,
|
private val appDatabase: AppDatabase,
|
||||||
private val scrambler: Scrambler,
|
private val scrambler: Scrambler,
|
||||||
) {
|
) {
|
||||||
@ -39,10 +37,9 @@ class StudentRepository @Inject constructor(
|
|||||||
pin: String,
|
pin: String,
|
||||||
symbol: String,
|
symbol: String,
|
||||||
token: String
|
token: String
|
||||||
): RegisterUser = wulkanowySdkFactory.create()
|
): RegisterUser = sdk
|
||||||
.getStudentsFromHebe(token, pin, symbol, "")
|
.getStudentsFromHebe(token, pin, symbol, "")
|
||||||
.mapToPojo(null)
|
.mapToPojo(null)
|
||||||
.also { it.logErrors() }
|
|
||||||
|
|
||||||
suspend fun getUserSubjectsFromScrapper(
|
suspend fun getUserSubjectsFromScrapper(
|
||||||
email: String,
|
email: String,
|
||||||
@ -50,20 +47,18 @@ class StudentRepository @Inject constructor(
|
|||||||
scrapperBaseUrl: String,
|
scrapperBaseUrl: String,
|
||||||
domainSuffix: String,
|
domainSuffix: String,
|
||||||
symbol: String
|
symbol: String
|
||||||
): RegisterUser = wulkanowySdkFactory.create()
|
): RegisterUser = sdk
|
||||||
.getUserSubjectsFromScrapper(email, password, scrapperBaseUrl, domainSuffix, symbol)
|
.getUserSubjectsFromScrapper(email, password, scrapperBaseUrl, domainSuffix, symbol)
|
||||||
.mapToPojo(password)
|
.mapToPojo(password)
|
||||||
.also { it.logErrors() }
|
|
||||||
|
|
||||||
suspend fun getStudentsHybrid(
|
suspend fun getStudentsHybrid(
|
||||||
email: String,
|
email: String,
|
||||||
password: String,
|
password: String,
|
||||||
scrapperBaseUrl: String,
|
scrapperBaseUrl: String,
|
||||||
symbol: String
|
symbol: String
|
||||||
): RegisterUser = wulkanowySdkFactory.create()
|
): RegisterUser = sdk
|
||||||
.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol)
|
.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol)
|
||||||
.mapToPojo(password)
|
.mapToPojo(password)
|
||||||
.also { it.logErrors() }
|
|
||||||
|
|
||||||
suspend fun getSavedStudents(decryptPass: Boolean = true): List<StudentWithSemesters> {
|
suspend fun getSavedStudents(decryptPass: Boolean = true): List<StudentWithSemesters> {
|
||||||
return studentDb.loadStudentsWithSemesters().map { (student, semesters) ->
|
return studentDb.loadStudentsWithSemesters().map { (student, semesters) ->
|
||||||
@ -105,46 +100,6 @@ class StudentRepository @Inject constructor(
|
|||||||
return student
|
return student
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateCurrentStudentAuthStatus() {
|
|
||||||
Timber.i("Check isAuthorized: started")
|
|
||||||
val student = getCurrentStudent()
|
|
||||||
if (student.isAuthorized) {
|
|
||||||
Timber.i("Check isAuthorized: already authorized")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val initializedSdk = wulkanowySdkFactory.create(student)
|
|
||||||
val newCurrentStudent = runCatching { initializedSdk.getCurrentStudent() }
|
|
||||||
.onFailure { Timber.e(it, "Check isAuthorized: error occurred") }
|
|
||||||
.getOrNull()
|
|
||||||
|
|
||||||
if (newCurrentStudent == null) {
|
|
||||||
Timber.d("Check isAuthorized: current user is null")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val currentStudentSemesters = semesterDb.loadAll(student.studentId, student.classId)
|
|
||||||
if (currentStudentSemesters.isEmpty()) {
|
|
||||||
Timber.d("Check isAuthorized: apply empty semesters workaround")
|
|
||||||
semesterDb.insertSemesters(
|
|
||||||
items = newCurrentStudent.semesters.mapToEntities(student.studentId),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!newCurrentStudent.isAuthorized) {
|
|
||||||
Timber.i("Check isAuthorized: authorization required")
|
|
||||||
throw NoAuthorizationException()
|
|
||||||
}
|
|
||||||
|
|
||||||
val studentIsAuthorized = StudentIsAuthorized(
|
|
||||||
id = student.id,
|
|
||||||
isAuthorized = true
|
|
||||||
)
|
|
||||||
|
|
||||||
Timber.i("Check isAuthorized: already authorized, update local status")
|
|
||||||
studentDb.update(studentIsAuthorized)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun getCurrentStudent(decryptPass: Boolean = true): Student {
|
suspend fun getCurrentStudent(decryptPass: Boolean = true): Student {
|
||||||
val student = studentDb.loadCurrent() ?: throw NoCurrentStudentException()
|
val student = studentDb.loadCurrent() ?: throw NoCurrentStudentException()
|
||||||
|
|
||||||
@ -194,24 +149,20 @@ class StudentRepository @Inject constructor(
|
|||||||
.distinctBy { it.student.studentName }.size == 1
|
.distinctBy { it.student.studentName }.size == 1
|
||||||
|
|
||||||
suspend fun authorizePermission(student: Student, semester: Semester, pesel: String) =
|
suspend fun authorizePermission(student: Student, semester: Semester, pesel: String) =
|
||||||
wulkanowySdkFactory.create(student, semester)
|
sdk.init(student)
|
||||||
|
.switchSemester(semester)
|
||||||
.authorizePermission(pesel)
|
.authorizePermission(pesel)
|
||||||
|
|
||||||
suspend fun refreshStudentAfterAuthorize(student: Student, semester: Semester) {
|
suspend fun refreshStudentName(student: Student, semester: Semester) {
|
||||||
val wulkanowySdk = wulkanowySdkFactory.create(student, semester)
|
val newCurrentApiStudent = sdk.init(student)
|
||||||
val newCurrentApiStudent = runCatching { wulkanowySdk.getCurrentStudent() }
|
.switchSemester(semester)
|
||||||
.onFailure { Timber.e(it, "Can't find student with id ${student.studentId}") }
|
.getCurrentStudent() ?: return
|
||||||
.getOrNull() ?: return
|
|
||||||
|
|
||||||
val studentName = StudentName(
|
val studentName = StudentName(
|
||||||
studentName = "${newCurrentApiStudent.studentName} ${newCurrentApiStudent.studentSurname}"
|
studentName = "${newCurrentApiStudent.studentName} ${newCurrentApiStudent.studentSurname}"
|
||||||
).apply { id = student.id }
|
).apply { id = student.id }
|
||||||
|
|
||||||
studentDb.update(studentName)
|
studentDb.update(studentName)
|
||||||
semesterDb.removeOldAndSaveNew(
|
|
||||||
oldItems = semesterDb.loadAll(student.studentId, semester.classId),
|
|
||||||
newItems = newCurrentApiStudent.semesters.mapToEntities(newCurrentApiStudent.studentId)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteStudentsAssociatedWithAccount(student: Student) {
|
suspend fun deleteStudentsAssociatedWithAccount(student: Student) {
|
||||||
@ -224,18 +175,4 @@ class StudentRepository @Inject constructor(
|
|||||||
appDatabase.clearAllTables()
|
appDatabase.clearAllTables()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun RegisterUser.logErrors() {
|
|
||||||
val symbolsErrors = symbols.filter { it.error != null }
|
|
||||||
.map { it.error }
|
|
||||||
val unitsErrors = symbols.flatMap { it.schools }
|
|
||||||
.filter { it.error != null }
|
|
||||||
.map { it.error }
|
|
||||||
|
|
||||||
(symbolsErrors + unitsErrors).forEach { error ->
|
|
||||||
Timber.e(error, "Error occurred while fetching students")
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NoAuthorizationException : Exception()
|
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
|
||||||
import io.github.wulkanowy.data.db.dao.SubjectDao
|
import io.github.wulkanowy.data.db.dao.SubjectDao
|
||||||
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.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
|
import io.github.wulkanowy.utils.init
|
||||||
|
import io.github.wulkanowy.utils.switchSemester
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -16,7 +18,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class SubjectRepository @Inject constructor(
|
class SubjectRepository @Inject constructor(
|
||||||
private val subjectDao: SubjectDao,
|
private val subjectDao: SubjectDao,
|
||||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
private val sdk: Sdk,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -37,15 +39,15 @@ class SubjectRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { subjectDao.loadAll(semester.diaryId, semester.studentId) },
|
query = { subjectDao.loadAll(semester.diaryId, semester.studentId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
wulkanowySdkFactory.create(student, semester)
|
sdk.init(student)
|
||||||
|
.switchSemester(semester)
|
||||||
.getSubjects()
|
.getSubjects()
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
subjectDao.removeOldAndSaveNew(
|
subjectDao.deleteAll(old uniqueSubtract new)
|
||||||
oldItems = old uniqueSubtract new,
|
subjectDao.insertAll(new uniqueSubtract old)
|
||||||
newItems = new uniqueSubtract old
|
|
||||||
)
|
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
|
||||||
import io.github.wulkanowy.data.db.dao.TeacherDao
|
import io.github.wulkanowy.data.db.dao.TeacherDao
|
||||||
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.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
|
import io.github.wulkanowy.utils.init
|
||||||
|
import io.github.wulkanowy.utils.switchSemester
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -16,7 +18,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class TeacherRepository @Inject constructor(
|
class TeacherRepository @Inject constructor(
|
||||||
private val teacherDb: TeacherDao,
|
private val teacherDb: TeacherDao,
|
||||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
private val sdk: Sdk,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -37,15 +39,15 @@ class TeacherRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { teacherDb.loadAll(semester.studentId, semester.classId) },
|
query = { teacherDb.loadAll(semester.studentId, semester.classId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
wulkanowySdkFactory.create(student, semester)
|
sdk.init(student)
|
||||||
|
.switchSemester(semester)
|
||||||
.getTeachers()
|
.getTeachers()
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
teacherDb.removeOldAndSaveNew(
|
teacherDb.deleteAll(old uniqueSubtract new)
|
||||||
oldItems = old uniqueSubtract new,
|
teacherDb.insertAll(new uniqueSubtract old)
|
||||||
newItems = new uniqueSubtract old,
|
|
||||||
)
|
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
|
||||||
import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao
|
import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao
|
||||||
import io.github.wulkanowy.data.db.dao.TimetableDao
|
import io.github.wulkanowy.data.db.dao.TimetableDao
|
||||||
import io.github.wulkanowy.data.db.dao.TimetableHeaderDao
|
import io.github.wulkanowy.data.db.dao.TimetableHeaderDao
|
||||||
@ -12,13 +11,14 @@ import io.github.wulkanowy.data.db.entities.TimetableHeader
|
|||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.data.pojos.TimetableFull
|
import io.github.wulkanowy.data.pojos.TimetableFull
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
|
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
|
||||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider
|
|
||||||
import io.github.wulkanowy.utils.AppWidgetUpdater
|
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
|
import io.github.wulkanowy.utils.init
|
||||||
import io.github.wulkanowy.utils.monday
|
import io.github.wulkanowy.utils.monday
|
||||||
import io.github.wulkanowy.utils.sunday
|
import io.github.wulkanowy.utils.sunday
|
||||||
|
import io.github.wulkanowy.utils.switchSemester
|
||||||
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.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
@ -28,16 +28,14 @@ import java.time.LocalDate
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class TimetableRepository @Inject constructor(
|
class TimetableRepository @Inject constructor(
|
||||||
private val timetableDb: TimetableDao,
|
private val timetableDb: TimetableDao,
|
||||||
private val timetableAdditionalDb: TimetableAdditionalDao,
|
private val timetableAdditionalDb: TimetableAdditionalDao,
|
||||||
private val timetableHeaderDb: TimetableHeaderDao,
|
private val timetableHeaderDb: TimetableHeaderDao,
|
||||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
private val sdk: Sdk,
|
||||||
private val schedulerHelper: TimetableNotificationSchedulerHelper,
|
private val schedulerHelper: TimetableNotificationSchedulerHelper,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
private val appWidgetUpdater: AppWidgetUpdater,
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val saveFetchResultMutex = Mutex()
|
private val saveFetchResultMutex = Mutex()
|
||||||
@ -56,8 +54,7 @@ class TimetableRepository @Inject constructor(
|
|||||||
forceRefresh: Boolean,
|
forceRefresh: Boolean,
|
||||||
refreshAdditional: Boolean = false,
|
refreshAdditional: Boolean = false,
|
||||||
notify: Boolean = false,
|
notify: Boolean = false,
|
||||||
timetableType: TimetableType = TimetableType.NORMAL,
|
timetableType: TimetableType = TimetableType.NORMAL
|
||||||
isFromAppWidget: Boolean = false
|
|
||||||
) = networkBoundResource(
|
) = networkBoundResource(
|
||||||
mutex = saveFetchResultMutex,
|
mutex = saveFetchResultMutex,
|
||||||
isResultEmpty = {
|
isResultEmpty = {
|
||||||
@ -77,7 +74,8 @@ class TimetableRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { getFullTimetableFromDatabase(student, semester, start, end) },
|
query = { getFullTimetableFromDatabase(student, semester, start, end) },
|
||||||
fetch = {
|
fetch = {
|
||||||
val timetableFull = wulkanowySdkFactory.create(student, semester)
|
val timetableFull = sdk.init(student)
|
||||||
|
.switchSemester(semester)
|
||||||
.getTimetable(start.monday, end.sunday)
|
.getTimetable(start.monday, end.sunday)
|
||||||
|
|
||||||
timetableFull.mapToEntities(semester)
|
timetableFull.mapToEntities(semester)
|
||||||
@ -88,9 +86,6 @@ class TimetableRepository @Inject constructor(
|
|||||||
refreshDayHeaders(timetableOld.headers, timetableNew.headers)
|
refreshDayHeaders(timetableOld.headers, timetableNew.headers)
|
||||||
|
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
||||||
if (!isFromAppWidget) {
|
|
||||||
appWidgetUpdater.updateAllAppWidgetsByProvider(TimetableWidgetProvider::class)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
filterResult = { (timetable, additional, headers) ->
|
filterResult = { (timetable, additional, headers) ->
|
||||||
TimetableFull(
|
TimetableFull(
|
||||||
@ -159,10 +154,8 @@ class TimetableRepository @Inject constructor(
|
|||||||
new.apply { if (notify) isNotified = false }
|
new.apply { if (notify) isNotified = false }
|
||||||
}
|
}
|
||||||
|
|
||||||
timetableDb.removeOldAndSaveNew(
|
timetableDb.deleteAll(lessonsToRemove)
|
||||||
oldItems = lessonsToRemove,
|
timetableDb.insertAll(lessonsToAdd)
|
||||||
newItems = lessonsToAdd,
|
|
||||||
)
|
|
||||||
|
|
||||||
schedulerHelper.cancelScheduled(lessonsToRemove, student)
|
schedulerHelper.cancelScheduled(lessonsToRemove, student)
|
||||||
schedulerHelper.scheduleNotifications(lessonsToAdd, student)
|
schedulerHelper.scheduleNotifications(lessonsToAdd, student)
|
||||||
@ -173,17 +166,13 @@ class TimetableRepository @Inject constructor(
|
|||||||
new: List<TimetableAdditional>
|
new: List<TimetableAdditional>
|
||||||
) {
|
) {
|
||||||
val oldFiltered = old.filter { !it.isAddedByUser }
|
val oldFiltered = old.filter { !it.isAddedByUser }
|
||||||
timetableAdditionalDb.removeOldAndSaveNew(
|
timetableAdditionalDb.deleteAll(oldFiltered uniqueSubtract new)
|
||||||
oldItems = oldFiltered uniqueSubtract new,
|
timetableAdditionalDb.insertAll(new uniqueSubtract old)
|
||||||
newItems = new uniqueSubtract old,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun refreshDayHeaders(old: List<TimetableHeader>, new: List<TimetableHeader>) {
|
private suspend fun refreshDayHeaders(old: List<TimetableHeader>, new: List<TimetableHeader>) {
|
||||||
timetableHeaderDb.removeOldAndSaveNew(
|
timetableHeaderDb.deleteAll(old uniqueSubtract new)
|
||||||
oldItems = old uniqueSubtract new,
|
timetableHeaderDb.insertAll(new uniqueSubtract old)
|
||||||
newItems = new uniqueSubtract old,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLastRefreshTimestamp(semester: Semester, start: LocalDate, end: LocalDate): Instant {
|
fun getLastRefreshTimestamp(semester: Semester, start: LocalDate, end: LocalDate): Instant {
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
|
||||||
|
|
||||||
import io.github.wulkanowy.data.Resource
|
|
||||||
import io.github.wulkanowy.data.api.models.Mapping
|
|
||||||
import io.github.wulkanowy.data.api.services.WulkanowyService
|
|
||||||
import io.github.wulkanowy.data.db.dao.AdminMessageDao
|
|
||||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.filterNot
|
|
||||||
import kotlinx.coroutines.sync.Mutex
|
|
||||||
import timber.log.Timber
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class WulkanowyRepository @Inject constructor(
|
|
||||||
private val wulkanowyService: WulkanowyService,
|
|
||||||
private val adminMessageDao: AdminMessageDao,
|
|
||||||
private val preferencesRepository: PreferencesRepository,
|
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
|
||||||
) {
|
|
||||||
|
|
||||||
private val saveFetchResultMutex = Mutex()
|
|
||||||
|
|
||||||
private val cacheKey = "mapping_refresh_key"
|
|
||||||
|
|
||||||
fun getAdminMessages(): Flow<Resource<List<AdminMessage>>> =
|
|
||||||
networkBoundResource(
|
|
||||||
mutex = saveFetchResultMutex,
|
|
||||||
isResultEmpty = { false },
|
|
||||||
query = { adminMessageDao.loadAll() },
|
|
||||||
fetch = { wulkanowyService.getAdminMessages() },
|
|
||||||
shouldFetch = { true },
|
|
||||||
saveFetchResult = { oldItems, newItems ->
|
|
||||||
adminMessageDao.removeOldAndSaveNew(oldItems, newItems)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.filterNot { it is Resource.Intermediate }
|
|
||||||
|
|
||||||
suspend fun getMapping(): Mapping? {
|
|
||||||
var savedMapping = preferencesRepository.mapping
|
|
||||||
|
|
||||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
|
||||||
key = getRefreshKey(cacheKey)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (savedMapping == null || isExpired) {
|
|
||||||
fetchMapping()
|
|
||||||
savedMapping = preferencesRepository.mapping
|
|
||||||
}
|
|
||||||
|
|
||||||
return savedMapping
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun fetchMapping() {
|
|
||||||
runCatching { wulkanowyService.getMapping() }
|
|
||||||
.onFailure { Timber.e(it) }
|
|
||||||
.onSuccess {
|
|
||||||
preferencesRepository.mapping = it
|
|
||||||
refreshHelper.updateLastRefreshTimestamp(cacheKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
package io.github.wulkanowy.data.serializers
|
|
||||||
|
|
||||||
import io.github.wulkanowy.data.enums.MessageType
|
|
||||||
import kotlinx.serialization.ExperimentalSerializationApi
|
|
||||||
import kotlinx.serialization.KSerializer
|
|
||||||
import kotlinx.serialization.builtins.ListSerializer
|
|
||||||
import kotlinx.serialization.builtins.serializer
|
|
||||||
import kotlinx.serialization.encoding.Decoder
|
|
||||||
import kotlinx.serialization.encoding.Encoder
|
|
||||||
|
|
||||||
@OptIn(ExperimentalSerializationApi::class)
|
|
||||||
object SafeMessageTypeEnumListSerializer : KSerializer<List<MessageType>> {
|
|
||||||
|
|
||||||
private val serializer = ListSerializer(String.serializer())
|
|
||||||
|
|
||||||
override val descriptor = serializer.descriptor
|
|
||||||
|
|
||||||
override fun serialize(encoder: Encoder, value: List<MessageType>) {
|
|
||||||
encoder.encodeNotNullMark()
|
|
||||||
serializer.serialize(encoder, value.map { it.name })
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun deserialize(decoder: Decoder): List<MessageType> =
|
|
||||||
serializer.deserialize(decoder).mapNotNull { enumName ->
|
|
||||||
MessageType.entries.find { it.name == enumName }
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,14 +5,14 @@ import io.github.wulkanowy.data.db.entities.AdminMessage
|
|||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.enums.MessageType
|
import io.github.wulkanowy.data.enums.MessageType
|
||||||
import io.github.wulkanowy.data.mapResourceData
|
import io.github.wulkanowy.data.mapResourceData
|
||||||
|
import io.github.wulkanowy.data.repositories.AdminMessageRepository
|
||||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||||
import io.github.wulkanowy.data.repositories.WulkanowyRepository
|
|
||||||
import io.github.wulkanowy.utils.AppInfo
|
import io.github.wulkanowy.utils.AppInfo
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class GetAppropriateAdminMessageUseCase @Inject constructor(
|
class GetAppropriateAdminMessageUseCase @Inject constructor(
|
||||||
private val wulkanowyRepository: WulkanowyRepository,
|
private val adminMessageRepository: AdminMessageRepository,
|
||||||
private val preferencesRepository: PreferencesRepository,
|
private val preferencesRepository: PreferencesRepository,
|
||||||
private val appInfo: AppInfo
|
private val appInfo: AppInfo
|
||||||
) {
|
) {
|
||||||
@ -22,7 +22,7 @@ class GetAppropriateAdminMessageUseCase @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
operator fun invoke(scrapperBaseUrl: String, type: MessageType): Flow<Resource<AdminMessage?>> {
|
operator fun invoke(scrapperBaseUrl: String, type: MessageType): Flow<Resource<AdminMessage?>> {
|
||||||
return wulkanowyRepository.getAdminMessages().mapResourceData { adminMessages ->
|
return adminMessageRepository.getAdminMessages().mapResourceData { adminMessages ->
|
||||||
adminMessages
|
adminMessages
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.filter { it.isNotDismissed() }
|
.filter { it.isNotDismissed() }
|
||||||
|
@ -1,106 +0,0 @@
|
|||||||
package io.github.wulkanowy.domain.attendance
|
|
||||||
|
|
||||||
import io.github.wulkanowy.data.*
|
|
||||||
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
|
||||||
import io.github.wulkanowy.data.db.entities.Subject
|
|
||||||
import io.github.wulkanowy.data.enums.AttendanceCalculatorSortingMode
|
|
||||||
import io.github.wulkanowy.data.enums.AttendanceCalculatorSortingMode.*
|
|
||||||
import io.github.wulkanowy.data.pojos.AttendanceData
|
|
||||||
import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository
|
|
||||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
|
||||||
import io.github.wulkanowy.data.repositories.SubjectRepository
|
|
||||||
import io.github.wulkanowy.utils.allAbsences
|
|
||||||
import io.github.wulkanowy.utils.allPresences
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import javax.inject.Inject
|
|
||||||
import kotlin.math.ceil
|
|
||||||
import kotlin.math.floor
|
|
||||||
|
|
||||||
class GetAttendanceCalculatorDataUseCase @Inject constructor(
|
|
||||||
private val subjectRepository: SubjectRepository,
|
|
||||||
private val attendanceSummaryRepository: AttendanceSummaryRepository,
|
|
||||||
private val preferencesRepository: PreferencesRepository,
|
|
||||||
) {
|
|
||||||
|
|
||||||
operator fun invoke(
|
|
||||||
student: Student,
|
|
||||||
semester: Semester,
|
|
||||||
forceRefresh: Boolean,
|
|
||||||
): Flow<Resource<List<AttendanceData>>> =
|
|
||||||
subjectRepository.getSubjects(student, semester, forceRefresh)
|
|
||||||
.mapResourceData { subjects -> subjects.sortedBy(Subject::name) }
|
|
||||||
.combineWithResourceData(preferencesRepository.targetAttendanceFlow, ::Pair)
|
|
||||||
.flatMapResourceData { (subjects, targetFreq) ->
|
|
||||||
combineResourceFlows(subjects.map { subject ->
|
|
||||||
attendanceSummaryRepository.getAttendanceSummary(
|
|
||||||
student = student,
|
|
||||||
semester = semester,
|
|
||||||
subjectId = subject.realId,
|
|
||||||
forceRefresh = forceRefresh
|
|
||||||
).mapResourceData { summaries ->
|
|
||||||
summaries.toAttendanceData(subject.name, targetFreq)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// Every individual combined flow causes separate network requests to update data.
|
|
||||||
// When there is N child flows, they can cause up to N-1 items to be emitted. Since all
|
|
||||||
// requests are usually completed in less than 5s, there is no need to emit multiple
|
|
||||||
// intermediates that will be visible for barely any time.
|
|
||||||
.debounceIntermediates()
|
|
||||||
}
|
|
||||||
.combineWithResourceData(preferencesRepository.attendanceCalculatorShowEmptySubjects) { attendanceDataList, showEmptySubjects ->
|
|
||||||
attendanceDataList.filter { it.total != 0 || showEmptySubjects }
|
|
||||||
}
|
|
||||||
.combineWithResourceData(preferencesRepository.attendanceCalculatorSortingModeFlow, List<AttendanceData>::sortedBy)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun List<AttendanceSummary>.toAttendanceData(subjectName: String, targetFreq: Int): AttendanceData {
|
|
||||||
val presences = sumOf { it.allPresences }
|
|
||||||
val absences = sumOf { it.allAbsences }
|
|
||||||
return AttendanceData(
|
|
||||||
subjectName = subjectName,
|
|
||||||
lessonBalance = calcLessonBalance(
|
|
||||||
targetFreq.toDouble() / 100, presences, absences
|
|
||||||
),
|
|
||||||
presences = presences,
|
|
||||||
absences = absences,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun calcLessonBalance(targetFreq: Double, presences: Int, absences: Int): Int {
|
|
||||||
val total = presences + absences
|
|
||||||
// The `+ 1` is to avoid false positives in close cases. Eg.:
|
|
||||||
// target frequency 99%, 1 presence. Without the `+ 1` this would be reported shown as
|
|
||||||
// a positive balance of +1, however that is not actually true as skipping one class
|
|
||||||
// would make it so that the balance would actually be negative (-98). The `+ 1`
|
|
||||||
// fixes this and makes sure that in situations like these, it's not reporting incorrect
|
|
||||||
// balances
|
|
||||||
return when {
|
|
||||||
presences / (total + 1f) >= targetFreq -> calcMissingAbsences(
|
|
||||||
targetFreq, absences, presences
|
|
||||||
)
|
|
||||||
presences / (total + 0f) < targetFreq -> -calcMissingPresences(
|
|
||||||
targetFreq, absences, presences
|
|
||||||
)
|
|
||||||
else -> 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun calcMissingPresences(targetFreq: Double, absences: Int, presences: Int) =
|
|
||||||
calcMinRequiredPresencesFor(targetFreq, absences) - presences
|
|
||||||
|
|
||||||
private fun calcMinRequiredPresencesFor(targetFreq: Double, absences: Int) =
|
|
||||||
ceil((targetFreq / (1 - targetFreq)) * absences).toInt()
|
|
||||||
|
|
||||||
private fun calcMissingAbsences(targetFreq: Double, absences: Int, presences: Int) =
|
|
||||||
calcMinRequiredAbsencesFor(targetFreq, presences) - absences
|
|
||||||
|
|
||||||
private fun calcMinRequiredAbsencesFor(targetFreq: Double, presences: Int) =
|
|
||||||
floor((presences * (1 - targetFreq)) / targetFreq).toInt()
|
|
||||||
|
|
||||||
private fun List<AttendanceData>.sortedBy(mode: AttendanceCalculatorSortingMode) = when (mode) {
|
|
||||||
ALPHABETIC -> sortedBy(AttendanceData::subjectName)
|
|
||||||
ATTENDANCE -> sortedByDescending(AttendanceData::presencePercentage)
|
|
||||||
LESSON_BALANCE -> sortedBy(AttendanceData::lessonBalance)
|
|
||||||
}
|
|
@ -59,7 +59,7 @@ class GetMailboxByStudentUseCase @Inject constructor(
|
|||||||
private fun String.getUnauthorizedVersion(): String {
|
private fun String.getUnauthorizedVersion(): String {
|
||||||
return normalizeStudentName().split(" ")
|
return normalizeStudentName().split(" ")
|
||||||
.joinToString(" ") {
|
.joinToString(" ") {
|
||||||
it.firstOrNull()?.toString().orEmpty() + "*".repeat((it.length - 1).coerceAtLeast(0))
|
it.first() + "*".repeat(it.length - 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ import io.github.wulkanowy.data.repositories.SemesterRepository
|
|||||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||||
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
|
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
|
||||||
import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException
|
import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException
|
||||||
import io.github.wulkanowy.sdk.scrapper.exception.FeatureUnavailableException
|
|
||||||
import io.github.wulkanowy.services.sync.channels.DebugChannel
|
import io.github.wulkanowy.services.sync.channels.DebugChannel
|
||||||
import io.github.wulkanowy.services.sync.works.Work
|
import io.github.wulkanowy.services.sync.works.Work
|
||||||
import io.github.wulkanowy.utils.DispatchersProvider
|
import io.github.wulkanowy.utils.DispatchersProvider
|
||||||
@ -49,7 +48,6 @@ class SyncWorker @AssistedInject constructor(
|
|||||||
val semester = semesterRepository.getCurrentSemester(student, true)
|
val semester = semesterRepository.getCurrentSemester(student, true)
|
||||||
student to semester
|
student to semester
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Timber.e(e)
|
|
||||||
return@withContext getResultFromErrors(listOf(e))
|
return@withContext getResultFromErrors(listOf(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +59,7 @@ class SyncWorker @AssistedInject constructor(
|
|||||||
null
|
null
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Timber.w("${work::class.java.simpleName} result: An exception ${e.message} occurred")
|
Timber.w("${work::class.java.simpleName} result: An exception ${e.message} occurred")
|
||||||
if (e is FeatureDisabledException || e is FeatureNotAvailableException || e is FeatureUnavailableException) {
|
if (e is FeatureDisabledException || e is FeatureNotAvailableException) {
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
|
@ -19,9 +19,7 @@ class NewGradeNotification @Inject constructor(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun notifyDetails(items: List<Grade>, student: Student) {
|
suspend fun notifyDetails(items: List<Grade>, student: Student) {
|
||||||
val notificationDataList = items
|
val notificationDataList = items.map {
|
||||||
.filter { !it.isNotified }
|
|
||||||
.map {
|
|
||||||
NotificationData(
|
NotificationData(
|
||||||
title = context.getPlural(R.plurals.grade_new_items, 1),
|
title = context.getPlural(R.plurals.grade_new_items, 1),
|
||||||
content = buildString {
|
content = buildString {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package io.github.wulkanowy.ui.base
|
package io.github.wulkanowy.ui.base
|
||||||
|
|
||||||
import android.app.ActivityManager
|
import android.app.ActivityManager
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
@ -18,8 +17,6 @@ import io.github.wulkanowy.utils.FragmentLifecycleLogger
|
|||||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
||||||
import io.github.wulkanowy.utils.openInternetBrowser
|
import io.github.wulkanowy.utils.openInternetBrowser
|
||||||
import timber.log.Timber
|
|
||||||
import java.time.Instant
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
||||||
@ -39,26 +36,16 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
|||||||
|
|
||||||
abstract var presenter: T
|
abstract var presenter: T
|
||||||
|
|
||||||
private var lastDialogOpenTime = mutableMapOf<String, Instant>()
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
inject()
|
inject()
|
||||||
themeManager.applyActivityTheme(this)
|
themeManager.applyActivityTheme(this)
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleLogger, true)
|
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleLogger, true)
|
||||||
applyCustomTaskDescription()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
private fun applyCustomTaskDescription() {
|
setTaskDescription(
|
||||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) return
|
ActivityManager.TaskDescription(null, null, getThemeAttrColor(R.attr.colorSurface))
|
||||||
try {
|
)
|
||||||
val newColor = getThemeAttrColor(R.attr.colorSurface)
|
|
||||||
val taskDescription = ActivityManager.TaskDescription(null, null, newColor)
|
|
||||||
setTaskDescription(taskDescription)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.e(e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showError(text: String, error: Throwable) {
|
override fun showError(text: String, error: Throwable) {
|
||||||
@ -83,8 +70,6 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showExpiredCredentialsDialog() {
|
override fun showExpiredCredentialsDialog() {
|
||||||
if (!shouldShowDialog(DIALOG_ERROR_BAD_CREDENTIALS)) return
|
|
||||||
|
|
||||||
MaterialAlertDialogBuilder(this)
|
MaterialAlertDialogBuilder(this)
|
||||||
.setTitle(R.string.main_expired_credentials_title)
|
.setTitle(R.string.main_expired_credentials_title)
|
||||||
.setMessage(R.string.main_expired_credentials_description)
|
.setMessage(R.string.main_expired_credentials_description)
|
||||||
@ -98,8 +83,6 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showDecryptionFailedDialog() {
|
override fun showDecryptionFailedDialog() {
|
||||||
if (!shouldShowDialog(DIALOG_ERROR_DECRYPTION_FAILED)) return
|
|
||||||
|
|
||||||
MaterialAlertDialogBuilder(this)
|
MaterialAlertDialogBuilder(this)
|
||||||
.setTitle(R.string.main_session_expired)
|
.setTitle(R.string.main_session_expired)
|
||||||
.setMessage(R.string.main_session_relogin)
|
.setMessage(R.string.main_session_relogin)
|
||||||
@ -136,21 +119,4 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
|||||||
protected open fun inject() {
|
protected open fun inject() {
|
||||||
throw UnsupportedOperationException()
|
throw UnsupportedOperationException()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shouldShowDialog(name: String): Boolean {
|
|
||||||
val lastOpenTime = lastDialogOpenTime[name]
|
|
||||||
val now = Instant.now()
|
|
||||||
|
|
||||||
if (lastOpenTime != null && now.isBefore(lastOpenTime.plusSeconds(1))) {
|
|
||||||
Timber.i("Dialog $name was shown less than a second ago. Skip")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
lastDialogOpenTime[name] = Instant.now()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val DIALOG_ERROR_BAD_CREDENTIALS = "dialog_error_bad_credentials"
|
|
||||||
private const val DIALOG_ERROR_DECRYPTION_FAILED = "dialog_error_decryption_failed"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package io.github.wulkanowy.ui.base
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
||||||
import io.github.wulkanowy.data.repositories.NoAuthorizationException
|
import io.github.wulkanowy.sdk.scrapper.exception.AuthorizationRequiredException
|
||||||
import io.github.wulkanowy.sdk.scrapper.exception.CloudflareVerificationException
|
import io.github.wulkanowy.sdk.scrapper.exception.CloudflareVerificationException
|
||||||
import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException
|
import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException
|
||||||
import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
|
import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
|
||||||
@ -40,7 +40,7 @@ open class ErrorHandler @Inject constructor(@ApplicationContext protected val co
|
|||||||
is ScramblerException -> onDecryptionFailed()
|
is ScramblerException -> onDecryptionFailed()
|
||||||
is BadCredentialsException -> onExpiredCredentials()
|
is BadCredentialsException -> onExpiredCredentials()
|
||||||
is NoCurrentStudentException -> onNoCurrentStudent()
|
is NoCurrentStudentException -> onNoCurrentStudent()
|
||||||
is NoAuthorizationException -> onAuthorizationRequired()
|
is AuthorizationRequiredException -> onAuthorizationRequired()
|
||||||
is CloudflareVerificationException -> onCaptchaVerificationRequired(error.originalUrl)
|
is CloudflareVerificationException -> onCaptchaVerificationRequired(error.originalUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,19 +3,14 @@ package io.github.wulkanowy.ui.modules.about
|
|||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
|
||||||
import io.github.wulkanowy.databinding.ItemAboutBinding
|
import io.github.wulkanowy.databinding.ItemAboutBinding
|
||||||
import io.github.wulkanowy.databinding.ScrollableHeaderAboutBinding
|
import io.github.wulkanowy.databinding.ScrollableHeaderAboutBinding
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class AboutAdapter @Inject constructor(
|
class AboutAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
private val preferencesRepository: PreferencesRepository
|
|
||||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
|
||||||
|
|
||||||
private var developerModeClicks = 0
|
|
||||||
private enum class ViewType(val id: Int) {
|
private enum class ViewType(val id: Int) {
|
||||||
ITEM_HEADER(1),
|
ITEM_HEADER(1),
|
||||||
ITEM_ELEMENT(2)
|
ITEM_ELEMENT(2)
|
||||||
@ -51,19 +46,6 @@ class AboutAdapter @Inject constructor(
|
|||||||
|
|
||||||
private fun bindHeaderViewHolder(binding: ScrollableHeaderAboutBinding) {
|
private fun bindHeaderViewHolder(binding: ScrollableHeaderAboutBinding) {
|
||||||
with(binding.aboutScrollableHeaderIcon) {
|
with(binding.aboutScrollableHeaderIcon) {
|
||||||
setOnClickListener {
|
|
||||||
if (++developerModeClicks == 5 && !preferencesRepository.developerMode) {
|
|
||||||
preferencesRepository.developerMode = true
|
|
||||||
developerModeClicks = 0
|
|
||||||
|
|
||||||
Toast.makeText(
|
|
||||||
context,
|
|
||||||
"done!",
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setImageDrawable(ResourcesCompat.getDrawableForDensity(
|
setImageDrawable(ResourcesCompat.getDrawableForDensity(
|
||||||
context.resources, context.applicationInfo.icon, 640, null)
|
context.resources, context.applicationInfo.icon, 640, null)
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package io.github.wulkanowy.ui.modules.attendance
|
package io.github.wulkanowy.ui.modules.attendance
|
||||||
|
|
||||||
import android.content.res.ColorStateList
|
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@ -34,17 +33,17 @@ class AttendanceAdapter @Inject constructor() :
|
|||||||
)
|
)
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
|
||||||
val context = holder.binding.root.context
|
|
||||||
val item = items[position]
|
val item = items[position]
|
||||||
|
|
||||||
with(holder.binding) {
|
with(holder.binding) {
|
||||||
attendanceItemNumber.text = item.number.toString()
|
attendanceItemNumber.text = item.number.toString()
|
||||||
attendanceItemSubject.text = item.subject
|
attendanceItemSubject.text = item.subject.ifBlank {
|
||||||
.ifBlank { context.getString(R.string.all_no_data) }
|
root.context.getString(R.string.all_no_data)
|
||||||
|
}
|
||||||
attendanceItemDescription.setText(item.descriptionRes)
|
attendanceItemDescription.setText(item.descriptionRes)
|
||||||
|
|
||||||
attendanceItemDescription.setTextColor(
|
attendanceItemDescription.setTextColor(
|
||||||
context.getThemeAttrColor(
|
root.context.getThemeAttrColor(
|
||||||
when {
|
when {
|
||||||
item.absence && !item.excused -> R.attr.colorAttendanceAbsence
|
item.absence && !item.excused -> R.attr.colorAttendanceAbsence
|
||||||
item.lateness && !item.excused -> R.attr.colorAttendanceLateness
|
item.lateness && !item.excused -> R.attr.colorAttendanceLateness
|
||||||
@ -62,15 +61,13 @@ class AttendanceAdapter @Inject constructor() :
|
|||||||
attendanceItemAlert.isVisible =
|
attendanceItemAlert.isVisible =
|
||||||
item.let { (it.absence && !it.excused) || (it.lateness && !it.excused) }
|
item.let { (it.absence && !it.excused) || (it.lateness && !it.excused) }
|
||||||
|
|
||||||
attendanceItemAlert.imageTintList = ColorStateList.valueOf(
|
attendanceItemAlert.setColorFilter(root.context.getThemeAttrColor(
|
||||||
context.getThemeAttrColor(
|
|
||||||
when{
|
when{
|
||||||
item.absence && !item.excused -> R.attr.colorAttendanceAbsence
|
item.absence && !item.excused -> R.attr.colorAttendanceAbsence
|
||||||
item.lateness && !item.excused -> R.attr.colorAttendanceLateness
|
item.lateness && !item.excused -> R.attr.colorAttendanceLateness
|
||||||
else -> android.R.attr.colorPrimary
|
else -> android.R.attr.colorPrimary
|
||||||
}
|
}
|
||||||
)
|
))
|
||||||
)
|
|
||||||
attendanceItemNumber.visibility = View.GONE
|
attendanceItemNumber.visibility = View.GONE
|
||||||
attendanceItemExcuseInfo.visibility = View.GONE
|
attendanceItemExcuseInfo.visibility = View.GONE
|
||||||
attendanceItemExcuseCheckbox.visibility = View.GONE
|
attendanceItemExcuseCheckbox.visibility = View.GONE
|
||||||
|
@ -2,8 +2,14 @@ package io.github.wulkanowy.ui.modules.attendance
|
|||||||
|
|
||||||
import android.content.DialogInterface.BUTTON_POSITIVE
|
import android.content.DialogInterface.BUTTON_POSITIVE
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.*
|
import android.view.LayoutInflater
|
||||||
import android.view.View.*
|
import android.view.Menu
|
||||||
|
import android.view.MenuInflater
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.view.View.GONE
|
||||||
|
import android.view.View.INVISIBLE
|
||||||
|
import android.view.View.VISIBLE
|
||||||
import androidx.appcompat.view.ActionMode
|
import androidx.appcompat.view.ActionMode
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
@ -14,7 +20,6 @@ import io.github.wulkanowy.data.db.entities.Attendance
|
|||||||
import io.github.wulkanowy.databinding.DialogExcuseBinding
|
import io.github.wulkanowy.databinding.DialogExcuseBinding
|
||||||
import io.github.wulkanowy.databinding.FragmentAttendanceBinding
|
import io.github.wulkanowy.databinding.FragmentAttendanceBinding
|
||||||
import io.github.wulkanowy.ui.base.BaseFragment
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
import io.github.wulkanowy.ui.modules.attendance.calculator.AttendanceCalculatorFragment
|
|
||||||
import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment
|
import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment
|
||||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
@ -64,6 +69,8 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
|
|||||||
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
|
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||||
val inflater = mode.menuInflater
|
val inflater = mode.menuInflater
|
||||||
inflater.inflate(R.menu.context_menu_attendance, menu)
|
inflater.inflate(R.menu.context_menu_attendance, menu)
|
||||||
|
menu.findItem(R.id.excuseMenuDaySubmit).setVisible(presenter.isWholeDayExcusable)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,6 +86,7 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
|
|||||||
|
|
||||||
override fun onActionItemClicked(mode: ActionMode, menu: MenuItem): Boolean {
|
override fun onActionItemClicked(mode: ActionMode, menu: MenuItem): Boolean {
|
||||||
return when (menu.itemId) {
|
return when (menu.itemId) {
|
||||||
|
R.id.excuseMenuDaySubmit -> presenter.onExcuseDayButtonClick()
|
||||||
R.id.excuseMenuSubmit -> presenter.onExcuseSubmitButtonClick()
|
R.id.excuseMenuSubmit -> presenter.onExcuseSubmitButtonClick()
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
@ -135,7 +143,6 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
|
|||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
return if (item.itemId == R.id.attendanceMenuSummary) presenter.onSummarySwitchSelected()
|
return if (item.itemId == R.id.attendanceMenuSummary) presenter.onSummarySwitchSelected()
|
||||||
else if (item.itemId == R.id.attendanceMenuCalculator) presenter.onCalculatorSwitchSelected()
|
|
||||||
else false
|
else false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,19 +262,22 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
|
|||||||
(activity as? MainActivity)?.pushView(AttendanceSummaryFragment.newInstance())
|
(activity as? MainActivity)?.pushView(AttendanceSummaryFragment.newInstance())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun openCalculatorView() {
|
|
||||||
(activity as? MainActivity)?.pushView(AttendanceCalculatorFragment.newInstance())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun startActionMode() {
|
override fun startActionMode() {
|
||||||
actionMode = (activity as MainActivity?)?.startSupportActionMode(actionModeCallback)
|
actionMode = (activity as MainActivity?)?.startSupportActionMode(actionModeCallback)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun startSendMessageIntent(date: LocalDate, numbers: String, reason: String) {
|
override fun startSendMessageIntent(date: LocalDate, lessons: String, reason: String) {
|
||||||
val reasonFullText = getString(
|
val reasonFullText = if (lessons.isEmpty()) {
|
||||||
R.string.attendance_excuse_formula,
|
getString(
|
||||||
|
R.string.attendance_excuse_day_formula,
|
||||||
date,
|
date,
|
||||||
numbers,
|
if (reason.isNotBlank()) " ${getString(R.string.attendance_excuse_reason)} " else "",
|
||||||
|
reason.ifBlank { "" }
|
||||||
|
)
|
||||||
|
} else getString(
|
||||||
|
R.string.attendance_excuse_lessons_formula,
|
||||||
|
date,
|
||||||
|
lessons,
|
||||||
if (reason.isNotBlank()) " ${getString(R.string.attendance_excuse_reason)} " else "",
|
if (reason.isNotBlank()) " ${getString(R.string.attendance_excuse_reason)} " else "",
|
||||||
reason.ifBlank { "" }
|
reason.ifBlank { "" }
|
||||||
)
|
)
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user