Compare commits
No commits in common. "2.6.11" and "1.2.3" have entirely different histories.
847 changed files with 12662 additions and 91294 deletions
|
@ -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,12 +0,0 @@
|
||||||
[*]
|
|
||||||
charset=utf-8
|
|
||||||
end_of_line=lf
|
|
||||||
insert_final_newline=true
|
|
||||||
indent_style=space
|
|
||||||
indent_size=4
|
|
||||||
|
|
||||||
[*.json]
|
|
||||||
indent_size=2
|
|
||||||
|
|
||||||
[*.{kt,kts}]
|
|
||||||
disabled_rules=import-ordering,no-wildcard-imports
|
|
4
.github/FUNDING.yml
vendored
4
.github/FUNDING.yml
vendored
|
@ -1,4 +0,0 @@
|
||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
github: wulkanowy
|
|
||||||
custom: https://www.paypal.com/paypalme/wulkanowy
|
|
39
.github/workflows/deploy-store.yml
vendored
39
.github/workflows/deploy-store.yml
vendored
|
@ -1,4 +1,4 @@
|
||||||
name: Deploy release
|
name: Deploy to app stores
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
|
@ -7,17 +7,16 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
deploy-google-play:
|
deploy-google-play:
|
||||||
name: Google Play
|
name: Deploy to google play
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
environment: google-play
|
environment: google-play
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-java@v3
|
- uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
java-version: 11
|
||||||
java-version: 17
|
- uses: actions/cache@v2
|
||||||
- uses: actions/cache@v3
|
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.gradle/caches
|
~/.gradle/caches
|
||||||
|
@ -29,31 +28,27 @@ jobs:
|
||||||
SERVICES_ENCRYPT_KEY: ${{ secrets.SERVICES_ENCRYPT_KEY }}
|
SERVICES_ENCRYPT_KEY: ${{ secrets.SERVICES_ENCRYPT_KEY }}
|
||||||
run: |
|
run: |
|
||||||
gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/google-services.json.gpg
|
gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/google-services.json.gpg
|
||||||
|
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/key.p12.gpg
|
||||||
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg
|
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg
|
||||||
- name: Upload apk to google play
|
- name: Upload apk to google play
|
||||||
env:
|
env:
|
||||||
PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }}
|
|
||||||
PLAY_KEY_ALIAS: ${{ secrets.PLAY_KEY_ALIAS }}
|
PLAY_KEY_ALIAS: ${{ secrets.PLAY_KEY_ALIAS }}
|
||||||
PLAY_KEY_PASSWORD: ${{ secrets.PLAY_KEY_PASSWORD }}
|
PLAY_KEY_PASSWORD: ${{ secrets.PLAY_KEY_PASSWORD }}
|
||||||
ANDROID_PUBLISHER_CREDENTIALS: ${{ secrets.ANDROID_PUBLISHER_CREDENTIALS }}
|
PLAY_SERVICE_ACCOUNT_EMAIL: ${{ secrets.PLAY_SERVICE_ACCOUNT_EMAIL }}
|
||||||
ADMOB_PROJECT_ID: ${{ secrets.ADMOB_PROJECT_ID }}
|
PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }}
|
||||||
SINGLE_SUPPORT_AD_ID: ${{ secrets.SINGLE_SUPPORT_AD_ID }}
|
run: ./gradlew publishPlayRelease -PenableFirebase --stacktrace;
|
||||||
DASHBOARD_TILE_AD_ID: ${{ secrets.DASHBOARD_TILE_AD_ID }}
|
|
||||||
SET_BUILD_TIMESTAMP: ${{ secrets.SET_BUILD_TIMESTAMP }}
|
|
||||||
run: ./gradlew publishPlayReleaseApps --stacktrace;
|
|
||||||
|
|
||||||
deploy-app-gallery:
|
deploy-app-gallery:
|
||||||
name: AppGallery
|
name: Deploy to AppGallery
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
environment: app-gallery
|
environment: app-gallery
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-java@v3
|
- uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
java-version: 11
|
||||||
java-version: 17
|
- uses: actions/cache@v2
|
||||||
- uses: actions/cache@v3
|
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.gradle/caches
|
~/.gradle/caches
|
||||||
|
@ -65,6 +60,7 @@ jobs:
|
||||||
SERVICES_ENCRYPT_KEY: ${{ secrets.SERVICES_ENCRYPT_KEY }}
|
SERVICES_ENCRYPT_KEY: ${{ secrets.SERVICES_ENCRYPT_KEY }}
|
||||||
run: |
|
run: |
|
||||||
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/upload-key.jks.gpg
|
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg
|
||||||
- name: Prepare credentials
|
- name: Prepare credentials
|
||||||
env:
|
env:
|
||||||
|
@ -72,8 +68,7 @@ jobs:
|
||||||
run: echo $AGC_CREDENTIALS > ./app/src/release/agconnect-credentials.json
|
run: echo $AGC_CREDENTIALS > ./app/src/release/agconnect-credentials.json
|
||||||
- name: Build and publish HMS version
|
- name: Build and publish HMS version
|
||||||
env:
|
env:
|
||||||
PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }}
|
|
||||||
PLAY_KEY_ALIAS: ${{ secrets.PLAY_KEY_ALIAS }}
|
PLAY_KEY_ALIAS: ${{ secrets.PLAY_KEY_ALIAS }}
|
||||||
PLAY_KEY_PASSWORD: ${{ secrets.PLAY_KEY_PASSWORD }}
|
PLAY_KEY_PASSWORD: ${{ secrets.PLAY_KEY_PASSWORD }}
|
||||||
SET_BUILD_TIMESTAMP: ${{ secrets.SET_BUILD_TIMESTAMP }}
|
PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }}
|
||||||
run: ./gradlew bundleHmsRelease --stacktrace && ./gradlew publishHuaweiAppGalleryHmsRelease --stacktrace
|
run: ./gradlew bundleHmsRelease --stacktrace && ./gradlew publishHuaweiAppGalleryHmsRelease --stacktrace
|
||||||
|
|
29
.github/workflows/deploy-test.yml
vendored
29
.github/workflows/deploy-test.yml
vendored
|
@ -1,4 +1,4 @@
|
||||||
name: Deploy DEV
|
name: Deploy to app tests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
@ -18,12 +18,11 @@ jobs:
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
environment: app-center
|
environment: app-center
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-java@v3
|
- uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
java-version: 11
|
||||||
java-version: 17
|
- uses: actions/cache@v2
|
||||||
- uses: actions/cache@v3
|
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.gradle/caches
|
~/.gradle/caches
|
||||||
|
@ -36,7 +35,8 @@ jobs:
|
||||||
- name: Prepare build configuration
|
- name: Prepare build configuration
|
||||||
run: |
|
run: |
|
||||||
sed -i -e "s#applicationIdSuffix \".dev\"#applicationIdSuffix \".${GITHUB_HEAD_REF//[-.\/]/_}\"#" app/build.gradle
|
sed -i -e "s#applicationIdSuffix \".dev\"#applicationIdSuffix \".${GITHUB_HEAD_REF//[-.\/]/_}\"#" app/build.gradle
|
||||||
sed -i -e "s#.dev\"#.${GITHUB_HEAD_REF//[-.\/]/_}\"#" app/google-services.json
|
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
|
sed -i -e '/versionNameSuffix/d' app/build.gradle
|
||||||
- name: Add signing config
|
- name: Add signing config
|
||||||
run: |
|
run: |
|
||||||
|
@ -66,7 +66,7 @@ jobs:
|
||||||
BITRISE_KEY_PASSWORD: ${{ secrets.BITRISE_KEY_PASSWORD }}
|
BITRISE_KEY_PASSWORD: ${{ secrets.BITRISE_KEY_PASSWORD }}
|
||||||
run: ./gradlew assembleFdroidDebug --stacktrace
|
run: ./gradlew assembleFdroidDebug --stacktrace
|
||||||
- name: Upload apk to github artifacts
|
- name: Upload apk to github artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: wulkanowyDEV-${{ env.RUN_NUMBER }}.apk
|
name: wulkanowyDEV-${{ env.RUN_NUMBER }}.apk
|
||||||
path: app/build/outputs/apk/fdroid/debug/app-fdroid-debug.apk
|
path: app/build/outputs/apk/fdroid/debug/app-fdroid-debug.apk
|
||||||
|
@ -87,12 +87,11 @@ jobs:
|
||||||
environment: app-distribution
|
environment: app-distribution
|
||||||
if: github.event_name != 'pull_request_target'
|
if: github.event_name != 'pull_request_target'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-java@v3
|
- uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
java-version: 11
|
||||||
java-version: 17
|
- uses: actions/cache@v2
|
||||||
- uses: actions/cache@v3
|
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.gradle/caches
|
~/.gradle/caches
|
||||||
|
@ -130,9 +129,9 @@ jobs:
|
||||||
BITRISE_KEYSTORE_PASSWORD: ${{ secrets.BITRISE_KEYSTORE_PASSWORD }}
|
BITRISE_KEYSTORE_PASSWORD: ${{ secrets.BITRISE_KEYSTORE_PASSWORD }}
|
||||||
BITRISE_KEY_ALIAS: ${{ secrets.BITRISE_KEY_ALIAS }}
|
BITRISE_KEY_ALIAS: ${{ secrets.BITRISE_KEY_ALIAS }}
|
||||||
BITRISE_KEY_PASSWORD: ${{ secrets.BITRISE_KEY_PASSWORD }}
|
BITRISE_KEY_PASSWORD: ${{ secrets.BITRISE_KEY_PASSWORD }}
|
||||||
run: ./gradlew assemblePlayDebug --stacktrace
|
run: ./gradlew assemblePlayDebug -PenableFirebase --stacktrace
|
||||||
- name: Upload apk to github artifacts
|
- name: Upload apk to github artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: wulkanowyDEV-${{ env.RUN_NUMBER }}-dev.apk
|
name: wulkanowyDEV-${{ env.RUN_NUMBER }}-dev.apk
|
||||||
path: app/build/outputs/apk/play/debug/app-play-debug.apk
|
path: app/build/outputs/apk/play/debug/app-play-debug.apk
|
||||||
|
|
74
.github/workflows/test.yml
vendored
74
.github/workflows/test.yml
vendored
|
@ -2,28 +2,24 @@ name: Tests
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches: [ master, develop ]
|
||||||
- master
|
|
||||||
- develop
|
|
||||||
- 'hotfix/**'
|
|
||||||
tags: [ '*' ]
|
tags: [ '*' ]
|
||||||
pull_request:
|
pull_request:
|
||||||
|
branches: [ master, develop ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
unit-tests:
|
||||||
tests-fdroid:
|
name: Unit tests
|
||||||
name: F-Droid
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
steps:
|
steps:
|
||||||
- uses: fkirc/skip-duplicate-actions@master
|
- uses: fkirc/skip-duplicate-actions@master
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
- uses: gradle/wrapper-validation-action@v1
|
- uses: gradle/wrapper-validation-action@v1
|
||||||
- uses: actions/setup-java@v3
|
- uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
java-version: 11
|
||||||
java-version: 17
|
- uses: actions/cache@v2
|
||||||
- uses: actions/cache@v3
|
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.gradle/caches
|
~/.gradle/caches
|
||||||
|
@ -33,58 +29,6 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
./gradlew testFdroidDebugUnitTest --stacktrace
|
./gradlew testFdroidDebugUnitTest --stacktrace
|
||||||
./gradlew jacocoTestReport --stacktrace
|
./gradlew jacocoTestReport --stacktrace
|
||||||
- uses: codecov/codecov-action@v3
|
- uses: codecov/codecov-action@v1
|
||||||
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:
|
with:
|
||||||
flags: unit
|
flags: unit
|
||||||
|
|
17
.gitignore
vendored
17
.gitignore
vendored
|
@ -65,12 +65,6 @@ captures/
|
||||||
.idea/uiDesigner.xml
|
.idea/uiDesigner.xml
|
||||||
.idea/runConfigurations.xml
|
.idea/runConfigurations.xml
|
||||||
.idea/discord.xml
|
.idea/discord.xml
|
||||||
.idea/migrations.xml
|
|
||||||
.idea/androidTestResultsUserPreferences.xml
|
|
||||||
.idea/copilot
|
|
||||||
.idea/deploymentTargetDropDown.xml
|
|
||||||
.idea/deploymentTargetSelector.xml
|
|
||||||
.idea/kotlinc.xml
|
|
||||||
|
|
||||||
# Keystore files
|
# Keystore files
|
||||||
*.jks
|
*.jks
|
||||||
|
@ -117,14 +111,11 @@ 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
|
||||||
|
|
15
.idea/codeStyles/Project.xml
generated
15
.idea/codeStyles/Project.xml
generated
|
@ -2,6 +2,14 @@
|
||||||
<code_scheme name="Project" version="173">
|
<code_scheme name="Project" version="173">
|
||||||
<option name="LINE_SEPARATOR" value=" " />
|
<option name="LINE_SEPARATOR" value=" " />
|
||||||
<JetCodeStyleSettings>
|
<JetCodeStyleSettings>
|
||||||
|
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||||
|
<value>
|
||||||
|
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
|
||||||
|
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
|
||||||
|
<option name="WRAP_ELVIS_EXPRESSIONS" value="0" />
|
||||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
</JetCodeStyleSettings>
|
</JetCodeStyleSettings>
|
||||||
<codeStyleSettings language="XML">
|
<codeStyleSettings language="XML">
|
||||||
|
@ -118,6 +126,13 @@
|
||||||
</codeStyleSettings>
|
</codeStyleSettings>
|
||||||
<codeStyleSettings language="kotlin">
|
<codeStyleSettings language="kotlin">
|
||||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
|
||||||
|
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
||||||
|
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="0" />
|
||||||
|
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||||
|
</indentOptions>
|
||||||
</codeStyleSettings>
|
</codeStyleSettings>
|
||||||
</code_scheme>
|
</code_scheme>
|
||||||
</component>
|
</component>
|
|
@ -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:
|
||||||
|
|
2
LICENSE
2
LICENSE
|
@ -186,7 +186,7 @@
|
||||||
same "printed page" as the copyright notice for easier
|
same "printed page" as the copyright notice for easier
|
||||||
identification within third-party archives.
|
identification within third-party archives.
|
||||||
|
|
||||||
Copyright 2023 Wulkanowy
|
Copyright 2019 Wulkanowy
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|
73
README.cs.md
73
README.cs.md
|
@ -1,73 +0,0 @@
|
||||||
Česká verze / [Deutsche Version](README.de.md) / [English version](README.en.md) / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md)
|
|
||||||
|
|
||||||
# Wulkanowy
|
|
||||||
|
|
||||||
[](https://github.com/wulkanowy/wulkanowy/actions)
|
|
||||||
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
|
||||||
[](https://discord.gg/vccAQBr)
|
|
||||||
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
|
||||||
[](https://github.com/wulkanowy/wulkanowy/releases)
|
|
||||||
[](https://translate.wulkanowy.net.pl)
|
|
||||||
|
|
||||||
Neoficiální klient deníku VULCAN UONET+ pro žáka a rodiče
|
|
||||||
|
|
||||||
## Funkce
|
|
||||||
|
|
||||||
* přihlášení pomocí emailu a hesla
|
|
||||||
* funkce z webové stránky deníku:
|
|
||||||
* známky
|
|
||||||
* statistiky známek
|
|
||||||
* 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áhnout
|
|
||||||
|
|
||||||
Aktuální verzi si můžete stáhnout z Google Play, F-Droid nebo Huawei AppGallery
|
|
||||||
|
|
||||||
[<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)
|
|
73
README.de.md
73
README.de.md
|
@ -1,73 +0,0 @@
|
||||||
[Česká verze](README.cs.md) / Deutsche Version / [English version](README.en.md) / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md)
|
|
||||||
|
|
||||||
# Wulkanowy
|
|
||||||
|
|
||||||
[](https://github.com/wulkanowy/wulkanowy/actions)
|
|
||||||
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
|
||||||
[](https://discord.gg/vccAQBr)
|
|
||||||
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
|
||||||
[](https://github.com/wulkanowy/wulkanowy/releases)
|
|
||||||
[](https://translate.wulkanowy.net.pl)
|
|
||||||
|
|
||||||
Inoffizieller Android VULCAN UONET+ Registrierungsclient für Schüler und ihre Eltern
|
|
||||||
|
|
||||||
## Merkmale
|
|
||||||
|
|
||||||
* Einloggen mit E-Mail und Passwort
|
|
||||||
* Funktionen von der Registerwebsite:
|
|
||||||
* Noten
|
|
||||||
* Notenstatistik
|
|
||||||
* 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
|
|
||||||
|
|
||||||
## Herunterladen
|
|
||||||
|
|
||||||
Die aktuelle Version können Sie von der Google Play, F-Droid oder Huawei AppGallery store herunterladen
|
|
||||||
|
|
||||||
[<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
|
|
|
@ -1,13 +1,12 @@
|
||||||
[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / English version / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md)
|
[Polska wersja README](README.md)
|
||||||
|
|
||||||
# Wulkanowy
|
# Wulkanowy
|
||||||
|
|
||||||
[](https://github.com/wulkanowy/wulkanowy/actions)
|
[](https://github.com/wulkanowy/wulkanowy/actions)
|
||||||
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
||||||
[](https://discord.gg/vccAQBr)
|
[](https://discord.gg/vccAQBr)
|
||||||
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||||
[](https://github.com/wulkanowy/wulkanowy/releases)
|
[](https://github.com/wulkanowy/wulkanowy/releases)
|
||||||
[](https://translate.wulkanowy.net.pl)
|
|
||||||
|
|
||||||
Unofficial android VULCAN UONET+ register client for both students and their parents
|
Unofficial android VULCAN UONET+ register client for both students and their parents
|
||||||
|
|
||||||
|
@ -34,7 +33,7 @@ Unofficial android VULCAN UONET+ register client for both students and their par
|
||||||
* support for multiple accounts with the ability to rename students
|
* support for multiple accounts with the ability to rename students
|
||||||
* dark and black (AMOLED) theme
|
* dark and black (AMOLED) theme
|
||||||
* offline mode
|
* offline mode
|
||||||
* optional ads which allow to support the project
|
* no ads
|
||||||
|
|
||||||
## Download
|
## Download
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / [English version](README.en.md) / Polska wersja / [Slovenská verzia](README.sk.md)
|
[English version of README](README.en.md)
|
||||||
|
|
||||||
# Wulkanowy
|
# Wulkanowy
|
||||||
|
|
||||||
[](https://github.com/wulkanowy/wulkanowy/actions)
|
[](https://github.com/wulkanowy/wulkanowy/actions)
|
||||||
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
||||||
[](https://discord.gg/vccAQBr)
|
[](https://discord.gg/vccAQBr)
|
||||||
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||||
[](https://github.com/wulkanowy/wulkanowy/releases)
|
[](https://github.com/wulkanowy/wulkanowy/releases)
|
||||||
[](https://translate.wulkanowy.net.pl)
|
|
||||||
|
|
||||||
Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica
|
Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica
|
||||||
|
|
||||||
|
@ -34,7 +33,7 @@ Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica
|
||||||
* obsługa wielu kont wraz z możliwością zmiany nazwy ucznia
|
* obsługa wielu kont wraz z możliwością zmiany nazwy ucznia
|
||||||
* ciemny i czarny (AMOLED) motyw
|
* ciemny i czarny (AMOLED) motyw
|
||||||
* tryb offline
|
* tryb offline
|
||||||
* opcjonalne reklamy umożliwiające wsparcie projektu
|
* brak reklam
|
||||||
|
|
||||||
## Pobierz
|
## Pobierz
|
||||||
|
|
||||||
|
|
73
README.sk.md
73
README.sk.md
|
@ -1,73 +0,0 @@
|
||||||
[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / [English version](README.en.md) / [Polska wersja](README.md) / Slovenská verzia
|
|
||||||
|
|
||||||
# Wulkanowy
|
|
||||||
|
|
||||||
[](https://github.com/wulkanowy/wulkanowy/actions)
|
|
||||||
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
|
||||||
[](https://discord.gg/vccAQBr)
|
|
||||||
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
|
||||||
[](https://github.com/wulkanowy/wulkanowy/releases)
|
|
||||||
[](https://translate.wulkanowy.net.pl)
|
|
||||||
|
|
||||||
Neoficiálny klient denníka VULCAN UONET+ pre žiaka a rodičov
|
|
||||||
|
|
||||||
## Funkcie
|
|
||||||
|
|
||||||
* prihlásenie pomocou emailu a hesla
|
|
||||||
* funkcie z webovej stránky denníka:
|
|
||||||
* známky
|
|
||||||
* štatistiky známok
|
|
||||||
* 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
|
|
||||||
|
|
||||||
## Stiahnuť
|
|
||||||
|
|
||||||
Aktuálnu verziu si môžete stiahnuť z Google Play, F-Droid alebo Huawei AppGallery
|
|
||||||
|
|
||||||
[<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)
|
|
227
app/build.gradle
227
app/build.gradle
|
@ -1,11 +1,7 @@
|
||||||
import com.github.triplet.gradle.androidpublisher.ReleaseStatus
|
|
||||||
import ru.cian.huawei.publish.ReleaseNote
|
|
||||||
|
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
apply plugin: 'kotlinx-serialization'
|
|
||||||
apply plugin: 'kotlin-parcelize'
|
apply plugin: 'kotlin-parcelize'
|
||||||
apply plugin: 'com.google.devtools.ksp'
|
apply plugin: 'kotlin-kapt'
|
||||||
apply plugin: 'dagger.hilt.android.plugin'
|
apply plugin: 'dagger.hilt.android.plugin'
|
||||||
apply plugin: 'com.google.gms.google-services'
|
apply plugin: 'com.google.gms.google-services'
|
||||||
apply plugin: 'com.google.firebase.crashlytics'
|
apply plugin: 'com.google.firebase.crashlytics'
|
||||||
|
@ -13,34 +9,36 @@ apply plugin: 'com.github.triplet.play'
|
||||||
apply plugin: 'ru.cian.huawei-publish'
|
apply plugin: 'ru.cian.huawei-publish'
|
||||||
apply plugin: 'com.mikepenz.aboutlibraries.plugin'
|
apply plugin: 'com.mikepenz.aboutlibraries.plugin'
|
||||||
apply plugin: 'com.huawei.agconnect'
|
apply plugin: 'com.huawei.agconnect'
|
||||||
apply plugin: 'kotlin-kapt'
|
|
||||||
apply from: 'jacoco.gradle'
|
apply from: 'jacoco.gradle'
|
||||||
apply from: 'sonarqube.gradle'
|
apply from: 'sonarqube.gradle'
|
||||||
apply from: 'hooks.gradle'
|
apply from: 'hooks.gradle'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace 'io.github.wulkanowy'
|
compileSdkVersion 30
|
||||||
compileSdk 34
|
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "io.github.wulkanowy"
|
applicationId "io.github.wulkanowy"
|
||||||
testApplicationId "io.github.tests.wulkanowy"
|
testApplicationId "io.github.tests.wulkanowy"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 34
|
targetSdkVersion 30
|
||||||
versionCode 171
|
versionCode 96
|
||||||
versionName "2.6.11"
|
versionName "1.2.3"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
|
||||||
resValue "string", "app_name", "Wulkanowy"
|
resValue "string", "app_name", "Wulkanowy"
|
||||||
manifestPlaceholders = [admob_project_id: ""]
|
buildConfigField "long", "BUILD_TIMESTAMP", String.valueOf(System.currentTimeMillis())
|
||||||
|
|
||||||
buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "null"
|
manifestPlaceholders = [
|
||||||
buildConfigField "String", "DASHBOARD_TILE_AD_ID", "null"
|
firebase_enabled: project.hasProperty("enableFirebase")
|
||||||
|
]
|
||||||
if (System.env.SET_BUILD_TIMESTAMP) {
|
javaCompileOptions {
|
||||||
buildConfigField "long", "BUILD_TIMESTAMP", String.valueOf(System.currentTimeMillis())
|
annotationProcessorOptions {
|
||||||
} else {
|
arguments += [
|
||||||
buildConfigField "long", "BUILD_TIMESTAMP", "1486235849000"
|
"room.schemaLocation": "$projectDir/schemas".toString(),
|
||||||
|
"room.incremental" : "true"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,84 +62,62 @@ android {
|
||||||
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", "SCHOOLS_BASE_URL", '"https://schools.wulkanowy.net.pl"'
|
|
||||||
}
|
}
|
||||||
debug {
|
debug {
|
||||||
minifyEnabled false
|
resValue "string", "app_name", "Wulkanowy DEV " + defaultConfig.versionCode
|
||||||
shrinkResources false
|
|
||||||
resValue "string", "app_name", "Wulkanowy DEV"
|
|
||||||
applicationIdSuffix ".dev"
|
applicationIdSuffix ".dev"
|
||||||
versionNameSuffix "-dev"
|
versionNameSuffix "-dev"
|
||||||
buildConfigField "String", "MESSAGES_BASE_URL", "\"https://messages.wulkanowy.net.pl\""
|
ext.enableCrashlytics = project.hasProperty("enableFirebase")
|
||||||
buildConfigField "String", "SCHOOLS_BASE_URL", '"https://schools.wulkanowy.net.pl"'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flavorDimensions += "platform"
|
flavorDimensions "platform"
|
||||||
|
|
||||||
productFlavors {
|
productFlavors {
|
||||||
hms {
|
hms {
|
||||||
dimension "platform"
|
dimension "platform"
|
||||||
manifestPlaceholders = [install_channel: "AppGallery"]
|
manifestPlaceholders = [
|
||||||
|
install_channel: "AppGallery"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
play {
|
play {
|
||||||
dimension "platform"
|
dimension "platform"
|
||||||
manifestPlaceholders = [
|
manifestPlaceholders = [
|
||||||
install_channel : "Google Play",
|
install_channel: "Google Play"
|
||||||
admob_project_id: System.getenv("ADMOB_PROJECT_ID") ?: "ca-app-pub-3940256099942544~3347511713"
|
|
||||||
]
|
]
|
||||||
buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "\"${System.getenv("SINGLE_SUPPORT_AD_ID") ?: "ca-app-pub-3940256099942544/5354046379"}\""
|
|
||||||
buildConfigField "String", "DASHBOARD_TILE_AD_ID", "\"${System.getenv("DASHBOARD_TILE_AD_ID") ?: "ca-app-pub-3940256099942544/6300978111"}\""
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fdroid {
|
fdroid {
|
||||||
dimension "platform"
|
dimension "platform"
|
||||||
manifestPlaceholders = [install_channel: "F-Droid"]
|
manifestPlaceholders = [
|
||||||
|
install_channel: "F-Droid"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
playConfigs {
|
|
||||||
play { enabled.set(true) }
|
|
||||||
}
|
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding true
|
viewBinding true
|
||||||
buildConfig true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bundle {
|
testOptions.unitTests {
|
||||||
language {
|
includeAndroidResources = true
|
||||||
enableSplit = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
testOptions {
|
|
||||||
unitTests.includeAndroidResources = true
|
|
||||||
// workaround HMS test errors https://github.com/robolectric/robolectric/issues/2750
|
|
||||||
unitTests.all { jvmArgs '-noverify' }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
coreLibraryDesugaringEnabled true
|
coreLibraryDesugaringEnabled true
|
||||||
sourceCompatibility JavaVersion.VERSION_17
|
sourceCompatibility JavaVersion.VERSION_11
|
||||||
targetCompatibility JavaVersion.VERSION_17
|
targetCompatibility JavaVersion.VERSION_11
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "17"
|
jvmTarget = "11"
|
||||||
freeCompilerArgs += ["-opt-in=kotlin.RequiresOptIn", "-Xjvm-default=all"]
|
freeCompilerArgs += ["-Xopt-in=kotlin.RequiresOptIn", "-Xjvm-default=all"]
|
||||||
}
|
}
|
||||||
|
|
||||||
packagingOptions {
|
packagingOptions {
|
||||||
resources {
|
exclude 'META-INF/library_release.kotlin_module'
|
||||||
excludes += ['META-INF/library_release.kotlin_module',
|
exclude 'META-INF/library-core_release.kotlin_module'
|
||||||
'META-INF/library-core_release.kotlin_module',
|
|
||||||
'META-INF/LICENSE.md',
|
|
||||||
'META-INF/LICENSE-notice.md']
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
aboutLibraries {
|
aboutLibraries {
|
||||||
|
@ -152,17 +128,13 @@ android {
|
||||||
kapt {
|
kapt {
|
||||||
correctErrorTypes true
|
correctErrorTypes true
|
||||||
}
|
}
|
||||||
ksp {
|
|
||||||
arg("room.schemaLocation", "$projectDir/schemas".toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
play {
|
play {
|
||||||
|
serviceAccountEmail = System.getenv("PLAY_SERVICE_ACCOUNT_EMAIL") ?: "jan@fakelog.cf"
|
||||||
|
serviceAccountCredentials = file('key.p12')
|
||||||
defaultToAppBundles = false
|
defaultToAppBundles = false
|
||||||
track = 'production'
|
track = 'production'
|
||||||
releaseStatus = ReleaseStatus.IN_PROGRESS
|
updatePriority = 3
|
||||||
userFraction = 0.99d
|
|
||||||
updatePriority = 2
|
|
||||||
enabled.set(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
huaweiPublish {
|
huaweiPublish {
|
||||||
|
@ -170,62 +142,53 @@ huaweiPublish {
|
||||||
hmsRelease {
|
hmsRelease {
|
||||||
credentialsPath = "$rootDir/app/src/release/agconnect-credentials.json"
|
credentialsPath = "$rootDir/app/src/release/agconnect-credentials.json"
|
||||||
buildFormat = "aab"
|
buildFormat = "aab"
|
||||||
deployType = "publish"
|
deployType = "draft"
|
||||||
releaseNotes = [
|
|
||||||
new ReleaseNote(
|
|
||||||
"pl-PL",
|
|
||||||
"$projectDir/src/main/play/release-notes/pl-PL/default.txt"
|
|
||||||
)
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
work_manager = "2.9.0"
|
work_manager = "2.6.0"
|
||||||
android_hilt = "1.2.0"
|
android_hilt = "1.0.0"
|
||||||
room = "2.6.1"
|
room = "2.3.0"
|
||||||
chucker = "4.0.0"
|
chucker = "3.5.2"
|
||||||
mockk = "1.13.10"
|
mockk = "1.12.0"
|
||||||
coroutines = "1.8.1"
|
moshi = "1.12.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'io.github.wulkanowy:sdk:2.6.10'
|
implementation "io.github.wulkanowy:sdk:1.2.3"
|
||||||
|
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||||
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2"
|
||||||
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.6.0"
|
||||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
implementation "androidx.activity:activity-ktx:1.3.1"
|
||||||
implementation "androidx.activity:activity-ktx:1.9.0"
|
implementation "androidx.appcompat:appcompat:1.3.1"
|
||||||
implementation "androidx.appcompat:appcompat:1.6.1"
|
implementation "androidx.appcompat:appcompat-resources:1.3.1"
|
||||||
implementation "androidx.fragment:fragment-ktx:1.7.0"
|
implementation "androidx.fragment:fragment-ktx:1.3.6"
|
||||||
implementation "androidx.annotation:annotation:1.7.1"
|
implementation "androidx.annotation:annotation:1.2.0"
|
||||||
implementation "androidx.javascriptengine:javascriptengine:1.0.0-beta01"
|
|
||||||
|
|
||||||
implementation "androidx.preference:preference-ktx:1.2.1"
|
implementation "androidx.preference:preference-ktx:1.1.1"
|
||||||
implementation "androidx.recyclerview:recyclerview:1.3.2"
|
implementation "androidx.recyclerview:recyclerview:1.2.1"
|
||||||
implementation "androidx.viewpager2:viewpager2:1.1.0-rc01"
|
implementation "androidx.viewpager:viewpager:1.0.0"
|
||||||
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.0"
|
||||||
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
|
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
|
||||||
implementation "com.google.android.material:material:1.10.0"
|
implementation "com.google.android.material:material:1.4.0"
|
||||||
implementation "com.github.wulkanowy:material-chips-input:2.3.1"
|
implementation "com.github.wulkanowy:material-chips-input:2.3.1"
|
||||||
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
|
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
|
||||||
implementation 'com.github.lopspower:CircularImageView:4.3.0'
|
implementation 'com.github.lopspower:CircularImageView:4.2.0'
|
||||||
|
|
||||||
implementation "androidx.work:work-runtime:$work_manager"
|
implementation "androidx.work:work-runtime-ktx:$work_manager"
|
||||||
playImplementation "androidx.work:work-gcm:$work_manager"
|
playImplementation "androidx.work:work-gcm:$work_manager"
|
||||||
|
|
||||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.7.0"
|
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
|
||||||
|
|
||||||
implementation "androidx.room:room-runtime:$room"
|
implementation "androidx.room:room-runtime:$room"
|
||||||
implementation "androidx.room:room-ktx:$room"
|
implementation "androidx.room:room-ktx:$room"
|
||||||
ksp "androidx.room:room-compiler:$room"
|
kapt "androidx.room:room-compiler:$room"
|
||||||
|
|
||||||
implementation "com.google.dagger:hilt-android:$hilt_version"
|
implementation "com.google.dagger:hilt-android:$hilt_version"
|
||||||
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
|
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
|
||||||
|
@ -235,58 +198,50 @@ 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.moshi:moshi:$moshi"
|
||||||
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0"
|
implementation "com.squareup.moshi:moshi-adapters:$moshi"
|
||||||
implementation "com.squareup.okhttp3:logging-interceptor:4.12.0"
|
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi"
|
||||||
implementation "com.squareup.okhttp3:okhttp-urlconnection:4.12.0"
|
|
||||||
|
|
||||||
implementation "com.jakewharton.timber:timber:5.0.1"
|
implementation "com.jakewharton.timber:timber:5.0.1"
|
||||||
implementation 'com.github.Faierbel:slf4j-timber:2.0'
|
implementation "at.favre.lib:slf4j-timber:1.0.1"
|
||||||
implementation 'com.github.bastienpaulfr:Treessence:1.1.2'
|
implementation 'com.github.bastienpaulfr:Treessence:1.0.4'
|
||||||
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
|
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
|
||||||
implementation 'io.coil-kt:coil:2.6.0'
|
implementation "io.coil-kt:coil:1.3.2"
|
||||||
implementation "io.github.wulkanowy:AppKillerManager:3.0.1"
|
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
|
||||||
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
|
implementation 'me.xdrop:fuzzywuzzy:1.3.1'
|
||||||
implementation 'com.fredporciuncula:flow-preferences:1.9.1'
|
implementation 'com.fredporciuncula:flow-preferences:1.5.0'
|
||||||
implementation 'org.apache.commons:commons-text:1.12.0'
|
|
||||||
|
|
||||||
playImplementation platform('com.google.firebase:firebase-bom:33.0.0')
|
playImplementation platform('com.google.firebase:firebase-bom:28.4.1')
|
||||||
playImplementation 'com.google.firebase:firebase-analytics'
|
playImplementation 'com.google.firebase:firebase-analytics-ktx'
|
||||||
playImplementation 'com.google.firebase:firebase-messaging'
|
playImplementation 'com.google.firebase:firebase-messaging:'
|
||||||
playImplementation 'com.google.firebase:firebase-crashlytics:'
|
playImplementation 'com.google.firebase:firebase-crashlytics:'
|
||||||
playImplementation 'com.google.firebase:firebase-config'
|
playImplementation 'com.google.android.play:core:1.10.1'
|
||||||
|
playImplementation 'com.google.android.play:core-ktx:1.8.1'
|
||||||
|
|
||||||
playImplementation 'com.google.android.gms:play-services-ads:22.6.0'
|
hmsImplementation 'com.huawei.hms:hianalytics:6.2.0.301'
|
||||||
playImplementation "com.google.android.play:integrity:1.3.0"
|
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.0.300'
|
||||||
playImplementation 'com.google.android.play:app-update-ktx:2.1.0'
|
|
||||||
playImplementation 'com.google.android.play:review-ktx:2.0.1'
|
|
||||||
playImplementation "com.google.android.ump:user-messaging-platform:2.1.0"
|
|
||||||
|
|
||||||
hmsImplementation 'com.huawei.hms:hianalytics:6.12.0.301'
|
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
|
||||||
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.9.1.303'
|
|
||||||
|
|
||||||
releaseImplementation "com.github.chuckerteam.chucker:library-no-op:$chucker"
|
debugImplementation "com.github.ChuckerTeam.Chucker:library:$chucker"
|
||||||
|
debugImplementation 'com.github.amitshekhariitbhu.Android-Debug-Database:debug-db:v1.0.6'
|
||||||
debugImplementation "com.github.chuckerteam.chucker:library:$chucker"
|
|
||||||
debugImplementation 'com.github.amitshekhariitbhu.Android-Debug-Database:debug-db:1.0.6'
|
|
||||||
debugImplementation 'com.github.haroldadmin:WhatTheStack:1.0.0-alpha04'
|
|
||||||
|
|
||||||
testImplementation "junit:junit:4.13.2"
|
testImplementation "junit:junit:4.13.2"
|
||||||
testImplementation "io.mockk:mockk:$mockk"
|
testImplementation "io.mockk:mockk:$mockk"
|
||||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines"
|
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.5.2'
|
||||||
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.6.1'
|
||||||
testImplementation "androidx.test:runner:1.5.2"
|
testImplementation "androidx.test:runner:1.4.0"
|
||||||
testImplementation "androidx.test.ext:junit:1.1.5"
|
testImplementation "androidx.test.ext:junit:1.1.3"
|
||||||
testImplementation "androidx.test:core:1.5.0"
|
testImplementation "androidx.test:core:1.4.0"
|
||||||
testImplementation "androidx.room:room-testing:$room"
|
testImplementation "androidx.room:room-testing:$room"
|
||||||
testImplementation "com.google.dagger:hilt-android-testing:$hilt_version"
|
testImplementation "com.google.dagger:hilt-android-testing:$hilt_version"
|
||||||
kaptTest "com.google.dagger:hilt-android-compiler:$hilt_version"
|
kaptTest "com.google.dagger:hilt-android-compiler:$hilt_version"
|
||||||
|
|
||||||
androidTestImplementation "androidx.test:core:1.5.0"
|
androidTestImplementation "androidx.test:core:1.4.0"
|
||||||
androidTestImplementation "androidx.test:runner:1.5.2"
|
androidTestImplementation "androidx.test:runner:1.4.0"
|
||||||
androidTestImplementation "androidx.test.ext:junit:1.1.5"
|
androidTestImplementation "androidx.test.ext:junit:1.1.3"
|
||||||
androidTestImplementation "io.mockk:mockk-android:$mockk"
|
androidTestImplementation "io.mockk:mockk-android:$mockk"
|
||||||
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
apply plugin: "jacoco"
|
apply plugin: "jacoco"
|
||||||
|
|
||||||
jacoco {
|
jacoco {
|
||||||
toolVersion "0.8.11"
|
toolVersion "0.8.7"
|
||||||
reportsDirectory.set(file("$buildDir/reports"))
|
reportsDirectory.set(file("$buildDir/reports"))
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(Test).configureEach {
|
tasks.withType(Test) {
|
||||||
jacoco.includeNoLocationClasses = true
|
jacoco.includeNoLocationClasses = true
|
||||||
jacoco.excludes = ['jdk.internal.*']
|
jacoco.excludes = ['jdk.internal.*']
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register('jacocoTestReport', JacocoReport) {
|
task jacocoTestReport(type: JacocoReport) {
|
||||||
|
|
||||||
group = "Reporting"
|
group = "Reporting"
|
||||||
description = "Generate Jacoco coverage reports"
|
description = "Generate Jacoco coverage reports"
|
||||||
|
@ -33,19 +33,19 @@ tasks.register('jacocoTestReport', JacocoReport) {
|
||||||
'**/*_Factory.*']
|
'**/*_Factory.*']
|
||||||
|
|
||||||
classDirectories.setFrom(fileTree(
|
classDirectories.setFrom(fileTree(
|
||||||
dir: "$buildDir/intermediates/classes/debug",
|
dir: "$buildDir/intermediates/classes/debug",
|
||||||
excludes: excludes
|
excludes: excludes
|
||||||
) + fileTree(
|
) + fileTree(
|
||||||
dir: "$buildDir/tmp/kotlin-classes/fdroidDebug",
|
dir: "$buildDir/tmp/kotlin-classes/fdroidDebug",
|
||||||
excludes: excludes
|
excludes: excludes
|
||||||
))
|
))
|
||||||
|
|
||||||
sourceDirectories.setFrom(files([
|
sourceDirectories.setFrom(files([
|
||||||
"src/main/java",
|
"src/main/java",
|
||||||
"src/fdroid/java"
|
"src/fdroid/java"
|
||||||
]))
|
]))
|
||||||
executionData.setFrom(fileTree(
|
executionData.setFrom(fileTree(
|
||||||
dir: project.projectDir,
|
dir: project.projectDir,
|
||||||
includes: ["**/*.exec", "**/*.ec"]
|
includes: ["**/*.exec", "**/*.ec"]
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
BIN
app/key.p12.gpg
Normal file
BIN
app/key.p12.gpg
Normal file
Binary file not shown.
|
@ -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
|
||||||
|
|
16
app/proguard-rules.pro
vendored
16
app/proguard-rules.pro
vendored
|
@ -1,6 +1,5 @@
|
||||||
# General
|
# General
|
||||||
-dontobfuscate
|
-dontobfuscate
|
||||||
-ignorewarnings
|
|
||||||
|
|
||||||
|
|
||||||
#Config for wulkanowy
|
#Config for wulkanowy
|
||||||
|
@ -25,18 +24,3 @@
|
||||||
|
|
||||||
#Config for Material Components
|
#Config for Material Components
|
||||||
-keep class com.google.android.material.tabs.** { *; }
|
-keep class com.google.android.material.tabs.** { *; }
|
||||||
|
|
||||||
|
|
||||||
#Config for HMS SDK
|
|
||||||
-keepattributes *Annotation*
|
|
||||||
-keepattributes Exceptions
|
|
||||||
-keepattributes InnerClasses
|
|
||||||
-keepattributes Signature
|
|
||||||
-keep class com.huawei.agconnect.**{*;}
|
|
||||||
-keep class com.huawei.hianalytics.**{*;}
|
|
||||||
-keep class com.huawei.updatesdk.**{*;}
|
|
||||||
-keep class com.huawei.hms.**{*;}
|
|
||||||
|
|
||||||
|
|
||||||
#Config for Wulkanowy SDK
|
|
||||||
-keep,allowobfuscation,allowshrinking class retrofit2.Response
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -14,37 +14,34 @@ import kotlin.test.assertFailsWith
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class ScramblerTest {
|
class ScramblerTest {
|
||||||
|
|
||||||
private val scrambler = Scrambler(ApplicationProvider.getApplicationContext())
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun encryptDecryptTest() {
|
fun encryptDecryptTest() {
|
||||||
assertEquals(
|
assertEquals("TEST", decrypt(encrypt("TEST",
|
||||||
"TEST", scrambler.decrypt(scrambler.encrypt("TEST"))
|
ApplicationProvider.getApplicationContext())))
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun emptyTextEncryptTest() {
|
fun emptyTextEncryptTest() {
|
||||||
assertFailsWith<ScramblerException> {
|
assertFailsWith<ScramblerException> {
|
||||||
scrambler.decrypt("")
|
decrypt("")
|
||||||
}
|
}
|
||||||
|
|
||||||
assertFailsWith<ScramblerException> {
|
assertFailsWith<ScramblerException> {
|
||||||
scrambler.encrypt("")
|
encrypt("", ApplicationProvider.getApplicationContext())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SdkSuppress(minSdkVersion = 18)
|
@SdkSuppress(minSdkVersion = 18)
|
||||||
fun emptyKeyStoreTest() {
|
fun emptyKeyStoreTest() {
|
||||||
val text = scrambler.encrypt("test")
|
val text = encrypt("test", ApplicationProvider.getApplicationContext())
|
||||||
|
|
||||||
val keyStore = KeyStore.getInstance("AndroidKeyStore")
|
val keyStore = KeyStore.getInstance("AndroidKeyStore")
|
||||||
keyStore.load(null)
|
keyStore.load(null)
|
||||||
keyStore.deleteEntry("wulkanowy_password")
|
keyStore.deleteEntry("wulkanowy_password")
|
||||||
|
|
||||||
assertFailsWith<ScramblerException> {
|
assertFailsWith<ScramblerException> {
|
||||||
scrambler.decrypt(text)
|
decrypt(text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
33
app/src/debug/agconnect-services.json
Normal file
33
app/src/debug/agconnect-services.json
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"agcgw":{
|
||||||
|
"backurl":"connect-dre.dbankcloud.cn",
|
||||||
|
"url":"connect-dre.hispace.hicloud.com"
|
||||||
|
},
|
||||||
|
"client":{
|
||||||
|
"cp_id":"890048000024105546",
|
||||||
|
"product_id":"",
|
||||||
|
"client_id":"",
|
||||||
|
"client_secret":"",
|
||||||
|
"app_id":"101440411",
|
||||||
|
"package_name":"io.github.wulkanowy.dev",
|
||||||
|
"api_key":""
|
||||||
|
},
|
||||||
|
"service":{
|
||||||
|
"analytics":{
|
||||||
|
"collector_url":"datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn",
|
||||||
|
"resource_id":"p1",
|
||||||
|
"channel_id":""
|
||||||
|
},
|
||||||
|
"search":{
|
||||||
|
"url":"https://search-dre.cloud.huawei.com"
|
||||||
|
},
|
||||||
|
"cloudstorage":{
|
||||||
|
"storage_url":"https://ops-dre.agcstorage.link"
|
||||||
|
},
|
||||||
|
"ml":{
|
||||||
|
"mlservice_url":"ml-api-dre.ai.dbankcloud.com,ml-api-dre.ai.dbankcloud.cn"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"region":"DE",
|
||||||
|
"configuration_version":"1.0"
|
||||||
|
}
|
|
@ -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"
|
|
@ -1,6 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/colorIcon" />
|
<background android:drawable="@color/colorPrimary" />
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground_dev" />
|
<foreground android:drawable="@drawable/ic_launcher_foreground_dev" />
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_foreground_dev_mono" />
|
</adaptive-icon>
|
||||||
</adaptive-icon>
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/colorPrimary" />
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground_dev" />
|
||||||
|
</adaptive-icon>
|
BIN
app/src/debug/res/mipmap-hdpi/ic_launcher_round.png
Normal file
BIN
app/src/debug/res/mipmap-hdpi/ic_launcher_round.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
BIN
app/src/debug/res/mipmap-mdpi/ic_launcher_round.png
Normal file
BIN
app/src/debug/res/mipmap-mdpi/ic_launcher_round.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
BIN
app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
BIN
app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6 KiB |
BIN
app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
BIN
app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
BIN
app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
BIN
app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
|
@ -1,31 +0,0 @@
|
||||||
package io.github.wulkanowy.utils
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.view.View
|
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
||||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
|
||||||
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
class AdsHelper @Inject constructor(
|
|
||||||
@ApplicationContext private val context: Context,
|
|
||||||
private val preferencesRepository: PreferencesRepository
|
|
||||||
) {
|
|
||||||
|
|
||||||
val isMobileAdsSdkInitialized = MutableStateFlow(false)
|
|
||||||
val canShowAd = false
|
|
||||||
|
|
||||||
fun initialize() {
|
|
||||||
preferencesRepository.isAdsEnabled = false
|
|
||||||
preferencesRepository.selectedDashboardTiles -= DashboardItem.Tile.ADS
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("RedundantSuspendModifier", "UNUSED_PARAMETER")
|
|
||||||
suspend fun getDashboardTileAdBanner(width: Int): AdBanner {
|
|
||||||
throw IllegalStateException("Can't get ad banner (F-droid)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class AdBanner(val view: View)
|
|
|
@ -8,7 +8,15 @@ import javax.inject.Singleton
|
||||||
@Suppress("UNUSED_PARAMETER")
|
@Suppress("UNUSED_PARAMETER")
|
||||||
class AnalyticsHelper @Inject constructor() {
|
class AnalyticsHelper @Inject constructor() {
|
||||||
|
|
||||||
fun logEvent(name: String, vararg params: Pair<String, Any?>) = Unit
|
fun logEvent(name: String, vararg params: Pair<String, Any?>) {
|
||||||
fun setCurrentScreen(activity: Activity, name: String?) = Unit
|
// do nothing
|
||||||
fun popCurrentScreen(name: String?) = Unit
|
}
|
||||||
|
|
||||||
|
fun setCurrentScreen(activity: Activity, name: String?) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
fun popCurrentScreen(name: String?) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@Suppress("UNUSED_PARAMETER", "unused")
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class InAppReviewHelper @Inject constructor(
|
class InAppReviewHelper @Inject constructor(
|
||||||
@ApplicationContext private val context: Context
|
@ApplicationContext private val context: Context
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
package io.github.wulkanowy.utils
|
|
||||||
|
|
||||||
import android.view.View
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
class InAppUpdateHelper @Inject constructor() {
|
|
||||||
|
|
||||||
lateinit var messageContainer: View
|
|
||||||
|
|
||||||
fun checkAndInstallUpdates() {}
|
|
||||||
|
|
||||||
fun onResume() {}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
package io.github.wulkanowy.utils
|
|
||||||
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class IntegrityHelper @Inject constructor() {
|
|
||||||
|
|
||||||
@Suppress("UNUSED_PARAMETER")
|
|
||||||
fun getIntegrityToken(requestId: String): String? = null
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
package io.github.wulkanowy.utils
|
|
||||||
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class RemoteConfigHelper @Inject constructor() : BaseRemoteConfigHelper()
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package io.github.wulkanowy.utils
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.view.View
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
class UpdateHelper @Inject constructor() {
|
||||||
|
|
||||||
|
lateinit var messageContainer: View
|
||||||
|
|
||||||
|
fun checkAndInstallUpdates(activity: Activity) {}
|
||||||
|
|
||||||
|
fun onActivityResult(requestCode: Int, resultCode: Int) {}
|
||||||
|
|
||||||
|
fun onResume(activity: Activity) {}
|
||||||
|
}
|
|
@ -1,30 +0,0 @@
|
||||||
package io.github.wulkanowy.utils
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.view.View
|
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
||||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
|
||||||
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
class AdsHelper @Inject constructor(
|
|
||||||
@ApplicationContext private val context: Context,
|
|
||||||
private val preferencesRepository: PreferencesRepository
|
|
||||||
) {
|
|
||||||
val isMobileAdsSdkInitialized = MutableStateFlow(false)
|
|
||||||
val canShowAd = false
|
|
||||||
|
|
||||||
fun initialize() {
|
|
||||||
preferencesRepository.isAdsEnabled = false
|
|
||||||
preferencesRepository.selectedDashboardTiles -= DashboardItem.Tile.ADS
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("RedundantSuspendModifier", "UNUSED_PARAMETER")
|
|
||||||
suspend fun getDashboardTileAdBanner(width: Int): AdBanner {
|
|
||||||
throw IllegalStateException("Can't get ad banner (HMS)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class AdBanner(val view: View)
|
|
|
@ -3,38 +3,26 @@ package io.github.wulkanowy.utils
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.huawei.agconnect.crash.AGConnectCrash
|
|
||||||
import com.huawei.hms.analytics.HiAnalytics
|
import com.huawei.hms.analytics.HiAnalytics
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class AnalyticsHelper @Inject constructor(
|
class AnalyticsHelper @Inject constructor(
|
||||||
@ApplicationContext private val context: Context,
|
@ApplicationContext private val context: Context
|
||||||
preferencesRepository: PreferencesRepository,
|
|
||||||
appInfo: AppInfo,
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val analytics by lazy { HiAnalytics.getInstance(context) }
|
private val analytics by lazy { HiAnalytics.getInstance(context) }
|
||||||
|
|
||||||
private val connectCrash by lazy { AGConnectCrash.getInstance() }
|
|
||||||
|
|
||||||
init {
|
|
||||||
if (!appInfo.isDebug) {
|
|
||||||
connectCrash.setUserId(preferencesRepository.installationId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun logEvent(name: String, vararg params: Pair<String, Any?>) {
|
fun logEvent(name: String, vararg params: Pair<String, Any?>) {
|
||||||
Bundle().apply {
|
Bundle().apply {
|
||||||
params.forEach { (key, value) ->
|
params.forEach {
|
||||||
if (value == null) return@forEach
|
if (it.second == null) return@forEach
|
||||||
when (value) {
|
when (it.second) {
|
||||||
is String -> putString(key, value)
|
is String, is String? -> putString(it.first, it.second as String)
|
||||||
is Int -> putInt(key, value)
|
is Int, is Int? -> putInt(it.first, it.second as Int)
|
||||||
is Boolean -> putBoolean(key, value)
|
is Boolean, is Boolean? -> putBoolean(it.first, it.second as Boolean)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
analytics.onEvent(name, this)
|
analytics.onEvent(name, this)
|
||||||
|
|
|
@ -2,8 +2,7 @@ package io.github.wulkanowy.utils
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.huawei.agconnect.crash.AGConnectCrash
|
import com.huawei.agconnect.crash.AGConnectCrash
|
||||||
import fr.bipi.treessence.base.FormatterPriorityTree
|
import fr.bipi.tressence.base.FormatterPriorityTree
|
||||||
import fr.bipi.treessence.common.StackTraceRecorder
|
|
||||||
|
|
||||||
class CrashLogTree : FormatterPriorityTree(Log.VERBOSE) {
|
class CrashLogTree : FormatterPriorityTree(Log.VERBOSE) {
|
||||||
|
|
||||||
|
@ -23,10 +22,16 @@ class CrashLogExceptionTree : FormatterPriorityTree(Log.ERROR, ExceptionFilter)
|
||||||
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
|
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
|
||||||
if (skipLog(priority, tag, message, t)) return
|
if (skipLog(priority, tag, message, t)) return
|
||||||
|
|
||||||
|
// Disabled due to a bug in the Huawei library
|
||||||
|
|
||||||
|
/*connectCrash.setCustomKey("priority", priority)
|
||||||
|
connectCrash.setCustomKey("tag", tag.orEmpty())
|
||||||
|
connectCrash.setCustomKey("message", message)
|
||||||
|
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
connectCrash.recordException(t)
|
connectCrash.recordException(t)
|
||||||
} else {
|
} else {
|
||||||
connectCrash.recordException(StackTraceRecorder(format(priority, tag, message)))
|
connectCrash.recordException(StackTraceRecorder(format(priority, tag, message)))
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Suppress("UNUSED_PARAMETER", "unused")
|
|
||||||
class InAppReviewHelper @Inject constructor(
|
class InAppReviewHelper @Inject constructor(
|
||||||
@ApplicationContext private val context: Context
|
@ApplicationContext private val context: Context
|
||||||
) {
|
) {
|
||||||
|
@ -15,4 +14,4 @@ class InAppReviewHelper @Inject constructor(
|
||||||
fun showInAppReview(activity: MainActivity) {
|
fun showInAppReview(activity: MainActivity) {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,13 +0,0 @@
|
||||||
package io.github.wulkanowy.utils
|
|
||||||
|
|
||||||
import android.view.View
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
class InAppUpdateHelper @Inject constructor() {
|
|
||||||
|
|
||||||
lateinit var messageContainer: View
|
|
||||||
|
|
||||||
fun checkAndInstallUpdates() {}
|
|
||||||
|
|
||||||
fun onResume() {}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
package io.github.wulkanowy.utils
|
|
||||||
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class IntegrityHelper @Inject constructor() {
|
|
||||||
|
|
||||||
@Suppress("UNUSED_PARAMETER")
|
|
||||||
fun getIntegrityToken(requestId: String): String? = null
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
package io.github.wulkanowy.utils
|
|
||||||
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class RemoteConfigHelper @Inject constructor() : BaseRemoteConfigHelper()
|
|
17
app/src/hms/java/io/github/wulkanowy/utils/UpdateHelper.kt
Normal file
17
app/src/hms/java/io/github/wulkanowy/utils/UpdateHelper.kt
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package io.github.wulkanowy.utils
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.view.View
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
class UpdateHelper @Inject constructor() {
|
||||||
|
|
||||||
|
lateinit var messageContainer: View
|
||||||
|
|
||||||
|
fun checkAndInstallUpdates(activity: Activity) {}
|
||||||
|
|
||||||
|
fun onActivityResult(requestCode: Int, resultCode: Int) {}
|
||||||
|
|
||||||
|
fun onResume(activity: Activity) {}
|
||||||
|
}
|
|
@ -1,17 +1,14 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
package="io.github.wulkanowy"
|
||||||
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" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
||||||
|
|
||||||
<queries>
|
<queries>
|
||||||
<intent>
|
<intent>
|
||||||
|
@ -39,21 +36,18 @@
|
||||||
<application
|
<application
|
||||||
android:name=".WulkanowyApp"
|
android:name=".WulkanowyApp"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
|
||||||
android:enableOnBackInvokedCallback="true"
|
|
||||||
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:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:resizeableActivity="true"
|
|
||||||
android:supportsRtl="false"
|
android:supportsRtl="false"
|
||||||
android:theme="@style/WulkanowyTheme"
|
android:theme="@style/WulkanowyTheme"
|
||||||
tools:ignore="DataExtractionRules,UnusedAttribute">
|
android:usesCleartextTraffic="true"
|
||||||
|
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute">
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.modules.splash.SplashActivity"
|
android:name=".ui.modules.splash.SplashActivity"
|
||||||
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" />
|
||||||
|
@ -75,12 +69,11 @@
|
||||||
android:name=".ui.modules.message.send.SendMessageActivity"
|
android:name=".ui.modules.message.send.SendMessageActivity"
|
||||||
android:configChanges="orientation|screenSize"
|
android:configChanges="orientation|screenSize"
|
||||||
android:label="@string/send_message_title"
|
android:label="@string/send_message_title"
|
||||||
android:theme="@style/WulkanowyTheme.NoActionBar"
|
android:theme="@style/WulkanowyTheme.MessageSend"
|
||||||
android:windowSoftInputMode="adjustResize" />
|
android:windowSoftInputMode="adjustResize" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"
|
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
android:exported="true"
|
|
||||||
android:noHistory="true"
|
android:noHistory="true"
|
||||||
android:theme="@style/WulkanowyTheme.WidgetAccountSwitcher">
|
android:theme="@style/WulkanowyTheme.WidgetAccountSwitcher">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
@ -90,7 +83,6 @@
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity"
|
android:name=".ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
android:exported="true"
|
|
||||||
android:noHistory="true"
|
android:noHistory="true"
|
||||||
android:theme="@style/WulkanowyTheme.WidgetAccountSwitcher">
|
android:theme="@style/WulkanowyTheme.WidgetAccountSwitcher">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
@ -101,22 +93,6 @@
|
||||||
<service
|
<service
|
||||||
android:name=".services.widgets.TimetableWidgetService"
|
android:name=".services.widgets.TimetableWidgetService"
|
||||||
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||||
<service
|
|
||||||
android:name=".services.piggyback.VulcanNotificationListenerService"
|
|
||||||
android:exported="true"
|
|
||||||
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.service.notification.NotificationListenerService" />
|
|
||||||
</intent-filter>
|
|
||||||
</service>
|
|
||||||
<service
|
|
||||||
android:name=".services.messaging.AppMessagingService"
|
|
||||||
android:exported="false"
|
|
||||||
tools:ignore="MissingClass">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
|
||||||
</intent-filter>
|
|
||||||
</service>
|
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".ui.modules.timetablewidget.TimetableWidgetProvider"
|
android:name=".ui.modules.timetablewidget.TimetableWidgetProvider"
|
||||||
|
@ -131,7 +107,6 @@
|
||||||
</receiver>
|
</receiver>
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".ui.modules.luckynumberwidget.LuckyNumberWidgetProvider"
|
android:name=".ui.modules.luckynumberwidget.LuckyNumberWidgetProvider"
|
||||||
android:exported="true"
|
|
||||||
android:label="@string/lucky_number_title">
|
android:label="@string/lucky_number_title">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
|
@ -160,17 +135,41 @@
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="install_channel"
|
android:name="install_channel"
|
||||||
android:value="${install_channel}" />
|
android:value="${install_channel}" />
|
||||||
|
|
||||||
|
<!-- 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" />
|
||||||
|
|
||||||
|
<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" />
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.firebase.messaging.default_notification_channel_id"
|
android:name="com.google.firebase.messaging.default_notification_channel_id"
|
||||||
android:value="push_channel" />
|
android:value="push_channel" />
|
||||||
<meta-data
|
|
||||||
android:name="com.google.android.gms.ads.APPLICATION_ID"
|
|
||||||
android:value="${admob_project_id}" />
|
|
||||||
<meta-data
|
|
||||||
android:name="com.google.android.gms.ads.DELAY_APP_MEASUREMENT_INIT"
|
|
||||||
android:value="true" />
|
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
@ -50,13 +50,5 @@
|
||||||
{
|
{
|
||||||
"displayName": "Tomasz F.",
|
"displayName": "Tomasz F.",
|
||||||
"githubUsername": "Pengwius"
|
"githubUsername": "Pengwius"
|
||||||
},
|
|
||||||
{
|
|
||||||
"displayName": "Antoni Paduch",
|
|
||||||
"githubUsername": "janAte1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"displayName": "Kamil Wąsik",
|
|
||||||
"githubUsername": "JestemKamil"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
package io.github.wulkanowy
|
package io.github.wulkanowy
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.util.Log.DEBUG
|
import android.util.Log.DEBUG
|
||||||
import android.util.Log.INFO
|
import android.util.Log.INFO
|
||||||
import android.util.Log.VERBOSE
|
import android.util.Log.VERBOSE
|
||||||
|
import android.webkit.WebView
|
||||||
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.hilt.work.HiltWorkerFactory
|
import androidx.hilt.work.HiltWorkerFactory
|
||||||
import androidx.work.Configuration
|
import androidx.work.Configuration
|
||||||
import com.yariksoffice.lingver.Lingver
|
import com.yariksoffice.lingver.Lingver
|
||||||
import dagger.hilt.android.HiltAndroidApp
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
import fr.bipi.treessence.file.FileLoggerTree
|
import fr.bipi.tressence.file.FileLoggerTree
|
||||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||||
import io.github.wulkanowy.ui.base.ThemeManager
|
import io.github.wulkanowy.ui.base.ThemeManager
|
||||||
import io.github.wulkanowy.utils.ActivityLifecycleLogger
|
import io.github.wulkanowy.utils.ActivityLifecycleLogger
|
||||||
|
@ -17,13 +20,15 @@ import io.github.wulkanowy.utils.AppInfo
|
||||||
import io.github.wulkanowy.utils.CrashLogExceptionTree
|
import io.github.wulkanowy.utils.CrashLogExceptionTree
|
||||||
import io.github.wulkanowy.utils.CrashLogTree
|
import io.github.wulkanowy.utils.CrashLogTree
|
||||||
import io.github.wulkanowy.utils.DebugLogTree
|
import io.github.wulkanowy.utils.DebugLogTree
|
||||||
import io.github.wulkanowy.utils.RemoteConfigHelper
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltAndroidApp
|
@HiltAndroidApp
|
||||||
class WulkanowyApp : Application(), Configuration.Provider {
|
class WulkanowyApp : Application(), Configuration.Provider {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var workerFactory: HiltWorkerFactory
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var themeManager: ThemeManager
|
lateinit var themeManager: ThemeManager
|
||||||
|
|
||||||
|
@ -36,24 +41,14 @@ class WulkanowyApp : Application(), Configuration.Provider {
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var analyticsHelper: AnalyticsHelper
|
lateinit var analyticsHelper: AnalyticsHelper
|
||||||
|
|
||||||
@Inject
|
@SuppressLint("UnsafeOptInUsageWarning")
|
||||||
lateinit var remoteConfigHelper: RemoteConfigHelper
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var workerFactory: HiltWorkerFactory
|
|
||||||
|
|
||||||
override val workManagerConfiguration: Configuration
|
|
||||||
get() = Configuration.Builder()
|
|
||||||
.setWorkerFactory(workerFactory)
|
|
||||||
.setMinimumLoggingLevel(if (appInfo.isDebug) VERBOSE else INFO)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
FragmentManager.enableNewStateManager(false)
|
||||||
initializeAppLanguage()
|
initializeAppLanguage()
|
||||||
themeManager.applyDefaultTheme()
|
themeManager.applyDefaultTheme()
|
||||||
remoteConfigHelper.initialize()
|
|
||||||
initLogging()
|
initLogging()
|
||||||
|
fixWebViewLocale()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initLogging() {
|
private fun initLogging() {
|
||||||
|
@ -84,4 +79,18 @@ class WulkanowyApp : Application(), Configuration.Provider {
|
||||||
analyticsHelper.logEvent("language", "startup" to preferencesRepository.appLanguage)
|
analyticsHelper.logEvent("language", "startup" to preferencesRepository.appLanguage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun fixWebViewLocale() {
|
||||||
|
//https://stackoverflow.com/questions/40398528/android-webview-language-changes-abruptly-on-android-7-0-and-above
|
||||||
|
try {
|
||||||
|
WebView(this).destroy()
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
//Ignore exceptions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getWorkManagerConfiguration() = Configuration.Builder()
|
||||||
|
.setWorkerFactory(workerFactory)
|
||||||
|
.setMinimumLoggingLevel(if (appInfo.isDebug) VERBOSE else INFO)
|
||||||
|
.build()
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,94 +2,62 @@ package io.github.wulkanowy.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import android.content.res.AssetManager
|
||||||
|
import android.content.res.Resources
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.chuckerteam.chucker.api.ChuckerCollector
|
import com.chuckerteam.chucker.api.ChuckerCollector
|
||||||
import com.chuckerteam.chucker.api.ChuckerInterceptor
|
import com.chuckerteam.chucker.api.ChuckerInterceptor
|
||||||
import com.chuckerteam.chucker.api.RetentionManager
|
import com.chuckerteam.chucker.api.RetentionManager
|
||||||
|
import com.squareup.moshi.Moshi
|
||||||
import com.fredporciuncula.flow.preferences.FlowSharedPreferences
|
import com.fredporciuncula.flow.preferences.FlowSharedPreferences
|
||||||
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
|
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
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.services.WulkanowyService
|
|
||||||
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 kotlinx.serialization.json.Json
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import timber.log.Timber
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.logging.HttpLoggingInterceptor
|
|
||||||
import retrofit2.Retrofit
|
|
||||||
import retrofit2.create
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@InstallIn(SingletonComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
internal class DataModule {
|
internal class RepositoryModule {
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Provides
|
||||||
|
fun provideSdk(chuckerCollector: ChuckerCollector, @ApplicationContext context: Context): Sdk {
|
||||||
|
return Sdk().apply {
|
||||||
|
androidVersion = android.os.Build.VERSION.RELEASE
|
||||||
|
buildTag = android.os.Build.MODEL
|
||||||
|
setSimpleHttpLogger { Timber.d(it) }
|
||||||
|
|
||||||
|
// for debug only
|
||||||
|
addInterceptor(
|
||||||
|
ChuckerInterceptor.Builder(context)
|
||||||
|
.collector(chuckerCollector)
|
||||||
|
.alwaysReadResponseBody(true)
|
||||||
|
.build(), network = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideChuckerCollector(
|
fun provideChuckerCollector(
|
||||||
@ApplicationContext context: Context,
|
@ApplicationContext context: Context,
|
||||||
prefRepository: PreferencesRepository
|
prefRepository: PreferencesRepository
|
||||||
) = ChuckerCollector(
|
): ChuckerCollector {
|
||||||
context = context,
|
return ChuckerCollector(
|
||||||
showNotification = prefRepository.isDebugNotificationEnable,
|
context = context,
|
||||||
retentionPeriod = RetentionManager.Period.ONE_HOUR
|
showNotification = prefRepository.isDebugNotificationEnable,
|
||||||
)
|
retentionPeriod = RetentionManager.Period.ONE_HOUR
|
||||||
|
)
|
||||||
@Singleton
|
}
|
||||||
@Provides
|
|
||||||
fun provideChuckerInterceptor(
|
|
||||||
@ApplicationContext context: Context,
|
|
||||||
chuckerCollector: ChuckerCollector
|
|
||||||
) = ChuckerInterceptor.Builder(context)
|
|
||||||
.collector(chuckerCollector)
|
|
||||||
.alwaysReadResponseBody(true)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
fun provideOkHttpClient(chuckerInterceptor: ChuckerInterceptor): OkHttpClient =
|
|
||||||
OkHttpClient.Builder()
|
|
||||||
.addNetworkInterceptor(chuckerInterceptor)
|
|
||||||
.addInterceptor(HttpLoggingInterceptor().apply {
|
|
||||||
level = HttpLoggingInterceptor.Level.BASIC
|
|
||||||
})
|
|
||||||
.connectTimeout(30, TimeUnit.SECONDS)
|
|
||||||
.readTimeout(30, TimeUnit.SECONDS)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
fun provideAdminMessageService(
|
|
||||||
okHttpClient: OkHttpClient,
|
|
||||||
json: Json,
|
|
||||||
appInfo: AppInfo
|
|
||||||
): WulkanowyService = Retrofit.Builder()
|
|
||||||
.baseUrl(appInfo.messagesBaseUrl)
|
|
||||||
.client(okHttpClient)
|
|
||||||
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
|
|
||||||
.build()
|
|
||||||
.create()
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
fun provideSchoolsService(
|
|
||||||
okHttpClient: OkHttpClient,
|
|
||||||
json: Json,
|
|
||||||
appInfo: AppInfo,
|
|
||||||
): SchoolsService = Retrofit.Builder()
|
|
||||||
.baseUrl(appInfo.schoolsBaseUrl)
|
|
||||||
.client(okHttpClient)
|
|
||||||
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
|
|
||||||
.build()
|
|
||||||
.create()
|
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
|
@ -99,11 +67,20 @@ internal class DataModule {
|
||||||
appInfo: AppInfo
|
appInfo: AppInfo
|
||||||
) = AppDatabase.newInstance(context, sharedPrefProvider, appInfo)
|
) = AppDatabase.newInstance(context, sharedPrefProvider, appInfo)
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Provides
|
||||||
|
fun provideResources(@ApplicationContext context: Context): Resources = context.resources
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Provides
|
||||||
|
fun provideAssets(@ApplicationContext context: Context): AssetManager = context.assets
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideSharedPref(@ApplicationContext context: Context): SharedPreferences =
|
fun provideSharedPref(@ApplicationContext context: Context): SharedPreferences =
|
||||||
PreferenceManager.getDefaultSharedPreferences(context)
|
PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideFlowSharedPref(sharedPreferences: SharedPreferences) =
|
fun provideFlowSharedPref(sharedPreferences: SharedPreferences) =
|
||||||
|
@ -111,9 +88,7 @@ internal class DataModule {
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideJson() = Json {
|
fun provideMoshi() = Moshi.Builder().build()
|
||||||
ignoreUnknownKeys = true
|
|
||||||
}
|
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
|
@ -190,7 +165,7 @@ internal class DataModule {
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideMailboxesDao(database: AppDatabase) = database.mailboxDao
|
fun provideReportingUnitDao(database: AppDatabase) = database.reportingUnitDao
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
|
@ -227,20 +202,4 @@ internal class DataModule {
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideSchoolAnnouncementDao(database: AppDatabase) = database.schoolAnnouncementDao
|
fun provideSchoolAnnouncementDao(database: AppDatabase) = database.schoolAnnouncementDao
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
fun provideNotificationDao(database: AppDatabase) = database.notificationDao
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
fun provideAdminMessageDao(database: AppDatabase) = database.adminMessagesDao
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
fun provideMutesDao(database: AppDatabase) = database.mutedMessageSendersDao
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
fun provideGradeDescriptiveDao(database: AppDatabase) = database.gradeDescriptiveDao
|
|
||||||
}
|
}
|
|
@ -1,285 +1,23 @@
|
||||||
package io.github.wulkanowy.data
|
package io.github.wulkanowy.data
|
||||||
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
data class Resource<T>(val status: Status, val data: T?, val error: Throwable?) {
|
||||||
import kotlinx.coroutines.FlowPreview
|
companion object {
|
||||||
import kotlinx.coroutines.flow.Flow
|
fun <T> success(data: T?): Resource<T> {
|
||||||
import kotlinx.coroutines.flow.catch
|
return Resource(Status.SUCCESS, data, null)
|
||||||
import kotlinx.coroutines.flow.collect
|
|
||||||
import kotlinx.coroutines.flow.combine
|
|
||||||
import kotlinx.coroutines.flow.debounce
|
|
||||||
import kotlinx.coroutines.flow.emitAll
|
|
||||||
import kotlinx.coroutines.flow.filter
|
|
||||||
import kotlinx.coroutines.flow.filterNot
|
|
||||||
import kotlinx.coroutines.flow.first
|
|
||||||
import kotlinx.coroutines.flow.flatMapLatest
|
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import kotlinx.coroutines.flow.flowOf
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.flow.onEach
|
|
||||||
import kotlinx.coroutines.flow.takeWhile
|
|
||||||
import kotlinx.coroutines.sync.Mutex
|
|
||||||
import kotlinx.coroutines.sync.withLock
|
|
||||||
import timber.log.Timber
|
|
||||||
import kotlin.time.Duration
|
|
||||||
import kotlin.time.Duration.Companion.seconds
|
|
||||||
|
|
||||||
sealed interface Resource<out T> {
|
|
||||||
/**
|
|
||||||
* The initial value of a resource flow. Indicates no data that is currently available to be shown,
|
|
||||||
* 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>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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?
|
|
||||||
get() = when (this) {
|
|
||||||
is Resource.Success -> this.data
|
|
||||||
is Resource.Intermediate -> this.data
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
|
|
||||||
val <T> Resource<T>.dataOrThrow: T
|
|
||||||
get() = when (this) {
|
|
||||||
is Resource.Success -> this.data
|
|
||||||
is Resource.Intermediate -> this.data
|
|
||||||
is Resource.Loading -> throw IllegalStateException("Resource is in loading state")
|
|
||||||
is Resource.Error -> throw this.error
|
|
||||||
}
|
|
||||||
|
|
||||||
val <T> Resource<T>.errorOrNull: Throwable?
|
|
||||||
get() = when (this) {
|
|
||||||
is Resource.Error -> this.error
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> resourceFlow(block: suspend () -> T) = flow {
|
|
||||||
emit(Resource.Loading())
|
|
||||||
emit(Resource.Success(block()))
|
|
||||||
}.catch { emit(Resource.Error(it)) }
|
|
||||||
|
|
||||||
fun <T> flatResourceFlow(block: suspend () -> Flow<Resource<T>>) = flow {
|
|
||||||
emit(Resource.Loading())
|
|
||||||
emitAll(block().filter { it is Resource.Intermediate || it !is Resource.Loading })
|
|
||||||
}.catch { emit(Resource.Error(it)) }
|
|
||||||
|
|
||||||
fun <T, U> Resource<T>.mapData(block: (T) -> U) = when (this) {
|
|
||||||
is Resource.Success -> Resource.Success(block(this.data))
|
|
||||||
is Resource.Intermediate -> Resource.Intermediate(block(this.data))
|
|
||||||
is Resource.Loading -> Resource.Loading()
|
|
||||||
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 {
|
|
||||||
val description = when (it) {
|
|
||||||
is Resource.Intermediate -> "intermediate data received" + if (showData) " (data: `${it.data}`)" else ""
|
|
||||||
is Resource.Loading -> "started"
|
|
||||||
is Resource.Success -> "success" + if (showData) " (data: `${it.data}`)" else ""
|
|
||||||
is Resource.Error -> "exception occurred: ${it.error}"
|
|
||||||
}
|
|
||||||
Timber.i("$name: $description")
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <T, U> Flow<Resource<T>>.mapResourceData(crossinline block: suspend (T) -> U) = map {
|
|
||||||
when (it) {
|
|
||||||
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())
|
fun <T> error(error: Throwable?, data: T? = null): Resource<T> {
|
||||||
is Resource.Error -> flowOf(Resource.Error(it.error))
|
return Resource(Status.ERROR, data, error)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> Flow<Resource<T>>.onResourceData(block: suspend (T) -> Unit) = onEach {
|
|
||||||
when (it) {
|
|
||||||
is Resource.Success -> block(it.data)
|
|
||||||
is Resource.Intermediate -> block(it.data)
|
|
||||||
is Resource.Error,
|
|
||||||
is Resource.Loading -> Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> Flow<Resource<T>>.onResourceLoading(block: suspend () -> Unit) = onEach {
|
|
||||||
if (it is Resource.Loading) {
|
|
||||||
block()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> Flow<Resource<T>>.onResourceIntermediate(block: suspend (T) -> Unit) = onEach {
|
|
||||||
if (it is Resource.Intermediate) {
|
|
||||||
block(it.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> Flow<Resource<T>>.onResourceSuccess(block: suspend (T) -> Unit) = onEach {
|
|
||||||
if (it is Resource.Success) {
|
|
||||||
block(it.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> Flow<Resource<T>>.onResourceError(block: suspend (Throwable) -> Unit) = onEach {
|
|
||||||
if (it is Resource.Error) {
|
|
||||||
block(it.error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> Flow<Resource<T>>.onResourceNotLoading(block: suspend () -> Unit) = onEach {
|
|
||||||
if (it !is Resource.Loading) {
|
|
||||||
block()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun <T> Flow<Resource<T>>.toFirstResult() = filter { it !is Resource.Loading }.first()
|
|
||||||
|
|
||||||
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,
|
|
||||||
// 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.
|
fun <T> loading(data: T? = null): Resource<T> {
|
||||||
// mapData is functionally useless and exists only to satisfy the type checker
|
return Resource(Status.LOADING, data, null)
|
||||||
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
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class Status {
|
||||||
inline fun <OutputType, ApiType> networkBoundResource(
|
LOADING,
|
||||||
mutex: Mutex = Mutex(),
|
SUCCESS,
|
||||||
crossinline isResultEmpty: (OutputType) -> Boolean,
|
ERROR
|
||||||
crossinline query: () -> Flow<OutputType>,
|
|
||||||
crossinline fetch: suspend () -> ApiType,
|
|
||||||
crossinline saveFetchResult: suspend (old: OutputType, new: ApiType) -> Unit,
|
|
||||||
crossinline shouldFetch: (OutputType) -> Boolean = { true },
|
|
||||||
crossinline filterResult: (OutputType) -> OutputType = { it }
|
|
||||||
) = networkBoundResource(
|
|
||||||
mutex = mutex,
|
|
||||||
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 {
|
|
||||||
emit(Resource.Loading())
|
|
||||||
|
|
||||||
val data = query().first()
|
|
||||||
if (shouldFetch(data)) {
|
|
||||||
emit(Resource.Intermediate(data))
|
|
||||||
|
|
||||||
try {
|
|
||||||
val newData = fetch()
|
|
||||||
mutex.withLock { saveFetchResult(query().first(), newData) }
|
|
||||||
} catch (throwable: Throwable) {
|
|
||||||
emit(Resource.Error(throwable))
|
|
||||||
return@flow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
emitAll(query().map { Resource.Success(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,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(),
|
|
||||||
)
|
|
|
@ -1,14 +0,0 @@
|
||||||
package io.github.wulkanowy.data.api.services
|
|
||||||
|
|
||||||
import io.github.wulkanowy.data.pojos.IntegrityRequest
|
|
||||||
import io.github.wulkanowy.data.pojos.LoginEvent
|
|
||||||
import retrofit2.http.Body
|
|
||||||
import retrofit2.http.POST
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
interface SchoolsService {
|
|
||||||
|
|
||||||
@POST("/log/loginEvent")
|
|
||||||
suspend fun logLoginEvent(@Body request: IntegrityRequest<LoginEvent>)
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
package io.github.wulkanowy.data.api.services
|
|
||||||
|
|
||||||
import io.github.wulkanowy.data.api.models.Mapping
|
|
||||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
|
||||||
import retrofit2.http.GET
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
interface WulkanowyService {
|
|
||||||
|
|
||||||
@GET("/v1.json")
|
|
||||||
suspend fun getAdminMessages(): List<AdminMessage>
|
|
||||||
|
|
||||||
@GET("/mapping2.json")
|
|
||||||
suspend fun getMapping(): Mapping
|
|
||||||
}
|
|
|
@ -1,35 +1,30 @@
|
||||||
package io.github.wulkanowy.data.db
|
package io.github.wulkanowy.data.db
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.room.AutoMigration
|
|
||||||
import androidx.room.Database
|
import androidx.room.Database
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
import androidx.room.RoomDatabase.JournalMode.TRUNCATE
|
import androidx.room.RoomDatabase.JournalMode.TRUNCATE
|
||||||
import androidx.room.TypeConverters
|
import androidx.room.TypeConverters
|
||||||
import io.github.wulkanowy.data.db.dao.AdminMessageDao
|
|
||||||
import io.github.wulkanowy.data.db.dao.AttendanceDao
|
import io.github.wulkanowy.data.db.dao.AttendanceDao
|
||||||
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
|
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
|
||||||
import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
|
import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
|
||||||
import io.github.wulkanowy.data.db.dao.ConferenceDao
|
import io.github.wulkanowy.data.db.dao.ConferenceDao
|
||||||
|
import io.github.wulkanowy.data.db.dao.SchoolAnnouncementDao
|
||||||
import io.github.wulkanowy.data.db.dao.ExamDao
|
import io.github.wulkanowy.data.db.dao.ExamDao
|
||||||
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.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
|
||||||
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
|
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
|
||||||
import io.github.wulkanowy.data.db.dao.HomeworkDao
|
import io.github.wulkanowy.data.db.dao.HomeworkDao
|
||||||
import io.github.wulkanowy.data.db.dao.LuckyNumberDao
|
import io.github.wulkanowy.data.db.dao.LuckyNumberDao
|
||||||
import io.github.wulkanowy.data.db.dao.MailboxDao
|
|
||||||
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
|
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
|
||||||
import io.github.wulkanowy.data.db.dao.MessagesDao
|
import io.github.wulkanowy.data.db.dao.MessagesDao
|
||||||
import io.github.wulkanowy.data.db.dao.MobileDeviceDao
|
import io.github.wulkanowy.data.db.dao.MobileDeviceDao
|
||||||
import io.github.wulkanowy.data.db.dao.MutedMessageSendersDao
|
|
||||||
import io.github.wulkanowy.data.db.dao.NoteDao
|
import io.github.wulkanowy.data.db.dao.NoteDao
|
||||||
import io.github.wulkanowy.data.db.dao.NotificationDao
|
|
||||||
import io.github.wulkanowy.data.db.dao.RecipientDao
|
import io.github.wulkanowy.data.db.dao.RecipientDao
|
||||||
import io.github.wulkanowy.data.db.dao.SchoolAnnouncementDao
|
import io.github.wulkanowy.data.db.dao.ReportingUnitDao
|
||||||
import io.github.wulkanowy.data.db.dao.SchoolDao
|
import io.github.wulkanowy.data.db.dao.SchoolDao
|
||||||
import io.github.wulkanowy.data.db.dao.SemesterDao
|
import io.github.wulkanowy.data.db.dao.SemesterDao
|
||||||
import io.github.wulkanowy.data.db.dao.StudentDao
|
import io.github.wulkanowy.data.db.dao.StudentDao
|
||||||
|
@ -39,30 +34,26 @@ import io.github.wulkanowy.data.db.dao.TeacherDao
|
||||||
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
|
||||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
|
||||||
import io.github.wulkanowy.data.db.entities.Attendance
|
import io.github.wulkanowy.data.db.entities.Attendance
|
||||||
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
||||||
import io.github.wulkanowy.data.db.entities.CompletedLesson
|
import io.github.wulkanowy.data.db.entities.CompletedLesson
|
||||||
import io.github.wulkanowy.data.db.entities.Conference
|
import io.github.wulkanowy.data.db.entities.Conference
|
||||||
|
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
||||||
import io.github.wulkanowy.data.db.entities.Exam
|
import io.github.wulkanowy.data.db.entities.Exam
|
||||||
import io.github.wulkanowy.data.db.entities.Grade
|
import io.github.wulkanowy.data.db.entities.Grade
|
||||||
import io.github.wulkanowy.data.db.entities.GradeDescriptive
|
|
||||||
import io.github.wulkanowy.data.db.entities.GradePartialStatistics
|
import io.github.wulkanowy.data.db.entities.GradePartialStatistics
|
||||||
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
||||||
import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
|
import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
|
||||||
import io.github.wulkanowy.data.db.entities.GradeSummary
|
import io.github.wulkanowy.data.db.entities.GradeSummary
|
||||||
import io.github.wulkanowy.data.db.entities.Homework
|
import io.github.wulkanowy.data.db.entities.Homework
|
||||||
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
||||||
import io.github.wulkanowy.data.db.entities.Mailbox
|
|
||||||
import io.github.wulkanowy.data.db.entities.Message
|
import io.github.wulkanowy.data.db.entities.Message
|
||||||
import io.github.wulkanowy.data.db.entities.MessageAttachment
|
import io.github.wulkanowy.data.db.entities.MessageAttachment
|
||||||
import io.github.wulkanowy.data.db.entities.MobileDevice
|
import io.github.wulkanowy.data.db.entities.MobileDevice
|
||||||
import io.github.wulkanowy.data.db.entities.MutedMessageSender
|
|
||||||
import io.github.wulkanowy.data.db.entities.Note
|
import io.github.wulkanowy.data.db.entities.Note
|
||||||
import io.github.wulkanowy.data.db.entities.Notification
|
|
||||||
import io.github.wulkanowy.data.db.entities.Recipient
|
import io.github.wulkanowy.data.db.entities.Recipient
|
||||||
|
import io.github.wulkanowy.data.db.entities.ReportingUnit
|
||||||
import io.github.wulkanowy.data.db.entities.School
|
import io.github.wulkanowy.data.db.entities.School
|
||||||
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.db.entities.StudentInfo
|
import io.github.wulkanowy.data.db.entities.StudentInfo
|
||||||
|
@ -104,23 +95,8 @@ import io.github.wulkanowy.data.db.migrations.Migration37
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration38
|
import io.github.wulkanowy.data.db.migrations.Migration38
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration39
|
import io.github.wulkanowy.data.db.migrations.Migration39
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration4
|
import io.github.wulkanowy.data.db.migrations.Migration4
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration40
|
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration41
|
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration42
|
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration43
|
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration44
|
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration46
|
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration49
|
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration5
|
import io.github.wulkanowy.data.db.migrations.Migration5
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration50
|
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration51
|
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration53
|
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration54
|
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration55
|
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration57
|
|
||||||
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
|
||||||
|
@ -148,7 +124,7 @@ import javax.inject.Singleton
|
||||||
Subject::class,
|
Subject::class,
|
||||||
LuckyNumber::class,
|
LuckyNumber::class,
|
||||||
CompletedLesson::class,
|
CompletedLesson::class,
|
||||||
Mailbox::class,
|
ReportingUnit::class,
|
||||||
Recipient::class,
|
Recipient::class,
|
||||||
MobileDevice::class,
|
MobileDevice::class,
|
||||||
Teacher::class,
|
Teacher::class,
|
||||||
|
@ -158,26 +134,6 @@ import javax.inject.Singleton
|
||||||
StudentInfo::class,
|
StudentInfo::class,
|
||||||
TimetableHeader::class,
|
TimetableHeader::class,
|
||||||
SchoolAnnouncement::class,
|
SchoolAnnouncement::class,
|
||||||
Notification::class,
|
|
||||||
AdminMessage::class,
|
|
||||||
MutedMessageSender::class,
|
|
||||||
GradeDescriptive::class,
|
|
||||||
],
|
|
||||||
autoMigrations = [
|
|
||||||
AutoMigration(from = 44, to = 45),
|
|
||||||
AutoMigration(from = 46, to = 47),
|
|
||||||
AutoMigration(from = 47, to = 48),
|
|
||||||
AutoMigration(from = 51, to = 52),
|
|
||||||
AutoMigration(from = 54, to = 55, spec = Migration55::class),
|
|
||||||
AutoMigration(from = 55, to = 56),
|
|
||||||
AutoMigration(from = 56, to = 57, spec = Migration57::class),
|
|
||||||
AutoMigration(from = 57, to = 58, spec = Migration58::class),
|
|
||||||
AutoMigration(from = 58, to = 59),
|
|
||||||
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 +142,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 = 39
|
||||||
|
|
||||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
||||||
Migration2(),
|
Migration2(),
|
||||||
|
@ -227,17 +183,6 @@ abstract class AppDatabase : RoomDatabase() {
|
||||||
Migration37(),
|
Migration37(),
|
||||||
Migration38(),
|
Migration38(),
|
||||||
Migration39(),
|
Migration39(),
|
||||||
Migration40(),
|
|
||||||
Migration41(sharedPrefProvider),
|
|
||||||
Migration42(),
|
|
||||||
Migration43(),
|
|
||||||
Migration44(),
|
|
||||||
Migration46(),
|
|
||||||
Migration49(),
|
|
||||||
Migration50(),
|
|
||||||
Migration51(),
|
|
||||||
Migration53(),
|
|
||||||
Migration54(),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun newInstance(
|
fun newInstance(
|
||||||
|
@ -288,7 +233,7 @@ abstract class AppDatabase : RoomDatabase() {
|
||||||
|
|
||||||
abstract val completedLessonsDao: CompletedLessonsDao
|
abstract val completedLessonsDao: CompletedLessonsDao
|
||||||
|
|
||||||
abstract val mailboxDao: MailboxDao
|
abstract val reportingUnitDao: ReportingUnitDao
|
||||||
|
|
||||||
abstract val recipientDao: RecipientDao
|
abstract val recipientDao: RecipientDao
|
||||||
|
|
||||||
|
@ -307,12 +252,4 @@ abstract class AppDatabase : RoomDatabase() {
|
||||||
abstract val timetableHeaderDao: TimetableHeaderDao
|
abstract val timetableHeaderDao: TimetableHeaderDao
|
||||||
|
|
||||||
abstract val schoolAnnouncementDao: SchoolAnnouncementDao
|
abstract val schoolAnnouncementDao: SchoolAnnouncementDao
|
||||||
|
|
||||||
abstract val notificationDao: NotificationDao
|
|
||||||
|
|
||||||
abstract val adminMessagesDao: AdminMessageDao
|
|
||||||
|
|
||||||
abstract val mutedMessageSendersDao: MutedMessageSendersDao
|
|
||||||
|
|
||||||
abstract val gradeDescriptiveDao: GradeDescriptiveDao
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,47 @@
|
||||||
package io.github.wulkanowy.data.db
|
package io.github.wulkanowy.data.db
|
||||||
|
|
||||||
import androidx.room.TypeConverter
|
import androidx.room.TypeConverter
|
||||||
import io.github.wulkanowy.data.enums.MessageType
|
import com.squareup.moshi.Moshi
|
||||||
import io.github.wulkanowy.ui.modules.Destination
|
import com.squareup.moshi.Types
|
||||||
import io.github.wulkanowy.utils.toTimestamp
|
import io.github.wulkanowy.data.db.adapters.PairAdapterFactory
|
||||||
import kotlinx.serialization.SerializationException
|
|
||||||
import kotlinx.serialization.decodeFromString
|
|
||||||
import kotlinx.serialization.encodeToString
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import java.time.*
|
|
||||||
import java.util.*
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
import java.time.LocalDateTime
|
||||||
import java.time.Month
|
import java.time.Month
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
import java.util.*
|
import java.util.Date
|
||||||
|
|
||||||
class Converters {
|
class Converters {
|
||||||
|
|
||||||
private val json = Json
|
private val moshi by lazy { Moshi.Builder().add(PairAdapterFactory).build() }
|
||||||
|
|
||||||
|
private val integerListAdapter by lazy {
|
||||||
|
moshi.adapter<List<Int>>(Types.newParameterizedType(List::class.java, Integer::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
|
private val stringListPairAdapter by lazy {
|
||||||
|
moshi.adapter<List<Pair<String, String>>>(Types.newParameterizedType(List::class.java, Pair::class.java, String::class.java, String::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun timestampToLocalDate(value: Long?): LocalDate? =
|
fun timestampToDate(value: Long?): LocalDate? = value?.run {
|
||||||
value?.let(::Date)?.toInstant()?.atZone(ZoneOffset.UTC)?.toLocalDate()
|
Date(value).toInstant().atZone(ZoneOffset.UTC).toLocalDate()
|
||||||
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun dateToTimestamp(date: LocalDate?): Long? = date?.toTimestamp()
|
fun dateToTimestamp(date: LocalDate?): Long? {
|
||||||
|
return date?.atStartOfDay()?.toInstant(ZoneOffset.UTC)?.toEpochMilli()
|
||||||
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun instantToTimestamp(instant: Instant?): Long? = instant?.toEpochMilli()
|
fun timestampToTime(value: Long?): LocalDateTime? = value?.let {
|
||||||
|
LocalDateTime.ofInstant(Instant.ofEpochMilli(value), ZoneOffset.UTC)
|
||||||
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun timestampToInstant(timestamp: Long?): Instant? = timestamp?.let(Instant::ofEpochMilli)
|
fun timeToTimestamp(date: LocalDateTime?): Long? {
|
||||||
|
return date?.atZone(ZoneOffset.UTC)?.toInstant()?.toEpochMilli()
|
||||||
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun monthToInt(month: Month?) = month?.value
|
fun monthToInt(month: Month?) = month?.value
|
||||||
|
@ -41,37 +51,21 @@ class Converters {
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun intListToJson(list: List<Int>): String {
|
fun intListToJson(list: List<Int>): String {
|
||||||
return json.encodeToString(list)
|
return integerListAdapter.toJson(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun jsonToIntList(value: String): List<Int> {
|
fun jsonToIntList(value: String): List<Int> {
|
||||||
return json.decodeFromString(value)
|
return integerListAdapter.fromJson(value).orEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun stringPairListToJson(list: List<Pair<String, String>>): String {
|
fun stringPairListToJson(list: List<Pair<String, String>>): String {
|
||||||
return json.encodeToString(list)
|
return stringListPairAdapter.toJson(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun jsonToStringPairList(value: String): List<Pair<String, String>> {
|
fun jsonToStringPairList(value: String): List<Pair<String, String>> {
|
||||||
return try {
|
return stringListPairAdapter.fromJson(value).orEmpty()
|
||||||
json.decodeFromString(value)
|
|
||||||
} catch (e: SerializationException) {
|
|
||||||
emptyList() // handle errors from old gson Pair serialized data
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@TypeConverter
|
|
||||||
fun destinationToString(destination: Destination) = json.encodeToString(destination)
|
|
||||||
|
|
||||||
@TypeConverter
|
|
||||||
fun stringToDestination(destination: String): Destination = json.decodeFromString(destination)
|
|
||||||
|
|
||||||
@TypeConverter
|
|
||||||
fun messageTypesToString(types: List<MessageType>): String = json.encodeToString(types)
|
|
||||||
|
|
||||||
@TypeConverter
|
|
||||||
fun stringToMessageTypes(text: String): List<MessageType> = json.decodeFromString(text)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,14 +22,11 @@ class SharedPrefProvider @Inject constructor(
|
||||||
|
|
||||||
fun getString(key: String) = sharedPref.getString(key, null)
|
fun getString(key: String) = sharedPref.getString(key, null)
|
||||||
|
|
||||||
fun getString(key: String, defaultValue: String): String =
|
fun getString(key: String, defaultValue: String): String = sharedPref.getString(key, defaultValue) ?: defaultValue
|
||||||
sharedPref.getString(key, defaultValue) ?: defaultValue
|
|
||||||
|
|
||||||
fun getBoolean(key: String, defaultValue: Boolean): Boolean =
|
fun getBoolean(key: String, defaultValue: Boolean): Boolean = sharedPref.getBoolean(key, defaultValue)
|
||||||
sharedPref.getBoolean(key, defaultValue)
|
|
||||||
|
|
||||||
fun putBoolean(key: String, value: Boolean, sync: Boolean = false) =
|
fun putBoolean(key: String, value: Boolean, sync: Boolean = false) = sharedPref.edit(sync) { putBoolean(key, value) }
|
||||||
sharedPref.edit(sync) { putBoolean(key, value) }
|
|
||||||
|
|
||||||
fun putString(key: String, value: String?, sync: Boolean = false) {
|
fun putString(key: String, value: String?, sync: Boolean = false) {
|
||||||
sharedPref.edit(sync) { putString(key, value) }
|
sharedPref.edit(sync) { putString(key, value) }
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
package io.github.wulkanowy.data.db.adapters
|
||||||
|
|
||||||
|
import com.squareup.moshi.JsonAdapter
|
||||||
|
import com.squareup.moshi.JsonReader
|
||||||
|
import com.squareup.moshi.JsonWriter
|
||||||
|
import com.squareup.moshi.Moshi
|
||||||
|
import com.squareup.moshi.Types
|
||||||
|
import java.lang.reflect.ParameterizedType
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
|
||||||
|
object PairAdapterFactory : JsonAdapter.Factory {
|
||||||
|
|
||||||
|
override fun create(type: Type, annotations: MutableSet<out Annotation>, moshi: Moshi): JsonAdapter<*>? {
|
||||||
|
if (type !is ParameterizedType || List::class.java != type.rawType) return null
|
||||||
|
if (type.actualTypeArguments[0] != Pair::class.java) return null
|
||||||
|
|
||||||
|
val listType = Types.newParameterizedType(List::class.java, Map::class.java, String::class.java)
|
||||||
|
val listAdapter = moshi.adapter<List<Map<String, String>>>(listType)
|
||||||
|
|
||||||
|
val mapType = Types.newParameterizedType(MutableMap::class.java, String::class.java, String::class.java)
|
||||||
|
val mapAdapter = moshi.adapter<Map<String, String>>(mapType)
|
||||||
|
|
||||||
|
return PairAdapter(listAdapter, mapAdapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PairAdapter(
|
||||||
|
private val listAdapter: JsonAdapter<List<Map<String, String>>>,
|
||||||
|
private val mapAdapter: JsonAdapter<Map<String, String>>,
|
||||||
|
) : JsonAdapter<List<Pair<String, String>>>() {
|
||||||
|
|
||||||
|
override fun toJson(writer: JsonWriter, value: List<Pair<String, String>>?) {
|
||||||
|
writer.beginArray()
|
||||||
|
value?.forEach {
|
||||||
|
writer.beginObject()
|
||||||
|
writer.name("first").value(it.first)
|
||||||
|
writer.name("second").value(it.second)
|
||||||
|
writer.endObject()
|
||||||
|
}
|
||||||
|
writer.endArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fromJson(reader: JsonReader): List<Pair<String, String>>? {
|
||||||
|
return if (reader.peek() == JsonReader.Token.BEGIN_OBJECT) deserializeMoshiMap(reader)
|
||||||
|
else deserializeGsonPair(reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
// for compatibility with 0.21.0
|
||||||
|
private fun deserializeMoshiMap(reader: JsonReader): List<Pair<String, String>>? {
|
||||||
|
val map = mapAdapter.fromJson(reader) ?: return null
|
||||||
|
|
||||||
|
return map.entries.map {
|
||||||
|
it.key to it.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deserializeGsonPair(reader: JsonReader): List<Pair<String, String>>? {
|
||||||
|
val list = listAdapter.fromJson(reader) ?: return null
|
||||||
|
|
||||||
|
return list.map {
|
||||||
|
require(it.size == 2) {
|
||||||
|
"pair with more or less than two elements: $list"
|
||||||
|
}
|
||||||
|
|
||||||
|
it["first"].orEmpty() to it["second"].orEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +0,0 @@
|
||||||
package io.github.wulkanowy.data.db.dao
|
|
||||||
|
|
||||||
import androidx.room.Dao
|
|
||||||
import androidx.room.Query
|
|
||||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Dao
|
|
||||||
interface AdminMessageDao : BaseDao<AdminMessage> {
|
|
||||||
|
|
||||||
@Query("SELECT * FROM AdminMessages")
|
|
||||||
fun loadAll(): Flow<List<AdminMessage>>
|
|
||||||
}
|
|
|
@ -11,11 +11,6 @@ import javax.inject.Singleton
|
||||||
@Dao
|
@Dao
|
||||||
interface AttendanceDao : BaseDao<Attendance> {
|
interface AttendanceDao : BaseDao<Attendance> {
|
||||||
|
|
||||||
@Query("SELECT * FROM Attendance WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :start AND date <= :end")
|
@Query("SELECT * FROM Attendance WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
||||||
fun loadAll(
|
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Attendance>>
|
||||||
diaryId: Int,
|
|
||||||
studentId: Int,
|
|
||||||
start: LocalDate,
|
|
||||||
end: LocalDate
|
|
||||||
): Flow<List<Attendance>>
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,11 @@ 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.Transaction
|
|
||||||
import androidx.room.Update
|
import androidx.room.Update
|
||||||
|
|
||||||
interface BaseDao<T> {
|
interface BaseDao<T> {
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert
|
||||||
suspend fun insertAll(items: List<T>): List<Long>
|
suspend fun insertAll(items: List<T>): List<Long>
|
||||||
|
|
||||||
@Update
|
@Update
|
||||||
|
@ -16,10 +14,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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import androidx.room.Dao
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import io.github.wulkanowy.data.db.entities.Conference
|
import io.github.wulkanowy.data.db.entities.Conference
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import java.time.Instant
|
import java.time.LocalDateTime
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
|
@ -12,5 +12,5 @@ import javax.inject.Singleton
|
||||||
interface ConferenceDao : BaseDao<Conference> {
|
interface ConferenceDao : BaseDao<Conference> {
|
||||||
|
|
||||||
@Query("SELECT * FROM Conferences WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :startDate")
|
@Query("SELECT * FROM Conferences WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :startDate")
|
||||||
fun loadAll(diaryId: Int, studentId: Int, startDate: Instant): Flow<List<Conference>>
|
fun loadAll(diaryId: Int, studentId: Int, startDate: LocalDateTime): Flow<List<Conference>>
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
package io.github.wulkanowy.data.db.dao
|
|
||||||
|
|
||||||
import androidx.room.Dao
|
|
||||||
import androidx.room.Query
|
|
||||||
import io.github.wulkanowy.data.db.entities.GradeDescriptive
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Dao
|
|
||||||
interface GradeDescriptiveDao : BaseDao<GradeDescriptive> {
|
|
||||||
|
|
||||||
@Query("SELECT * FROM GradesDescriptive WHERE semester_id = :semesterId AND student_id = :studentId")
|
|
||||||
fun loadAll(semesterId: Int, studentId: Int): Flow<List<GradeDescriptive>>
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package io.github.wulkanowy.data.db.dao
|
|
||||||
|
|
||||||
import androidx.room.Dao
|
|
||||||
import androidx.room.Query
|
|
||||||
import io.github.wulkanowy.data.db.entities.Mailbox
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Dao
|
|
||||||
interface MailboxDao : BaseDao<Mailbox> {
|
|
||||||
|
|
||||||
@Query("SELECT * FROM Mailboxes WHERE email = :email")
|
|
||||||
suspend fun loadAll(email: String): List<Mailbox>
|
|
||||||
|
|
||||||
@Query("SELECT * FROM Mailboxes WHERE email = :email AND symbol = :symbol AND schoolId = :schoolId")
|
|
||||||
fun loadAll(email: String, symbol: String, schoolId: String): Flow<List<Mailbox>>
|
|
||||||
}
|
|
|
@ -5,26 +5,15 @@ import androidx.room.Query
|
||||||
import androidx.room.Transaction
|
import androidx.room.Transaction
|
||||||
import io.github.wulkanowy.data.db.entities.Message
|
import io.github.wulkanowy.data.db.entities.Message
|
||||||
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
|
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
|
||||||
import io.github.wulkanowy.data.db.entities.MessageWithMutedAuthor
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface MessagesDao : BaseDao<Message> {
|
interface MessagesDao : BaseDao<Message> {
|
||||||
@Transaction
|
|
||||||
@Query("SELECT * FROM Messages WHERE message_global_key = :messageGlobalKey")
|
|
||||||
fun loadMessageWithAttachment(messageGlobalKey: String): Flow<MessageWithAttachment?>
|
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT * FROM Messages WHERE mailbox_key = :mailboxKey AND folder_id = :folder ORDER BY date DESC")
|
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND message_id = :messageId")
|
||||||
fun loadMessagesWithMutedAuthor(mailboxKey: String, folder: Int): Flow<List<MessageWithMutedAuthor>>
|
fun loadMessageWithAttachment(studentId: Int, messageId: Int): Flow<MessageWithAttachment?>
|
||||||
|
|
||||||
@Transaction
|
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder ORDER BY date DESC")
|
||||||
@Query("SELECT * FROM Messages WHERE email = :email AND folder_id = :folder ORDER BY date DESC")
|
fun loadAll(studentId: Int, folder: Int): Flow<List<Message>>
|
||||||
fun loadMessagesWithMutedAuthor(folder: Int, email: String): Flow<List<MessageWithMutedAuthor>>
|
|
||||||
|
|
||||||
@Query("SELECT * FROM Messages WHERE mailbox_key = :mailboxKey AND folder_id = :folder ORDER BY date DESC")
|
|
||||||
fun loadAll(mailboxKey: String, folder: Int): Flow<List<Message>>
|
|
||||||
|
|
||||||
@Query("SELECT * FROM Messages WHERE email = :email AND folder_id = :folder ORDER BY date DESC")
|
|
||||||
fun loadAll(folder: Int, email: String): Flow<List<Message>>
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 student_id = :userLoginId ORDER BY date DESC")
|
||||||
fun loadAll(studentId: Int): Flow<List<MobileDevice>>
|
fun loadAll(userLoginId: Int): Flow<List<MobileDevice>>
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
package io.github.wulkanowy.data.db.dao
|
|
||||||
|
|
||||||
import androidx.room.Dao
|
|
||||||
import androidx.room.Insert
|
|
||||||
import androidx.room.OnConflictStrategy
|
|
||||||
import androidx.room.Query
|
|
||||||
import io.github.wulkanowy.data.db.entities.MutedMessageSender
|
|
||||||
|
|
||||||
@Dao
|
|
||||||
interface MutedMessageSendersDao : BaseDao<MutedMessageSender> {
|
|
||||||
|
|
||||||
@Query("SELECT COUNT(*) FROM MutedMessageSenders WHERE author = :author")
|
|
||||||
suspend fun checkMute(author: String): Boolean
|
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
|
||||||
suspend fun insertMute(mute: MutedMessageSender): Long
|
|
||||||
|
|
||||||
@Query("DELETE FROM MutedMessageSenders WHERE author = :author")
|
|
||||||
suspend fun deleteMute(author: String)
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package io.github.wulkanowy.data.db.dao
|
|
||||||
|
|
||||||
import androidx.room.Dao
|
|
||||||
import androidx.room.Query
|
|
||||||
import io.github.wulkanowy.data.db.entities.Notification
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Dao
|
|
||||||
interface NotificationDao : BaseDao<Notification> {
|
|
||||||
|
|
||||||
@Query("SELECT * FROM Notifications WHERE student_id = :studentId OR student_id = -1")
|
|
||||||
fun loadAll(studentId: Long): Flow<List<Notification>>
|
|
||||||
}
|
|
|
@ -2,7 +2,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.MailboxType
|
|
||||||
import io.github.wulkanowy.data.db.entities.Recipient
|
import io.github.wulkanowy.data.db.entities.Recipient
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@ -10,6 +9,6 @@ import javax.inject.Singleton
|
||||||
@Dao
|
@Dao
|
||||||
interface RecipientDao : BaseDao<Recipient> {
|
interface RecipientDao : BaseDao<Recipient> {
|
||||||
|
|
||||||
@Query("SELECT * FROM Recipients WHERE type = :type AND studentMailboxGlobalKey = :studentMailboxGlobalKey")
|
@Query("SELECT * FROM Recipients WHERE student_id = :studentId AND unit_id = :unitId AND role = :role")
|
||||||
suspend fun loadAll(type: MailboxType, studentMailboxGlobalKey: String): List<Recipient>
|
suspend fun loadAll(studentId: Int, unitId: Int, role: Int): List<Recipient>
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package io.github.wulkanowy.data.db.dao
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Query
|
||||||
|
import io.github.wulkanowy.data.db.entities.ReportingUnit
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Dao
|
||||||
|
interface ReportingUnitDao : BaseDao<ReportingUnit> {
|
||||||
|
|
||||||
|
@Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId")
|
||||||
|
suspend fun load(studentId: Int): List<ReportingUnit>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId AND real_id = :unitId")
|
||||||
|
suspend fun loadOne(studentId: Int, unitId: Int): ReportingUnit?
|
||||||
|
}
|
|
@ -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 student_id = :studentId")
|
||||||
fun loadAll(studentId: Int): Flow<List<SchoolAnnouncement>>
|
fun loadAll(studentId: 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>
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,40 +3,28 @@ package io.github.wulkanowy.data.db.dao
|
||||||
import androidx.room.Dao
|
import androidx.room.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.ABORT
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import androidx.room.Transaction
|
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.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.StudentNickAndAvatar
|
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Dao
|
@Dao
|
||||||
abstract class StudentDao {
|
abstract class StudentDao {
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.ABORT)
|
@Insert(onConflict = ABORT)
|
||||||
abstract suspend fun insertAll(student: List<Student>): List<Long>
|
abstract suspend fun insertAll(student: List<Student>): List<Long>
|
||||||
|
|
||||||
@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)
|
||||||
|
|
||||||
@Update(entity = Student::class)
|
|
||||||
abstract suspend fun update(studentName: StudentName)
|
|
||||||
|
|
||||||
@Query("SELECT * FROM Students WHERE is_current = 1")
|
@Query("SELECT * FROM Students WHERE is_current = 1")
|
||||||
abstract suspend fun loadCurrent(): Student?
|
abstract suspend fun loadCurrent(): Student?
|
||||||
|
|
||||||
|
@ -47,12 +35,8 @@ 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")
|
||||||
abstract suspend fun loadStudentsWithSemesters(): Map<Student, List<Semester>>
|
abstract suspend fun loadStudentsWithSemesters(): List<StudentWithSemesters>
|
||||||
|
|
||||||
@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")
|
|
||||||
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")
|
||||||
abstract suspend fun updateCurrent(id: Long)
|
abstract suspend fun updateCurrent(id: Long)
|
||||||
|
@ -60,9 +44,6 @@ abstract class StudentDao {
|
||||||
@Query("UPDATE Students SET is_current = 0")
|
@Query("UPDATE Students SET is_current = 0")
|
||||||
abstract suspend fun resetCurrent()
|
abstract suspend fun resetCurrent()
|
||||||
|
|
||||||
@Query("DELETE FROM Students WHERE email = :email AND user_name = :userName")
|
|
||||||
abstract suspend fun deleteByEmailAndUserName(email: String, userName: String)
|
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
open suspend fun switchCurrent(id: Long) {
|
open suspend fun switchCurrent(id: Long) {
|
||||||
resetCurrent()
|
resetCurrent()
|
||||||
|
|
|
@ -5,7 +5,6 @@ import androidx.room.Query
|
||||||
import io.github.wulkanowy.data.db.entities.TimetableAdditional
|
import io.github.wulkanowy.data.db.entities.TimetableAdditional
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.util.UUID
|
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
|
@ -13,13 +12,5 @@ import javax.inject.Singleton
|
||||||
interface TimetableAdditionalDao : BaseDao<TimetableAdditional> {
|
interface TimetableAdditionalDao : BaseDao<TimetableAdditional> {
|
||||||
|
|
||||||
@Query("SELECT * FROM TimetableAdditional WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
@Query("SELECT * FROM TimetableAdditional WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
||||||
fun loadAll(
|
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<TimetableAdditional>>
|
||||||
diaryId: Int,
|
|
||||||
studentId: Int,
|
|
||||||
from: LocalDate,
|
|
||||||
end: LocalDate
|
|
||||||
): Flow<List<TimetableAdditional>>
|
|
||||||
|
|
||||||
@Query("DELETE FROM TimetableAdditional WHERE repeat_id = :repeatId")
|
|
||||||
suspend fun deleteAllByRepeatId(repeatId: UUID)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,4 @@ interface TimetableDao : BaseDao<Timetable> {
|
||||||
|
|
||||||
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
||||||
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Timetable>>
|
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Timetable>>
|
||||||
|
|
||||||
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
|
||||||
suspend fun load(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): List<Timetable>
|
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue