Compare commits

..

No commits in common. "develop" and "feature/elearning-events" have entirely different histories.

749 changed files with 13714 additions and 31244 deletions

View File

@ -23,11 +23,11 @@ if __name__ == "__main__":
(title, changelog) = get_changelog(project_dir, format="plain") (title, changelog) = get_changelog(project_dir, format="plain")
# plain text changelog - Firebase App Distribution # plain text changelog - Firebase App Distribution
with open(dir + "whatsnew_titled.txt", "w", encoding="utf-8") as f: with open(dir + "whatsnew-titled.txt", "w", encoding="utf-8") as f:
f.write(title) f.write(title)
f.write("\n") f.write("\n")
f.write(changelog) f.write(changelog)
print("::set-output name=changelogPlainTitledFile::" + dir + "whatsnew_titled.txt") print("::set-output name=changelogPlainTitledFile::" + dir + "whatsnew-titled.txt")
print("::set-output name=changelogTitle::" + title) print("::set-output name=changelogTitle::" + title)

View File

@ -51,11 +51,10 @@ jobs:
androidHome: ${{ env.ANDROID_HOME }} androidHome: ${{ env.ANDROID_HOME }}
androidSdkRoot: ${{ env.ANDROID_SDK_ROOT }} androidSdkRoot: ${{ env.ANDROID_SDK_ROOT }}
steps: steps:
- name: Setup JDK 11 - name: Setup JDK 1.8
uses: actions/setup-java@v2 uses: actions/setup-java@v1
with: with:
distribution: 'temurin' java-version: 1.8
java-version: '11'
- name: Setup Android SDK - name: Setup Android SDK
uses: android-actions/setup-android@v2 uses: android-actions/setup-android@v2
- name: Clean build artifacts - name: Clean build artifacts
@ -64,9 +63,7 @@ jobs:
rm -rf app/build/outputs/apk/* rm -rf app/build/outputs/apk/*
rm -rf app/build/outputs/bundle/* rm -rf app/build/outputs/bundle/*
- name: Assemble official release with Gradle - name: Assemble official release with Gradle
uses: gradle/gradle-build-action@v2 run: ./gradlew assembleOfficialRelease
with:
arguments: assembleOfficialRelease
sign: sign:
name: Sign APK name: Sign APK
runs-on: self-hosted runs-on: self-hosted

View File

@ -43,11 +43,10 @@ jobs:
androidHome: ${{ env.ANDROID_HOME }} androidHome: ${{ env.ANDROID_HOME }}
androidSdkRoot: ${{ env.ANDROID_SDK_ROOT }} androidSdkRoot: ${{ env.ANDROID_SDK_ROOT }}
steps: steps:
- name: Setup JDK 11 - name: Setup JDK 1.8
uses: actions/setup-java@v2 uses: actions/setup-java@v1
with: with:
distribution: 'temurin' java-version: 1.8
java-version: '11'
- name: Setup Android SDK - name: Setup Android SDK
uses: android-actions/setup-android@v2 uses: android-actions/setup-android@v2
- name: Clean build artifacts - name: Clean build artifacts
@ -56,9 +55,7 @@ jobs:
rm -rf app/build/outputs/apk/* rm -rf app/build/outputs/apk/*
rm -rf app/build/outputs/bundle/* rm -rf app/build/outputs/bundle/*
- name: Bundle play release with Gradle - name: Bundle play release with Gradle
uses: gradle/gradle-build-action@v2 run: ./gradlew bundlePlayRelease
with:
arguments: bundlePlayRelease
sign: sign:
name: Sign App Bundle name: Sign App Bundle
runs-on: self-hosted runs-on: self-hosted
@ -113,11 +110,10 @@ jobs:
with: with:
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }} serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
packageName: pl.szczodrzynski.edziennik packageName: pl.szczodrzynski.edziennik
releaseFiles: ${{ needs.sign.outputs.signedReleaseFile }} releaseFile: ${{ needs.sign.outputs.signedReleaseFile }}
releaseName: ${{ steps.changelog.outputs.appVersionName }} releaseName: ${{ steps.changelog.outputs.appVersionName }}
track: ${{ secrets.PLAY_RELEASE_TRACK }} track: ${{ secrets.PLAY_RELEASE_TRACK }}
whatsNewDirectory: ${{ steps.changelog.outputs.changelogDir }} whatsNewDirectory: ${{ steps.changelog.outputs.changelogDir }}
status: completed
- name: Upload workflow artifact - name: Upload workflow artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2

View File

@ -43,11 +43,10 @@ jobs:
androidHome: ${{ env.ANDROID_HOME }} androidHome: ${{ env.ANDROID_HOME }}
androidSdkRoot: ${{ env.ANDROID_SDK_ROOT }} androidSdkRoot: ${{ env.ANDROID_SDK_ROOT }}
steps: steps:
- name: Setup JDK 11 - name: Setup JDK 1.8
uses: actions/setup-java@v2 uses: actions/setup-java@v1
with: with:
distribution: 'temurin' java-version: 1.8
java-version: '11'
- name: Setup Android SDK - name: Setup Android SDK
uses: android-actions/setup-android@v2 uses: android-actions/setup-android@v2
- name: Clean build artifacts - name: Clean build artifacts
@ -56,9 +55,7 @@ jobs:
rm -rf app/build/outputs/apk/* rm -rf app/build/outputs/apk/*
rm -rf app/build/outputs/bundle/* rm -rf app/build/outputs/bundle/*
- name: Assemble official release with Gradle - name: Assemble official release with Gradle
uses: gradle/gradle-build-action@v2 run: ./gradlew assembleOfficialRelease
with:
arguments: assembleOfficialRelease
sign: sign:
name: Sign APK name: Sign APK
runs-on: self-hosted runs-on: self-hosted

1
.gitignore vendored
View File

@ -265,4 +265,3 @@ fabric.properties
# End of https://www.toptal.com/developers/gitignore/api/android,androidstudio,gradle,java,kotlin # End of https://www.toptal.com/developers/gitignore/api/android,androidstudio,gradle,java,kotlin
signatures/ signatures/
.idea/*.xml

View File

@ -1,17 +1,8 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<JetCodeStyleSettings> <JetCodeStyleSettings>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="ALLOW_TRAILING_COMMA" value="true" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings> </JetCodeStyleSettings>
<codeStyleSettings language="JSON">
<indentOptions>
<option name="INDENT_SIZE" value="4" />
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="XML"> <codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" /> <option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions> <indentOptions>

View File

@ -1,6 +0,0 @@
<component name="CopyrightManager">
<copyright>
<option name="notice" value="Copyright (c) Antoni Czaplicki &amp;#36;{today.year}-&amp;#36;{today.month}-&amp;#36;{today.day}. " />
<option name="myName" value="Antoni" />
</copyright>
</component>

View File

@ -5,7 +5,6 @@
<w>ciasteczko</w> <w>ciasteczko</w>
<w>csrf</w> <w>csrf</w>
<w>edziennik</w> <w>edziennik</w>
<w>eggfall</w>
<w>elearning</w> <w>elearning</w>
<w>gson</w> <w>gson</w>
<w>hebe</w> <w>hebe</w>
@ -14,7 +13,6 @@
<w>synergia</w> <w>synergia</w>
<w>szczodrzyński</w> <w>szczodrzyński</w>
<w>szkolny</w> <w>szkolny</w>
<w>usos</w>
</words> </words>
</dictionary> </dictionary>
</component> </component>

9
.idea/discord.xml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DiscordProjectSettings">
<option name="show" value="PROJECT_FILES" />
</component>
<component name="ProjectNotificationSettings">
<option name="askShowProject" value="false" />
</component>
</project>

6
.idea/kotlinc.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Kotlin2JvmCompilerArguments">
<option name="jvmTarget" value="1.8" />
</component>
</project>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>

View File

@ -1,7 +1,6 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-parcelize'
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'
@ -31,21 +30,11 @@ android {
cppFlags "-std=c++11" cppFlags "-std=c++11"
} }
} }
kapt {
arguments {
arg("room.schemaLocation", "$projectDir/schemas")
}
}
} }
buildTypes { buildTypes {
debug { debug {
getIsDefault().set(true)
minifyEnabled = false minifyEnabled = false
manifestPlaceholders = [
buildTimestamp: 0
]
} }
release { release {
minifyEnabled = true minifyEnabled = true
@ -56,34 +45,23 @@ android {
} }
flavorDimensions "platform" flavorDimensions "platform"
productFlavors { productFlavors {
unofficial { main {
getIsDefault().set(true) versionName gitInfo.versionHuman
versionName "${release.versionName}-${gitInfo.versionSuffix}"
} }
official {} official {}
play {} play {}
} }
variantFilter { variant -> variantFilter { variant ->
def flavors = variant.flavors*.name def flavors = variant.flavors*.name
setIgnore(variant.buildType.name == "debug" && !flavors.contains("unofficial") || flavors.contains("main")) setIgnore(variant.buildType.name == "debug" && !flavors.contains("main"))
}
sourceSets {
unofficial {
java.srcDirs = ["src/main/java", "src/play-not/java"]
manifest.srcFile("src/play-not/AndroidManifest.xml")
}
official {
java.srcDirs = ["src/main/java", "src/play-not/java"]
manifest.srcFile("src/play-not/AndroidManifest.xml")
}
play {
java.srcDirs = ["src/main/java", "src/play/java"]
}
} }
defaultConfig { defaultConfig {
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
} }
lintOptions {
checkReleaseBuilds = false
}
buildFeatures { buildFeatures {
dataBinding = true dataBinding = true
viewBinding = true viewBinding = true
@ -97,9 +75,7 @@ android {
jvmTarget = "1.8" jvmTarget = "1.8"
} }
packagingOptions { packagingOptions {
resources { exclude 'META-INF/library-core_release.kotlin_module'
excludes += ['META-INF/library-core_release.kotlin_module']
}
} }
externalNativeBuild { externalNativeBuild {
cmake { cmake {
@ -107,9 +83,6 @@ android {
version "3.10.2" version "3.10.2"
} }
} }
lint {
checkReleaseBuilds false
}
} }
tasks.whenTaskAdded { task -> tasks.whenTaskAdded { task ->
@ -144,30 +117,28 @@ dependencies {
// Language cores // Language cores
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "androidx.multidex:multidex:2.0.1"
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.5" coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.5"
// Android Jetpack // Android Jetpack
implementation "androidx.appcompat:appcompat:1.5.1" implementation "androidx.appcompat:appcompat:1.2.0"
implementation "androidx.cardview:cardview:1.0.0" implementation "androidx.cardview:cardview:1.0.0"
implementation "androidx.constraintlayout:constraintlayout:2.1.4" implementation "androidx.constraintlayout:constraintlayout:2.0.4"
implementation "androidx.core:core-ktx:1.9.0" implementation "androidx.core:core-ktx:1.3.2"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.1" implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.0"
implementation "androidx.navigation:navigation-fragment-ktx:2.5.2" implementation "androidx.navigation:navigation-fragment-ktx:2.3.4"
implementation "androidx.recyclerview:recyclerview:1.2.1" implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation "androidx.room:room-runtime:2.4.3" implementation "androidx.room:room-runtime:2.2.6"
implementation "androidx.room:room-ktx:2.4.3" implementation "androidx.work:work-runtime-ktx:2.5.0"
implementation "androidx.work:work-runtime-ktx:2.7.1" kapt "androidx.room:room-compiler:2.2.6"
kapt "androidx.room:room-compiler:2.4.3"
// Google design libs // Google design libs
implementation "com.google.android.material:material:1.6.1" implementation "com.google.android.material:material:1.3.0"
implementation "com.google.android.flexbox:flexbox:3.0.0" implementation "com.google.android:flexbox:2.0.1"
// Play Services/Firebase // Play Services/Firebase
implementation "com.google.android.gms:play-services-wearable:17.1.0" implementation "com.google.android.gms:play-services-wearable:17.0.0"
implementation("com.google.firebase:firebase-core") { version { strictly "19.0.2" } } implementation "com.google.firebase:firebase-core:18.0.2"
implementation "com.google.firebase:firebase-crashlytics:18.2.13" implementation "com.google.firebase:firebase-crashlytics:17.4.0"
implementation("com.google.firebase:firebase-messaging") { version { strictly "20.1.3" } } implementation("com.google.firebase:firebase-messaging") { version { strictly "20.1.3" } }
// OkHttp, Retrofit, Gson, Jsoup // OkHttp, Retrofit, Gson, Jsoup
@ -175,22 +146,21 @@ dependencies {
implementation "com.squareup.retrofit2:retrofit:2.9.0" implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-gson:2.9.0" implementation "com.squareup.retrofit2:converter-gson:2.9.0"
implementation "com.squareup.retrofit2:converter-scalars:2.9.0" implementation "com.squareup.retrofit2:converter-scalars:2.9.0"
implementation 'com.google.code.gson:gson:2.8.8' implementation 'com.google.code.gson:gson:2.8.6'
implementation 'org.jsoup:jsoup:1.14.3' implementation "org.jsoup:jsoup:1.13.1"
implementation "pl.droidsonroids:jspoon:1.3.2" implementation "pl.droidsonroids:jspoon:1.3.2"
implementation "pl.droidsonroids.retrofit2:converter-jspoon:1.3.2" implementation "pl.droidsonroids.retrofit2:converter-jspoon:1.3.2"
// Szkolny.eu libraries/forks // Szkolny.eu libraries/forks
implementation "eu.szkolny:android-snowfall:1ca9ea2da3" implementation "eu.szkolny:android-snowfall:1ca9ea2da3"
implementation "eu.szkolny:agendacalendarview:1.0.4" implementation "eu.szkolny:agendacalendarview:5431f03098"
implementation "eu.szkolny:cafebar:5bf0c618de" implementation "eu.szkolny:cafebar:5bf0c618de"
implementation "eu.szkolny.fslogin:lib:2.0.0" implementation "eu.szkolny.fslogin:lib:2.0.0"
implementation "eu.szkolny:material-about-library:1d5ebaf47c" implementation "eu.szkolny:material-about-library:1d5ebaf47c"
implementation "eu.szkolny:mhttp:af4b62e6e9" implementation "eu.szkolny:mhttp:af4b62e6e9"
implementation "eu.szkolny:nachos:0e5dfcaceb" implementation "eu.szkolny:nachos:0e5dfcaceb"
implementation "eu.szkolny.selective-dao:annotation:27f8f3f194" implementation "eu.szkolny.selective-dao:annotation:27f8f3f194"
officialImplementation "eu.szkolny:ssl-provider:1.0.0" implementation "eu.szkolny:ssl-provider:1.0.0"
unofficialImplementation "eu.szkolny:ssl-provider:1.0.0"
implementation "pl.szczodrzynski:navlib:0.8.0" implementation "pl.szczodrzynski:navlib:0.8.0"
implementation "pl.szczodrzynski:numberslidingpicker:2921225f76" implementation "pl.szczodrzynski:numberslidingpicker:2921225f76"
implementation "pl.szczodrzynski:recyclertablayout:700f980584" implementation "pl.szczodrzynski:recyclertablayout:700f980584"
@ -198,34 +168,33 @@ dependencies {
kapt "eu.szkolny.selective-dao:codegen:27f8f3f194" kapt "eu.szkolny.selective-dao:codegen:27f8f3f194"
// Iconics & related // Iconics & related
implementation "com.mikepenz:iconics-core:5.3.2" implementation "com.mikepenz:iconics-core:5.3.0-b01"
implementation "com.mikepenz:iconics-views:5.3.2" implementation "com.mikepenz:iconics-views:5.3.0-b01"
implementation "com.mikepenz:community-material-typeface:5.8.55.0-kotlin@aar" implementation "com.mikepenz:community-material-typeface:5.8.55.0-kotlin@aar"
implementation "eu.szkolny:szkolny-font:77e33acc2a" implementation "eu.szkolny:szkolny-font:1.3"
// Other dependencies // Other dependencies
implementation "cat.ereza:customactivityoncrash:2.3.0" implementation "cat.ereza:customactivityoncrash:2.3.0"
implementation "com.android.volley:volley:1.2.1" implementation "com.applandeo:material-calendar-view:1.5.0"
implementation "com.daimajia.swipelayout:library:1.2.0@aar" implementation "com.daimajia.swipelayout:library:1.2.0@aar"
implementation "com.github.Applandeo:Material-Calendar-View:15de569cbc" // https://github.com/Applandeo/Material-Calendar-View implementation "com.github.antonKozyriatskyi:CircularProgressIndicator:1.2.2"
implementation "com.github.CanHub:Android-Image-Cropper:2.2.2" // https://github.com/CanHub/Android-Image-Cropper implementation "com.github.bassaer:chatmessageview:2.0.1"
implementation "com.github.ChuckerTeam.Chucker:library:3.5.2" // https://github.com/ChuckerTeam/chucker implementation "com.github.CanHub:Android-Image-Cropper:2.2.2"
implementation "com.github.antonKozyriatskyi:CircularProgressIndicator:1.2.2" // https://github.com/antonKozyriatskyi/CircularProgressIndicator implementation "com.github.ChuckerTeam.Chucker:library:3.0.1"
implementation "com.github.bassaer:chatmessageview:2.0.1" // https://github.com/bassaer/ChatMessageView implementation "com.github.wulkanowy.uonet-request-signer:hebe-jvm:a99ca50a31"
implementation "com.github.hypertrack:hyperlog-android:0.0.10" // https://github.com/hypertrack/hyperlog-android implementation("com.heinrichreimersoftware:material-intro") { version { strictly "1.5.8" } }
implementation "com.github.smuyyh:JsonViewer:V1.0.6" // https://github.com/smuyyh/JsonViewer implementation "com.hypertrack:hyperlog:0.0.10"
implementation "com.github.underwindfall.PowerPermission:powerpermission-coroutines:1.4.0" // https://github.com/underwindfall/PowerPermission
implementation "com.github.underwindfall.PowerPermission:powerpermission:1.4.0" // https://github.com/underwindfall/PowerPermission
implementation "com.github.wulkanowy.uonet-request-signer:hebe-jvm:a99ca50a31" // https://github.com/wulkanowy/uonet-request-signer
implementation "com.jaredrummler:colorpicker:1.1.0" implementation "com.jaredrummler:colorpicker:1.1.0"
implementation "com.qifan.powerpermission:powerpermission-coroutines:1.3.0"
implementation "com.qifan.powerpermission:powerpermission:1.3.0"
implementation "com.yuyh.json:jsonviewer:1.0.6"
implementation "io.coil-kt:coil:1.1.1" implementation "io.coil-kt:coil:1.1.1"
implementation "me.dm7.barcodescanner:zxing:1.9.8" implementation "me.dm7.barcodescanner:zxing:1.9.8"
implementation "me.grantland:autofittextview:0.2.1" implementation "me.grantland:autofittextview:0.2.1"
implementation "me.leolin:ShortcutBadger:1.1.22@aar" implementation "me.leolin:ShortcutBadger:1.1.22@aar"
implementation "org.greenrobot:eventbus:3.2.0" implementation "org.greenrobot:eventbus:3.2.0"
implementation("com.heinrichreimersoftware:material-intro") { version { strictly "1.5.8" } }
implementation("pl.droidsonroids.gif:android-gif-drawable") { version { strictly "1.2.15" } } implementation("pl.droidsonroids.gif:android-gif-drawable") { version { strictly "1.2.15" } }
// Debug-only dependencies // Debug-only dependencies
debugImplementation "com.github.amitshekhariitbhu.Android-Debug-Database:debug-db:v1.0.6" debugImplementation "com.amitshekhar.android:debug-db:1.0.5"
} }

View File

@ -97,6 +97,7 @@ private def buildGitInfo() {
def tag = getLastTag(repo, git, head) def tag = getLastTag(repo, git, head)
def tagName = tag[1] def tagName = tag[1]
def tagRevCount = tag[2] def tagRevCount = tag[2]
def versionName = tagName.replace("v", "")
def result = [ def result = [
hash : head.objectId.name, hash : head.objectId.name,
@ -107,7 +108,7 @@ private def buildGitInfo() {
tag : tagName, tag : tagName,
revCount : tagRevCount, revCount : tagRevCount,
version : """$tagName-$tagRevCount-g${head.objectId.name.substring(0, 8)}""" + (dirty ? ".dirty" : ""), version : """$tagName-$tagRevCount-g${head.objectId.name.substring(0, 8)}""" + (dirty ? ".dirty" : ""),
versionSuffix : """${repo.branch.replace("/", "_")}""" + (dirty ? ".dirty" : "") versionHuman: """$versionName-${repo.branch.replace("/", "_")}""" + (dirty ? ".dirty" : "")
] ]
return result return result
} }

View File

@ -22,19 +22,14 @@
-keep class android.support.v7.widget.** { *; } -keep class android.support.v7.widget.** { *; }
-keep class pl.szczodrzynski.edziennik.utils.models.** { *; } -keep class pl.szczodrzynski.edziennik.utils.models.** { *; }
-keep class pl.szczodrzynski.edziennik.data.db.enums.* { *; }
-keep class pl.szczodrzynski.edziennik.data.db.entity.Event { *; } -keep class pl.szczodrzynski.edziennik.data.db.entity.Event { *; }
-keep class pl.szczodrzynski.edziennik.data.db.full.EventFull { *; } -keep class pl.szczodrzynski.edziennik.data.db.full.EventFull { *; }
-keep class pl.szczodrzynski.edziennik.data.db.entity.FeedbackMessage { *; } -keep class pl.szczodrzynski.edziennik.data.db.entity.FeedbackMessage { *; }
-keep class pl.szczodrzynski.edziennik.data.db.entity.Note { *; } -keep class pl.szczodrzynski.edziennik.ui.modules.home.HomeCardModel { *; }
-keep class pl.szczodrzynski.edziennik.ui.home.HomeCardModel { *; }
-keepclassmembers class pl.szczodrzynski.edziennik.ui.widgets.WidgetConfig { public *; } -keepclassmembers class pl.szczodrzynski.edziennik.ui.widgets.WidgetConfig { public *; }
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.timetable.WidgetTimetableProvider -keepnames class pl.szczodrzynski.edziennik.ui.widgets.timetable.WidgetTimetableProvider
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.notifications.WidgetNotificationsProvider -keepnames class pl.szczodrzynski.edziennik.ui.widgets.notifications.WidgetNotificationsProvider
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.luckynumber.WidgetLuckyNumberProvider -keepnames class pl.szczodrzynski.edziennik.ui.widgets.luckynumber.WidgetLuckyNumberProvider
-keep class pl.szczodrzynski.edziennik.config.AppData { *; }
-keep class pl.szczodrzynski.edziennik.config.AppData$** { *; }
-keep class pl.szczodrzynski.edziennik.utils.managers.TextStylingManager$HtmlMode { *; }
-keepnames class androidx.appcompat.view.menu.MenuBuilder { setHeaderTitleInt(java.lang.CharSequence); } -keepnames class androidx.appcompat.view.menu.MenuBuilder { setHeaderTitleInt(java.lang.CharSequence); }
-keepnames class androidx.appcompat.view.menu.MenuPopupHelper { showPopup(int, int, boolean, boolean); } -keepnames class androidx.appcompat.view.menu.MenuPopupHelper { showPopup(int, int, boolean, boolean); }
@ -72,7 +67,7 @@
-keepclassmembers class pl.szczodrzynski.edziennik.data.api.szkolny.request.** { *; } -keepclassmembers class pl.szczodrzynski.edziennik.data.api.szkolny.request.** { *; }
-keepclassmembers class pl.szczodrzynski.edziennik.data.api.szkolny.response.** { *; } -keepclassmembers class pl.szczodrzynski.edziennik.data.api.szkolny.response.** { *; }
-keepclassmembernames class pl.szczodrzynski.edziennik.ui.login.LoginInfo$Platform { *; } -keepclassmembernames class pl.szczodrzynski.edziennik.ui.modules.login.LoginInfo$Platform { *; }
-keepclassmembernames class pl.szczodrzynski.fslogin.realm.RealmData { *; } -keepclassmembernames class pl.szczodrzynski.fslogin.realm.RealmData { *; }
-keepclassmembernames class pl.szczodrzynski.fslogin.realm.RealmData$Type { *; } -keepclassmembernames class pl.szczodrzynski.fslogin.realm.RealmData$Type { *; }

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

View File

@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
package="pl.szczodrzynski.edziennik"> package="pl.szczodrzynski.edziennik">
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
@ -12,7 +13,7 @@
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<!-- PowerPermission uses minSdk 21, it's safe to override as it is used only in >= 23 --> <!-- PowerPermission uses minSdk 21, it's safe to override as it is used only in >= 23 -->
<uses-sdk tools:overrideLibrary="com.qifan.powerpermission.coroutines, com.qifan.powerpermission.core" /> <uses-sdk tools:overrideLibrary="com.qifan.powerpermission.coroutines, com.qifan.powerpermission.core" />
@ -42,7 +43,6 @@
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:label="@string/app_name" android:label="@string/app_name"
android:launchMode="singleTop" android:launchMode="singleTop"
android:exported="true"
android:theme="@style/SplashTheme"> android:theme="@style/SplashTheme">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@ -66,7 +66,6 @@
android:configChanges="orientation|keyboardHidden" android:configChanges="orientation|keyboardHidden"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:noHistory="true" android:noHistory="true"
android:exported="true"
android:theme="@style/AppTheme.Dark.NoDisplay"> android:theme="@style/AppTheme.Dark.NoDisplay">
<intent-filter> <intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" /> <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
@ -74,8 +73,7 @@
</activity> </activity>
<!-- TIMETABLE --> <!-- TIMETABLE -->
<receiver android:name=".ui.widgets.timetable.WidgetTimetableProvider" <receiver android:name=".ui.widgets.timetable.WidgetTimetableProvider"
android:label="@string/widget_timetable_title" android:label="@string/widget_timetable_title">
android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter> </intent-filter>
@ -90,12 +88,10 @@
android:configChanges="orientation|keyboardHidden" android:configChanges="orientation|keyboardHidden"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:noHistory="true" android:noHistory="true"
android:exported="true"
android:theme="@style/AppTheme.Dark.NoDisplay" /> android:theme="@style/AppTheme.Dark.NoDisplay" />
<!-- NOTIFICATIONS --> <!-- NOTIFICATIONS -->
<receiver android:name=".ui.widgets.notifications.WidgetNotificationsProvider" <receiver android:name=".ui.widgets.notifications.WidgetNotificationsProvider"
android:label="@string/widget_notifications_title" android:label="@string/widget_notifications_title">
android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter> </intent-filter>
@ -108,8 +104,7 @@
android:permission="android.permission.BIND_REMOTEVIEWS" /> android:permission="android.permission.BIND_REMOTEVIEWS" />
<!-- LUCKY NUMBER --> <!-- LUCKY NUMBER -->
<receiver android:name=".ui.widgets.luckynumber.WidgetLuckyNumberProvider" <receiver android:name=".ui.widgets.luckynumber.WidgetLuckyNumberProvider"
android:label="@string/widget_lucky_number_title" android:label="@string/widget_lucky_number_title">
android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter> </intent-filter>
@ -126,47 +121,31 @@
/ ____ \ (__| |_| |\ V /| | |_| | __/\__ \ / ____ \ (__| |_| |\ V /| | |_| | __/\__ \
/_/ \_\___|\__|_| \_/ |_|\__|_|\___||___/ /_/ \_\___|\__|_| \_/ |_|\__|_|\___||___/
--> -->
<activity android:name=".ui.base.CrashActivity" <activity android:name=".ui.modules.base.CrashActivity"
android:configChanges="orientation|screenSize|keyboardHidden" android:configChanges="orientation|screenSize|keyboardHidden"
android:process=":error_activity" android:process=":error_activity"
android:exported="false"
android:theme="@style/DeadTheme" /> android:theme="@style/DeadTheme" />
<activity android:name=".ui.intro.ChangelogIntroActivity" <activity android:name=".ui.modules.intro.ChangelogIntroActivity"
android:configChanges="orientation|keyboardHidden" android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name" android:label="@string/app_name"
android:exported="false"
android:theme="@style/Theme.Intro" /> android:theme="@style/Theme.Intro" />
<activity android:name=".ui.login.LoginActivity" <activity android:name=".ui.modules.login.LoginActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:launchMode="singleTop" android:launchMode="singleTop"
android:exported="false"
android:theme="@style/AppTheme.Light" /> android:theme="@style/AppTheme.Light" />
<activity android:name=".ui.home.CounterActivity" <activity android:name=".ui.modules.home.CounterActivity"
android:exported="false"
android:theme="@style/AppTheme.Black" /> android:theme="@style/AppTheme.Black" />
<activity android:name=".ui.feedback.FeedbackActivity" <activity android:name=".ui.modules.feedback.FeedbackActivity"
android:configChanges="orientation|screenSize|keyboardHidden" android:configChanges="orientation|screenSize|keyboardHidden"
android:label="@string/app_name" android:label="@string/app_name"
android:exported="false"
android:theme="@style/AppTheme" /> android:theme="@style/AppTheme" />
<activity android:name=".ui.settings.SettingsLicenseActivity" <activity android:name=".ui.modules.settings.SettingsLicenseActivity"
android:configChanges="orientation|keyboardHidden" android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:theme="@style/AppTheme" /> android:theme="@style/AppTheme" />
<activity android:name="com.canhub.cropper.CropImageActivity" <activity android:name="com.canhub.cropper.CropImageActivity"
android:configChanges="orientation|keyboardHidden" android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:theme="@style/Base.Theme.AppCompat" /> android:theme="@style/Base.Theme.AppCompat" />
<activity android:name=".ui.login.oauth.OAuthLoginActivity" <activity android:name=".ui.modules.base.BuildInvalidActivity" />
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:theme="@style/Theme.MaterialComponents.Light.DarkActionBar" />
<activity android:name=".ui.login.recaptcha.RecaptchaActivity"
android:configChanges="orientation|keyboardHidden"
android:exported="false"
android:theme="@style/Theme.MaterialComponents.Light.DarkActionBar" />
<activity android:name=".ui.base.BuildInvalidActivity" android:exported="false" />
<activity android:name=".ui.settings.contributors.ContributorsActivity" android:exported="false" />
<!-- _____ _ <!-- _____ _
| __ \ (_) | __ \ (_)
@ -176,14 +155,12 @@
|_| \_\___|\___\___|_| \_/ \___|_| |___/ |_| \_\___|\___\___|_| \_/ \___|_| |___/
--> -->
<receiver android:name=".receivers.UserPresentReceiver" <receiver android:name=".receivers.UserPresentReceiver"
android:enabled="true" android:enabled="true">
android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.USER_PRESENT" /> <action android:name="android.intent.action.USER_PRESENT" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver android:name=".sync.UpdateDownloaderService$DownloadProgressReceiver" <receiver android:name=".sync.UpdateDownloaderService$DownloadProgressReceiver">
android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" /> <action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
</intent-filter> </intent-filter>

View File

@ -1,10 +1,9 @@
<h3>Wersja 4.13.6, 2023-03-24</h3> <h3>Wersja 4.7.1, 2021-04-12</h3>
<ul> <ul>
<li>Naprawiono pobieranie załączników na Androidzie 13 i nowszym.</li> <li>Poprawiono sprawdzanie dostępności e-dziennika.</li>
<li>Dodano opcję odświeżenia planu lekcji na wybrany tydzień.</li> <li>Zmieniono datę w informacjach o aplikacji. @Luncenok</li>
<li>Usunięto błędy logowania. @BxOxSxS</li>
</ul> </ul>
<br> <br>
<br> <br>
Dzięki za korzystanie ze Szkolnego!<br> Dzięki za korzystanie ze Szkolnego!<br>
<i>&copy; [Kuba Szczodrzyński](@kuba2k2) 2023</i> <i>&copy; [Kuba Szczodrzyński](@kuba2k2), [Kacper Ziubryniewicz](@kapi2289) 2021</i>

View File

@ -9,7 +9,7 @@
/*secret password - removed for source code publication*/ /*secret password - removed for source code publication*/
static toys AES_IV[16] = { static toys AES_IV[16] = {
0x6d, 0xa5, 0x32, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 0xcc, 0x64, 0xdb, 0x3a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat); unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);

View File

@ -12,7 +12,6 @@ import android.graphics.drawable.Icon
import android.os.Build import android.os.Build
import android.provider.Settings import android.provider.Settings
import android.util.Log import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.multidex.MultiDexApplication import androidx.multidex.MultiDexApplication
import androidx.work.Configuration import androidx.work.Configuration
@ -27,88 +26,50 @@ import com.google.firebase.messaging.FirebaseMessaging
import com.google.gson.Gson import com.google.gson.Gson
import com.hypertrack.hyperlog.HyperLog import com.hypertrack.hyperlog.HyperLog
import com.mikepenz.iconics.Iconics import com.mikepenz.iconics.Iconics
import eu.szkolny.sslprovider.SSLProvider
import eu.szkolny.sslprovider.enableSupportedTls
import im.wangchao.mhttp.MHttp import im.wangchao.mhttp.MHttp
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.leolin.shortcutbadger.ShortcutBadger import me.leolin.shortcutbadger.ShortcutBadger
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.config.AppData
import pl.szczodrzynski.edziennik.config.Config import pl.szczodrzynski.edziennik.config.Config
import pl.szczodrzynski.edziennik.data.api.events.ProfileListEmptyEvent import pl.szczodrzynski.edziennik.data.api.events.ProfileListEmptyEvent
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing
import pl.szczodrzynski.edziennik.data.db.AppDb import pl.szczodrzynski.edziennik.data.db.AppDb
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.enums.LoginType
import pl.szczodrzynski.edziennik.ext.DAY
import pl.szczodrzynski.edziennik.ext.MS
import pl.szczodrzynski.edziennik.ext.putExtras
import pl.szczodrzynski.edziennik.ext.setLanguage
import pl.szczodrzynski.edziennik.network.SSLProviderInstaller
import pl.szczodrzynski.edziennik.network.cookie.DumbCookieJar import pl.szczodrzynski.edziennik.network.cookie.DumbCookieJar
import pl.szczodrzynski.edziennik.sync.SyncWorker import pl.szczodrzynski.edziennik.sync.SyncWorker
import pl.szczodrzynski.edziennik.sync.UpdateWorker import pl.szczodrzynski.edziennik.sync.UpdateWorker
import pl.szczodrzynski.edziennik.ui.base.CrashActivity import pl.szczodrzynski.edziennik.ui.modules.base.CrashActivity
import pl.szczodrzynski.edziennik.ui.base.enums.NavTarget import pl.szczodrzynski.edziennik.utils.*
import pl.szczodrzynski.edziennik.utils.DebugLogFormat
import pl.szczodrzynski.edziennik.utils.PermissionChecker
import pl.szczodrzynski.edziennik.utils.Themes
import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.managers.AttendanceManager import pl.szczodrzynski.edziennik.utils.managers.*
import pl.szczodrzynski.edziennik.utils.managers.AvailabilityManager import timber.log.Timber
import pl.szczodrzynski.edziennik.utils.managers.BuildManager
import pl.szczodrzynski.edziennik.utils.managers.EventManager
import pl.szczodrzynski.edziennik.utils.managers.GradesManager
import pl.szczodrzynski.edziennik.utils.managers.MessageManager
import pl.szczodrzynski.edziennik.utils.managers.NoteManager
import pl.szczodrzynski.edziennik.utils.managers.NotificationChannelsManager
import pl.szczodrzynski.edziennik.utils.managers.PermissionManager
import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager
import pl.szczodrzynski.edziennik.utils.managers.TimetableManager
import pl.szczodrzynski.edziennik.utils.managers.UpdateManager
import pl.szczodrzynski.edziennik.utils.managers.UserActionManager
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.system.exitProcess
class App : MultiDexApplication(), Configuration.Provider, CoroutineScope { class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
companion object { companion object {
@Volatile @Volatile
lateinit var db: AppDb lateinit var db: AppDb
private set
lateinit var config: Config lateinit var config: Config
// private set // for LabFragment
lateinit var profile: Profile lateinit var profile: Profile
private set
lateinit var data: AppData
private set
val profileId val profileId
get() = profile.id get() = profile.id
var enableChucker = false
var debugMode = false var debugMode = false
var devMode = false var devMode = false
} }
val api by lazy { SzkolnyApi(this) }
val attendanceManager by lazy { AttendanceManager(this) }
val availabilityManager by lazy { AvailabilityManager(this) }
val buildManager by lazy { BuildManager(this) }
val eventManager by lazy { EventManager(this) }
val gradesManager by lazy { GradesManager(this) }
val messageManager by lazy { MessageManager(this) }
val noteManager by lazy { NoteManager(this) }
val notificationChannelsManager by lazy { NotificationChannelsManager(this) } val notificationChannelsManager by lazy { NotificationChannelsManager(this) }
val permissionManager by lazy { PermissionManager(this) }
val textStylingManager by lazy { TextStylingManager(this) }
val timetableManager by lazy { TimetableManager(this) }
val updateManager by lazy { UpdateManager(this) }
val userActionManager by lazy { UserActionManager(this) } val userActionManager by lazy { UserActionManager(this) }
val gradesManager by lazy { GradesManager(this) }
val timetableManager by lazy { TimetableManager(this) }
val eventManager by lazy { EventManager(this) }
val permissionManager by lazy { PermissionManager(this) }
val attendanceManager by lazy { AttendanceManager(this) }
val buildManager by lazy { BuildManager(this) }
val db val db
get() = App.db get() = App.db
@ -118,8 +79,6 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
get() = App.profile get() = App.profile
val profileId val profileId
get() = App.profileId get() = App.profileId
val data
get() = App.data
private val job = Job() private val job = Job()
override val coroutineContext: CoroutineContext override val coroutineContext: CoroutineContext
@ -150,16 +109,16 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
.connectTimeout(15, TimeUnit.SECONDS) .connectTimeout(15, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS)
.enableSupportedTls(enableCleartext = true)
SSLProviderInstaller.enableSupportedTls(builder, enableCleartext = true)
if (devMode) { if (devMode) {
if (enableChucker) { HyperLog.initialize(this)
HyperLog.setLogLevel(Log.VERBOSE)
HyperLog.setLogFormat(DebugLogFormat(this))
val chuckerCollector = ChuckerCollector(this, true, RetentionManager.Period.ONE_HOUR) val chuckerCollector = ChuckerCollector(this, true, RetentionManager.Period.ONE_HOUR)
val chuckerInterceptor = ChuckerInterceptor(this, chuckerCollector) val chuckerInterceptor = ChuckerInterceptor(this, chuckerCollector)
builder.addInterceptor(chuckerInterceptor) builder.addInterceptor(chuckerInterceptor)
} }
}
http = builder.build() http = builder.build()
@ -208,23 +167,14 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
Iconics.respectFontBoundsDefault = true Iconics.respectFontBoundsDefault = true
// initialize companion object values // initialize companion object values
AppData.read(this)
App.db = AppDb(this) App.db = AppDb(this)
App.config = Config(App.db) App.config = Config(App.db)
App.profile = Profile(0, 0, 0, "")
debugMode = BuildConfig.DEBUG debugMode = BuildConfig.DEBUG
devMode = config.devMode ?: debugMode devMode = config.debugMode || debugMode
enableChucker = config.enableChucker ?: devMode
if (devMode) {
HyperLog.initialize(this)
HyperLog.setLogLevel(Log.VERBOSE)
HyperLog.setLogFormat(DebugLogFormat(this))
}
if (!profileLoadById(config.lastProfileId)) { if (!profileLoadById(config.lastProfileId)) {
val success = db.profileDao().firstId?.let { profileLoadById(it) } db.profileDao().firstId?.let { profileLoadById(it) }
if (success != true)
profileLoad(Profile(0, 0, LoginType.TEMPLATE, ""))
} }
buildHttp() buildHttp()
@ -235,13 +185,23 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
} }
Signing.getCert(this) Signing.getCert(this)
Utils.initializeStorageDir(this)
launch { launch {
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
config.migrate(this@App) config.migrate(this@App)
SSLProviderInstaller.install(applicationContext, this@App::buildHttp) SSLProvider.install(
applicationContext,
downloadIfNeeded = true,
supportTls13 = false,
onFinish = {
buildHttp()
},
onError = {
Timber.e("Failed to install SSLProvider: $it")
it.printStackTrace()
}
)
if (config.devModePassword != null) if (config.devModePassword != null)
checkDevModePassword() checkDevModePassword()
@ -263,35 +223,35 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
.setShortLabel(getString(R.string.shortcut_timetable)).setLongLabel(getString(R.string.shortcut_timetable)) .setShortLabel(getString(R.string.shortcut_timetable)).setLongLabel(getString(R.string.shortcut_timetable))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_timetable)) .setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_timetable))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java) .setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtras("fragmentId" to NavTarget.TIMETABLE)) .putExtra("fragmentId", MainActivity.DRAWER_ITEM_TIMETABLE))
.build() .build()
val shortcutAgenda = ShortcutInfo.Builder(this@App, "item_agenda") val shortcutAgenda = ShortcutInfo.Builder(this@App, "item_agenda")
.setShortLabel(getString(R.string.shortcut_agenda)).setLongLabel(getString(R.string.shortcut_agenda)) .setShortLabel(getString(R.string.shortcut_agenda)).setLongLabel(getString(R.string.shortcut_agenda))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_agenda)) .setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_agenda))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java) .setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtras("fragmentId" to NavTarget.AGENDA)) .putExtra("fragmentId", MainActivity.DRAWER_ITEM_AGENDA))
.build() .build()
val shortcutGrades = ShortcutInfo.Builder(this@App, "item_grades") val shortcutGrades = ShortcutInfo.Builder(this@App, "item_grades")
.setShortLabel(getString(R.string.shortcut_grades)).setLongLabel(getString(R.string.shortcut_grades)) .setShortLabel(getString(R.string.shortcut_grades)).setLongLabel(getString(R.string.shortcut_grades))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_grades)) .setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_grades))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java) .setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtras("fragmentId" to NavTarget.GRADES)) .putExtra("fragmentId", MainActivity.DRAWER_ITEM_GRADES))
.build() .build()
val shortcutHomework = ShortcutInfo.Builder(this@App, "item_homeworks") val shortcutHomework = ShortcutInfo.Builder(this@App, "item_homeworks")
.setShortLabel(getString(R.string.shortcut_homework)).setLongLabel(getString(R.string.shortcut_homework)) .setShortLabel(getString(R.string.shortcut_homework)).setLongLabel(getString(R.string.shortcut_homework))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_homework)) .setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_homework))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java) .setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtras("fragmentId" to NavTarget.HOMEWORK)) .putExtra("fragmentId", MainActivity.DRAWER_ITEM_HOMEWORK))
.build() .build()
val shortcutMessages = ShortcutInfo.Builder(this@App, "item_messages") val shortcutMessages = ShortcutInfo.Builder(this@App, "item_messages")
.setShortLabel(getString(R.string.shortcut_messages)).setLongLabel(getString(R.string.shortcut_messages)) .setShortLabel(getString(R.string.shortcut_messages)).setLongLabel(getString(R.string.shortcut_messages))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_messages)) .setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_messages))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java) .setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtras("fragmentId" to NavTarget.MESSAGES)) .putExtra("fragmentId", MainActivity.DRAWER_ITEM_MESSAGES))
.build() .build()
shortcutManager.dynamicShortcuts = listOf( shortcutManager.dynamicShortcuts = listOf(
@ -417,28 +377,10 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
} }
} }
fun profileLoad(profile: Profile) {
App.profile = profile
App.config.lastProfileId = profile.id
try {
App.data = AppData.get(profile.loginStoreType)
d("App", "Loaded AppData: ${App.data}")
// apply newly-added config overrides, if not changed by the user yet
for ((key, value) in App.data.configOverrides) {
val config = App.profile.config
if (!config.has(key))
config.set(key, value)
}
} catch (e: Exception) {
Log.e("App", "Cannot load AppData", e)
Toast.makeText(this, R.string.app_cannot_load_data, Toast.LENGTH_LONG).show()
exitProcess(0)
}
}
private fun profileLoadById(profileId: Int): Boolean { private fun profileLoadById(profileId: Int): Boolean {
db.profileDao().getByIdNow(profileId)?.also { db.profileDao().getByIdNow(profileId)?.also {
profileLoad(it) App.profile = it
App.config.lastProfileId = it.id
return true return true
} }
return false return false
@ -469,8 +411,6 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
} }
fun profileSave() = profileSave(profile) fun profileSave() = profileSave(profile)
fun profileSave(profile: Profile) { fun profileSave(profile: Profile) {
if (profile.id == profileId)
App.profile = profile
launch(Dispatchers.Default) { launch(Dispatchers.Default) {
App.db.profileDao().add(profile) App.db.profileDao().add(profile)
} }

View File

@ -4,11 +4,8 @@
package pl.szczodrzynski.edziennik package pl.szczodrzynski.edziennik
import android.graphics.Paint import android.graphics.Paint
import android.view.View
import android.widget.TextView import android.widget.TextView
import androidx.core.view.isVisible
import androidx.databinding.BindingAdapter import androidx.databinding.BindingAdapter
import pl.szczodrzynski.edziennik.ext.dp
object Binding { object Binding {
@JvmStatic @JvmStatic
@ -20,64 +17,4 @@ object Binding {
textView.paintFlags = textView.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv() textView.paintFlags = textView.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
} }
} }
@JvmStatic
@BindingAdapter("android:isVisible")
fun isVisible(view: View, isVisible: Boolean) {
view.isVisible = isVisible
}
private fun resizeDrawable(textView: TextView, index: Int, size: Int) {
val drawables = textView.compoundDrawables
drawables[index]?.setBounds(0, 0, size, size)
textView.setCompoundDrawables(drawables[0], drawables[1], drawables[2], drawables[3])
}
@JvmStatic
@BindingAdapter("android:drawableLeftAutoSize")
fun drawableLeftAutoSize(textView: TextView, enable: Boolean) = resizeDrawable(
textView,
index = 0,
size = textView.textSize.toInt(),
)
@JvmStatic
@BindingAdapter("android:drawableRightAutoSize")
fun drawableRightAutoSize(textView: TextView, enable: Boolean) = resizeDrawable(
textView,
index = 2,
size = textView.textSize.toInt(),
)
@JvmStatic
@BindingAdapter("android:drawableLeftSize")
fun drawableLeftSize(textView: TextView, sizeDp: Int) = resizeDrawable(
textView,
index = 0,
size = sizeDp.dp,
)
@JvmStatic
@BindingAdapter("android:drawableTopSize")
fun drawableTopSize(textView: TextView, sizeDp: Int) = resizeDrawable(
textView,
index = 1,
size = sizeDp.dp,
)
@JvmStatic
@BindingAdapter("android:drawableRightSize")
fun drawableRightSize(textView: TextView, sizeDp: Int) = resizeDrawable(
textView,
index = 2,
size = sizeDp.dp,
)
@JvmStatic
@BindingAdapter("android:drawableBottomSize")
fun drawableBottomSize(textView: TextView, sizeDp: Int) = resizeDrawable(
textView,
index = 3,
size = sizeDp.dp,
)
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@ import android.provider.OpenableColumns
import com.canhub.cropper.CropImage import com.canhub.cropper.CropImage
import com.canhub.cropper.CropImageView import com.canhub.cropper.CropImageView
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.ui.login.LoginActivity import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream

View File

@ -0,0 +1,9 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-27.
*/
package pl.szczodrzynski.edziennik.config
interface AbstractConfig {
fun set(key: String, value: String?)
}

View File

@ -1,70 +0,0 @@
/*
* Copyright (c) Kuba Szczodrzyński 2022-10-21.
*/
package pl.szczodrzynski.edziennik.config
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import com.google.gson.stream.JsonReader
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.enums.LoginType
import pl.szczodrzynski.edziennik.ext.getJsonObject
import pl.szczodrzynski.edziennik.ext.mergeWith
import pl.szczodrzynski.edziennik.utils.managers.TextStylingManager.HtmlMode
data class AppData(
val configOverrides: Map<String, String>,
val messagesConfig: MessagesConfig,
val uiConfig: UIConfig,
val eventTypes: List<EventType>,
) {
companion object {
private var data: JsonObject? = null
private val appData = mutableMapOf<LoginType, AppData>()
fun read(app: App) {
val res = app.resources.openRawResource(R.raw.app_data)
data = JsonParser.parseReader(JsonReader(res.reader())).asJsonObject
}
fun get(loginType: LoginType): AppData {
if (loginType in appData)
return appData.getValue(loginType)
val json = data?.getJsonObject("base")?.deepCopy()
?: throw NoSuchElementException("Base data not found")
val overrides = setOf(loginType, loginType.schoolType)
for (overrideType in overrides) {
val override = data?.getJsonObject(overrideType.name.lowercase()) ?: continue
json.mergeWith(override)
}
val value = Gson().fromJson(json, AppData::class.java)
appData[loginType] = value
return value
}
}
data class MessagesConfig(
val subjectLength: Int?,
val bodyLength: Int?,
val textStyling: Boolean,
val syncRecipientList: Boolean,
val htmlMode: HtmlMode,
val needsReadStatus: Boolean,
)
data class UIConfig(
val lessonHeight: Int,
val enableMarkAsReadAnnouncements: Boolean,
val enableNoticePoints: Boolean,
val eventManualShowSubjectDropdown: Boolean,
)
data class EventType(
val id: Long,
val color: String,
val name: String,
)
}

View File

@ -1,48 +0,0 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-27.
*/
package pl.szczodrzynski.edziennik.config
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.config.db.ConfigEntry
import pl.szczodrzynski.edziennik.data.db.AppDb
import pl.szczodrzynski.edziennik.ext.takePositive
import kotlin.coroutines.CoroutineContext
abstract class BaseConfig(
@Transient
val db: AppDb,
val profileId: Int? = null,
protected var entries: List<ConfigEntry>? = null,
) : CoroutineScope {
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Default
val values = hashMapOf<String, String?>()
init {
if (entries == null)
entries = db.configDao().getAllNow()
values.clear()
for ((profileId, key, value) in entries!!) {
if (profileId.takePositive() != this.profileId)
continue
values[key] = value
}
}
fun set(key: String, value: String?) {
values[key] = value
launch(Dispatchers.IO) {
db.configDao().add(ConfigEntry(profileId ?: -1, key, value))
}
}
fun has(key: String) = values.containsKey(key)
}

View File

@ -5,59 +5,144 @@
package pl.szczodrzynski.edziennik.config package pl.szczodrzynski.edziennik.config
import com.google.gson.JsonObject import com.google.gson.JsonObject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.BuildConfig import pl.szczodrzynski.edziennik.BuildConfig
import pl.szczodrzynski.edziennik.config.utils.* import pl.szczodrzynski.edziennik.config.db.ConfigEntry
import pl.szczodrzynski.edziennik.config.utils.ConfigMigration
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.config.utils.toHashMap
import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update
import pl.szczodrzynski.edziennik.data.db.AppDb import pl.szczodrzynski.edziennik.data.db.AppDb
import kotlin.coroutines.CoroutineContext
@Suppress("RemoveExplicitTypeArguments") class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
class Config(db: AppDb) : BaseConfig(db) {
companion object { companion object {
const val DATA_VERSION = 12 const val DATA_VERSION = 12
} }
private val profileConfigs: HashMap<Int, ProfileConfig> = hashMapOf() private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Default
val values: HashMap<String, String?> = hashMapOf()
val ui by lazy { ConfigUI(this) } val ui by lazy { ConfigUI(this) }
val sync by lazy { ConfigSync(this) } val sync by lazy { ConfigSync(this) }
val timetable by lazy { ConfigTimetable(this) } val timetable by lazy { ConfigTimetable(this) }
val grades by lazy { ConfigGrades(this) } val grades by lazy { ConfigGrades(this) }
var dataVersion by config<Int>(DATA_VERSION) private var mDataVersion: Int? = null
var hash by config<String>("") var dataVersion: Int
get() { mDataVersion = mDataVersion ?: values.get("dataVersion", 0); return mDataVersion ?: 0 }
set(value) { set("dataVersion", value); mDataVersion = value }
var lastProfileId by config<Int>(0) private var mHash: String? = null
var loginFinished by config<Boolean>(false) var hash: String
var privacyPolicyAccepted by config<Boolean>(false) get() { mHash = mHash ?: values.get("hash", ""); return mHash ?: "" }
var update by config<Update?>(null) set(value) { set("hash", value); mHash = value }
var updatesChannel by config<String>("release")
var devMode by config<Boolean?>("debugMode", null) private var mLastProfileId: Int? = null
var devModePassword by config<String?>(null) var lastProfileId: Int
var enableChucker by config<Boolean?>(null) get() { mLastProfileId = mLastProfileId ?: values.get("lastProfileId", 0); return mLastProfileId ?: 0 }
set(value) { set("lastProfileId", value); mLastProfileId = value }
var apiAvailabilityCheck by config<Boolean>(true) private var mUpdatesChannel: String? = null
var apiInvalidCert by config<String?>(null) var updatesChannel: String
var apiKeyCustom by config<String?>(null) get() { mUpdatesChannel = mUpdatesChannel ?: values.get("updatesChannel", "release"); return mUpdatesChannel ?: "release" }
var appInstalledTime by config<Long>(0L) set(value) { set("updatesChannel", value); mUpdatesChannel = value }
var appRateSnackbarTime by config<Long>(0L) private var mUpdate: Update? = null
var appVersion by config<Int>(BuildConfig.VERSION_CODE) var update: Update?
var validation by config<String?>(null, "buildValidation") get() { mUpdate = mUpdate ?: values.get("update", null as Update?); return mUpdate ?: null as Update? }
set(value) { set("update", value); mUpdate = value }
var archiverEnabled by config<Boolean>(true) private var mAppVersion: Int? = null
var runSync by config<Boolean>(false) var appVersion: Int
var widgetConfigs by config<JsonObject> { JsonObject() } get() { mAppVersion = mAppVersion ?: values.get("appVersion", BuildConfig.VERSION_CODE); return mAppVersion ?: BuildConfig.VERSION_CODE }
set(value) { set("appVersion", value); mAppVersion = value }
private var mLoginFinished: Boolean? = null
var loginFinished: Boolean
get() { mLoginFinished = mLoginFinished ?: values.get("loginFinished", false); return mLoginFinished ?: false }
set(value) { set("loginFinished", value); mLoginFinished = value }
private var mPrivacyPolicyAccepted: Boolean? = null
var privacyPolicyAccepted: Boolean
get() { mPrivacyPolicyAccepted = mPrivacyPolicyAccepted ?: values.get("privacyPolicyAccepted", false); return mPrivacyPolicyAccepted ?: false }
set(value) { set("privacyPolicyAccepted", value); mPrivacyPolicyAccepted = value }
private var mDebugMode: Boolean? = null
var debugMode: Boolean
get() { mDebugMode = mDebugMode ?: values.get("debugMode", false); return mDebugMode ?: false }
set(value) { set("debugMode", value); mDebugMode = value }
private var mDevModePassword: String? = null
var devModePassword: String?
get() { mDevModePassword = mDevModePassword ?: values.get("devModePassword", null as String?); return mDevModePassword }
set(value) { set("devModePassword", value); mDevModePassword = value }
private var mAppInstalledTime: Long? = null
var appInstalledTime: Long
get() { mAppInstalledTime = mAppInstalledTime ?: values.get("appInstalledTime", 0L); return mAppInstalledTime ?: 0L }
set(value) { set("appInstalledTime", value); mAppInstalledTime = value }
private var mAppRateSnackbarTime: Long? = null
var appRateSnackbarTime: Long
get() { mAppRateSnackbarTime = mAppRateSnackbarTime ?: values.get("appRateSnackbarTime", 0L); return mAppRateSnackbarTime ?: 0L }
set(value) { set("appRateSnackbarTime", value); mAppRateSnackbarTime = value }
private var mRunSync: Boolean? = null
var runSync: Boolean
get() { mRunSync = mRunSync ?: values.get("runSync", false); return mRunSync ?: false }
set(value) { set("runSync", value); mRunSync = value }
private var mWidgetConfigs: JsonObject? = null
var widgetConfigs: JsonObject
get() { mWidgetConfigs = mWidgetConfigs ?: values.get("widgetConfigs", JsonObject()); return mWidgetConfigs ?: JsonObject() }
set(value) { set("widgetConfigs", value); mWidgetConfigs = value }
private var mArchiverEnabled: Boolean? = null
var archiverEnabled: Boolean
get() { mArchiverEnabled = mArchiverEnabled ?: values.get("archiverEnabled", true); return mArchiverEnabled ?: true }
set(value) { set("archiverEnabled", value); mArchiverEnabled = value }
private var mValidation: String? = null
var validation: String?
get() { mValidation = mValidation ?: values["buildValidation"]; return mValidation }
set(value) { set("buildValidation", value); mValidation = value }
private var mApiInvalidCert: String? = null
var apiInvalidCert: String?
get() { mApiInvalidCert = mApiInvalidCert ?: values["apiInvalidCert"]; return mApiInvalidCert }
set(value) { set("apiInvalidCert", value); mApiInvalidCert = value }
private var rawEntries: List<ConfigEntry> = db.configDao().getAllNow()
private val profileConfigs: HashMap<Int, ProfileConfig> = hashMapOf()
init {
rawEntries.toHashMap(-1, values)
}
fun migrate(app: App) { fun migrate(app: App) {
if (dataVersion < DATA_VERSION || hash == "") if (dataVersion < DATA_VERSION)
// migrate old data version OR freshly installed app (or updated from 3.x)
ConfigMigration(app, this) ConfigMigration(app, this)
} }
fun getFor(profileId: Int): ProfileConfig {
operator fun get(profileId: Int): ProfileConfig { return profileConfigs[profileId] ?: ProfileConfig(db, profileId, db.configDao().getAllNow(profileId)).also {
return profileConfigs[profileId] ?: ProfileConfig(db, profileId, entries).also {
profileConfigs[profileId] = it profileConfigs[profileId] = it
} }
} }
fun forProfile() = getFor(App.profileId)
fun setProfile(profileId: Int) {
}
override fun set(key: String, value: String?) {
values[key] = value
launch {
db.configDao().add(ConfigEntry(-1, key, value))
}
}
} }

View File

@ -4,10 +4,13 @@
package pl.szczodrzynski.edziennik.config package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.ORDER_BY_DATE_DESC import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.utils.managers.GradesManager
@Suppress("RemoveExplicitTypeArguments") class ConfigGrades(private val config: Config) {
class ConfigGrades(base: Config) { private var mOrderBy: Int? = null
var orderBy: Int
var orderBy by base.config<Int>("gradesOrderBy", ORDER_BY_DATE_DESC) get() { mOrderBy = mOrderBy ?: config.values.get("gradesOrderBy", 0); return mOrderBy ?: GradesManager.ORDER_BY_DATE_DESC }
set(value) { config.set("gradesOrderBy", value); mOrderBy = value }
} }

View File

@ -4,53 +4,139 @@
package pl.szczodrzynski.edziennik.config package pl.szczodrzynski.edziennik.config
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import pl.szczodrzynski.edziennik.BuildConfig import pl.szczodrzynski.edziennik.BuildConfig
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.getIntList
import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.config.utils.setMap
import pl.szczodrzynski.edziennik.data.api.szkolny.response.RegisterAvailabilityStatus import pl.szczodrzynski.edziennik.data.api.szkolny.response.RegisterAvailabilityStatus
import pl.szczodrzynski.edziennik.ext.HOUR
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
@Suppress("RemoveExplicitTypeArguments") class ConfigSync(private val config: Config) {
class ConfigSync(base: Config) { private val gson = Gson()
var enabled by base.config<Boolean>("syncEnabled", true) private var mDontShowAppManagerDialog: Boolean? = null
var interval by base.config<Int>("syncInterval", 1 * HOUR.toInt()) var dontShowAppManagerDialog: Boolean
var onlyWifi by base.config<Boolean>("syncOnlyWifi", false) get() { mDontShowAppManagerDialog = mDontShowAppManagerDialog ?: config.values.get("dontShowAppManagerDialog", false); return mDontShowAppManagerDialog ?: false }
set(value) { config.set("dontShowAppManagerDialog", value); mDontShowAppManagerDialog = value }
var dontShowAppManagerDialog by base.config<Boolean>(false) private var mSyncEnabled: Boolean? = null
var lastAppSync by base.config<Long>(0L) var enabled: Boolean
var notifyAboutUpdates by base.config<Boolean>(true) get() { mSyncEnabled = mSyncEnabled ?: config.values.get("syncEnabled", true); return mSyncEnabled ?: true }
var webPushEnabled by base.config<Boolean>(true) set(value) { config.set("syncEnabled", value); mSyncEnabled = value }
// Quiet Hours private var mWebPushEnabled: Boolean? = null
var quietHoursEnabled by base.config<Boolean>(false) var webPushEnabled: Boolean
var quietHoursStart by base.config<Time?>(null) get() { mWebPushEnabled = mWebPushEnabled ?: config.values.get("webPushEnabled", true); return mWebPushEnabled ?: true }
var quietHoursEnd by base.config<Time?>(null) set(value) { config.set("webPushEnabled", value); mWebPushEnabled = value }
var quietDuringLessons by base.config<Boolean>(false)
// FCM Tokens private var mSyncOnlyWifi: Boolean? = null
var tokenApp by base.config<String?>(null) var onlyWifi: Boolean
var tokenMobidziennik by base.config<String?>(null) get() { mSyncOnlyWifi = mSyncOnlyWifi ?: config.values.get("syncOnlyWifi", false); return mSyncOnlyWifi ?: notifyAboutUpdates }
var tokenLibrus by base.config<String?>(null) set(value) { config.set("syncOnlyWifi", value); mSyncOnlyWifi = value }
var tokenVulcan by base.config<String?>(null)
var tokenVulcanHebe by base.config<String?>(null)
var tokenMobidziennikList by base.config<List<Int>> { listOf() } private var mSyncInterval: Int? = null
var tokenLibrusList by base.config<List<Int>> { listOf() } var interval: Int
var tokenVulcanList by base.config<List<Int>> { listOf() } get() { mSyncInterval = mSyncInterval ?: config.values.get("syncInterval", 60*60); return mSyncInterval ?: 60*60 }
var tokenVulcanHebeList by base.config<List<Int>> { listOf() } set(value) { config.set("syncInterval", value); mSyncInterval = value }
// Register Availability private var mNotifyAboutUpdates: Boolean? = null
private var registerAvailabilityMap by base.config<Map<String, RegisterAvailabilityStatus>>("registerAvailability") { mapOf() } var notifyAboutUpdates: Boolean
private var registerAvailabilityFlavor by base.config<String?>(null) get() { mNotifyAboutUpdates = mNotifyAboutUpdates ?: config.values.get("notifyAboutUpdates", true); return mNotifyAboutUpdates ?: true }
set(value) { config.set("notifyAboutUpdates", value); mNotifyAboutUpdates = value }
private var mLastAppSync: Long? = null
var lastAppSync: Long
get() { mLastAppSync = mLastAppSync ?: config.values.get("lastAppSync", 0L); return mLastAppSync ?: 0L }
set(value) { config.set("lastAppSync", value); mLastAppSync = value }
/* ____ _ _ _
/ __ \ (_) | | | |
| | | |_ _ _ ___| |_ | |__ ___ _ _ _ __ ___
| | | | | | | |/ _ \ __| | '_ \ / _ \| | | | '__/ __|
| |__| | |_| | | __/ |_ | | | | (_) | |_| | | \__ \
\___\_\\__,_|_|\___|\__| |_| |_|\___/ \__,_|_| |__*/
private var mQuietHoursEnabled: Boolean? = null
var quietHoursEnabled: Boolean
get() { mQuietHoursEnabled = mQuietHoursEnabled ?: config.values.get("quietHoursEnabled", false); return mQuietHoursEnabled ?: false }
set(value) { config.set("quietHoursEnabled", value); mQuietHoursEnabled = value }
private var mQuietHoursStart: Time? = null
var quietHoursStart: Time?
get() { mQuietHoursStart = mQuietHoursStart ?: config.values.get("quietHoursStart", null as Time?); return mQuietHoursStart }
set(value) { config.set("quietHoursStart", value); mQuietHoursStart = value }
private var mQuietHoursEnd: Time? = null
var quietHoursEnd: Time?
get() { mQuietHoursEnd = mQuietHoursEnd ?: config.values.get("quietHoursEnd", null as Time?); return mQuietHoursEnd }
set(value) { config.set("quietHoursEnd", value); mQuietHoursEnd = value }
private var mQuietDuringLessons: Boolean? = null
var quietDuringLessons: Boolean
get() { mQuietDuringLessons = mQuietDuringLessons ?: config.values.get("quietDuringLessons", false); return mQuietDuringLessons ?: false }
set(value) { config.set("quietDuringLessons", value); mQuietDuringLessons = value }
/* ______ _____ __ __ _______ _
| ____/ ____| \/ | |__ __| | |
| |__ | | | \ / | | | ___ | | _____ _ __ ___
| __|| | | |\/| | | |/ _ \| |/ / _ \ '_ \/ __|
| | | |____| | | | | | (_) | < __/ | | \__ \
|_| \_____|_| |_| |_|\___/|_|\_\___|_| |_|__*/
private var mTokenApp: String? = null
var tokenApp: String?
get() { mTokenApp = mTokenApp ?: config.values.get("tokenApp", null as String?); return mTokenApp }
set(value) { config.set("tokenApp", value); mTokenApp = value }
private var mTokenMobidziennik: String? = null
var tokenMobidziennik: String?
get() { mTokenMobidziennik = mTokenMobidziennik ?: config.values.get("tokenMobidziennik", null as String?); return mTokenMobidziennik }
set(value) { config.set("tokenMobidziennik", value); mTokenMobidziennik = value }
private var mTokenLibrus: String? = null
var tokenLibrus: String?
get() { mTokenLibrus = mTokenLibrus ?: config.values.get("tokenLibrus", null as String?); return mTokenLibrus }
set(value) { config.set("tokenLibrus", value); mTokenLibrus = value }
private var mTokenVulcan: String? = null
var tokenVulcan: String?
get() { mTokenVulcan = mTokenVulcan ?: config.values.get("tokenVulcan", null as String?); return mTokenVulcan }
set(value) { config.set("tokenVulcan", value); mTokenVulcan = value }
private var mTokenVulcanHebe: String? = null
var tokenVulcanHebe: String?
get() { mTokenVulcanHebe = mTokenVulcanHebe ?: config.values.get("tokenVulcanHebe", null as String?); return mTokenVulcanHebe }
set(value) { config.set("tokenVulcanHebe", value); mTokenVulcanHebe = value }
private var mTokenMobidziennikList: List<Int>? = null
var tokenMobidziennikList: List<Int>
get() { mTokenMobidziennikList = mTokenMobidziennikList ?: config.values.getIntList("tokenMobidziennikList", listOf()); return mTokenMobidziennikList ?: listOf() }
set(value) { config.set("tokenMobidziennikList", value); mTokenMobidziennikList = value }
private var mTokenLibrusList: List<Int>? = null
var tokenLibrusList: List<Int>
get() { mTokenLibrusList = mTokenLibrusList ?: config.values.getIntList("tokenLibrusList", listOf()); return mTokenLibrusList ?: listOf() }
set(value) { config.set("tokenLibrusList", value); mTokenLibrusList = value }
private var mTokenVulcanList: List<Int>? = null
var tokenVulcanList: List<Int>
get() { mTokenVulcanList = mTokenVulcanList ?: config.values.getIntList("tokenVulcanList", listOf()); return mTokenVulcanList ?: listOf() }
set(value) { config.set("tokenVulcanList", value); mTokenVulcanList = value }
private var mTokenVulcanHebeList: List<Int>? = null
var tokenVulcanHebeList: List<Int>
get() { mTokenVulcanHebeList = mTokenVulcanHebeList ?: config.values.getIntList("tokenVulcanHebeList", listOf()); return mTokenVulcanHebeList ?: listOf() }
set(value) { config.set("tokenVulcanHebeList", value); mTokenVulcanHebeList = value }
private var mRegisterAvailability: Map<String, RegisterAvailabilityStatus>? = null
var registerAvailability: Map<String, RegisterAvailabilityStatus> var registerAvailability: Map<String, RegisterAvailabilityStatus>
get() { get() {
if (BuildConfig.FLAVOR != registerAvailabilityFlavor) val flavor = config.values.get("registerAvailabilityFlavor", null as String?)
if (BuildConfig.FLAVOR != flavor)
return mapOf() return mapOf()
return registerAvailabilityMap
mRegisterAvailability = mRegisterAvailability ?: config.values.get("registerAvailability", null as String?)?.let { it ->
gson.fromJson(it, object: TypeToken<Map<String, RegisterAvailabilityStatus>>(){}.type)
}
return mRegisterAvailability ?: mapOf()
} }
set(value) { set(value) {
registerAvailabilityMap = value config.setMap("registerAvailability", value)
registerAvailabilityFlavor = BuildConfig.FLAVOR config.set("registerAvailabilityFlavor", BuildConfig.FLAVOR)
mRegisterAvailability = value
} }
} }

View File

@ -4,12 +4,23 @@
package pl.szczodrzynski.edziennik.config package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
@Suppress("RemoveExplicitTypeArguments") class ConfigTimetable(private val config: Config) {
class ConfigTimetable(base: Config) { private var mBellSyncMultiplier: Int? = null
var bellSyncMultiplier: Int
get() { mBellSyncMultiplier = mBellSyncMultiplier ?: config.values.get("bellSyncMultiplier", 0); return mBellSyncMultiplier ?: 0 }
set(value) { config.set("bellSyncMultiplier", value); mBellSyncMultiplier = value }
var bellSyncMultiplier by base.config<Int>(0) private var mBellSyncDiff: Time? = null
var bellSyncDiff by base.config<Time?>(null) var bellSyncDiff: Time?
var countInSeconds by base.config<Boolean>(false) get() { mBellSyncDiff = mBellSyncDiff ?: config.values.get("bellSyncDiff", null as Time?); return mBellSyncDiff }
set(value) { config.set("bellSyncDiff", value); mBellSyncDiff = value }
private var mCountInSeconds: Boolean? = null
var countInSeconds: Boolean
get() { mCountInSeconds = mCountInSeconds ?: config.values.get("countInSeconds", false); return mCountInSeconds ?: false }
set(value) { config.set("countInSeconds", value); mCountInSeconds = value }
} }

View File

@ -4,32 +4,58 @@
package pl.szczodrzynski.edziennik.config package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.ui.base.enums.NavTarget import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.getIntList
import pl.szczodrzynski.edziennik.config.utils.set
@Suppress("RemoveExplicitTypeArguments") class ConfigUI(private val config: Config) {
class ConfigUI(base: Config) { private var mTheme: Int? = null
var theme: Int
get() { mTheme = mTheme ?: config.values.get("theme", 1); return mTheme ?: 1 }
set(value) { config.set("theme", value); mTheme = value }
var theme by base.config<Int>(1) private var mLanguage: String? = null
var language by base.config<String?>(null) var language: String?
get() { mLanguage = mLanguage ?: config.values.get("language", null as String?); return mLanguage }
set(value) { config.set("language", value); mLanguage = value }
var appBackground by base.config<String?>("appBg", null) private var mHeaderBackground: String? = null
var headerBackground by base.config<String?>("headerBg", null) var headerBackground: String?
get() { mHeaderBackground = mHeaderBackground ?: config.values.get("headerBg", null as String?); return mHeaderBackground }
set(value) { config.set("headerBg", value); mHeaderBackground = value }
var miniMenuVisible by base.config<Boolean>(false) private var mAppBackground: String? = null
var miniMenuButtons by base.config<Set<NavTarget>> { var appBackground: String?
setOf( get() { mAppBackground = mAppBackground ?: config.values.get("appBg", null as String?); return mAppBackground }
NavTarget.HOME, set(value) { config.set("appBg", value); mAppBackground = value }
NavTarget.TIMETABLE,
NavTarget.AGENDA, private var mMiniMenuVisible: Boolean? = null
NavTarget.GRADES, var miniMenuVisible: Boolean
NavTarget.MESSAGES, get() { mMiniMenuVisible = mMiniMenuVisible ?: config.values.get("miniMenuVisible", false); return mMiniMenuVisible ?: false }
NavTarget.HOMEWORK, set(value) { config.set("miniMenuVisible", value); mMiniMenuVisible = value }
NavTarget.SETTINGS
) private var mMiniMenuButtons: List<Int>? = null
} var miniMenuButtons: List<Int>
var openDrawerOnBackPressed by base.config<Boolean>(false) get() { mMiniMenuButtons = mMiniMenuButtons ?: config.values.getIntList("miniMenuButtons", listOf()); return mMiniMenuButtons ?: listOf() }
set(value) { config.set("miniMenuButtons", value); mMiniMenuButtons = value }
var bottomSheetOpened by base.config<Boolean>(false)
var snowfall by base.config<Boolean>(false) private var mOpenDrawerOnBackPressed: Boolean? = null
var eggfall by base.config<Boolean>(false) var openDrawerOnBackPressed: Boolean
get() { mOpenDrawerOnBackPressed = mOpenDrawerOnBackPressed ?: config.values.get("openDrawerOnBackPressed", false); return mOpenDrawerOnBackPressed ?: false }
set(value) { config.set("openDrawerOnBackPressed", value); mOpenDrawerOnBackPressed = value }
private var mSnowfall: Boolean? = null
var snowfall: Boolean
get() { mSnowfall = mSnowfall ?: config.values.get("snowfall", false); return mSnowfall ?: false }
set(value) { config.set("snowfall", value); mSnowfall = value }
private var mEggfall: Boolean? = null
var eggfall: Boolean
get() { mEggfall = mEggfall ?: config.values.get("eggfall", false); return mEggfall ?: false }
set(value) { config.set("eggfall", value); mEggfall = value }
private var mBottomSheetOpened: Boolean? = null
var bottomSheetOpened: Boolean
get() { mBottomSheetOpened = mBottomSheetOpened ?: config.values.get("bottomSheetOpened", false); return mBottomSheetOpened ?: false }
set(value) { config.set("bottomSheetOpened", value); mBottomSheetOpened = value }
} }

View File

@ -1,175 +0,0 @@
/*
* Copyright (c) Kuba Szczodrzyński 2022-10-21.
*/
package pl.szczodrzynski.edziennik.config
import com.google.gson.Gson
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.reflect.TypeToken
import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
import java.lang.reflect.ParameterizedType
import java.lang.reflect.WildcardType
import kotlin.reflect.KProperty
private val gson = Gson()
inline fun <reified T> BaseConfig.config(name: String? = null, noinline default: () -> T) = ConfigDelegate(
config = this,
type = T::class.java,
nullable = null is T,
typeToken = object : TypeToken<T>() {},
defaultFunc = default,
defaultValue = null,
fieldName = name,
)
inline fun <reified T> BaseConfig.config(default: T) = ConfigDelegate(
config = this,
type = T::class.java,
nullable = null is T,
typeToken = object : TypeToken<T>() {},
defaultFunc = null,
defaultValue = default,
fieldName = null,
)
inline fun <reified T> BaseConfig.config(name: String? = null, default: T) = ConfigDelegate(
config = this,
type = T::class.java,
nullable = null is T,
typeToken = object : TypeToken<T>() {},
defaultFunc = null,
defaultValue = default,
fieldName = name,
)
@Suppress("UNCHECKED_CAST")
class ConfigDelegate<T>(
private val config: BaseConfig,
private val type: Class<T>,
private val nullable: Boolean,
private val typeToken: TypeToken<T>,
private val defaultFunc: (() -> T)?,
private val defaultValue: T?,
private val fieldName: String?,
) {
private var value: T? = null
private var isInitialized = false
private fun getDefault(): T = when {
defaultFunc != null -> defaultFunc.invoke()
else -> defaultValue as T
}
private fun getGenericType(index: Int = 0): Class<*> {
val parameterizedType = typeToken.type as ParameterizedType
val typeArgument = parameterizedType.actualTypeArguments[index] as WildcardType
return typeArgument.upperBounds[0] as Class<*>
}
operator fun setValue(_thisRef: Any, property: KProperty<*>, newValue: T) {
value = newValue
isInitialized = true
config.set(fieldName ?: property.name, serialize(newValue)?.toString())
}
operator fun getValue(_thisRef: Any, property: KProperty<*>): T {
if (isInitialized)
return value as T
val key = fieldName ?: property.name
if (key !in config.values) {
value = getDefault()
isInitialized = true
return value as T
}
val str = config.values[key]
value = if (str == null && nullable)
null as T
else if (str == null)
getDefault()
else
deserialize(str)
isInitialized = true
return value as T
}
private fun <I> serialize(value: I?, serializeObjects: Boolean = true): Any? {
if (value == null)
return null
return when (value) {
is String -> value
is Date -> value.stringY_m_d
is Time -> value.stringValue
is JsonObject -> value
is JsonArray -> value
// primitives
is Number -> value
is Boolean -> value
// enums, maps & collections
is Enum<*> -> value.toInt()
is Collection<*> -> value.map {
if (it is Number || it is Boolean) it else serialize(it, serializeObjects = false)
}.toJsonElement()
is Map<*, *> -> gson.toJson(value.mapValues { (_, it) ->
if (it is Number || it is Boolean) it else serialize(it, serializeObjects = false)
})
// objects or else
else -> if (serializeObjects) gson.toJson(value) else value
}
}
private fun <I> deserialize(value: String?, type: Class<*> = this.type): I? {
if (value == null)
return null
@Suppress("TYPE_MISMATCH_WARNING")
return when (type) {
String::class.java -> value
Date::class.java -> Date.fromY_m_d(value)
Time::class.java -> Time.fromHms(value)
JsonObject::class.java -> value.toJsonObject()
JsonArray::class.java -> value.toJsonArray()
// primitives
java.lang.Integer::class.java -> value.toIntOrNull()
java.lang.Boolean::class.java -> value.toBooleanStrictOrNull()
java.lang.Long::class.java -> value.toLongOrNull()
java.lang.Float::class.java -> value.toFloatOrNull()
// enums, maps & collections
else -> when {
Enum::class.java.isAssignableFrom(type) -> value.toIntOrNull()?.toEnum(type) as Enum<*>
Collection::class.java.isAssignableFrom(type) -> {
val array = value.toJsonArray()
val genericType = getGenericType()
val list = array?.map {
val str = if (it.isJsonPrimitive) it.asString else it.toString()
deserialize<Any>(str, genericType)
}
when {
List::class.java.isAssignableFrom(type) -> list
Set::class.java.isAssignableFrom(type) -> list?.toSet()
else -> list?.toTypedArray()
}
}
Map::class.java.isAssignableFrom(type) -> {
val obj = value.toJsonObject()
val genericType = getGenericType(index = 1)
val map = obj?.entrySet()?.associate { (key, it) ->
val str = if (it.isJsonPrimitive) it.asString else it.toString()
key to deserialize<Any>(str, genericType)
}
map
}
// objects or else
else -> gson.fromJson(value, type)
}
} as? I
}
}

View File

@ -4,20 +4,29 @@
package pl.szczodrzynski.edziennik.config package pl.szczodrzynski.edziennik.config
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.config.db.ConfigEntry import pl.szczodrzynski.edziennik.config.db.ConfigEntry
import pl.szczodrzynski.edziennik.config.utils.ProfileConfigMigration import pl.szczodrzynski.edziennik.config.utils.ProfileConfigMigration
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.config.utils.toHashMap
import pl.szczodrzynski.edziennik.data.db.AppDb import pl.szczodrzynski.edziennik.data.db.AppDb
import kotlin.coroutines.CoroutineContext
@Suppress("RemoveExplicitTypeArguments") class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEntry>) : CoroutineScope, AbstractConfig {
class ProfileConfig(
db: AppDb,
profileId: Int,
entries: List<ConfigEntry>?,
) : BaseConfig(db, profileId, entries) {
companion object { companion object {
const val DATA_VERSION = 5 const val DATA_VERSION = 2
} }
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Default
val values: HashMap<String, String?> = hashMapOf()
val grades by lazy { ProfileConfigGrades(this) } val grades by lazy { ProfileConfigGrades(this) }
val ui by lazy { ProfileConfigUI(this) } val ui by lazy { ProfileConfigUI(this) }
val sync by lazy { ProfileConfigSync(this) } val sync by lazy { ProfileConfigSync(this) }
@ -26,13 +35,26 @@ class ProfileConfig(
val timetable by lazy { ConfigTimetable(this) } val timetable by lazy { ConfigTimetable(this) }
val grades by lazy { ConfigGrades(this) }*/ val grades by lazy { ConfigGrades(this) }*/
var dataVersion by config<Int>(DATA_VERSION) private var mDataVersion: Int? = null
var hash by config<String>("") var dataVersion: Int
get() { mDataVersion = mDataVersion ?: values.get("dataVersion", 0); return mDataVersion ?: 0 }
set(value) { set("dataVersion", value); mDataVersion = value }
var shareByDefault by config<Boolean>(false) private var mHash: String? = null
var hash: String
get() { mHash = mHash ?: values.get("hash", ""); return mHash ?: "" }
set(value) { set("hash", value); mHash = value }
init { init {
rawEntries.toHashMap(profileId, values)
if (dataVersion < DATA_VERSION) if (dataVersion < DATA_VERSION)
ProfileConfigMigration(this) ProfileConfigMigration(this)
} }
override fun set(key: String, value: String?) {
values[key] = value
launch {
db.configDao().add(ConfigEntry(profileId, key, value))
}
}
} }

View File

@ -4,11 +4,27 @@
package pl.szczodrzynski.edziennik.config package pl.szczodrzynski.edziennik.config
@Suppress("RemoveExplicitTypeArguments") import pl.szczodrzynski.edziennik.config.utils.get
class ProfileConfigAttendance(base: ProfileConfig) { import pl.szczodrzynski.edziennik.config.utils.set
var attendancePageSelection by base.config<Int>(1) class ProfileConfigAttendance(private val config: ProfileConfig) {
var groupConsecutiveDays by base.config<Boolean>(true) private var mAttendancePageSelection: Int? = null
var showPresenceInMonth by base.config<Boolean>(false) var attendancePageSelection: Int
var useSymbols by base.config<Boolean>(false) get() { mAttendancePageSelection = mAttendancePageSelection ?: config.values.get("attendancePageSelection", 1); return mAttendancePageSelection ?: 1 }
set(value) { config.set("attendancePageSelection", value); mAttendancePageSelection = value }
private var mUseSymbols: Boolean? = null
var useSymbols: Boolean
get() { mUseSymbols = mUseSymbols ?: config.values.get("useSymbols", false); return mUseSymbols ?: false }
set(value) { config.set("useSymbols", value); mUseSymbols = value }
private var mGroupConsecutiveDays: Boolean? = null
var groupConsecutiveDays: Boolean
get() { mGroupConsecutiveDays = mGroupConsecutiveDays ?: config.values.get("groupConsecutiveDays", true); return mGroupConsecutiveDays ?: true }
set(value) { config.set("groupConsecutiveDays", value); mGroupConsecutiveDays = value }
private var mShowPresenceInMonth: Boolean? = null
var showPresenceInMonth: Boolean
get() { mShowPresenceInMonth = mShowPresenceInMonth ?: config.values.get("showPresenceInMonth", false); return mShowPresenceInMonth ?: false }
set(value) { config.set("showPresenceInMonth", value); mShowPresenceInMonth = value }
} }

View File

@ -4,19 +4,54 @@
package pl.szczodrzynski.edziennik.config package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.getFloat
import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_WEIGHTED import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_WEIGHTED
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES
@Suppress("RemoveExplicitTypeArguments") class ProfileConfigGrades(private val config: ProfileConfig) {
class ProfileConfigGrades(base: ProfileConfig) { private var mColorMode: Int? = null
var colorMode: Int
get() { mColorMode = mColorMode ?: config.values.get("gradesColorMode", COLOR_MODE_WEIGHTED); return mColorMode ?: COLOR_MODE_WEIGHTED }
set(value) { config.set("gradesColorMode", value); mColorMode = value }
var averageWithoutWeight by base.config<Boolean>(true) private var mYearAverageMode: Int? = null
var colorMode by base.config<Int>(COLOR_MODE_WEIGHTED) var yearAverageMode: Int
var dontCountEnabled by base.config<Boolean>(false) get() { mYearAverageMode = mYearAverageMode ?: config.values.get("yearAverageMode", YEAR_ALL_GRADES); return mYearAverageMode ?: YEAR_ALL_GRADES }
var dontCountGrades by base.config<List<String>> { listOf() } set(value) { config.set("yearAverageMode", value); mYearAverageMode = value }
var hideImproved by base.config<Boolean>(false)
var hideSticksFromOld by base.config<Boolean>(false) private var mHideImproved: Boolean? = null
var minusValue by base.config<Float?>(null) var hideImproved: Boolean
var plusValue by base.config<Float?>(null) get() { mHideImproved = mHideImproved ?: config.values.get("hideImproved", false); return mHideImproved ?: false }
var yearAverageMode by base.config<Int>(YEAR_ALL_GRADES) set(value) { config.set("hideImproved", value); mHideImproved = value }
private var mAverageWithoutWeight: Boolean? = null
var averageWithoutWeight: Boolean
get() { mAverageWithoutWeight = mAverageWithoutWeight ?: config.values.get("averageWithoutWeight", true); return mAverageWithoutWeight ?: true }
set(value) { config.set("averageWithoutWeight", value); mAverageWithoutWeight = value }
private var mPlusValue: Float? = null
var plusValue: Float?
get() { mPlusValue = mPlusValue ?: config.values.getFloat("plusValue"); return mPlusValue }
set(value) { config.set("plusValue", value); mPlusValue = value }
private var mMinusValue: Float? = null
var minusValue: Float?
get() { mMinusValue = mMinusValue ?: config.values.getFloat("minusValue"); return mMinusValue }
set(value) { config.set("minusValue", value); mMinusValue = value }
private var mDontCountEnabled: Boolean? = null
var dontCountEnabled: Boolean
get() { mDontCountEnabled = mDontCountEnabled ?: config.values.get("dontCountEnabled", false); return mDontCountEnabled ?: false }
set(value) { config.set("dontCountEnabled", value); mDontCountEnabled = value }
private var mDontCountGrades: List<String>? = null
var dontCountGrades: List<String>
get() { mDontCountGrades = mDontCountGrades ?: config.values.get("dontCountGrades", listOf()); return mDontCountGrades ?: listOf() }
set(value) { config.set("dontCountGrades", value); mDontCountGrades = value }
private var mHideSticksFromOld: Boolean? = null
var hideSticksFromOld: Boolean
get() { mHideSticksFromOld = mHideSticksFromOld ?: config.values.get("hideSticksFromOld", false); return mHideSticksFromOld ?: false }
set(value) { config.set("hideSticksFromOld", value); mHideSticksFromOld = value }
} }

View File

@ -4,14 +4,12 @@
package pl.szczodrzynski.edziennik.config package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.data.db.enums.NotificationType import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set
@Suppress("RemoveExplicitTypeArguments") class ProfileConfigSync(private val config: ProfileConfig) {
class ProfileConfigSync(base: ProfileConfig) { private var mNotificationFilter: List<Int>? = null
var notificationFilter: List<Int>
var notificationFilter by base.config<Set<NotificationType>> { get() { mNotificationFilter = mNotificationFilter ?: config.values.get("notificationFilter", listOf()); return mNotificationFilter ?: listOf() }
NotificationType.values() set(value) { config.set("notificationFilter", value); mNotificationFilter = value }
.filter { it.enabledByDefault == false }
.toSet()
}
} }

View File

@ -4,30 +4,69 @@
package pl.szczodrzynski.edziennik.config package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.AGENDA_DEFAULT import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.AGENDA_DEFAULT
import pl.szczodrzynski.edziennik.ui.home.HomeCardModel import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardModel
@Suppress("RemoveExplicitTypeArguments") class ProfileConfigUI(private val config: ProfileConfig) {
class ProfileConfigUI(base: ProfileConfig) { private var mAgendaViewType: Int? = null
var agendaViewType: Int
get() { mAgendaViewType = mAgendaViewType ?: config.values.get("agendaViewType", 0); return mAgendaViewType ?: AGENDA_DEFAULT }
set(value) { config.set("agendaViewType", value); mAgendaViewType = value }
var agendaViewType by base.config<Int>(AGENDA_DEFAULT) private var mAgendaCompactMode: Boolean? = null
var agendaCompactMode by base.config<Boolean>(false) var agendaCompactMode: Boolean
var agendaGroupByType by base.config<Boolean>(false) get() { mAgendaCompactMode = mAgendaCompactMode ?: config.values.get("agendaCompactMode", false); return mAgendaCompactMode ?: false }
var agendaLessonChanges by base.config<Boolean>(true) set(value) { config.set("agendaCompactMode", value); mAgendaCompactMode = value }
var agendaTeacherAbsence by base.config<Boolean>(true)
var agendaSubjectImportant by base.config<Boolean>(false)
var agendaElearningMark by base.config<Boolean>(false)
var agendaElearningGroup by base.config<Boolean>(true)
var homeCards by base.config<List<HomeCardModel>> { listOf() } private var mAgendaGroupByType: Boolean? = null
var agendaGroupByType: Boolean
get() { mAgendaGroupByType = mAgendaGroupByType ?: config.values.get("agendaGroupByType", false); return mAgendaGroupByType ?: false }
set(value) { config.set("agendaGroupByType", value); mAgendaGroupByType = value }
var messagesGreetingOnCompose by base.config<Boolean>(true) private var mAgendaLessonChanges: Boolean? = null
var messagesGreetingOnReply by base.config<Boolean>(true) var agendaLessonChanges: Boolean
var messagesGreetingOnForward by base.config<Boolean>(false) get() { mAgendaLessonChanges = mAgendaLessonChanges ?: config.values.get("agendaLessonChanges", true); return mAgendaLessonChanges ?: true }
var messagesGreetingText by base.config<String?>(null) set(value) { config.set("agendaLessonChanges", value); mAgendaLessonChanges = value }
var timetableShowAttendance by base.config<Boolean>(true) private var mAgendaTeacherAbsence: Boolean? = null
var timetableShowEvents by base.config<Boolean>(true) var agendaTeacherAbsence: Boolean
var timetableTrimHourRange by base.config<Boolean>(false) get() { mAgendaTeacherAbsence = mAgendaTeacherAbsence ?: config.values.get("agendaTeacherAbsence", true); return mAgendaTeacherAbsence ?: true }
var timetableColorSubjectName by base.config<Boolean>(false) set(value) { config.set("agendaTeacherAbsence", value); mAgendaTeacherAbsence = value }
private var mAgendaElearningMark: Boolean? = null
var agendaElearningMark: Boolean
get() { mAgendaElearningMark = mAgendaElearningMark ?: config.values.get("agendaElearningMark", false); return mAgendaElearningMark ?: false }
set(value) { config.set("agendaElearningMark", value); mAgendaElearningMark = value }
private var mAgendaElearningGroup: Boolean? = null
var agendaElearningGroup: Boolean
get() { mAgendaElearningGroup = mAgendaElearningGroup ?: config.values.get("agendaElearningGroup", true); return mAgendaElearningGroup ?: true }
set(value) { config.set("agendaElearningGroup", value); mAgendaElearningGroup = value }
private var mHomeCards: List<HomeCardModel>? = null
var homeCards: List<HomeCardModel>
get() { mHomeCards = mHomeCards ?: config.values.get("homeCards", listOf(), HomeCardModel::class.java); return mHomeCards ?: listOf() }
set(value) { config.set("homeCards", value); mHomeCards = value }
private var mMessagesGreetingOnCompose: Boolean? = null
var messagesGreetingOnCompose: Boolean
get() { mMessagesGreetingOnCompose = mMessagesGreetingOnCompose ?: config.values.get("messagesGreetingOnCompose", true); return mMessagesGreetingOnCompose ?: true }
set(value) { config.set("messagesGreetingOnCompose", value); mMessagesGreetingOnCompose = value }
private var mMessagesGreetingOnReply: Boolean? = null
var messagesGreetingOnReply: Boolean
get() { mMessagesGreetingOnReply = mMessagesGreetingOnReply ?: config.values.get("messagesGreetingOnReply", true); return mMessagesGreetingOnReply ?: true }
set(value) { config.set("messagesGreetingOnReply", value); mMessagesGreetingOnReply = value }
private var mMessagesGreetingOnForward: Boolean? = null
var messagesGreetingOnForward: Boolean
get() { mMessagesGreetingOnForward = mMessagesGreetingOnForward ?: config.values.get("messagesGreetingOnForward", false); return mMessagesGreetingOnForward ?: false }
set(value) { config.set("messagesGreetingOnForward", value); mMessagesGreetingOnForward = value }
private var mMessagesGreetingText: String? = null
var messagesGreetingText: String?
get() { mMessagesGreetingText = mMessagesGreetingText ?: config.values["messagesGreetingText"]; return mMessagesGreetingText }
set(value) { config.set("messagesGreetingText", value); mMessagesGreetingText = value }
} }

View File

@ -17,7 +17,7 @@ interface ConfigDao {
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
fun addAll(list: List<ConfigEntry>) fun addAll(list: List<ConfigEntry>)
@Query("SELECT * FROM config") @Query("SELECT * FROM config WHERE profileId = -1")
fun getAllNow(): List<ConfigEntry> fun getAllNow(): List<ConfigEntry>
@Query("SELECT * FROM config WHERE profileId = :profileId") @Query("SELECT * FROM config WHERE profileId = :profileId")

View File

@ -8,35 +8,38 @@ import android.content.SharedPreferences
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import pl.szczodrzynski.edziennik.BuildConfig import pl.szczodrzynski.edziennik.BuildConfig
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.config.Config import pl.szczodrzynski.edziennik.config.Config
import pl.szczodrzynski.edziennik.data.db.enums.LoginType import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_LIBRUS
import pl.szczodrzynski.edziennik.ext.asNavTargetOrNull import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_MOBIDZIENNIK
import pl.szczodrzynski.edziennik.ui.base.enums.NavTarget import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_VULCAN
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
import kotlin.math.abs import kotlin.math.abs
class AppConfigMigrationV3(p: SharedPreferences, config: Config) { class AppConfigMigrationV3(p: SharedPreferences, config: Config) {
init { init { config.apply {
val s = "app.appConfig" val s = "app.appConfig"
config.apply { if (dataVersion < 1) {
ui.theme = p.getString("$s.appTheme", null)?.toIntOrNull() ?: 1 ui.theme = p.getString("$s.appTheme", null)?.toIntOrNull() ?: 1
sync.enabled = p.getString("$s.registerSyncEnabled", null)?.toBoolean() ?: true sync.enabled = p.getString("$s.registerSyncEnabled", null)?.toBoolean() ?: true
sync.interval = p.getString("$s.registerSyncInterval", null)?.toIntOrNull() ?: 3600 sync.interval = p.getString("$s.registerSyncInterval", null)?.toIntOrNull() ?: 3600
val oldButtons = p.getString("$s.miniDrawerButtonIds", null)?.let { str -> val oldButtons = p.getString("$s.miniDrawerButtonIds", null)?.let { str ->
str.replace("[\\[\\]]*".toRegex(), "") str.replace("[\\[\\]]*".toRegex(), "")
.split(",\\s?".toRegex()) .split(",\\s?".toRegex())
.mapNotNull { it.toIntOrNull().asNavTargetOrNull() } .mapNotNull { it.toIntOrNull() }
.toSet()
} }
ui.miniMenuButtons = oldButtons ?: setOf( ui.miniMenuButtons = oldButtons ?: listOf(
NavTarget.HOME, MainActivity.DRAWER_ITEM_HOME,
NavTarget.TIMETABLE, MainActivity.DRAWER_ITEM_TIMETABLE,
NavTarget.AGENDA, MainActivity.DRAWER_ITEM_AGENDA,
NavTarget.GRADES, MainActivity.DRAWER_ITEM_GRADES,
NavTarget.MESSAGES, MainActivity.DRAWER_ITEM_MESSAGES,
NavTarget.HOMEWORK, MainActivity.DRAWER_ITEM_HOMEWORK,
NavTarget.SETTINGS MainActivity.DRAWER_ITEM_SETTINGS
) )
dataVersion = 1
}
if (dataVersion < 2) {
devModePassword = p.getString("$s.devModePassword", null).fix() devModePassword = p.getString("$s.devModePassword", null).fix()
sync.tokenApp = p.getString("$s.fcmToken", null).fix() sync.tokenApp = p.getString("$s.fcmToken", null).fix()
timetable.bellSyncMultiplier = p.getString("$s.bellSyncMultiplier", null)?.toIntOrNull() ?: 0 timetable.bellSyncMultiplier = p.getString("$s.bellSyncMultiplier", null)?.toIntOrNull() ?: 0
@ -78,13 +81,14 @@ class AppConfigMigrationV3(p: SharedPreferences, config: Config) {
tokens?.forEach { tokens?.forEach {
val token = it.value.first val token = it.value.first
when (it.key) { when (it.key) {
LoginType.MOBIDZIENNIK.id -> sync.tokenMobidziennik = token LOGIN_TYPE_MOBIDZIENNIK -> sync.tokenMobidziennik = token
LoginType.VULCAN.id -> sync.tokenVulcan = token LOGIN_TYPE_VULCAN -> sync.tokenVulcan = token
LoginType.LIBRUS.id -> sync.tokenLibrus = token LOGIN_TYPE_LIBRUS -> sync.tokenLibrus = token
}
} }
} }
dataVersion = 2
} }
}}
private fun String?.fix(): String? { private fun String?.fix(): String? {
return this?.replace("\"", "")?.let { if (it == "null") null else it } return this?.replace("\"", "")?.let { if (it == "null") null else it }

View File

@ -0,0 +1,110 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-27.
*/
package pl.szczodrzynski.edziennik.config.utils
import com.google.gson.*
import com.google.gson.reflect.TypeToken
import pl.szczodrzynski.edziennik.config.AbstractConfig
import pl.szczodrzynski.edziennik.config.db.ConfigEntry
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
private val gson = Gson()
fun AbstractConfig.set(key: String, value: Int) {
set(key, value.toString())
}
fun AbstractConfig.set(key: String, value: Boolean) {
set(key, value.toString())
}
fun AbstractConfig.set(key: String, value: Long) {
set(key, value.toString())
}
fun AbstractConfig.set(key: String, value: Float) {
set(key, value.toString())
}
fun AbstractConfig.set(key: String, value: Date?) {
set(key, value?.stringY_m_d)
}
fun AbstractConfig.set(key: String, value: Time?) {
set(key, value?.stringValue)
}
fun AbstractConfig.set(key: String, value: JsonElement?) {
set(key, value?.toString())
}
fun AbstractConfig.set(key: String, value: List<Any>?) {
set(key, value?.let { gson.toJson(it) })
}
fun AbstractConfig.set(key: String, value: Any?) {
set(key, value?.let { gson.toJson(it) })
}
fun AbstractConfig.setStringList(key: String, value: List<String>?) {
set(key, value?.let { gson.toJson(it) })
}
fun AbstractConfig.setIntList(key: String, value: List<Int>?) {
set(key, value?.let { gson.toJson(it) })
}
fun AbstractConfig.setLongList(key: String, value: List<Long>?) {
set(key, value?.let { gson.toJson(it) })
}
fun <K, V> AbstractConfig.setMap(key: String, value: Map<K, V>?) {
set(key, value?.let { gson.toJson(it) })
}
fun HashMap<String, String?>.get(key: String, default: String?): String? {
return this[key] ?: default
}
fun HashMap<String, String?>.get(key: String, default: Boolean): Boolean {
return this[key]?.toBoolean() ?: default
}
fun HashMap<String, String?>.get(key: String, default: Int): Int {
return this[key]?.toIntOrNull() ?: default
}
fun HashMap<String, String?>.get(key: String, default: Long): Long {
return this[key]?.toLongOrNull() ?: default
}
fun HashMap<String, String?>.get(key: String, default: Float): Float {
return this[key]?.toFloatOrNull() ?: default
}
fun HashMap<String, String?>.get(key: String, default: Date?): Date? {
return this[key]?.let { Date.fromY_m_d(it) } ?: default
}
fun HashMap<String, String?>.get(key: String, default: Time?): Time? {
return this[key]?.let { Time.fromHms(it) } ?: default
}
fun HashMap<String, String?>.get(key: String, default: JsonObject?): JsonObject? {
return this[key]?.let { JsonParser().parse(it)?.asJsonObject } ?: default
}
fun HashMap<String, String?>.get(key: String, default: JsonArray?): JsonArray? {
return this[key]?.let { JsonParser().parse(it)?.asJsonArray } ?: default
}
inline fun <reified T> HashMap<String, String?>.get(key: String, default: T?): T? {
return this[key]?.let { Gson().fromJson(it, T::class.java) } ?: default
}
/* !!! cannot use mutable list here - modifying it will not update the DB */
fun <T> HashMap<String, String?>.get(key: String, default: List<T>?, classOfT: Class<T>): List<T>? {
return this[key]?.let { ConfigGsonUtils().deserializeList<T>(gson, it, classOfT) } ?: default
}
fun HashMap<String, String?>.getStringList(key: String, default: List<String>?): List<String>? {
return this[key]?.let { gson.fromJson<List<String>>(it, object: TypeToken<List<String>>(){}.type) } ?: default
}
fun HashMap<String, String?>.getIntList(key: String, default: List<Int>?): List<Int>? {
return this[key]?.let { gson.fromJson<List<Int>>(it, object: TypeToken<List<Int>>(){}.type) } ?: default
}
fun HashMap<String, String?>.getLongList(key: String, default: List<Long>?): List<Long>? {
return this[key]?.let { gson.fromJson<List<Long>>(it, object: TypeToken<List<Long>>(){}.type) } ?: default
}
fun HashMap<String, String?>.getFloat(key: String): Float? {
return this[key]?.toFloatOrNull()
}
fun List<ConfigEntry>.toHashMap(profileId: Int, map: HashMap<String, String?>) {
map.clear()
forEach {
if (it.profileId == profileId)
map[it.key] = it.value
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-12-2.
*/
package pl.szczodrzynski.edziennik.config.utils
import com.google.gson.Gson
import com.google.gson.JsonParser
import pl.szczodrzynski.edziennik.getInt
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardModel
import pl.szczodrzynski.edziennik.utils.models.Time
class ConfigGsonUtils {
fun <T> deserializeList(gson: Gson, str: String?, classOfT: Class<T>): List<T> {
val json = JsonParser().parse(str)
val list: MutableList<T> = mutableListOf()
if (!json.isJsonArray)
return list
json.asJsonArray.forEach { e ->
when (classOfT) {
String::class.java -> {
list += e.asString as T
}
HomeCardModel::class.java -> {
val o = e.asJsonObject
list += HomeCardModel(
o.getInt("profileId", 0),
o.getInt("cardId", 0)
) as T
}
Time::class.java -> {
val o = e.asJsonObject
list += Time(
o.getInt("hour", 0),
o.getInt("minute", 0),
o.getInt("second", 0)
) as T
}
}
}
return list
}
}

View File

@ -5,9 +5,12 @@
package pl.szczodrzynski.edziennik.config.utils package pl.szczodrzynski.edziennik.config.utils
import android.content.Context import android.content.Context
import androidx.core.content.edit
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.BuildConfig
import pl.szczodrzynski.edziennik.HOUR
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.config.Config import pl.szczodrzynski.edziennik.config.Config
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.ORDER_BY_DATE_DESC
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
import kotlin.math.abs import kotlin.math.abs
@ -19,14 +22,74 @@ class ConfigMigration(app: App, config: Config) {
// migrate appConfig from app version 3.x and lower. // migrate appConfig from app version 3.x and lower.
// Updates dataVersion to level 2. // Updates dataVersion to level 2.
AppConfigMigrationV3(p, config) AppConfigMigrationV3(p, config)
p.edit {
remove("app.appConfig.appTheme")
} }
if (dataVersion < 2) {
appVersion = BuildConfig.VERSION_CODE
loginFinished = false
ui.language = null
ui.theme = 1
ui.appBackground = null
ui.headerBackground = null
ui.miniMenuVisible = false
ui.miniMenuButtons = listOf(
MainActivity.DRAWER_ITEM_HOME,
MainActivity.DRAWER_ITEM_TIMETABLE,
MainActivity.DRAWER_ITEM_AGENDA,
MainActivity.DRAWER_ITEM_GRADES,
MainActivity.DRAWER_ITEM_MESSAGES,
MainActivity.DRAWER_ITEM_HOMEWORK,
MainActivity.DRAWER_ITEM_SETTINGS
)
sync.enabled = true
sync.interval = 1*HOUR.toInt()
sync.notifyAboutUpdates = true
sync.onlyWifi = false
sync.quietHoursEnabled = false
sync.quietHoursStart = null
sync.quietHoursEnd = null
sync.quietDuringLessons = false
sync.tokenApp = null
sync.tokenMobidziennik = null
sync.tokenMobidziennikList = listOf()
sync.tokenLibrus = null
sync.tokenLibrusList = listOf()
sync.tokenVulcan = null
sync.tokenVulcanList = listOf()
timetable.bellSyncMultiplier = 0
timetable.bellSyncDiff = null
timetable.countInSeconds = false
grades.orderBy = ORDER_BY_DATE_DESC
dataVersion = 2
}
if (dataVersion < 3) {
update = null
privacyPolicyAccepted = false
debugMode = false
devModePassword = null
appInstalledTime = 0L
appRateSnackbarTime = 0L
dataVersion = 3
}
if (dataVersion < 10) {
ui.openDrawerOnBackPressed = false
ui.snowfall = false
ui.bottomSheetOpened = false
sync.dontShowAppManagerDialog = false
sync.webPushEnabled = true
sync.lastAppSync = 0L
dataVersion = 10
} }
if (dataVersion < 11) { if (dataVersion < 11) {
val startMillis = config.values["quietHoursStart"]?.toLongOrNull() ?: 0L val startMillis = config.values.get("quietHoursStart", 0L)
val endMillis = config.values["quietHoursEnd"]?.toLongOrNull() ?: 0L val endMillis = config.values.get("quietHoursEnd", 0L)
if (startMillis > 0) { if (startMillis > 0) {
try { try {
sync.quietHoursStart = Time.fromMillis(abs(startMillis)) sync.quietHoursStart = Time.fromMillis(abs(startMillis))
@ -43,7 +106,5 @@ class ConfigMigration(app: App, config: Config) {
dataVersion = 11 dataVersion = 11
} }
hash = "invalid"
}} }}
} }

View File

@ -5,57 +5,33 @@
package pl.szczodrzynski.edziennik.config.utils package pl.szczodrzynski.edziennik.config.utils
import pl.szczodrzynski.edziennik.config.ProfileConfig import pl.szczodrzynski.edziennik.config.ProfileConfig
import pl.szczodrzynski.edziennik.data.db.entity.Notification
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.AGENDA_DEFAULT import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.AGENDA_DEFAULT
import pl.szczodrzynski.edziennik.data.db.enums.NotificationType
import pl.szczodrzynski.edziennik.data.db.enums.SchoolType
import pl.szczodrzynski.edziennik.ui.home.HomeCard
import pl.szczodrzynski.edziennik.ui.home.HomeCardModel
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_WEIGHTED import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_WEIGHTED
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES
class ProfileConfigMigration(config: ProfileConfig) { class ProfileConfigMigration(config: ProfileConfig) {
init { config.apply { init { config.apply {
val profile = db.profileDao().getByIdNow(profileId ?: -1) if (dataVersion < 1) {
grades.colorMode = COLOR_MODE_WEIGHTED
grades.yearAverageMode = YEAR_ALL_GRADES
grades.hideImproved = false
grades.averageWithoutWeight = true
grades.plusValue = null
grades.minusValue = null
grades.dontCountEnabled = false
grades.dontCountGrades = listOf()
ui.agendaViewType = AGENDA_DEFAULT
// no migration for ui.homeCards
dataVersion = 1
}
if (dataVersion < 2) { if (dataVersion < 2) {
sync.notificationFilter = sync.notificationFilter + NotificationType.TEACHER_ABSENCE sync.notificationFilter = sync.notificationFilter + Notification.TYPE_TEACHER_ABSENCE
dataVersion = 2 dataVersion = 2
} }
if (dataVersion < 3) {
if (ui.homeCards.isNotEmpty()) {
ui.homeCards = ui.homeCards + HomeCardModel(
profileId = config.profileId ?: -1,
cardId = HomeCard.CARD_NOTES,
)
}
dataVersion = 3
}
if (dataVersion < 4) {
// switch to new event types (USOS)
dataVersion = 4
if (profile?.loginStoreType?.schoolType == SchoolType.UNIVERSITY) {
db.eventTypeDao().clear(profileId ?: -1)
db.eventTypeDao().addDefaultTypes(profile)
}
}
if (dataVersion < 5) {
// update USOS event types and the appropriate events (2022-12-25)
dataVersion = 5
if (profile?.loginStoreType?.schoolType == SchoolType.UNIVERSITY) {
db.eventTypeDao().getAllWithDefaults(profile)
// wejściówka (4) -> kartkówka (3)
db.eventDao().getRawNow("UPDATE events SET eventType = 3 WHERE profileId = $profileId AND eventType = 4;")
// zadanie (6) -> zadanie domowe (-1)
db.eventDao().getRawNow("UPDATE events SET eventType = -1 WHERE profileId = $profileId AND eventType = 6;")
}
}
}} }}
} }

View File

@ -22,7 +22,7 @@ import pl.szczodrzynski.edziennik.data.api.task.ErrorReportTask
import pl.szczodrzynski.edziennik.data.api.task.IApiTask import pl.szczodrzynski.edziennik.data.api.task.IApiTask
import pl.szczodrzynski.edziennik.data.api.task.SzkolnyTask import pl.szczodrzynski.edziennik.data.api.task.SzkolnyTask
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.ext.toApiError import pl.szczodrzynski.edziennik.toApiError
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
import kotlin.math.min import kotlin.math.min
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -84,21 +84,19 @@ class ApiService : Service() {
runTask() runTask()
} }
override fun onRequiresUserAction(event: UserActionRequiredEvent) {
app.userActionManager.sendToUser(event)
taskRunning?.cancel()
clearTask()
runTask()
}
override fun onError(apiError: ApiError) { override fun onError(apiError: ApiError) {
lastEventTime = System.currentTimeMillis() lastEventTime = System.currentTimeMillis()
d(TAG, "Task $taskRunningId threw an error - $apiError") d(TAG, "Task $taskRunningId threw an error - $apiError")
apiError.profileId = taskProfileId apiError.profileId = taskProfileId
if (app.userActionManager.requiresUserAction(apiError)) {
app.userActionManager.sendToUser(apiError)
}
else {
EventBus.getDefault().postSticky(ApiTaskErrorEvent(apiError)) EventBus.getDefault().postSticky(ApiTaskErrorEvent(apiError))
errorList.add(apiError) errorList.add(apiError)
apiError.throwable?.printStackTrace() apiError.throwable?.printStackTrace()
}
if (apiError.isCritical) { if (apiError.isCritical) {
taskRunning?.cancel() taskRunning?.cancel()

View File

@ -24,12 +24,11 @@ const val FAKE_LIBRUS_ACCOUNTS = "/synergia_accounts.php"
val LIBRUS_USER_AGENT = "${SYSTEM_USER_AGENT}LibrusMobileApp" val LIBRUS_USER_AGENT = "${SYSTEM_USER_AGENT}LibrusMobileApp"
const val SYNERGIA_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Gecko/20100101 Firefox/62.0" const val SYNERGIA_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Gecko/20100101 Firefox/62.0"
const val LIBRUS_CLIENT_ID = "VaItV6oRutdo8fnjJwysnTjVlvaswf52ZqmXsJGP" const val LIBRUS_CLIENT_ID = "0RbsDOkV9tyKEQYzlLv5hs3DM1ukrynFI4p6C1Yc"
const val LIBRUS_REDIRECT_URL = "app://librus" const val LIBRUS_REDIRECT_URL = "app://librus"
const val LIBRUS_AUTHORIZE_URL = "https://portal.librus.pl/konto-librus/redirect/dru" const val LIBRUS_AUTHORIZE_URL = "https://portal.librus.pl/oauth2/authorize?client_id=$LIBRUS_CLIENT_ID&redirect_uri=$LIBRUS_REDIRECT_URL&response_type=code"
const val LIBRUS_LOGIN_URL = "https://portal.librus.pl/konto-librus/login/action" const val LIBRUS_LOGIN_URL = "https://portal.librus.pl/rodzina/login/action"
const val LIBRUS_TOKEN_URL = "https://portal.librus.pl/oauth2/access_token" const val LIBRUS_TOKEN_URL = "https://portal.librus.pl/oauth2/access_token"
const val LIBRUS_HEADER = "pl.librus.synergiaDru2"
const val LIBRUS_ACCOUNT_URL = "/v3/SynergiaAccounts/fresh/" // + login const val LIBRUS_ACCOUNT_URL = "/v3/SynergiaAccounts/fresh/" // + login
const val LIBRUS_ACCOUNTS_URL = "/v3/SynergiaAccounts" const val LIBRUS_ACCOUNTS_URL = "/v3/SynergiaAccounts"
@ -44,7 +43,7 @@ const val LIBRUS_API_TOKEN_URL = "https://api.librus.pl/OAuth/Token"
const val LIBRUS_API_TOKEN_JST_URL = "https://api.librus.pl/OAuth/TokenJST" const val LIBRUS_API_TOKEN_JST_URL = "https://api.librus.pl/OAuth/TokenJST"
const val LIBRUS_API_AUTHORIZATION = "Mjg6ODRmZGQzYTg3YjAzZDNlYTZmZmU3NzdiNThiMzMyYjE=" const val LIBRUS_API_AUTHORIZATION = "Mjg6ODRmZGQzYTg3YjAzZDNlYTZmZmU3NzdiNThiMzMyYjE="
const val LIBRUS_API_SECRET_JST = "18b7c1ee08216f636a1b1a2440e68398" const val LIBRUS_API_SECRET_JST = "18b7c1ee08216f636a1b1a2440e68398"
const val LIBRUS_API_CLIENT_ID_JST = "59" const val LIBRUS_API_CLIENT_ID_JST = "49"
//const val LIBRUS_API_CLIENT_ID_JST_REFRESH = "42" //const val LIBRUS_API_CLIENT_ID_JST_REFRESH = "42"
const val LIBRUS_JST_DEMO_CODE = "68656A21" const val LIBRUS_JST_DEMO_CODE = "68656A21"
@ -60,12 +59,40 @@ const val LIBRUS_SANDBOX_URL = "https://sandbox.librus.pl/index.php?action="
const val LIBRUS_SYNERGIA_HOMEWORK_ATTACHMENT_URL = "https://synergia.librus.pl/homework/downloadFile" const val LIBRUS_SYNERGIA_HOMEWORK_ATTACHMENT_URL = "https://synergia.librus.pl/homework/downloadFile"
const val LIBRUS_SYNERGIA_MESSAGES_ATTACHMENT_URL = "https://synergia.librus.pl/wiadomosci/pobierz_zalacznik" const val LIBRUS_SYNERGIA_MESSAGES_ATTACHMENT_URL = "https://synergia.librus.pl/wiadomosci/pobierz_zalacznik"
const val IDZIENNIK_USER_AGENT = SYNERGIA_USER_AGENT
const val IDZIENNIK_WEB_URL = "https://iuczniowie.progman.pl/idziennik"
const val IDZIENNIK_WEB_LOGIN = "login.aspx"
const val IDZIENNIK_WEB_SETTINGS = "mod_panelRodzica/Ustawienia.aspx"
const val IDZIENNIK_WEB_HOME = "mod_panelRodzica/StronaGlowna.aspx"
const val IDZIENNIK_WEB_TIMETABLE = "mod_panelRodzica/plan/WS_Plan.asmx/pobierzPlanZajec"
const val IDZIENNIK_WEB_GRADES = "mod_panelRodzica/oceny/WS_ocenyUcznia.asmx/pobierzOcenyUcznia"
const val IDZIENNIK_WEB_MISSING_GRADES = "mod_panelRodzica/brak_ocen/WS_BrakOcenUcznia.asmx/pobierzBrakujaceOcenyUcznia"
const val IDZIENNIK_WEB_EXAMS = "mod_panelRodzica/sprawdziany/mod_sprawdzianyPanel.asmx/pobierzListe"
const val IDZIENNIK_WEB_HOMEWORK = "mod_panelRodzica/pracaDomowa/WS_pracaDomowa.asmx/pobierzPraceDomowe"
const val IDZIENNIK_WEB_NOTICES = "mod_panelRodzica/uwagi/WS_uwagiUcznia.asmx/pobierzUwagiUcznia"
const val IDZIENNIK_WEB_ATTENDANCE = "mod_panelRodzica/obecnosci/WS_obecnosciUcznia.asmx/pobierzObecnosciUcznia"
const val IDZIENNIK_WEB_ANNOUNCEMENTS = "mod_panelRodzica/tabOgl/WS_tablicaOgloszen.asmx/GetOgloszenia"
const val IDZIENNIK_WEB_MESSAGES_LIST = "mod_komunikator/WS_wiadomosci.asmx/PobierzListeWiadomosci"
const val IDZIENNIK_WEB_GET_MESSAGE = "mod_komunikator/WS_wiadomosci.asmx/PobierzWiadomosc"
const val IDZIENNIK_WEB_GET_RECIPIENT_LIST = "mod_komunikator/WS_wiadomosci.asmx/pobierzListeOdbiorcowPanelRodzic"
const val IDZIENNIK_WEB_SEND_MESSAGE = "mod_komunikator/WS_wiadomosci.asmx/WyslijWiadomosc"
const val IDZIENNIK_WEB_GET_ATTACHMENT = "mod_komunikator/Download.ashx"
const val IDZIENNIK_WEB_GET_HOMEWORK = "mod_panelRodzica/pracaDomowa/WS_pracaDomowa.asmx/pobierzJednaPraceDomowa"
const val IDZIENNIK_WEB_GET_HOMEWORK_ATTACHMENT = "mod_panelRodzica/pracaDomowa.aspx"
val IDZIENNIK_API_USER_AGENT = SYSTEM_USER_AGENT
const val IDZIENNIK_API_URL = "https://iuczniowie.progman.pl/idziennik/api"
const val IDZIENNIK_API_CURRENT_REGISTER = "Uczniowie/\$STUDENT_ID/AktualnyDziennik"
const val IDZIENNIK_API_GRADES = "Uczniowie/\$STUDENT_ID/Oceny/" /* + semester */
const val IDZIENNIK_API_MESSAGES_INBOX = "Wiadomosci/Odebrane"
const val IDZIENNIK_API_MESSAGES_SENT = "Wiadomosci/Wyslane"
val MOBIDZIENNIK_USER_AGENT = SYSTEM_USER_AGENT val MOBIDZIENNIK_USER_AGENT = SYSTEM_USER_AGENT
const val VULCAN_HEBE_USER_AGENT = "Dart/2.10 (dart:io)" const val VULCAN_HEBE_USER_AGENT = "Dart/2.10 (dart:io)"
const val VULCAN_HEBE_APP_NAME = "DzienniczekPlus 2.0" const val VULCAN_HEBE_APP_NAME = "DzienniczekPlus 2.0"
const val VULCAN_HEBE_APP_VERSION = "22.09.02 (G)" const val VULCAN_HEBE_APP_VERSION = "21.02.09 (G)"
private const val VULCAN_API_DEVICE_NAME_PREFIX = "Szkolny.eu " private const val VULCAN_API_DEVICE_NAME_PREFIX = "Szkolny.eu "
private const val VULCAN_API_DEVICE_NAME_SUFFIX = " - nie usuwać" private const val VULCAN_API_DEVICE_NAME_SUFFIX = " - nie usuwać"
val VULCAN_API_DEVICE_NAME by lazy { val VULCAN_API_DEVICE_NAME by lazy {
@ -82,30 +109,20 @@ const val VULCAN_HEBE_ENDPOINT_PUSH_ALL = "api/mobile/push/all"
const val VULCAN_HEBE_ENDPOINT_TIMETABLE = "api/mobile/schedule" const val VULCAN_HEBE_ENDPOINT_TIMETABLE = "api/mobile/schedule"
const val VULCAN_HEBE_ENDPOINT_TIMETABLE_CHANGES = "api/mobile/schedule/changes" const val VULCAN_HEBE_ENDPOINT_TIMETABLE_CHANGES = "api/mobile/schedule/changes"
const val VULCAN_HEBE_ENDPOINT_ADDRESSBOOK = "api/mobile/addressbook" const val VULCAN_HEBE_ENDPOINT_ADDRESSBOOK = "api/mobile/addressbook"
const val VULCAN_HEBE_ENDPOINT_TEACHERS = "api/mobile/teacher"
const val VULCAN_HEBE_ENDPOINT_EXAMS = "api/mobile/exam" const val VULCAN_HEBE_ENDPOINT_EXAMS = "api/mobile/exam"
const val VULCAN_HEBE_ENDPOINT_GRADES = "api/mobile/grade" const val VULCAN_HEBE_ENDPOINT_GRADES = "api/mobile/grade"
const val VULCAN_HEBE_ENDPOINT_GRADE_SUMMARY = "api/mobile/grade/summary" const val VULCAN_HEBE_ENDPOINT_GRADE_SUMMARY = "api/mobile/grade/summary"
const val VULCAN_HEBE_ENDPOINT_HOMEWORK = "api/mobile/homework" const val VULCAN_HEBE_ENDPOINT_HOMEWORK = "api/mobile/homework"
const val VULCAN_HEBE_ENDPOINT_NOTICES = "api/mobile/note" const val VULCAN_HEBE_ENDPOINT_NOTICES = "api/mobile/note"
const val VULCAN_HEBE_ENDPOINT_ATTENDANCE = "api/mobile/lesson" const val VULCAN_HEBE_ENDPOINT_ATTENDANCE = "api/mobile/lesson"
const val VULCAN_HEBE_ENDPOINT_MESSAGEBOX = "api/mobile/messagebox" const val VULCAN_HEBE_ENDPOINT_MESSAGES = "api/mobile/message"
const val VULCAN_HEBE_ENDPOINT_MESSAGEBOX_ADDRESSBOOK = "api/mobile/messagebox/addressbook" const val VULCAN_HEBE_ENDPOINT_MESSAGES_STATUS = "api/mobile/message/status"
const val VULCAN_HEBE_ENDPOINT_MESSAGEBOX_MESSAGES = "api/mobile/messagebox/message" const val VULCAN_HEBE_ENDPOINT_MESSAGES_SEND = "api/mobile/message"
const val VULCAN_HEBE_ENDPOINT_MESSAGEBOX_STATUS = "api/mobile/messagebox/message/status"
const val VULCAN_HEBE_ENDPOINT_MESSAGEBOX_SEND = "api/mobile/messagebox/message"
const val VULCAN_HEBE_ENDPOINT_LUCKY_NUMBER = "api/mobile/school/lucky" const val VULCAN_HEBE_ENDPOINT_LUCKY_NUMBER = "api/mobile/school/lucky"
const val EDUDZIENNIK_USER_AGENT = "Szkolny.eu/${BuildConfig.VERSION_NAME}"
const val PODLASIE_API_VERSION = "1.0.62" const val PODLASIE_API_VERSION = "1.0.62"
const val PODLASIE_API_URL = "https://cpdklaser.zeto.bialystok.pl/api" const val PODLASIE_API_URL = "https://cpdklaser.zeto.bialystok.pl/api"
const val PODLASIE_API_USER_ENDPOINT = "/pobierzDaneUcznia" const val PODLASIE_API_USER_ENDPOINT = "/pobierzDaneUcznia"
const val PODLASIE_API_LOGOUT_DEVICES_ENDPOINT = "/wyczyscUrzadzenia" const val PODLASIE_API_LOGOUT_DEVICES_ENDPOINT = "/wyczyscUrzadzenia"
const val USOS_API_OAUTH_REDIRECT_URL = "szkolny://redirect/usos"
val USOS_API_SCOPES by lazy { listOf(
"offline_access",
"studies",
"grades",
"events",
) }

View File

@ -11,9 +11,8 @@ import android.content.Context
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.PRIORITY_MIN import androidx.core.app.NotificationCompat.PRIORITY_MIN
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.Bundle
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.ext.Bundle
import pl.szczodrzynski.edziennik.ext.pendingIntentFlag
import pl.szczodrzynski.edziennik.receivers.SzkolnyReceiver import pl.szczodrzynski.edziennik.receivers.SzkolnyReceiver
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -41,14 +40,14 @@ class EdziennikNotification(val app: App) {
"task" to "TaskCancelRequest", "task" to "TaskCancelRequest",
"taskId" to taskId "taskId" to taskId
)) ))
return PendingIntent.getBroadcast(app, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or pendingIntentFlag()) as PendingIntent return PendingIntent.getBroadcast(app, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) as PendingIntent
} }
private val closePendingIntent: PendingIntent private val closePendingIntent: PendingIntent
get() { get() {
val intent = SzkolnyReceiver.getIntent(app, Bundle( val intent = SzkolnyReceiver.getIntent(app, Bundle(
"task" to "ServiceCloseRequest" "task" to "ServiceCloseRequest"
)) ))
return PendingIntent.getBroadcast(app, 0, intent, pendingIntentFlag()) as PendingIntent return PendingIntent.getBroadcast(app, 0, intent, 0) as PendingIntent
} }
private fun errorCountText(): String? { private fun errorCountText(): String? {

View File

@ -2,116 +2,115 @@ package pl.szczodrzynski.edziennik.data.api
import pl.szczodrzynski.edziennik.data.api.models.Data import pl.szczodrzynski.edziennik.data.api.models.Data
import pl.szczodrzynski.edziennik.data.api.models.Feature import pl.szczodrzynski.edziennik.data.api.models.Feature
import pl.szczodrzynski.edziennik.data.api.models.LoginMethod
import pl.szczodrzynski.edziennik.data.db.entity.EndpointTimer import pl.szczodrzynski.edziennik.data.db.entity.EndpointTimer
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_NEVER import pl.szczodrzynski.edziennik.data.db.entity.SYNC_NEVER
import pl.szczodrzynski.edziennik.data.db.enums.FeatureType
import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod
import pl.szczodrzynski.edziennik.ext.getFeatureTypesNecessary
import pl.szczodrzynski.edziennik.ext.getFeatureTypesUnnecessary
import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty
fun Data.prepare( fun Data.prepare(loginMethods: List<LoginMethod>, features: List<Feature>, featureIds: List<Int>, viewId: Int?, onlyEndpoints: List<Int>?) {
features: List<Feature>, val data = this
featureTypes: Set<FeatureType>?,
onlyEndpoints: Set<Int>?, val possibleLoginMethods = data.loginMethods.toMutableList()
) {
val loginType = this.loginStore.type for (loginMethod in loginMethods) {
val possibleLoginMethods = this.loginMethods.toMutableList() if (loginMethod.isPossible(profile, loginStore))
possibleLoginMethods += LoginMethod.values().filter { possibleLoginMethods += loginMethod.loginMethodId
it.loginType == loginType && it.isPossible?.invoke(profile, loginStore) != false
} }
//var highestLoginMethod = 0 //var highestLoginMethod = 0
var possibleFeatures = mutableListOf<Feature>() var endpointList = mutableListOf<Feature>()
val requiredLoginMethods = mutableListOf<LoginMethod>() val requiredLoginMethods = mutableListOf<Int>()
val syncFeatureTypes = when { data.targetEndpointIds.clear()
featureTypes.isNotNullNorEmpty() -> featureTypes!! data.targetLoginMethodIds.clear()
else -> getFeatureTypesUnnecessary()
} + getFeatureTypesNecessary()
val forceFeatureType = featureTypes?.singleOrNull()
this.targetEndpoints.clear()
this.targetLoginMethods.clear()
// get all endpoints for every feature, only if possible to login and possible/necessary to sync // get all endpoints for every feature, only if possible to login and possible/necessary to sync
for (featureId in syncFeatureTypes) { for (featureId in featureIds) {
possibleFeatures += features.filter { features.filter {
it.featureType == featureId // feature ID matches it.featureId == featureId // feature ID matches
&& possibleLoginMethods.containsAll(it.requiredLoginMethods) // is possible to login && possibleLoginMethods.containsAll(it.requiredLoginMethods) // is possible to login
&& it.shouldSync?.invoke(this) ?: true // is necessary/possible to sync && it.shouldSync?.invoke(data) ?: true // is necessary/possible to sync
}.let {
endpointList.addAll(it)
} }
} }
val timestamp = System.currentTimeMillis() val timestamp = System.currentTimeMillis()
possibleFeatures = possibleFeatures endpointList = endpointList
// sort the endpoint list by feature ID and priority // sort the endpoint list by feature ID and priority
.sortedWith(compareBy(Feature::featureType, Feature::priority)) .sortedWith(compareBy(Feature::featureId, Feature::priority))
// select only the most important endpoint for each feature // select only the most important endpoint for each feature
.distinctBy { it.featureType } .distinctBy { it.featureId }
.toMutableList() .toMutableList()
for (feature in possibleFeatures) {
// add all endpoint IDs and required login methods, filtering using timers // add all endpoint IDs and required login methods, filtering using timers
feature.endpoints.forEach { endpoint -> .onEach { feature ->
feature.endpointIds.forEach { endpoint ->
if (onlyEndpoints?.contains(endpoint.first) == false) if (onlyEndpoints?.contains(endpoint.first) == false)
return@forEach return@forEach
val timer = this.endpointTimers (data.endpointTimers
.singleOrNull { it.endpointId == endpoint.first } .singleOrNull { it.endpointId == endpoint.first } ?: EndpointTimer(data.profile?.id
?: EndpointTimer(this.profileId, endpoint.first) ?: -1, endpoint.first))
.let { timer ->
if ( if (
onlyEndpoints?.contains(endpoint.first) == true || onlyEndpoints?.contains(endpoint.first) == true ||
timer.nextSync == SYNC_ALWAYS || timer.nextSync == SYNC_ALWAYS ||
forceFeatureType != null && timer.featureType == forceFeatureType || viewId != null && timer.viewId == viewId ||
timer.nextSync != SYNC_NEVER && timer.nextSync < timestamp timer.nextSync != SYNC_NEVER && timer.nextSync < timestamp
) { ) {
this.targetEndpoints[endpoint.first] = timer.lastSync data.targetEndpointIds[endpoint.first] = timer.lastSync
requiredLoginMethods += endpoint.second requiredLoginMethods.add(endpoint.second)
}
} }
} }
} }
// check every login method for any dependencies // check every login method for any dependencies
for (loginMethod in requiredLoginMethods) { for (loginMethodId in requiredLoginMethods) {
var requiredLoginMethod: LoginMethod? = loginMethod var requiredLoginMethod: Int? = loginMethodId
while (requiredLoginMethod != null) { while (requiredLoginMethod != LOGIN_METHOD_NOT_NEEDED) {
this.targetLoginMethods += requiredLoginMethod loginMethods.singleOrNull { it.loginMethodId == requiredLoginMethod }?.let { loginMethod ->
requiredLoginMethod = requiredLoginMethod.requiredLoginMethod?.invoke(this.profile, this.loginStore) if (requiredLoginMethod != null)
data.targetLoginMethodIds.add(requiredLoginMethod!!)
requiredLoginMethod = loginMethod.requiredLoginMethod(data.profile, data.loginStore)
}
} }
} }
// sort and distinct every login method and endpoint // sort and distinct every login method and endpoint
this.targetLoginMethods = this.targetLoginMethods.toHashSet().toMutableList() data.targetLoginMethodIds = data.targetLoginMethodIds.toHashSet().toMutableList()
this.targetLoginMethods.sort() data.targetLoginMethodIds.sort()
//data.targetEndpointIds = data.targetEndpointIds.toHashSet().toMutableList() //data.targetEndpointIds = data.targetEndpointIds.toHashSet().toMutableList()
//data.targetEndpointIds.sort() //data.targetEndpointIds.sort()
progressCount = targetLoginMethods.size + targetEndpoints.size progressCount = targetLoginMethodIds.size + targetEndpointIds.size
progressStep = if (progressCount <= 0) 0f else 100f / progressCount.toFloat() progressStep = if (progressCount <= 0) 0f else 100f / progressCount.toFloat()
} }
fun Data.prepareFor(loginMethod: LoginMethod) { fun Data.prepareFor(loginMethods: List<LoginMethod>, loginMethodId: Int) {
val loginType = loginStore.type
val possibleLoginMethods = this.loginMethods.toMutableList() val possibleLoginMethods = this.loginMethods.toMutableList()
possibleLoginMethods += LoginMethod.values().filter {
it.loginType == loginType && it.isPossible?.invoke(profile, loginStore) != false loginMethods.forEach {
if (it.isPossible(profile, loginStore))
possibleLoginMethods += it.loginMethodId
} }
this.targetLoginMethods.clear() targetLoginMethodIds.clear()
// check the login method for any dependencies // check the login method for any dependencies
var requiredLoginMethod: LoginMethod? = loginMethod var requiredLoginMethod: Int? = loginMethodId
while (requiredLoginMethod != null) { while (requiredLoginMethod != LOGIN_METHOD_NOT_NEEDED) {
this.targetLoginMethods += requiredLoginMethod loginMethods.singleOrNull { it.loginMethodId == requiredLoginMethod }?.let {
requiredLoginMethod = requiredLoginMethod.requiredLoginMethod?.invoke(this.profile, this.loginStore) if (requiredLoginMethod != null)
targetLoginMethodIds.add(requiredLoginMethod!!)
requiredLoginMethod = it.requiredLoginMethod(profile, loginStore)
}
} }
// sort and distinct every login method // sort and distinct every login method
this.targetLoginMethods = this.targetLoginMethods.toHashSet().toMutableList() targetLoginMethodIds = targetLoginMethodIds.toHashSet().toMutableList()
this.targetLoginMethods.sort() targetLoginMethodIds.sort()
progressCount = 0 progressCount = 0
progressStep = 0f progressStep = 0f

View File

@ -58,7 +58,11 @@ const val ERROR_INVALID_LOGIN_MODE = 110
const val ERROR_LOGIN_METHOD_NOT_SATISFIED = 111 const val ERROR_LOGIN_METHOD_NOT_SATISFIED = 111
const val ERROR_NOT_IMPLEMENTED = 112 const val ERROR_NOT_IMPLEMENTED = 112
const val ERROR_FILE_DOWNLOAD = 113 const val ERROR_FILE_DOWNLOAD = 113
const val ERROR_REQUIRES_USER_ACTION = 114
const val ERROR_NO_STUDENTS_IN_ACCOUNT = 115
const val ERROR_CAPTCHA_NEEDED = 3000
const val ERROR_CAPTCHA_LIBRUS_PORTAL = 3001
const val ERROR_API_PDO_ERROR = 5000 const val ERROR_API_PDO_ERROR = 5000
const val ERROR_API_INVALID_CLIENT = 5001 const val ERROR_API_INVALID_CLIENT = 5001
@ -191,21 +195,21 @@ const val ERROR_VULCAN_HEBE_FIREBASE_ERROR = 362
const val ERROR_VULCAN_HEBE_CERTIFICATE_GONE = 363 const val ERROR_VULCAN_HEBE_CERTIFICATE_GONE = 363
const val ERROR_VULCAN_HEBE_SERVER_ERROR = 364 const val ERROR_VULCAN_HEBE_SERVER_ERROR = 364
const val ERROR_VULCAN_HEBE_ENTITY_NOT_FOUND = 365 const val ERROR_VULCAN_HEBE_ENTITY_NOT_FOUND = 365
const val ERROR_VULCAN_HEBE_MISSING_SENDER_ENTRY = 366
const val ERROR_VULCAN_API_DEPRECATED = 390 const val ERROR_VULCAN_API_DEPRECATED = 390
const val ERROR_LOGIN_EDUDZIENNIK_WEB_INVALID_LOGIN = 501
const val ERROR_LOGIN_EDUDZIENNIK_WEB_OTHER = 510
const val ERROR_LOGIN_EDUDZIENNIK_WEB_NO_SESSION_ID = 511
const val ERROR_EDUDZIENNIK_WEB_LIMITED_ACCESS = 521
const val ERROR_EDUDZIENNIK_WEB_SESSION_EXPIRED = 522
const val ERROR_EDUDZIENNIK_WEB_TEAM_MISSING = 530
const val ERROR_LOGIN_PODLASIE_API_INVALID_TOKEN = 601 const val ERROR_LOGIN_PODLASIE_API_INVALID_TOKEN = 601
const val ERROR_LOGIN_PODLASIE_API_DEVICE_LIMIT = 602 const val ERROR_LOGIN_PODLASIE_API_DEVICE_LIMIT = 602
const val ERROR_PODLASIE_API_NO_TOKEN = 630 const val ERROR_PODLASIE_API_NO_TOKEN = 630
const val ERROR_PODLASIE_API_OTHER = 631 const val ERROR_PODLASIE_API_OTHER = 631
const val ERROR_PODLASIE_API_DATA_MISSING = 632 const val ERROR_PODLASIE_API_DATA_MISSING = 632
const val ERROR_USOS_OAUTH_GOT_DIFFERENT_TOKEN = 702
const val ERROR_USOS_OAUTH_INCOMPLETE_RESPONSE = 703
const val ERROR_USOS_NO_STUDENT_PROGRAMMES = 704
const val ERROR_USOS_API_INCOMPLETE_RESPONSE = 705
const val ERROR_USOS_API_MISSING_RESPONSE = 706
const val ERROR_TEMPLATE_WEB_OTHER = 801 const val ERROR_TEMPLATE_WEB_OTHER = 801
const val EXCEPTION_API_TASK = 900 const val EXCEPTION_API_TASK = 900
@ -219,6 +223,8 @@ const val EXCEPTION_MOBIDZIENNIK_WEB_FILE_REQUEST = 908
const val EXCEPTION_LIBRUS_MESSAGES_FILE_REQUEST = 909 const val EXCEPTION_LIBRUS_MESSAGES_FILE_REQUEST = 909
const val EXCEPTION_NOTIFY = 910 const val EXCEPTION_NOTIFY = 910
const val EXCEPTION_LIBRUS_MESSAGES_REQUEST = 911 const val EXCEPTION_LIBRUS_MESSAGES_REQUEST = 911
const val EXCEPTION_EDUDZIENNIK_WEB_REQUEST = 920
const val EXCEPTION_EDUDZIENNIK_FILE_REQUEST = 921
const val ERROR_ONEDRIVE_DOWNLOAD = 930 const val ERROR_ONEDRIVE_DOWNLOAD = 930
const val EXCEPTION_VULCAN_WEB_LOGIN = 931 const val EXCEPTION_VULCAN_WEB_LOGIN = 931
const val EXCEPTION_VULCAN_WEB_REQUEST = 932 const val EXCEPTION_VULCAN_WEB_REQUEST = 932

View File

@ -0,0 +1,86 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-29.
*/
package pl.szczodrzynski.edziennik.data.api
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ANNOUNCEMENTS
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ATTENDANCE
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_BEHAVIOUR
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_GRADES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOME
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_SENT
internal const val FEATURE_TIMETABLE = 1
internal const val FEATURE_AGENDA = 2
internal const val FEATURE_GRADES = 3
internal const val FEATURE_HOMEWORK = 4
internal const val FEATURE_BEHAVIOUR = 5
internal const val FEATURE_ATTENDANCE = 6
internal const val FEATURE_MESSAGES_INBOX = 7
internal const val FEATURE_MESSAGES_SENT = 8
internal const val FEATURE_ANNOUNCEMENTS = 9
internal const val FEATURE_ALWAYS_NEEDED = 100
internal const val FEATURE_STUDENT_INFO = 101
internal const val FEATURE_STUDENT_NUMBER = 109
internal const val FEATURE_SCHOOL_INFO = 102
internal const val FEATURE_CLASS_INFO = 103
internal const val FEATURE_TEAM_INFO = 104
internal const val FEATURE_LUCKY_NUMBER = 105
internal const val FEATURE_TEACHERS = 106
internal const val FEATURE_SUBJECTS = 107
internal const val FEATURE_CLASSROOMS = 108
internal const val FEATURE_PUSH_CONFIG = 120
object Features {
private fun getAllNecessary(): List<Int> = listOf(
FEATURE_ALWAYS_NEEDED,
FEATURE_PUSH_CONFIG,
FEATURE_STUDENT_INFO,
FEATURE_STUDENT_NUMBER,
FEATURE_SCHOOL_INFO,
FEATURE_CLASS_INFO,
FEATURE_TEAM_INFO,
FEATURE_LUCKY_NUMBER,
FEATURE_TEACHERS,
FEATURE_SUBJECTS,
FEATURE_CLASSROOMS)
private fun getAllFeatures(): List<Int> = listOf(
FEATURE_TIMETABLE,
FEATURE_AGENDA,
FEATURE_GRADES,
FEATURE_HOMEWORK,
FEATURE_BEHAVIOUR,
FEATURE_ATTENDANCE,
FEATURE_MESSAGES_INBOX,
FEATURE_MESSAGES_SENT,
FEATURE_ANNOUNCEMENTS)
fun getAllIds(): List<Int> = getAllFeatures() + getAllNecessary()
fun getIdsByView(targetId: Int, targetType: Int): List<Int> {
return (when (targetId) {
DRAWER_ITEM_HOME -> getAllFeatures()
DRAWER_ITEM_TIMETABLE -> listOf(FEATURE_TIMETABLE)
DRAWER_ITEM_AGENDA -> listOf(FEATURE_AGENDA)
DRAWER_ITEM_GRADES -> listOf(FEATURE_GRADES)
DRAWER_ITEM_MESSAGES -> when (targetType) {
TYPE_RECEIVED -> listOf(FEATURE_MESSAGES_INBOX)
TYPE_SENT -> listOf(FEATURE_MESSAGES_SENT)
else -> listOf(FEATURE_MESSAGES_INBOX, FEATURE_MESSAGES_SENT)
}
DRAWER_ITEM_HOMEWORK -> listOf(FEATURE_HOMEWORK)
DRAWER_ITEM_BEHAVIOUR -> listOf(FEATURE_BEHAVIOUR)
DRAWER_ITEM_ATTENDANCE -> listOf(FEATURE_ATTENDANCE)
DRAWER_ITEM_ANNOUNCEMENTS -> listOf(FEATURE_ANNOUNCEMENTS)
else -> getAllFeatures()
} + getAllNecessary()).sorted()
}
}

View File

@ -0,0 +1,147 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-20.
*/
package pl.szczodrzynski.edziennik.data.api
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.login.EdudziennikLoginWeb
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginApi
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginMessages
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginPortal
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginSynergia
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.login.MobidziennikLoginApi2
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.login.MobidziennikLoginWeb
import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.login.PodlasieLoginApi
import pl.szczodrzynski.edziennik.data.api.edziennik.template.login.TemplateLoginApi
import pl.szczodrzynski.edziennik.data.api.edziennik.template.login.TemplateLoginWeb
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.VulcanLoginHebe
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.VulcanLoginWebMain
import pl.szczodrzynski.edziennik.data.api.models.LoginMethod
// librus
// mobidziennik
// idziennik [*]
// vulcan
// mobireg
const val SYNERGIA_API_ENABLED = false
const val LOGIN_TYPE_IDZIENNIK = 3
const val LOGIN_TYPE_TEMPLATE = 21
// LOGIN MODES
const val LOGIN_MODE_TEMPLATE_WEB = 0
// LOGIN METHODS
const val LOGIN_METHOD_NOT_NEEDED = -1
const val LOGIN_METHOD_TEMPLATE_WEB = 100
const val LOGIN_METHOD_TEMPLATE_API = 200
const val LOGIN_TYPE_LIBRUS = 2
const val LOGIN_MODE_LIBRUS_EMAIL = 0
const val LOGIN_MODE_LIBRUS_SYNERGIA = 1
const val LOGIN_MODE_LIBRUS_JST = 2
const val LOGIN_METHOD_LIBRUS_PORTAL = 100
const val LOGIN_METHOD_LIBRUS_API = 200
const val LOGIN_METHOD_LIBRUS_SYNERGIA = 300
const val LOGIN_METHOD_LIBRUS_MESSAGES = 400
val librusLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_PORTAL, LibrusLoginPortal::class.java)
.withIsPossible { _, loginStore ->
loginStore.mode == LOGIN_MODE_LIBRUS_EMAIL
}
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED },
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_API, LibrusLoginApi::class.java)
.withIsPossible { _, loginStore ->
loginStore.mode != LOGIN_MODE_LIBRUS_SYNERGIA || SYNERGIA_API_ENABLED
}
.withRequiredLoginMethod { _, loginStore ->
if (loginStore.mode == LOGIN_MODE_LIBRUS_EMAIL) LOGIN_METHOD_LIBRUS_PORTAL else LOGIN_METHOD_NOT_NEEDED
},
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_SYNERGIA, LibrusLoginSynergia::class.java)
.withIsPossible { _, loginStore -> !loginStore.hasLoginData("fakeLogin") }
.withRequiredLoginMethod { profile, _ ->
if (profile?.hasStudentData("accountPassword") == false || true) LOGIN_METHOD_LIBRUS_API else LOGIN_METHOD_NOT_NEEDED
},
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_MESSAGES, LibrusLoginMessages::class.java)
.withIsPossible { _, loginStore -> !loginStore.hasLoginData("fakeLogin") }
.withRequiredLoginMethod { profile, _ ->
if (profile?.hasStudentData("accountPassword") == false || true) LOGIN_METHOD_LIBRUS_SYNERGIA else LOGIN_METHOD_NOT_NEEDED
}
)
const val LOGIN_TYPE_MOBIDZIENNIK = 1
const val LOGIN_MODE_MOBIDZIENNIK_WEB = 0
const val LOGIN_METHOD_MOBIDZIENNIK_WEB = 100
const val LOGIN_METHOD_MOBIDZIENNIK_API2 = 300
val mobidziennikLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_MOBIDZIENNIK, LOGIN_METHOD_MOBIDZIENNIK_WEB, MobidziennikLoginWeb::class.java)
.withIsPossible { _, _ -> true }
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED },
LoginMethod(LOGIN_TYPE_MOBIDZIENNIK, LOGIN_METHOD_MOBIDZIENNIK_API2, MobidziennikLoginApi2::class.java)
.withIsPossible { profile, _ -> profile?.getStudentData("email", null) != null }
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }
)
const val LOGIN_TYPE_VULCAN = 4
const val LOGIN_MODE_VULCAN_API = 0
const val LOGIN_MODE_VULCAN_WEB = 1
const val LOGIN_MODE_VULCAN_HEBE = 2
const val LOGIN_METHOD_VULCAN_WEB_MAIN = 100
const val LOGIN_METHOD_VULCAN_WEB_NEW = 200
const val LOGIN_METHOD_VULCAN_WEB_OLD = 300
const val LOGIN_METHOD_VULCAN_WEB_MESSAGES = 400
const val LOGIN_METHOD_VULCAN_HEBE = 600
val vulcanLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_VULCAN, LOGIN_METHOD_VULCAN_WEB_MAIN, VulcanLoginWebMain::class.java)
.withIsPossible { _, loginStore -> loginStore.hasLoginData("webHost") }
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED },
/*LoginMethod(LOGIN_TYPE_VULCAN, LOGIN_METHOD_VULCAN_WEB_NEW, VulcanLoginWebNew::class.java)
.withIsPossible { _, _ -> false }
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_VULCAN_WEB_MAIN },
LoginMethod(LOGIN_TYPE_VULCAN, LOGIN_METHOD_VULCAN_WEB_OLD, VulcanLoginWebOld::class.java)
.withIsPossible { _, _ -> false }
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_VULCAN_WEB_MAIN },*/
LoginMethod(LOGIN_TYPE_VULCAN, LOGIN_METHOD_VULCAN_HEBE, VulcanLoginHebe::class.java)
.withIsPossible { _, loginStore ->
loginStore.mode != LOGIN_MODE_VULCAN_API
}
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }
)
const val LOGIN_TYPE_EDUDZIENNIK = 5
const val LOGIN_MODE_EDUDZIENNIK_WEB = 0
const val LOGIN_METHOD_EDUDZIENNIK_WEB = 100
val edudziennikLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_EDUDZIENNIK, LOGIN_METHOD_EDUDZIENNIK_WEB, EdudziennikLoginWeb::class.java)
.withIsPossible { _, _ -> true }
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }
)
const val LOGIN_TYPE_PODLASIE = 6
const val LOGIN_MODE_PODLASIE_API = 0
const val LOGIN_METHOD_PODLASIE_API = 100
val podlasieLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_PODLASIE, LOGIN_METHOD_PODLASIE_API, PodlasieLoginApi::class.java)
.withIsPossible { _, _ -> true }
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }
)
val templateLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_TEMPLATE, LOGIN_METHOD_TEMPLATE_WEB, TemplateLoginWeb::class.java)
.withIsPossible { _, _ -> true }
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED },
LoginMethod(LOGIN_TYPE_TEMPLATE, LOGIN_METHOD_TEMPLATE_API, TemplateLoginApi::class.java)
.withIsPossible { _, _ -> true }
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_TEMPLATE_WEB }
)

View File

@ -16,33 +16,6 @@ object Regexes {
"""[^0-9]""".toRegex() """[^0-9]""".toRegex()
} }
val HTML_BR by lazy {
"""<br\s?/?>""".toRegex()
}
val MESSAGE_META by lazy {
"""^\[META:([A-z0-9-&=]+)]""".toRegex()
}
val HTML_INPUT_HIDDEN by lazy {
"""<input .*?type="hidden".+?>""".toRegex()
}
val HTML_INPUT_NAME by lazy {
"""name="(.+?)"""".toRegex()
}
val HTML_INPUT_VALUE by lazy {
"""value="(.+?)"""".toRegex()
}
val HTML_CSRF_TOKEN by lazy {
"""name="csrf-token" content="([A-z0-9=+/\-_]+?)"""".toRegex()
}
val HTML_FORM_ACTION by lazy {
"""<form .*?action="(.+?)"""".toRegex()
}
val HTML_RECAPTCHA_KEY by lazy {
"""data-sitekey="(.+?)"""".toRegex()
}
val MOBIDZIENNIK_GRADES_SUBJECT_NAME by lazy { val MOBIDZIENNIK_GRADES_SUBJECT_NAME by lazy {
@ -77,16 +50,15 @@ object Regexes {
"""events: (.+),$""".toRegex(RegexOption.MULTILINE) """events: (.+),$""".toRegex(RegexOption.MULTILINE)
} }
val MOBIDZIENNIK_WEB_ATTACHMENT by lazy {
"""href="https://.+?\.mobidziennik.pl/.+?&(?:amp;)?zalacznik(_rozwiazania)?=([0-9]+)".+?>(.+?)(?: <small.+?\(([0-9.]+)\s(M|K|G|)B\)</small>)?</a>""".toRegex()
}
val MOBIDZIENNIK_MESSAGE_READ_DATE by lazy { val MOBIDZIENNIK_MESSAGE_READ_DATE by lazy {
"""czas przeczytania:.+?,\s([0-9]+)\s(.+?)\s([0-9]{4}),\sgodzina\s([0-9:]+)""".toRegex(DOT_MATCHES_ALL) """czas przeczytania:.+?,\s([0-9]+)\s(.+?)\s([0-9]{4}),\sgodzina\s([0-9:]+)""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_MESSAGE_SENT_READ_DATE by lazy { val MOBIDZIENNIK_MESSAGE_SENT_READ_DATE by lazy {
""".+?,\s([0-9]+)\s(.+?)\s([0-9]{4}),\sgodzina\s([0-9:]+)""".toRegex(DOT_MATCHES_ALL) """.+?,\s([0-9]+)\s(.+?)\s([0-9]{4}),\sgodzina\s([0-9:]+)""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_MESSAGE_ATTACHMENT by lazy {
"""href="https://.+?\.mobidziennik.pl/.+?&(?:amp;)?zalacznik=([0-9]+)"(?:.+?<small.+?\(([0-9.]+)\s(M|K|G|)B\))*""".toRegex(DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_MESSAGE_SENT_READ_BY by lazy { val MOBIDZIENNIK_MESSAGE_SENT_READ_BY by lazy {
"""([0-9]+)/([0-9]+)""".toRegex() """([0-9]+)/([0-9]+)""".toRegex()
} }
@ -128,43 +100,61 @@ object Regexes {
"""<strong>(.+?)</strong>\s*<small>\s*\((.+?),\s*(.+?)\)""".toRegex(DOT_MATCHES_ALL) """<strong>(.+?)</strong>\s*<small>\s*\((.+?),\s*(.+?)\)""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_MOBILE_HOMEWORK_ROW by lazy { val MOBIDZIENNIK_HOMEWORK_ROW by lazy {
"""class="rowRolling">(.+?</div>\s*</td>)""".toRegex(DOT_MATCHES_ALL) """class="rowRolling">(.+?</div>\s*</td>)""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_MOBILE_HOMEWORK_ITEM by lazy { val MOBIDZIENNIK_HOMEWORK_ITEM by lazy {
"""<p><b>(.+?):</b>\s*(.+?)\s*</p>""".toRegex(DOT_MATCHES_ALL) """<p><b>(.+?):</b>\s*(.+?)\s*</p>""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_MOBILE_HOMEWORK_BODY by lazy { val MOBIDZIENNIK_HOMEWORK_BODY by lazy {
"""Treść:</b>(.+?)<p><b>""".toRegex(DOT_MATCHES_ALL) """Treść:</b>(.+?)<p><b>""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_MOBILE_HOMEWORK_ID by lazy { val MOBIDZIENNIK_HOMEWORK_ID by lazy {
"""name="id_zadania" value="([0-9]+)"""".toRegex(DOT_MATCHES_ALL) """zadanieFormularz\(([0-9]+),""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_MOBILE_HOMEWORK_ATTACHMENT by lazy { val MOBIDZIENNIK_HOMEWORK_ATTACHMENT by lazy {
"""zalacznik(_zadania)?=([0-9]+)'.+?word-break">(.+?)</td>""".toRegex(DOT_MATCHES_ALL) """zalacznik(_zadania)?=([0-9]+)'.+?word-break">(.+?)</td>""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_WEB_HOMEWORK_ADDED_DATE by lazy {
"""Wpisał\(a\):</td>\s+<th>\s+(.+?), (.+?), ([0-9]{1,2}) (.+?) ([0-9]{4}), godzina ([0-9:]+)""".toRegex()
val IDZIENNIK_LOGIN_HIDDEN_FIELDS by lazy {
"""<input type="hidden".+?name="([A-z0-9_]+)?".+?value="([A-z0-9_+-/=]+)?".+?>""".toRegex(DOT_MATCHES_ALL)
}
val IDZIENNIK_LOGIN_ERROR by lazy {
"""id="spanErrorMessage">(.*?)</""".toRegex(DOT_MATCHES_ALL)
}
val IDZIENNIK_LOGIN_FIRST_ACCOUNT_NAME by lazy {
"""Imię i nazwisko:.+?">(.+?)</div>""".toRegex(DOT_MATCHES_ALL)
}
val IDZIENNIK_LOGIN_FIRST_IS_PARENT by lazy {
"""id="ctl00_CzyRodzic" value="([01])" />""".toRegex()
}
val IDZIENNIK_LOGIN_FIRST_SCHOOL_YEAR by lazy {
"""name="ctl00\${"$"}dxComboRokSzkolny".+?selected="selected".*?value="([0-9]+)">([0-9]+)/([0-9]+)<""".toRegex(DOT_MATCHES_ALL)
}
val IDZIENNIK_LOGIN_FIRST_STUDENT_SELECT by lazy {
"""<select.*?name="ctl00\${"$"}dxComboUczniowie".*?</select>""".toRegex(DOT_MATCHES_ALL)
}
val IDZIENNIK_LOGIN_FIRST_STUDENT by lazy {
"""<option.*?value="([0-9]+)"\sdata-id-ucznia="([A-z0-9]+?)".*?>(.+?)\s(.+?)\s*\((.+?),\s*(.+?)\)</option>""".toRegex(DOT_MATCHES_ALL)
}
val IDZIENNIK_MESSAGES_RECIPIENT_PARENT by lazy {
"""(.+?)\s\((.+)\)""".toRegex()
}
/*<span id="ctl00_spanSzczesliwyLos">Szczęśliwy los na dzisiaj to <b>19</b>. Los na jutro to <b>22</b></span>*/
val IDZIENNIK_WEB_LUCKY_NUMBER by lazy {
"""dzisiaj to <b>([0-9]+)</b>""".toRegex()
}
val IDZIENNIK_WEB_SELECTED_REGISTER by lazy {
"""selected="selected" value="([0-9]+)" data-id-ucznia""".toRegex()
} }
val MOBIDZIENNIK_TIMETABLE_TOP by lazy {
"""<div class="plansc_top">.+?</div></div>""".toRegex(DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_TIMETABLE_CELL by lazy {
"""<div class="plansc_cnt_w" style="(.+?)">.+?style="(.+?)".+?title="(.+?)".+?>\s+(.+?)\s+</div>""".toRegex(DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_TIMETABLE_LEFT by lazy {
"""<div class="plansc_godz">.+?</div></div>""".toRegex(DOT_MATCHES_ALL)
}
val VULCAN_SHIFT_ANNOTATION by lazy {
val MOBIDZIENNIK_EVENT_CONTENT by lazy { """\(przeniesiona (z|na) lekcj[ię] ([0-9]+), (.+)\)""".toRegex()
"""<h1>(.+?) <small>\(wpisał\(a\) (.+?) w dniu ([0-9-]{10})\).+?<strong>(.+?)</strong><br""".toRegex(DOT_MATCHES_ALL)
} }
val VULCAN_WEB_PERMISSIONS by lazy { val VULCAN_WEB_PERMISSIONS by lazy {
"""permissions: '([A-z0-9/=+\-_|]+?)'""".toRegex() """permissions: '([A-z0-9/=+\-_|]+?)'""".toRegex()
} }
@ -182,6 +172,82 @@ object Regexes {
} }
val EDUDZIENNIK_STUDENTS_START by lazy {
"""<li><a href="/Students/([\w-_]+?)/start/">(.*?)</a>""".toRegex()
}
val EDUDZIENNIK_ACCOUNT_NAME_START by lazy {
"""<span id='user_dn'>(.*?)</span>""".toRegex()
}
val EDUDZIENNIK_SUBJECTS_START by lazy {
"""<a class="menu-course" href="/Students/[\w-_]+?/Courses/([\w-_]+)/">(.+?)</a>""".toRegex()
}
val EDUDZIENNIK_ATTENDANCE_ENTRIES by lazy {
"""<td id="([\d-]+?):(\d+?)".*?>(.+?)</td>""".toRegex()
}
val EDUDZIENNIK_ATTENDANCE_TYPES by lazy {
"""<div class="info">.*?<p>(.*?)</p>""".toRegex(DOT_MATCHES_ALL)
}
val EDUDZIENNIK_ATTENDANCE_TYPE by lazy {
"""\((.+?)\) (.+)""".toRegex()
}
val EDUDZIENNIK_ANNOUNCEMENT_DESCRIPTION by lazy {
"""<div class="desc">.*?<p>(.*?)</p>""".toRegex(DOT_MATCHES_ALL)
}
val EDUDZIENNIK_HOMEWORK_DESCRIPTION by lazy {
"""<div class="desc">(.*?)</div>""".toRegex(DOT_MATCHES_ALL)
}
val EDUDZIENNIK_SUBJECT_ID by lazy {
"""/Courses/([\w-_]+?)/""".toRegex()
}
val EDUDZIENNIK_GRADE_ID by lazy {
"""/Grades/([\w-_]+?)/""".toRegex()
}
val EDUDZIENNIK_EXAM_ID by lazy {
"""/Evaluations/([\w-_]+?)/""".toRegex()
}
val EDUDZIENNIK_EVENT_TYPE_ID by lazy {
"""/GradeLabels/([\w-_]+?)/""".toRegex()
}
val EDUDZIENNIK_ANNOUNCEMENT_ID by lazy {
"""/Announcement/([\w-_]+?)/""".toRegex()
}
val EDUDZIENNIK_HOMEWORK_ID by lazy {
"""/Homework/([\w-_]+?)/""".toRegex()
}
val EDUDZIENNIK_TEACHER_ID by lazy {
"""/Teachers/([\w-_]+?)/""".toRegex()
}
val EDUDZIENNIK_EVENT_ID by lazy {
"""/KlassEvent/([\w-_]+?)/""".toRegex()
}
val EDUDZIENNIK_NOTE_ID by lazy {
"""/RegistryNotes/([\w-_]+?)/""".toRegex()
}
val EDUDZIENNIK_SCHOOL_DETAIL_ID by lazy {
"""<a id="School_detail".*?/School/([\w-_]+?)/""".toRegex(DOT_MATCHES_ALL)
}
val EDUDZIENNIK_SCHOOL_DETAIL_NAME by lazy {
"""</li>.*?<p>(.*?)</p>.*?<li>""".toRegex(DOT_MATCHES_ALL)
}
val EDUDZIENNIK_CLASS_DETAIL_ID by lazy {
"""<a id="Klass_detail".*?/Klass/([\w-_]+?)/""".toRegex(DOT_MATCHES_ALL)
}
val EDUDZIENNIK_CLASS_DETAIL_NAME by lazy {
"""<a id="Klass_detail".*?>(.*?)</a>""".toRegex(DOT_MATCHES_ALL)
}
val EDUDZIENNIK_TEACHERS by lazy {
"""<div class="teacher">.*?<p>(.+?) (.+?)</p>""".toRegex(DOT_MATCHES_ALL)
}
val LINKIFY_DATE_YMD by lazy { val LINKIFY_DATE_YMD by lazy {
"""(1\d{3}|20\d{2})[\-./](1[0-2]|0?\d)[\-./]([1-2]\d|3[0-1]|0?\d)""".toRegex() """(1\d{3}|20\d{2})[\-./](1[0-2]|0?\d)[\-./]([1-2]\d|3[0-1]|0?\d)""".toRegex()
} }

View File

@ -6,32 +6,27 @@ package pl.szczodrzynski.edziennik.data.api.edziennik
import com.google.gson.JsonObject import com.google.gson.JsonObject
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.ERROR_PROFILE_ARCHIVED import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.Edudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.Librus import pl.szczodrzynski.edziennik.data.api.edziennik.librus.Librus
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.Mobidziennik import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.Mobidziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.Podlasie import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.Podlasie
import pl.szczodrzynski.edziennik.data.api.edziennik.template.Template import pl.szczodrzynski.edziennik.data.api.edziennik.template.Template
import pl.szczodrzynski.edziennik.data.api.edziennik.usos.Usos
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.Vulcan import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.Vulcan
import pl.szczodrzynski.edziennik.data.api.events.RegisterAvailabilityEvent import pl.szczodrzynski.edziennik.data.api.events.RegisterAvailabilityEvent
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
import pl.szczodrzynski.edziennik.data.api.task.IApiTask import pl.szczodrzynski.edziennik.data.api.task.IApiTask
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.enums.FeatureType
import pl.szczodrzynski.edziennik.data.db.enums.LoginType
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.EventFull import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.ext.isBeforeYear
import pl.szczodrzynski.edziennik.ext.shouldArchive
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.managers.AvailabilityManager.Error.Type
open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTask(profileId) { open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTask(profileId) {
companion object { companion object {
@ -42,10 +37,10 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
fun firstLogin(loginStore: LoginStore) = EdziennikTask(-1, FirstLoginRequest(loginStore)) fun firstLogin(loginStore: LoginStore) = EdziennikTask(-1, FirstLoginRequest(loginStore))
fun sync() = EdziennikTask(-1, SyncRequest()) fun sync() = EdziennikTask(-1, SyncRequest())
fun syncProfile(profileId: Int, featureTypes: Set<FeatureType>? = null, onlyEndpoints: Set<Int>? = null, arguments: JsonObject? = null) = EdziennikTask(profileId, SyncProfileRequest(featureTypes, onlyEndpoints, arguments)) fun syncProfile(profileId: Int, viewIds: List<Pair<Int, Int>>? = null, onlyEndpoints: List<Int>? = null, arguments: JsonObject? = null) = EdziennikTask(profileId, SyncProfileRequest(viewIds, onlyEndpoints, arguments))
fun syncProfileList(profileList: Set<Int>) = EdziennikTask(-1, SyncProfileListRequest(profileList)) fun syncProfileList(profileList: List<Int>) = EdziennikTask(-1, SyncProfileListRequest(profileList))
fun messageGet(profileId: Int, message: MessageFull) = EdziennikTask(profileId, MessageGetRequest(message)) fun messageGet(profileId: Int, message: MessageFull) = EdziennikTask(profileId, MessageGetRequest(message))
fun messageSend(profileId: Int, recipients: Set<Teacher>, subject: String, text: String) = EdziennikTask(profileId, MessageSendRequest(recipients, subject, text)) fun messageSend(profileId: Int, recipients: List<Teacher>, subject: String, text: String) = EdziennikTask(profileId, MessageSendRequest(recipients, subject, text))
fun announcementsRead(profileId: Int) = EdziennikTask(profileId, AnnouncementsReadRequest()) fun announcementsRead(profileId: Int) = EdziennikTask(profileId, AnnouncementsReadRequest())
fun announcementGet(profileId: Int, announcement: AnnouncementFull) = EdziennikTask(profileId, AnnouncementGetRequest(announcement)) fun announcementGet(profileId: Int, announcement: AnnouncementFull) = EdziennikTask(profileId, AnnouncementGetRequest(announcement))
fun attachmentGet(profileId: Int, owner: Any, attachmentId: Long, attachmentName: String) = EdziennikTask(profileId, AttachmentGetRequest(owner, attachmentId, attachmentName)) fun attachmentGet(profileId: Int, owner: Any, attachmentId: Long, attachmentName: String) = EdziennikTask(profileId, AttachmentGetRequest(owner, attachmentId, attachmentName))
@ -95,31 +90,45 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
return return
} }
val error = app.availabilityManager.check(profile) profile.registerName?.also { registerName ->
when (error?.type) { var status = app.config.sync.registerAvailability[registerName]
Type.NOT_AVAILABLE -> { if (status == null || status.nextCheckAt < currentTimeUnix()) {
val api = SzkolnyApi(app)
api.runCatching({
val availability = getRegisterAvailability()
app.config.sync.registerAvailability = availability
status = availability[registerName]
}, onError = {
val apiError = it.toApiError(TAG)
if (apiError.errorCode == ERROR_API_INVALID_SIGNATURE) {
return@also
}
taskCallback.onError(apiError)
return
})
}
if (status?.available != true
|| status?.minVersionCode ?: BuildConfig.VERSION_CODE > BuildConfig.VERSION_CODE) {
if (EventBus.getDefault().hasSubscriberForEvent(RegisterAvailabilityEvent::class.java)) { if (EventBus.getDefault().hasSubscriberForEvent(RegisterAvailabilityEvent::class.java)) {
EventBus.getDefault().postSticky(RegisterAvailabilityEvent()) EventBus.getDefault().postSticky(
RegisterAvailabilityEvent(app.config.sync.registerAvailability)
)
} }
cancel() cancel()
taskCallback.onCompleted() taskCallback.onCompleted()
return return
} }
Type.API_ERROR -> {
taskCallback.onError(error.apiError!!)
return
}
else -> return@let
} }
} }
edziennikInterface = when (loginStore.type) { edziennikInterface = when (loginStore.type) {
LoginType.LIBRUS -> Librus(app, profile, loginStore, taskCallback) LOGIN_TYPE_LIBRUS -> Librus(app, profile, loginStore, taskCallback)
LoginType.MOBIDZIENNIK -> Mobidziennik(app, profile, loginStore, taskCallback) LOGIN_TYPE_MOBIDZIENNIK -> Mobidziennik(app, profile, loginStore, taskCallback)
LoginType.VULCAN -> Vulcan(app, profile, loginStore, taskCallback) LOGIN_TYPE_VULCAN -> Vulcan(app, profile, loginStore, taskCallback)
LoginType.PODLASIE -> Podlasie(app, profile, loginStore, taskCallback) LOGIN_TYPE_EDUDZIENNIK -> Edudziennik(app, profile, loginStore, taskCallback)
LoginType.TEMPLATE -> Template(app, profile, loginStore, taskCallback) LOGIN_TYPE_PODLASIE -> Podlasie(app, profile, loginStore, taskCallback)
LoginType.USOS -> Usos(app, profile, loginStore, taskCallback) LOGIN_TYPE_TEMPLATE -> Template(app, profile, loginStore, taskCallback)
else -> null else -> null
} }
if (edziennikInterface == null) { if (edziennikInterface == null) {
@ -128,7 +137,9 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
when (request) { when (request) {
is SyncProfileRequest -> edziennikInterface?.sync( is SyncProfileRequest -> edziennikInterface?.sync(
featureTypes = request.featureTypes, featureIds = request.viewIds?.flatMap { Features.getIdsByView(it.first, it.second) }
?: Features.getAllIds(),
viewId = request.viewIds?.get(0)?.first,
onlyEndpoints = request.onlyEndpoints, onlyEndpoints = request.onlyEndpoints,
arguments = request.arguments) arguments = request.arguments)
is MessageGetRequest -> edziennikInterface?.getMessage(request.message) is MessageGetRequest -> edziennikInterface?.getMessage(request.message)
@ -153,10 +164,10 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
data class FirstLoginRequest(val loginStore: LoginStore) data class FirstLoginRequest(val loginStore: LoginStore)
class SyncRequest class SyncRequest
data class SyncProfileRequest(val featureTypes: Set<FeatureType>? = null, val onlyEndpoints: Set<Int>? = null, val arguments: JsonObject? = null) data class SyncProfileRequest(val viewIds: List<Pair<Int, Int>>? = null, val onlyEndpoints: List<Int>? = null, val arguments: JsonObject? = null)
data class SyncProfileListRequest(val profileList: Set<Int>) data class SyncProfileListRequest(val profileList: List<Int>)
data class MessageGetRequest(val message: MessageFull) data class MessageGetRequest(val message: MessageFull)
data class MessageSendRequest(val recipients: Set<Teacher>, val subject: String, val text: String) data class MessageSendRequest(val recipients: List<Teacher>, val subject: String, val text: String)
class AnnouncementsReadRequest class AnnouncementsReadRequest
data class AnnouncementGetRequest(val announcement: AnnouncementFull) data class AnnouncementGetRequest(val announcement: AnnouncementFull)
data class AttachmentGetRequest(val owner: Any, val attachmentId: Long, val attachmentName: String) data class AttachmentGetRequest(val owner: Any, val attachmentId: Long, val attachmentName: String)

View File

@ -6,9 +6,9 @@ package pl.szczodrzynski.edziennik.data.api.edziennik
import android.content.Intent import android.content.Intent
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.Intent
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.enums.LoginType
import pl.szczodrzynski.edziennik.ext.Intent
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
@ -51,32 +51,35 @@ class ProfileArchiver(val app: App, val profile: Profile) {
d(TAG, "New profile ID for ${profile.name}: ${profile.id}") d(TAG, "New profile ID for ${profile.name}: ${profile.id}")
when (profile.loginStoreType) { when (profile.loginStoreType) {
LoginType.LIBRUS -> { LOGIN_TYPE_LIBRUS -> {
profile.studentData.remove("isPremium") profile.removeStudentData("isPremium")
profile.studentData.remove("pushDeviceId") profile.removeStudentData("pushDeviceId")
profile.studentData.remove("startPointsSemester1") profile.removeStudentData("startPointsSemester1")
profile.studentData.remove("startPointsSemester2") profile.removeStudentData("startPointsSemester2")
profile.studentData.remove("enablePointGrades") profile.removeStudentData("enablePointGrades")
profile.studentData.remove("enableDescriptiveGrades") profile.removeStudentData("enableDescriptiveGrades")
} }
LoginType.MOBIDZIENNIK -> {} LOGIN_TYPE_MOBIDZIENNIK -> {
LoginType.VULCAN -> {
}
LOGIN_TYPE_VULCAN -> {
// DataVulcan.isApiLoginValid() returns false so it will update the semester // DataVulcan.isApiLoginValid() returns false so it will update the semester
profile.studentData.remove("currentSemesterEndDate") profile.removeStudentData("currentSemesterEndDate")
profile.studentData.remove("studentSemesterId") profile.removeStudentData("studentSemesterId")
profile.studentData.remove("studentSemesterNumber") profile.removeStudentData("studentSemesterNumber")
profile.studentData.remove("semester1Id") profile.removeStudentData("semester1Id")
profile.studentData.remove("semester2Id") profile.removeStudentData("semester2Id")
profile.studentData.remove("studentClassId") profile.removeStudentData("studentClassId")
} }
LoginType.IDZIENNIK -> { LOGIN_TYPE_IDZIENNIK -> {
profile.studentData.remove("schoolYearId") profile.removeStudentData("schoolYearId")
}
LOGIN_TYPE_EDUDZIENNIK -> {
}
LOGIN_TYPE_PODLASIE -> {
} }
LoginType.EDUDZIENNIK -> {}
LoginType.PODLASIE -> {}
LoginType.USOS -> {}
LoginType.DEMO -> {}
LoginType.TEMPLATE -> {}
} }
d(TAG, "Processed student data: ${profile.studentData}") d(TAG, "Processed student data: ${profile.studentData}")

View File

@ -0,0 +1,153 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-22
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_EDUDZIENNIK_WEB
import pl.szczodrzynski.edziennik.data.api.models.Data
import pl.szczodrzynski.edziennik.data.db.entity.*
/**
* Use http://patorjk.com/software/taag/#p=display&f=Big for the ascii art
*
* Use https://codepen.io/kubasz/pen/RwwwbGN to easily generate the student data getters/setters
*/
class DataEdudziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) {
fun isWebLoginValid() = webSessionIdExpiryTime-30 > currentTimeUnix() && webSessionId.isNotNullNorEmpty()
override fun satisfyLoginMethods() {
loginMethods.clear()
if (isWebLoginValid()) {
loginMethods += LOGIN_METHOD_EDUDZIENNIK_WEB
}
}
override fun generateUserCode() = "$schoolName:$loginEmail:${studentId?.crc32()}"
private var mLoginEmail: String? = null
var loginEmail: String?
get() { mLoginEmail = mLoginEmail ?: loginStore.getLoginData("email", null); return mLoginEmail }
set(value) { loginStore.putLoginData("email", value); mLoginEmail = value }
private var mLoginPassword: String? = null
var loginPassword: String?
get() { mLoginPassword = mLoginPassword ?: loginStore.getLoginData("password", null); return mLoginPassword }
set(value) { loginStore.putLoginData("password", value); mLoginPassword = value }
private var mStudentId: String? = null
var studentId: String?
get() { mStudentId = mStudentId ?: profile?.getStudentData("studentId", null); return mStudentId }
set(value) { profile?.putStudentData("studentId", value) ?: return; mStudentId = value }
private var mSchoolId: String? = null
var schoolId: String?
get() { mSchoolId = mSchoolId ?: profile?.getStudentData("schoolId", null); return mSchoolId }
set(value) { profile?.putStudentData("schoolId", value) ?: return; mSchoolId = value }
private var mClassId: String? = null
var classId: String?
get() { mClassId = mClassId ?: profile?.getStudentData("classId", null); return mClassId }
set(value) { profile?.putStudentData("classId", value) ?: return; mClassId = value }
/* __ __ _
\ \ / / | |
\ \ /\ / /__| |__
\ \/ \/ / _ \ '_ \
\ /\ / __/ |_) |
\/ \/ \___|_._*/
private var mWebSessionId: String? = null
var webSessionId: String?
get() { mWebSessionId = mWebSessionId ?: loginStore.getLoginData("webSessionId", null); return mWebSessionId }
set(value) { loginStore.putLoginData("webSessionId", value); mWebSessionId = value }
private var mWebSessionIdExpiryTime: Long? = null
var webSessionIdExpiryTime: Long
get() { mWebSessionIdExpiryTime = mWebSessionIdExpiryTime ?: loginStore.getLoginData("webSessionIdExpiryTime", 0L); return mWebSessionIdExpiryTime ?: 0L }
set(value) { loginStore.putLoginData("webSessionIdExpiryTime", value); mWebSessionIdExpiryTime = value }
/* ____ _ _
/ __ \| | | |
| | | | |_| |__ ___ _ __
| | | | __| '_ \ / _ \ '__|
| |__| | |_| | | | __/ |
\____/ \__|_| |_|\___|*/
private var mCurrentSemester: Int? = null
var currentSemester: Int
get() { mCurrentSemester = mCurrentSemester ?: profile?.getStudentData("currentSemester", 1); return mCurrentSemester ?: 1 }
set(value) { profile?.putStudentData("currentSemester", value) ?: return; mCurrentSemester = value }
private var mSchoolName: String? = null
var schoolName: String?
get() { mSchoolName = mSchoolName ?: profile?.getStudentData("schoolName", null); return mSchoolName }
set(value) { profile?.putStudentData("schoolName", value) ?: return; mSchoolName = value }
val studentEndpoint: String
get() = "Students/$studentId/"
val schoolEndpoint: String
get() = "Schools/$schoolId/"
val classStudentEndpoint: String
get() = "Class/$studentId/"
val schoolClassEndpoint: String
get() = "Schools/$classId/"
val studentAndClassEndpoint: String
get() = "Students/$studentId/Klass/$classId/"
val studentAndClassesEndpoint: String
get() = "Students/$studentId/Classes/$classId/"
val timetableEndpoint: String
get() = "Plan/$studentId/"
val studentAndTeacherClassEndpoint: String
get() = "Students/$studentId/Teachers/$classId/"
val courseStudentEndpoint: String
get() = "Course/$studentId/"
fun getSubject(longId: String, name: String): Subject {
val id = longId.crc32()
return subjectList.singleOrNull { it.id == id } ?: run {
val subject = Subject(profileId, id, name, name)
subjectList.put(id, subject)
subject
}
}
fun getTeacher(firstName: String, lastName: String, longId: String? = null): Teacher {
val name = "$firstName $lastName".fixName()
val id = name.crc32()
return teacherList.singleOrNull { it.id == id }?.also {
if (longId != null && it.loginId == null) it.loginId = longId
} ?: run {
val teacher = Teacher(profileId, id, firstName, lastName, longId)
teacherList.put(id, teacher)
teacher
}
}
fun getTeacherByFirstLast(nameFirstLast: String, longId: String? = null): Teacher {
val nameParts = nameFirstLast.split(" ")
return getTeacher(nameParts[0], nameParts[1], longId)
}
fun getTeacherByLastFirst(nameLastFirst: String, longId: String? = null): Teacher {
val nameParts = nameLastFirst.split(" ")
return getTeacher(nameParts[1], nameParts[0], longId)
}
fun getEventType(longId: String, name: String): EventType {
val id = longId.crc16().toLong()
return eventTypes.singleOrNull { it.id == id } ?: run {
val eventType = EventType(profileId, id, name, colorFromName(name))
eventTypes.put(id, eventType)
eventType
}
}
}

View File

@ -0,0 +1,142 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-22
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik
import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikData
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web.EdudziennikWebGetAnnouncement
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web.EdudziennikWebGetHomework
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.firstlogin.EdudziennikFirstLogin
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.login.EdudziennikLogin
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.login.EdudziennikLoginWeb
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.utils.Utils.d
class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
companion object {
private const val TAG = "Edudziennik"
}
val internalErrorList = mutableListOf<Int>()
val data: DataEdudziennik
private var afterLogin: (() -> Unit)? = null
init {
data = DataEdudziennik(app, profile, loginStore).apply {
callback = wrapCallback(this@Edudziennik.callback)
satisfyLoginMethods()
}
}
private fun completed() {
data.saveData()
callback.onCompleted()
}
/* _______ _ _ _ _ _
|__ __| | /\ | | (_) | | |
| | | |__ ___ / \ | | __ _ ___ _ __ _| |_| |__ _ __ ___
| | | '_ \ / _ \ / /\ \ | |/ _` |/ _ \| '__| | __| '_ \| '_ ` _ \
| | | | | | __/ / ____ \| | (_| | (_) | | | | |_| | | | | | | | |
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
__/ |
|__*/
override fun sync(featureIds: List<Int>, viewId: Int?, onlyEndpoints: List<Int>?, arguments: JsonObject?) {
data.arguments = arguments
data.prepare(edudziennikLoginMethods, EdudziennikFeatures, featureIds, viewId, onlyEndpoints)
login()
}
private fun login(loginMethodId: Int? = null, afterLogin: (() -> Unit)? = null) {
d(TAG, "Trying to login with ${data.targetLoginMethodIds}")
if (internalErrorList.isNotEmpty()) {
d(TAG, " - Internal errors:")
internalErrorList.forEach { d(TAG, " - code $it") }
}
loginMethodId?.let { data.prepareFor(edudziennikLoginMethods, it) }
afterLogin?.let { this.afterLogin = it }
EdudziennikLogin(data) {
data()
}
}
private fun data() {
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
if (internalErrorList.isNotEmpty()) {
d(TAG, " - Internal errors:")
internalErrorList.forEach { d(TAG, " - code $it") }
}
afterLogin?.invoke() ?: EdudziennikData(data) {
completed()
}
}
override fun getMessage(message: MessageFull) {}
override fun sendMessage(recipients: List<Teacher>, subject: String, text: String) {}
override fun markAllAnnouncementsAsRead() {}
override fun getAnnouncement(announcement: AnnouncementFull) {
EdudziennikLoginWeb(data) {
EdudziennikWebGetAnnouncement(data, announcement) {
completed()
}
}
}
override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) {}
override fun getRecipientList() {}
override fun getEvent(eventFull: EventFull) {
EdudziennikLoginWeb(data) {
EdudziennikWebGetHomework(data, eventFull) {
completed()
}
}
}
override fun firstLogin() { EdudziennikFirstLogin(data) { completed() } }
override fun cancel() {
d(TAG, "Cancelled")
data.cancel()
}
private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {
return object : EdziennikCallback {
override fun onCompleted() { callback.onCompleted() }
override fun onProgress(step: Float) { callback.onProgress(step) }
override fun onStartProgress(stringRes: Int) { callback.onStartProgress(stringRes) }
override fun onError(apiError: ApiError) {
if (apiError.errorCode in internalErrorList) {
// finish immediately if the same error occurs twice during the same sync
callback.onError(apiError)
return
}
internalErrorList.add(apiError.errorCode)
when (apiError.errorCode) {
ERROR_EDUDZIENNIK_WEB_SESSION_EXPIRED -> {
login()
}
ERROR_LOGIN_EDUDZIENNIK_WEB_NO_SESSION_ID -> {
login()
}
ERROR_EDUDZIENNIK_WEB_LIMITED_ACCESS -> {
data()
}
else -> callback.onError(apiError)
}
}
}
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-23
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.models.Feature
const val ENDPOINT_EDUDZIENNIK_WEB_START = 1000
const val ENDPOINT_EDUDZIENNIK_WEB_TEACHERS = 1001
const val ENDPOINT_EDUDZIENNIK_WEB_GRADES = 1011
const val ENDPOINT_EDUDZIENNIK_WEB_TIMETABLE = 1012
const val ENDPOINT_EDUDZIENNIK_WEB_EXAMS = 1013
const val ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE = 1014
const val ENDPOINT_EDUDZIENNIK_WEB_ANNOUNCEMENTS = 1015
const val ENDPOINT_EDUDZIENNIK_WEB_HOMEWORK = 1016
const val ENDPOINT_EDUDZIENNIK_WEB_EVENTS = 1017
const val ENDPOINT_EDUDZIENNIK_WEB_NOTES = 1018
const val ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER = 1030
val EdudziennikFeatures = listOf(
/* School and team info and subjects */
Feature(LOGIN_TYPE_EDUDZIENNIK, FEATURE_STUDENT_INFO, listOf(
ENDPOINT_EDUDZIENNIK_WEB_START to LOGIN_METHOD_EDUDZIENNIK_WEB
), listOf(LOGIN_METHOD_EDUDZIENNIK_WEB)),
/* Teachers */
Feature(LOGIN_TYPE_EDUDZIENNIK, FEATURE_TEACHERS, listOf(
ENDPOINT_EDUDZIENNIK_WEB_TEACHERS to LOGIN_METHOD_EDUDZIENNIK_WEB
), listOf(LOGIN_METHOD_EDUDZIENNIK_WEB)),
/* Timetable */
Feature(LOGIN_TYPE_EDUDZIENNIK, FEATURE_TIMETABLE, listOf(
ENDPOINT_EDUDZIENNIK_WEB_TIMETABLE to LOGIN_METHOD_EDUDZIENNIK_WEB
), listOf(LOGIN_METHOD_EDUDZIENNIK_WEB)),
/* Grades */
Feature(LOGIN_TYPE_EDUDZIENNIK, FEATURE_GRADES, listOf(
ENDPOINT_EDUDZIENNIK_WEB_GRADES to LOGIN_METHOD_EDUDZIENNIK_WEB
), listOf(LOGIN_METHOD_EDUDZIENNIK_WEB)),
/* Agenda */
Feature(LOGIN_TYPE_EDUDZIENNIK, FEATURE_AGENDA, listOf(
ENDPOINT_EDUDZIENNIK_WEB_EXAMS to LOGIN_METHOD_EDUDZIENNIK_WEB,
ENDPOINT_EDUDZIENNIK_WEB_HOMEWORK to LOGIN_METHOD_EDUDZIENNIK_WEB,
ENDPOINT_EDUDZIENNIK_WEB_EVENTS to LOGIN_METHOD_EDUDZIENNIK_WEB
), listOf(LOGIN_METHOD_EDUDZIENNIK_WEB)),
/* Homework */
Feature(LOGIN_TYPE_EDUDZIENNIK, FEATURE_HOMEWORK, listOf(
ENDPOINT_EDUDZIENNIK_WEB_HOMEWORK to LOGIN_METHOD_EDUDZIENNIK_WEB
), listOf(LOGIN_METHOD_EDUDZIENNIK_WEB)),
/* Behaviour */
Feature(LOGIN_TYPE_EDUDZIENNIK, FEATURE_BEHAVIOUR, listOf(
ENDPOINT_EDUDZIENNIK_WEB_NOTES to LOGIN_METHOD_EDUDZIENNIK_WEB
), listOf(LOGIN_METHOD_EDUDZIENNIK_WEB)),
/* Attendance */
Feature(LOGIN_TYPE_EDUDZIENNIK, FEATURE_ATTENDANCE, listOf(
ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE to LOGIN_METHOD_EDUDZIENNIK_WEB
), listOf(LOGIN_METHOD_EDUDZIENNIK_WEB)),
/* Announcements */
Feature(LOGIN_TYPE_EDUDZIENNIK, FEATURE_ANNOUNCEMENTS, listOf(
ENDPOINT_EDUDZIENNIK_WEB_ANNOUNCEMENTS to LOGIN_METHOD_EDUDZIENNIK_WEB
), listOf(LOGIN_METHOD_EDUDZIENNIK_WEB)),
/* Lucky number */
Feature(LOGIN_TYPE_EDUDZIENNIK, FEATURE_LUCKY_NUMBER, listOf(
ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER to LOGIN_METHOD_EDUDZIENNIK_WEB
), listOf(LOGIN_METHOD_EDUDZIENNIK_WEB))
)

View File

@ -0,0 +1,88 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-22
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web.*
import pl.szczodrzynski.edziennik.utils.Utils
class EdudziennikData(val data: DataEdudziennik, val onSuccess: () -> Unit) {
companion object {
private const val TAG = "EdudziennikData"
}
init {
nextEndpoint(onSuccess)
}
private fun nextEndpoint(onSuccess: () -> Unit) {
if (data.targetEndpointIds.isEmpty()) {
onSuccess()
return
}
if (data.cancelled) {
onSuccess()
return
}
val id = data.targetEndpointIds.firstKey()
val lastSync = data.targetEndpointIds.remove(id)
useEndpoint(id, lastSync) { endpointId ->
data.progress(data.progressStep)
nextEndpoint(onSuccess)
}
}
private fun useEndpoint(endpointId: Int, lastSync: Long?, onSuccess: (endpointId: Int) -> Unit) {
Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync")
when (endpointId) {
ENDPOINT_EDUDZIENNIK_WEB_START -> {
data.startProgress(R.string.edziennik_progress_endpoint_data)
EdudziennikWebStart(data, lastSync, onSuccess)
}
ENDPOINT_EDUDZIENNIK_WEB_TEACHERS -> {
data.startProgress(R.string.edziennik_progress_endpoint_teachers)
EdudziennikWebTeachers(data, lastSync, onSuccess)
}
ENDPOINT_EDUDZIENNIK_WEB_GRADES -> {
data.startProgress(R.string.edziennik_progress_endpoint_grades)
EdudziennikWebGrades(data, lastSync, onSuccess)
}
ENDPOINT_EDUDZIENNIK_WEB_TIMETABLE -> {
data.startProgress(R.string.edziennik_progress_endpoint_timetable)
EdudziennikWebTimetable(data, lastSync, onSuccess)
}
ENDPOINT_EDUDZIENNIK_WEB_EXAMS -> {
data.startProgress(R.string.edziennik_progress_endpoint_exams)
EdudziennikWebExams(data, lastSync, onSuccess)
}
ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE -> {
data.startProgress(R.string.edziennik_progress_endpoint_attendance)
EdudziennikWebAttendance(data, lastSync, onSuccess)
}
ENDPOINT_EDUDZIENNIK_WEB_ANNOUNCEMENTS -> {
data.startProgress(R.string.edziennik_progress_endpoint_announcements)
EdudziennikWebAnnouncements(data, lastSync, onSuccess)
}
ENDPOINT_EDUDZIENNIK_WEB_HOMEWORK -> {
data.startProgress(R.string.edziennik_progress_endpoint_homework)
EdudziennikWebHomework(data, lastSync, onSuccess)
}
ENDPOINT_EDUDZIENNIK_WEB_EVENTS -> {
data.startProgress(R.string.edziennik_progress_endpoint_events)
EdudziennikWebEvents(data, lastSync, onSuccess)
}
ENDPOINT_EDUDZIENNIK_WEB_NOTES -> {
data.startProgress(R.string.edziennik_progress_endpoint_notices)
EdudziennikWebNotes(data, lastSync, onSuccess)
}
ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER -> {
data.startProgress(R.string.edziennik_progress_endpoint_lucky_number)
EdudziennikWebLuckyNumber(data, lastSync, onSuccess)
}
else -> onSuccess(endpointId)
}
}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-22
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data
import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import im.wangchao.mhttp.callback.TextCallbackHandler
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.models.Date
open class EdudziennikWeb(open val data: DataEdudziennik, open val lastSync: Long?) {
companion object {
private const val TAG = "EdudziennikWeb"
}
val profileId
get() = data.profile?.id ?: -1
val profile
get() = data.profile
fun webGet(tag: String, endpoint: String, xhr: Boolean = false, semester: Int? = null, onSuccess: (text: String) -> Unit) {
val url = "https://dziennikel.appspot.com/" + when (endpoint.endsWith('/') || endpoint.contains('?') || endpoint.isEmpty()) {
true -> endpoint
else -> "$endpoint/"
} + (semester?.let { "?semester=" + if(it == -1) "all" else it } ?: "")
d(tag, "Request: Edudziennik/Web - $url")
val callback = object : TextCallbackHandler() {
override fun onSuccess(text: String?, response: Response?) {
if (text == null || response == null) {
data.error(ApiError(tag, ERROR_RESPONSE_EMPTY)
.withResponse(response))
return
}
if (semester == null && url.contains("start")) {
profile?.also { profile ->
val cookies = data.app.cookieJar.getAll("dziennikel.appspot.com")
val semesterCookie = cookies["semester"]?.toIntOrNull()
semesterCookie?.let { data.currentSemester = it }
if (semesterCookie == 2 && profile.dateSemester2Start > Date.getToday())
profile.dateSemester2Start = Date.getToday().stepForward(0, 0, -1)
}
}
try {
onSuccess(text)
} catch (e: Exception) {
data.error(ApiError(tag, EXCEPTION_EDUDZIENNIK_WEB_REQUEST)
.withThrowable(e)
.withResponse(response)
.withApiResponse(text))
}
}
override fun onFailure(response: Response?, throwable: Throwable?) {
val error = when (response?.code()) {
402 -> ERROR_EDUDZIENNIK_WEB_LIMITED_ACCESS
403 -> ERROR_EDUDZIENNIK_WEB_SESSION_EXPIRED
else -> ERROR_REQUEST_FAILURE
}
data.error(ApiError(tag, error)
.withResponse(response)
.withThrowable(throwable))
}
}
data.app.cookieJar.set("dziennikel.appspot.com", "sessionid", data.webSessionId)
Request.builder()
.url(url)
.userAgent(EDUDZIENNIK_USER_AGENT)
.apply {
if (xhr) header("X-Requested-With", "XMLHttpRequest")
}
.get()
.callback(callback)
.build()
.enqueue()
}
}

View File

@ -0,0 +1,75 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-26
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web
import org.jsoup.Jsoup
import pl.szczodrzynski.edziennik.crc32
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_ANNOUNCEMENT_ID
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_ANNOUNCEMENTS
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.data.db.entity.Announcement
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.utils.models.Date
class EdudziennikWebAnnouncements(override val data: DataEdudziennik,
override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : EdudziennikWeb(data, lastSync) {
companion object {
const val TAG = "EdudziennikWebAnnouncements"
}
init { data.profile?.also { profile ->
webGet(TAG, data.schoolClassEndpoint + "Announcements") { text ->
val doc = Jsoup.parse(text)
if (doc.getElementsByClass("message").text().trim() != "Brak ogłoszeń.") {
doc.select("table.list tbody tr").forEach { announcementElement ->
val titleElement = announcementElement.child(0).child(0)
val longId = EDUDZIENNIK_ANNOUNCEMENT_ID.find(titleElement.attr("href"))?.get(1)
?: return@forEach
val id = longId.crc32()
val subject = titleElement.text()
val teacherName = announcementElement.child(1).text()
val teacher = data.getTeacherByFirstLast(teacherName)
val dateString = announcementElement.getElementsByClass("datetime").first().text()
val startDate = Date.fromY_m_d(dateString)
val addedDate = Date.fromIsoHm(dateString)
val announcementObject = Announcement(
profileId = profileId,
id = id,
subject = subject,
text = null,
startDate = startDate,
endDate = null,
teacherId = teacher.id,
addedDate = addedDate
).also {
it.idString = longId
}
data.announcementList.add(announcementObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_ANNOUNCEMENT,
id,
profile.empty,
profile.empty
))
}
}
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_ANNOUNCEMENTS, SYNC_ALWAYS)
onSuccess(ENDPOINT_EDUDZIENNIK_WEB_ANNOUNCEMENTS)
}
} ?: onSuccess(ENDPOINT_EDUDZIENNIK_WEB_ANNOUNCEMENTS) }
}

View File

@ -0,0 +1,114 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-24
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web
import pl.szczodrzynski.edziennik.crc32
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_ATTENDANCE_ENTRIES
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_ATTENDANCE_TYPE
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_ATTENDANCE_TYPES
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.data.db.entity.Attendance
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.singleOrNull
import pl.szczodrzynski.edziennik.utils.models.Date
import java.util.*
class EdudziennikWebAttendance(override val data: DataEdudziennik,
override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : EdudziennikWeb(data, lastSync) {
companion object {
private const val TAG = "EdudziennikWebAttendance"
}
private var requestSemester: Int? = null
init {
if (profile?.empty == true && data.currentSemester == 2) requestSemester = 1
getAttendances()
}
private fun getAttendances() { data.profile?.also { profile ->
webGet(TAG, data.studentEndpoint + "Presence", semester = requestSemester) { text ->
val attendanceTypes = EDUDZIENNIK_ATTENDANCE_TYPES.find(text)?.get(1)?.split(',')?.map {
val type = EDUDZIENNIK_ATTENDANCE_TYPE.find(it.trim())
val symbol = type?.get(1)?.trim() ?: "?"
val name = type?.get(2)?.trim() ?: "nieznany rodzaj"
return@map Triple(
symbol,
name,
when (name.toLowerCase(Locale.ROOT)) {
"obecność" -> Attendance.TYPE_PRESENT
"nieobecność" -> Attendance.TYPE_ABSENT
"spóźnienie" -> Attendance.TYPE_BELATED
"nieobecność usprawiedliwiona" -> Attendance.TYPE_ABSENT_EXCUSED
"dzień wolny" -> Attendance.TYPE_DAY_FREE
"brak zajęć" -> Attendance.TYPE_DAY_FREE
"oddelegowany" -> Attendance.TYPE_RELEASED
else -> Attendance.TYPE_UNKNOWN
}
)
} ?: emptyList()
EDUDZIENNIK_ATTENDANCE_ENTRIES.findAll(text).forEach { attendanceElement ->
val date = Date.fromY_m_d(attendanceElement[1])
val lessonNumber = attendanceElement[2].toInt()
val attendanceSymbol = attendanceElement[3]
val lessons = data.app.db.timetableDao().getAllForDateNow(profileId, date)
val lesson = lessons.firstOrNull { it.lessonNumber == lessonNumber }
val id = "${date.stringY_m_d}:$lessonNumber:$attendanceSymbol".crc32()
val (typeSymbol, typeName, baseType) = attendanceTypes.firstOrNull { (symbol, _, _) -> symbol == attendanceSymbol }
?: return@forEach
val startTime = data.lessonRanges.singleOrNull { it.lessonNumber == lessonNumber }?.startTime
?: return@forEach
val attendanceObject = Attendance(
profileId = profileId,
id = id,
baseType = baseType,
typeName = typeName,
typeShort = data.app.attendanceManager.getTypeShort(baseType),
typeSymbol = typeSymbol,
typeColor = null,
date = date,
startTime = lesson?.displayStartTime ?: startTime,
semester = profile.currentSemester,
teacherId = lesson?.displayTeacherId ?: -1,
subjectId = lesson?.displaySubjectId ?: -1
).also {
it.lessonNumber = lessonNumber
}
data.attendanceList.add(attendanceObject)
if (baseType != Attendance.TYPE_PRESENT) {
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_ATTENDANCE,
id,
profile.empty || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == Attendance.TYPE_UNKNOWN,
profile.empty || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == Attendance.TYPE_UNKNOWN
))
}
}
if (profile.empty && requestSemester == 1 && data.currentSemester == 2) {
requestSemester = null
getAttendances()
} else {
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE, SYNC_ALWAYS)
onSuccess(ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE)
}
}
} ?: onSuccess(ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE) }
}

View File

@ -0,0 +1,70 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2020-1-1
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web
import org.jsoup.Jsoup
import pl.szczodrzynski.edziennik.crc32
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_EVENT_ID
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_EVENTS
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.utils.models.Date
class EdudziennikWebEvents(override val data: DataEdudziennik,
override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : EdudziennikWeb(data, lastSync) {
companion object {
const val TAG = "EdudziennikWebEvents"
}
init { data.profile?.also { profile ->
webGet(TAG, data.studentAndClassesEndpoint + "KlassEvent", xhr = true) { text ->
val doc = Jsoup.parseBodyFragment("<table>" + text.trim() + "</table>")
doc.getElementsByTag("tr").forEach { eventElement ->
val date = Date.fromY_m_d(eventElement.child(1).text())
val titleElement = eventElement.child(2).child(0)
val title = titleElement.text().trim()
val id = EDUDZIENNIK_EVENT_ID.find(titleElement.attr("href"))?.get(1)?.crc32()
?: return@forEach
val eventObject = Event(
profileId = profileId,
id = id,
date = date,
time = null,
topic = title,
color = null,
type = Event.TYPE_CLASS_EVENT,
teacherId = -1,
subjectId = -1,
teamId = data.teamClass?.id ?: -1
)
data.eventList.add(eventObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_EVENT,
id,
profile.empty,
profile.empty
))
}
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_CLASS_EVENT))
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_EVENTS, SYNC_ALWAYS)
onSuccess(ENDPOINT_EDUDZIENNIK_WEB_EVENTS)
}
} ?: onSuccess(ENDPOINT_EDUDZIENNIK_WEB_EVENTS) }
}

View File

@ -0,0 +1,90 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-24
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web
import org.jsoup.Jsoup
import pl.szczodrzynski.edziennik.crc32
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_EVENT_TYPE_ID
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_EXAM_ID
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_SUBJECT_ID
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_EXAMS
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.utils.models.Date
class EdudziennikWebExams(override val data: DataEdudziennik,
override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : EdudziennikWeb(data, lastSync) {
companion object {
const val TAG = "EdudziennikWebExams"
}
init { profile?.also { profile ->
webGet(TAG, data.studentAndClassEndpoint + "Evaluations", xhr = true) { text ->
val doc = Jsoup.parseBodyFragment("<table>" + text.trim() + "</table>")
doc.select("tr").forEach { examElement ->
val id = EDUDZIENNIK_EXAM_ID.find(examElement.child(0).child(0).attr("href"))
?.get(1)?.crc32() ?: return@forEach
val topic = examElement.child(0).text().trim()
val subjectElement = examElement.child(1).child(0)
val subjectId = EDUDZIENNIK_SUBJECT_ID.find(subjectElement.attr("href"))?.get(1)
?: return@forEach
val subjectName = subjectElement.text().trim()
val subject = data.getSubject(subjectId, subjectName)
val dateString = examElement.child(2).text().trim()
if (dateString.isBlank()) return@forEach
val date = Date.fromY_m_d(dateString)
val lessons = data.app.db.timetableDao().getAllForDateNow(profileId, date)
val startTime = lessons.firstOrNull { it.displaySubjectId == subject.id }?.displayStartTime
val eventTypeElement = examElement.child(3).child(0)
val eventTypeId = EDUDZIENNIK_EVENT_TYPE_ID.find(eventTypeElement.attr("href"))?.get(1)
?: return@forEach
val eventTypeName = eventTypeElement.text()
val eventType = data.getEventType(eventTypeId, eventTypeName)
val eventObject = Event(
profileId = profileId,
id = id,
date = date,
time = startTime,
topic = topic,
color = null,
type = eventType.id,
teacherId = -1,
subjectId = subject.id,
teamId = data.teamClass?.id ?: -1
)
data.eventList.add(eventObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_EVENT,
id,
profile.empty,
profile.empty
))
}
data.toRemove.add(DataRemoveModel.Events.futureExceptTypes(listOf(
Event.TYPE_HOMEWORK,
Event.TYPE_CLASS_EVENT
)))
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_EXAMS, SYNC_ALWAYS)
onSuccess(ENDPOINT_EDUDZIENNIK_WEB_EXAMS)
}
}}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-26
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.Regexes
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.data.api.events.AnnouncementGetEvent
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.get
class EdudziennikWebGetAnnouncement(override val data: DataEdudziennik,
private val announcement: AnnouncementFull,
val onSuccess: () -> Unit
) : EdudziennikWeb(data, null) {
companion object {
const val TAG = "EdudziennikWebGetAnnouncement"
}
init {
webGet(TAG, "Announcement/${announcement.idString}") { text ->
val description = Regexes.EDUDZIENNIK_ANNOUNCEMENT_DESCRIPTION.find(text)?.get(1)?.trim() ?: ""
announcement.text = description
EventBus.getDefault().postSticky(AnnouncementGetEvent(announcement))
data.announcementList.add(announcement)
onSuccess()
}
}
}

View File

@ -0,0 +1,45 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web
import android.text.Html
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.Regexes
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.data.api.events.EventGetEvent
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
class EdudziennikWebGetHomework(
override val data: DataEdudziennik,
val event: EventFull,
val onSuccess: () -> Unit
) : EdudziennikWeb(data, null) {
companion object {
const val TAG = "EdudziennikWebGetHomework"
}
init {
if (event.attachmentNames.isNotNullNorEmpty()) {
val id = event.attachmentNames!![0]
webGet(TAG, "Homework/$id") { text ->
val description = Regexes.EDUDZIENNIK_HOMEWORK_DESCRIPTION.find(text)?.get(1)?.trim()
if (description != null) event.topic = Html.fromHtml(description).toString()
event.homeworkBody = ""
event.attachmentNames = null
data.eventList += event
data.eventListReplace = true
EventBus.getDefault().postSticky(EventGetEvent(event))
onSuccess()
}
} else {
EventBus.getDefault().postSticky(EventGetEvent(event))
onSuccess()
}
}
}

View File

@ -0,0 +1,230 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-25
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web
import android.graphics.Color
import org.jsoup.Jsoup
import pl.szczodrzynski.edziennik.colorFromCssName
import pl.szczodrzynski.edziennik.crc32
import pl.szczodrzynski.edziennik.data.api.Regexes
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_GRADES
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_NORMAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_POINT_SUM
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.models.Date
class EdudziennikWebGrades(override val data: DataEdudziennik,
override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : EdudziennikWeb(data, lastSync) {
companion object {
private const val TAG = "EdudziennikWebGrades"
}
private var requestSemester: Int? = null
init {
if (profile?.empty == true && data.currentSemester == 2) requestSemester = 1
getGrades()
}
private fun getGrades() { data.profile?.also { profile ->
webGet(TAG, data.studentEndpoint + "start", semester = requestSemester) { text ->
val semester = requestSemester ?: data.currentSemester
val doc = Jsoup.parse(text)
val subjects = doc.select("#student_grades tbody").firstOrNull()?.children()
subjects?.forEach { subjectElement ->
if (subjectElement.id().isBlank()) return@forEach
val subjectId = subjectElement.id().trim()
val subjectName = subjectElement.child(0).text().trim()
val subject = data.getSubject(subjectId, subjectName)
val gradeType = when {
subjectElement.select("#sum").text().isNotBlank() -> TYPE_POINT_SUM
else -> TYPE_NORMAL
}
val gradeCountToAverage = subjectElement.select("#avg").text().isNotBlank()
val grades = subjectElement.select(".grade[data-edited]")
val gradesInfo = subjectElement.select(".grade-tip")
val gradeValues = if (grades.isNotEmpty()) {
subjects.select(".avg-$subjectId .grade-tip > p").first()
.text().split('+').map {
val split = it.split('*')
val value = split[1].trim().toFloatOrNull()
val weight = value?.let { split[0].trim().toFloatOrNull() } ?: 0f
Pair(value ?: 0f, weight)
}
} else emptyList()
grades.forEachIndexed { index, gradeElement ->
val id = Regexes.EDUDZIENNIK_GRADE_ID.find(gradeElement.attr("href"))?.get(1)?.crc32()
?: return@forEachIndexed
val (value, weight) = gradeValues[index]
val name = gradeElement.text().trim().let {
if (it.contains(',') || it.contains('.')) {
val replaced = it.replace(',', '.')
val float = replaced.toFloatOrNull()
if (float != null && float % 1 == 0f) float.toInt().toString()
else it
} else it
}
val info = gradesInfo[index]
val fullName = info.child(0).text().trim()
val columnName = info.child(4).text().trim()
val comment = info.ownText()
val description = columnName + if (comment.isNotBlank()) " - $comment" else null
val teacherName = info.child(1).text()
val teacher = data.getTeacherByLastFirst(teacherName)
val addedDate = info.child(2).text().split(' ').let {
val day = it[0].toInt()
val month = Utils.monthFromName(it[1])
val year = it[2].toInt()
Date(year, month, day).inMillis
}
val color = Regexes.STYLE_CSS_COLOR.find(gradeElement.attr("style"))?.get(1)?.let {
if (it.startsWith('#')) Color.parseColor(it)
else colorFromCssName(it)
} ?: -1
val gradeObject = Grade(
profileId = profileId,
id = id,
name = name,
type = gradeType,
value = value,
weight = if (gradeCountToAverage) weight else 0f,
color = color,
category = fullName,
description = description,
comment = null,
semester = semester,
teacherId = teacher.id,
subjectId = subject.id,
addedDate = addedDate
)
data.gradeList.add(gradeObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_GRADE,
id,
profile.empty,
profile.empty
))
}
val proposed = subjectElement.select(".proposal").firstOrNull()?.text()?.trim()
if (proposed != null && proposed.isNotBlank()) {
val proposedGradeObject = Grade(
profileId = profileId,
id = (-1 * subject.id) - 1,
name = proposed,
type = when (semester) {
1 -> TYPE_SEMESTER1_PROPOSED
else -> TYPE_SEMESTER2_PROPOSED
},
value = proposed.toFloatOrNull() ?: 0f,
weight = 0f,
color = -1,
category = null,
description = null,
comment = null,
semester = semester,
teacherId = -1,
subjectId = subject.id
)
data.gradeList.add(proposedGradeObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_GRADE,
proposedGradeObject.id,
profile.empty,
profile.empty
))
}
val final = subjectElement.select(".final").firstOrNull()?.text()?.trim()
if (final != null && final.isNotBlank()) {
val finalGradeObject = Grade(
profileId = profileId,
id = (-1 * subject.id) - 2,
name = final,
type = when (semester) {
1 -> TYPE_SEMESTER1_FINAL
else -> TYPE_SEMESTER2_FINAL
},
value = final.toFloatOrNull() ?: 0f,
weight = 0f,
color = -1,
category = null,
description = null,
comment = null,
semester = semester,
teacherId = -1,
subjectId = subject.id
)
data.gradeList.add(finalGradeObject)
data.metadataList.add(Metadata(
data.profileId,
Metadata.TYPE_GRADE,
finalGradeObject.id,
profile.empty,
profile.empty
))
}
}
if (!subjects.isNullOrEmpty()) {
data.toRemove.addAll(listOf(
TYPE_NORMAL,
TYPE_POINT_SUM,
TYPE_SEMESTER1_PROPOSED,
TYPE_SEMESTER2_PROPOSED,
TYPE_SEMESTER1_FINAL,
TYPE_SEMESTER2_FINAL
).map {
DataRemoveModel.Grades.semesterWithType(semester, it)
})
}
if (profile.empty && requestSemester == 1 && data.currentSemester == 2) {
requestSemester = null
getGrades()
} else {
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_GRADES, SYNC_ALWAYS)
onSuccess(ENDPOINT_EDUDZIENNIK_WEB_GRADES)
}
}
} ?: onSuccess(ENDPOINT_EDUDZIENNIK_WEB_GRADES) }
}

View File

@ -0,0 +1,86 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-29
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web
import org.jsoup.Jsoup
import pl.szczodrzynski.edziennik.crc32
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_HOMEWORK_ID
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_SUBJECT_ID
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_HOMEWORK
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.utils.models.Date
class EdudziennikWebHomework(override val data: DataEdudziennik,
override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : EdudziennikWeb(data, lastSync) {
companion object {
const val TAG = "EdudziennikWebHomework"
}
init { data.profile?.also { profile ->
webGet(TAG, data.courseStudentEndpoint + "Homework", xhr = true) { text ->
val doc = Jsoup.parseBodyFragment("<table>" + text.trim() + "</table>")
if (doc.getElementsByClass("message").text().trim() != "Brak prac domowych") {
doc.getElementsByTag("tr").forEach { homeworkElement ->
val dateElement = homeworkElement.getElementsByClass("date").first().child(0)
val idStr = EDUDZIENNIK_HOMEWORK_ID.find(dateElement.attr("href"))?.get(1) ?: return@forEach
val id = idStr.crc32()
val date = Date.fromY_m_d(dateElement.text())
val subjectElement = homeworkElement.child(1).child(0)
val subjectId = EDUDZIENNIK_SUBJECT_ID.find(subjectElement.attr("href"))?.get(1)
?: return@forEach
val subjectName = subjectElement.text()
val subject = data.getSubject(subjectId, subjectName)
val lessons = data.app.db.timetableDao().getAllForDateNow(profileId, date)
val startTime = lessons.firstOrNull { it.subjectId == subject.id }?.displayStartTime
val teacherName = homeworkElement.child(2).text()
val teacher = data.getTeacherByFirstLast(teacherName)
val topic = homeworkElement.child(4).text()?.trim()
val eventObject = Event(
profileId = profileId,
id = id,
date = date,
time = startTime,
topic = topic ?: "",
color = null,
type = Event.TYPE_HOMEWORK,
teacherId = teacher.id,
subjectId = subject.id,
teamId = data.teamClass?.id ?: -1
)
eventObject.attachmentNames = mutableListOf(idStr)
data.eventList.add(eventObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_HOMEWORK,
id,
profile.empty,
profile.empty
))
}
}
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_HOMEWORK))
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_HOMEWORK, SYNC_ALWAYS)
onSuccess(ENDPOINT_EDUDZIENNIK_WEB_HOMEWORK)
}
} ?: onSuccess(ENDPOINT_EDUDZIENNIK_WEB_HOMEWORK) }
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-23
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.data.db.entity.LuckyNumber
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.utils.models.Date
class EdudziennikWebLuckyNumber(override val data: DataEdudziennik,
override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : EdudziennikWeb(data, lastSync) {
companion object {
private const val TAG = "EdudziennikWebLuckyNumber"
}
init { data.profile?.also { profile ->
webGet(TAG, data.schoolEndpoint + "Lucky", xhr = true) { text ->
text.toIntOrNull()?.also { luckyNumber ->
val luckyNumberObject = LuckyNumber(
profileId = profileId,
date = Date.getToday(),
number = luckyNumber
)
data.luckyNumberList.add(luckyNumberObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_LUCKY_NUMBER,
luckyNumberObject.date.value.toLong(),
true,
profile.empty
))
}
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER, SYNC_ALWAYS)
onSuccess(ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER)
}
} ?: onSuccess(ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER) }
}

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2020-1-1
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web
import org.jsoup.Jsoup
import pl.szczodrzynski.edziennik.crc32
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_NOTE_ID
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_NOTES
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Notice
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.utils.models.Date
class EdudziennikWebNotes(override val data: DataEdudziennik,
override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : EdudziennikWeb(data, lastSync) {
companion object {
const val TAG = "EdudziennikWebNotes"
}
init { data.profile?.also { profile ->
webGet(TAG, data.classStudentEndpoint + "RegistryNotesStudent", xhr = true) { text ->
val doc = Jsoup.parseBodyFragment("<table>" + text.trim() + "</table>")
doc.getElementsByTag("tr").forEach { noteElement ->
val dateElement = noteElement.getElementsByClass("date").first().child(0)
val addedDate = Date.fromY_m_d(dateElement.text()).inMillis
val id = EDUDZIENNIK_NOTE_ID.find(dateElement.attr("href"))?.get(0)?.crc32()
?: return@forEach
val teacherName = noteElement.child(1).text()
val teacher = data.getTeacherByFirstLast(teacherName)
val description = noteElement.child(3).text()
val noticeObject = Notice(
profileId = profileId,
id = id,
type = Notice.TYPE_NEUTRAL,
semester = profile.currentSemester,
text = description,
category = null,
points = null,
teacherId = teacher.id,
addedDate = addedDate
)
data.noticeList.add(noticeObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_NOTICE,
id,
profile.empty,
profile.empty
))
}
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_NOTES, SYNC_ALWAYS)
onSuccess(ENDPOINT_EDUDZIENNIK_WEB_NOTES)
}
} ?: onSuccess(ENDPOINT_EDUDZIENNIK_WEB_NOTES) }
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-23
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web
import pl.szczodrzynski.edziennik.MONTH
import pl.szczodrzynski.edziennik.crc32
import pl.szczodrzynski.edziennik.data.api.ERROR_EDUDZIENNIK_WEB_TEAM_MISSING
import pl.szczodrzynski.edziennik.data.api.Regexes
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_SUBJECTS_START
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_START
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.Team
import pl.szczodrzynski.edziennik.firstLettersName
import pl.szczodrzynski.edziennik.get
class EdudziennikWebStart(override val data: DataEdudziennik,
override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : EdudziennikWeb(data, lastSync) {
companion object {
private const val TAG = "EdudziennikWebStart"
}
init {
webGet(TAG, data.studentEndpoint + "start") { text ->
getSchoolAndTeam(text)
getSubjects(text)
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_START, MONTH)
onSuccess(ENDPOINT_EDUDZIENNIK_WEB_START)
}
}
private fun getSchoolAndTeam(text: String) {
val schoolId = Regexes.EDUDZIENNIK_SCHOOL_DETAIL_ID.find(text)?.get(1)?.trim()
val schoolLongName = Regexes.EDUDZIENNIK_SCHOOL_DETAIL_NAME.find(text)?.get(1)?.trim()
data.schoolId = schoolId
val classId = Regexes.EDUDZIENNIK_CLASS_DETAIL_ID.find(text)?.get(1)?.trim()
val className = Regexes.EDUDZIENNIK_CLASS_DETAIL_NAME.find(text)?.get(1)?.trim()
data.classId = classId
if (classId == null || className == null || schoolId == null || schoolLongName == null) {
data.error(ApiError(TAG, ERROR_EDUDZIENNIK_WEB_TEAM_MISSING)
.withApiResponse(text))
return
}
val schoolName = schoolId.crc32().toString() + schoolLongName.firstLettersName + "_edu"
data.schoolName = schoolName
val teamId = classId.crc32()
val teamCode = "$schoolName:$className"
val teamObject = Team(
data.profileId,
teamId,
className,
Team.TYPE_CLASS,
teamCode,
-1
)
data.teamClass = teamObject
data.teamList.put(teamObject.id, teamObject)
}
private fun getSubjects(text: String) {
EDUDZIENNIK_SUBJECTS_START.findAll(text).forEach {
val id = it[1].trim()
val name = it[2].trim()
data.getSubject(id, name)
}
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-25
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web
import pl.szczodrzynski.edziennik.MONTH
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_TEACHERS
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_TEACHERS
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.get
class EdudziennikWebTeachers(override val data: DataEdudziennik,
override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : EdudziennikWeb(data, lastSync) {
companion object {
private const val TAG = "EdudziennikWebTeachers"
}
init {
webGet(TAG, data.studentAndTeacherClassEndpoint + "grid") { text ->
EDUDZIENNIK_TEACHERS.findAll(text).forEach {
val lastName = it[1].trim()
val firstName = it[2].trim()
data.getTeacher(firstName, lastName)
}
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_TEACHERS, MONTH)
onSuccess(ENDPOINT_EDUDZIENNIK_WEB_TEACHERS)
}
}
}

View File

@ -0,0 +1,149 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-23
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web
import org.jsoup.Jsoup
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_SUBJECT_ID
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_TEACHER_ID
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_TIMETABLE
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
import pl.szczodrzynski.edziennik.data.db.entity.LessonRange
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.singleOrNull
import pl.szczodrzynski.edziennik.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
import pl.szczodrzynski.edziennik.utils.models.Week
class EdudziennikWebTimetable(override val data: DataEdudziennik,
override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : EdudziennikWeb(data, lastSync) {
companion object {
private const val TAG = "EdudziennikWebTimetable"
}
init { data.profile?.also { profile ->
val currentWeekStart = Week.getWeekStart()
if (Date.getToday().weekDay > 4) {
currentWeekStart.stepForward(0, 0, 7)
}
val getDate = data.arguments?.getString("weekStart") ?: currentWeekStart.stringY_m_d
val weekStart = Date.fromY_m_d(getDate)
val weekEnd = weekStart.clone().stepForward(0, 0, 6)
webGet(TAG, data.timetableEndpoint + "print?date=$getDate") { text ->
val doc = Jsoup.parse(text)
val dataDays = mutableListOf<Int>()
val dataStart = weekStart.clone()
while (dataStart <= weekEnd) {
dataDays += dataStart.value
dataStart.stepForward(0, 0, 1)
}
val table = doc.select("#Schedule tbody").first()
if (!table.text().contains("Brak planu lekcji.")) {
table.children().forEach { row ->
val rowElements = row.children()
val lessonNumber = rowElements[0].text().toInt()
val times = rowElements[1].text().split('-')
val startTime = Time.fromH_m(times[0].trim())
val endTime = Time.fromH_m(times[1].trim())
data.lessonRanges.singleOrNull {
it.lessonNumber == lessonNumber && it.startTime == startTime && it.endTime == endTime
} ?: run {
data.lessonRanges.put(lessonNumber, LessonRange(profileId, lessonNumber, startTime, endTime))
}
rowElements.subList(2, rowElements.size).forEachIndexed { index, lesson ->
val course = lesson.select(".course").firstOrNull() ?: return@forEachIndexed
val info = course.select("span > span")
if (info.isEmpty()) return@forEachIndexed
val type = when (course.hasClass("substitute")) {
true -> Lesson.TYPE_CHANGE
else -> Lesson.TYPE_NORMAL
}
/* Getting subject */
val subjectElement = info[0].child(0)
val subjectId = EDUDZIENNIK_SUBJECT_ID.find(subjectElement.attr("href"))?.get(1)
?: return@forEachIndexed
val subjectName = subjectElement.text().trim()
val subject = data.getSubject(subjectId, subjectName)
/* Getting teacher */
val teacherId = if (info.size >= 2) {
val teacherElement = info[1].child(0)
val teacherLongId = EDUDZIENNIK_TEACHER_ID.find(teacherElement.attr("href"))?.get(1)
val teacherName = teacherElement.text().trim()
data.getTeacherByLastFirst(teacherName, teacherLongId).id
} else null
val lessonObject = Lesson(profileId, -1).also {
it.type = type
it.date = weekStart.clone().stepForward(0, 0, index)
it.lessonNumber = lessonNumber
it.startTime = startTime
it.endTime = endTime
it.subjectId = subject.id
it.teacherId = teacherId
it.teamId = data.teamClass?.id
it.id = it.buildId()
}
data.lessonList.add(lessonObject)
dataDays.remove(lessonObject.date!!.value)
if (type != Lesson.TYPE_NORMAL) {
val seen = profile.empty || lessonObject.date!! < Date.getToday()
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_LESSON_CHANGE,
lessonObject.id,
seen,
seen
))
}
}
}
}
for (day in dataDays) {
val lessonDate = Date.fromValue(day)
data.lessonList += Lesson(profileId, lessonDate.value.toLong()).apply {
type = Lesson.TYPE_NO_LESSONS
date = lessonDate
}
}
d(TAG, "Clearing lessons between ${weekStart.stringY_m_d} and ${weekEnd.stringY_m_d} - timetable downloaded for $getDate")
data.toRemove.add(DataRemoveModel.Timetable.between(weekStart, weekEnd))
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_TIMETABLE, SYNC_ALWAYS)
onSuccess(ENDPOINT_EDUDZIENNIK_WEB_TIMETABLE)
}
} ?: onSuccess(ENDPOINT_EDUDZIENNIK_WEB_TIMETABLE) }
}

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-22
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.firstlogin
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_EDUDZIENNIK
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_ACCOUNT_NAME_START
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_STUDENTS_START
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.login.EdudziennikLoginWeb
import pl.szczodrzynski.edziennik.data.api.events.FirstLoginFinishedEvent
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.fixName
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.getShortName
import pl.szczodrzynski.edziennik.set
class EdudziennikFirstLogin(val data: DataEdudziennik, val onSuccess: () -> Unit) {
companion object {
private const val TAG = "EdudziennikFirstLogin"
}
private val web = EdudziennikWeb(data, null)
private val profileList = mutableListOf<Profile>()
init {
val loginStoreId = data.loginStore.id
val loginStoreType = LOGIN_TYPE_EDUDZIENNIK
var firstProfileId = loginStoreId
EdudziennikLoginWeb(data) {
web.webGet(TAG, "") { text ->
val accountNameLong = EDUDZIENNIK_ACCOUNT_NAME_START.find(text)?.get(1)?.fixName()
EDUDZIENNIK_STUDENTS_START.findAll(text).forEach {
val studentId = it[1]
val studentNameLong = it[2].fixName()
if (studentId.isBlank() || studentNameLong.isBlank()) return@forEach
val studentNameShort = studentNameLong.getShortName()
val accountName = if (accountNameLong == studentNameLong) null else accountNameLong
val profile = Profile(
firstProfileId++,
loginStoreId,
loginStoreType,
studentNameLong,
data.loginEmail,
studentNameLong,
studentNameShort,
accountName
).apply {
studentData["studentId"] = studentId
}
profileList.add(profile)
}
EventBus.getDefault().postSticky(FirstLoginFinishedEvent(profileList, data.loginStore))
onSuccess()
}
}
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-22
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.login
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_EDUDZIENNIK_WEB
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.utils.Utils
class EdudziennikLogin(val data: DataEdudziennik, val onSuccess: () -> Unit) {
companion object {
private const val TAG = "EdudziennikLogin"
}
private var cancelled = false
init {
nextLoginMethod(onSuccess)
}
private fun nextLoginMethod(onSuccess: () -> Unit) {
if (data.targetLoginMethodIds.isEmpty()) {
onSuccess()
return
}
if (cancelled) {
onSuccess()
return
}
useLoginMethod(data.targetLoginMethodIds.removeAt(0)) { usedMethodId ->
data.progress(data.progressStep)
if (usedMethodId != -1)
data.loginMethods.add(usedMethodId)
nextLoginMethod(onSuccess)
}
}
private fun useLoginMethod(loginMethodId: Int, onSuccess: (usedMethodId: Int) -> Unit) {
// this should never be true
if (data.loginMethods.contains(loginMethodId)) {
onSuccess(-1)
return
}
Utils.d(TAG, "Using login method $loginMethodId")
when (loginMethodId) {
LOGIN_METHOD_EDUDZIENNIK_WEB -> {
data.startProgress(R.string.edziennik_progress_login_edudziennik_web)
EdudziennikLoginWeb(data) { onSuccess(loginMethodId) }
}
}
}
}

View File

@ -0,0 +1,97 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-22
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.login
import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import im.wangchao.mhttp.callback.TextCallbackHandler
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.getUnixDate
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
import pl.szczodrzynski.edziennik.utils.Utils.d
class EdudziennikLoginWeb(val data: DataEdudziennik, val onSuccess: () -> Unit) {
companion object {
private const val TAG = "EdudziennikLoginWeb"
}
init { run {
if (data.isWebLoginValid()) {
onSuccess()
}
else {
data.app.cookieJar.clear("dziennikel.appspot.com")
if (data.loginEmail.isNotNullNorEmpty() && data.loginPassword.isNotNullNorEmpty()) {
loginWithCredentials()
}
else {
data.error(ApiError(TAG, ERROR_LOGIN_DATA_MISSING))
}
}
}}
private fun loginWithCredentials() {
d(TAG, "Request: Edudziennik/Login/Web - https://dziennikel.appspot.com/login/?next=/")
val callback = object : TextCallbackHandler() {
override fun onSuccess(text: String?, response: Response?) {
if (text == null || response == null) {
data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY)
.withResponse(response))
return
}
val url = response.raw().request().url().toString()
if (!url.contains("Student")) {
when {
text.contains("Wprowadzono nieprawidłową nazwę użytkownika lub hasło.") -> ERROR_LOGIN_EDUDZIENNIK_WEB_INVALID_LOGIN
else -> ERROR_LOGIN_EDUDZIENNIK_WEB_OTHER
}.let { errorCode ->
data.error(ApiError(TAG, errorCode)
.withApiResponse(text)
.withResponse(response))
return
}
}
val cookies = data.app.cookieJar.getAll("dziennikel.appspot.com")
val sessionId = cookies["sessionid"]
if (sessionId == null) {
data.error(ApiError(TAG, ERROR_LOGIN_EDUDZIENNIK_WEB_NO_SESSION_ID)
.withResponse(response)
.withApiResponse(text))
return
}
data.webSessionId = sessionId
data.webSessionIdExpiryTime = response.getUnixDate() + 45 * 60 /* 45 min */
onSuccess()
}
override fun onFailure(response: Response?, throwable: Throwable?) {
data.error(ApiError(TAG, ERROR_REQUEST_FAILURE)
.withResponse(response)
.withThrowable(throwable))
}
}
Request.builder()
.url("https://dziennikel.appspot.com/login/?next=/")
.userAgent(EDUDZIENNIK_USER_AGENT)
.contentType("application/x-www-form-urlencoded")
.addParameter("email", data.loginEmail)
.addParameter("password", data.loginPassword)
.addParameter("auth_method", "password")
.addParameter("next", "/")
.post()
.callback(callback)
.build()
.enqueue()
}
}

View File

@ -5,14 +5,15 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.librus package pl.szczodrzynski.edziennik.data.api.edziennik.librus
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.currentTimeUnix
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_LIBRUS_API
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_LIBRUS_MESSAGES
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_LIBRUS_PORTAL
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_LIBRUS_SYNERGIA
import pl.szczodrzynski.edziennik.data.api.models.Data import pl.szczodrzynski.edziennik.data.api.models.Data
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod import pl.szczodrzynski.edziennik.isNotNullNorEmpty
import pl.szczodrzynski.edziennik.ext.currentTimeUnix
import pl.szczodrzynski.edziennik.ext.getStudentData
import pl.szczodrzynski.edziennik.ext.isNotNullNorEmpty
import pl.szczodrzynski.edziennik.ext.set
class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) { class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) {
@ -24,15 +25,15 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
override fun satisfyLoginMethods() { override fun satisfyLoginMethods() {
loginMethods.clear() loginMethods.clear()
if (isPortalLoginValid()) if (isPortalLoginValid())
loginMethods += LoginMethod.LIBRUS_PORTAL loginMethods += LOGIN_METHOD_LIBRUS_PORTAL
if (isApiLoginValid()) if (isApiLoginValid())
loginMethods += LoginMethod.LIBRUS_API loginMethods += LOGIN_METHOD_LIBRUS_API
if (isSynergiaLoginValid()) { if (isSynergiaLoginValid()) {
loginMethods += LoginMethod.LIBRUS_SYNERGIA loginMethods += LOGIN_METHOD_LIBRUS_SYNERGIA
app.cookieJar.set("synergia.librus.pl", "DZIENNIKSID", synergiaSessionId) app.cookieJar.set("synergia.librus.pl", "DZIENNIKSID", synergiaSessionId)
} }
if (isMessagesLoginValid()) { if (isMessagesLoginValid()) {
loginMethods += LoginMethod.LIBRUS_MESSAGES loginMethods += LOGIN_METHOD_LIBRUS_MESSAGES
app.cookieJar.set("wiadomosci.librus.pl", "DZIENNIKSID", messagesSessionId) app.cookieJar.set("wiadomosci.librus.pl", "DZIENNIKSID", messagesSessionId)
} }
} }
@ -119,7 +120,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiLogin: String? = null private var mApiLogin: String? = null
var apiLogin: String? var apiLogin: String?
get() { mApiLogin = mApiLogin ?: profile?.getStudentData("accountLogin", null); return mApiLogin } get() { mApiLogin = mApiLogin ?: profile?.getStudentData("accountLogin", null); return mApiLogin }
set(value) { profile["accountLogin"] = value; mApiLogin = value } set(value) { profile?.putStudentData("accountLogin", value); mApiLogin = value }
/** /**
* A Synergia password. * A Synergia password.
* Used: for login (API Login Method) in Synergia mode. * Used: for login (API Login Method) in Synergia mode.
@ -128,7 +129,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiPassword: String? = null private var mApiPassword: String? = null
var apiPassword: String? var apiPassword: String?
get() { mApiPassword = mApiPassword ?: profile?.getStudentData("accountPassword", null); return mApiPassword } get() { mApiPassword = mApiPassword ?: profile?.getStudentData("accountPassword", null); return mApiPassword }
set(value) { profile["accountPassword"] = value; mApiPassword = value } set(value) { profile?.putStudentData("accountPassword", value); mApiPassword = value }
/** /**
* A JST login Code. * A JST login Code.
@ -137,7 +138,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiCode: String? = null private var mApiCode: String? = null
var apiCode: String? var apiCode: String?
get() { mApiCode = mApiCode ?: loginStore.getLoginData("accountCode", null); return mApiCode } get() { mApiCode = mApiCode ?: loginStore.getLoginData("accountCode", null); return mApiCode }
set(value) { profile["accountCode"] = value; mApiCode = value } set(value) { profile?.putStudentData("accountCode", value); mApiCode = value }
/** /**
* A JST login PIN. * A JST login PIN.
* Used only during first login in JST mode. * Used only during first login in JST mode.
@ -145,7 +146,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiPin: String? = null private var mApiPin: String? = null
var apiPin: String? var apiPin: String?
get() { mApiPin = mApiPin ?: loginStore.getLoginData("accountPin", null); return mApiPin } get() { mApiPin = mApiPin ?: loginStore.getLoginData("accountPin", null); return mApiPin }
set(value) { profile["accountPin"] = value; mApiPin = value } set(value) { profile?.putStudentData("accountPin", value); mApiPin = value }
/** /**
* A Synergia API access token. * A Synergia API access token.
@ -156,7 +157,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiAccessToken: String? = null private var mApiAccessToken: String? = null
var apiAccessToken: String? var apiAccessToken: String?
get() { mApiAccessToken = mApiAccessToken ?: profile?.getStudentData("accountToken", null); return mApiAccessToken } get() { mApiAccessToken = mApiAccessToken ?: profile?.getStudentData("accountToken", null); return mApiAccessToken }
set(value) { mApiAccessToken = value; profile["accountToken"] = value ?: return; } set(value) { mApiAccessToken = value; profile?.putStudentData("accountToken", value) ?: return; }
/** /**
* A Synergia API refresh token. * A Synergia API refresh token.
* Used when refreshing the [apiAccessToken] in JST, Synergia modes. * Used when refreshing the [apiAccessToken] in JST, Synergia modes.
@ -164,7 +165,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiRefreshToken: String? = null private var mApiRefreshToken: String? = null
var apiRefreshToken: String? var apiRefreshToken: String?
get() { mApiRefreshToken = mApiRefreshToken ?: profile?.getStudentData("accountRefreshToken", null); return mApiRefreshToken } get() { mApiRefreshToken = mApiRefreshToken ?: profile?.getStudentData("accountRefreshToken", null); return mApiRefreshToken }
set(value) { mApiRefreshToken = value; profile["accountRefreshToken"] = value ?: return; } set(value) { mApiRefreshToken = value; profile?.putStudentData("accountRefreshToken", value) ?: return; }
/** /**
* The expiry time for [apiAccessToken], as a UNIX timestamp. * The expiry time for [apiAccessToken], as a UNIX timestamp.
* Used when refreshing the [apiAccessToken] in JST, Synergia modes. * Used when refreshing the [apiAccessToken] in JST, Synergia modes.
@ -173,7 +174,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiTokenExpiryTime: Long? = null private var mApiTokenExpiryTime: Long? = null
var apiTokenExpiryTime: Long var apiTokenExpiryTime: Long
get() { mApiTokenExpiryTime = mApiTokenExpiryTime ?: profile?.getStudentData("accountTokenTime", 0L); return mApiTokenExpiryTime ?: 0L } get() { mApiTokenExpiryTime = mApiTokenExpiryTime ?: profile?.getStudentData("accountTokenTime", 0L); return mApiTokenExpiryTime ?: 0L }
set(value) { mApiTokenExpiryTime = value; profile["accountTokenTime"] = value; } set(value) { mApiTokenExpiryTime = value; profile?.putStudentData("accountTokenTime", value) ?: return; }
/** /**
* A push device ID, generated by Librus when registering * A push device ID, generated by Librus when registering
@ -183,7 +184,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mPushDeviceId: Int? = null private var mPushDeviceId: Int? = null
var pushDeviceId: Int var pushDeviceId: Int
get() { mPushDeviceId = mPushDeviceId ?: profile?.getStudentData("pushDeviceId", 0); return mPushDeviceId ?: 0 } get() { mPushDeviceId = mPushDeviceId ?: profile?.getStudentData("pushDeviceId", 0); return mPushDeviceId ?: 0 }
set(value) { mPushDeviceId = value; profile["pushDeviceId"] = value; } set(value) { mPushDeviceId = value; profile?.putStudentData("pushDeviceId", value) ?: return; }
/* _____ _ /* _____ _
/ ____| (_) / ____| (_)
@ -200,7 +201,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mSynergiaSessionId: String? = null private var mSynergiaSessionId: String? = null
var synergiaSessionId: String? var synergiaSessionId: String?
get() { mSynergiaSessionId = mSynergiaSessionId ?: profile?.getStudentData("accountSID", null); return mSynergiaSessionId } get() { mSynergiaSessionId = mSynergiaSessionId ?: profile?.getStudentData("accountSID", null); return mSynergiaSessionId }
set(value) { profile["accountSID"] = value; mSynergiaSessionId = value } set(value) { profile?.putStudentData("accountSID", value) ?: return; mSynergiaSessionId = value }
/** /**
* The expiry time for [synergiaSessionId], as a UNIX timestamp. * The expiry time for [synergiaSessionId], as a UNIX timestamp.
* Used in endpoints with Synergia login method. * Used in endpoints with Synergia login method.
@ -209,7 +210,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mSynergiaSessionIdExpiryTime: Long? = null private var mSynergiaSessionIdExpiryTime: Long? = null
var synergiaSessionIdExpiryTime: Long var synergiaSessionIdExpiryTime: Long
get() { mSynergiaSessionIdExpiryTime = mSynergiaSessionIdExpiryTime ?: profile?.getStudentData("accountSIDTime", 0L); return mSynergiaSessionIdExpiryTime ?: 0L } get() { mSynergiaSessionIdExpiryTime = mSynergiaSessionIdExpiryTime ?: profile?.getStudentData("accountSIDTime", 0L); return mSynergiaSessionIdExpiryTime ?: 0L }
set(value) { profile["accountSIDTime"] = value; mSynergiaSessionIdExpiryTime = value } set(value) { profile?.putStudentData("accountSIDTime", value) ?: return; mSynergiaSessionIdExpiryTime = value }
/** /**
@ -219,7 +220,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mMessagesSessionId: String? = null private var mMessagesSessionId: String? = null
var messagesSessionId: String? var messagesSessionId: String?
get() { mMessagesSessionId = mMessagesSessionId ?: profile?.getStudentData("messagesSID", null); return mMessagesSessionId } get() { mMessagesSessionId = mMessagesSessionId ?: profile?.getStudentData("messagesSID", null); return mMessagesSessionId }
set(value) { profile["messagesSID"] = value; mMessagesSessionId = value } set(value) { profile?.putStudentData("messagesSID", value) ?: return; mMessagesSessionId = value }
/** /**
* The expiry time for [messagesSessionId], as a UNIX timestamp. * The expiry time for [messagesSessionId], as a UNIX timestamp.
* Used in endpoints with Messages login method. * Used in endpoints with Messages login method.
@ -228,7 +229,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mMessagesSessionIdExpiryTime: Long? = null private var mMessagesSessionIdExpiryTime: Long? = null
var messagesSessionIdExpiryTime: Long var messagesSessionIdExpiryTime: Long
get() { mMessagesSessionIdExpiryTime = mMessagesSessionIdExpiryTime ?: profile?.getStudentData("messagesSIDTime", 0L); return mMessagesSessionIdExpiryTime ?: 0L } get() { mMessagesSessionIdExpiryTime = mMessagesSessionIdExpiryTime ?: profile?.getStudentData("messagesSIDTime", 0L); return mMessagesSessionIdExpiryTime ?: 0L }
set(value) { profile["messagesSIDTime"] = value; mMessagesSessionIdExpiryTime = value } set(value) { profile?.putStudentData("messagesSIDTime", value) ?: return; mMessagesSessionIdExpiryTime = value }
/* ____ _ _ /* ____ _ _
/ __ \| | | | / __ \| | | |
@ -238,42 +239,42 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
\____/ \__|_| |_|\___|*/ \____/ \__|_| |_|\___|*/
var isPremium var isPremium
get() = profile?.getStudentData("isPremium", false) ?: false get() = profile?.getStudentData("isPremium", false) ?: false
set(value) { profile["isPremium"] = value } set(value) { profile?.putStudentData("isPremium", value) }
private var mSchoolName: String? = null private var mSchoolName: String? = null
var schoolName: String? var schoolName: String?
get() { mSchoolName = mSchoolName ?: profile?.getStudentData("schoolName", null); return mSchoolName } get() { mSchoolName = mSchoolName ?: profile?.getStudentData("schoolName", null); return mSchoolName }
set(value) { profile["schoolName"] = value; mSchoolName = value } set(value) { profile?.putStudentData("schoolName", value) ?: return; mSchoolName = value }
private var mUnitId: Long? = null private var mUnitId: Long? = null
var unitId: Long var unitId: Long
get() { mUnitId = mUnitId ?: profile?.getStudentData("unitId", 0L); return mUnitId ?: 0L } get() { mUnitId = mUnitId ?: profile?.getStudentData("unitId", 0L); return mUnitId ?: 0L }
set(value) { profile["unitId"] = value; mUnitId = value } set(value) { profile?.putStudentData("unitId", value) ?: return; mUnitId = value }
private var mStartPointsSemester1: Int? = null private var mStartPointsSemester1: Int? = null
var startPointsSemester1: Int var startPointsSemester1: Int
get() { mStartPointsSemester1 = mStartPointsSemester1 ?: profile?.getStudentData("startPointsSemester1", 0); return mStartPointsSemester1 ?: 0 } get() { mStartPointsSemester1 = mStartPointsSemester1 ?: profile?.getStudentData("startPointsSemester1", 0); return mStartPointsSemester1 ?: 0 }
set(value) { profile["startPointsSemester1"] = value; mStartPointsSemester1 = value } set(value) { profile?.putStudentData("startPointsSemester1", value) ?: return; mStartPointsSemester1 = value }
private var mStartPointsSemester2: Int? = null private var mStartPointsSemester2: Int? = null
var startPointsSemester2: Int var startPointsSemester2: Int
get() { mStartPointsSemester2 = mStartPointsSemester2 ?: profile?.getStudentData("startPointsSemester2", 0); return mStartPointsSemester2 ?: 0 } get() { mStartPointsSemester2 = mStartPointsSemester2 ?: profile?.getStudentData("startPointsSemester2", 0); return mStartPointsSemester2 ?: 0 }
set(value) { profile["startPointsSemester2"] = value; mStartPointsSemester2 = value } set(value) { profile?.putStudentData("startPointsSemester2", value) ?: return; mStartPointsSemester2 = value }
private var mEnablePointGrades: Boolean? = null private var mEnablePointGrades: Boolean? = null
var enablePointGrades: Boolean var enablePointGrades: Boolean
get() { mEnablePointGrades = mEnablePointGrades ?: profile?.getStudentData("enablePointGrades", true); return mEnablePointGrades ?: true } get() { mEnablePointGrades = mEnablePointGrades ?: profile?.getStudentData("enablePointGrades", true); return mEnablePointGrades ?: true }
set(value) { profile["enablePointGrades"] = value; mEnablePointGrades = value } set(value) { profile?.putStudentData("enablePointGrades", value) ?: return; mEnablePointGrades = value }
private var mEnableDescriptiveGrades: Boolean? = null private var mEnableDescriptiveGrades: Boolean? = null
var enableDescriptiveGrades: Boolean var enableDescriptiveGrades: Boolean
get() { mEnableDescriptiveGrades = mEnableDescriptiveGrades ?: profile?.getStudentData("enableDescriptiveGrades", true); return mEnableDescriptiveGrades ?: true } get() { mEnableDescriptiveGrades = mEnableDescriptiveGrades ?: profile?.getStudentData("enableDescriptiveGrades", true); return mEnableDescriptiveGrades ?: true }
set(value) { profile["enableDescriptiveGrades"] = value; mEnableDescriptiveGrades = value } set(value) { profile?.putStudentData("enableDescriptiveGrades", value) ?: return; mEnableDescriptiveGrades = value }
private var mTimetableNotPublic: Boolean? = null private var mTimetableNotPublic: Boolean? = null
var timetableNotPublic: Boolean var timetableNotPublic: Boolean
get() { mTimetableNotPublic = mTimetableNotPublic ?: profile?.getStudentData("timetableNotPublic", false); return mTimetableNotPublic ?: false } get() { mTimetableNotPublic = mTimetableNotPublic ?: profile?.getStudentData("timetableNotPublic", false); return mTimetableNotPublic ?: false }
set(value) { profile["timetableNotPublic"] = value; mTimetableNotPublic = value } set(value) { profile?.putStudentData("timetableNotPublic", value) ?: return; mTimetableNotPublic = value }
/** /**
* Set to false when Recaptcha helper doesn't provide a working token. * Set to false when Recaptcha helper doesn't provide a working token.

View File

@ -16,7 +16,6 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.Librus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.* import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.firstlogin.LibrusFirstLogin import pl.szczodrzynski.edziennik.data.api.edziennik.librus.firstlogin.LibrusFirstLogin
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLogin import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLogin
import pl.szczodrzynski.edziennik.data.api.events.UserActionRequiredEvent
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
@ -24,8 +23,6 @@ import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.enums.FeatureType
import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.EventFull import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull
@ -60,19 +57,19 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_| |_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
__/ | __/ |
|__*/ |__*/
override fun sync(featureTypes: Set<FeatureType>?, onlyEndpoints: Set<Int>?, arguments: JsonObject?) { override fun sync(featureIds: List<Int>, viewId: Int?, onlyEndpoints: List<Int>?, arguments: JsonObject?) {
data.arguments = arguments data.arguments = arguments
data.prepare(LibrusFeatures, featureTypes, onlyEndpoints) data.prepare(librusLoginMethods, LibrusFeatures, featureIds, viewId, onlyEndpoints)
login() login()
} }
private fun login(loginMethod: LoginMethod? = null, afterLogin: (() -> Unit)? = null) { private fun login(loginMethodId: Int? = null, afterLogin: (() -> Unit)? = null) {
d(TAG, "Trying to login with ${data.targetLoginMethods}") d(TAG, "Trying to login with ${data.targetLoginMethodIds}")
if (internalErrorList.isNotEmpty()) { if (internalErrorList.isNotEmpty()) {
d(TAG, " - Internal errors:") d(TAG, " - Internal errors:")
internalErrorList.forEach { d(TAG, " - code $it") } internalErrorList.forEach { d(TAG, " - code $it") }
} }
loginMethod?.let { data.prepareFor(it) } loginMethodId?.let { data.prepareFor(librusLoginMethods, it) }
afterLogin?.let { this.afterLogin = it } afterLogin?.let { this.afterLogin = it }
LibrusLogin(data) { LibrusLogin(data) {
data() data()
@ -80,7 +77,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
} }
private fun data() { private fun data() {
d(TAG, "Endpoint IDs: ${data.targetEndpoints}") d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
if (internalErrorList.isNotEmpty()) { if (internalErrorList.isNotEmpty()) {
d(TAG, " - Internal errors:") d(TAG, " - Internal errors:")
internalErrorList.forEach { d(TAG, " - code $it") } internalErrorList.forEach { d(TAG, " - code $it") }
@ -91,14 +88,14 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
} }
override fun getMessage(message: MessageFull) { override fun getMessage(message: MessageFull) {
login(LoginMethod.LIBRUS_MESSAGES) { login(LOGIN_METHOD_LIBRUS_MESSAGES) {
if (data.messagesLoginSuccessful) LibrusMessagesGetMessage(data, message) { completed() } if (data.messagesLoginSuccessful) LibrusMessagesGetMessage(data, message) { completed() }
else LibrusSynergiaGetMessage(data, message) { completed() } else LibrusSynergiaGetMessage(data, message) { completed() }
} }
} }
override fun sendMessage(recipients: Set<Teacher>, subject: String, text: String) { override fun sendMessage(recipients: List<Teacher>, subject: String, text: String) {
login(LoginMethod.LIBRUS_MESSAGES) { login(LOGIN_METHOD_LIBRUS_MESSAGES) {
LibrusMessagesSendMessage(data, recipients, subject, text) { LibrusMessagesSendMessage(data, recipients, subject, text) {
completed() completed()
} }
@ -106,7 +103,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
} }
override fun markAllAnnouncementsAsRead() { override fun markAllAnnouncementsAsRead() {
login(LoginMethod.LIBRUS_SYNERGIA) { login(LOGIN_METHOD_LIBRUS_SYNERGIA) {
LibrusSynergiaMarkAllAnnouncementsAsRead(data) { LibrusSynergiaMarkAllAnnouncementsAsRead(data) {
completed() completed()
} }
@ -114,7 +111,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
} }
override fun getAnnouncement(announcement: AnnouncementFull) { override fun getAnnouncement(announcement: AnnouncementFull) {
login(LoginMethod.LIBRUS_API) { login(LOGIN_METHOD_LIBRUS_API) {
LibrusApiAnnouncementMarkAsRead(data, announcement) { LibrusApiAnnouncementMarkAsRead(data, announcement) {
completed() completed()
} }
@ -124,13 +121,13 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) { override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) {
when (owner) { when (owner) {
is Message -> { is Message -> {
login(LoginMethod.LIBRUS_SYNERGIA) { login(LOGIN_METHOD_LIBRUS_SYNERGIA) {
if (data.messagesLoginSuccessful) LibrusMessagesGetAttachment(data, owner, attachmentId, attachmentName) { completed() } if (data.messagesLoginSuccessful) LibrusMessagesGetAttachment(data, owner, attachmentId, attachmentName) { completed() }
LibrusSynergiaGetAttachment(data, owner, attachmentId, attachmentName) { completed() } LibrusSynergiaGetAttachment(data, owner, attachmentId, attachmentName) { completed() }
} }
} }
is EventFull -> { is EventFull -> {
login(LoginMethod.LIBRUS_SYNERGIA) { login(LOGIN_METHOD_LIBRUS_SYNERGIA) {
LibrusSynergiaHomeworkGetAttachment(data, owner, attachmentId, attachmentName) { LibrusSynergiaHomeworkGetAttachment(data, owner, attachmentId, attachmentName) {
completed() completed()
} }
@ -141,7 +138,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
} }
override fun getRecipientList() { override fun getRecipientList() {
login(LoginMethod.LIBRUS_MESSAGES) { login(LOGIN_METHOD_LIBRUS_MESSAGES) {
LibrusMessagesGetRecipientList(data) { LibrusMessagesGetRecipientList(data) {
completed() completed()
} }
@ -149,7 +146,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
} }
override fun getEvent(eventFull: EventFull) { override fun getEvent(eventFull: EventFull) {
login(LoginMethod.LIBRUS_SYNERGIA) { login(LOGIN_METHOD_LIBRUS_SYNERGIA) {
LibrusSynergiaGetHomework(data, eventFull) { LibrusSynergiaGetHomework(data, eventFull) {
completed() completed()
} }
@ -165,7 +162,6 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback { private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {
return object : EdziennikCallback { return object : EdziennikCallback {
override fun onCompleted() { callback.onCompleted() } override fun onCompleted() { callback.onCompleted() }
override fun onRequiresUserAction(event: UserActionRequiredEvent) { callback.onRequiresUserAction(event) }
override fun onProgress(step: Float) { callback.onProgress(step) } override fun onProgress(step: Float) { callback.onProgress(step) }
override fun onStartProgress(stringRes: Int) { callback.onStartProgress(stringRes) } override fun onStartProgress(stringRes: Int) { callback.onStartProgress(stringRes) }
override fun onError(apiError: ApiError) { override fun onError(apiError: ApiError) {
@ -177,27 +173,27 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
internalErrorList.add(apiError.errorCode) internalErrorList.add(apiError.errorCode)
when (apiError.errorCode) { when (apiError.errorCode) {
ERROR_LIBRUS_PORTAL_ACCESS_DENIED -> { ERROR_LIBRUS_PORTAL_ACCESS_DENIED -> {
data.loginMethods.remove(LoginMethod.LIBRUS_PORTAL) data.loginMethods.remove(LOGIN_METHOD_LIBRUS_PORTAL)
data.prepareFor(LoginMethod.LIBRUS_PORTAL) data.prepareFor(librusLoginMethods, LOGIN_METHOD_LIBRUS_PORTAL)
data.portalTokenExpiryTime = 0 data.portalTokenExpiryTime = 0
login() login()
} }
ERROR_LIBRUS_API_ACCESS_DENIED, ERROR_LIBRUS_API_ACCESS_DENIED,
ERROR_LIBRUS_API_TOKEN_EXPIRED -> { ERROR_LIBRUS_API_TOKEN_EXPIRED -> {
data.loginMethods.remove(LoginMethod.LIBRUS_API) data.loginMethods.remove(LOGIN_METHOD_LIBRUS_API)
data.prepareFor(LoginMethod.LIBRUS_API) data.prepareFor(librusLoginMethods, LOGIN_METHOD_LIBRUS_API)
data.apiTokenExpiryTime = 0 data.apiTokenExpiryTime = 0
login() login()
} }
ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED -> { ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED -> {
data.loginMethods.remove(LoginMethod.LIBRUS_SYNERGIA) data.loginMethods.remove(LOGIN_METHOD_LIBRUS_SYNERGIA)
data.prepareFor(LoginMethod.LIBRUS_SYNERGIA) data.prepareFor(librusLoginMethods, LOGIN_METHOD_LIBRUS_SYNERGIA)
data.synergiaSessionIdExpiryTime = 0 data.synergiaSessionIdExpiryTime = 0
login() login()
} }
ERROR_LIBRUS_MESSAGES_ACCESS_DENIED -> { ERROR_LIBRUS_MESSAGES_ACCESS_DENIED -> {
data.loginMethods.remove(LoginMethod.LIBRUS_MESSAGES) data.loginMethods.remove(LOGIN_METHOD_LIBRUS_MESSAGES)
data.prepareFor(LoginMethod.LIBRUS_MESSAGES) data.prepareFor(librusLoginMethods, LOGIN_METHOD_LIBRUS_MESSAGES)
data.messagesSessionIdExpiryTime = 0 data.messagesSessionIdExpiryTime = 0
login() login()
} }

View File

@ -4,10 +4,8 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.librus package pl.szczodrzynski.edziennik.data.api.edziennik.librus
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.models.Feature import pl.szczodrzynski.edziennik.data.api.models.Feature
import pl.szczodrzynski.edziennik.data.db.enums.FeatureType
import pl.szczodrzynski.edziennik.data.db.enums.LoginMethod
import pl.szczodrzynski.edziennik.data.db.enums.LoginType
const val ENDPOINT_LIBRUS_API_ME = 1001 const val ENDPOINT_LIBRUS_API_ME = 1001
const val ENDPOINT_LIBRUS_API_SCHOOLS = 1002 const val ENDPOINT_LIBRUS_API_SCHOOLS = 1002
@ -60,14 +58,14 @@ const val ENDPOINT_LIBRUS_MESSAGES_TRASH = 3030
val LibrusFeatures = listOf( val LibrusFeatures = listOf(
Feature(LoginType.LIBRUS, FeatureType.ALWAYS_NEEDED, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_ALWAYS_NEEDED, listOf(
ENDPOINT_LIBRUS_API_LESSONS to LoginMethod.LIBRUS_API ENDPOINT_LIBRUS_API_LESSONS to LOGIN_METHOD_LIBRUS_API
)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
// push config // push config
Feature(LoginType.LIBRUS, FeatureType.PUSH_CONFIG, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_PUSH_CONFIG, listOf(
ENDPOINT_LIBRUS_API_PUSH_CONFIG to LoginMethod.LIBRUS_API ENDPOINT_LIBRUS_API_PUSH_CONFIG to LOGIN_METHOD_LIBRUS_API
)).withShouldSync { data -> ), listOf(LOGIN_METHOD_LIBRUS_API)).withShouldSync { data ->
(data as DataLibrus).isPremium && !data.app.config.sync.tokenLibrusList.contains(data.profileId) (data as DataLibrus).isPremium && !data.app.config.sync.tokenLibrusList.contains(data.profileId)
}, },
@ -78,72 +76,72 @@ val LibrusFeatures = listOf(
/** /**
* Timetable - using API. * Timetable - using API.
*/ */
Feature(LoginType.LIBRUS, FeatureType.TIMETABLE, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_TIMETABLE, listOf(
ENDPOINT_LIBRUS_API_TIMETABLES to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_TIMETABLES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_SUBSTITUTIONS to LoginMethod.LIBRUS_API ENDPOINT_LIBRUS_API_SUBSTITUTIONS to LOGIN_METHOD_LIBRUS_API
)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
/** /**
* Agenda - using API. * Agenda - using API.
* Events, Parent-teacher meetings, free days (teacher/school/class). * Events, Parent-teacher meetings, free days (teacher/school/class).
*/ */
Feature(LoginType.LIBRUS, FeatureType.AGENDA, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_AGENDA, listOf(
ENDPOINT_LIBRUS_API_EVENTS to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_EVENTS to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_EVENT_TYPES to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_EVENT_TYPES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_PT_MEETINGS to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_PT_MEETINGS to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_TEACHER_FREE_DAY_TYPES to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_TEACHER_FREE_DAY_TYPES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_SCHOOL_FREE_DAYS to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_SCHOOL_FREE_DAYS to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS to LoginMethod.LIBRUS_API ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS to LOGIN_METHOD_LIBRUS_API
)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
/** /**
* Grades - using API. * Grades - using API.
* All grades + categories. * All grades + categories.
*/ */
Feature(LoginType.LIBRUS, FeatureType.GRADES, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf(
ENDPOINT_LIBRUS_API_NORMAL_GRADE_CATEGORIES to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_NORMAL_GRADE_CATEGORIES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_POINT_GRADE_CATEGORIES to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_POINT_GRADE_CATEGORIES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADE_CATEGORIES to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADE_CATEGORIES to LOGIN_METHOD_LIBRUS_API,
// Commented out, because TextGrades/Categories is the same as Grades/Categories // Commented out, because TextGrades/Categories is the same as Grades/Categories
/* ENDPOINT_LIBRUS_API_TEXT_GRADE_CATEGORIES to LoginMethod.LIBRUS_API, */ /* ENDPOINT_LIBRUS_API_TEXT_GRADE_CATEGORIES to LOGIN_METHOD_LIBRUS_API, */
ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADE_CATEGORIES to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADE_CATEGORIES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_CATEGORIES to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_CATEGORIES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_COMMENTS to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_COMMENTS to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_NORMAL_GRADES to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_NORMAL_GRADES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_POINT_GRADES to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_POINT_GRADES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_TEXT_GRADES to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_TEXT_GRADES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADES to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES to LoginMethod.LIBRUS_API ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES to LOGIN_METHOD_LIBRUS_API
)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
/** /**
* Homework - using API. * Homework - using API.
* Sync only if account has premium access. * Sync only if account has premium access.
*/ */
/*Feature(LoginType.LIBRUS, FeatureType.HOMEWORK, listOf( /*Feature(LOGIN_TYPE_LIBRUS, FEATURE_HOMEWORK, listOf(
ENDPOINT_LIBRUS_API_HOMEWORK to LoginMethod.LIBRUS_API ENDPOINT_LIBRUS_API_HOMEWORK to LOGIN_METHOD_LIBRUS_API
)).withShouldSync { data -> ), listOf(LOGIN_METHOD_LIBRUS_API)).withShouldSync { data ->
(data as DataLibrus).isPremium (data as DataLibrus).isPremium
},*/ },*/
/** /**
* Behaviour - using API. * Behaviour - using API.
*/ */
Feature(LoginType.LIBRUS, FeatureType.BEHAVIOUR, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_BEHAVIOUR, listOf(
ENDPOINT_LIBRUS_API_NOTICES to LoginMethod.LIBRUS_API ENDPOINT_LIBRUS_API_NOTICES to LOGIN_METHOD_LIBRUS_API
)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
/** /**
* Attendance - using API. * Attendance - using API.
*/ */
Feature(LoginType.LIBRUS, FeatureType.ATTENDANCE, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_ATTENDANCE, listOf(
ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_ATTENDANCES to LoginMethod.LIBRUS_API ENDPOINT_LIBRUS_API_ATTENDANCES to LOGIN_METHOD_LIBRUS_API
)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
/** /**
* Announcements - using API. * Announcements - using API.
*/ */
Feature(LoginType.LIBRUS, FeatureType.ANNOUNCEMENTS, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_ANNOUNCEMENTS, listOf(
ENDPOINT_LIBRUS_API_ANNOUNCEMENTS to LoginMethod.LIBRUS_API ENDPOINT_LIBRUS_API_ANNOUNCEMENTS to LOGIN_METHOD_LIBRUS_API
)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
@ -152,99 +150,99 @@ val LibrusFeatures = listOf(
/** /**
* Student info - using API. * Student info - using API.
*/ */
Feature(LoginType.LIBRUS, FeatureType.STUDENT_INFO, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_INFO, listOf(
ENDPOINT_LIBRUS_API_ME to LoginMethod.LIBRUS_API ENDPOINT_LIBRUS_API_ME to LOGIN_METHOD_LIBRUS_API
)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
/** /**
* School info - using API. * School info - using API.
*/ */
Feature(LoginType.LIBRUS, FeatureType.SCHOOL_INFO, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_SCHOOL_INFO, listOf(
ENDPOINT_LIBRUS_API_SCHOOLS to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_SCHOOLS to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_UNITS to LoginMethod.LIBRUS_API ENDPOINT_LIBRUS_API_UNITS to LOGIN_METHOD_LIBRUS_API
)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
/** /**
* Class info - using API. * Class info - using API.
*/ */
Feature(LoginType.LIBRUS, FeatureType.CLASS_INFO, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_CLASS_INFO, listOf(
ENDPOINT_LIBRUS_API_CLASSES to LoginMethod.LIBRUS_API ENDPOINT_LIBRUS_API_CLASSES to LOGIN_METHOD_LIBRUS_API
)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
/** /**
* Team info - using API. * Team info - using API.
*/ */
Feature(LoginType.LIBRUS, FeatureType.TEAM_INFO, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_TEAM_INFO, listOf(
ENDPOINT_LIBRUS_API_VIRTUAL_CLASSES to LoginMethod.LIBRUS_API ENDPOINT_LIBRUS_API_VIRTUAL_CLASSES to LOGIN_METHOD_LIBRUS_API
)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
/** /**
* Lucky number - using API. * Lucky number - using API.
*/ */
Feature(LoginType.LIBRUS, FeatureType.LUCKY_NUMBER, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_LUCKY_NUMBER, listOf(
ENDPOINT_LIBRUS_API_LUCKY_NUMBER to LoginMethod.LIBRUS_API ENDPOINT_LIBRUS_API_LUCKY_NUMBER to LOGIN_METHOD_LIBRUS_API
)).withShouldSync { data -> data.shouldSyncLuckyNumber() }, ), listOf(LOGIN_METHOD_LIBRUS_API)).withShouldSync { data -> data.shouldSyncLuckyNumber() },
/** /**
* Teacher list - using API. * Teacher list - using API.
*/ */
Feature(LoginType.LIBRUS, FeatureType.TEACHERS, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_TEACHERS, listOf(
ENDPOINT_LIBRUS_API_USERS to LoginMethod.LIBRUS_API ENDPOINT_LIBRUS_API_USERS to LOGIN_METHOD_LIBRUS_API
)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
/** /**
* Subject list - using API. * Subject list - using API.
*/ */
Feature(LoginType.LIBRUS, FeatureType.SUBJECTS, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_SUBJECTS, listOf(
ENDPOINT_LIBRUS_API_SUBJECTS to LoginMethod.LIBRUS_API ENDPOINT_LIBRUS_API_SUBJECTS to LOGIN_METHOD_LIBRUS_API
)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
/** /**
* Classroom list - using API. * Classroom list - using API.
*/ */
Feature(LoginType.LIBRUS, FeatureType.CLASSROOMS, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_CLASSROOMS, listOf(
ENDPOINT_LIBRUS_API_CLASSROOMS to LoginMethod.LIBRUS_API ENDPOINT_LIBRUS_API_CLASSROOMS to LOGIN_METHOD_LIBRUS_API
)), ), listOf(LOGIN_METHOD_LIBRUS_API)),
/** /**
* Student info - using synergia scrapper. * Student info - using synergia scrapper.
*/ */
Feature(LoginType.LIBRUS, FeatureType.STUDENT_INFO, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_INFO, listOf(
ENDPOINT_LIBRUS_SYNERGIA_INFO to LoginMethod.LIBRUS_SYNERGIA ENDPOINT_LIBRUS_SYNERGIA_INFO to LOGIN_METHOD_LIBRUS_SYNERGIA
)), ), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)),
/** /**
* Student number - using synergia scrapper. * Student number - using synergia scrapper.
*/ */
Feature(LoginType.LIBRUS, FeatureType.STUDENT_NUMBER, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_STUDENT_NUMBER, listOf(
ENDPOINT_LIBRUS_SYNERGIA_INFO to LoginMethod.LIBRUS_SYNERGIA ENDPOINT_LIBRUS_SYNERGIA_INFO to LOGIN_METHOD_LIBRUS_SYNERGIA
)), ), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)),
/** /**
* Grades - using API + synergia scrapper. * Grades - using API + synergia scrapper.
*/ */
/*Feature(LoginType.LIBRUS, FeatureType.GRADES, listOf( /*Feature(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf(
ENDPOINT_LIBRUS_API_NORMAL_GC to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_NORMAL_GC to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_NORMAL_GRADES to LoginMethod.LIBRUS_API, ENDPOINT_LIBRUS_API_NORMAL_GRADES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_SYNERGIA_GRADES to LoginMethod.LIBRUS_SYNERGIA ENDPOINT_LIBRUS_SYNERGIA_GRADES to LOGIN_METHOD_LIBRUS_SYNERGIA
)),*/ ), listOf(LOGIN_METHOD_LIBRUS_API, LOGIN_METHOD_LIBRUS_SYNERGIA)),*/
/*Endpoint(LoginType.LIBRUS, FeatureType.GRADES, listOf( /*Endpoint(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf(
ENDPOINT_LIBRUS_SYNERGIA_GRADES to LoginMethod.LIBRUS_SYNERGIA ENDPOINT_LIBRUS_SYNERGIA_GRADES to LOGIN_METHOD_LIBRUS_SYNERGIA
)),*/ ), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)),*/
/** /**
* Homework - using scrapper. * Homework - using scrapper.
* Sync only if account has not premium access. * Sync only if account has not premium access.
*/ */
Feature(LoginType.LIBRUS, FeatureType.HOMEWORK, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_HOMEWORK, listOf(
ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK to LoginMethod.LIBRUS_SYNERGIA ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK to LOGIN_METHOD_LIBRUS_SYNERGIA
))/*.withShouldSync { data -> ), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA))/*.withShouldSync { data ->
!(data as DataLibrus).isPremium !(data as DataLibrus).isPremium
}*/, }*/,
/** /**
* Messages inbox - using messages website. * Messages inbox - using messages website.
*/ */
Feature(LoginType.LIBRUS, FeatureType.MESSAGES_INBOX, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_MESSAGES_INBOX, listOf(
ENDPOINT_LIBRUS_MESSAGES_RECEIVED to LoginMethod.LIBRUS_MESSAGES ENDPOINT_LIBRUS_MESSAGES_RECEIVED to LOGIN_METHOD_LIBRUS_MESSAGES
)), ), listOf(LOGIN_METHOD_LIBRUS_MESSAGES)),
/** /**
* Messages sent - using messages website. * Messages sent - using messages website.
*/ */
Feature(LoginType.LIBRUS, FeatureType.MESSAGES_SENT, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_MESSAGES_SENT, listOf(
ENDPOINT_LIBRUS_MESSAGES_SENT to LoginMethod.LIBRUS_MESSAGES ENDPOINT_LIBRUS_MESSAGES_SENT to LOGIN_METHOD_LIBRUS_MESSAGES
)) ), listOf(LOGIN_METHOD_LIBRUS_MESSAGES))
) )

View File

@ -10,7 +10,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.ext.startCoroutineTimer import pl.szczodrzynski.edziennik.startCoroutineTimer
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
class LibrusRecaptchaHelper( class LibrusRecaptchaHelper(

View File

@ -11,7 +11,7 @@ import im.wangchao.mhttp.callback.JsonCallbackHandler
import pl.szczodrzynski.edziennik.data.api.* import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.ext.getString import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
import java.net.HttpURLConnection.* import java.net.HttpURLConnection.*

View File

@ -24,7 +24,7 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
} }
private fun nextEndpoint(onSuccess: () -> Unit) { private fun nextEndpoint(onSuccess: () -> Unit) {
if (data.targetEndpoints.isEmpty()) { if (data.targetEndpointIds.isEmpty()) {
onSuccess() onSuccess()
return return
} }
@ -32,8 +32,8 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
onSuccess() onSuccess()
return return
} }
val id = data.targetEndpoints.firstKey() val id = data.targetEndpointIds.firstKey()
val lastSync = data.targetEndpoints.remove(id) val lastSync = data.targetEndpointIds.remove(id)
useEndpoint(id, lastSync) { endpointId -> useEndpoint(id, lastSync) { endpointId ->
data.progress(data.progressStep) data.progress(data.progressStep)
nextEndpoint(onSuccess) nextEndpoint(onSuccess)
@ -182,6 +182,10 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
data.startProgress(R.string.edziennik_progress_endpoint_pt_meetings) data.startProgress(R.string.edziennik_progress_endpoint_pt_meetings)
LibrusApiPtMeetings(data, lastSync, onSuccess) LibrusApiPtMeetings(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_TEACHER_FREE_DAY_TYPES -> {
data.startProgress(R.string.edziennik_progress_endpoint_teacher_free_day_types)
LibrusApiTeacherFreeDayTypes(data, lastSync, onSuccess)
}
ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS -> { ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS -> {
data.startProgress(R.string.edziennik_progress_endpoint_teacher_free_days) data.startProgress(R.string.edziennik_progress_endpoint_teacher_free_days)
LibrusApiTeacherFreeDays(data, lastSync, onSuccess) LibrusApiTeacherFreeDays(data, lastSync, onSuccess)

View File

@ -7,7 +7,7 @@ import im.wangchao.mhttp.callback.JsonCallbackHandler
import pl.szczodrzynski.edziennik.data.api.* import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.ext.getString import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
import java.net.HttpURLConnection import java.net.HttpURLConnection

View File

@ -12,7 +12,6 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.api.events.AnnouncementGetEvent import pl.szczodrzynski.edziennik.data.api.events.AnnouncementGetEvent
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
class LibrusApiAnnouncementMarkAsRead(override val data: DataLibrus, class LibrusApiAnnouncementMarkAsRead(override val data: DataLibrus,
@ -35,7 +34,7 @@ class LibrusApiAnnouncementMarkAsRead(override val data: DataLibrus,
data.setSeenMetadataList.add(Metadata( data.setSeenMetadataList.add(Metadata(
profileId, profileId,
MetadataType.ANNOUNCEMENT, Metadata.TYPE_ANNOUNCEMENT,
announcement.id, announcement.id,
announcement.seen, announcement.seen,
announcement.notified announcement.notified

View File

@ -11,8 +11,6 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.entity.Announcement import pl.szczodrzynski.edziennik.data.db.entity.Announcement
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class LibrusApiAnnouncements(override val data: DataLibrus, class LibrusApiAnnouncements(override val data: DataLibrus,
@ -55,7 +53,7 @@ class LibrusApiAnnouncements(override val data: DataLibrus,
data.announcementList.add(announcementObject) data.announcementList.add(announcementObject)
data.setSeenMetadataList.add(Metadata( data.setSeenMetadataList.add(Metadata(
profileId, profileId,
MetadataType.ANNOUNCEMENT, Metadata.TYPE_ANNOUNCEMENT,
id, id,
read, read,
profile.empty || read profile.empty || read

View File

@ -11,7 +11,6 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.entity.Attendance import pl.szczodrzynski.edziennik.data.db.entity.Attendance
import pl.szczodrzynski.edziennik.data.db.entity.AttendanceType import pl.szczodrzynski.edziennik.data.db.entity.AttendanceType
import pl.szczodrzynski.edziennik.ext.*
class LibrusApiAttendanceTypes(override val data: DataLibrus, class LibrusApiAttendanceTypes(override val data: DataLibrus,
override val lastSync: Long?, override val lastSync: Long?,

View File

@ -12,8 +12,6 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.entity.Attendance import pl.szczodrzynski.edziennik.data.db.entity.Attendance
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class LibrusApiAttendances(override val data: DataLibrus, class LibrusApiAttendances(override val data: DataLibrus,
@ -77,7 +75,7 @@ class LibrusApiAttendances(override val data: DataLibrus,
if(type?.baseType != Attendance.TYPE_PRESENT) { if(type?.baseType != Attendance.TYPE_PRESENT) {
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
profileId, profileId,
MetadataType.ATTENDANCE, Metadata.TYPE_ATTENDANCE,
id, id,
profile?.empty ?: false || type?.baseType == Attendance.TYPE_PRESENT_CUSTOM || type?.baseType == Attendance.TYPE_UNKNOWN, profile?.empty ?: false || type?.baseType == Attendance.TYPE_PRESENT_CUSTOM || type?.baseType == Attendance.TYPE_UNKNOWN,
profile?.empty ?: false || type?.baseType == Attendance.TYPE_PRESENT_CUSTOM || type?.baseType == Attendance.TYPE_UNKNOWN profile?.empty ?: false || type?.baseType == Attendance.TYPE_PRESENT_CUSTOM || type?.baseType == Attendance.TYPE_UNKNOWN

View File

@ -10,7 +10,6 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_CATEGORIES import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_CATEGORIES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.ext.*
class LibrusApiBehaviourGradeCategories(override val data: DataLibrus, class LibrusApiBehaviourGradeCategories(override val data: DataLibrus,
override val lastSync: Long?, override val lastSync: Long?,

View File

@ -10,7 +10,6 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.ext.*
class LibrusApiBehaviourGradeComments(override val data: DataLibrus, class LibrusApiBehaviourGradeComments(override val data: DataLibrus,
override val lastSync: Long?, override val lastSync: Long?,

View File

@ -14,8 +14,6 @@ import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_POINT_SUM
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import java.text.DecimalFormat import java.text.DecimalFormat
@ -64,7 +62,7 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
data.gradeList.add(semester1StartGradeObject) data.gradeList.add(semester1StartGradeObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
profileId, profileId,
MetadataType.GRADE, Metadata.TYPE_GRADE,
semester1StartGradeObject.id, semester1StartGradeObject.id,
true, true,
true true
@ -92,7 +90,7 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
data.gradeList.add(semester2StartGradeObject) data.gradeList.add(semester2StartGradeObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
profileId, profileId,
MetadataType.GRADE, Metadata.TYPE_GRADE,
semester2StartGradeObject.id, semester2StartGradeObject.id,
true, true,
true true
@ -166,7 +164,7 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
data.gradeList.add(gradeObject) data.gradeList.add(gradeObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
profileId, profileId,
MetadataType.GRADE, Metadata.TYPE_GRADE,
id, id,
profile.empty, profile.empty,
profile.empty profile.empty

View File

@ -4,14 +4,14 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import pl.szczodrzynski.edziennik.DAY
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_CLASSES import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_CLASSES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.entity.Team import pl.szczodrzynski.edziennik.data.db.entity.Team
import pl.szczodrzynski.edziennik.ext.DAY import pl.szczodrzynski.edziennik.getJsonObject
import pl.szczodrzynski.edziennik.ext.getJsonObject import pl.szczodrzynski.edziennik.getLong
import pl.szczodrzynski.edziennik.ext.getLong import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.ext.getString
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class LibrusApiClasses(override val data: DataLibrus, class LibrusApiClasses(override val data: DataLibrus,

View File

@ -9,7 +9,6 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_CLASSROOMS import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_CLASSROOMS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.entity.Classroom import pl.szczodrzynski.edziennik.data.db.entity.Classroom
import pl.szczodrzynski.edziennik.ext.*
import java.util.* import java.util.*
class LibrusApiClassrooms(override val data: DataLibrus, class LibrusApiClassrooms(override val data: DataLibrus,
@ -26,8 +25,8 @@ class LibrusApiClassrooms(override val data: DataLibrus,
classrooms?.forEach { classroom -> classrooms?.forEach { classroom ->
val id = classroom.getLong("Id") ?: return@forEach val id = classroom.getLong("Id") ?: return@forEach
val name = classroom.getString("Name")?.lowercase() ?: "" val name = classroom.getString("Name")?.toLowerCase(Locale.getDefault()) ?: ""
val symbol = classroom.getString("Symbol")?.lowercase() ?: "" val symbol = classroom.getString("Symbol")?.toLowerCase(Locale.getDefault()) ?: ""
val nameShort = name.fixWhiteSpaces().split(" ").onEach { it[0] }.joinToString() val nameShort = name.fixWhiteSpaces().split(" ").onEach { it[0] }.joinToString()
val symbolParts = symbol.fixWhiteSpaces().split(" ") val symbolParts = symbol.fixWhiteSpaces().split(" ")

View File

@ -10,7 +10,6 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADE_CATEGORIES import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADE_CATEGORIES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.ext.*
class LibrusApiDescriptiveGradeCategories(override val data: DataLibrus, class LibrusApiDescriptiveGradeCategories(override val data: DataLibrus,
override val lastSync: Long?, override val lastSync: Long?,

View File

@ -15,8 +15,6 @@ import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_TEXT
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class LibrusApiDescriptiveGrades(override val data: DataLibrus, class LibrusApiDescriptiveGrades(override val data: DataLibrus,
@ -74,7 +72,7 @@ class LibrusApiDescriptiveGrades(override val data: DataLibrus,
data.gradeList.add(gradeObject) data.gradeList.add(gradeObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
profileId, profileId,
MetadataType.GRADE, Metadata.TYPE_GRADE,
id, id,
profile.empty, profile.empty,
profile.empty profile.empty

View File

@ -9,7 +9,6 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_EVENT_TYPES import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_EVENT_TYPES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.entity.EventType import pl.szczodrzynski.edziennik.data.db.entity.EventType
import pl.szczodrzynski.edziennik.ext.*
class LibrusApiEventTypes(override val data: DataLibrus, class LibrusApiEventTypes(override val data: DataLibrus,
override val lastSync: Long?, override val lastSync: Long?,

View File

@ -13,8 +13,6 @@ import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Event import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
@ -72,7 +70,7 @@ class LibrusApiEvents(override val data: DataLibrus,
data.metadataList.add( data.metadataList.add(
Metadata( Metadata(
profileId, profileId,
MetadataType.EVENT, Metadata.TYPE_EVENT,
id, id,
profile?.empty ?: false, profile?.empty ?: false,
profile?.empty ?: false profile?.empty ?: false

View File

@ -11,7 +11,6 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.ext.*
class LibrusApiGradeCategories(override val data: DataLibrus, class LibrusApiGradeCategories(override val data: DataLibrus,
override val lastSync: Long?, override val lastSync: Long?,

View File

@ -4,12 +4,12 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.ext.*
class LibrusApiGradeComments(override val data: DataLibrus, class LibrusApiGradeComments(override val data: DataLibrus,
override val lastSync: Long?, override val lastSync: Long?,

View File

@ -16,8 +16,6 @@ import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_PROPO
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.enums.MetadataType
import pl.szczodrzynski.edziennik.ext.*
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
@ -98,7 +96,7 @@ class LibrusApiGrades(override val data: DataLibrus,
data.metadataList.add( data.metadataList.add(
Metadata( Metadata(
profileId, profileId,
MetadataType.GRADE, Metadata.TYPE_GRADE,
id, id,
profile.empty, profile.empty,
profile.empty profile.empty

Some files were not shown because too many files have changed in this diff Show More