mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-06-13 14:10:46 +02:00
Compare commits
188 Commits
v3.9.14-de
...
v4.0-beta.
Author | SHA1 | Date | |
---|---|---|---|
4763033f24 | |||
3b0570d21c | |||
16bf478d1a | |||
5bf181b6d1 | |||
21b2e5d194 | |||
759afcf3ca | |||
d48c7844a4 | |||
7d8caa8df7 | |||
62f53930da | |||
9a45cbb679 | |||
8e5a10f6d8 | |||
10c57d2272 | |||
67d4d0f898 | |||
97e0d04842 | |||
3ba30ede92 | |||
1035e411ab | |||
d5ae4b7ec9 | |||
111d040cf9 | |||
8cc594d170 | |||
d8a8bed68d | |||
eedbd954bd | |||
0eb8366027 | |||
894135104b | |||
7b2e408efc | |||
e4115c122e | |||
537b16949e | |||
ca60ceb2a7 | |||
0fad12fea5 | |||
6cd2c23aac | |||
512baaa43f | |||
d097fcc973 | |||
621dbd459c | |||
840ab4b0c4 | |||
904be34a87 | |||
b7fc6fcc38 | |||
55c6e40d6d | |||
4dfb015057 | |||
e40a0ba2bb | |||
fd48f10df9 | |||
6a54e7fef7 | |||
5c4d6ed140 | |||
9ed1be3594 | |||
c5ce582678 | |||
2050083bce | |||
92e6bdb562 | |||
93e70c38b7 | |||
45b96179a5 | |||
a29a534a40 | |||
8e2297359c | |||
92ba7248ef | |||
f657d37cbd | |||
9e312f60bf | |||
85f72b78f7 | |||
40acb67ceb | |||
3ae8100bda | |||
1a3dc41edf | |||
b111d33b04 | |||
ea5720d1c8 | |||
53675122c6 | |||
4ba7997bc1 | |||
19c446d267 | |||
1abb9ac378 | |||
f9c7492726 | |||
6ece6ca52a | |||
f6a8e9d2fa | |||
878de34546 | |||
7b97ef316d | |||
aafa87c661 | |||
26eb2e4381 | |||
4b08ea7a89 | |||
7f1f2d0039 | |||
0227762ddc | |||
ff0de8afc2 | |||
ddf66ef061 | |||
52ecfba0a5 | |||
e123ff1bec | |||
45753583ee | |||
5a77c481a2 | |||
4e796542d7 | |||
ae42c227a8 | |||
fc58035bbf | |||
834c4fc5f4 | |||
33fcffd2bd | |||
18b83e2ed8 | |||
f05b39736c | |||
31a293c5c0 | |||
1e6952c86a | |||
21ad38d33f | |||
1589a05a37 | |||
3f19e5d465 | |||
5e9bd98bba | |||
d626d98421 | |||
bce74a408c | |||
30c5b2d1c9 | |||
95a150f7d8 | |||
45d31d2358 | |||
fb59dfc677 | |||
30303f50ac | |||
a2fa133831 | |||
d735dcea05 | |||
a96fcabba5 | |||
21fd59c196 | |||
15f126416f | |||
7f1f9f81a6 | |||
de6b77baba | |||
c8e3a3d258 | |||
52ef24ae7b | |||
1553173300 | |||
f5b2c24ee3 | |||
2ddbc6bbac | |||
eae7189981 | |||
f292b3637d | |||
aff0b361a2 | |||
9f78b86c57 | |||
4950627850 | |||
5265f3eb6a | |||
3cca5e8e9a | |||
e9ca109c57 | |||
344da53888 | |||
62ae3c4c4b | |||
6f95eb3c3f | |||
f350a86946 | |||
868e529e62 | |||
62d82c88a1 | |||
a86e995113 | |||
5e2c7e89ab | |||
4a38906194 | |||
cc3e6d97dd | |||
90d6fb56d1 | |||
9b5cf3f636 | |||
3723abbbbb | |||
a626427788 | |||
35d88f8c78 | |||
c65872b29b | |||
e472d34f4d | |||
1257596104 | |||
5dd6519d27 | |||
e607577407 | |||
ade12e729f | |||
eee83ebb94 | |||
39ff47e866 | |||
6c81a506e9 | |||
a24620de31 | |||
70de47408a | |||
04103d1c84 | |||
d20102c3bd | |||
f165ee32e5 | |||
60ad2e81f3 | |||
5ca8b642da | |||
f40cd7f26c | |||
e04b519e9b | |||
658e59bed6 | |||
cb5eb19abc | |||
d336531ca8 | |||
30ee71f4e3 | |||
844d5b33bc | |||
e3741f1c75 | |||
21a6e4d8c6 | |||
ec14ba76c9 | |||
90e7b1e9c7 | |||
a09d943344 | |||
52ac40c826 | |||
8f8eb64364 | |||
fe40ab0ab4 | |||
5991ef820f | |||
d67c2a90b1 | |||
e85d6fbc3b | |||
62a9604bd2 | |||
0aae2174c1 | |||
b66bd6fec9 | |||
b399a3f5ad | |||
1f5927eec0 | |||
2d838e7003 | |||
f242c30476 | |||
2cf204ff79 | |||
38fc9e97bb | |||
3e4accb82c | |||
b905283b61 | |||
c7e5df5c91 | |||
99006d7923 | |||
16320b4486 | |||
d70b0c0c3f | |||
41cebc554f | |||
92e0fc2847 | |||
9978a11c52 | |||
cf77623e9c | |||
7ce4acc687 | |||
ffbac126bd |
4
.gitignore
vendored
4
.gitignore
vendored
@ -83,3 +83,7 @@ lint/tmp/
|
||||
# lint/reports/
|
||||
|
||||
app/schemas/
|
||||
|
||||
signatures/
|
||||
|
||||
app/.cxx
|
1
.idea/.name
generated
Normal file
1
.idea/.name
generated
Normal file
@ -0,0 +1 @@
|
||||
Szkolny.eu
|
45
.idea/jarRepositories.xml
generated
Normal file
45
.idea/jarRepositories.xml
generated
Normal file
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jboss.community" />
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="maven2" />
|
||||
<option name="name" value="maven2" />
|
||||
<option name="url" value="https://kotlin.bintray.com/kotlinx/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="maven3" />
|
||||
<option name="name" value="maven3" />
|
||||
<option name="url" value="https://dl.bintray.com/wulkanowy/wulkanowy" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="BintrayJCenter" />
|
||||
<option name="name" value="BintrayJCenter" />
|
||||
<option name="url" value="https://jcenter.bintray.com/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="maven" />
|
||||
<option name="name" value="maven" />
|
||||
<option name="url" value="https://jitpack.io" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="Google" />
|
||||
<option name="name" value="Google" />
|
||||
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="MavenRepo" />
|
||||
<option name="name" value="MavenRepo" />
|
||||
<option name="url" value="https://repo.maven.apache.org/maven2/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
1
.idea/misc.xml
generated
1
.idea/misc.xml
generated
@ -11,6 +11,7 @@
|
||||
<item index="1" class="java.lang.String" itemvalue="org.greenrobot.eventbus.Subscribe" />
|
||||
</list>
|
||||
</component>
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="NullableNotNullManager">
|
||||
<option name="myDefaultNullable" value="org.jetbrains.annotations.Nullable" />
|
||||
<option name="myDefaultNotNull" value="androidx.annotation.RecentlyNonNull" />
|
||||
|
@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
|
||||
//apply plugin: 'me.tatarka.retrolambda'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
compileSdkVersion setup.compileSdk
|
||||
|
||||
android {
|
||||
lintOptions {
|
||||
@ -12,7 +12,7 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
targetSdkVersion setup.targetSdk
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
@ -43,9 +43,9 @@ android {
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
// Google libraries
|
||||
implementation "androidx.appcompat:appcompat:${androidXAppCompat}"
|
||||
implementation "androidx.recyclerview:recyclerview:${androidXRecyclerView}"
|
||||
implementation "com.google.android.material:material:${googleMaterial}"
|
||||
implementation "androidx.appcompat:appcompat:${versions.appcompat}"
|
||||
implementation "androidx.recyclerview:recyclerview:${versions.recyclerView}"
|
||||
implementation "com.google.android.material:material:${versions.material}"
|
||||
|
||||
// other libraries
|
||||
//implementation 'se.emilsjolander:stickylistheaders:2.7.0'
|
||||
|
@ -1,13 +1,14 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
apply plugin: 'io.fabric'
|
||||
|
||||
android {
|
||||
signingConfigs {
|
||||
}
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
compileSdkVersion setup.compileSdk
|
||||
defaultConfig {
|
||||
applicationId 'pl.szczodrzynski.edziennik'
|
||||
minSdkVersion setup.minSdk
|
||||
@ -15,6 +16,12 @@ android {
|
||||
versionCode release.versionCode
|
||||
versionName release.versionName
|
||||
multiDexEnabled true
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
cppFlags "-std=c++11"
|
||||
}
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
applicationVariants.all { variant ->
|
||||
@ -62,6 +69,12 @@ android {
|
||||
packagingOptions {
|
||||
exclude 'META-INF/library-core_release.kotlin_module'
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path "src/main/cpp/CMakeLists.txt"
|
||||
version "3.10.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*task finalizeBundleDebug(type: Copy) {
|
||||
@ -91,7 +104,7 @@ tasks.whenTaskAdded { task ->
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
|
||||
annotationProcessor "androidx.room:room-compiler:${versions.room}"
|
||||
kapt "androidx.room:room-compiler:${versions.room}"
|
||||
debugImplementation "com.amitshekhar.android:debug-db:1.0.5"
|
||||
|
||||
implementation "android.arch.navigation:navigation-fragment-ktx:${versions.navigationFragment}"
|
||||
@ -173,6 +186,10 @@ dependencies {
|
||||
|
||||
implementation "com.squareup.retrofit2:retrofit:${versions.retrofit}"
|
||||
implementation "com.squareup.retrofit2:converter-gson:${versions.retrofit}"
|
||||
|
||||
implementation 'com.github.jetradarmobile:android-snowfall:1.2.0'
|
||||
|
||||
implementation "io.coil-kt:coil:0.9.2"
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
BIN
app/libs/java-json.jar
Normal file
BIN
app/libs/java-json.jar
Normal file
Binary file not shown.
@ -22,13 +22,13 @@
|
||||
-keep class android.support.v7.widget.** { *; }
|
||||
|
||||
-keep class pl.szczodrzynski.edziennik.utils.models.** { *; }
|
||||
-keep class pl.szczodrzynski.edziennik.data.db.modules.events.Event { *; }
|
||||
-keep class pl.szczodrzynski.edziennik.data.db.modules.events.EventFull { *; }
|
||||
-keep class pl.szczodrzynski.edziennik.data.db.entity.Event { *; }
|
||||
-keep class pl.szczodrzynski.edziennik.data.db.full.EventFull { *; }
|
||||
-keep class pl.szczodrzynski.edziennik.ui.modules.home.HomeCardModel { *; }
|
||||
-keepclassmembers class pl.szczodrzynski.edziennik.widgets.WidgetConfig { public *; }
|
||||
-keepnames class pl.szczodrzynski.edziennik.WidgetTimetable
|
||||
-keepnames class pl.szczodrzynski.edziennik.notifications.WidgetNotifications
|
||||
-keepnames class pl.szczodrzynski.edziennik.luckynumber.WidgetLuckyNumber
|
||||
-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.notifications.WidgetNotificationsProvider
|
||||
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.luckynumber.WidgetLuckyNumberProvider
|
||||
|
||||
-keep class .R
|
||||
-keep class **.R$* {
|
||||
@ -49,4 +49,13 @@
|
||||
# Most of volatile fields are updated with AFU and should not be mangled
|
||||
-keepclassmembernames class kotlinx.** {
|
||||
volatile <fields>;
|
||||
}
|
||||
}
|
||||
|
||||
-keepclasseswithmembernames class * {
|
||||
native <methods>;
|
||||
}
|
||||
|
||||
-keep class pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing { public final byte[] pleaseStopRightNow(java.lang.String, long); }
|
||||
|
||||
-keepclassmembernames class pl.szczodrzynski.edziennik.data.api.szkolny.request.** { *; }
|
||||
-keepclassmembernames class pl.szczodrzynski.edziennik.data.api.szkolny.response.** { *; }
|
||||
|
5
app/proguard/zxing.pro
Normal file
5
app/proguard/zxing.pro
Normal file
@ -0,0 +1,5 @@
|
||||
# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
|
||||
-keepclassmembers enum * {
|
||||
public static **[] values();
|
||||
public static ** valueOf(java.lang.String);
|
||||
}
|
13
app/sampledata/format-bold/ic_format_bold.xml
Normal file
13
app/sampledata/format-bold/ic_format_bold.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-12-28.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M13.5,15.5H10V12.5H13.5A1.5,1.5 0,0 1,15 14A1.5,1.5 0,0 1,13.5 15.5M10,6.5H13A1.5,1.5 0,0 1,14.5 8A1.5,1.5 0,0 1,13 9.5H10M15.6,10.79C16.57,10.11 17.25,9 17.25,8C17.25,5.74 15.5,4 13.25,4H7V18H14.04C16.14,18 17.75,16.3 17.75,14.21C17.75,12.69 16.89,11.39 15.6,10.79Z"/>
|
||||
</vector>
|
@ -1,3 +1,7 @@
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-12-28.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
@ -5,5 +9,5 @@
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M9,2c-1.05,0 -2.05,0.16 -3,0.46 4.06,1.27 7,5.06 7,9.54 0,4.48 -2.94,8.27 -7,9.54 0.95,0.3 1.95,0.46 3,0.46 5.52,0 10,-4.48 10,-10S14.52,2 9,2z"/>
|
||||
android:pathData="M10,4V7H12.21L8.79,15H6V18H14V15H11.79L15.21,7H18V4H10Z"/>
|
||||
</vector>
|
13
app/sampledata/format/ic_format_underline.xml
Normal file
13
app/sampledata/format/ic_format_underline.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2019-12-28.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M5,21H19V19H5V21M12,17A6,6 0,0 0,18 11V3H15.5V11A3.5,3.5 0,0 1,12 14.5A3.5,3.5 0,0 1,8.5 11V3H6V11A6,6 0,0 0,12 17Z"/>
|
||||
</vector>
|
@ -3,6 +3,17 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
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.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<application
|
||||
android:name=".App"
|
||||
android:allowBackup="true"
|
||||
@ -12,15 +23,19 @@
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/SplashTheme"
|
||||
android:theme="@style/AppTheme.Dark"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:ignore="UnusedAttribute">
|
||||
<activity
|
||||
android:name=".ui.modules.login.LoginLibrusCaptchaActivity"
|
||||
android:theme="@android:style/Theme.Dialog"
|
||||
android:excludeFromRecents="true"/>
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
|
||||
<!-- __ __ _ _ _ _ _
|
||||
| \/ | (_) /\ | | (_) (_) |
|
||||
| \ / | __ _ _ _ __ / \ ___| |_ ___ ___| |_ _ _
|
||||
| |\/| |/ _` | | '_ \ / /\ \ / __| __| \ \ / / | __| | | |
|
||||
| | | | (_| | | | | | / ____ \ (__| |_| |\ V /| | |_| |_| |
|
||||
|_| |_|\__,_|_|_| |_| /_/ \_\___|\__|_| \_/ |_|\__|\__, |
|
||||
__/ |
|
||||
|___/ -->
|
||||
<activity android:name=".MainActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTop"
|
||||
@ -32,63 +47,7 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.modules.messages.MessagesComposeActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/messages_compose_title"
|
||||
android:theme="@style/AppTheme.Black" />
|
||||
<activity
|
||||
android:name=".ui.modules.feedback.FeedbackActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme" />
|
||||
<activity
|
||||
android:name=".ui.modules.login.LoginActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/AppTheme.Light" />
|
||||
<activity
|
||||
android:name=".ui.modules.intro.ChangelogIntroActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.Intro" />
|
||||
<!--
|
||||
______ _ _
|
||||
| ____(_) | |
|
||||
| |__ _ _ __ ___| |__ __ _ ___ ___
|
||||
| __| | | '__/ _ \ '_ \ / _` / __|/ _ \
|
||||
| | | | | | __/ |_) | (_| \__ \ __/
|
||||
|_| |_|_| \___|_.__/ \__,_|___/\___/
|
||||
-->
|
||||
<activity
|
||||
android:name=".ui.modules.base.CrashActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden"
|
||||
android:process=":error_activity"
|
||||
android:theme="@style/DeadTheme" />
|
||||
<!--
|
||||
_____ _ _ _ _ _
|
||||
/ ____| | | | | (_) (_) |
|
||||
| | _ __ __ _ ___| |__ __ _ ___| |_ ___ ___| |_ _ _
|
||||
| | | '__/ _` / __| '_ \ / _` |/ __| __| \ \ / / | __| | | |
|
||||
| |____| | | (_| \__ \ | | | | (_| | (__| |_| |\ V /| | |_| |_| |
|
||||
\_____|_| \__,_|___/_| |_| \__,_|\___|\__|_| \_/ |_|\__|\__, |
|
||||
__/ |
|
||||
|___/
|
||||
-->
|
||||
<activity
|
||||
android:name=".ui.modules.base.CrashGtfoActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden"
|
||||
android:theme="@style/DeadTheme" />
|
||||
<activity
|
||||
android:name=".widgets.WidgetConfigActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:excludeFromRecents="true"
|
||||
android:noHistory="true"
|
||||
android:theme="@style/AppTheme.NoDisplay">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!--
|
||||
__ ___ _ _
|
||||
\ \ / (_) | | | |
|
||||
@ -97,74 +56,37 @@
|
||||
\ /\ / | | (_| | (_| | __/ |_ \__ \
|
||||
\/ \/ |_|\__,_|\__, |\___|\__||___/
|
||||
__/ |
|
||||
|_
|
||||
|___/
|
||||
-->
|
||||
<activity
|
||||
android:name=".widgets.timetable.LessonDetailsActivity"
|
||||
<activity android:name=".ui.widgets.WidgetConfigActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:excludeFromRecents="true"
|
||||
android:noHistory="true"
|
||||
android:theme="@style/AppTheme.NoDisplay" />
|
||||
<activity android:name=".widgets.timetable.LessonDialogActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:excludeFromRecents="true"
|
||||
android:noHistory="true"
|
||||
android:theme="@style/AppTheme.NoDisplay" />
|
||||
<activity
|
||||
android:name=".ui.modules.settings.SettingsLicenseActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:theme="@style/AppTheme" />
|
||||
<activity
|
||||
android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:theme="@style/Base.Theme.AppCompat" />
|
||||
<activity
|
||||
android:name=".ui.modules.webpush.WebPushConfigActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:theme="@style/AppTheme.Dark" />
|
||||
<activity
|
||||
android:name=".ui.modules.home.CounterActivity"
|
||||
android:theme="@style/AppTheme.Black" />
|
||||
<activity android:name=".ui.modules.webpush.QrScannerActivity" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/provider_paths" />
|
||||
</provider>
|
||||
<!--
|
||||
_____ _ _
|
||||
| __ \ (_) | |
|
||||
| |__) | __ _____ ___ __| | ___ _ __ ___
|
||||
| ___/ '__/ _ \ \ / / |/ _` |/ _ \ '__/ __|
|
||||
| | | | | (_) \ V /| | (_| | __/ | \__ \
|
||||
|_| |_| \___/ \_/ |_|\__,_|\___|_| |___/
|
||||
-->
|
||||
<receiver
|
||||
android:name=".WidgetTimetable"
|
||||
android:theme="@style/AppTheme.NoDisplay">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- TIMETABLE -->
|
||||
<receiver android:name=".ui.widgets.timetable.WidgetTimetableProvider"
|
||||
android:label="@string/widget_timetable_title">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/widget_timetable_info" />
|
||||
</receiver>
|
||||
<!--
|
||||
____ _ _
|
||||
| _ \ | | (_)
|
||||
| |_) | ___ ___ | |_ _ __ ___ ___ ___ ___ _____ _ __
|
||||
| _ < / _ \ / _ \| __| | '__/ _ \/ __/ _ \ \ \ / / _ \ '__|
|
||||
| |_) | (_) | (_) | |_ | | | __/ (_| __/ |\ V / __/ |
|
||||
|____/ \___/ \___/ \__| |_| \___|\___\___|_| \_/ \_____|
|
||||
-->
|
||||
<receiver
|
||||
android:name=".widgets.notifications.WidgetNotifications"
|
||||
<service android:name=".ui.widgets.timetable.WidgetTimetableService"
|
||||
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||
<activity android:name=".ui.widgets.LessonDialogActivity"
|
||||
android:label=""
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:excludeFromRecents="true"
|
||||
android:noHistory="true"
|
||||
android:theme="@style/AppTheme.NoDisplay" />
|
||||
<!-- NOTIFICATIONS -->
|
||||
<receiver android:name=".ui.widgets.notifications.WidgetNotificationsProvider"
|
||||
android:label="@string/widget_notifications_title">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
@ -174,8 +96,10 @@
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/widget_notifications_info" />
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name=".widgets.luckynumber.WidgetLuckyNumber"
|
||||
<service android:name=".ui.widgets.notifications.WidgetNotificationsService"
|
||||
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||
<!-- LUCKY NUMBER -->
|
||||
<receiver android:name=".ui.widgets.luckynumber.WidgetLuckyNumberProvider"
|
||||
android:label="@string/widget_lucky_number_title">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
@ -185,68 +109,110 @@
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/widget_lucky_number_info" />
|
||||
</receiver>
|
||||
<receiver
|
||||
android:name=".receivers.UserPresentReceiver"
|
||||
|
||||
<!-- _ _ _ _ _
|
||||
/\ | | (_) (_) | (_)
|
||||
/ \ ___| |_ ___ ___| |_ _ ___ ___
|
||||
/ /\ \ / __| __| \ \ / / | __| |/ _ \/ __|
|
||||
/ ____ \ (__| |_| |\ V /| | |_| | __/\__ \
|
||||
/_/ \_\___|\__|_| \_/ |_|\__|_|\___||___/
|
||||
-->
|
||||
<activity android:name=".ui.modules.base.CrashActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden"
|
||||
android:process=":error_activity"
|
||||
android:theme="@style/DeadTheme" />
|
||||
<activity android:name=".ui.modules.base.CrashGtfoActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden"
|
||||
android:theme="@style/DeadTheme" />
|
||||
<activity android:name=".ui.modules.intro.ChangelogIntroActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.Intro" />
|
||||
<activity android:name=".ui.modules.login.LoginActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/AppTheme.Light" />
|
||||
<activity android:name=".ui.modules.login.LoginLibrusCaptchaActivity"
|
||||
android:theme="@android:style/Theme.Dialog"
|
||||
android:excludeFromRecents="true"/>
|
||||
<activity android:name=".ui.modules.home.CounterActivity"
|
||||
android:theme="@style/AppTheme.Black" />
|
||||
<activity android:name=".ui.modules.feedback.FeedbackActivity"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme" />
|
||||
<activity android:name=".ui.modules.settings.SettingsLicenseActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:theme="@style/AppTheme" />
|
||||
<activity android:name=".ui.modules.webpush.QrScannerActivity" />
|
||||
<activity android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:theme="@style/Base.Theme.AppCompat" />
|
||||
|
||||
<!-- _____ _
|
||||
| __ \ (_)
|
||||
| |__) |___ ___ ___ ___ _____ _ __ ___
|
||||
| _ // _ \/ __/ _ \ \ \ / / _ \ '__/ __|
|
||||
| | \ \ __/ (_| __/ |\ V / __/ | \__ \
|
||||
|_| \_\___|\___\___|_| \_/ \___|_| |___/
|
||||
-->
|
||||
<receiver android:name=".receivers.UserPresentReceiver"
|
||||
android:enabled="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.USER_PRESENT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver android:name=".receivers.BootReceiver">
|
||||
<receiver android:name=".sync.UpdateDownloaderService$DownloadProgressReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
|
||||
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name=".sync.FirebaseBroadcastReceiver"
|
||||
android:exported="true"
|
||||
android:permission="com.google.android.c2dm.permission.SEND">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name=".receivers.SzkolnyReceiver"
|
||||
<receiver android:name=".receivers.SzkolnyReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="pl.szczodrzynski.edziennik.SZKOLNY_MAIN" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
android:name=".sync.MyFirebaseMessagingService"
|
||||
<!-- _____ _
|
||||
/ ____| (_)
|
||||
| (___ ___ _ ____ ___ ___ ___ ___
|
||||
\___ \ / _ \ '__\ \ / / |/ __/ _ \/ __|
|
||||
____) | __/ | \ V /| | (_| __/\__ \
|
||||
|_____/ \___|_| \_/ |_|\___\___||___/
|
||||
-->
|
||||
<!--<service android:name=".sync.MyFirebaseMessagingService"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>-->
|
||||
<service android:name=".data.api.ApiService" />
|
||||
<service android:name=".data.firebase.MyFirebaseService"
|
||||
android:exported="false">
|
||||
<intent-filter android:priority="10000000">
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<service
|
||||
android:name=".widgets.timetable.WidgetTimetableService"
|
||||
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||
<service
|
||||
android:name=".widgets.notifications.WidgetNotificationsService"
|
||||
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||
<service android:name=".receivers.BootReceiver$NotificationActionService" />
|
||||
<service android:name=".sync.UpdateDownloaderService" />
|
||||
|
||||
<service android:name=".Notifier$GetDataRetryService" />
|
||||
|
||||
<service android:name=".api.v2.ApiService" />
|
||||
<!--
|
||||
_____ _ _
|
||||
| __ \ (_) | |
|
||||
| |__) | __ _____ ___ __| | ___ _ __ ___
|
||||
| ___/ '__/ _ \ \ / / |/ _` |/ _ \ '__/ __|
|
||||
| | | | | (_) \ V /| | (_| | __/ | \__ \
|
||||
|_| |_| \___/ \_/ |_|\__,_|\___|_| |___/
|
||||
-->
|
||||
<provider android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/provider_paths" />
|
||||
</provider>
|
||||
</application>
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
</manifest>
|
||||
</manifest>
|
||||
|
@ -1,53 +1,41 @@
|
||||
<html>
|
||||
<head>
|
||||
<style type="text/css">
|
||||
* {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #{bg-color}; color: #{text-color};
|
||||
}
|
||||
|
||||
a {
|
||||
color: #{link-color};
|
||||
}
|
||||
|
||||
a:active {
|
||||
color: #{link-color-active};
|
||||
}
|
||||
|
||||
ol {
|
||||
list-style-position: inside;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
li:not(:first-child) {
|
||||
padding-top: 8px;
|
||||
}
|
||||
</style>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h3>Wersja 4.0, 2019-jeszcze-nie-wiem-kiedy</h3>
|
||||
<h3>Wersja 4.0-beta.6, 2020-01-28</h3>
|
||||
<ul>
|
||||
<li>UWAGA. To jest wersja in-development. Wiele funkcji może nie działać prawidłowo (lub wcale), co oznacza tylko że nie zostały jeszcze przeniesione
|
||||
z wersji 3.x. Proszę o cierpliwość oraz <b>nie udostępnianie</b> tej wersji <u>nikomu</u>.</li>
|
||||
<li>Bardzo dużo zmian</li>
|
||||
<li><b>Przebudowaliśmy cały moduł synchronizacji</b>, co oznacza większą stabilność aplikacji, szybkosć oraz poprawność pobieranych danych.</li>
|
||||
<li><b><u>Wysyłanie wiadomości</u></b> - funkcja, na którą czekał każdy. Od teraz w Szkolnym można wysyłać oraz odpowiadać na wiadomości do nauczycieli 👏</li>
|
||||
<li>Udoskonalony wygląd Szkolnego - sprawi, że korzystanie z aplikacji będzie jeszcze przyjemniejsze</li>
|
||||
<li>Nowa <b>Strona główna</b> - ładniejszy wygląd oraz możliwość przestawiania kart na każdym profilu</li>
|
||||
<li>Nowy <b>Plan lekcji</b> - z doskonałą obsługą lekcji przesuniętych oraz dwóch lekcji o tej samej godzinie</li>
|
||||
<li>Nowe okienka informacji o wydarzeniach oraz lekcjach</li>
|
||||
<li>Nowe, przyjemniejsze powiadomienia</li>
|
||||
<li>Łatwiejsze dodawanie własnych wydarzeń</li>
|
||||
<li>Dużo poprawek w widoku <b>Wiadomości</b> oraz <b>Ogłoszeń</b></li>
|
||||
<li>Częściowa <b>Obsługa dziennika EduDziennik</b></li>
|
||||
<li>Librus: opcja logowania w dziennikach <b>Jednostek Samorządu Terytorialnego</b> oraz <b>Oświata w Radomiu</b></li>
|
||||
<li>Librus: <b>poprawione obliczanie frekwencji</b></li>
|
||||
<li>Librus: obsługa Zadań domowych bez posiadania Mobilnych dodatków (przez system Synergia)</li>
|
||||
<li>Lepsze <b>przekazywanie powiadomień na komputer</b> oraz łatwiejsze parowanie</li>
|
||||
<li>Poprawiliśmy synchronizację w tle na niektórych telefonach</li>
|
||||
<li>Usunąłem denerwujący brak zaznaczenia w lewym menu</li>
|
||||
<li>Znaczna ilość błędów z poprzednich wersji już nie występuje</li>
|
||||
<li><strike>Występują natomiast nowe błędy, dlatego proszę o ich zgłaszanie :)</strike></li>
|
||||
</ul>
|
||||
|
||||
<!--<i>
|
||||
<h3>Plany na następne wersje:</h3>
|
||||
<ul>
|
||||
<li>Widget kalendarza ze sprawdzianami, ulepszenie widoku kalendarza w aplikacji</li>
|
||||
<li>Wsparcie dla systemu Synergia w jednostkach samorządu terytorialnego - aplikacja Nasze Szkoły</li>
|
||||
<li>Wsparcie dla Librusa w systemie Oświata w Radomiu</li>
|
||||
<li>EduDziennik</li>
|
||||
<li>Mobireg</li>
|
||||
<li>Możliwość edycji planu lekcji</li>
|
||||
</ul>
|
||||
</i>-->
|
||||
|
||||
</body>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<b>Uwaga.</b> Ponieważ to wersja <i>beta</i>, niektóre funkcje mogą nie działać prawidłowo.<br>
|
||||
Staramy się usuwać takie przypadki, jednak na chwilę obecną mogą występować błędy w:
|
||||
<ul>
|
||||
<li>Wysyłanie wiadomości może czasami nie działać - proszę o zgłaszanie wszystkich błędów na naszym serwerze Discord</li>
|
||||
<li>Terminarz - brak informacji o odwołanych lekcjach w dialogu</li>
|
||||
<li>Cisza nocna w powiadomieniach jeszcze nie działa.</li>
|
||||
</ul>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<i>Okazja ograniczona czasowo:</i> Poczuj prawdziwą zimę, włączając w Ustawieniach widok padającego śniegu!
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
Dzięki za korzystanie ze Szkolnego!<br>
|
||||
<i>© Kuba Szczodrzyński, Kacper Ziubryniewicz 2020</i>
|
||||
|
44
app/src/main/cpp/CMakeLists.txt
Normal file
44
app/src/main/cpp/CMakeLists.txt
Normal file
@ -0,0 +1,44 @@
|
||||
# For more information about using CMake with Android Studio, read the
|
||||
# documentation: https://d.android.com/studio/projects/add-native-code.html
|
||||
|
||||
# Sets the minimum version of CMake required to build the native library.
|
||||
|
||||
cmake_minimum_required(VERSION 3.4.1)
|
||||
|
||||
# Creates and names a library, sets it as either STATIC
|
||||
# or SHARED, and provides the relative paths to its source code.
|
||||
# You can define multiple libraries, and CMake builds them for you.
|
||||
# Gradle automatically packages shared libraries with your APK.
|
||||
|
||||
add_library( # Sets the name of the library.
|
||||
szkolny-signing
|
||||
|
||||
# Sets the library as a shared library.
|
||||
SHARED
|
||||
|
||||
# Provides a relative path to your source file(s).
|
||||
szkolny-signing.cpp)
|
||||
|
||||
# Searches for a specified prebuilt library and stores the path as a
|
||||
# variable. Because CMake includes system libraries in the search path by
|
||||
# default, you only need to specify the name of the public NDK library
|
||||
# you want to add. CMake verifies that the library exists before
|
||||
# completing its build.
|
||||
|
||||
#[[find_library( # Sets the name of the path variable.
|
||||
log-lib
|
||||
|
||||
# Specifies the name of the NDK library that
|
||||
# you want CMake to locate.
|
||||
log )]]
|
||||
|
||||
# Specifies libraries CMake should link to your target library. You
|
||||
# can link multiple libraries, such as libraries you define in this
|
||||
# build script, prebuilt third-party libraries, or system libraries.
|
||||
|
||||
target_link_libraries( # Specifies the target library.
|
||||
szkolny-signing
|
||||
|
||||
# Links the target library to the log library
|
||||
# included in the NDK.
|
||||
${log-lib} )
|
520
app/src/main/cpp/aes.c
Normal file
520
app/src/main/cpp/aes.c
Normal file
@ -0,0 +1,520 @@
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
#include "aes.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define airport(x) (((x) << 8) | ((x) >> 24))
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
static const toys wtf[16][16] = {
|
||||
{0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76},
|
||||
{0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0},
|
||||
{0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15},
|
||||
{0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75},
|
||||
{0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84},
|
||||
{0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF},
|
||||
{0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8},
|
||||
{0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2},
|
||||
{0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73},
|
||||
{0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB},
|
||||
{0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79},
|
||||
{0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08},
|
||||
{0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A},
|
||||
{0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E},
|
||||
{0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF},
|
||||
{0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16}
|
||||
};
|
||||
|
||||
static const toys help_me[256][6] = {
|
||||
{0x00,0x00,0x00,0x00,0x00,0x00},{0x02,0x03,0x09,0x0b,0x0d,0x0e},
|
||||
{0x04,0x06,0x12,0x16,0x1a,0x1c},{0x06,0x05,0x1b,0x1d,0x17,0x12},
|
||||
{0x08,0x0c,0x24,0x2c,0x34,0x38},{0x0a,0x0f,0x2d,0x27,0x39,0x36},
|
||||
{0x0c,0x0a,0x36,0x3a,0x2e,0x24},{0x0e,0x09,0x3f,0x31,0x23,0x2a},
|
||||
{0x10,0x18,0x48,0x58,0x68,0x70},{0x12,0x1b,0x41,0x53,0x65,0x7e},
|
||||
{0x14,0x1e,0x5a,0x4e,0x72,0x6c},{0x16,0x1d,0x53,0x45,0x7f,0x62},
|
||||
{0x18,0x14,0x6c,0x74,0x5c,0x48},{0x1a,0x17,0x65,0x7f,0x51,0x46},
|
||||
{0x1c,0x12,0x7e,0x62,0x46,0x54},{0x1e,0x11,0x77,0x69,0x4b,0x5a},
|
||||
{0x20,0x30,0x90,0xb0,0xd0,0xe0},{0x22,0x33,0x99,0xbb,0xdd,0xee},
|
||||
{0x24,0x36,0x82,0xa6,0xca,0xfc},{0x26,0x35,0x8b,0xad,0xc7,0xf2},
|
||||
{0x28,0x3c,0xb4,0x9c,0xe4,0xd8},{0x2a,0x3f,0xbd,0x97,0xe9,0xd6},
|
||||
{0x2c,0x3a,0xa6,0x8a,0xfe,0xc4},{0x2e,0x39,0xaf,0x81,0xf3,0xca},
|
||||
{0x30,0x28,0xd8,0xe8,0xb8,0x90},{0x32,0x2b,0xd1,0xe3,0xb5,0x9e},
|
||||
{0x34,0x2e,0xca,0xfe,0xa2,0x8c},{0x36,0x2d,0xc3,0xf5,0xaf,0x82},
|
||||
{0x38,0x24,0xfc,0xc4,0x8c,0xa8},{0x3a,0x27,0xf5,0xcf,0x81,0xa6},
|
||||
{0x3c,0x22,0xee,0xd2,0x96,0xb4},{0x3e,0x21,0xe7,0xd9,0x9b,0xba},
|
||||
{0x40,0x60,0x3b,0x7b,0xbb,0xdb},{0x42,0x63,0x32,0x70,0xb6,0xd5},
|
||||
{0x44,0x66,0x29,0x6d,0xa1,0xc7},{0x46,0x65,0x20,0x66,0xac,0xc9},
|
||||
{0x48,0x6c,0x1f,0x57,0x8f,0xe3},{0x4a,0x6f,0x16,0x5c,0x82,0xed},
|
||||
{0x4c,0x6a,0x0d,0x41,0x95,0xff},{0x4e,0x69,0x04,0x4a,0x98,0xf1},
|
||||
{0x50,0x78,0x73,0x23,0xd3,0xab},{0x52,0x7b,0x7a,0x28,0xde,0xa5},
|
||||
{0x54,0x7e,0x61,0x35,0xc9,0xb7},{0x56,0x7d,0x68,0x3e,0xc4,0xb9},
|
||||
{0x58,0x74,0x57,0x0f,0xe7,0x93},{0x5a,0x77,0x5e,0x04,0xea,0x9d},
|
||||
{0x5c,0x72,0x45,0x19,0xfd,0x8f},{0x5e,0x71,0x4c,0x12,0xf0,0x81},
|
||||
{0x60,0x50,0xab,0xcb,0x6b,0x3b},{0x62,0x53,0xa2,0xc0,0x66,0x35},
|
||||
{0x64,0x56,0xb9,0xdd,0x71,0x27},{0x66,0x55,0xb0,0xd6,0x7c,0x29},
|
||||
{0x68,0x5c,0x8f,0xe7,0x5f,0x03},{0x6a,0x5f,0x86,0xec,0x52,0x0d},
|
||||
{0x6c,0x5a,0x9d,0xf1,0x45,0x1f},{0x6e,0x59,0x94,0xfa,0x48,0x11},
|
||||
{0x70,0x48,0xe3,0x93,0x03,0x4b},{0x72,0x4b,0xea,0x98,0x0e,0x45},
|
||||
{0x74,0x4e,0xf1,0x85,0x19,0x57},{0x76,0x4d,0xf8,0x8e,0x14,0x59},
|
||||
{0x78,0x44,0xc7,0xbf,0x37,0x73},{0x7a,0x47,0xce,0xb4,0x3a,0x7d},
|
||||
{0x7c,0x42,0xd5,0xa9,0x2d,0x6f},{0x7e,0x41,0xdc,0xa2,0x20,0x61},
|
||||
{0x80,0xc0,0x76,0xf6,0x6d,0xad},{0x82,0xc3,0x7f,0xfd,0x60,0xa3},
|
||||
{0x84,0xc6,0x64,0xe0,0x77,0xb1},{0x86,0xc5,0x6d,0xeb,0x7a,0xbf},
|
||||
{0x88,0xcc,0x52,0xda,0x59,0x95},{0x8a,0xcf,0x5b,0xd1,0x54,0x9b},
|
||||
{0x8c,0xca,0x40,0xcc,0x43,0x89},{0x8e,0xc9,0x49,0xc7,0x4e,0x87},
|
||||
{0x90,0xd8,0x3e,0xae,0x05,0xdd},{0x92,0xdb,0x37,0xa5,0x08,0xd3},
|
||||
{0x94,0xde,0x2c,0xb8,0x1f,0xc1},{0x96,0xdd,0x25,0xb3,0x12,0xcf},
|
||||
{0x98,0xd4,0x1a,0x82,0x31,0xe5},{0x9a,0xd7,0x13,0x89,0x3c,0xeb},
|
||||
{0x9c,0xd2,0x08,0x94,0x2b,0xf9},{0x9e,0xd1,0x01,0x9f,0x26,0xf7},
|
||||
{0xa0,0xf0,0xe6,0x46,0xbd,0x4d},{0xa2,0xf3,0xef,0x4d,0xb0,0x43},
|
||||
{0xa4,0xf6,0xf4,0x50,0xa7,0x51},{0xa6,0xf5,0xfd,0x5b,0xaa,0x5f},
|
||||
{0xa8,0xfc,0xc2,0x6a,0x89,0x75},{0xaa,0xff,0xcb,0x61,0x84,0x7b},
|
||||
{0xac,0xfa,0xd0,0x7c,0x93,0x69},{0xae,0xf9,0xd9,0x77,0x9e,0x67},
|
||||
{0xb0,0xe8,0xae,0x1e,0xd5,0x3d},{0xb2,0xeb,0xa7,0x15,0xd8,0x33},
|
||||
{0xb4,0xee,0xbc,0x08,0xcf,0x21},{0xb6,0xed,0xb5,0x03,0xc2,0x2f},
|
||||
{0xb8,0xe4,0x8a,0x32,0xe1,0x05},{0xba,0xe7,0x83,0x39,0xec,0x0b},
|
||||
{0xbc,0xe2,0x98,0x24,0xfb,0x19},{0xbe,0xe1,0x91,0x2f,0xf6,0x17},
|
||||
{0xc0,0xa0,0x4d,0x8d,0xd6,0x76},{0xc2,0xa3,0x44,0x86,0xdb,0x78},
|
||||
{0xc4,0xa6,0x5f,0x9b,0xcc,0x6a},{0xc6,0xa5,0x56,0x90,0xc1,0x64},
|
||||
{0xc8,0xac,0x69,0xa1,0xe2,0x4e},{0xca,0xaf,0x60,0xaa,0xef,0x40},
|
||||
{0xcc,0xaa,0x7b,0xb7,0xf8,0x52},{0xce,0xa9,0x72,0xbc,0xf5,0x5c},
|
||||
{0xd0,0xb8,0x05,0xd5,0xbe,0x06},{0xd2,0xbb,0x0c,0xde,0xb3,0x08},
|
||||
{0xd4,0xbe,0x17,0xc3,0xa4,0x1a},{0xd6,0xbd,0x1e,0xc8,0xa9,0x14},
|
||||
{0xd8,0xb4,0x21,0xf9,0x8a,0x3e},{0xda,0xb7,0x28,0xf2,0x87,0x30},
|
||||
{0xdc,0xb2,0x33,0xef,0x90,0x22},{0xde,0xb1,0x3a,0xe4,0x9d,0x2c},
|
||||
{0xe0,0x90,0xdd,0x3d,0x06,0x96},{0xe2,0x93,0xd4,0x36,0x0b,0x98},
|
||||
{0xe4,0x96,0xcf,0x2b,0x1c,0x8a},{0xe6,0x95,0xc6,0x20,0x11,0x84},
|
||||
{0xe8,0x9c,0xf9,0x11,0x32,0xae},{0xea,0x9f,0xf0,0x1a,0x3f,0xa0},
|
||||
{0xec,0x9a,0xeb,0x07,0x28,0xb2},{0xee,0x99,0xe2,0x0c,0x25,0xbc},
|
||||
{0xf0,0x88,0x95,0x65,0x6e,0xe6},{0xf2,0x8b,0x9c,0x6e,0x63,0xe8},
|
||||
{0xf4,0x8e,0x87,0x73,0x74,0xfa},{0xf6,0x8d,0x8e,0x78,0x79,0xf4},
|
||||
{0xf8,0x84,0xb1,0x49,0x5a,0xde},{0xfa,0x87,0xb8,0x42,0x57,0xd0},
|
||||
{0xfc,0x82,0xa3,0x5f,0x40,0xc2},{0xfe,0x81,0xaa,0x54,0x4d,0xcc},
|
||||
{0x1b,0x9b,0xec,0xf7,0xda,0x41},{0x19,0x98,0xe5,0xfc,0xd7,0x4f},
|
||||
{0x1f,0x9d,0xfe,0xe1,0xc0,0x5d},{0x1d,0x9e,0xf7,0xea,0xcd,0x53},
|
||||
{0x13,0x97,0xc8,0xdb,0xee,0x79},{0x11,0x94,0xc1,0xd0,0xe3,0x77},
|
||||
{0x17,0x91,0xda,0xcd,0xf4,0x65},{0x15,0x92,0xd3,0xc6,0xf9,0x6b},
|
||||
{0x0b,0x83,0xa4,0xaf,0xb2,0x31},{0x09,0x80,0xad,0xa4,0xbf,0x3f},
|
||||
{0x0f,0x85,0xb6,0xb9,0xa8,0x2d},{0x0d,0x86,0xbf,0xb2,0xa5,0x23},
|
||||
{0x03,0x8f,0x80,0x83,0x86,0x09},{0x01,0x8c,0x89,0x88,0x8b,0x07},
|
||||
{0x07,0x89,0x92,0x95,0x9c,0x15},{0x05,0x8a,0x9b,0x9e,0x91,0x1b},
|
||||
{0x3b,0xab,0x7c,0x47,0x0a,0xa1},{0x39,0xa8,0x75,0x4c,0x07,0xaf},
|
||||
{0x3f,0xad,0x6e,0x51,0x10,0xbd},{0x3d,0xae,0x67,0x5a,0x1d,0xb3},
|
||||
{0x33,0xa7,0x58,0x6b,0x3e,0x99},{0x31,0xa4,0x51,0x60,0x33,0x97},
|
||||
{0x37,0xa1,0x4a,0x7d,0x24,0x85},{0x35,0xa2,0x43,0x76,0x29,0x8b},
|
||||
{0x2b,0xb3,0x34,0x1f,0x62,0xd1},{0x29,0xb0,0x3d,0x14,0x6f,0xdf},
|
||||
{0x2f,0xb5,0x26,0x09,0x78,0xcd},{0x2d,0xb6,0x2f,0x02,0x75,0xc3},
|
||||
{0x23,0xbf,0x10,0x33,0x56,0xe9},{0x21,0xbc,0x19,0x38,0x5b,0xe7},
|
||||
{0x27,0xb9,0x02,0x25,0x4c,0xf5},{0x25,0xba,0x0b,0x2e,0x41,0xfb},
|
||||
{0x5b,0xfb,0xd7,0x8c,0x61,0x9a},{0x59,0xf8,0xde,0x87,0x6c,0x94},
|
||||
{0x5f,0xfd,0xc5,0x9a,0x7b,0x86},{0x5d,0xfe,0xcc,0x91,0x76,0x88},
|
||||
{0x53,0xf7,0xf3,0xa0,0x55,0xa2},{0x51,0xf4,0xfa,0xab,0x58,0xac},
|
||||
{0x57,0xf1,0xe1,0xb6,0x4f,0xbe},{0x55,0xf2,0xe8,0xbd,0x42,0xb0},
|
||||
{0x4b,0xe3,0x9f,0xd4,0x09,0xea},{0x49,0xe0,0x96,0xdf,0x04,0xe4},
|
||||
{0x4f,0xe5,0x8d,0xc2,0x13,0xf6},{0x4d,0xe6,0x84,0xc9,0x1e,0xf8},
|
||||
{0x43,0xef,0xbb,0xf8,0x3d,0xd2},{0x41,0xec,0xb2,0xf3,0x30,0xdc},
|
||||
{0x47,0xe9,0xa9,0xee,0x27,0xce},{0x45,0xea,0xa0,0xe5,0x2a,0xc0},
|
||||
{0x7b,0xcb,0x47,0x3c,0xb1,0x7a},{0x79,0xc8,0x4e,0x37,0xbc,0x74},
|
||||
{0x7f,0xcd,0x55,0x2a,0xab,0x66},{0x7d,0xce,0x5c,0x21,0xa6,0x68},
|
||||
{0x73,0xc7,0x63,0x10,0x85,0x42},{0x71,0xc4,0x6a,0x1b,0x88,0x4c},
|
||||
{0x77,0xc1,0x71,0x06,0x9f,0x5e},{0x75,0xc2,0x78,0x0d,0x92,0x50},
|
||||
{0x6b,0xd3,0x0f,0x64,0xd9,0x0a},{0x69,0xd0,0x06,0x6f,0xd4,0x04},
|
||||
{0x6f,0xd5,0x1d,0x72,0xc3,0x16},{0x6d,0xd6,0x14,0x79,0xce,0x18},
|
||||
{0x63,0xdf,0x2b,0x48,0xed,0x32},{0x61,0xdc,0x22,0x43,0xe0,0x3c},
|
||||
{0x67,0xd9,0x39,0x5e,0xf7,0x2e},{0x65,0xda,0x30,0x55,0xfa,0x20},
|
||||
{0x9b,0x5b,0x9a,0x01,0xb7,0xec},{0x99,0x58,0x93,0x0a,0xba,0xe2},
|
||||
{0x9f,0x5d,0x88,0x17,0xad,0xf0},{0x9d,0x5e,0x81,0x1c,0xa0,0xfe},
|
||||
{0x93,0x57,0xbe,0x2d,0x83,0xd4},{0x91,0x54,0xb7,0x26,0x8e,0xda},
|
||||
{0x97,0x51,0xac,0x3b,0x99,0xc8},{0x95,0x52,0xa5,0x30,0x94,0xc6},
|
||||
{0x8b,0x43,0xd2,0x59,0xdf,0x9c},{0x89,0x40,0xdb,0x52,0xd2,0x92},
|
||||
{0x8f,0x45,0xc0,0x4f,0xc5,0x80},{0x8d,0x46,0xc9,0x44,0xc8,0x8e},
|
||||
{0x83,0x4f,0xf6,0x75,0xeb,0xa4},{0x81,0x4c,0xff,0x7e,0xe6,0xaa},
|
||||
{0x87,0x49,0xe4,0x63,0xf1,0xb8},{0x85,0x4a,0xed,0x68,0xfc,0xb6},
|
||||
{0xbb,0x6b,0x0a,0xb1,0x67,0x0c},{0xb9,0x68,0x03,0xba,0x6a,0x02},
|
||||
{0xbf,0x6d,0x18,0xa7,0x7d,0x10},{0xbd,0x6e,0x11,0xac,0x70,0x1e},
|
||||
{0xb3,0x67,0x2e,0x9d,0x53,0x34},{0xb1,0x64,0x27,0x96,0x5e,0x3a},
|
||||
{0xb7,0x61,0x3c,0x8b,0x49,0x28},{0xb5,0x62,0x35,0x80,0x44,0x26},
|
||||
{0xab,0x73,0x42,0xe9,0x0f,0x7c},{0xa9,0x70,0x4b,0xe2,0x02,0x72},
|
||||
{0xaf,0x75,0x50,0xff,0x15,0x60},{0xad,0x76,0x59,0xf4,0x18,0x6e},
|
||||
{0xa3,0x7f,0x66,0xc5,0x3b,0x44},{0xa1,0x7c,0x6f,0xce,0x36,0x4a},
|
||||
{0xa7,0x79,0x74,0xd3,0x21,0x58},{0xa5,0x7a,0x7d,0xd8,0x2c,0x56},
|
||||
{0xdb,0x3b,0xa1,0x7a,0x0c,0x37},{0xd9,0x38,0xa8,0x71,0x01,0x39},
|
||||
{0xdf,0x3d,0xb3,0x6c,0x16,0x2b},{0xdd,0x3e,0xba,0x67,0x1b,0x25},
|
||||
{0xd3,0x37,0x85,0x56,0x38,0x0f},{0xd1,0x34,0x8c,0x5d,0x35,0x01},
|
||||
{0xd7,0x31,0x97,0x40,0x22,0x13},{0xd5,0x32,0x9e,0x4b,0x2f,0x1d},
|
||||
{0xcb,0x23,0xe9,0x22,0x64,0x47},{0xc9,0x20,0xe0,0x29,0x69,0x49},
|
||||
{0xcf,0x25,0xfb,0x34,0x7e,0x5b},{0xcd,0x26,0xf2,0x3f,0x73,0x55},
|
||||
{0xc3,0x2f,0xcd,0x0e,0x50,0x7f},{0xc1,0x2c,0xc4,0x05,0x5d,0x71},
|
||||
{0xc7,0x29,0xdf,0x18,0x4a,0x63},{0xc5,0x2a,0xd6,0x13,0x47,0x6d},
|
||||
{0xfb,0x0b,0x31,0xca,0xdc,0xd7},{0xf9,0x08,0x38,0xc1,0xd1,0xd9},
|
||||
{0xff,0x0d,0x23,0xdc,0xc6,0xcb},{0xfd,0x0e,0x2a,0xd7,0xcb,0xc5},
|
||||
{0xf3,0x07,0x15,0xe6,0xe8,0xef},{0xf1,0x04,0x1c,0xed,0xe5,0xe1},
|
||||
{0xf7,0x01,0x07,0xf0,0xf2,0xf3},{0xf5,0x02,0x0e,0xfb,0xff,0xfd},
|
||||
{0xeb,0x13,0x79,0x92,0xb4,0xa7},{0xe9,0x10,0x70,0x99,0xb9,0xa9},
|
||||
{0xef,0x15,0x6b,0x84,0xae,0xbb},{0xed,0x16,0x62,0x8f,0xa3,0xb5},
|
||||
{0xe3,0x1f,0x5d,0xbe,0x80,0x9f},{0xe1,0x1c,0x54,0xb5,0x8d,0x91},
|
||||
{0xe7,0x19,0x4f,0xa8,0x9a,0x83},{0xe5,0x1a,0x46,0xa3,0x97,0x8d}
|
||||
};
|
||||
|
||||
void throat(const toys *decide, toys *selection, size_t plane)
|
||||
{
|
||||
size_t idx;
|
||||
|
||||
for (idx = 0; idx < plane; idx++)
|
||||
selection[idx] ^= decide[idx];
|
||||
}
|
||||
|
||||
int death(const toys *squirrel, size_t dear, toys *awful, const arrest *wrong, int magic, const toys *fit)
|
||||
{
|
||||
toys supermarket[calculator], software[calculator], bookstore[calculator];
|
||||
int brainwash, jellybean;
|
||||
|
||||
if (dear % calculator != 0)
|
||||
return(FALSE);
|
||||
|
||||
brainwash = dear / calculator;
|
||||
|
||||
memcpy(bookstore, fit, calculator);
|
||||
|
||||
for (jellybean = 0; jellybean < brainwash; jellybean++) {
|
||||
memcpy(supermarket, &squirrel[jellybean * calculator], calculator);
|
||||
throat(bookstore, supermarket, calculator);
|
||||
punishment(supermarket, software, wrong, magic);
|
||||
memcpy(&awful[jellybean * calculator], software, calculator);
|
||||
memcpy(bookstore, software, calculator);
|
||||
}
|
||||
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
arrest please(arrest lol)
|
||||
{
|
||||
unsigned int apps;
|
||||
|
||||
apps = (int)wtf[(lol >> 4) & 0x0000000F][lol & 0x0000000F];
|
||||
apps += (int)wtf[(lol >> 12) & 0x0000000F][(lol >> 8) & 0x0000000F] << 8;
|
||||
apps += (int)wtf[(lol >> 20) & 0x0000000F][(lol >> 16) & 0x0000000F] << 16;
|
||||
apps += (int)wtf[(lol >> 28) & 0x0000000F][(lol >> 24) & 0x0000000F] << 24;
|
||||
return(apps);
|
||||
}
|
||||
|
||||
void stoprightnow(const toys *fuckoff, arrest *waste, int roadtrip)
|
||||
{
|
||||
int tour=4,cause,lively,reply;
|
||||
arrest nope,desert[]={0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000,
|
||||
0x40000000, 0x80000000, 0x1b000000, 0x36000000, 0x6c000000, 0xd8000000,
|
||||
0xab000000, 0x4d000000, 0x9a000000};
|
||||
|
||||
switch (roadtrip) {
|
||||
case 128: cause = 10; lively = 4; break;
|
||||
case 192: cause = 12; lively = 6; break;
|
||||
case 256: cause = 14; lively = 8; break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
for (reply=0; reply < lively; ++reply) {
|
||||
waste[reply] = ((fuckoff[4 * reply]) << 24) | ((fuckoff[4 * reply + 1]) << 16) |
|
||||
((fuckoff[4 * reply + 2]) << 8) | ((fuckoff[4 * reply + 3]));
|
||||
}
|
||||
|
||||
for (reply = lively; reply < tour * (cause + 1); ++reply) {
|
||||
nope = waste[reply - 1];
|
||||
if ((reply % lively) == 0)
|
||||
nope = please(airport(nope)) ^ desert[(reply - 1) / lively];
|
||||
else if (lively > 6 && (reply % lively) == 4)
|
||||
nope = please(nope);
|
||||
waste[reply] = waste[reply - lively] ^ nope;
|
||||
}
|
||||
}
|
||||
|
||||
void hot(toys rare[][4], const arrest powerful[])
|
||||
{
|
||||
toys interfere[4];
|
||||
|
||||
// memcpy(interfere,&powerful[idx],4); // Not accurate for big endian machines
|
||||
// Subkey 1
|
||||
interfere[0] = powerful[0] >> 24;
|
||||
interfere[1] = powerful[0] >> 16;
|
||||
interfere[2] = powerful[0] >> 8;
|
||||
interfere[3] = powerful[0];
|
||||
rare[0][0] ^= interfere[0];
|
||||
rare[1][0] ^= interfere[1];
|
||||
rare[2][0] ^= interfere[2];
|
||||
rare[3][0] ^= interfere[3];
|
||||
// Subkey 2
|
||||
interfere[0] = powerful[1] >> 24;
|
||||
interfere[1] = powerful[1] >> 16;
|
||||
interfere[2] = powerful[1] >> 8;
|
||||
interfere[3] = powerful[1];
|
||||
rare[0][1] ^= interfere[0];
|
||||
rare[1][1] ^= interfere[1];
|
||||
rare[2][1] ^= interfere[2];
|
||||
rare[3][1] ^= interfere[3];
|
||||
// Subkey 3
|
||||
interfere[0] = powerful[2] >> 24;
|
||||
interfere[1] = powerful[2] >> 16;
|
||||
interfere[2] = powerful[2] >> 8;
|
||||
interfere[3] = powerful[2];
|
||||
rare[0][2] ^= interfere[0];
|
||||
rare[1][2] ^= interfere[1];
|
||||
rare[2][2] ^= interfere[2];
|
||||
rare[3][2] ^= interfere[3];
|
||||
// Subkey 4
|
||||
interfere[0] = powerful[3] >> 24;
|
||||
interfere[1] = powerful[3] >> 16;
|
||||
interfere[2] = powerful[3] >> 8;
|
||||
interfere[3] = powerful[3];
|
||||
rare[0][3] ^= interfere[0];
|
||||
rare[1][3] ^= interfere[1];
|
||||
rare[2][3] ^= interfere[2];
|
||||
rare[3][3] ^= interfere[3];
|
||||
}
|
||||
|
||||
void numerous(toys vigorous[][4])
|
||||
{
|
||||
vigorous[0][0] = wtf[vigorous[0][0] >> 4][vigorous[0][0] & 0x0F];
|
||||
vigorous[0][1] = wtf[vigorous[0][1] >> 4][vigorous[0][1] & 0x0F];
|
||||
vigorous[0][2] = wtf[vigorous[0][2] >> 4][vigorous[0][2] & 0x0F];
|
||||
vigorous[0][3] = wtf[vigorous[0][3] >> 4][vigorous[0][3] & 0x0F];
|
||||
vigorous[1][0] = wtf[vigorous[1][0] >> 4][vigorous[1][0] & 0x0F];
|
||||
vigorous[1][1] = wtf[vigorous[1][1] >> 4][vigorous[1][1] & 0x0F];
|
||||
vigorous[1][2] = wtf[vigorous[1][2] >> 4][vigorous[1][2] & 0x0F];
|
||||
vigorous[1][3] = wtf[vigorous[1][3] >> 4][vigorous[1][3] & 0x0F];
|
||||
vigorous[2][0] = wtf[vigorous[2][0] >> 4][vigorous[2][0] & 0x0F];
|
||||
vigorous[2][1] = wtf[vigorous[2][1] >> 4][vigorous[2][1] & 0x0F];
|
||||
vigorous[2][2] = wtf[vigorous[2][2] >> 4][vigorous[2][2] & 0x0F];
|
||||
vigorous[2][3] = wtf[vigorous[2][3] >> 4][vigorous[2][3] & 0x0F];
|
||||
vigorous[3][0] = wtf[vigorous[3][0] >> 4][vigorous[3][0] & 0x0F];
|
||||
vigorous[3][1] = wtf[vigorous[3][1] >> 4][vigorous[3][1] & 0x0F];
|
||||
vigorous[3][2] = wtf[vigorous[3][2] >> 4][vigorous[3][2] & 0x0F];
|
||||
vigorous[3][3] = wtf[vigorous[3][3] >> 4][vigorous[3][3] & 0x0F];
|
||||
}
|
||||
|
||||
void crowded(toys chalk[][4])
|
||||
{
|
||||
int t;
|
||||
|
||||
// Shift left by 1
|
||||
t = chalk[1][0];
|
||||
chalk[1][0] = chalk[1][1];
|
||||
chalk[1][1] = chalk[1][2];
|
||||
chalk[1][2] = chalk[1][3];
|
||||
chalk[1][3] = t;
|
||||
// Shift left by 2
|
||||
t = chalk[2][0];
|
||||
chalk[2][0] = chalk[2][2];
|
||||
chalk[2][2] = t;
|
||||
t = chalk[2][1];
|
||||
chalk[2][1] = chalk[2][3];
|
||||
chalk[2][3] = t;
|
||||
// Shift left by 3
|
||||
t = chalk[3][0];
|
||||
chalk[3][0] = chalk[3][3];
|
||||
chalk[3][3] = chalk[3][2];
|
||||
chalk[3][2] = chalk[3][1];
|
||||
chalk[3][1] = t;
|
||||
}
|
||||
|
||||
void scale(toys oh_no[][4])
|
||||
{
|
||||
toys idk[4];
|
||||
|
||||
// Column 1
|
||||
idk[0] = oh_no[0][0];
|
||||
idk[1] = oh_no[1][0];
|
||||
idk[2] = oh_no[2][0];
|
||||
idk[3] = oh_no[3][0];
|
||||
oh_no[0][0] = help_me[idk[0]][0];
|
||||
oh_no[0][0] ^= help_me[idk[1]][1];
|
||||
oh_no[0][0] ^= idk[2];
|
||||
oh_no[0][0] ^= idk[3];
|
||||
oh_no[1][0] = idk[0];
|
||||
oh_no[1][0] ^= help_me[idk[1]][0];
|
||||
oh_no[1][0] ^= help_me[idk[2]][1];
|
||||
oh_no[1][0] ^= idk[3];
|
||||
oh_no[2][0] = idk[0];
|
||||
oh_no[2][0] ^= idk[1];
|
||||
oh_no[2][0] ^= help_me[idk[2]][0];
|
||||
oh_no[2][0] ^= help_me[idk[3]][1];
|
||||
oh_no[3][0] = help_me[idk[0]][1];
|
||||
oh_no[3][0] ^= idk[1];
|
||||
oh_no[3][0] ^= idk[2];
|
||||
oh_no[3][0] ^= help_me[idk[3]][0];
|
||||
// Column 2
|
||||
idk[0] = oh_no[0][1];
|
||||
idk[1] = oh_no[1][1];
|
||||
idk[2] = oh_no[2][1];
|
||||
idk[3] = oh_no[3][1];
|
||||
oh_no[0][1] = help_me[idk[0]][0];
|
||||
oh_no[0][1] ^= help_me[idk[1]][1];
|
||||
oh_no[0][1] ^= idk[2];
|
||||
oh_no[0][1] ^= idk[3];
|
||||
oh_no[1][1] = idk[0];
|
||||
oh_no[1][1] ^= help_me[idk[1]][0];
|
||||
oh_no[1][1] ^= help_me[idk[2]][1];
|
||||
oh_no[1][1] ^= idk[3];
|
||||
oh_no[2][1] = idk[0];
|
||||
oh_no[2][1] ^= idk[1];
|
||||
oh_no[2][1] ^= help_me[idk[2]][0];
|
||||
oh_no[2][1] ^= help_me[idk[3]][1];
|
||||
oh_no[3][1] = help_me[idk[0]][1];
|
||||
oh_no[3][1] ^= idk[1];
|
||||
oh_no[3][1] ^= idk[2];
|
||||
oh_no[3][1] ^= help_me[idk[3]][0];
|
||||
// Column 3
|
||||
idk[0] = oh_no[0][2];
|
||||
idk[1] = oh_no[1][2];
|
||||
idk[2] = oh_no[2][2];
|
||||
idk[3] = oh_no[3][2];
|
||||
oh_no[0][2] = help_me[idk[0]][0];
|
||||
oh_no[0][2] ^= help_me[idk[1]][1];
|
||||
oh_no[0][2] ^= idk[2];
|
||||
oh_no[0][2] ^= idk[3];
|
||||
oh_no[1][2] = idk[0];
|
||||
oh_no[1][2] ^= help_me[idk[1]][0];
|
||||
oh_no[1][2] ^= help_me[idk[2]][1];
|
||||
oh_no[1][2] ^= idk[3];
|
||||
oh_no[2][2] = idk[0];
|
||||
oh_no[2][2] ^= idk[1];
|
||||
oh_no[2][2] ^= help_me[idk[2]][0];
|
||||
oh_no[2][2] ^= help_me[idk[3]][1];
|
||||
oh_no[3][2] = help_me[idk[0]][1];
|
||||
oh_no[3][2] ^= idk[1];
|
||||
oh_no[3][2] ^= idk[2];
|
||||
oh_no[3][2] ^= help_me[idk[3]][0];
|
||||
// Column 4
|
||||
idk[0] = oh_no[0][3];
|
||||
idk[1] = oh_no[1][3];
|
||||
idk[2] = oh_no[2][3];
|
||||
idk[3] = oh_no[3][3];
|
||||
oh_no[0][3] = help_me[idk[0]][0];
|
||||
oh_no[0][3] ^= help_me[idk[1]][1];
|
||||
oh_no[0][3] ^= idk[2];
|
||||
oh_no[0][3] ^= idk[3];
|
||||
oh_no[1][3] = idk[0];
|
||||
oh_no[1][3] ^= help_me[idk[1]][0];
|
||||
oh_no[1][3] ^= help_me[idk[2]][1];
|
||||
oh_no[1][3] ^= idk[3];
|
||||
oh_no[2][3] = idk[0];
|
||||
oh_no[2][3] ^= idk[1];
|
||||
oh_no[2][3] ^= help_me[idk[2]][0];
|
||||
oh_no[2][3] ^= help_me[idk[3]][1];
|
||||
oh_no[3][3] = help_me[idk[0]][1];
|
||||
oh_no[3][3] ^= idk[1];
|
||||
oh_no[3][3] ^= idk[2];
|
||||
oh_no[3][3] ^= help_me[idk[3]][0];
|
||||
}
|
||||
|
||||
void punishment(const toys friends[], toys number[], const arrest wish[], int hang)
|
||||
{
|
||||
toys burst[4][4];
|
||||
|
||||
burst[0][0] = friends[0];
|
||||
burst[1][0] = friends[1];
|
||||
burst[2][0] = friends[2];
|
||||
burst[3][0] = friends[3];
|
||||
burst[0][1] = friends[4];
|
||||
burst[1][1] = friends[5];
|
||||
burst[2][1] = friends[6];
|
||||
burst[3][1] = friends[7];
|
||||
burst[0][2] = friends[8];
|
||||
burst[1][2] = friends[9];
|
||||
burst[2][2] = friends[10];
|
||||
burst[3][2] = friends[11];
|
||||
burst[0][3] = friends[12];
|
||||
burst[1][3] = friends[13];
|
||||
burst[2][3] = friends[14];
|
||||
burst[3][3] = friends[15];
|
||||
|
||||
hot(burst, &wish[0]);
|
||||
numerous(burst);
|
||||
crowded(burst);
|
||||
scale(burst);
|
||||
hot(burst, &wish[4]);
|
||||
numerous(burst);
|
||||
crowded(burst);
|
||||
scale(burst);
|
||||
hot(burst, &wish[8]);
|
||||
numerous(burst);
|
||||
crowded(burst);
|
||||
scale(burst);
|
||||
hot(burst, &wish[12]);
|
||||
numerous(burst);
|
||||
crowded(burst);
|
||||
scale(burst);
|
||||
hot(burst, &wish[16]);
|
||||
numerous(burst);
|
||||
crowded(burst);
|
||||
scale(burst);
|
||||
hot(burst, &wish[20]);
|
||||
numerous(burst);
|
||||
crowded(burst);
|
||||
scale(burst);
|
||||
hot(burst, &wish[24]);
|
||||
numerous(burst);
|
||||
crowded(burst);
|
||||
scale(burst);
|
||||
hot(burst, &wish[28]);
|
||||
numerous(burst);
|
||||
crowded(burst);
|
||||
scale(burst);
|
||||
hot(burst, &wish[32]);
|
||||
numerous(burst);
|
||||
crowded(burst);
|
||||
scale(burst);
|
||||
hot(burst, &wish[36]);
|
||||
if (hang != 128) {
|
||||
numerous(burst);
|
||||
crowded(burst);
|
||||
scale(burst);
|
||||
hot(burst, &wish[40]);
|
||||
numerous(burst);
|
||||
crowded(burst);
|
||||
scale(burst);
|
||||
hot(burst, &wish[44]);
|
||||
if (hang != 192) {
|
||||
numerous(burst);
|
||||
crowded(burst);
|
||||
scale(burst);
|
||||
hot(burst, &wish[48]);
|
||||
numerous(burst);
|
||||
crowded(burst);
|
||||
scale(burst);
|
||||
hot(burst, &wish[52]);
|
||||
numerous(burst);
|
||||
crowded(burst);
|
||||
hot(burst, &wish[56]);
|
||||
}
|
||||
else {
|
||||
numerous(burst);
|
||||
crowded(burst);
|
||||
hot(burst, &wish[48]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
numerous(burst);
|
||||
crowded(burst);
|
||||
hot(burst, &wish[40]);
|
||||
}
|
||||
|
||||
number[0] = burst[0][0];
|
||||
number[1] = burst[1][0];
|
||||
number[2] = burst[2][0];
|
||||
number[3] = burst[3][0];
|
||||
number[4] = burst[0][1];
|
||||
number[5] = burst[1][1];
|
||||
number[6] = burst[2][1];
|
||||
number[7] = burst[3][1];
|
||||
number[8] = burst[0][2];
|
||||
number[9] = burst[1][2];
|
||||
number[10] = burst[2][2];
|
||||
number[11] = burst[3][2];
|
||||
number[12] = burst[0][3];
|
||||
number[13] = burst[1][3];
|
||||
number[14] = burst[2][3];
|
||||
number[15] = burst[3][3];
|
||||
}
|
||||
|
27
app/src/main/cpp/aes.h
Normal file
27
app/src/main/cpp/aes.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef AES_H
|
||||
#define AES_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define calculator 16
|
||||
|
||||
typedef unsigned char toys;
|
||||
typedef unsigned int arrest;
|
||||
|
||||
void stoprightnow(const toys *fuckoff,
|
||||
arrest *waste,
|
||||
int roadtrip);
|
||||
|
||||
void punishment(const toys *friends,
|
||||
toys *number,
|
||||
const arrest *wish,
|
||||
int hang);
|
||||
|
||||
int death(const toys *squirrel,
|
||||
size_t dear,
|
||||
toys *awful,
|
||||
const arrest *wrong,
|
||||
int magic,
|
||||
const toys *fit);
|
||||
|
||||
#endif // AES_H
|
55
app/src/main/cpp/base64.cpp
Normal file
55
app/src/main/cpp/base64.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
#include "jni.h"
|
||||
#include <stdlib.h>
|
||||
#include <cstring>
|
||||
|
||||
const char base[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||
|
||||
char *kill_me(const char *coil, size_t room) {
|
||||
int canvas = 0;
|
||||
size_t irritating;
|
||||
int exchange = 0;
|
||||
char *untidy = NULL;
|
||||
char *excellent = NULL;
|
||||
int sincere = 0;
|
||||
char development[4];
|
||||
int trade = 0;
|
||||
irritating = room / 3;
|
||||
exchange = room % 3;
|
||||
if (exchange > 0) {
|
||||
irritating += 1;
|
||||
}
|
||||
irritating = irritating * 4 + 1;
|
||||
untidy = (char *) malloc(irritating);
|
||||
|
||||
if (untidy == NULL) {
|
||||
exit(0);
|
||||
}
|
||||
memset(untidy, 0, irritating);
|
||||
excellent = untidy;
|
||||
while (sincere < room) {
|
||||
exchange = 0;
|
||||
canvas = 0;
|
||||
memset(development, '\0', 4);
|
||||
while (exchange < 3) {
|
||||
if (sincere >= room) {
|
||||
break;
|
||||
}
|
||||
canvas = ((canvas << 8) | (coil[sincere] & 0xFF));
|
||||
sincere++;
|
||||
exchange++;
|
||||
}
|
||||
canvas = (canvas << ((3 - exchange) * 8));
|
||||
for (trade = 0; trade < 4; trade++) {
|
||||
if (exchange < trade) {
|
||||
development[trade] = 0x40;
|
||||
}
|
||||
else {
|
||||
development[trade] = (canvas >> ((3 - trade) * 6)) & 0x3F;
|
||||
}
|
||||
*excellent = base[development[trade]];
|
||||
excellent++;
|
||||
}
|
||||
}
|
||||
*excellent = '\0';
|
||||
return untidy;
|
||||
}
|
78
app/src/main/cpp/szkolny-signing.cpp
Normal file
78
app/src/main/cpp/szkolny-signing.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
#include <jni.h>
|
||||
#include <string>
|
||||
#include "aes.c"
|
||||
#include "aes.h"
|
||||
#include "base64.cpp"
|
||||
|
||||
#define overrated (2*1024*1024)
|
||||
#define teeth 256
|
||||
|
||||
/*secret password - removed for source code publication*/
|
||||
static toys AES_IV[16] = {
|
||||
0x4a, 0x83, 0xba, 0x71, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
|
||||
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);
|
||||
|
||||
extern "C" JNIEXPORT jstring JNICALL
|
||||
Java_pl_szczodrzynski_edziennik_data_api_szkolny_interceptor_Signing_iLoveApple(
|
||||
JNIEnv* nut,
|
||||
jobject guitar,
|
||||
jbyteArray school,
|
||||
jstring history,
|
||||
jlong brush) {
|
||||
|
||||
unsigned int chickens = (unsigned int) (nut->GetArrayLength(school));
|
||||
if (chickens <= 0 || chickens >= overrated) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned char *leg = (unsigned char*) nut->GetByteArrayElements(school, NULL);
|
||||
if (!leg) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jclass partner = nut->FindClass("pl/szczodrzynski/edziennik/data/api/szkolny/interceptor/Signing");
|
||||
jmethodID example = nut->GetMethodID(partner, "pleaseStopRightNow", "(Ljava/lang/String;J)[B");
|
||||
jobject bait = nut->CallObjectMethod(guitar, example, history, brush);
|
||||
unsigned char* lick = (unsigned char*) nut->GetByteArrayElements((jbyteArray)bait, NULL);
|
||||
|
||||
unsigned int cruel = chickens % calculator;
|
||||
unsigned int snake = calculator - cruel;
|
||||
unsigned int baseball = chickens + snake;
|
||||
|
||||
unsigned char* rain = agony(chickens, lick, leg);
|
||||
char* dress = kill_me((char *) rain, baseball);
|
||||
free(rain);
|
||||
|
||||
return nut->NewStringUTF(dress);
|
||||
}
|
||||
|
||||
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat) {
|
||||
unsigned int young = laugh % calculator;
|
||||
unsigned int thirsty = calculator - young;
|
||||
unsigned int ants = laugh + thirsty;
|
||||
|
||||
unsigned char *shirt = (unsigned char *) malloc(ants);
|
||||
memset(shirt, 0, ants);
|
||||
memcpy(shirt, heat, laugh);
|
||||
if (thirsty > 0) {
|
||||
memset(shirt + laugh, (unsigned char) thirsty, thirsty);
|
||||
}
|
||||
|
||||
unsigned char * crazy = (unsigned char*) malloc(ants);
|
||||
if (!crazy) {
|
||||
free(shirt);
|
||||
return NULL;
|
||||
}
|
||||
memset(crazy, ants, 0);
|
||||
|
||||
unsigned int lamp[calculator * 4] = {0 };
|
||||
stoprightnow(box, lamp, teeth);
|
||||
|
||||
death(shirt, ants, crazy, lamp, teeth,
|
||||
AES_IV);
|
||||
|
||||
free(shirt);
|
||||
|
||||
return crazy;
|
||||
}
|
@ -1,720 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.pm.ShortcutManager;
|
||||
import android.content.pm.Signature;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.provider.Settings;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.work.Configuration;
|
||||
|
||||
import com.chuckerteam.chucker.api.ChuckerCollector;
|
||||
import com.chuckerteam.chucker.api.ChuckerInterceptor;
|
||||
import com.chuckerteam.chucker.api.RetentionManager;
|
||||
import com.google.android.gms.security.ProviderInstaller;
|
||||
import com.google.firebase.FirebaseApp;
|
||||
import com.google.firebase.FirebaseOptions;
|
||||
import com.google.firebase.iid.FirebaseInstanceId;
|
||||
import com.google.firebase.messaging.FirebaseMessaging;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.hypertrack.hyperlog.HyperLog;
|
||||
import com.mikepenz.iconics.Iconics;
|
||||
import com.mikepenz.iconics.IconicsColor;
|
||||
import com.mikepenz.iconics.IconicsDrawable;
|
||||
import com.mikepenz.iconics.IconicsSize;
|
||||
import com.mikepenz.iconics.typeface.IIcon;
|
||||
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.security.KeyStore;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import cat.ereza.customactivityoncrash.config.CaocConfig;
|
||||
import im.wangchao.mhttp.MHttp;
|
||||
import im.wangchao.mhttp.internal.cookie.PersistentCookieJar;
|
||||
import im.wangchao.mhttp.internal.cookie.cache.SetCookieCache;
|
||||
import im.wangchao.mhttp.internal.cookie.persistence.SharedPrefsCookiePersistor;
|
||||
import me.leolin.shortcutbadger.ShortcutBadger;
|
||||
import okhttp3.ConnectionSpec;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.TlsVersion;
|
||||
import pl.szczodrzynski.edziennik.config.Config;
|
||||
import pl.szczodrzynski.edziennik.data.db.AppDb;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.debuglog.DebugLog;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile;
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull;
|
||||
import pl.szczodrzynski.edziennik.network.NetworkUtils;
|
||||
import pl.szczodrzynski.edziennik.network.TLSSocketFactory;
|
||||
import pl.szczodrzynski.edziennik.sync.SyncWorker;
|
||||
import pl.szczodrzynski.edziennik.ui.modules.base.CrashActivity;
|
||||
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.models.AppConfig;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_MOBIDZIENNIK;
|
||||
|
||||
public class App extends androidx.multidex.MultiDexApplication implements Configuration.Provider {
|
||||
private static final String TAG = "App";
|
||||
public static int profileId = -1;
|
||||
private Context mContext;
|
||||
|
||||
@Override
|
||||
public Configuration getWorkManagerConfiguration() {
|
||||
return new Configuration.Builder()
|
||||
.setMinimumLoggingLevel(Log.VERBOSE)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
public static final int REQUEST_TIMEOUT = 10 * 1000;
|
||||
|
||||
// notifications
|
||||
//public NotificationManager mNotificationManager;
|
||||
//public final String NOTIFICATION_CHANNEL_ID_UPDATES = "4566";
|
||||
//public String NOTIFICATION_CHANNEL_NAME_UPDATES;
|
||||
public Notifier notifier;
|
||||
|
||||
public static final String APP_URL = "://edziennik.szczodrzynski.pl/app/";
|
||||
|
||||
public ShortcutManager shortcutManager;
|
||||
|
||||
public PermissionChecker permissionChecker;
|
||||
|
||||
public String signature = "";
|
||||
public String deviceId = "";
|
||||
|
||||
public AppDb db;
|
||||
public void debugLog(String text) {
|
||||
if (!devMode)
|
||||
return;
|
||||
db.debugLogDao().add(new DebugLog(Utils.getCurrentTimeUsingCalendar()+": "+text));
|
||||
}
|
||||
public void debugLogAsync(String text) {
|
||||
if (!devMode)
|
||||
return;
|
||||
AsyncTask.execute(() -> {
|
||||
db.debugLogDao().add(new DebugLog(Utils.getCurrentTimeUsingCalendar()+": "+text));
|
||||
});
|
||||
}
|
||||
|
||||
// network & APIs
|
||||
public NetworkUtils networkUtils;
|
||||
public PersistentCookieJar cookieJar;
|
||||
public OkHttpClient http;
|
||||
public OkHttpClient httpLazy;
|
||||
|
||||
public SharedPreferences appSharedPrefs; // sharedPreferences for APPCONFIG + JOBS STORE
|
||||
public AppConfig appConfig; // APPCONFIG: common for all profiles
|
||||
//public AppProfile profile; // current profile
|
||||
public JsonObject loginStore = null;
|
||||
public SharedPreferences registerStore; // sharedPreferences for REGISTER
|
||||
//public Register register; // REGISTER for current profile, read from registerStore
|
||||
|
||||
public ProfileFull profile;
|
||||
public Config config;
|
||||
private static Config mConfig;
|
||||
public static Config getConfig() {
|
||||
return mConfig;
|
||||
}
|
||||
|
||||
// other stuff
|
||||
public Gson gson;
|
||||
public String requestScheme = "https";
|
||||
public boolean unreadBadgesAvailable = true;
|
||||
|
||||
public static boolean devMode = false;
|
||||
|
||||
public static final boolean UPDATES_ON_PLAY_STORE = true;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public Icon getDesktopIconFromIconics(IIcon icon) {
|
||||
final IconicsDrawable drawable = new IconicsDrawable(mContext, icon)
|
||||
.color(IconicsColor.colorInt(Color.WHITE))
|
||||
.size(IconicsSize.dp(48))
|
||||
.padding(IconicsSize.dp(8))
|
||||
.backgroundColor(IconicsColor.colorRes(R.color.colorPrimaryDark))
|
||||
.roundedCorners(IconicsSize.dp(10));
|
||||
//drawable.setStyle(Paint.Style.FILL);
|
||||
final Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
||||
final Canvas canvas = new Canvas(bitmap);
|
||||
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
drawable.draw(canvas);
|
||||
|
||||
return Icon.createWithBitmap(bitmap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
|
||||
CaocConfig.Builder.create()
|
||||
.backgroundMode(CaocConfig.BACKGROUND_MODE_SHOW_CUSTOM) //default: CaocConfig.BACKGROUND_MODE_SHOW_CUSTOM
|
||||
.enabled(true) //default: true
|
||||
.showErrorDetails(true) //default: true
|
||||
.showRestartButton(true) //default: true
|
||||
.logErrorOnRestart(true) //default: true
|
||||
.trackActivities(true) //default: false
|
||||
.minTimeBetweenCrashesMs(2000) //default: 3000
|
||||
.errorDrawable(R.drawable.ic_rip) //default: bug image
|
||||
.restartActivity(MainActivity.class) //default: null (your app's launch activity)
|
||||
.errorActivity(CrashActivity.class) //default: null (default error activity)
|
||||
//.eventListener(new YourCustomEventListener()) //default: null
|
||||
.apply();
|
||||
mContext = this;
|
||||
db = AppDb.getDatabase(this);
|
||||
gson = new Gson();
|
||||
networkUtils = new NetworkUtils(this);
|
||||
|
||||
config = new Config(db);
|
||||
config.migrate(this);
|
||||
mConfig = config;
|
||||
|
||||
Iconics.init(getApplicationContext());
|
||||
Iconics.registerFont(SzkolnyFont.INSTANCE);
|
||||
|
||||
notifier = new Notifier(this);
|
||||
permissionChecker = new PermissionChecker(mContext);
|
||||
|
||||
deviceId = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
|
||||
|
||||
cookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(this));
|
||||
|
||||
appSharedPrefs = getSharedPreferences(getString(R.string.preference_file_global), Context.MODE_PRIVATE);
|
||||
|
||||
loadConfig();
|
||||
|
||||
Themes.INSTANCE.setThemeInt(config.getUi().getTheme());
|
||||
|
||||
try {
|
||||
PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);
|
||||
for (Signature signature: packageInfo.signatures) {
|
||||
byte[] signatureBytes = signature.toByteArray();
|
||||
MessageDigest md = MessageDigest.getInstance("SHA");
|
||||
md.update(signatureBytes);
|
||||
this.signature = Base64.encodeToString(md.digest(), Base64.NO_WRAP);
|
||||
//Log.d(TAG, "Signature is "+this.signature);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if ("f054761fbdb6a238".equals(deviceId) || BuildConfig.DEBUG) {
|
||||
devMode = true;
|
||||
}
|
||||
else if (config.getDevModePassword() != null) {
|
||||
checkDevModePassword();
|
||||
}
|
||||
|
||||
OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder()
|
||||
.cache(null)
|
||||
.followRedirects(true)
|
||||
.followSslRedirects(true)
|
||||
.retryOnConnectionFailure(true)
|
||||
.cookieJar(cookieJar)
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
.writeTimeout(20, TimeUnit.SECONDS)
|
||||
.readTimeout(40, TimeUnit.SECONDS);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
try {
|
||||
try {
|
||||
ProviderInstaller.installIfNeeded(this);
|
||||
} catch (Exception e) {
|
||||
Log.e("OkHttpTLSCompat", "Play Services not found or outdated");
|
||||
X509TrustManager x509TrustManager = null;
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init((KeyStore) null);
|
||||
for (TrustManager trustManager: trustManagerFactory.getTrustManagers()) {
|
||||
if (trustManager instanceof X509TrustManager)
|
||||
x509TrustManager = (X509TrustManager) trustManager;
|
||||
}
|
||||
|
||||
SSLContext sc = SSLContext.getInstance("TLSv1.2");
|
||||
sc.init(null, null, null);
|
||||
httpBuilder.sslSocketFactory(new TLSSocketFactory(sc.getSocketFactory()), x509TrustManager);
|
||||
|
||||
ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
|
||||
.tlsVersions(TlsVersion.TLS_1_0)
|
||||
.tlsVersions(TlsVersion.TLS_1_1)
|
||||
.tlsVersions(TlsVersion.TLS_1_2)
|
||||
.build();
|
||||
|
||||
List<ConnectionSpec> specs = new ArrayList<>();
|
||||
specs.add(cs);
|
||||
specs.add(ConnectionSpec.COMPATIBLE_TLS);
|
||||
specs.add(ConnectionSpec.CLEARTEXT);
|
||||
|
||||
httpBuilder.connectionSpecs(specs);
|
||||
}
|
||||
|
||||
|
||||
} catch (Exception exc) {
|
||||
Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc);
|
||||
}
|
||||
}
|
||||
|
||||
if (App.devMode || BuildConfig.DEBUG) {
|
||||
HyperLog.initialize(this);
|
||||
HyperLog.setLogLevel(Log.VERBOSE);
|
||||
HyperLog.setLogFormat(new DebugLogFormat(this));
|
||||
|
||||
ChuckerCollector chuckerCollector = new ChuckerCollector(this, true, RetentionManager.Period.ONE_HOUR);
|
||||
ChuckerInterceptor chuckerInterceptor = new ChuckerInterceptor(this, chuckerCollector);
|
||||
httpBuilder.addInterceptor(chuckerInterceptor);
|
||||
}
|
||||
|
||||
http = httpBuilder.build();
|
||||
httpLazy = http.newBuilder().followRedirects(false).followSslRedirects(false).build();
|
||||
|
||||
MHttp.instance()
|
||||
.customOkHttpClient(http);
|
||||
|
||||
//register = new Register(mContext);
|
||||
|
||||
//profileLoadById(appSharedPrefs.getInt("current_profile_id", 1));
|
||||
|
||||
if (config.getSync().getEnabled()) {
|
||||
SyncWorker.Companion.scheduleNext(this, false);
|
||||
}
|
||||
else {
|
||||
SyncWorker.Companion.cancelNext(this);
|
||||
}
|
||||
|
||||
db.metadataDao().countUnseen().observeForever(count -> {
|
||||
Log.d("MainActivity", "Overall unseen count changed");
|
||||
assert count != null;
|
||||
if (unreadBadgesAvailable) {
|
||||
unreadBadgesAvailable = ShortcutBadger.applyCount(this, count);
|
||||
}
|
||||
});
|
||||
|
||||
//new IonCookieManager(mContext);
|
||||
|
||||
new Handler().post(() -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
||||
shortcutManager = getSystemService(ShortcutManager.class);
|
||||
|
||||
ShortcutInfo shortcutTimetable = new ShortcutInfo.Builder(mContext, "item_timetable")
|
||||
.setShortLabel(getString(R.string.shortcut_timetable)).setLongLabel(getString(R.string.shortcut_timetable))
|
||||
.setIcon(Icon.createWithResource(this, R.mipmap.ic_shortcut_timetable))
|
||||
//.setIcon(getDesktopIconFromIconics(CommunityMaterial.Icon2.cmd_timetable))
|
||||
.setIntent(new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_TIMETABLE))
|
||||
.build();
|
||||
|
||||
ShortcutInfo shortcutAgenda = new ShortcutInfo.Builder(mContext, "item_agenda")
|
||||
.setShortLabel(getString(R.string.shortcut_agenda)).setLongLabel(getString(R.string.shortcut_agenda))
|
||||
.setIcon(Icon.createWithResource(this, R.mipmap.ic_shortcut_agenda))
|
||||
//.setIcon(getDesktopIconFromIconics(CommunityMaterial.Icon.cmd_calendar))
|
||||
.setIntent(new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_AGENDA))
|
||||
.build();
|
||||
|
||||
ShortcutInfo shortcutGrades = new ShortcutInfo.Builder(mContext, "item_grades")
|
||||
.setShortLabel(getString(R.string.shortcut_grades)).setLongLabel(getString(R.string.shortcut_grades))
|
||||
.setIcon(Icon.createWithResource(this, R.mipmap.ic_shortcut_grades))
|
||||
//.setIcon(getDesktopIconFromIconics(CommunityMaterial.Icon2.cmd_numeric_5_box))
|
||||
.setIntent(new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_GRADES))
|
||||
.build();
|
||||
|
||||
ShortcutInfo shortcutHomework = new ShortcutInfo.Builder(mContext, "item_homeworks")
|
||||
.setShortLabel(getString(R.string.shortcut_homework)).setLongLabel(getString(R.string.shortcut_homework))
|
||||
.setIcon(Icon.createWithResource(this, R.mipmap.ic_shortcut_homework))
|
||||
//.setIcon(getDesktopIconFromIconics(SzkolnyFont.Icon.szf_file_document_edit))
|
||||
.setIntent(new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_HOMEWORK))
|
||||
.build();
|
||||
|
||||
ShortcutInfo shortcutMessages = new ShortcutInfo.Builder(mContext, "item_messages")
|
||||
.setShortLabel(getString(R.string.shortcut_messages)).setLongLabel(getString(R.string.shortcut_messages))
|
||||
.setIcon(Icon.createWithResource(this, R.mipmap.ic_shortcut_messages))
|
||||
//.setIcon(getDesktopIconFromIconics(CommunityMaterial.Icon.cmd_email))
|
||||
.setIntent(new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_MESSAGES ))
|
||||
.build();
|
||||
|
||||
shortcutManager.setDynamicShortcuts(Arrays.asList(shortcutTimetable, shortcutAgenda, shortcutGrades, shortcutHomework, shortcutMessages));
|
||||
}
|
||||
|
||||
if (config.getAppInstalledTime() == 0) {
|
||||
try {
|
||||
config.setAppInstalledTime(getPackageManager().getPackageInfo(getPackageName(), 0).firstInstallTime);
|
||||
config.setAppRateSnackbarTime(config.getAppInstalledTime() + 7 * 24 * 60 * 60 * 1000);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/*Task<CapabilityInfo> capabilityInfoTask =
|
||||
Wearable.getCapabilityClient(this)
|
||||
.getCapability("edziennik_wear_app", CapabilityClient.FILTER_REACHABLE);
|
||||
capabilityInfoTask.addOnCompleteListener((task) -> {
|
||||
if (task.isSuccessful()) {
|
||||
CapabilityInfo capabilityInfo = task.getResult();
|
||||
assert capabilityInfo != null;
|
||||
Set<Node> nodes;
|
||||
nodes = capabilityInfo.getNodes();
|
||||
Log.d(TAG, "Nodes "+nodes);
|
||||
|
||||
if (nodes.size() > 0) {
|
||||
Wearable.getMessageClient(this).sendMessage(
|
||||
nodes.toArray(new Node[]{})[0].getId(), "/ping", "Hello world".getBytes());
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "Capability request failed to return any results.");
|
||||
}
|
||||
});
|
||||
|
||||
Wearable.getDataClient(this).addListener(dataEventBuffer -> {
|
||||
Log.d(TAG, "onDataChanged(): " + dataEventBuffer);
|
||||
|
||||
for (DataEvent event : dataEventBuffer) {
|
||||
if (event.getType() == DataEvent.TYPE_CHANGED) {
|
||||
String path = event.getDataItem().getUri().getPath();
|
||||
Log.d(TAG, "Data "+path+ " :: "+Arrays.toString(event.getDataItem().getData()));
|
||||
}
|
||||
}
|
||||
});*/
|
||||
|
||||
FirebaseApp pushMobidziennikApp = FirebaseApp.initializeApp(
|
||||
this,
|
||||
new FirebaseOptions.Builder()
|
||||
.setApiKey("AIzaSyCi5LmsZ5BBCQnGtrdvWnp1bWLCNP8OWQE")
|
||||
.setApplicationId("1:747285019373:android:f6341bf7b158621d")
|
||||
.build(),
|
||||
"Mobidziennik2"
|
||||
);
|
||||
|
||||
FirebaseApp pushLibrusApp = FirebaseApp.initializeApp(
|
||||
this,
|
||||
new FirebaseOptions.Builder()
|
||||
.setApiKey("AIzaSyDfTuEoYPKdv4aceEws1CO3n0-HvTndz-o")
|
||||
.setApplicationId("1:513056078587:android:1e29083b760af544")
|
||||
.build(),
|
||||
"Librus"
|
||||
);
|
||||
|
||||
FirebaseApp pushVulcanApp = FirebaseApp.initializeApp(
|
||||
this,
|
||||
new FirebaseOptions.Builder()
|
||||
.setApiKey("AIzaSyDW8MUtanHy64_I0oCpY6cOxB3jrvJd_iA")
|
||||
.setApplicationId("1:987828170337:android:ac97431a0a4578c3")
|
||||
.build(),
|
||||
"Vulcan"
|
||||
);
|
||||
|
||||
try {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(instanceIdResult -> {
|
||||
Log.d(TAG, "Token for App is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId()+". Time is "+(System.currentTimeMillis() - startTime));
|
||||
config.getSync().setTokenApp(instanceIdResult.getToken());
|
||||
});
|
||||
/*FirebaseInstanceId.getInstance(pushMobidziennikApp).getInstanceId().addOnSuccessListener(instanceIdResult -> {
|
||||
Log.d(TAG, "Token for Mobidziennik is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId());
|
||||
appConfig.fcmTokens.put(LOGIN_TYPE_MOBIDZIENNIK, new Pair<>(instanceIdResult.getToken(), new ArrayList<>()));
|
||||
});
|
||||
FirebaseInstanceId.getInstance(pushLibrusApp).getInstanceId().addOnSuccessListener(instanceIdResult -> {
|
||||
Log.d(TAG, "Token for Librus is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId());
|
||||
appConfig.fcmTokens.put(LOGIN_TYPE_LIBRUS, new Pair<>(instanceIdResult.getToken(), new ArrayList<>()));
|
||||
});
|
||||
FirebaseInstanceId.getInstance(pushVulcanApp).getInstanceId().addOnSuccessListener(instanceIdResult -> {
|
||||
Log.d(TAG, "Token for Vulcan is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId());
|
||||
Pair<String, List<Integer>> pair = appConfig.fcmTokens.get(LOGIN_TYPE_VULCAN);
|
||||
if (pair == null || pair.first == null || !pair.first.equals(instanceIdResult.getToken())) {
|
||||
appConfig.fcmTokens.put(LOGIN_TYPE_VULCAN, new Pair<>(instanceIdResult.getToken(), new ArrayList<>()));
|
||||
}
|
||||
});*/
|
||||
|
||||
|
||||
FirebaseMessaging.getInstance().subscribeToTopic(getPackageName());
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void loadConfig()
|
||||
{
|
||||
appConfig = new AppConfig(this);
|
||||
|
||||
|
||||
if (appSharedPrefs.contains("config")) {
|
||||
// remove old-format config, save the new one and empty the incorrectly-nulled config
|
||||
appConfig = gson.fromJson(appSharedPrefs.getString("config", ""), AppConfig.class);
|
||||
appSharedPrefs.edit().remove("config").apply();
|
||||
saveConfig();
|
||||
appConfig = new AppConfig(this);
|
||||
}
|
||||
|
||||
if (appSharedPrefs.contains("profiles")) {
|
||||
SharedPreferences.Editor appSharedPrefsEditor = appSharedPrefs.edit();
|
||||
/*List<Integer> appProfileIds = gson.fromJson(appSharedPrefs.getString("profiles", ""), new TypeToken<List<Integer>>(){}.getType());
|
||||
for (int id: appProfileIds) {
|
||||
AppProfile appProfile = gson.fromJson(appSharedPrefs.getString("profile"+id, ""), AppProfile.class);
|
||||
if (appProfile != null) {
|
||||
appConfig.profiles.add(appProfile);
|
||||
}
|
||||
appSharedPrefsEditor.remove("profile"+id);
|
||||
}*/
|
||||
appSharedPrefsEditor.remove("profiles");
|
||||
appSharedPrefsEditor.apply();
|
||||
//profilesSave();
|
||||
}
|
||||
|
||||
|
||||
Map<String,?> keys = appSharedPrefs.getAll();
|
||||
for (Map.Entry<String,?> entry : keys.entrySet()) {
|
||||
if (entry.getKey().startsWith("app.appConfig.")) {
|
||||
String fieldName = entry.getKey().replace("app.appConfig.", "");
|
||||
|
||||
try {
|
||||
Field field = AppConfig.class.getField(fieldName);
|
||||
Object object;
|
||||
try {
|
||||
object = gson.fromJson(entry.getValue().toString(), field.getGenericType());
|
||||
} catch (JsonSyntaxException e) {
|
||||
Log.d(TAG, "For field "+fieldName);
|
||||
e.printStackTrace();
|
||||
object = entry.getValue().toString();
|
||||
}
|
||||
if (object != null)
|
||||
field.set(appConfig, object);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
Log.w(TAG, "Should remove app.appConfig."+fieldName);
|
||||
//appSharedPrefs.edit().remove("app.appConfig."+fieldName).apply(); TODO migration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*if (appConfig.lastAppVersion > BuildConfig.VERSION_CODE) {
|
||||
BootReceiver br = new BootReceiver();
|
||||
Intent i = new Intent();
|
||||
//i.putExtra("UserChecked", true);
|
||||
br.onReceive(getContext(), i);
|
||||
Toast.makeText(mContext, R.string.warning_older_version_running, Toast.LENGTH_LONG).show();
|
||||
//Toast.makeText(mContext, "Zaktualizuj aplikację.", Toast.LENGTH_LONG).show();
|
||||
//System.exit(0);
|
||||
}*/
|
||||
|
||||
if (appConfig == null) {
|
||||
appConfig = new AppConfig(this);
|
||||
}
|
||||
}
|
||||
public void saveConfig()
|
||||
{
|
||||
try {
|
||||
appConfig.savePending = false;
|
||||
|
||||
SharedPreferences.Editor appSharedPrefsEditor = appSharedPrefs.edit();
|
||||
|
||||
JsonObject appConfigJson = gson.toJsonTree(appConfig).getAsJsonObject();
|
||||
for (Map.Entry<String, JsonElement> entry : appConfigJson.entrySet()) {
|
||||
String jsonObj;
|
||||
jsonObj = entry.getValue().toString();
|
||||
/*if (entry.getValue().isJsonObject()) {
|
||||
jsonObj = entry.getValue().getAsJsonObject().toString();
|
||||
}
|
||||
else if (entry.getValue().isJsonArray()) {
|
||||
jsonObj = entry.getValue().getAsJsonArray().toString();
|
||||
}
|
||||
else {
|
||||
jsonObj = entry.getValue().toString();
|
||||
}*/
|
||||
appSharedPrefsEditor.putString("app.appConfig." + entry.getKey(), jsonObj);
|
||||
}
|
||||
|
||||
appSharedPrefsEditor.apply();
|
||||
}
|
||||
catch (ConcurrentModificationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//appSharedPrefs.edit().putString("config", gson.toJson(appConfig)).apply();
|
||||
}
|
||||
public void saveConfig(String ... fieldNames)
|
||||
{
|
||||
appConfig.savePending = false;
|
||||
|
||||
SharedPreferences.Editor appSharedPrefsEditor = appSharedPrefs.edit();
|
||||
|
||||
for (String fieldName: fieldNames) {
|
||||
try {
|
||||
Object object = AppConfig.class.getField(fieldName).get(appConfig);
|
||||
appSharedPrefsEditor.putString("app.appConfig."+fieldName, gson.toJson(object));
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ConcurrentModificationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
appSharedPrefsEditor.apply();
|
||||
//appSharedPrefs.edit().putString("config", gson.toJson(appConfig)).apply();
|
||||
}
|
||||
|
||||
public void profileSave() {
|
||||
AsyncTask.execute(() -> {
|
||||
db.profileDao().add(profile);
|
||||
});
|
||||
}
|
||||
|
||||
public void profileSaveAsync() {
|
||||
AsyncTask.execute(() -> {
|
||||
db.profileDao().add(profile);
|
||||
});
|
||||
}
|
||||
public void profileSaveAsync(Profile profile) {
|
||||
AsyncTask.execute(() -> {
|
||||
db.profileDao().add(profile);
|
||||
});
|
||||
}
|
||||
public void profileSaveFullAsync(ProfileFull profile) {
|
||||
AsyncTask.execute(() -> {
|
||||
profileSaveFull(profile);
|
||||
});
|
||||
}
|
||||
public void profileSaveFull(ProfileFull profileFull) {
|
||||
db.profileDao().add(profileFull);
|
||||
db.loginStoreDao().add(profileFull);
|
||||
}
|
||||
|
||||
public void profileLoadById(int id) {
|
||||
profileLoadById(id, false);
|
||||
}
|
||||
public void profileLoadById(int id, boolean loadedLast) {
|
||||
//Log.d(TAG, "Loading ID "+id);
|
||||
/*if (profile == null) {
|
||||
profile = profileNew();
|
||||
AppDb.profileId = profile.id;
|
||||
appSharedPrefs.edit().putInt("current_profile_id", profile.id).apply();
|
||||
return;
|
||||
}*/
|
||||
if (profile == null || profile.getId() != id) {
|
||||
profile = db.profileDao().getFullByIdNow(id);
|
||||
/*if (profile == null) {
|
||||
profileLoadById(id);
|
||||
return;
|
||||
}*/
|
||||
if (profile != null) {
|
||||
MainActivity.Companion.setUseOldMessages(profile.getLoginStoreType() == LOGIN_TYPE_MOBIDZIENNIK && appConfig.mobidziennikOldMessages == 1);
|
||||
profileId = profile.getId();
|
||||
appSharedPrefs.edit().putInt("current_profile_id", profile.getId()).apply();
|
||||
config.setProfile(profileId);
|
||||
}
|
||||
else if (!loadedLast) {
|
||||
profileLoadById(profileLastId(), true);
|
||||
}
|
||||
else {
|
||||
profileId = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void profileLoad(ProfileFull profile) {
|
||||
MainActivity.Companion.setUseOldMessages(profile.getLoginStoreType() == LOGIN_TYPE_MOBIDZIENNIK && appConfig.mobidziennikOldMessages == 1);
|
||||
this.profile = profile;
|
||||
profileId = profile.getId();
|
||||
}
|
||||
|
||||
/*public void profileRemove(int id)
|
||||
{
|
||||
Profile profile = db.profileDao().getFullByIdNow(id);
|
||||
|
||||
if (profile.id == profile.loginStoreId) {
|
||||
// this profile is the owner of the login store
|
||||
// we need to check if any other profile is using it
|
||||
List<Integer> transferProfileIds = db.profileDao().getIdsByLoginStoreIdNow(profile.loginStoreId);
|
||||
if (transferProfileIds.size() == 1) {
|
||||
// this login store is free of users, remove it along with the profile
|
||||
db.loginStoreDao().remove(profile.loginStoreId);
|
||||
// the current store is removed, we are ready to remove the profile
|
||||
}
|
||||
else if (transferProfileIds.size() > 1) {
|
||||
transferProfileIds.remove(transferProfileIds.indexOf(profile.id));
|
||||
// someone is using the store
|
||||
// we need to transfer it to the firstProfileId
|
||||
db.loginStoreDao().changeId(profile.loginStoreId, transferProfileIds.get(0));
|
||||
db.profileDao().changeStoreId(profile.loginStoreId, transferProfileIds.get(0));
|
||||
// the current store is removed, we are ready to remove the profile
|
||||
}
|
||||
}
|
||||
// else, the profile uses a store that it doesn't own
|
||||
// leave the store and go on with removing
|
||||
|
||||
Log.d(TAG, "Before removal: "+db.profileDao().getAllNow().toString());
|
||||
db.profileDao().remove(profile.id);
|
||||
Log.d(TAG, "After removal: "+db.profileDao().getAllNow().toString());
|
||||
|
||||
*//*int newId = 1;
|
||||
if (appConfig.profiles.size() > 0) {
|
||||
newId = appConfig.profiles.get(appConfig.profiles.size() - 1).id;
|
||||
}
|
||||
Log.d(TAG, "New ID: "+newId);
|
||||
//Toast.makeText(mContext, "selected new id "+newId, Toast.LENGTH_SHORT).show();
|
||||
profileLoadById(newId);*//*
|
||||
}*/
|
||||
|
||||
public int profileFirstId() {
|
||||
return db.profileDao().getFirstId();
|
||||
}
|
||||
|
||||
public int profileLastId() {
|
||||
return db.profileDao().getLastId();
|
||||
}
|
||||
|
||||
|
||||
public Context getContext()
|
||||
{
|
||||
return mContext;
|
||||
}
|
||||
|
||||
public void checkDevModePassword() {
|
||||
try {
|
||||
devMode = Utils.AESCrypt.decrypt("nWFVxY65Pa8/aRrT7EylNAencmOD+IxUY2Gg/beiIWY=", config.getDevModePassword()).equals("ok here you go it's enabled now")
|
||||
|| BuildConfig.DEBUG;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
devMode = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -4,20 +4,93 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik
|
||||
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.ShortcutInfo
|
||||
import android.content.pm.ShortcutManager
|
||||
import android.graphics.drawable.Icon
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.multidex.MultiDexApplication
|
||||
import androidx.work.Configuration
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import cat.ereza.customactivityoncrash.config.CaocConfig
|
||||
import com.chuckerteam.chucker.api.ChuckerCollector
|
||||
import com.chuckerteam.chucker.api.ChuckerInterceptor
|
||||
import com.chuckerteam.chucker.api.RetentionManager
|
||||
import com.google.firebase.FirebaseApp
|
||||
import com.google.firebase.FirebaseOptions
|
||||
import com.google.firebase.iid.FirebaseInstanceId
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
import com.google.gson.Gson
|
||||
import com.hypertrack.hyperlog.HyperLog
|
||||
import com.mikepenz.iconics.Iconics
|
||||
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
|
||||
import im.wangchao.mhttp.MHttp
|
||||
import im.wangchao.mhttp.internal.cookie.PersistentCookieJar
|
||||
import im.wangchao.mhttp.internal.cookie.cache.SetCookieCache
|
||||
import im.wangchao.mhttp.internal.cookie.persistence.SharedPrefsCookiePersistor
|
||||
import kotlinx.coroutines.*
|
||||
import me.leolin.shortcutbadger.ShortcutBadger
|
||||
import okhttp3.OkHttpClient
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import pl.szczodrzynski.edziennik.config.Config
|
||||
import pl.szczodrzynski.edziennik.data.api.events.ProfileListEmptyEvent
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing
|
||||
import pl.szczodrzynski.edziennik.data.db.AppDb
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.network.NetworkUtils
|
||||
import pl.szczodrzynski.edziennik.sync.SyncWorker
|
||||
import pl.szczodrzynski.edziennik.sync.UpdateWorker
|
||||
import pl.szczodrzynski.edziennik.ui.modules.base.CrashActivity
|
||||
import pl.szczodrzynski.edziennik.utils.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScope {
|
||||
class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
|
||||
companion object {
|
||||
@Volatile
|
||||
lateinit var db: AppDb
|
||||
val config: Config by lazy { Config(db) }
|
||||
var profile: Profile by mutableLazy { Profile(0, 0, 0, "") }
|
||||
val profileId
|
||||
get() = profile.id
|
||||
|
||||
var devMode = false
|
||||
}
|
||||
|
||||
//lateinit var db: AppDb
|
||||
//val config by lazy { Config(db); // TODO migrate }
|
||||
val notifications by lazy { Notifications() }
|
||||
inner class Notifications {
|
||||
val syncId = 1
|
||||
val syncKey = "pl.szczodrzynski.edziennik.SYNC"
|
||||
val syncChannelName: String by lazy { getString(R.string.notification_channel_get_data_name) }
|
||||
val syncChannelDesc: String by lazy { getString(R.string.notification_channel_get_data_desc) }
|
||||
val dataId = 50
|
||||
val dataKey = "pl.szczodrzynski.edziennik.DATA"
|
||||
val dataChannelName: String by lazy { getString(R.string.notification_channel_notifications_name) }
|
||||
val dataChannelDesc: String by lazy { getString(R.string.notification_channel_notifications_desc) }
|
||||
val dataQuietId = 60
|
||||
val dataQuietKey = "pl.szczodrzynski.edziennik.DATA_QUIET"
|
||||
val dataQuietChannelName: String by lazy { getString(R.string.notification_channel_notifications_quiet_name) }
|
||||
val dataQuietChannelDesc: String by lazy { getString(R.string.notification_channel_notifications_quiet_desc) }
|
||||
val updatesId = 100
|
||||
val updatesKey = "pl.szczodrzynski.edziennik.UPDATES"
|
||||
val updatesChannelName: String by lazy { getString(R.string.notification_channel_updates_name) }
|
||||
val updatesChannelDesc: String by lazy { getString(R.string.notification_channel_updates_desc) }
|
||||
}
|
||||
|
||||
val db
|
||||
get() = App.db
|
||||
val config
|
||||
get() = App.config
|
||||
val profile
|
||||
get() = App.profile
|
||||
val profileId
|
||||
get() = App.profileId
|
||||
|
||||
private val job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
@ -26,11 +99,10 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
|
||||
.setMinimumLoggingLevel(Log.VERBOSE)
|
||||
.build()
|
||||
|
||||
/*val preferences by lazy { getSharedPreferences(getString(R.string.preference_file), Context.MODE_PRIVATE) }
|
||||
val notifier by lazy { Notifier(this) }
|
||||
val preferences by lazy { getSharedPreferences(getString(R.string.preference_file), Context.MODE_PRIVATE) }
|
||||
val permissionChecker by lazy { PermissionChecker(this) }
|
||||
|
||||
lateinit var profile: ProfileFull
|
||||
val networkUtils by lazy { NetworkUtils(this) }
|
||||
val gson by lazy { Gson() }
|
||||
|
||||
/* _ _ _______ _______ _____
|
||||
| | | |__ __|__ __| __ \
|
||||
@ -48,13 +120,13 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
|
||||
.connectTimeout(20, TimeUnit.SECONDS)
|
||||
.writeTimeout(5, TimeUnit.SECONDS)
|
||||
.readTimeout(10, TimeUnit.SECONDS)
|
||||
builder.installHttpsSupport()
|
||||
builder.installHttpsSupport(this)
|
||||
|
||||
if (devMode || BuildConfig.DEBUG) {
|
||||
HyperLog.initialize(this)
|
||||
HyperLog.setLogLevel(Log.VERBOSE)
|
||||
HyperLog.setLogFormat(DebugLogFormat(this))
|
||||
val chuckerCollector = ChuckerCollector(this, true, Period.ONE_HOUR)
|
||||
val chuckerCollector = ChuckerCollector(this, true, RetentionManager.Period.ONE_HOUR)
|
||||
val chuckerInterceptor = ChuckerInterceptor(this, chuckerCollector)
|
||||
builder.addInterceptor(chuckerInterceptor)
|
||||
}
|
||||
@ -77,22 +149,7 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
|
||||
|_____/|_|\__, |_| |_|\__,_|\__|\__,_|_| \___|
|
||||
__/ |
|
||||
|__*/
|
||||
private val deviceId: String by lazy { Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID) ?: "" }
|
||||
private val signature: String by lazy {
|
||||
var str = ""
|
||||
try {
|
||||
val packageInfo: PackageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
|
||||
for (signature in packageInfo.signatures) {
|
||||
val signatureBytes = signature.toByteArray()
|
||||
val md = MessageDigest.getInstance("SHA")
|
||||
md.update(signatureBytes)
|
||||
str = Base64.encodeToString(md.digest(), Base64.DEFAULT)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
str
|
||||
}
|
||||
val deviceId: String by lazy { Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID) ?: "" }
|
||||
private var unreadBadgesAvailable = true
|
||||
|
||||
/* _____ _
|
||||
@ -118,193 +175,214 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
|
||||
.apply()
|
||||
Iconics.init(applicationContext)
|
||||
Iconics.registerFont(SzkolnyFont)
|
||||
db = AppDb.getDatabase(this)
|
||||
App.db = AppDb(this)
|
||||
Themes.themeInt = config.ui.theme
|
||||
MHttp.instance().customOkHttpClient(http)
|
||||
|
||||
if (!profileLoadById(config.lastProfileId)) {
|
||||
db.profileDao().firstId?.let { profileLoadById(it) }
|
||||
}
|
||||
|
||||
devMode = "f054761fbdb6a238" == deviceId || BuildConfig.DEBUG
|
||||
if (config.devModePassword != null)
|
||||
checkDevModePassword()
|
||||
|
||||
Signing.getCert(this)
|
||||
|
||||
launch {
|
||||
withContext(Dispatchers.Default) {
|
||||
config.migrate(this@App)
|
||||
|
||||
if (config.devModePassword != null)
|
||||
checkDevModePassword()
|
||||
|
||||
if (config.sync.enabled)
|
||||
SyncWorker.scheduleNext(this@App, false)
|
||||
else
|
||||
SyncWorker.cancelNext(this@App)
|
||||
|
||||
if (config.sync.notifyAboutUpdates)
|
||||
UpdateWorker.scheduleNext(this@App, false)
|
||||
else
|
||||
UpdateWorker.cancelNext(this@App)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
||||
val shortcutManager = getSystemService(ShortcutManager::class.java)
|
||||
|
||||
val shortcutTimetable = ShortcutInfo.Builder(this@App, "item_timetable")
|
||||
.setShortLabel(getString(R.string.shortcut_timetable)).setLongLabel(getString(R.string.shortcut_timetable))
|
||||
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_timetable))
|
||||
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_TIMETABLE))
|
||||
.build()
|
||||
|
||||
val shortcutAgenda = ShortcutInfo.Builder(this@App, "item_agenda")
|
||||
.setShortLabel(getString(R.string.shortcut_agenda)).setLongLabel(getString(R.string.shortcut_agenda))
|
||||
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_agenda))
|
||||
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_AGENDA))
|
||||
.build()
|
||||
|
||||
val shortcutGrades = ShortcutInfo.Builder(this@App, "item_grades")
|
||||
.setShortLabel(getString(R.string.shortcut_grades)).setLongLabel(getString(R.string.shortcut_grades))
|
||||
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_grades))
|
||||
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_GRADES))
|
||||
.build()
|
||||
|
||||
val shortcutHomework = ShortcutInfo.Builder(this@App, "item_homeworks")
|
||||
.setShortLabel(getString(R.string.shortcut_homework)).setLongLabel(getString(R.string.shortcut_homework))
|
||||
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_homework))
|
||||
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_HOMEWORK))
|
||||
.build()
|
||||
|
||||
val shortcutMessages = ShortcutInfo.Builder(this@App, "item_messages")
|
||||
.setShortLabel(getString(R.string.shortcut_messages)).setLongLabel(getString(R.string.shortcut_messages))
|
||||
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_messages))
|
||||
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_MESSAGES))
|
||||
.build()
|
||||
|
||||
shortcutManager.dynamicShortcuts = listOf(
|
||||
shortcutTimetable,
|
||||
shortcutAgenda,
|
||||
shortcutGrades,
|
||||
shortcutHomework,
|
||||
shortcutMessages
|
||||
)
|
||||
} // shortcuts - end
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(notifications.syncKey, notifications.syncChannelName, NotificationManager.IMPORTANCE_MIN).apply {
|
||||
description = notifications.syncChannelDesc
|
||||
})
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(notifications.dataKey, notifications.dataChannelName, NotificationManager.IMPORTANCE_HIGH).apply {
|
||||
description = notifications.dataChannelDesc
|
||||
enableLights(true)
|
||||
lightColor = 0xff2196f3.toInt()
|
||||
})
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(notifications.dataQuietKey, notifications.dataQuietChannelName, NotificationManager.IMPORTANCE_LOW).apply {
|
||||
description = notifications.dataQuietChannelDesc
|
||||
setSound(null, null)
|
||||
enableVibration(false)
|
||||
})
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(notifications.updatesKey, notifications.updatesChannelName, NotificationManager.IMPORTANCE_DEFAULT).apply {
|
||||
description = notifications.updatesChannelDesc
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
if (config.appInstalledTime == 0L)
|
||||
try {
|
||||
config.appInstalledTime = packageManager.getPackageInfo(packageName, 0).firstInstallTime
|
||||
config.appRateSnackbarTime = config.appInstalledTime + 7 * DAY * MS
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
launch { async(Dispatchers.Default) {
|
||||
if (config.sync.enabled) {
|
||||
scheduleNext(this@App, false)
|
||||
} else {
|
||||
cancelNext(this@App)
|
||||
val pushMobidziennikApp = FirebaseApp.initializeApp(
|
||||
this@App,
|
||||
FirebaseOptions.Builder()
|
||||
.setApiKey("AIzaSyCi5LmsZ5BBCQnGtrdvWnp1bWLCNP8OWQE")
|
||||
.setApplicationId("1:747285019373:android:f6341bf7b158621d")
|
||||
.build(),
|
||||
"Mobidziennik2"
|
||||
)
|
||||
|
||||
val pushLibrusApp = FirebaseApp.initializeApp(
|
||||
this@App,
|
||||
FirebaseOptions.Builder()
|
||||
.setApiKey("AIzaSyDfTuEoYPKdv4aceEws1CO3n0-HvTndz-o")
|
||||
.setApplicationId("1:513056078587:android:1e29083b760af544")
|
||||
.build(),
|
||||
"Librus"
|
||||
)
|
||||
|
||||
val pushVulcanApp = FirebaseApp.initializeApp(
|
||||
this@App,
|
||||
FirebaseOptions.Builder()
|
||||
.setApiKey("AIzaSyDW8MUtanHy64_I0oCpY6cOxB3jrvJd_iA")
|
||||
.setApplicationId("1:987828170337:android:ac97431a0a4578c3")
|
||||
.build(),
|
||||
"Vulcan"
|
||||
)
|
||||
|
||||
try {
|
||||
FirebaseInstanceId.getInstance().instanceId.addOnSuccessListener { instanceIdResult ->
|
||||
val token = instanceIdResult.token
|
||||
config.sync.tokenApp = token
|
||||
}
|
||||
FirebaseInstanceId.getInstance(pushMobidziennikApp).instanceId.addOnSuccessListener { instanceIdResult ->
|
||||
val token = instanceIdResult.token
|
||||
if (token != config.sync.tokenMobidziennik) {
|
||||
config.sync.tokenMobidziennik = token
|
||||
config.sync.tokenMobidziennikList = listOf()
|
||||
}
|
||||
}
|
||||
FirebaseInstanceId.getInstance(pushLibrusApp).instanceId.addOnSuccessListener { instanceIdResult ->
|
||||
val token = instanceIdResult.token
|
||||
if (token != config.sync.tokenLibrus) {
|
||||
config.sync.tokenLibrus = token
|
||||
config.sync.tokenLibrusList = listOf()
|
||||
}
|
||||
}
|
||||
FirebaseInstanceId.getInstance(pushVulcanApp).instanceId.addOnSuccessListener { instanceIdResult ->
|
||||
val token = instanceIdResult.token
|
||||
if (token != config.sync.tokenVulcan) {
|
||||
config.sync.tokenVulcan = token
|
||||
config.sync.tokenVulcanList = listOf()
|
||||
}
|
||||
}
|
||||
FirebaseMessaging.getInstance().subscribeToTopic(packageName)
|
||||
} catch (e: IllegalStateException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
db.metadataDao().countUnseen().observeForever { count: Int ->
|
||||
if (unreadBadgesAvailable)
|
||||
unreadBadgesAvailable = ShortcutBadger.applyCount(this@App, count)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
||||
val shortcutManager = getSystemService(ShortcutManager::class.java)
|
||||
|
||||
val shortcutTimetable = ShortcutInfo.Builder(this@App, "item_timetable")
|
||||
.setShortLabel(getString(R.string.shortcut_timetable)).setLongLabel(getString(R.string.shortcut_timetable))
|
||||
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_timetable))
|
||||
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_TIMETABLE))
|
||||
.build()
|
||||
|
||||
val shortcutAgenda = ShortcutInfo.Builder(this@App, "item_agenda")
|
||||
.setShortLabel(getString(R.string.shortcut_agenda)).setLongLabel(getString(R.string.shortcut_agenda))
|
||||
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_agenda))
|
||||
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_AGENDA))
|
||||
.build()
|
||||
|
||||
val shortcutGrades = ShortcutInfo.Builder(this@App, "item_grades")
|
||||
.setShortLabel(getString(R.string.shortcut_grades)).setLongLabel(getString(R.string.shortcut_grades))
|
||||
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_grades))
|
||||
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_GRADES))
|
||||
.build()
|
||||
|
||||
val shortcutHomework = ShortcutInfo.Builder(this@App, "item_homeworks")
|
||||
.setShortLabel(getString(R.string.shortcut_homework)).setLongLabel(getString(R.string.shortcut_homework))
|
||||
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_homework))
|
||||
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_HOMEWORK))
|
||||
.build()
|
||||
|
||||
val shortcutMessages = ShortcutInfo.Builder(this@App, "item_messages")
|
||||
.setShortLabel(getString(R.string.shortcut_messages)).setLongLabel(getString(R.string.shortcut_messages))
|
||||
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_messages))
|
||||
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_MESSAGES))
|
||||
.build()
|
||||
|
||||
shortcutManager.dynamicShortcuts = listOf(
|
||||
shortcutTimetable,
|
||||
shortcutAgenda,
|
||||
shortcutGrades,
|
||||
shortcutHomework,
|
||||
shortcutMessages
|
||||
)
|
||||
} // shortcuts - end
|
||||
|
||||
if (config.appInstalledTime == 0L)
|
||||
try {
|
||||
config.appInstalledTime = packageManager.getPackageInfo(packageName, 0).firstInstallTime
|
||||
config.appRateSnackbarTime = config.appInstalledTime + 7*DAY*MS
|
||||
} catch (e: NameNotFoundException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
val pushMobidziennikApp = FirebaseApp.initializeApp(
|
||||
this@App,
|
||||
FirebaseOptions.Builder()
|
||||
.setApiKey("AIzaSyCi5LmsZ5BBCQnGtrdvWnp1bWLCNP8OWQE")
|
||||
.setApplicationId("1:747285019373:android:f6341bf7b158621d")
|
||||
.build(),
|
||||
"Mobidziennik2"
|
||||
)
|
||||
|
||||
val pushLibrusApp = FirebaseApp.initializeApp(
|
||||
this@App,
|
||||
FirebaseOptions.Builder()
|
||||
.setApiKey("AIzaSyDfTuEoYPKdv4aceEws1CO3n0-HvTndz-o")
|
||||
.setApplicationId("1:513056078587:android:1e29083b760af544")
|
||||
.build(),
|
||||
"Librus"
|
||||
)
|
||||
|
||||
val pushVulcanApp = FirebaseApp.initializeApp(
|
||||
this@App,
|
||||
FirebaseOptions.Builder()
|
||||
.setApiKey("AIzaSyDW8MUtanHy64_I0oCpY6cOxB3jrvJd_iA")
|
||||
.setApplicationId("1:987828170337:android:ac97431a0a4578c3")
|
||||
.build(),
|
||||
"Vulcan"
|
||||
)
|
||||
|
||||
try {
|
||||
FirebaseInstanceId.getInstance().instanceId.addOnSuccessListener { instanceIdResult ->
|
||||
val token = instanceIdResult.token
|
||||
config.sync.tokenApp = token
|
||||
}
|
||||
FirebaseInstanceId.getInstance(pushMobidziennikApp).instanceId.addOnSuccessListener { instanceIdResult ->
|
||||
val token = instanceIdResult.token
|
||||
if (token != config.sync.tokenMobidziennik) {
|
||||
config.sync.tokenMobidziennik = token
|
||||
config.sync.tokenMobidziennikList = listOf()
|
||||
}
|
||||
}
|
||||
FirebaseInstanceId.getInstance(pushLibrusApp).instanceId.addOnSuccessListener { instanceIdResult ->
|
||||
val token = instanceIdResult.token
|
||||
if (token != config.sync.tokenLibrus) {
|
||||
config.sync.tokenLibrus = token
|
||||
config.sync.tokenLibrusList = listOf()
|
||||
}
|
||||
}
|
||||
FirebaseInstanceId.getInstance(pushVulcanApp).instanceId.addOnSuccessListener { instanceIdResult ->
|
||||
val token = instanceIdResult.token
|
||||
if (token != config.sync.tokenVulcan) {
|
||||
config.sync.tokenVulcan = token
|
||||
config.sync.tokenVulcanList = listOf()
|
||||
}
|
||||
}
|
||||
FirebaseMessaging.getInstance().subscribeToTopic(packageName)
|
||||
} catch (e: IllegalStateException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
private fun profileLoad(profileId: Int) {
|
||||
db.profileDao().getFullByIdNow(profileId)?.also {
|
||||
profile = it
|
||||
} ?: run {
|
||||
if (!::profile.isInitialized) {
|
||||
profile = ProfileFull(-1, "", "", -1)
|
||||
}
|
||||
}
|
||||
}
|
||||
fun profileLoad(profileId: Int, onSuccess: (profile: ProfileFull) -> Unit) {
|
||||
|
||||
private fun profileLoadById(profileId: Int): Boolean {
|
||||
db.profileDao().getByIdNow(profileId)?.also {
|
||||
App.profile = it
|
||||
App.config.lastProfileId = it.id
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
fun profileLoad(profileId: Int, onSuccess: (profile: Profile) -> Unit) {
|
||||
launch {
|
||||
val deferred = async(Dispatchers.Default) {
|
||||
profileLoad(profileId)
|
||||
val success = withContext(Dispatchers.Default) {
|
||||
profileLoadById(profileId)
|
||||
}
|
||||
deferred.await()
|
||||
onSuccess(profile)
|
||||
if (success)
|
||||
onSuccess(profile)
|
||||
else
|
||||
profileLoadLast(onSuccess)
|
||||
}
|
||||
}
|
||||
|
||||
private fun OkHttpClient.Builder.installHttpsSupport() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
try {
|
||||
try {
|
||||
ProviderInstaller.installIfNeeded(this@App)
|
||||
} catch (e: Exception) {
|
||||
Log.e("OkHttpTLSCompat", "Play Services not found or outdated")
|
||||
|
||||
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
|
||||
trustManagerFactory.init(null as KeyStore?)
|
||||
|
||||
val x509TrustManager = trustManagerFactory.trustManagers.singleOrNull { it is X509TrustManager } as X509TrustManager?
|
||||
?: return
|
||||
|
||||
val sc = SSLContext.getInstance("TLSv1.2")
|
||||
sc.init(null, null, null)
|
||||
sslSocketFactory(TLSSocketFactory(sc.socketFactory), x509TrustManager)
|
||||
val cs: ConnectionSpec = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
|
||||
.tlsVersions(TlsVersion.TLS_1_0)
|
||||
.tlsVersions(TlsVersion.TLS_1_1)
|
||||
.tlsVersions(TlsVersion.TLS_1_2)
|
||||
.build()
|
||||
val specs: MutableList<ConnectionSpec> = ArrayList()
|
||||
specs.add(cs)
|
||||
specs.add(ConnectionSpec.COMPATIBLE_TLS)
|
||||
specs.add(ConnectionSpec.CLEARTEXT)
|
||||
connectionSpecs(specs)
|
||||
}
|
||||
} catch (exc: Exception) {
|
||||
Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc)
|
||||
fun profileLoadLast(onSuccess: (profile: Profile) -> Unit) {
|
||||
launch {
|
||||
val success = withContext(Dispatchers.Default) {
|
||||
profileLoadById(db.profileDao().lastId ?: return@withContext false)
|
||||
}
|
||||
if (!success) {
|
||||
EventBus.getDefault().post(ProfileListEmptyEvent())
|
||||
}
|
||||
}
|
||||
}
|
||||
fun profileSave() = profileSave(profile)
|
||||
fun profileSave(profile: Profile) {
|
||||
launch(Dispatchers.Default) {
|
||||
App.db.profileDao().add(profile)
|
||||
}
|
||||
}
|
||||
|
||||
@ -315,5 +393,5 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
@ -3,8 +3,11 @@ package pl.szczodrzynski.edziennik
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.ColorStateList
|
||||
import android.content.res.Resources
|
||||
import android.database.Cursor
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import android.graphics.Typeface
|
||||
@ -15,20 +18,25 @@ import android.text.*
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.StrikethroughSpan
|
||||
import android.text.style.StyleSpan
|
||||
import android.util.*
|
||||
import android.util.Base64
|
||||
import android.util.Base64.NO_WRAP
|
||||
import android.util.Base64.encodeToString
|
||||
import android.util.LongSparseArray
|
||||
import android.util.SparseArray
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.widget.CheckBox
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.RadioButton
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.*
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.database.getIntOrNull
|
||||
import androidx.core.database.getLongOrNull
|
||||
import androidx.core.database.getStringOrNull
|
||||
import androidx.core.util.forEach
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import com.google.android.gms.security.ProviderInstaller
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
@ -36,20 +44,32 @@ import im.wangchao.mhttp.Response
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.ConnectionSpec
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.TlsVersion
|
||||
import okio.Buffer
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teams.Team
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Team
|
||||
import pl.szczodrzynski.edziennik.network.TLSSocketFactory
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import pl.szczodrzynski.navlib.getColorFromRes
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import java.math.BigInteger
|
||||
import java.nio.charset.Charset
|
||||
import java.security.KeyStore
|
||||
import java.security.MessageDigest
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.zip.CRC32
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
import javax.net.ssl.SSLContext
|
||||
import javax.net.ssl.TrustManagerFactory
|
||||
import javax.net.ssl.X509TrustManager
|
||||
import kotlin.Pair
|
||||
|
||||
|
||||
fun List<Teacher>.byId(id: Long) = firstOrNull { it.id == id }
|
||||
@ -65,6 +85,7 @@ fun JsonObject?.getString(key: String): String? = get(key)?.let { if (it.isJsonN
|
||||
fun JsonObject?.getInt(key: String): Int? = get(key)?.let { if (it.isJsonNull) null else it.asInt }
|
||||
fun JsonObject?.getLong(key: String): Long? = get(key)?.let { if (it.isJsonNull) null else it.asLong }
|
||||
fun JsonObject?.getFloat(key: String): Float? = get(key)?.let { if(it.isJsonNull) null else it.asFloat }
|
||||
fun JsonObject?.getChar(key: String): Char? = get(key)?.let { if(it.isJsonNull) null else it.asCharacter }
|
||||
fun JsonObject?.getJsonObject(key: String): JsonObject? = get(key)?.let { if (it.isJsonNull) null else it.asJsonObject }
|
||||
fun JsonObject?.getJsonArray(key: String): JsonArray? = get(key)?.let { if (it.isJsonNull) null else it.asJsonArray }
|
||||
|
||||
@ -73,15 +94,32 @@ fun JsonObject?.getString(key: String, defaultValue: String): String = get(key)?
|
||||
fun JsonObject?.getInt(key: String, defaultValue: Int): Int = get(key)?.let { if (it.isJsonNull) defaultValue else it.asInt } ?: defaultValue
|
||||
fun JsonObject?.getLong(key: String, defaultValue: Long): Long = get(key)?.let { if (it.isJsonNull) defaultValue else it.asLong } ?: defaultValue
|
||||
fun JsonObject?.getFloat(key: String, defaultValue: Float): Float = get(key)?.let { if(it.isJsonNull) defaultValue else it.asFloat } ?: defaultValue
|
||||
fun JsonObject?.getChar(key: String, defaultValue: Char): Char = get(key)?.let { if(it.isJsonNull) defaultValue else it.asCharacter } ?: defaultValue
|
||||
fun JsonObject?.getJsonObject(key: String, defaultValue: JsonObject): JsonObject = get(key)?.let { if (it.isJsonNull) defaultValue else it.asJsonObject } ?: defaultValue
|
||||
fun JsonObject?.getJsonArray(key: String, defaultValue: JsonArray): JsonArray = get(key)?.let { if (it.isJsonNull) defaultValue else it.asJsonArray } ?: defaultValue
|
||||
|
||||
fun JsonArray?.asJsonObjectList() = this?.map { it.asJsonObject }
|
||||
operator fun JsonObject.set(key: String, value: JsonElement) = this.add(key, value)
|
||||
operator fun JsonObject.set(key: String, value: Boolean) = this.addProperty(key, value)
|
||||
operator fun JsonObject.set(key: String, value: String?) = this.addProperty(key, value)
|
||||
operator fun JsonObject.set(key: String, value: Number) = this.addProperty(key, value)
|
||||
operator fun JsonObject.set(key: String, value: Char) = this.addProperty(key, value)
|
||||
|
||||
operator fun Profile.set(key: String, value: JsonElement) = this.studentData.add(key, value)
|
||||
operator fun Profile.set(key: String, value: Boolean) = this.studentData.addProperty(key, value)
|
||||
operator fun Profile.set(key: String, value: String?) = this.studentData.addProperty(key, value)
|
||||
operator fun Profile.set(key: String, value: Number) = this.studentData.addProperty(key, value)
|
||||
operator fun Profile.set(key: String, value: Char) = this.studentData.addProperty(key, value)
|
||||
|
||||
fun JsonArray.asJsonObjectList() = this.mapNotNull { it.asJsonObject }
|
||||
|
||||
fun CharSequence?.isNotNullNorEmpty(): Boolean {
|
||||
return this != null && this.isNotEmpty()
|
||||
}
|
||||
|
||||
fun CharSequence?.isNotNullNorBlank(): Boolean {
|
||||
return this != null && this.isNotBlank()
|
||||
}
|
||||
|
||||
fun currentTimeUnix() = System.currentTimeMillis() / 1000
|
||||
|
||||
fun Bundle?.getInt(key: String, defaultValue: Int): Int {
|
||||
@ -97,12 +135,31 @@ fun Bundle?.getString(key: String, defaultValue: String): String {
|
||||
return this?.getString(key, defaultValue) ?: defaultValue
|
||||
}
|
||||
|
||||
fun String.fixName(): String {
|
||||
return this.fixWhiteSpaces().toProperCase()
|
||||
/**
|
||||
* ` The quick BROWN_fox Jumps OveR THE LAZy-DOG. `
|
||||
*
|
||||
* converts to
|
||||
*
|
||||
* `The Quick Brown_fox Jumps Over The Lazy-Dog.`
|
||||
*/
|
||||
fun String?.fixName(): String {
|
||||
return this?.fixWhiteSpaces()?.toProperCase() ?: ""
|
||||
}
|
||||
|
||||
/**
|
||||
* `The quick BROWN_fox Jumps OveR THE LAZy-DOG.`
|
||||
*
|
||||
* converts to
|
||||
*
|
||||
* `The Quick Brown_fox Jumps Over The Lazy-Dog.`
|
||||
*/
|
||||
fun String.toProperCase(): String = changeStringCase(this)
|
||||
|
||||
/**
|
||||
* `John Smith` -> `Smith John`
|
||||
*
|
||||
* `JOHN SMith` -> `SMith JOHN`
|
||||
*/
|
||||
fun String.swapFirstLastName(): String {
|
||||
return this.split(" ").let {
|
||||
if (it.size > 1)
|
||||
@ -112,15 +169,13 @@ fun String.swapFirstLastName(): String {
|
||||
}
|
||||
}
|
||||
|
||||
fun String.getFirstLastName(): Pair<String, String>? {
|
||||
fun String.splitName(): Pair<String, String>? {
|
||||
return this.split(" ").let {
|
||||
if (it.size >= 2) Pair(it[0], it[1])
|
||||
else null
|
||||
}
|
||||
}
|
||||
|
||||
fun String.getLastFirstName() = this.getFirstLastName()
|
||||
|
||||
fun changeStringCase(s: String): String {
|
||||
val delimiters = " '-/"
|
||||
val sb = StringBuilder()
|
||||
@ -150,40 +205,68 @@ fun String.getShortName(): String {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* "John Smith" -> "JS"
|
||||
*
|
||||
* "JOHN SMith" -> "JS"
|
||||
*
|
||||
* "John" -> "J"
|
||||
*
|
||||
* "John " -> "J"
|
||||
*
|
||||
* "John Smith " -> "JS"
|
||||
*
|
||||
* " " -> ""
|
||||
*
|
||||
* " " -> ""
|
||||
*/
|
||||
fun String?.getNameInitials(): String {
|
||||
if (this.isNullOrBlank()) return ""
|
||||
return this.toUpperCase().fixWhiteSpaces().split(" ").take(2).map { it[0] }.joinToString("")
|
||||
}
|
||||
|
||||
fun List<String>.join(delimiter: String): String {
|
||||
return this.joinToString(delimiter)
|
||||
return concat(delimiter).toString()
|
||||
}
|
||||
|
||||
fun colorFromName(context: Context, name: String?): Int {
|
||||
var crc = (name ?: "").crc16()
|
||||
crc = (crc and 0xff) or (crc shr 8)
|
||||
crc %= 16
|
||||
val color = when (crc) {
|
||||
13 -> R.color.md_red_500
|
||||
4 -> R.color.md_pink_A400
|
||||
2 -> R.color.md_purple_A400
|
||||
9 -> R.color.md_deep_purple_A700
|
||||
5 -> R.color.md_indigo_500
|
||||
1 -> R.color.md_indigo_A700
|
||||
6 -> R.color.md_cyan_A200
|
||||
14 -> R.color.md_teal_400
|
||||
15 -> R.color.md_green_500
|
||||
7 -> R.color.md_yellow_A700
|
||||
3 -> R.color.md_deep_orange_A400
|
||||
8 -> R.color.md_deep_orange_A700
|
||||
10 -> R.color.md_brown_500
|
||||
12 -> R.color.md_grey_400
|
||||
11 -> R.color.md_blue_grey_400
|
||||
else -> R.color.md_light_green_A700
|
||||
}
|
||||
return context.getColorFromRes(color)
|
||||
fun colorFromName(name: String?): Int {
|
||||
val i = (name ?: "").crc32()
|
||||
return when ((i / 10 % 16 + 1).toInt()) {
|
||||
13 -> 0xffF44336
|
||||
4 -> 0xffF50057
|
||||
2 -> 0xffD500F9
|
||||
9 -> 0xff6200EA
|
||||
5 -> 0xffFFAB00
|
||||
1 -> 0xff304FFE
|
||||
6 -> 0xff40C4FF
|
||||
14 -> 0xff26A69A
|
||||
15 -> 0xff00C853
|
||||
7 -> 0xffFFD600
|
||||
3 -> 0xffFF3D00
|
||||
8 -> 0xffDD2C00
|
||||
10 -> 0xff795548
|
||||
12 -> 0xff2979FF
|
||||
11 -> 0xffFF6D00
|
||||
else -> 0xff64DD17
|
||||
}.toInt()
|
||||
}
|
||||
|
||||
fun MutableList<Profile>.filterOutArchived(): MutableList<Profile> {
|
||||
this.removeAll { it.archived }
|
||||
return this
|
||||
fun colorFromCssName(name: String): Int {
|
||||
return when (name) {
|
||||
"red" -> 0xffff0000
|
||||
"green" -> 0xff008000
|
||||
"blue" -> 0xff0000ff
|
||||
"violet" -> 0xffee82ee
|
||||
"brown" -> 0xffa52a2a
|
||||
"orange" -> 0xffffa500
|
||||
"black" -> 0xff000000
|
||||
"white" -> 0xffffffff
|
||||
else -> -1
|
||||
}.toInt()
|
||||
}
|
||||
|
||||
fun List<Profile>.filterOutArchived() = this.filter { !it.archived }
|
||||
|
||||
fun Activity.isStoragePermissionGranted(): Boolean {
|
||||
return if (Build.VERSION.SDK_INT >= 23) {
|
||||
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
|
||||
@ -220,6 +303,13 @@ fun <T> LongSparseArray<T>.values(): List<T> {
|
||||
return result
|
||||
}
|
||||
|
||||
fun SparseArray<*>.keys(): List<Int> {
|
||||
val result = mutableListOf<Int>()
|
||||
forEach { key, _ ->
|
||||
result += key
|
||||
}
|
||||
return result
|
||||
}
|
||||
fun <T> SparseArray<T>.values(): List<T> {
|
||||
val result = mutableListOf<T>()
|
||||
forEach { _, value ->
|
||||
@ -228,6 +318,36 @@ fun <T> SparseArray<T>.values(): List<T> {
|
||||
return result
|
||||
}
|
||||
|
||||
fun SparseIntArray.keys(): List<Int> {
|
||||
val result = mutableListOf<Int>()
|
||||
forEach { key, _ ->
|
||||
result += key
|
||||
}
|
||||
return result
|
||||
}
|
||||
fun SparseIntArray.values(): List<Int> {
|
||||
val result = mutableListOf<Int>()
|
||||
forEach { _, value ->
|
||||
result += value
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun <K, V> List<Pair<K, V>>.keys(): List<K> {
|
||||
val result = mutableListOf<K>()
|
||||
forEach { pair ->
|
||||
result += pair.first
|
||||
}
|
||||
return result
|
||||
}
|
||||
fun <K, V> List<Pair<K, V>>.values(): List<V> {
|
||||
val result = mutableListOf<V>()
|
||||
forEach { pair ->
|
||||
result += pair.second
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun <T> List<T>.toSparseArray(destination: SparseArray<T>, key: (T) -> Int) {
|
||||
forEach {
|
||||
destination.put(key(it), it)
|
||||
@ -381,6 +501,12 @@ fun String.md5(): String {
|
||||
return BigInteger(1, md.digest(toByteArray())).toString(16).padStart(32, '0')
|
||||
}
|
||||
|
||||
fun String.sha256(): ByteArray {
|
||||
val md = MessageDigest.getInstance("SHA-256")
|
||||
md.update(toByteArray())
|
||||
return md.digest()
|
||||
}
|
||||
|
||||
fun RequestBody.bodyToString(): String {
|
||||
val buffer = Buffer()
|
||||
writeTo(buffer)
|
||||
@ -404,6 +530,29 @@ fun CharSequence?.asItalicSpannable(): Spannable {
|
||||
spannable.setSpan(StyleSpan(Typeface.ITALIC), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
return spannable
|
||||
}
|
||||
fun CharSequence?.asBoldSpannable(): Spannable {
|
||||
val spannable = SpannableString(this)
|
||||
spannable.setSpan(StyleSpan(Typeface.BOLD), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
return spannable
|
||||
}
|
||||
fun CharSequence.asSpannable(vararg spans: Any, substring: String? = null, ignoreCase: Boolean = false): Spannable {
|
||||
val spannable = SpannableString(this)
|
||||
if (substring == null) {
|
||||
spans.forEach {
|
||||
spannable.setSpan(it, 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
}
|
||||
else if (substring.isNotEmpty()) {
|
||||
var index = indexOf(substring, ignoreCase = ignoreCase)
|
||||
while (index >= 0) {
|
||||
spans.forEach {
|
||||
spannable.setSpan(it, index, index + substring.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
index = indexOf(substring, startIndex = index + 1, ignoreCase = ignoreCase)
|
||||
}
|
||||
}
|
||||
return spannable
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new read-only list only of those given elements, that are not empty.
|
||||
@ -411,7 +560,7 @@ fun CharSequence?.asItalicSpannable(): Spannable {
|
||||
*/
|
||||
fun <T : CharSequence> listOfNotEmpty(vararg elements: T): List<T> = elements.filterNot { it.isEmpty() }
|
||||
|
||||
fun List<CharSequence?>.concat(delimiter: String? = null): CharSequence {
|
||||
fun List<CharSequence?>.concat(delimiter: CharSequence? = null): CharSequence {
|
||||
if (this.isEmpty()) {
|
||||
return ""
|
||||
}
|
||||
@ -420,11 +569,13 @@ fun List<CharSequence?>.concat(delimiter: String? = null): CharSequence {
|
||||
return this[0] ?: ""
|
||||
}
|
||||
|
||||
var spanned = false
|
||||
for (piece in this) {
|
||||
if (piece is Spanned) {
|
||||
spanned = true
|
||||
break
|
||||
var spanned = delimiter is Spanned
|
||||
if (!spanned) {
|
||||
for (piece in this) {
|
||||
if (piece is Spanned) {
|
||||
spanned = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -472,8 +623,72 @@ fun JsonObject(vararg properties: Pair<String, Any?>): JsonObject {
|
||||
}
|
||||
}
|
||||
|
||||
fun JsonArray(vararg properties: Any?): JsonArray {
|
||||
return JsonArray().apply {
|
||||
for (property in properties) {
|
||||
when (property) {
|
||||
is JsonElement -> add(property as JsonElement?)
|
||||
is String -> add(property as String?)
|
||||
is Char -> add(property as Char?)
|
||||
is Number -> add(property as Number?)
|
||||
is Boolean -> add(property as Boolean?)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Bundle(vararg properties: Pair<String, Any?>): Bundle {
|
||||
return Bundle().apply {
|
||||
for (property in properties) {
|
||||
when (property.second) {
|
||||
is String -> putString(property.first, property.second as String?)
|
||||
is Char -> putChar(property.first, property.second as Char)
|
||||
is Int -> putInt(property.first, property.second as Int)
|
||||
is Long -> putLong(property.first, property.second as Long)
|
||||
is Float -> putFloat(property.first, property.second as Float)
|
||||
is Short -> putShort(property.first, property.second as Short)
|
||||
is Double -> putDouble(property.first, property.second as Double)
|
||||
is Boolean -> putBoolean(property.first, property.second as Boolean)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fun Intent(action: String? = null, vararg properties: Pair<String, Any?>): Intent {
|
||||
return Intent(action).putExtras(Bundle(*properties))
|
||||
}
|
||||
fun Intent(packageContext: Context, cls: Class<*>, vararg properties: Pair<String, Any?>): Intent {
|
||||
return Intent(packageContext, cls).putExtras(Bundle(*properties))
|
||||
}
|
||||
|
||||
fun Bundle.toJsonObject(): JsonObject {
|
||||
val json = JsonObject()
|
||||
keySet()?.forEach { key ->
|
||||
get(key)?.let {
|
||||
when (it) {
|
||||
is String -> json.addProperty(key, it)
|
||||
is Char -> json.addProperty(key, it)
|
||||
is Int -> json.addProperty(key, it)
|
||||
is Long -> json.addProperty(key, it)
|
||||
is Float -> json.addProperty(key, it)
|
||||
is Short -> json.addProperty(key, it)
|
||||
is Double -> json.addProperty(key, it)
|
||||
is Boolean -> json.addProperty(key, it)
|
||||
is Bundle -> json.add(key, it.toJsonObject())
|
||||
else -> json.addProperty(key, it.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
return json
|
||||
}
|
||||
fun Intent.toJsonObject() = extras?.toJsonObject()
|
||||
|
||||
fun JsonArray?.isNullOrEmpty(): Boolean = (this?.size() ?: 0) == 0
|
||||
fun JsonArray.isEmpty(): Boolean = this.size() == 0
|
||||
operator fun JsonArray.plusAssign(o: JsonElement) = this.add(o)
|
||||
operator fun JsonArray.plusAssign(o: String) = this.add(o)
|
||||
operator fun JsonArray.plusAssign(o: Char) = this.add(o)
|
||||
operator fun JsonArray.plusAssign(o: Number) = this.add(o)
|
||||
operator fun JsonArray.plusAssign(o: Boolean) = this.add(o)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
inline fun <T : View> T.onClick(crossinline onClickListener: (v: T) -> Unit) {
|
||||
@ -570,52 +785,65 @@ operator fun StringBuilder.plusAssign(str: String?) {
|
||||
this.append(str)
|
||||
}
|
||||
|
||||
fun Context.timeTill(time: Int, delimiter: String = " "): String {
|
||||
fun Context.timeTill(time: Int, delimiter: String = " ", countInSeconds: Boolean = false): String {
|
||||
val parts = mutableListOf<Pair<Int, Int>>()
|
||||
|
||||
val hours = time / 3600
|
||||
val minutes = (time - hours*3600) / 60
|
||||
val seconds = time - minutes*60 - hours*3600
|
||||
|
||||
var prefixAdded = false
|
||||
if (hours > 0) {
|
||||
if (!prefixAdded) parts += R.plurals.time_till_text to hours; prefixAdded = true
|
||||
parts += R.plurals.time_till_hours to hours
|
||||
}
|
||||
if (minutes > 0) {
|
||||
if (!prefixAdded) parts += R.plurals.time_till_text to minutes; prefixAdded = true
|
||||
parts += R.plurals.time_till_minutes to minutes
|
||||
}
|
||||
if (hours == 0 && minutes < 10) {
|
||||
if (!prefixAdded) parts += R.plurals.time_till_text to seconds; prefixAdded = true
|
||||
parts += R.plurals.time_till_seconds to seconds
|
||||
if (!countInSeconds) {
|
||||
var prefixAdded = false
|
||||
if (hours > 0) {
|
||||
if (!prefixAdded) parts += R.plurals.time_till_text to hours
|
||||
prefixAdded = true
|
||||
parts += R.plurals.time_till_hours to hours
|
||||
}
|
||||
if (minutes > 0) {
|
||||
if (!prefixAdded) parts += R.plurals.time_till_text to minutes
|
||||
prefixAdded = true
|
||||
parts += R.plurals.time_till_minutes to minutes
|
||||
}
|
||||
if (hours == 0 && minutes < 10) {
|
||||
if (!prefixAdded) parts += R.plurals.time_till_text to seconds
|
||||
prefixAdded = true
|
||||
parts += R.plurals.time_till_seconds to seconds
|
||||
}
|
||||
} else {
|
||||
parts += R.plurals.time_till_text to time
|
||||
parts += R.plurals.time_till_seconds to time
|
||||
}
|
||||
|
||||
return parts.joinToString(delimiter) { resources.getQuantityString(it.first, it.second, it.second) }
|
||||
}
|
||||
|
||||
fun Context.timeLeft(time: Int, delimiter: String = " "): String {
|
||||
fun Context.timeLeft(time: Int, delimiter: String = " ", countInSeconds: Boolean = false): String {
|
||||
val parts = mutableListOf<Pair<Int, Int>>()
|
||||
|
||||
val hours = time / 3600
|
||||
val minutes = (time - hours*3600) / 60
|
||||
val seconds = time - minutes*60 - hours*3600
|
||||
|
||||
var prefixAdded = false
|
||||
if (hours > 0) {
|
||||
if (!prefixAdded) parts += R.plurals.time_left_text to hours
|
||||
prefixAdded = true
|
||||
parts += R.plurals.time_left_hours to hours
|
||||
}
|
||||
if (minutes > 0) {
|
||||
if (!prefixAdded) parts += R.plurals.time_left_text to minutes
|
||||
prefixAdded = true
|
||||
parts += R.plurals.time_left_minutes to minutes
|
||||
}
|
||||
if (hours == 0 && minutes < 10) {
|
||||
if (!prefixAdded) parts += R.plurals.time_left_text to seconds
|
||||
prefixAdded = true
|
||||
parts += R.plurals.time_left_seconds to seconds
|
||||
if (!countInSeconds) {
|
||||
var prefixAdded = false
|
||||
if (hours > 0) {
|
||||
if (!prefixAdded) parts += R.plurals.time_left_text to hours
|
||||
prefixAdded = true
|
||||
parts += R.plurals.time_left_hours to hours
|
||||
}
|
||||
if (minutes > 0) {
|
||||
if (!prefixAdded) parts += R.plurals.time_left_text to minutes
|
||||
prefixAdded = true
|
||||
parts += R.plurals.time_left_minutes to minutes
|
||||
}
|
||||
if (hours == 0 && minutes < 10) {
|
||||
if (!prefixAdded) parts += R.plurals.time_left_text to seconds
|
||||
prefixAdded = true
|
||||
parts += R.plurals.time_left_seconds to seconds
|
||||
}
|
||||
} else {
|
||||
parts += R.plurals.time_left_text to time
|
||||
parts += R.plurals.time_left_seconds to time
|
||||
}
|
||||
|
||||
return parts.joinToString(delimiter) { resources.getQuantityString(it.first, it.second, it.second) }
|
||||
@ -635,3 +863,176 @@ fun Drawable.setTintColor(color: Int): Drawable {
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
inline fun <T> List<T>.ifNotEmpty(block: (List<T>) -> Unit) {
|
||||
if (!isEmpty())
|
||||
block(this)
|
||||
}
|
||||
|
||||
val String.firstLettersName: String
|
||||
get() {
|
||||
var nameShort = ""
|
||||
this.split(" ").forEach {
|
||||
if (it.isBlank())
|
||||
return@forEach
|
||||
nameShort += it[0].toLowerCase()
|
||||
}
|
||||
return nameShort
|
||||
}
|
||||
|
||||
val Throwable.stackTraceString: String
|
||||
get() {
|
||||
val sw = StringWriter()
|
||||
printStackTrace(PrintWriter(sw))
|
||||
return sw.toString()
|
||||
}
|
||||
|
||||
inline fun <T> LongSparseArray<T>.filter(predicate: (T) -> Boolean): List<T> {
|
||||
val destination = ArrayList<T>()
|
||||
this.forEach { _, element -> if (predicate(element)) destination.add(element) }
|
||||
return destination
|
||||
}
|
||||
|
||||
fun CharSequence.replace(oldValue: String, newValue: CharSequence, ignoreCase: Boolean = false): CharSequence =
|
||||
splitToSequence(oldValue, ignoreCase = ignoreCase).toList().concat(newValue)
|
||||
|
||||
fun Int.toColorStateList(): ColorStateList {
|
||||
val states = arrayOf(
|
||||
intArrayOf( android.R.attr.state_enabled ),
|
||||
intArrayOf(-android.R.attr.state_enabled ),
|
||||
intArrayOf(-android.R.attr.state_checked ),
|
||||
intArrayOf( android.R.attr.state_pressed )
|
||||
)
|
||||
|
||||
val colors = intArrayOf(
|
||||
this,
|
||||
this,
|
||||
this,
|
||||
this
|
||||
)
|
||||
|
||||
return ColorStateList(states, colors)
|
||||
}
|
||||
|
||||
fun SpannableStringBuilder.appendText(text: CharSequence): SpannableStringBuilder {
|
||||
append(text)
|
||||
return this
|
||||
}
|
||||
fun SpannableStringBuilder.appendSpan(text: CharSequence, what: Any, flags: Int): SpannableStringBuilder {
|
||||
val start: Int = length
|
||||
append(text)
|
||||
setSpan(what, start, length, flags)
|
||||
return this
|
||||
}
|
||||
|
||||
fun joinNotNullStrings(delimiter: String = "", vararg parts: String?): String {
|
||||
var first = true
|
||||
val sb = StringBuilder()
|
||||
for (part in parts) {
|
||||
if (part == null)
|
||||
continue
|
||||
if (!first)
|
||||
sb += delimiter
|
||||
first = false
|
||||
sb += part
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
fun String.notEmptyOrNull(): String? {
|
||||
return if (isEmpty())
|
||||
null
|
||||
else
|
||||
this
|
||||
}
|
||||
|
||||
fun String.base64Encode(): String {
|
||||
return encodeToString(toByteArray(), NO_WRAP)
|
||||
}
|
||||
fun ByteArray.base64Encode(): String {
|
||||
return encodeToString(this, NO_WRAP)
|
||||
}
|
||||
fun String.base64Decode(): ByteArray {
|
||||
return Base64.decode(this, Base64.DEFAULT)
|
||||
}
|
||||
fun String.base64DecodeToString(): String {
|
||||
return Base64.decode(this, Base64.DEFAULT).toString(Charset.defaultCharset())
|
||||
}
|
||||
|
||||
fun CheckBox.trigger() { isChecked = !isChecked }
|
||||
|
||||
fun Context.plural(@PluralsRes resId: Int, value: Int): String = resources.getQuantityString(resId, value, value)
|
||||
|
||||
fun Context.getNotificationTitle(type: Int): String {
|
||||
return getString(when (type) {
|
||||
Notification.TYPE_UPDATE -> R.string.notification_type_update
|
||||
Notification.TYPE_ERROR -> R.string.notification_type_error
|
||||
Notification.TYPE_TIMETABLE_CHANGED -> R.string.notification_type_timetable_change
|
||||
Notification.TYPE_TIMETABLE_LESSON_CHANGE -> R.string.notification_type_timetable_lesson_change
|
||||
Notification.TYPE_NEW_GRADE -> R.string.notification_type_new_grade
|
||||
Notification.TYPE_NEW_EVENT -> R.string.notification_type_new_event
|
||||
Notification.TYPE_NEW_HOMEWORK -> R.string.notification_type_new_homework
|
||||
Notification.TYPE_NEW_SHARED_EVENT -> R.string.notification_type_new_shared_event
|
||||
Notification.TYPE_NEW_SHARED_HOMEWORK -> R.string.notification_type_new_shared_homework
|
||||
Notification.TYPE_REMOVED_SHARED_EVENT -> R.string.notification_type_removed_shared_event
|
||||
Notification.TYPE_NEW_MESSAGE -> R.string.notification_type_new_message
|
||||
Notification.TYPE_NEW_NOTICE -> R.string.notification_type_notice
|
||||
Notification.TYPE_NEW_ATTENDANCE -> R.string.notification_type_attendance
|
||||
Notification.TYPE_SERVER_MESSAGE -> R.string.notification_type_server_message
|
||||
Notification.TYPE_LUCKY_NUMBER -> R.string.notification_type_lucky_number
|
||||
Notification.TYPE_FEEDBACK_MESSAGE -> R.string.notification_type_feedback_message
|
||||
Notification.TYPE_NEW_ANNOUNCEMENT -> R.string.notification_type_new_announcement
|
||||
Notification.TYPE_AUTO_ARCHIVING -> R.string.notification_type_auto_archiving
|
||||
Notification.TYPE_GENERAL -> R.string.notification_type_general
|
||||
else -> R.string.notification_type_general
|
||||
})
|
||||
}
|
||||
|
||||
fun Cursor?.getString(columnName: String) = this?.getStringOrNull(getColumnIndex(columnName))
|
||||
fun Cursor?.getInt(columnName: String) = this?.getIntOrNull(getColumnIndex(columnName))
|
||||
fun Cursor?.getLong(columnName: String) = this?.getLongOrNull(getColumnIndex(columnName))
|
||||
|
||||
fun OkHttpClient.Builder.installHttpsSupport(context: Context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
try {
|
||||
try {
|
||||
ProviderInstaller.installIfNeeded(context)
|
||||
} catch (e: Exception) {
|
||||
Log.e("OkHttpTLSCompat", "Play Services not found or outdated")
|
||||
|
||||
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
|
||||
trustManagerFactory.init(null as KeyStore?)
|
||||
|
||||
val x509TrustManager = trustManagerFactory.trustManagers.singleOrNull { it is X509TrustManager } as X509TrustManager?
|
||||
?: return
|
||||
|
||||
val sc = SSLContext.getInstance("TLSv1.2")
|
||||
sc.init(null, null, null)
|
||||
sslSocketFactory(TLSSocketFactory(sc.socketFactory), x509TrustManager)
|
||||
val cs: ConnectionSpec = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
|
||||
.tlsVersions(TlsVersion.TLS_1_0)
|
||||
.tlsVersions(TlsVersion.TLS_1_1)
|
||||
.tlsVersions(TlsVersion.TLS_1_2)
|
||||
.build()
|
||||
val specs: MutableList<ConnectionSpec> = ArrayList()
|
||||
specs.add(cs)
|
||||
specs.add(ConnectionSpec.COMPATIBLE_TLS)
|
||||
specs.add(ConnectionSpec.CLEARTEXT)
|
||||
connectionSpecs(specs)
|
||||
}
|
||||
} catch (exc: Exception) {
|
||||
Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun CharSequence.containsAll(list: List<CharSequence>, ignoreCase: Boolean = false): Boolean {
|
||||
for (i in list) {
|
||||
if (!contains(i, ignoreCase))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun RadioButton.setOnSelectedListener(listener: (buttonView: CompoundButton) -> Unit)
|
||||
= setOnCheckedChangeListener { buttonView, isChecked -> if (isChecked) listener(buttonView) }
|
||||
|
@ -1,6 +1,5 @@
|
||||
package pl.szczodrzynski.edziennik
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.ActivityManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
@ -9,8 +8,11 @@ import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.os.*
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
@ -19,6 +21,7 @@ import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.danimahardhika.cafebar.CafeBar
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.mikepenz.iconics.IconicsColor
|
||||
@ -31,18 +34,22 @@ import com.mikepenz.materialdrawer.model.ProfileDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.interfaces.IProfile
|
||||
import kotlinx.coroutines.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import pl.droidsonroids.gif.GifDrawable
|
||||
import pl.szczodrzynski.edziennik.App.APP_URL
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.task.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.data.api.events.*
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata.*
|
||||
import pl.szczodrzynski.edziennik.databinding.ActivitySzkolnyBinding
|
||||
import pl.szczodrzynski.edziennik.network.ServerRequest
|
||||
import pl.szczodrzynski.edziennik.sync.AppManagerDetectedEvent
|
||||
import pl.szczodrzynski.edziennik.sync.SyncWorker
|
||||
import pl.szczodrzynski.edziennik.sync.UpdateWorker
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.ServerMessageDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.settings.ProfileRemoveDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.sync.SyncViewListDialog
|
||||
@ -50,27 +57,32 @@ import pl.szczodrzynski.edziennik.ui.modules.agenda.AgendaFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.announcements.AnnouncementsFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.base.DebugFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.base.MainSnackbar
|
||||
import pl.szczodrzynski.edziennik.ui.modules.behaviour.BehaviourFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar
|
||||
import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.feedback.HelpFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.editor.GradesEditorFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragmentV2
|
||||
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessageFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesComposeFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesListFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsNewFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.timetable.v2.TimetableFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.webpush.WebPushFragment
|
||||
import pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoTouch
|
||||
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.dpToPx
|
||||
import pl.szczodrzynski.edziennik.utils.appManagerIntentList
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.NavTarget
|
||||
import pl.szczodrzynski.navlib.*
|
||||
import pl.szczodrzynski.navlib.SystemBarsUtil.Companion.COLOR_HALF_TRANSPARENT
|
||||
@ -83,9 +95,10 @@ import pl.szczodrzynski.navlib.drawer.items.withAppTitle
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
class MainActivity : AppCompatActivity(), CoroutineScope {
|
||||
companion object {
|
||||
|
||||
var useOldMessages = false
|
||||
@ -98,6 +111,7 @@ class MainActivity : AppCompatActivity() {
|
||||
const val DRAWER_PROFILE_SYNC_ALL = 201
|
||||
const val DRAWER_PROFILE_EXPORT_DATA = 202
|
||||
const val DRAWER_PROFILE_MANAGE = 203
|
||||
const val DRAWER_PROFILE_MARK_ALL_AS_READ = 204
|
||||
const val DRAWER_ITEM_HOME = 1
|
||||
const val DRAWER_ITEM_TIMETABLE = 11
|
||||
const val DRAWER_ITEM_AGENDA = 12
|
||||
@ -115,6 +129,8 @@ class MainActivity : AppCompatActivity() {
|
||||
const val TARGET_HELP = 502
|
||||
const val TARGET_FEEDBACK = 120
|
||||
const val TARGET_MESSAGES_DETAILS = 503
|
||||
const val TARGET_MESSAGES_COMPOSE = 504
|
||||
const val TARGET_WEB_PUSH = 140
|
||||
|
||||
const val HOME_ID = DRAWER_ITEM_HOME
|
||||
|
||||
@ -122,7 +138,7 @@ class MainActivity : AppCompatActivity() {
|
||||
val list: MutableList<NavTarget> = mutableListOf()
|
||||
|
||||
// home item
|
||||
list += NavTarget(DRAWER_ITEM_HOME, R.string.menu_home_page, HomeFragmentV2::class)
|
||||
list += NavTarget(DRAWER_ITEM_HOME, R.string.menu_home_page, HomeFragment::class)
|
||||
.withTitle(R.string.app_name)
|
||||
.withIcon(CommunityMaterial.Icon2.cmd_home_outline)
|
||||
.isInDrawer(true)
|
||||
@ -196,6 +212,10 @@ class MainActivity : AppCompatActivity() {
|
||||
.withDescription(R.string.drawer_manage_profiles_desc)
|
||||
.isInProfileList(false)
|
||||
|
||||
list += NavTarget(DRAWER_PROFILE_MARK_ALL_AS_READ, R.string.menu_mark_everything_as_read, null)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
|
||||
.isInProfileList(true)
|
||||
|
||||
list += NavTarget(DRAWER_PROFILE_SYNC_ALL, R.string.menu_sync_all, null)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_download_outline)
|
||||
.isInProfileList(true)
|
||||
@ -205,17 +225,24 @@ class MainActivity : AppCompatActivity() {
|
||||
list += NavTarget(TARGET_GRADES_EDITOR, R.string.menu_grades_editor, GradesEditorFragment::class)
|
||||
list += NavTarget(TARGET_HELP, R.string.menu_help, HelpFragment::class)
|
||||
list += NavTarget(TARGET_FEEDBACK, R.string.menu_feedback, FeedbackFragment::class)
|
||||
list += NavTarget(TARGET_MESSAGES_DETAILS, R.string.menu_message, MessageFragment::class)
|
||||
list += NavTarget(TARGET_MESSAGES_DETAILS, R.string.menu_message, MessageFragment::class).withPopTo(DRAWER_ITEM_MESSAGES)
|
||||
list += NavTarget(TARGET_MESSAGES_COMPOSE, R.string.menu_message_compose, MessagesComposeFragment::class)
|
||||
list += NavTarget(TARGET_WEB_PUSH, R.string.menu_web_push, WebPushFragment::class)
|
||||
list += NavTarget(DRAWER_ITEM_DEBUG, R.string.menu_debug, DebugFragment::class)
|
||||
|
||||
list
|
||||
}
|
||||
}
|
||||
|
||||
private var job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
val b: ActivitySzkolnyBinding by lazy { ActivitySzkolnyBinding.inflate(layoutInflater) }
|
||||
val navView: NavView by lazy { b.navView }
|
||||
val drawer: NavDrawer by lazy { navView.drawer }
|
||||
val bottomSheet: NavBottomSheet by lazy { navView.bottomSheet }
|
||||
val mainSnackbar: MainSnackbar by lazy { MainSnackbar(this) }
|
||||
val errorSnackbar: ErrorSnackbar by lazy { ErrorSnackbar(this) }
|
||||
|
||||
val swipeRefreshLayout: SwipeRefreshLayoutNoTouch by lazy { b.swipeRefreshLayout }
|
||||
@ -226,10 +253,11 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
private val fragmentManager by lazy { supportFragmentManager }
|
||||
private lateinit var navTarget: NavTarget
|
||||
private val navTargetId
|
||||
private var navArguments: Bundle? = null
|
||||
val navTargetId
|
||||
get() = navTarget.id
|
||||
|
||||
private val navBackStack = mutableListOf<NavTarget>()
|
||||
private val navBackStack = mutableListOf<Pair<NavTarget, Bundle?>>()
|
||||
private var navLoading = true
|
||||
|
||||
/* ____ _____ _
|
||||
@ -241,14 +269,26 @@ class MainActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
d(TAG, "Activity created")
|
||||
|
||||
setTheme(Themes.appTheme)
|
||||
|
||||
app.config.ui.language?.let {
|
||||
setLanguage(it)
|
||||
}
|
||||
|
||||
if (App.profileId == 0) {
|
||||
onProfileListEmptyEvent(ProfileListEmptyEvent())
|
||||
return
|
||||
}
|
||||
|
||||
d(TAG, "Profile is valid, inflating views")
|
||||
|
||||
setContentView(b.root)
|
||||
|
||||
Log.d(TAG, Signing.appPassword)
|
||||
|
||||
mainSnackbar.setCoordinator(b.navView.coordinator, b.navView.bottomBar)
|
||||
errorSnackbar.setCoordinator(b.navView.coordinator, b.navView.bottomBar)
|
||||
|
||||
navLoading = true
|
||||
@ -303,15 +343,17 @@ class MainActivity : AppCompatActivity() {
|
||||
removeAllItems()
|
||||
toggleGroupEnabled = false
|
||||
textInputEnabled = false
|
||||
onCloseListener = {
|
||||
if (!app.config.ui.bottomSheetOpened)
|
||||
app.config.ui.bottomSheetOpened = true
|
||||
}
|
||||
}
|
||||
|
||||
drawer.apply {
|
||||
setAccountHeaderBackground(app.config.ui.headerBackground)
|
||||
|
||||
drawerProfileListEmptyListener = {
|
||||
app.config.loginFinished = false
|
||||
app.saveConfig("loginFinished")
|
||||
profileListEmptyListener()
|
||||
onProfileListEmptyEvent(ProfileListEmptyEvent())
|
||||
}
|
||||
drawerItemSelectedListener = { id, position, drawerItem ->
|
||||
loadTarget(id)
|
||||
@ -340,32 +382,19 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
navTarget = navTargetList[0]
|
||||
|
||||
var profileListEmpty = drawer.profileListEmpty
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
intent?.putExtras(savedInstanceState)
|
||||
savedInstanceState.clear()
|
||||
}
|
||||
|
||||
if (!profileListEmpty) {
|
||||
handleIntent(intent?.extras)
|
||||
}
|
||||
app.db.profileDao().allFull.observe(this, Observer { profiles ->
|
||||
// TODO fix weird -1 profiles ???
|
||||
profiles.removeAll { it.id < 0 }
|
||||
drawer.setProfileList(profiles)
|
||||
if (profileListEmpty) {
|
||||
profileListEmpty = false
|
||||
handleIntent(intent?.extras)
|
||||
}
|
||||
else if (app.profile != null) {
|
||||
drawer.currentProfile = app.profile.id
|
||||
}
|
||||
app.db.profileDao().all.observe(this, Observer { profiles ->
|
||||
drawer.setProfileList(profiles.filter { it.id >= 0 }.toMutableList())
|
||||
drawer.currentProfile = App.profileId
|
||||
})
|
||||
|
||||
// if null, getAllFull will load a profile and update drawerItems
|
||||
if (app.profile != null)
|
||||
setDrawerItems()
|
||||
setDrawerItems()
|
||||
|
||||
handleIntent(intent?.extras)
|
||||
|
||||
app.db.metadataDao().unreadCounts.observe(this, Observer { unreadCounters ->
|
||||
unreadCounters.map {
|
||||
@ -385,6 +414,7 @@ class MainActivity : AppCompatActivity() {
|
||||
isStoragePermissionGranted()
|
||||
|
||||
SyncWorker.scheduleNext(app)
|
||||
UpdateWorker.scheduleNext(app)
|
||||
|
||||
// APP BACKGROUND
|
||||
if (app.config.ui.appBackground != null) {
|
||||
@ -410,18 +440,15 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
// IT'S WINTER MY DUDES
|
||||
val today = Date.getToday()
|
||||
if ((today.month == 12 || today.month == 1) && app.config.ui.snowfall) {
|
||||
b.rootFrame.addView(layoutInflater.inflate(R.layout.snowfall, b.rootFrame, false))
|
||||
}
|
||||
|
||||
// WHAT'S NEW DIALOG
|
||||
if (app.config.appVersion < BuildConfig.VERSION_CODE) {
|
||||
ServerRequest(app, app.requestScheme + APP_URL + "main.php?just_updated", "MainActivity/JU")
|
||||
.run { e, result ->
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
try {
|
||||
ChangelogDialog().show(supportFragmentManager, "whats_new")
|
||||
} catch (e2: Exception) {
|
||||
e2.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
ChangelogDialog(this)
|
||||
if (app.config.appVersion < 170) {
|
||||
//Intent intent = new Intent(this, ChangelogIntroActivity.class);
|
||||
//startActivity(intent);
|
||||
@ -490,29 +517,27 @@ class MainActivity : AppCompatActivity() {
|
||||
.withIcon(CommunityMaterial.Icon.cmd_android_studio)
|
||||
.withOnClickListener(View.OnClickListener { loadTarget(DRAWER_ITEM_DEBUG) })
|
||||
}
|
||||
|
||||
EventBus.getDefault().register(this)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
EventBus.getDefault().unregister(this)
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
var profileListEmptyListener = {
|
||||
startActivityForResult(Intent(this, LoginActivity::class.java), REQUEST_LOGIN_ACTIVITY)
|
||||
}
|
||||
private var profileSettingClickListener = { id: Int, view: View? ->
|
||||
when (id) {
|
||||
DRAWER_PROFILE_ADD_NEW -> {
|
||||
LoginActivity.privacyPolicyAccepted = true
|
||||
// else it would try to navigateUp onBackPressed, which it can't do. There is no parent fragment
|
||||
LoginActivity.firstCompleted = false
|
||||
profileListEmptyListener()
|
||||
startActivityForResult(Intent(this, LoginActivity::class.java), REQUEST_LOGIN_ACTIVITY)
|
||||
}
|
||||
DRAWER_PROFILE_SYNC_ALL -> {
|
||||
EdziennikTask.sync().enqueue(this)
|
||||
}
|
||||
DRAWER_PROFILE_MARK_ALL_AS_READ -> { launch {
|
||||
withContext(Dispatchers.Default) {
|
||||
app.db.profileDao().allNow.forEach { profile ->
|
||||
if (profile.loginStoreType != LoginStore.LOGIN_TYPE_LIBRUS)
|
||||
app.db.metadataDao().setAllSeenExceptMessagesAndAnnouncements(profile.id, true)
|
||||
else
|
||||
app.db.metadataDao().setAllSeenExceptMessages(profile.id, true)
|
||||
}
|
||||
}
|
||||
Toast.makeText(this@MainActivity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show()
|
||||
}}
|
||||
else -> {
|
||||
loadTarget(id)
|
||||
}
|
||||
@ -546,7 +571,7 @@ class MainActivity : AppCompatActivity() {
|
||||
).enqueue(this)
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onSyncStartedEvent(event: ApiTaskStartedEvent) {
|
||||
fun onApiTaskStartedEvent(event: ApiTaskStartedEvent) {
|
||||
swipeRefreshLayout.isRefreshing = true
|
||||
if (event.profileId == App.profileId) {
|
||||
navView.toolbar.apply {
|
||||
@ -557,7 +582,14 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onSyncProgressEvent(event: ApiTaskProgressEvent) {
|
||||
fun onProfileListEmptyEvent(event: ProfileListEmptyEvent) {
|
||||
d(TAG, "Profile list is empty. Launch LoginActivity.")
|
||||
app.config.loginFinished = false
|
||||
startActivity(Intent(this, LoginActivity::class.java))
|
||||
finish()
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onApiTaskProgressEvent(event: ApiTaskProgressEvent) {
|
||||
if (event.profileId == App.profileId) {
|
||||
navView.toolbar.apply {
|
||||
subtitleFormat = null
|
||||
@ -570,8 +602,9 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onSyncProfileFinishedEvent(event: ApiTaskFinishedEvent) {
|
||||
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||
fun onApiTaskFinishedEvent(event: ApiTaskFinishedEvent) {
|
||||
EventBus.getDefault().removeStickyEvent(event)
|
||||
if (event.profileId == App.profileId) {
|
||||
navView.toolbar.apply {
|
||||
subtitleFormat = R.string.toolbar_subtitle
|
||||
@ -580,22 +613,26 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onSyncFinishedEvent(event: ApiTaskAllFinishedEvent) {
|
||||
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||
fun onApiTaskAllFinishedEvent(event: ApiTaskAllFinishedEvent) {
|
||||
EventBus.getDefault().removeStickyEvent(event)
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onSyncErrorEvent(event: ApiTaskErrorEvent) {
|
||||
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||
fun onApiTaskErrorEvent(event: ApiTaskErrorEvent) {
|
||||
EventBus.getDefault().removeStickyEvent(event)
|
||||
navView.toolbar.apply {
|
||||
subtitleFormat = R.string.toolbar_subtitle
|
||||
subtitleFormatWithUnread = R.plurals.toolbar_subtitle_with_unread
|
||||
subtitle = "Gotowe"
|
||||
}
|
||||
mainSnackbar.dismiss()
|
||||
errorSnackbar.addError(event.error).show()
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||
fun onAppManagerDetectedEvent(event: AppManagerDetectedEvent) {
|
||||
if (app.appConfig.dontShowAppManagerDialog)
|
||||
EventBus.getDefault().removeStickyEvent(event)
|
||||
if (app.config.sync.dontShowAppManagerDialog)
|
||||
return
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.app_manager_dialog_title)
|
||||
@ -617,8 +654,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
.setNeutralButton(R.string.dont_ask_again) { dialog, which ->
|
||||
app.appConfig.dontShowAppManagerDialog = true
|
||||
app.saveConfig("dontShowAppManagerDialog")
|
||||
app.config.sync.dontShowAppManagerDialog = true
|
||||
}
|
||||
.setCancelable(false)
|
||||
.show()
|
||||
@ -660,26 +696,46 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
d(TAG, "}")
|
||||
|
||||
var intentProfileId = -1
|
||||
var intentTargetId = -1
|
||||
|
||||
if (extras?.containsKey("action") == true) {
|
||||
val handled = when (extras.getString("action")) {
|
||||
"serverMessage" -> {
|
||||
ServerMessageDialog(
|
||||
this,
|
||||
extras.getString("serverMessageTitle") ?: getString(R.string.app_name),
|
||||
extras.getString("serverMessageText") ?: ""
|
||||
)
|
||||
true
|
||||
}
|
||||
"feedbackMessage" -> {
|
||||
intentTargetId = TARGET_FEEDBACK
|
||||
false
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
if (handled)
|
||||
return
|
||||
}
|
||||
|
||||
if (extras?.containsKey("reloadProfileId") == true) {
|
||||
val reloadProfileId = extras.getInt("reloadProfileId", -1)
|
||||
extras.remove("reloadProfileId")
|
||||
if (reloadProfileId == -1 || (app.profile != null && app.profile.id == reloadProfileId)) {
|
||||
if (reloadProfileId == -1 || app.profile.id == reloadProfileId) {
|
||||
reloadTarget()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var intentProfileId = -1
|
||||
var intentTargetId = -1
|
||||
|
||||
if (extras?.containsKey("profileId") == true) {
|
||||
if (extras?.getInt("profileId", -1) != -1) {
|
||||
intentProfileId = extras.getInt("profileId", -1)
|
||||
extras.remove("profileId")
|
||||
extras?.remove("profileId")
|
||||
}
|
||||
|
||||
if (extras?.containsKey("fragmentId") == true) {
|
||||
if (extras?.getInt("fragmentId", -1) != -1) {
|
||||
intentTargetId = extras.getInt("fragmentId", -1)
|
||||
extras.remove("fragmentId")
|
||||
extras?.remove("fragmentId")
|
||||
}
|
||||
|
||||
/*if (intentTargetId == -1 && navController.currentDestination?.id == R.id.loadingFragment) {
|
||||
@ -693,9 +749,9 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
when {
|
||||
app.profile == null || app.profile.id == -1 -> {
|
||||
app.profile.id == 0 -> {
|
||||
if (intentProfileId == -1)
|
||||
intentProfileId = app.appSharedPrefs.getInt("current_profile_id", 1)
|
||||
intentProfileId = app.config.lastProfileId
|
||||
loadProfile(intentProfileId, intentTargetId, extras)
|
||||
}
|
||||
intentProfileId != -1 -> {
|
||||
@ -734,16 +790,32 @@ class MainActivity : AppCompatActivity() {
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
d(TAG, "Activity started")
|
||||
super.onStart()
|
||||
}
|
||||
override fun onStop() {
|
||||
d(TAG, "Activity stopped")
|
||||
super.onStop()
|
||||
}
|
||||
override fun onResume() {
|
||||
d(TAG, "Activity resumed")
|
||||
val filter = IntentFilter()
|
||||
filter.addAction(Intent.ACTION_MAIN)
|
||||
registerReceiver(intentReceiver, filter)
|
||||
EventBus.getDefault().register(this)
|
||||
super.onResume()
|
||||
}
|
||||
override fun onPause() {
|
||||
d(TAG, "Activity paused")
|
||||
unregisterReceiver(intentReceiver)
|
||||
EventBus.getDefault().unregister(this)
|
||||
super.onPause()
|
||||
}
|
||||
override fun onDestroy() {
|
||||
d(TAG, "Activity destroyed")
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
@ -757,15 +829,10 @@ class MainActivity : AppCompatActivity() {
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode == REQUEST_LOGIN_ACTIVITY) {
|
||||
if (resultCode == Activity.RESULT_CANCELED && false) {
|
||||
if (!app.config.loginFinished)
|
||||
finish()
|
||||
}
|
||||
else {
|
||||
if (!app.config.loginFinished)
|
||||
finish()
|
||||
else {
|
||||
handleIntent(data?.extras)
|
||||
}
|
||||
handleIntent(data?.extras)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -786,31 +853,23 @@ class MainActivity : AppCompatActivity() {
|
||||
fun loadProfile(id: Int) = loadProfile(id, navTargetId)
|
||||
fun loadProfile(id: Int, arguments: Bundle?) = loadProfile(id, navTargetId, arguments)
|
||||
fun loadProfile(id: Int, drawerSelection: Int, arguments: Bundle? = null) {
|
||||
//d("NavDebug", "loadProfile(id = $id, drawerSelection = $drawerSelection)")
|
||||
if (app.profile != null && App.profileId == id) {
|
||||
if (App.profileId == id) {
|
||||
drawer.currentProfile = app.profile.id
|
||||
loadTarget(drawerSelection, arguments)
|
||||
return
|
||||
}
|
||||
AsyncTask.execute {
|
||||
app.profileLoadById(id)
|
||||
app.profileLoad(id) {
|
||||
MessagesFragment.pageSelection = -1
|
||||
MessagesListFragment.tapPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
|
||||
MessagesListFragment.topPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
|
||||
MessagesListFragment.bottomPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
|
||||
|
||||
this.runOnUiThread {
|
||||
if (app.profile == null) {
|
||||
LoginActivity.firstCompleted = false
|
||||
if (app.config.loginFinished) {
|
||||
// this shouldn't run
|
||||
profileListEmptyListener()
|
||||
}
|
||||
} else {
|
||||
setDrawerItems()
|
||||
// the drawer profile is updated automatically when the drawer item is clicked
|
||||
// update it manually when switching profiles from other source
|
||||
//if (drawer.currentProfile != app.profile.id)
|
||||
drawer.currentProfile = app.profile.id
|
||||
loadTarget(drawerSelection, arguments)
|
||||
}
|
||||
}
|
||||
setDrawerItems()
|
||||
// the drawer profile is updated automatically when the drawer item is clicked
|
||||
// update it manually when switching profiles from other source
|
||||
//if (drawer.currentProfile != app.profile.id)
|
||||
drawer.currentProfile = app.profileId
|
||||
loadTarget(drawerSelection, arguments)
|
||||
}
|
||||
}
|
||||
fun loadTarget(id: Int, arguments: Bundle? = null) {
|
||||
@ -819,7 +878,7 @@ class MainActivity : AppCompatActivity() {
|
||||
loadId = DRAWER_ITEM_HOME
|
||||
}
|
||||
val target = navTargetList
|
||||
.singleOrNull { it.id == loadId }
|
||||
.firstOrNull { it.id == loadId }
|
||||
if (target == null) {
|
||||
Toast.makeText(this, getString(R.string.error_invalid_fragment, id), Toast.LENGTH_LONG).show()
|
||||
loadTarget(navTargetList.first(), arguments)
|
||||
@ -829,7 +888,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
private fun loadTarget(target: NavTarget, arguments: Bundle? = null) {
|
||||
d("NavDebug", "loadItem(id = ${target.id})")
|
||||
d("NavDebug", "loadTarget(target = $target, arguments = $arguments)")
|
||||
|
||||
bottomSheet.close()
|
||||
bottomSheet.removeAllContextual()
|
||||
@ -856,7 +915,7 @@ class MainActivity : AppCompatActivity() {
|
||||
)
|
||||
}
|
||||
else {
|
||||
navBackStack.lastIndexOf(target).let {
|
||||
navBackStack.keys().lastIndexOf(target).let {
|
||||
if (it == -1)
|
||||
return@let target
|
||||
// pop the back stack up until that target
|
||||
@ -887,8 +946,9 @@ class MainActivity : AppCompatActivity() {
|
||||
R.anim.task_open_enter,
|
||||
R.anim.task_open_exit
|
||||
)
|
||||
navBackStack.add(navTarget)
|
||||
navBackStack.add(navTarget to arguments)
|
||||
navTarget = target
|
||||
navArguments = arguments
|
||||
}
|
||||
}
|
||||
|
||||
@ -903,7 +963,7 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
d("NavDebug", "Current fragment ${navTarget.fragmentClass?.java?.simpleName}, pop to home ${navTarget.popToHome}, back stack:")
|
||||
navBackStack.forEachIndexed { index, target2 ->
|
||||
d("NavDebug", " - $index: ${target2.fragmentClass?.java?.simpleName}")
|
||||
d("NavDebug", " - $index: ${target2.first.fragmentClass?.java?.simpleName}")
|
||||
}
|
||||
|
||||
transaction.replace(R.id.fragment, fragment)
|
||||
@ -928,11 +988,18 @@ class MainActivity : AppCompatActivity() {
|
||||
return false
|
||||
}
|
||||
// TODO back stack argument support
|
||||
if (navTarget.popToHome) {
|
||||
loadTarget(HOME_ID)
|
||||
}
|
||||
else {
|
||||
loadTarget(navBackStack.last())
|
||||
when {
|
||||
navTarget.popToHome -> {
|
||||
loadTarget(HOME_ID)
|
||||
}
|
||||
navTarget.popTo != null -> {
|
||||
loadTarget(navTarget.popTo ?: HOME_ID)
|
||||
}
|
||||
else -> {
|
||||
navBackStack.last().let {
|
||||
loadTarget(it.first, it.second)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
@ -947,6 +1014,8 @@ class MainActivity : AppCompatActivity() {
|
||||
* that something has changed in the bottom sheet.
|
||||
*/
|
||||
fun gainAttention() {
|
||||
if (app.config.ui.bottomSheetOpened)
|
||||
return
|
||||
b.navView.postDelayed({
|
||||
navView.gainAttentionOnBottomBar()
|
||||
}, 2000)
|
||||
@ -994,12 +1063,11 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
fun setDrawerItems() {
|
||||
d("NavDebug", "setDrawerItems() app.profile = ${app.profile ?: "null"}")
|
||||
d("NavDebug", "setDrawerItems() app.profile = ${app.profile}")
|
||||
val drawerItems = arrayListOf<IDrawerItem<*>>()
|
||||
val drawerProfiles = arrayListOf<ProfileSettingDrawerItem>()
|
||||
|
||||
val supportedFragments = if (app.profile == null) arrayListOf<Int>()
|
||||
else app.profile.supportedFragments
|
||||
val supportedFragments = app.profile.supportedFragments
|
||||
|
||||
targetPopToHomeList.clear()
|
||||
|
||||
@ -1063,11 +1131,15 @@ class MainActivity : AppCompatActivity() {
|
||||
private var targetHomeId: Int = -1
|
||||
override fun onBackPressed() {
|
||||
if (!b.navView.onBackPressed()) {
|
||||
if (App.getConfig().ui.openDrawerOnBackPressed) {
|
||||
if (App.config.ui.openDrawerOnBackPressed) {
|
||||
b.navView.drawer.toggle()
|
||||
} else {
|
||||
navigateUp()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun error(error: ApiError) = errorSnackbar.addError(error).show()
|
||||
fun snackbar(text: String, actionText: String? = null, onClick: (() -> Unit)? = null) = mainSnackbar.snackbar(text, actionText, onClick)
|
||||
fun snackbarDismiss() = mainSnackbar.dismiss()
|
||||
}
|
||||
|
@ -1,353 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull;
|
||||
import pl.szczodrzynski.edziennik.receivers.BootReceiver;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time;
|
||||
|
||||
import static androidx.core.app.NotificationCompat.PRIORITY_DEFAULT;
|
||||
import static androidx.core.app.NotificationCompat.PRIORITY_MAX;
|
||||
|
||||
public class Notifier {
|
||||
|
||||
private static final String TAG = "Notifier";
|
||||
public static final int ID_GET_DATA = 1337000;
|
||||
public static final int ID_GET_DATA_ERROR = 1337001;
|
||||
private static String CHANNEL_GET_DATA_NAME;
|
||||
private static String CHANNEL_GET_DATA_DESC;
|
||||
private static final String GROUP_KEY_GET_DATA = "pl.szczodrzynski.edziennik.GET_DATA";
|
||||
|
||||
public static final int ID_NOTIFICATIONS = 1337002;
|
||||
public static String CHANNEL_NOTIFICATIONS_NAME;
|
||||
public static String CHANNEL_NOTIFICATIONS_DESC;
|
||||
public static final String GROUP_KEY_NOTIFICATIONS = "pl.szczodrzynski.edziennik.NOTIFICATIONS";
|
||||
|
||||
public static final int ID_NOTIFICATIONS_QUIET = 1337002;
|
||||
public static String CHANNEL_NOTIFICATIONS_QUIET_NAME;
|
||||
public static String CHANNEL_NOTIFICATIONS_QUIET_DESC;
|
||||
public static final String GROUP_KEY_NOTIFICATIONS_QUIET = "pl.szczodrzynski.edziennik.NOTIFICATIONS_QUIET";
|
||||
|
||||
private static final int ID_UPDATES = 1337003;
|
||||
private static String CHANNEL_UPDATES_NAME;
|
||||
private static String CHANNEL_UPDATES_DESC;
|
||||
private static final String GROUP_KEY_UPDATES = "pl.szczodrzynski.edziennik.UPDATES";
|
||||
|
||||
private App app;
|
||||
public NotificationManager notificationManager;
|
||||
private NotificationCompat.Builder getDataNotificationBuilder;
|
||||
public int notificationColor;
|
||||
|
||||
Notifier(App _app) {
|
||||
this.app = _app;
|
||||
|
||||
CHANNEL_GET_DATA_NAME = app.getString(R.string.notification_channel_get_data_name);
|
||||
CHANNEL_GET_DATA_DESC = app.getString(R.string.notification_channel_get_data_desc);
|
||||
CHANNEL_NOTIFICATIONS_NAME = app.getString(R.string.notification_channel_notifications_name);
|
||||
CHANNEL_NOTIFICATIONS_DESC = app.getString(R.string.notification_channel_notifications_desc);
|
||||
CHANNEL_NOTIFICATIONS_QUIET_NAME = app.getString(R.string.notification_channel_notifications_quiet_name);
|
||||
CHANNEL_NOTIFICATIONS_QUIET_DESC = app.getString(R.string.notification_channel_notifications_quiet_desc);
|
||||
CHANNEL_UPDATES_NAME = app.getString(R.string.notification_channel_updates_name);
|
||||
CHANNEL_UPDATES_DESC = app.getString(R.string.notification_channel_updates_desc);
|
||||
|
||||
notificationColor = ContextCompat.getColor(app.getContext(), R.color.colorPrimary);
|
||||
notificationManager = (NotificationManager) app.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channelGetData = new NotificationChannel(GROUP_KEY_GET_DATA, CHANNEL_GET_DATA_NAME, NotificationManager.IMPORTANCE_MIN);
|
||||
channelGetData.setDescription(CHANNEL_GET_DATA_DESC);
|
||||
notificationManager.createNotificationChannel(channelGetData);
|
||||
|
||||
NotificationChannel channelNotifications = new NotificationChannel(GROUP_KEY_NOTIFICATIONS, CHANNEL_NOTIFICATIONS_NAME, NotificationManager.IMPORTANCE_HIGH);
|
||||
channelNotifications.setDescription(CHANNEL_NOTIFICATIONS_DESC);
|
||||
channelNotifications.enableLights(true);
|
||||
channelNotifications.setLightColor(notificationColor);
|
||||
notificationManager.createNotificationChannel(channelNotifications);
|
||||
|
||||
NotificationChannel channelNotificationsQuiet = new NotificationChannel(GROUP_KEY_NOTIFICATIONS_QUIET, CHANNEL_NOTIFICATIONS_QUIET_NAME, NotificationManager.IMPORTANCE_DEFAULT);
|
||||
channelNotificationsQuiet.setDescription(CHANNEL_NOTIFICATIONS_QUIET_DESC);
|
||||
channelNotificationsQuiet.setSound(null, null);
|
||||
channelNotificationsQuiet.enableVibration(false);
|
||||
notificationManager.createNotificationChannel(channelNotificationsQuiet);
|
||||
|
||||
NotificationChannel channelUpdates = new NotificationChannel(GROUP_KEY_UPDATES, CHANNEL_UPDATES_NAME, NotificationManager.IMPORTANCE_HIGH);
|
||||
channelUpdates.setDescription(CHANNEL_UPDATES_DESC);
|
||||
notificationManager.createNotificationChannel(channelUpdates);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean shouldBeQuiet() {
|
||||
long now = Time.getNow().getInMillis();
|
||||
long start = app.config.getSync().getQuietHoursStart();
|
||||
long end = app.config.getSync().getQuietHoursEnd();
|
||||
if (start > end) {
|
||||
end += 1000 * 60 * 60 * 24;
|
||||
//Log.d(TAG, "Night passing");
|
||||
}
|
||||
if (start > now) {
|
||||
now += 1000 * 60 * 60 * 24;
|
||||
//Log.d(TAG, "Now is smaller");
|
||||
}
|
||||
//Log.d(TAG, "Start is "+start+", now is "+now+", end is "+end);
|
||||
return start > 0 && now >= start && now <= end;
|
||||
}
|
||||
|
||||
public int getNotificationDefaults() {
|
||||
return (shouldBeQuiet() ? 0 : Notification.DEFAULT_ALL);
|
||||
}
|
||||
public String getNotificationGroup() {
|
||||
return shouldBeQuiet() ? GROUP_KEY_NOTIFICATIONS_QUIET : GROUP_KEY_NOTIFICATIONS;
|
||||
}
|
||||
public int getNotificationPriority() {
|
||||
return shouldBeQuiet() ? PRIORITY_DEFAULT : PRIORITY_MAX;
|
||||
}
|
||||
|
||||
/* _____ _ _____ _
|
||||
| __ \ | | / ____| | |
|
||||
| | | | __ _| |_ __ _ | | __ ___| |_
|
||||
| | | |/ _` | __/ _` | | | |_ |/ _ \ __|
|
||||
| |__| | (_| | || (_| | | |__| | __/ |_
|
||||
|_____/ \__,_|\__\__,_| \_____|\___|\_*/
|
||||
public Notification notificationGetDataShow(int maxProgress) {
|
||||
/*Intent notificationIntent = new Intent(app.getContext(), SyncService.class);
|
||||
notificationIntent.setAction(ACTION_CANCEL);
|
||||
PendingIntent pendingIntent = PendingIntent.getService(app.getContext(), 0,
|
||||
notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);*/
|
||||
|
||||
getDataNotificationBuilder = new NotificationCompat.Builder(app, GROUP_KEY_GET_DATA)
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||
.setColor(notificationColor)
|
||||
.setContentTitle(app.getString(R.string.notification_get_data_title))
|
||||
.setContentText(app.getString(R.string.notification_get_data_text))
|
||||
//.addAction(R.drawable.ic_notification, app.getString(R.string.notification_get_data_cancel), pendingIntent)
|
||||
//.setGroup(GROUP_KEY_GET_DATA)
|
||||
.setOngoing(true)
|
||||
.setProgress(maxProgress, 0, false)
|
||||
.setTicker(app.getString(R.string.notification_get_data_summary))
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW);
|
||||
return getDataNotificationBuilder.build();
|
||||
}
|
||||
|
||||
public Notification notificationGetDataProgress(int progress, int maxProgress) {
|
||||
getDataNotificationBuilder.setProgress(maxProgress, progress, false);
|
||||
return getDataNotificationBuilder.build();
|
||||
}
|
||||
|
||||
public Notification notificationGetDataAction(int stringResId) {
|
||||
getDataNotificationBuilder.setContentTitle(app.getString(R.string.sync_action_format, app.getString(stringResId)));
|
||||
return getDataNotificationBuilder.build();
|
||||
}
|
||||
|
||||
public Notification notificationGetDataProfile(String profileName) {
|
||||
getDataNotificationBuilder.setContentText(profileName);
|
||||
return getDataNotificationBuilder.build();
|
||||
}
|
||||
|
||||
public Notification notificationGetDataError(String profileName, String error, int failedProfileId) {
|
||||
Intent notificationIntent = new Intent(app.getContext(), Notifier.GetDataRetryService.class);
|
||||
notificationIntent.putExtra("failedProfileId", failedProfileId);
|
||||
PendingIntent pendingIntent = PendingIntent.getService(app.getContext(), 0,
|
||||
notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
getDataNotificationBuilder.mActions.clear();
|
||||
/*try {
|
||||
//Use reflection clean up old actions
|
||||
Field f = getDataNotificationBuilder.getClass().getDeclaredField("mActions");
|
||||
f.setAccessible(true);
|
||||
f.set(getDataNotificationBuilder, new ArrayList<NotificationCompat.Action>());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}*/
|
||||
|
||||
getDataNotificationBuilder.setProgress(0, 0, false)
|
||||
.setTicker(app.getString(R.string.notification_get_data_error_summary))
|
||||
.setSmallIcon(android.R.drawable.stat_sys_warning)
|
||||
.addAction(R.drawable.ic_notification, app.getString(R.string.notification_get_data_once_again), pendingIntent)
|
||||
.setContentTitle(app.getString(R.string.notification_get_data_error_title, profileName))
|
||||
.setContentText(error)
|
||||
.setStyle(new NotificationCompat.BigTextStyle().bigText(error))
|
||||
.setOngoing(false);
|
||||
return getDataNotificationBuilder.build();
|
||||
}
|
||||
|
||||
public void notificationPost(int id, Notification notification) {
|
||||
notificationManager.notify(id, notification);
|
||||
}
|
||||
|
||||
public void notificationCancel(int id) {
|
||||
notificationManager.cancel(id);
|
||||
}
|
||||
|
||||
//public void notificationGetDataHide() {
|
||||
// notificationManager.cancel(ID_GET_DATA);
|
||||
// }
|
||||
|
||||
public static class GetDataRetryService extends IntentService {
|
||||
private static final String TAG = "Notifier/GetDataRetry";
|
||||
|
||||
public GetDataRetryService() {
|
||||
super(Notifier.GetDataRetryService.class.getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* _ _ _ _ __ _ _ _
|
||||
| \ | | | | (_)/ _(_) | | (_)
|
||||
| \| | ___ | |_ _| |_ _ ___ __ _| |_ _ ___ _ __
|
||||
| . ` |/ _ \| __| | _| |/ __/ _` | __| |/ _ \| '_ \
|
||||
| |\ | (_) | |_| | | | | (_| (_| | |_| | (_) | | | |
|
||||
|_| \_|\___/ \__|_|_| |_|\___\__,_|\__|_|\___/|_| |*/
|
||||
public void add(pl.szczodrzynski.edziennik.utils.models.Notification notification) {
|
||||
app.appConfig.notifications.add(notification);
|
||||
}
|
||||
|
||||
public void postAll(ProfileFull profile) {
|
||||
Collections.sort(app.appConfig.notifications, (o1, o2) -> (o2.addedDate - o1.addedDate > 0) ? 1 : (o2.addedDate - o1.addedDate < 0) ? -1 : 0);
|
||||
if (profile != null && !profile.getSyncNotifications())
|
||||
return;
|
||||
|
||||
if (app.appConfig.notifications.size() > 40) {
|
||||
app.appConfig.notifications.subList(40, app.appConfig.notifications.size() - 1).clear();
|
||||
}
|
||||
|
||||
int unreadCount = 0;
|
||||
List<pl.szczodrzynski.edziennik.utils.models.Notification> notificationList = new ArrayList<>();
|
||||
for (pl.szczodrzynski.edziennik.utils.models.Notification notification: app.appConfig.notifications) {
|
||||
if (!notification.notified) {
|
||||
notification.seen = false;
|
||||
notification.notified = true;
|
||||
unreadCount++;
|
||||
if (notificationList.size() < 10) {
|
||||
notificationList.add(notification);
|
||||
}
|
||||
}
|
||||
else {
|
||||
notification.seen = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (pl.szczodrzynski.edziennik.utils.models.Notification notification: notificationList) {
|
||||
Intent intent = new Intent(app, MainActivity.class);
|
||||
notification.fillIntent(intent);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(app, notification.id, intent, 0);
|
||||
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(app, getNotificationGroup())
|
||||
// title, text, type, date
|
||||
.setContentTitle(notification.title)
|
||||
.setContentText(notification.text)
|
||||
.setSubText(pl.szczodrzynski.edziennik.utils.models.Notification.stringType(app, notification.type))
|
||||
.setWhen(notification.addedDate)
|
||||
.setTicker(app.getString(R.string.notification_ticker_format, pl.szczodrzynski.edziennik.utils.models.Notification.stringType(app, notification.type)))
|
||||
// icon, color, lights, priority
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setColor(notificationColor)
|
||||
.setLights(0xFF00FFFF, 2000, 2000)
|
||||
.setPriority(getNotificationPriority())
|
||||
// channel, group, style
|
||||
.setChannelId(getNotificationGroup())
|
||||
.setGroup(getNotificationGroup())
|
||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
||||
.setStyle(new NotificationCompat.BigTextStyle().bigText(notification.text))
|
||||
// intent, auto cancel
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true);
|
||||
if (!shouldBeQuiet()) {
|
||||
notificationBuilder.setDefaults(getNotificationDefaults());
|
||||
}
|
||||
notificationManager.notify(notification.id, notificationBuilder.build());
|
||||
}
|
||||
|
||||
if (notificationList.size() > 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
Intent intent = new Intent(app, MainActivity.class);
|
||||
intent.setAction("android.intent.action.MAIN");
|
||||
intent.putExtra("fragmentId", MainActivity.DRAWER_ITEM_NOTIFICATIONS);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(app, ID_NOTIFICATIONS,
|
||||
intent, 0);
|
||||
|
||||
NotificationCompat.Builder groupBuilder =
|
||||
new NotificationCompat.Builder(app, getNotificationGroup())
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setColor(notificationColor)
|
||||
.setContentTitle(app.getString(R.string.notification_new_notification_title_format, unreadCount))
|
||||
.setGroupSummary(true)
|
||||
.setAutoCancel(true)
|
||||
.setChannelId(getNotificationGroup())
|
||||
.setGroup(getNotificationGroup())
|
||||
.setLights(0xFF00FFFF, 2000, 2000)
|
||||
.setPriority(getNotificationPriority())
|
||||
.setContentIntent(pendingIntent)
|
||||
.setStyle(new NotificationCompat.BigTextStyle());
|
||||
if (!shouldBeQuiet()) {
|
||||
groupBuilder.setDefaults(getNotificationDefaults());
|
||||
}
|
||||
notificationManager.notify(ID_NOTIFICATIONS, groupBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
/* _ _ _ _
|
||||
| | | | | | | |
|
||||
| | | |_ __ __| | __ _| |_ ___ ___
|
||||
| | | | '_ \ / _` |/ _` | __/ _ \/ __|
|
||||
| |__| | |_) | (_| | (_| | || __/\__ \
|
||||
\____/| .__/ \__,_|\__,_|\__\___||___/
|
||||
| |
|
||||
|*/
|
||||
public void notificationUpdatesShow(String updateVersion, String updateUrl, String updateFilename, boolean updateDirect) {
|
||||
if (!app.config.getSync().getNotifyAboutUpdates())
|
||||
return;
|
||||
Intent notificationIntent = new Intent(app.getContext(), BootReceiver.NotificationActionService.class)
|
||||
.putExtra("update_version", updateVersion)
|
||||
.putExtra("update_url", updateUrl)
|
||||
.putExtra("update_filename", updateFilename)
|
||||
.putExtra("update_direct", updateDirect);
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getService(app.getContext(), 0,
|
||||
notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(app, GROUP_KEY_UPDATES)
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download_done)
|
||||
.setColor(notificationColor)
|
||||
.setContentTitle(app.getString(R.string.notification_updates_title))
|
||||
.setContentText(app.getString(R.string.notification_updates_text, updateVersion))
|
||||
.setLights(0xFF00FFFF, 2000, 2000)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setTicker(app.getString(R.string.notification_updates_summary))
|
||||
.setPriority(PRIORITY_MAX)
|
||||
.setAutoCancel(true);
|
||||
if (!shouldBeQuiet()) {
|
||||
notificationBuilder.setDefaults(getNotificationDefaults());
|
||||
}
|
||||
notificationManager.notify(ID_UPDATES, notificationBuilder.build());
|
||||
}
|
||||
|
||||
public void notificationUpdatesHide() {
|
||||
if (!app.config.getSync().getNotifyAboutUpdates())
|
||||
return;
|
||||
notificationManager.cancel(ID_UPDATES);
|
||||
}
|
||||
|
||||
public void dump() {
|
||||
for (pl.szczodrzynski.edziennik.utils.models.Notification notification: app.appConfig.notifications) {
|
||||
Log.d(TAG, "Profile"+notification.profileId+" Notification from "+ Date.fromMillis(notification.addedDate).getFormattedString()+" "+ Time.fromMillis(notification.addedDate).getStringHMS()+" - "+notification.text);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,210 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2
|
||||
|
||||
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.R
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.Data
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.*
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_LUCKY_NUMBER
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_ANNOUNCEMENT
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_ATTENDANCE
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_EVENT
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_GRADE
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_HOMEWORK
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_MESSAGE
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_NOTICE
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_TIMETABLE_LESSON_CHANGE
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.notification.getNotificationTitle
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class DataNotifications(val data: Data) {
|
||||
companion object {
|
||||
private const val TAG = "DataNotifications"
|
||||
}
|
||||
|
||||
val app = data.app
|
||||
val profileId = data.profile?.id ?: -1
|
||||
val profileName = data.profile?.name ?: ""
|
||||
val profile = data.profile
|
||||
val loginStore = data.loginStore
|
||||
|
||||
init { run {
|
||||
if (profile == null) {
|
||||
return@run
|
||||
}
|
||||
|
||||
for (lesson in app.db.timetableDao().getNotNotifiedNow(profileId)) {
|
||||
val text = app.getString(R.string.notification_lesson_change_format, lesson.getDisplayChangeType(app), if (lesson.displayDate == null) "" else lesson.displayDate!!.formattedString, lesson.changeSubjectName)
|
||||
data.notifications += Notification(
|
||||
title = app.getNotificationTitle(TYPE_TIMETABLE_LESSON_CHANGE),
|
||||
text = text,
|
||||
type = TYPE_TIMETABLE_LESSON_CHANGE,
|
||||
profileId = profileId,
|
||||
profileName = profileName,
|
||||
viewId = DRAWER_ITEM_TIMETABLE,
|
||||
addedDate = lesson.addedDate
|
||||
).addExtra("timetableDate", lesson.displayDate?.stringY_m_d ?: "")
|
||||
}
|
||||
|
||||
for (event in app.db.eventDao().getNotNotifiedNow(profileId)) {
|
||||
val text = if (event.type == Event.TYPE_HOMEWORK)
|
||||
app.getString(
|
||||
if (event.subjectLongName.isNullOrEmpty())
|
||||
R.string.notification_homework_no_subject_format
|
||||
else
|
||||
R.string.notification_homework_format,
|
||||
event.subjectLongName,
|
||||
event.eventDate.formattedString
|
||||
)
|
||||
else
|
||||
app.getString(
|
||||
if (event.subjectLongName.isNullOrEmpty())
|
||||
R.string.notification_event_no_subject_format
|
||||
else
|
||||
R.string.notification_event_format,
|
||||
event.typeName,
|
||||
event.eventDate.formattedString,
|
||||
event.subjectLongName
|
||||
)
|
||||
val type = if (event.type == Event.TYPE_HOMEWORK) TYPE_NEW_HOMEWORK else TYPE_NEW_EVENT
|
||||
data.notifications += Notification(
|
||||
title = app.getNotificationTitle(type),
|
||||
text = text,
|
||||
type = type,
|
||||
profileId = profileId,
|
||||
profileName = profileName,
|
||||
viewId = if (event.type == Event.TYPE_HOMEWORK) DRAWER_ITEM_HOMEWORK else DRAWER_ITEM_AGENDA,
|
||||
addedDate = event.addedDate
|
||||
).addExtra("eventId", event.id).addExtra("eventDate", event.eventDate.value.toLong())
|
||||
}
|
||||
|
||||
val today = Date.getToday()
|
||||
val todayValue = today.value
|
||||
profile.currentSemester = profile.dateToSemester(today)
|
||||
|
||||
for (grade in app.db.gradeDao().getNotNotifiedNow(profileId)) {
|
||||
val gradeName = when (grade.type) {
|
||||
TYPE_SEMESTER1_PROPOSED, TYPE_SEMESTER2_PROPOSED -> app.getString(R.string.grade_semester_proposed_format_2, grade.name)
|
||||
TYPE_SEMESTER1_FINAL, TYPE_SEMESTER2_FINAL -> app.getString(R.string.grade_semester_final_format_2, grade.name)
|
||||
TYPE_YEAR_PROPOSED -> app.getString(R.string.grade_year_proposed_format_2, grade.name)
|
||||
TYPE_YEAR_FINAL -> app.getString(R.string.grade_year_final_format_2, grade.name)
|
||||
else -> grade.name
|
||||
}
|
||||
val text = app.getString(R.string.notification_grade_format, gradeName, grade.subjectLongName)
|
||||
data.notifications += Notification(
|
||||
title = app.getNotificationTitle(TYPE_NEW_GRADE),
|
||||
text = text,
|
||||
type = TYPE_NEW_GRADE,
|
||||
profileId = profileId,
|
||||
profileName = profileName,
|
||||
viewId = DRAWER_ITEM_GRADES,
|
||||
addedDate = grade.addedDate
|
||||
).addExtra("gradeId", grade.id).addExtra("gradesSubjectId", grade.subjectId)
|
||||
}
|
||||
|
||||
for (notice in app.db.noticeDao().getNotNotifiedNow(profileId)) {
|
||||
val noticeTypeStr = if (notice.type == Notice.TYPE_POSITIVE) app.getString(R.string.notification_notice_praise) else if (notice.type == Notice.TYPE_NEGATIVE) app.getString(R.string.notification_notice_warning) else app.getString(R.string.notification_notice_new)
|
||||
val text = app.getString(R.string.notification_notice_format, noticeTypeStr, notice.teacherFullName, Date.fromMillis(notice.addedDate).formattedString)
|
||||
data.notifications += Notification(
|
||||
title = app.getNotificationTitle(TYPE_NEW_NOTICE),
|
||||
text = text,
|
||||
type = TYPE_NEW_NOTICE,
|
||||
profileId = profileId,
|
||||
profileName = profileName,
|
||||
viewId = DRAWER_ITEM_BEHAVIOUR,
|
||||
addedDate = notice.addedDate
|
||||
).addExtra("noticeId", notice.id)
|
||||
}
|
||||
|
||||
for (attendance in app.db.attendanceDao().getNotNotifiedNow(profileId)) {
|
||||
var attendanceTypeStr = app.getString(R.string.notification_type_attendance)
|
||||
when (attendance.type) {
|
||||
Attendance.TYPE_ABSENT -> attendanceTypeStr = app.getString(R.string.notification_absence)
|
||||
Attendance.TYPE_ABSENT_EXCUSED -> attendanceTypeStr = app.getString(R.string.notification_absence_excused)
|
||||
Attendance.TYPE_BELATED -> attendanceTypeStr = app.getString(R.string.notification_belated)
|
||||
Attendance.TYPE_BELATED_EXCUSED -> attendanceTypeStr = app.getString(R.string.notification_belated_excused)
|
||||
Attendance.TYPE_RELEASED -> attendanceTypeStr = app.getString(R.string.notification_release)
|
||||
}
|
||||
val text = app.getString(
|
||||
if (attendance.subjectLongName.isNullOrEmpty())
|
||||
R.string.notification_attendance_no_lesson_format
|
||||
else
|
||||
R.string.notification_attendance_format,
|
||||
attendanceTypeStr,
|
||||
attendance.subjectLongName,
|
||||
attendance.lessonDate.formattedString
|
||||
)
|
||||
data.notifications += Notification(
|
||||
title = app.getNotificationTitle(TYPE_NEW_ATTENDANCE),
|
||||
text = text,
|
||||
type = TYPE_NEW_ATTENDANCE,
|
||||
profileId = profileId,
|
||||
profileName = profileName,
|
||||
viewId = DRAWER_ITEM_ATTENDANCE,
|
||||
addedDate = attendance.addedDate
|
||||
).addExtra("attendanceId", attendance.id).addExtra("attendanceSubjectId", attendance.subjectId)
|
||||
}
|
||||
|
||||
for (announcement in app.db.announcementDao().getNotNotifiedNow(profileId)) {
|
||||
val text = app.context.getString(R.string.notification_announcement_format, announcement.subject)
|
||||
data.notifications += Notification(
|
||||
title = app.getNotificationTitle(TYPE_NEW_ANNOUNCEMENT),
|
||||
text = text,
|
||||
type = TYPE_NEW_ANNOUNCEMENT,
|
||||
profileId = profileId,
|
||||
profileName = profileName,
|
||||
viewId = DRAWER_ITEM_ANNOUNCEMENTS,
|
||||
addedDate = announcement.addedDate
|
||||
).addExtra("announcementId", announcement.id)
|
||||
}
|
||||
|
||||
for (message in app.db.messageDao().getReceivedNotNotifiedNow(profileId)) {
|
||||
val text = app.context.getString(R.string.notification_message_format, message.senderFullName, message.subject)
|
||||
data.notifications += Notification(
|
||||
title = app.getNotificationTitle(TYPE_NEW_MESSAGE),
|
||||
text = text,
|
||||
type = TYPE_NEW_MESSAGE,
|
||||
profileId = profileId,
|
||||
profileName = profileName,
|
||||
viewId = DRAWER_ITEM_MESSAGES,
|
||||
addedDate = message.addedDate
|
||||
).addExtra("messageType", Message.TYPE_RECEIVED.toLong()).addExtra("messageId", message.id)
|
||||
}
|
||||
|
||||
val luckyNumbers = app.db.luckyNumberDao().getNotNotifiedNow(profileId)
|
||||
luckyNumbers?.removeAll { it.date < today }
|
||||
luckyNumbers?.forEach { luckyNumber ->
|
||||
val text = when (luckyNumber.date.value) {
|
||||
todayValue -> // LN for today
|
||||
app.getString(if (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) R.string.notification_lucky_number_yours_format else R.string.notification_lucky_number_format, luckyNumber.number)
|
||||
todayValue + 1 -> // LN for tomorrow
|
||||
app.getString(if (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) R.string.notification_lucky_number_yours_tomorrow_format else R.string.notification_lucky_number_tomorrow_format, luckyNumber.number)
|
||||
else -> // LN for later
|
||||
app.getString(if (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) R.string.notification_lucky_number_yours_later_format else R.string.notification_lucky_number_later_format, luckyNumber.date.formattedString, luckyNumber.number)
|
||||
}
|
||||
data.notifications += Notification(
|
||||
title = app.getNotificationTitle(TYPE_LUCKY_NUMBER),
|
||||
text = text,
|
||||
type = TYPE_LUCKY_NUMBER,
|
||||
profileId = profileId,
|
||||
profileName = profileName,
|
||||
viewId = DRAWER_ITEM_HOME,
|
||||
addedDate = luckyNumber.addedDate
|
||||
)
|
||||
}
|
||||
|
||||
data.db.metadataDao().setAllNotified(profileId, true)
|
||||
}}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-10-6.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2
|
||||
|
||||
object Regexes {
|
||||
val MOBIDZIENNIK_GRADES_SUBJECT_NAME by lazy {
|
||||
"""<div.*?>\n*\s*(.+?)\s*\n*(?:<.*?)??</div>""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_GRADES_COLOR by lazy {
|
||||
"""background-color:([#A-Fa-f0-9]+);""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_GRADES_CATEGORY by lazy {
|
||||
"""> (.+?):</span>""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_GRADES_CLASS_AVERAGE by lazy {
|
||||
"""Średnia ocen:.*<strong>([0-9]*\.?[0-9]*)</strong>""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_GRADES_ADDED_DATE by lazy {
|
||||
"""Wpisano:.*<strong>.+?,\s([0-9]+)\s(.+?)\s([0-9]{4}),\sgodzina\s([0-9:]+)</strong>""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_GRADES_COUNT_TO_AVG by lazy {
|
||||
"""Liczona do średniej:.*?<strong>nie<br/?></strong>""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_GRADES_DETAILS by lazy {
|
||||
"""<strong.*?>(.+?)</strong>.*?<sup>.+?</sup>.*?(?:<small>\((.+?)\)</small>.*?)?<span>.*?Wartość oceny:.*?<strong>([0-9.]+)</strong>.*?Wpisał\(a\):.*?<strong>(.+?)</strong>.*?(?:Komentarz:.*?<strong>(.+?)</strong>)?</span>""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
|
||||
val MOBIDZIENNIK_EVENT_TYPE by lazy {
|
||||
"""\(([0-9A-ząęóżźńśłć]*?)\)$""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_LUCKY_NUMBER by lazy {
|
||||
"""class="szczesliwy_numerek".*>0*([0-9]+)(?:/0*[0-9]+)*</a>""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_CLASS_CALENDAR by lazy {
|
||||
"""events: (.+),$""".toRegex(RegexOption.MULTILINE)
|
||||
}
|
||||
|
||||
val MOBIDZIENNIK_MESSAGE_READ_DATE by lazy {
|
||||
"""czas przeczytania:.+?,\s([0-9]+)\s(.+?)\s([0-9]{4}),\sgodzina\s([0-9:]+)""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_MESSAGE_SENT_READ_DATE by lazy {
|
||||
""".+?,\s([0-9]+)\s(.+?)\s([0-9]{4}),\sgodzina\s([0-9:]+)""".toRegex(RegexOption.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(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
|
||||
|
||||
|
||||
val IDZIENNIK_LOGIN_HIDDEN_FIELDS by lazy {
|
||||
"""<input type="hidden".+?name="([A-z0-9_]+)?".+?value="([A-z0-9_+-/=]+)?".+?>""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
val IDZIENNIK_LOGIN_ERROR by lazy {
|
||||
"""id="spanErrorMessage">(.*?)</""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
val IDZIENNIK_LOGIN_FIRST_ACCOUNT_NAME by lazy {
|
||||
"""Imię i nazwisko:.+?">(.+?)</div>""".toRegex(RegexOption.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/]+)<""".toRegex(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
val IDZIENNIK_LOGIN_FIRST_STUDENT_SELECT by lazy {
|
||||
"""<select.*?name="ctl00\${"$"}dxComboUczniowie".*?</select>""".toRegex(RegexOption.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(RegexOption.DOT_MATCHES_ALL)
|
||||
}
|
||||
|
||||
|
||||
|
||||
val VULCAN_SHITFT_ANNOTATION by lazy {
|
||||
"""\(przeniesiona (z|na) lekcj[ię] ([0-9]+), (.+)\)""".toRegex()
|
||||
}
|
||||
|
||||
|
||||
|
||||
val LIBRUS_ATTACHMENT_KEY by lazy {
|
||||
"""singleUseKey=([0-9A-f_]+)""".toRegex()
|
||||
}
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-10-25.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik.data.IdziennikData
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik.firstlogin.IdziennikFirstLogin
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik.login.IdziennikLogin
|
||||
import pl.szczodrzynski.edziennik.api.v2.idziennikLoginMethods
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.api.v2.prepare
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
|
||||
companion object {
|
||||
private const val TAG = "Idziennik"
|
||||
}
|
||||
|
||||
val internalErrorList = mutableListOf<Int>()
|
||||
val data: DataIdziennik
|
||||
|
||||
init {
|
||||
data = DataIdziennik(app, profile, loginStore).apply {
|
||||
callback = wrapCallback(this@Idziennik.callback)
|
||||
satisfyLoginMethods()
|
||||
}
|
||||
}
|
||||
|
||||
private fun completed() {
|
||||
data.saveData()
|
||||
data.notify {
|
||||
callback.onCompleted()
|
||||
}
|
||||
}
|
||||
|
||||
/* _______ _ _ _ _ _
|
||||
|__ __| | /\ | | (_) | | |
|
||||
| | | |__ ___ / \ | | __ _ ___ _ __ _| |_| |__ _ __ ___
|
||||
| | | '_ \ / _ \ / /\ \ | |/ _` |/ _ \| '__| | __| '_ \| '_ ` _ \
|
||||
| | | | | | __/ / ____ \| | (_| | (_) | | | | |_| | | | | | | | |
|
||||
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
||||
__/ |
|
||||
|__*/
|
||||
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
||||
data.arguments = arguments
|
||||
data.prepare(idziennikLoginMethods, IdziennikFeatures, featureIds, viewId)
|
||||
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
|
||||
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
||||
IdziennikLogin(data) {
|
||||
IdziennikData(data) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMessage(message: MessageFull) {
|
||||
|
||||
}
|
||||
|
||||
override fun markAllAnnouncementsAsRead() {
|
||||
|
||||
}
|
||||
|
||||
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
|
||||
|
||||
}
|
||||
|
||||
override fun firstLogin() {
|
||||
IdziennikFirstLogin(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) {
|
||||
when (apiError.errorCode) {
|
||||
in internalErrorList -> {
|
||||
// finish immediately if the same error occurs twice during the same sync
|
||||
callback.onError(apiError)
|
||||
}
|
||||
CODE_INTERNAL_LIBRUS_ACCOUNT_410 -> {
|
||||
internalErrorList.add(apiError.errorCode)
|
||||
loginStore.removeLoginData("refreshToken") // force a clean login
|
||||
//loginLibrus()
|
||||
}
|
||||
else -> callback.onError(apiError)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-10-27.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik.firstlogin
|
||||
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import pl.szczodrzynski.edziennik.api.v2.ERROR_LOGIN_IDZIENNIK_FIRST_NO_SCHOOL_YEAR
|
||||
import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_WEB_SETTINGS
|
||||
import pl.szczodrzynski.edziennik.api.v2.Regexes
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik.DataIdziennik
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik.data.IdziennikWeb
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik.login.IdziennikLoginWeb
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.FirstLoginFinishedEvent
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.fixName
|
||||
import pl.szczodrzynski.edziennik.get
|
||||
import pl.szczodrzynski.edziennik.swapFirstLastName
|
||||
|
||||
class IdziennikFirstLogin(val data: DataIdziennik, val onSuccess: () -> Unit) {
|
||||
companion object {
|
||||
private const val TAG = "IdziennikFirstLogin"
|
||||
}
|
||||
|
||||
private val web = IdziennikWeb(data)
|
||||
private val profileList = mutableListOf<Profile>()
|
||||
|
||||
init {
|
||||
IdziennikLoginWeb(data) {
|
||||
web.webGet(TAG, IDZIENNIK_WEB_SETTINGS) { text ->
|
||||
//val accounts = json.getJsonArray("accounts")
|
||||
|
||||
val isParent = Regexes.IDZIENNIK_LOGIN_FIRST_IS_PARENT.find(text)?.get(1) != "0"
|
||||
val accountNameLong = if (isParent)
|
||||
Regexes.IDZIENNIK_LOGIN_FIRST_ACCOUNT_NAME.find(text)?.get(1)?.swapFirstLastName()?.fixName()
|
||||
else
|
||||
null
|
||||
|
||||
var schoolYearName: String? = null
|
||||
val schoolYear = Regexes.IDZIENNIK_LOGIN_FIRST_SCHOOL_YEAR.find(text)?.let {
|
||||
schoolYearName = it[2]
|
||||
it[1].toIntOrNull()
|
||||
} ?: run {
|
||||
data.error(ApiError(TAG, ERROR_LOGIN_IDZIENNIK_FIRST_NO_SCHOOL_YEAR)
|
||||
.withApiResponse(text))
|
||||
return@webGet
|
||||
}
|
||||
|
||||
Regexes.IDZIENNIK_LOGIN_FIRST_STUDENT.findAll(text)
|
||||
.toMutableList()
|
||||
.reversed()
|
||||
.forEach { match ->
|
||||
val registerId = match[1].toIntOrNull() ?: return@forEach
|
||||
val studentId = match[2]
|
||||
val firstName = match[3]
|
||||
val lastName = match[4]
|
||||
val className = match[5] + " " + match[6]
|
||||
|
||||
val profile = Profile()
|
||||
profile.studentNameLong = "$firstName $lastName".fixName()
|
||||
profile.studentNameShort = "$firstName ${lastName[0]}.".fixName()
|
||||
profile.accountNameLong = accountNameLong
|
||||
profile.studentClassName = className
|
||||
profile.studentSchoolYear = schoolYearName
|
||||
profile.name = profile.studentNameLong
|
||||
profile.subname = data.webUsername
|
||||
profile.empty = true
|
||||
profile.putStudentData("studentId", studentId)
|
||||
profile.putStudentData("registerId", registerId)
|
||||
profile.putStudentData("schoolYearId", schoolYear)
|
||||
profileList.add(profile)
|
||||
}
|
||||
|
||||
EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore))
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-10-23.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.edziennik.librus.data.api
|
||||
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.librus.ENDPOINT_LIBRUS_API_USERS
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.librus.data.LibrusApi
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
|
||||
class LibrusApiUsers(override val data: DataLibrus,
|
||||
val onSuccess: () -> Unit) : LibrusApi(data) {
|
||||
companion object {
|
||||
const val TAG = "LibrusApiUsers"
|
||||
}
|
||||
|
||||
init {
|
||||
apiGet(TAG, "Users") { json ->
|
||||
val users = json.getJsonArray("Users").asJsonObjectList()
|
||||
|
||||
users?.forEach { user ->
|
||||
val id = user.getLong("Id") ?: return@forEach
|
||||
val firstName = user.getString("FirstName")?.fixName() ?: ""
|
||||
val lastName = user.getString("LastName")?.fixName() ?: ""
|
||||
|
||||
data.teacherList.put(id, Teacher(profileId, id, firstName, lastName))
|
||||
}
|
||||
|
||||
data.setSyncNext(ENDPOINT_LIBRUS_API_USERS, 4*DAY)
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.edziennik.librus.firstlogin
|
||||
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.ERROR_NO_STUDENTS_IN_ACCOUNT
|
||||
import pl.szczodrzynski.edziennik.api.v2.FAKE_LIBRUS_ACCOUNTS
|
||||
import pl.szczodrzynski.edziennik.api.v2.LIBRUS_ACCOUNTS_URL
|
||||
import pl.szczodrzynski.edziennik.api.v2.LOGIN_MODE_LIBRUS_EMAIL
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.librus.data.LibrusApi
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.librus.data.LibrusPortal
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.librus.login.LibrusLoginApi
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.librus.login.LibrusLoginPortal
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.FirstLoginFinishedEvent
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.AppError.CODE_LIBRUS_DISCONNECTED
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.AppError.CODE_SYNERGIA_NOT_ACTIVATED
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
|
||||
class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) {
|
||||
companion object {
|
||||
private const val TAG = "LibrusFirstLogin"
|
||||
}
|
||||
|
||||
private val portal = LibrusPortal(data)
|
||||
private val api = LibrusApi(data)
|
||||
private val profileList = mutableListOf<Profile>()
|
||||
|
||||
init {
|
||||
if (data.loginStore.mode == LOGIN_MODE_LIBRUS_EMAIL) {
|
||||
// email login: use Portal for account list
|
||||
LibrusLoginPortal(data) {
|
||||
portal.portalGet(TAG, if (data.fakeLogin) FAKE_LIBRUS_ACCOUNTS else LIBRUS_ACCOUNTS_URL) { json, response ->
|
||||
val accounts = json.getJsonArray("accounts")
|
||||
|
||||
if (accounts == null || accounts.size() < 1) {
|
||||
data.error(ApiError(TAG, ERROR_NO_STUDENTS_IN_ACCOUNT)
|
||||
.withResponse(response)
|
||||
.withApiResponse(json))
|
||||
return@portalGet
|
||||
}
|
||||
val accountDataTime = json.getLong("lastModification")
|
||||
|
||||
for (accountEl in accounts) {
|
||||
val account = accountEl.asJsonObject
|
||||
|
||||
val state = account.getString("state")
|
||||
when (state) {
|
||||
"requiring_an_action" -> CODE_LIBRUS_DISCONNECTED
|
||||
"need-activation" -> CODE_SYNERGIA_NOT_ACTIVATED
|
||||
else -> null
|
||||
}?.let { errorCode ->
|
||||
data.error(ApiError(TAG, errorCode)
|
||||
.withApiResponse(json)
|
||||
.withResponse(response))
|
||||
return@portalGet
|
||||
}
|
||||
|
||||
val id = account.getInt("id") ?: continue
|
||||
val login = account.getString("login") ?: continue
|
||||
val token = account.getString("accessToken") ?: continue
|
||||
val tokenTime = (accountDataTime ?: 0) + DAY
|
||||
val name = account.getString("studentName")?.fixName() ?: ""
|
||||
|
||||
val profile = Profile()
|
||||
profile.studentNameLong = name
|
||||
profile.studentNameShort = name.getShortName()
|
||||
profile.name = profile.studentNameLong
|
||||
profile.subname = data.portalEmail
|
||||
profile.empty = true
|
||||
profile.putStudentData("accountId", id)
|
||||
profile.putStudentData("accountLogin", login)
|
||||
profile.putStudentData("accountToken", token)
|
||||
profile.putStudentData("accountTokenTime", tokenTime)
|
||||
profileList.add(profile)
|
||||
}
|
||||
|
||||
EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore))
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// synergia or JST login: use Api for account info
|
||||
LibrusLoginApi(data) {
|
||||
api.apiGet(TAG, "Me") { json ->
|
||||
|
||||
val profile = Profile()
|
||||
|
||||
val me = json.getJsonObject("Me")
|
||||
val account = me?.getJsonObject("Account")
|
||||
val user = me?.getJsonObject("User")
|
||||
|
||||
profile.putStudentData("isPremium", account?.getBoolean("IsPremium") == true || account?.getBoolean("IsPremiumDemo") == true)
|
||||
|
||||
val isParent = account?.getInt("GroupId") == 5
|
||||
profile.accountNameLong =
|
||||
if (isParent)
|
||||
buildFullName(account?.getString("FirstName"), account?.getString("LastName"))
|
||||
else null
|
||||
|
||||
profile.studentNameLong =
|
||||
buildFullName(user?.getString("FirstName"), user?.getString("LastName"))
|
||||
|
||||
profile.studentNameShort = profile.studentNameLong?.getShortName()
|
||||
profile.name = profile.studentNameLong
|
||||
profile.subname = account.getString("Login")
|
||||
profile.empty = true
|
||||
profile.putStudentData("accountId", account.getInt("Id") ?: 0)
|
||||
profile.putStudentData("accountLogin", profile.subname)
|
||||
profile.putStudentData("accountToken", data.apiAccessToken)
|
||||
profile.putStudentData("accountRefreshToken", data.apiRefreshToken)
|
||||
profile.putStudentData("accountTokenTime", data.apiTokenExpiryTime)
|
||||
profileList.add(profile)
|
||||
|
||||
EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore))
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-10-5.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.edziennik.mobidziennik
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.mobidziennik.data.MobidziennikData
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.mobidziennik.data.web.MobidziennikWebGetAttachment
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.mobidziennik.data.web.MobidziennikWebGetMessage
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.mobidziennik.firstlogin.MobidziennikFirstLogin
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.mobidziennik.login.MobidziennikLogin
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.mobidziennik.login.MobidziennikLoginWeb
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
|
||||
import pl.szczodrzynski.edziennik.api.v2.mobidziennikLoginMethods
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.api.v2.prepare
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
|
||||
companion object {
|
||||
private const val TAG = "Mobidziennik"
|
||||
|
||||
const val API_KEY = "szkolny_eu_72c7dbc8b97f1e5dd2d118cacf51c2b8543d15c0f65b7a59979adb0a1296b235d7febb826dd2a28688def6efe0811b924b04d7f3c7b7d005354e06dc56815d57"
|
||||
}
|
||||
|
||||
val internalErrorList = mutableListOf<Int>()
|
||||
val data: DataMobidziennik
|
||||
|
||||
init {
|
||||
data = DataMobidziennik(app, profile, loginStore).apply {
|
||||
callback = wrapCallback(this@Mobidziennik.callback)
|
||||
satisfyLoginMethods()
|
||||
}
|
||||
}
|
||||
|
||||
private fun completed() {
|
||||
data.saveData()
|
||||
data.notify {
|
||||
callback.onCompleted()
|
||||
}
|
||||
}
|
||||
|
||||
/* _______ _ _ _ _ _
|
||||
|__ __| | /\ | | (_) | | |
|
||||
| | | |__ ___ / \ | | __ _ ___ _ __ _| |_| |__ _ __ ___
|
||||
| | | '_ \ / _ \ / /\ \ | |/ _` |/ _ \| '__| | __| '_ \| '_ ` _ \
|
||||
| | | | | | __/ / ____ \| | (_| | (_) | | | | |_| | | | | | | | |
|
||||
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
||||
__/ |
|
||||
|__*/
|
||||
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
||||
data.arguments = arguments
|
||||
data.prepare(mobidziennikLoginMethods, MobidziennikFeatures, featureIds, viewId)
|
||||
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
|
||||
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
||||
MobidziennikLogin(data) {
|
||||
MobidziennikData(data) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMessage(message: MessageFull) {
|
||||
MobidziennikLoginWeb(data) {
|
||||
MobidziennikWebGetMessage(data, message) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun markAllAnnouncementsAsRead() {
|
||||
|
||||
}
|
||||
|
||||
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
|
||||
MobidziennikLoginWeb(data) {
|
||||
MobidziennikWebGetAttachment(data, message, attachmentId, attachmentName) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun firstLogin() {
|
||||
MobidziennikFirstLogin(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) {
|
||||
when (apiError.errorCode) {
|
||||
in internalErrorList -> {
|
||||
// finish immediately if the same error occurs twice during the same sync
|
||||
callback.onError(apiError)
|
||||
}
|
||||
CODE_INTERNAL_LIBRUS_ACCOUNT_410 -> {
|
||||
internalErrorList.add(apiError.errorCode)
|
||||
loginStore.removeLoginData("refreshToken") // force a clean login
|
||||
//loginLibrus()
|
||||
}
|
||||
else -> callback.onError(apiError)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,201 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-10-11.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.edziennik.mobidziennik.data.api
|
||||
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.mobidziennik.DataMobidziennik
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
|
||||
import pl.szczodrzynski.edziennik.fixName
|
||||
import pl.szczodrzynski.edziennik.singleOrNull
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
|
||||
init { data.profile?.also { profile ->
|
||||
val lessons = rows.filterNot { it.isEmpty() }.map { it.split("|") }
|
||||
|
||||
val dataStart = Date.getToday()
|
||||
val dataEnd = dataStart.clone().stepForward(0, 0, 7 + (6 - dataStart.weekDay))
|
||||
|
||||
data.toRemove.add(DataRemoveModel.Timetable.between(dataStart.clone(), dataEnd))
|
||||
|
||||
val dataDays = mutableListOf<Int>()
|
||||
while (dataStart <= dataEnd) {
|
||||
dataDays += dataStart.value
|
||||
dataStart.stepForward(0, 0, 1)
|
||||
}
|
||||
|
||||
for (lesson in lessons) {
|
||||
val date = Date.fromYmd(lesson[2])
|
||||
val startTime = Time.fromYmdHm(lesson[3])
|
||||
val endTime = Time.fromYmdHm(lesson[4])
|
||||
|
||||
dataDays.remove(date.value)
|
||||
|
||||
val subjectId = data.subjectList.singleOrNull { it.longName == lesson[5] }?.id ?: -1
|
||||
val teacherId = data.teacherList.singleOrNull { it.fullNameLastFirst == (lesson[7]+" "+lesson[6]).fixName() }?.id ?: -1
|
||||
val teamId = data.teamList.singleOrNull { it.name == lesson[8]+lesson[9] }?.id ?: -1
|
||||
val classroom = lesson[11]
|
||||
|
||||
Lesson(data.profileId, -1).also {
|
||||
when (lesson[1]) {
|
||||
"plan_lekcji", "lekcja" -> {
|
||||
it.type = Lesson.TYPE_NORMAL
|
||||
it.date = date
|
||||
it.startTime = startTime
|
||||
it.endTime = endTime
|
||||
it.subjectId = subjectId
|
||||
it.teacherId = teacherId
|
||||
it.teamId = teamId
|
||||
it.classroom = classroom
|
||||
}
|
||||
"lekcja_odwolana" -> {
|
||||
it.type = Lesson.TYPE_CANCELLED
|
||||
it.date = date
|
||||
it.startTime = startTime
|
||||
it.endTime = endTime
|
||||
it.oldSubjectId = subjectId
|
||||
//it.oldTeacherId = teacherId
|
||||
it.oldTeamId = teamId
|
||||
//it.oldClassroom = classroom
|
||||
}
|
||||
"zastepstwo" -> {
|
||||
it.type = Lesson.TYPE_CHANGE
|
||||
it.date = date
|
||||
it.startTime = startTime
|
||||
it.endTime = endTime
|
||||
it.subjectId = subjectId
|
||||
it.teacherId = teacherId
|
||||
it.teamId = teamId
|
||||
it.classroom = classroom
|
||||
}
|
||||
}
|
||||
|
||||
it.id = it.buildId()
|
||||
|
||||
val seen = profile.empty || date < Date.getToday()
|
||||
|
||||
if (it.type != Lesson.TYPE_NORMAL) {
|
||||
data.metadataList.add(
|
||||
Metadata(
|
||||
data.profileId,
|
||||
Metadata.TYPE_LESSON_CHANGE,
|
||||
it.id,
|
||||
seen,
|
||||
seen,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
data.lessonNewList += it
|
||||
}
|
||||
}
|
||||
|
||||
for (day in dataDays) {
|
||||
val lessonDate = Date.fromValue(day)
|
||||
data.lessonNewList += Lesson(data.profileId, lessonDate.value.toLong()).apply {
|
||||
type = Lesson.TYPE_NO_LESSONS
|
||||
date = lessonDate
|
||||
}
|
||||
}
|
||||
|
||||
/*for (lessonStr in rows) {
|
||||
if (lessonStr.isNotEmpty()) {
|
||||
val lesson = lessonStr.split("|")
|
||||
|
||||
if (lesson[0].toInt() != data.studentId)
|
||||
continue
|
||||
|
||||
if (lesson[1] == "plan_lekcji" || lesson[1] == "lekcja") {
|
||||
val lessonObject = Lesson(data.profileId, lesson[2], lesson[3], lesson[4])
|
||||
|
||||
data.subjectList.singleOrNull { it.longName == lesson[5] }?.let {
|
||||
lessonObject.subjectId = it.id
|
||||
}
|
||||
data.teacherList.singleOrNull { it.fullNameLastFirst == (lesson[7]+" "+lesson[6]).fixName() }?.let {
|
||||
lessonObject.teacherId = it.id
|
||||
}
|
||||
data.teamList.singleOrNull { it.name == lesson[8]+lesson[9] }?.let {
|
||||
lessonObject.teamId = it.id
|
||||
}
|
||||
lessonObject.classroomName = lesson[11]
|
||||
data.lessonList.add(lessonObject)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// searching for all changes
|
||||
for (lessonStr in rows) {
|
||||
if (lessonStr.isNotEmpty()) {
|
||||
val lesson = lessonStr.split("|")
|
||||
|
||||
if (lesson[0].toInt() != data.studentId)
|
||||
continue
|
||||
|
||||
if (lesson[1] == "zastepstwo" || lesson[1] == "lekcja_odwolana") {
|
||||
val lessonChange = LessonChange(data.profileId, lesson[2], lesson[3], lesson[4])
|
||||
|
||||
data.subjectList.singleOrNull { it.longName == lesson[5] }?.let {
|
||||
lessonChange.subjectId = it.id
|
||||
}
|
||||
data.teacherList.singleOrNull { it.fullNameLastFirst == (lesson[7]+" "+lesson[6]).fixName() }?.let {
|
||||
lessonChange.teacherId = it.id
|
||||
}
|
||||
data.teamList.singleOrNull { it.name == lesson[8]+lesson[9] }?.let {
|
||||
lessonChange.teamId = it.id
|
||||
}
|
||||
|
||||
if (lesson[1] == "zastepstwo") {
|
||||
lessonChange.type = LessonChange.TYPE_CHANGE
|
||||
}
|
||||
else if (lesson[1] == "lekcja_odwolana") {
|
||||
lessonChange.type = LessonChange.TYPE_CANCELLED
|
||||
}
|
||||
else if (lesson[1] == "lekcja") {
|
||||
lessonChange.type = LessonChange.TYPE_ADDED
|
||||
}
|
||||
lessonChange.classroomName = lesson[11]
|
||||
|
||||
val originalLesson = lessonChange.getOriginalLesson(data.lessonList)
|
||||
|
||||
if (lessonChange.type == LessonChange.TYPE_ADDED) {
|
||||
if (originalLesson == null) {
|
||||
// original lesson doesn't exist, save a new addition
|
||||
// TODO
|
||||
*//*if (!RegisterLessonChange.existsAddition(app.profile, registerLessonChange)) {
|
||||
app.profile.timetable.addLessonAddition(registerLessonChange);
|
||||
}*//*
|
||||
} else {
|
||||
// original lesson exists, so we need to compare them
|
||||
if (!lessonChange.matches(originalLesson)) {
|
||||
// the lessons are different, so it's probably a lesson change
|
||||
// ahhh this damn API
|
||||
lessonChange.type = LessonChange.TYPE_CHANGE
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (lessonChange.type != LessonChange.TYPE_ADDED) {
|
||||
// it's not a lesson addition
|
||||
data.lessonChangeList.add(lessonChange)
|
||||
data.metadataList.add(
|
||||
Metadata(
|
||||
data.profileId,
|
||||
Metadata.TYPE_LESSON_CHANGE,
|
||||
lessonChange.id,
|
||||
data.profile?.empty ?: false,
|
||||
data.profile?.empty ?: false,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
if (originalLesson == null) {
|
||||
// there is no original lesson, so we have to add one in order to change it
|
||||
data.lessonList.add(Lesson.fromLessonChange(lessonChange))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.edziennik.mobidziennik.firstlogin
|
||||
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.mobidziennik.DataMobidziennik
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.mobidziennik.data.MobidziennikWeb
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.mobidziennik.login.MobidziennikLoginWeb
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.FirstLoginFinishedEvent
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.fixName
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class MobidziennikFirstLogin(val data: DataMobidziennik, val onSuccess: () -> Unit) {
|
||||
companion object {
|
||||
private const val TAG = "MobidziennikFirstLogin"
|
||||
}
|
||||
|
||||
private val web = MobidziennikWeb(data)
|
||||
private val profileList = mutableListOf<Profile>()
|
||||
|
||||
init {
|
||||
MobidziennikLoginWeb(data) {
|
||||
web.webGet(TAG, "/api/zrzutbazy") { text ->
|
||||
val tables = text.split("T@B#LA")
|
||||
|
||||
val accountNameLong = run {
|
||||
tables[0]
|
||||
.split("\n")
|
||||
.map { it.split("|") }
|
||||
.singleOrNull { it.getOrNull(1) != "*" }
|
||||
?.let {
|
||||
"${it[4]} ${it[5]}".fixName()
|
||||
}
|
||||
}
|
||||
|
||||
tables[8].split("\n").forEach { student ->
|
||||
if (student.isEmpty())
|
||||
return@forEach
|
||||
val student1 = student.split("|")
|
||||
if (student1.size == 2)
|
||||
return@forEach
|
||||
|
||||
val today = Date.getToday()
|
||||
val profile = Profile()
|
||||
profile.studentNameLong = "${student1[2]} ${student1[4]}".fixName()
|
||||
profile.studentNameShort = "${student1[2]} ${student1[4][0]}.".fixName()
|
||||
profile.accountNameLong = if (accountNameLong == profile.studentNameLong) null else accountNameLong
|
||||
profile.studentSchoolYear = "${today.year}/${today.year+1}"
|
||||
profile.name = profile.studentNameLong
|
||||
profile.subname = data.loginUsername
|
||||
profile.empty = true
|
||||
profile.putStudentData("studentId", student1[0].toInt())
|
||||
profileList.add(profile)
|
||||
}
|
||||
|
||||
EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore))
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-10-6.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.edziennik.vulcan
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.vulcan.data.VulcanData
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.vulcan.data.api.VulcanApiMessagesChangeStatus
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.vulcan.firstlogin.VulcanFirstLogin
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.vulcan.login.VulcanLogin
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.vulcan.login.VulcanLoginApi
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.api.v2.prepare
|
||||
import pl.szczodrzynski.edziennik.api.v2.vulcanLoginMethods
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
|
||||
companion object {
|
||||
private const val TAG = "Vulcan"
|
||||
}
|
||||
|
||||
val internalErrorList = mutableListOf<Int>()
|
||||
val data: DataVulcan
|
||||
|
||||
init {
|
||||
data = DataVulcan(app, profile, loginStore).apply {
|
||||
callback = wrapCallback(this@Vulcan.callback)
|
||||
satisfyLoginMethods()
|
||||
}
|
||||
}
|
||||
|
||||
private fun completed() {
|
||||
data.saveData()
|
||||
data.notify {
|
||||
callback.onCompleted()
|
||||
}
|
||||
}
|
||||
|
||||
/* _______ _ _ _ _ _
|
||||
|__ __| | /\ | | (_) | | |
|
||||
| | | |__ ___ / \ | | __ _ ___ _ __ _| |_| |__ _ __ ___
|
||||
| | | '_ \ / _ \ / /\ \ | |/ _` |/ _ \| '__| | __| '_ \| '_ ` _ \
|
||||
| | | | | | __/ / ____ \| | (_| | (_) | | | | |_| | | | | | | | |
|
||||
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
||||
__/ |
|
||||
|__*/
|
||||
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
||||
data.arguments = arguments
|
||||
data.prepare(vulcanLoginMethods, VulcanFeatures, featureIds, viewId)
|
||||
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
|
||||
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
|
||||
VulcanLogin(data) {
|
||||
VulcanData(data) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMessage(message: MessageFull) {
|
||||
VulcanLoginApi(data) {
|
||||
VulcanApiMessagesChangeStatus(data, message) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun markAllAnnouncementsAsRead() {
|
||||
|
||||
}
|
||||
|
||||
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
|
||||
|
||||
}
|
||||
|
||||
override fun firstLogin() {
|
||||
VulcanFirstLogin(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) {
|
||||
when (apiError.errorCode) {
|
||||
in internalErrorList -> {
|
||||
// finish immediately if the same error occurs twice during the same sync
|
||||
callback.onError(apiError)
|
||||
}
|
||||
CODE_INTERNAL_LIBRUS_ACCOUNT_410 -> {
|
||||
internalErrorList.add(apiError.errorCode)
|
||||
loginStore.removeLoginData("refreshToken") // force a clean login
|
||||
//loginLibrus()
|
||||
}
|
||||
else -> callback.onError(apiError)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2019-11-12
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.edziennik.vulcan.data.api
|
||||
|
||||
import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_MESSAGES_CHANGE_STATUS
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.vulcan.DataVulcan
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.vulcan.data.VulcanApi
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_SENT
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
|
||||
class VulcanApiMessagesChangeStatus(
|
||||
override val data: DataVulcan,
|
||||
private val messageObject: MessageFull,
|
||||
val onSuccess: () -> Unit
|
||||
) : VulcanApi(data) {
|
||||
companion object {
|
||||
const val TAG = "VulcanApiMessagesChangeStatus"
|
||||
}
|
||||
|
||||
init {
|
||||
data.profile?.also { profile ->
|
||||
apiGet(TAG, VULCAN_API_ENDPOINT_MESSAGES_CHANGE_STATUS, parameters = mapOf(
|
||||
"WiadomoscId" to messageObject.id,
|
||||
"FolderWiadomosci" to "Odebrane",
|
||||
"Status" to "Widoczna",
|
||||
"LoginId" to data.studentLoginId,
|
||||
"IdUczen" to data.studentId
|
||||
)) { _, _ ->
|
||||
|
||||
if (!messageObject.seen) {
|
||||
data.messageMetadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_MESSAGE,
|
||||
messageObject.id,
|
||||
true,
|
||||
true,
|
||||
messageObject.addedDate
|
||||
))
|
||||
}
|
||||
|
||||
if (messageObject.type != TYPE_SENT) {
|
||||
val messageRecipientObject = MessageRecipient(
|
||||
profileId,
|
||||
-1,
|
||||
-1,
|
||||
System.currentTimeMillis(),
|
||||
messageObject.id
|
||||
)
|
||||
|
||||
data.messageRecipientList.add(messageRecipientObject)
|
||||
}
|
||||
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2019-10-19
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.edziennik.vulcan.firstlogin
|
||||
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.ERROR_NO_STUDENTS_IN_ACCOUNT
|
||||
import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_STUDENT_LIST
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.vulcan.DataVulcan
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.vulcan.data.VulcanApi
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.vulcan.login.VulcanLoginApi
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.FirstLoginFinishedEvent
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class VulcanFirstLogin(val data: DataVulcan, val onSuccess: () -> Unit) {
|
||||
companion object {
|
||||
const val TAG = "VulcanFirstLogin"
|
||||
}
|
||||
|
||||
private val api = VulcanApi(data)
|
||||
private val profileList = mutableListOf<Profile>()
|
||||
|
||||
init {
|
||||
VulcanLoginApi(data) {
|
||||
api.apiGet(TAG, VULCAN_API_ENDPOINT_STUDENT_LIST, baseUrl = true) { json, response ->
|
||||
val students = json.getJsonArray("Data")
|
||||
|
||||
if (students == null || students.size() < 1) {
|
||||
data.error(ApiError(TAG, ERROR_NO_STUDENTS_IN_ACCOUNT)
|
||||
.withResponse(response)
|
||||
.withApiResponse(json))
|
||||
return@apiGet
|
||||
}
|
||||
|
||||
students.forEach { studentEl ->
|
||||
val student = studentEl.asJsonObject
|
||||
|
||||
val schoolSymbol = student.getString("JednostkaSprawozdawczaSymbol") ?: return@forEach
|
||||
val schoolName = "${data.symbol}_$schoolSymbol"
|
||||
val studentId = student.getInt("Id") ?: return@forEach
|
||||
val studentLoginId = student.getInt("UzytkownikLoginId") ?: return@forEach
|
||||
val studentClassId = student.getInt("IdOddzial") ?: return@forEach
|
||||
val studentClassName = student.getString("OkresPoziom").toString() + (student.getString("OddzialSymbol") ?: return@forEach)
|
||||
val studentSemesterId = student.getInt("IdOkresKlasyfikacyjny") ?: return@forEach
|
||||
val studentFirstName = student.getString("Imie") ?: ""
|
||||
val studentLastName = student.getString("Nazwisko") ?: ""
|
||||
val studentNameLong = "$studentFirstName $studentLastName".fixName()
|
||||
val studentNameShort = "$studentFirstName ${studentLastName[0]}.".fixName()
|
||||
|
||||
val userLogin = student.getString("UzytkownikLogin") ?: ""
|
||||
val currentSemesterStartDate = student.getLong("OkresDataOd") ?: return@forEach
|
||||
val currentSemesterEndDate = (student.getLong("OkresDataDo")
|
||||
?: return@forEach) + 86400
|
||||
val studentSemesterNumber = student.getInt("OkresNumer") ?: return@forEach
|
||||
|
||||
val newProfile = Profile()
|
||||
newProfile.empty = true
|
||||
|
||||
val isParent = student.getString("UzytkownikRola") == "opiekun"
|
||||
val userName = if (isParent)
|
||||
student.getString("UzytkownikNazwa")?.swapFirstLastName()?.fixName()
|
||||
else
|
||||
null
|
||||
newProfile.accountNameLong = userName
|
||||
newProfile.studentClassName = studentClassName
|
||||
val today = Date.getToday()
|
||||
newProfile.studentSchoolYear = "${today.year}/${today.year+1}"
|
||||
|
||||
newProfile.putStudentData("studentId", studentId)
|
||||
newProfile.putStudentData("studentLoginId", studentLoginId)
|
||||
newProfile.putStudentData("studentClassId", studentClassId)
|
||||
newProfile.putStudentData("studentSemesterId", studentSemesterId)
|
||||
newProfile.putStudentData("schoolSymbol", schoolSymbol)
|
||||
newProfile.putStudentData("schoolName", schoolName)
|
||||
newProfile.putStudentData("currentSemesterEndDate", currentSemesterEndDate)
|
||||
newProfile.putStudentData("studentSemesterNumber", studentSemesterNumber)
|
||||
|
||||
when (studentSemesterNumber) {
|
||||
1 -> {
|
||||
newProfile.dateSemester1Start = Date.fromMillis(currentSemesterStartDate * 1000)
|
||||
newProfile.dateSemester2Start = Date.fromMillis(currentSemesterEndDate * 1000)
|
||||
}
|
||||
2 -> {
|
||||
newProfile.dateSemester2Start = Date.fromMillis(currentSemesterStartDate * 1000)
|
||||
newProfile.dateYearEnd = Date.fromMillis(currentSemesterEndDate * 1000)
|
||||
}
|
||||
}
|
||||
|
||||
newProfile.studentNameLong = studentNameLong
|
||||
newProfile.studentNameShort = studentNameShort
|
||||
newProfile.name = studentNameLong
|
||||
newProfile.subname = userLogin
|
||||
|
||||
profileList.add(newProfile)
|
||||
}
|
||||
|
||||
EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore))
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-9-28.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.events
|
||||
|
||||
class ApiTaskAllFinishedEvent
|
@ -1,9 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-9-28.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.events
|
||||
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
|
||||
class ApiTaskErrorEvent(val error: ApiError)
|
@ -1,7 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-9-28.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.events
|
||||
|
||||
class ApiTaskFinishedEvent(val profileId: Int)
|
@ -1,9 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-9-28.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.events
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
|
||||
class ApiTaskStartedEvent(val profileId: Int, val profile: Profile? = null)
|
@ -1,6 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.events
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
|
||||
data class FirstLoginFinishedEvent(val profileList: List<Profile>, val loginStore: LoginStore)
|
@ -1,9 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-11-12.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.events
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
|
||||
data class MessageGetEvent(val message: MessageFull)
|
@ -1,7 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-10-1.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.events.requests
|
||||
|
||||
class ServiceCloseRequest
|
@ -1,7 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-10-1.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.events.requests
|
||||
|
||||
class TaskCancelRequest(val taskId: Int)
|
@ -1,18 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-9-29.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.interfaces
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
|
||||
interface EdziennikInterface {
|
||||
fun sync(featureIds: List<Int>, viewId: Int? = null, arguments: JsonObject? = null)
|
||||
fun getMessage(message: MessageFull)
|
||||
fun markAllAnnouncementsAsRead()
|
||||
fun getAttachment(message: Message, attachmentId: Long, attachmentName: String)
|
||||
fun firstLogin()
|
||||
fun cancel()
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-9-28.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.models
|
||||
|
||||
import android.content.Context
|
||||
import com.google.gson.JsonObject
|
||||
import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
|
||||
class ApiError(val tag: String, val errorCode: Int) {
|
||||
var profileId: Int? = null
|
||||
var throwable: Throwable? = null
|
||||
var apiResponse: String? = null
|
||||
var request: Request? = null
|
||||
var response: Response? = null
|
||||
var isCritical = true
|
||||
|
||||
fun withThrowable(throwable: Throwable?): ApiError {
|
||||
this.throwable = throwable
|
||||
return this
|
||||
}
|
||||
fun withApiResponse(apiResponse: String?): ApiError {
|
||||
this.apiResponse = apiResponse
|
||||
return this
|
||||
}
|
||||
fun withApiResponse(apiResponse: JsonObject?): ApiError {
|
||||
this.apiResponse = apiResponse?.toString()
|
||||
return this
|
||||
}
|
||||
fun withRequest(request: Request?): ApiError {
|
||||
this.request = request
|
||||
return this
|
||||
}
|
||||
fun withResponse(response: Response?): ApiError {
|
||||
this.response = response
|
||||
this.request = response?.request()
|
||||
return this
|
||||
}
|
||||
|
||||
fun setCritical(isCritical: Boolean): ApiError {
|
||||
this.isCritical = isCritical
|
||||
return this
|
||||
}
|
||||
|
||||
fun toAppError(): AppError {
|
||||
return AppError(
|
||||
tag,
|
||||
-1,
|
||||
errorCode, response, throwable, apiResponse
|
||||
)
|
||||
}
|
||||
|
||||
fun getStringReason(context: Context): String {
|
||||
return context.resources.getIdentifier("error_${errorCode}_reason", "string", context.packageName).let {
|
||||
if (it != 0)
|
||||
context.getString(it)
|
||||
else
|
||||
"?"
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "ApiError(tag='$tag', errorCode=$errorCode, profileId=$profileId, throwable=$throwable, apiResponse=$apiResponse, request=$request, response=$response, isCritical=$isCritical)"
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,324 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2019-11-26
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.models;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
import im.wangchao.mhttp.Request;
|
||||
import im.wangchao.mhttp.Response;
|
||||
import pl.szczodrzynski.edziennik.R;
|
||||
|
||||
|
||||
public class AppError {
|
||||
public static final int CODE_OTHER = 0;
|
||||
public static final int CODE_OK = 1;
|
||||
public static final int CODE_NO_INTERNET = 10;
|
||||
public static final int CODE_SSL_ERROR = 13;
|
||||
public static final int CODE_ARCHIVED = 5;
|
||||
public static final int CODE_MAINTENANCE = 6;
|
||||
public static final int CODE_LOGIN_ERROR = 7;
|
||||
public static final int CODE_ACCOUNT_MISMATCH = 8;
|
||||
public static final int CODE_APP_SERVER_ERROR = 9;
|
||||
public static final int CODE_MULTIACCOUNT_SETUP = 12;
|
||||
public static final int CODE_TIMEOUT = 11;
|
||||
public static final int CODE_PROFILE_NOT_FOUND = 14;
|
||||
public static final int CODE_ATTACHMENT_NOT_AVAILABLE = 28;
|
||||
// user's fault
|
||||
public static final int CODE_INVALID_LOGIN = 2;
|
||||
public static final int CODE_INVALID_SERVER_ADDRESS = 21;
|
||||
public static final int CODE_INVALID_SCHOOL_NAME = 22;
|
||||
public static final int CODE_INVALID_DEVICE = 23;
|
||||
public static final int CODE_OLD_PASSWORD = 4;
|
||||
public static final int CODE_INVALID_TOKEN = 24;
|
||||
public static final int CODE_EXPIRED_TOKEN = 27;
|
||||
public static final int CODE_INVALID_SYMBOL = 25;
|
||||
public static final int CODE_INVALID_PIN = 26;
|
||||
public static final int CODE_LIBRUS_NOT_ACTIVATED = 29;
|
||||
public static final int CODE_SYNERGIA_NOT_ACTIVATED = 32;
|
||||
public static final int CODE_LIBRUS_DISCONNECTED = 31;
|
||||
public static final int CODE_PROFILE_ARCHIVED = 30;
|
||||
|
||||
|
||||
public static final int CODE_INTERNAL_MISSING_DATA = 100;
|
||||
// internal errors - not for user's information.
|
||||
// these error codes are processed in API main classes
|
||||
|
||||
public String TAG;
|
||||
public int line;
|
||||
public int errorCode;
|
||||
public String errorText;
|
||||
public Response response;
|
||||
public Request request;
|
||||
public Throwable throwable;
|
||||
public String apiResponse;
|
||||
|
||||
public AppError(String TAG, int line, int errorCode, String errorText, Response response, Request request, Throwable throwable, String apiResponse) {
|
||||
this.TAG = TAG;
|
||||
this.line = line;
|
||||
this.errorCode = errorCode;
|
||||
this.errorText = errorText;
|
||||
this.response = response;
|
||||
this.request = request;
|
||||
this.throwable = throwable;
|
||||
this.apiResponse = apiResponse;
|
||||
}
|
||||
|
||||
public AppError(String TAG, int line, int errorCode) {
|
||||
this(TAG, line, errorCode, null, null, null, null, null);
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, Response response, Throwable throwable) {
|
||||
this(TAG, line, errorCode, null, response, response == null ? null : response.request(), throwable, null);
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, Response response) {
|
||||
this(TAG, line, errorCode, null, response, response == null ? null : response.request(), null, null);
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, Throwable throwable, String apiResponse) {
|
||||
this(TAG, line, errorCode, null, null, null, throwable, apiResponse);
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, Throwable throwable, JsonObject apiResponse) {
|
||||
this(TAG, line, errorCode, null, null, null, throwable, apiResponse == null ? null : apiResponse.toString());
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, String errorText, Response response, JsonObject apiResponse) {
|
||||
this(TAG, line, errorCode, errorText, response, response == null ? null : response.request(), null, apiResponse == null ? null : apiResponse.toString());
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, String errorText, Response response, String apiResponse) {
|
||||
this(TAG, line, errorCode, errorText, response, response == null ? null : response.request(), null, apiResponse);
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, String errorText, String apiResponse) {
|
||||
this(TAG, line, errorCode, errorText, null, null, null, apiResponse);
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, String errorText, JsonObject apiResponse) {
|
||||
this(TAG, line, errorCode, errorText, null, null, null, apiResponse == null ? null : apiResponse.toString());
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, String errorText) {
|
||||
this(TAG, line, errorCode, errorText, null, null, null, null);
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, JsonObject apiResponse) {
|
||||
this(TAG, line, errorCode, null, null, null, null, apiResponse.toString());
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, Response response, Throwable throwable, JsonObject apiResponse) {
|
||||
this(TAG, line, errorCode, null, response, response == null ? null : response.request(), throwable, apiResponse == null ? null : apiResponse.toString());
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, Response response, Throwable throwable, String apiResponse) {
|
||||
this(TAG, line, errorCode, null, response, response == null ? null : response.request(), throwable, apiResponse);
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, Response response, String apiResponse) {
|
||||
this(TAG, line, errorCode, null, response, response == null ? null : response.request(), null, apiResponse);
|
||||
}
|
||||
public AppError(String TAG, int line, int errorCode, Response response, JsonObject apiResponse) {
|
||||
this(TAG, line, errorCode, null, response, response == null ? null : response.request(), null, apiResponse == null ? null : apiResponse.toString());
|
||||
}
|
||||
|
||||
public String getDetails(Context context) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(stringErrorCode(context, errorCode, errorText)).append("\n");
|
||||
sb.append("(").append(stringErrorType(errorCode)).append("#").append(errorCode).append(")\n");
|
||||
sb.append("at ").append(TAG).append(":").append(line).append("\n");
|
||||
sb.append("\n");
|
||||
if (throwable == null)
|
||||
sb.append("Throwable is null");
|
||||
else
|
||||
sb.append(Log.getStackTraceString(throwable));
|
||||
sb.append("\n");
|
||||
sb.append(Build.MANUFACTURER).append(" ").append(Build.BRAND).append(" ").append(Build.MODEL).append(" ").append(Build.DEVICE).append("\n");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public interface GetApiResponseCallback {
|
||||
void onSuccess(String apiResponse);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param context a Context
|
||||
* @param apiResponseCallback a callback executed on a worker thread
|
||||
*/
|
||||
public void getApiResponse(Context context, GetApiResponseCallback apiResponseCallback) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Request:\n");
|
||||
if (request != null) {
|
||||
sb.append(request.method()).append(" ").append(request.url().toString()).append("\n");
|
||||
sb.append(request.headers().toString()).append("\n");
|
||||
sb.append("\n");
|
||||
sb.append(request.bodyToString()).append("\n\n");
|
||||
}
|
||||
else
|
||||
sb.append("null\n\n");
|
||||
|
||||
if (apiResponse == null && response != null)
|
||||
apiResponse = response.parserErrorBody;
|
||||
|
||||
sb.append("Response:\n");
|
||||
if (response != null) {
|
||||
sb.append(response.code()).append(" ").append(response.message()).append("\n");
|
||||
sb.append(response.headers().toString()).append("\n");
|
||||
sb.append("\n");
|
||||
if (apiResponse == null) {
|
||||
if (Thread.currentThread().getName().equals("main")) {
|
||||
AsyncTask.execute(() -> {
|
||||
if (response.raw().body() != null) {
|
||||
try {
|
||||
sb.append(response.raw().body().string());
|
||||
} catch (Exception e) {
|
||||
sb.append("Exception while getting response body:\n").append(Log.getStackTraceString(e));
|
||||
}
|
||||
}
|
||||
else {
|
||||
sb.append("null");
|
||||
}
|
||||
apiResponseCallback.onSuccess(sb.toString());
|
||||
});
|
||||
}
|
||||
else {
|
||||
if (response.raw().body() != null) {
|
||||
try {
|
||||
sb.append(response.raw().body().string());
|
||||
} catch (Exception e) {
|
||||
sb.append("Exception while getting response body:\n").append(Log.getStackTraceString(e));
|
||||
}
|
||||
}
|
||||
else {
|
||||
sb.append("null");
|
||||
}
|
||||
apiResponseCallback.onSuccess(sb.toString());
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
sb.append("null\n\n");
|
||||
|
||||
sb.append("API Response:\n");
|
||||
if (apiResponse != null) {
|
||||
sb.append(apiResponse).append("\n\n");
|
||||
}
|
||||
else {
|
||||
sb.append("null\n\n");
|
||||
}
|
||||
|
||||
apiResponseCallback.onSuccess(sb.toString());
|
||||
}
|
||||
|
||||
public AppError changeIfCodeOther() {
|
||||
if (errorCode != CODE_OTHER && errorCode != CODE_MAINTENANCE)
|
||||
return this;
|
||||
if (throwable instanceof UnknownHostException)
|
||||
errorCode = CODE_NO_INTERNET;
|
||||
else if (throwable instanceof SSLException)
|
||||
errorCode = CODE_SSL_ERROR;
|
||||
else if (throwable instanceof SocketTimeoutException)
|
||||
errorCode = CODE_TIMEOUT;
|
||||
else if (throwable instanceof InterruptedIOException)
|
||||
errorCode = CODE_NO_INTERNET;
|
||||
else if (response != null &&
|
||||
(response.code() == 424
|
||||
|| response.code() == 400
|
||||
|| response.code() == 401
|
||||
|| response.code() == 500
|
||||
|| response.code() == 503
|
||||
|| response.code() == 404))
|
||||
errorCode = CODE_MAINTENANCE;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String asReadableString(Context context) {
|
||||
return stringErrorCode(context, errorCode, errorText) + (errorCode == CODE_MAINTENANCE && errorText != null && !errorText.isEmpty() ? " ("+errorText+")" : "");
|
||||
}
|
||||
|
||||
public static String stringErrorCode(Context context, int errorCode, String errorText)
|
||||
{
|
||||
switch (errorCode) {
|
||||
case CODE_OK:
|
||||
return context.getString(R.string.sync_error_ok);
|
||||
case CODE_INVALID_LOGIN:
|
||||
return context.getString(R.string.sync_error_invalid_login);
|
||||
case CODE_LOGIN_ERROR:
|
||||
return context.getString(R.string.sync_error_login_error);
|
||||
case CODE_INVALID_DEVICE:
|
||||
return context.getString(R.string.sync_error_invalid_device);
|
||||
case CODE_OLD_PASSWORD:
|
||||
return context.getString(R.string.sync_error_old_password);
|
||||
case CODE_ARCHIVED:
|
||||
return context.getString(R.string.sync_error_archived);
|
||||
case CODE_MAINTENANCE:
|
||||
return context.getString(R.string.sync_error_maintenance);
|
||||
case CODE_NO_INTERNET:
|
||||
return context.getString(R.string.sync_error_no_internet);
|
||||
case CODE_ACCOUNT_MISMATCH:
|
||||
return context.getString(R.string.sync_error_account_mismatch);
|
||||
case CODE_APP_SERVER_ERROR:
|
||||
return context.getString(R.string.sync_error_app_server);
|
||||
case CODE_TIMEOUT:
|
||||
return context.getString(R.string.sync_error_timeout);
|
||||
case CODE_SSL_ERROR:
|
||||
return context.getString(R.string.sync_error_ssl);
|
||||
case CODE_INVALID_SERVER_ADDRESS:
|
||||
return context.getString(R.string.sync_error_invalid_server_address);
|
||||
case CODE_INVALID_SCHOOL_NAME:
|
||||
return context.getString(R.string.sync_error_invalid_school_name);
|
||||
case CODE_PROFILE_NOT_FOUND:
|
||||
return context.getString(R.string.sync_error_profile_not_found);
|
||||
case CODE_INVALID_TOKEN:
|
||||
return context.getString(R.string.sync_error_invalid_token);
|
||||
case CODE_ATTACHMENT_NOT_AVAILABLE:
|
||||
return context.getString(R.string.sync_error_attachment_not_available);
|
||||
case CODE_LIBRUS_NOT_ACTIVATED:
|
||||
return context.getString(R.string.sync_error_librus_not_activated);
|
||||
case CODE_PROFILE_ARCHIVED:
|
||||
return context.getString(R.string.sync_error_profile_archived);
|
||||
case CODE_LIBRUS_DISCONNECTED:
|
||||
return context.getString(R.string.sync_error_librus_disconnected);
|
||||
case CODE_SYNERGIA_NOT_ACTIVATED:
|
||||
return context.getString(R.string.sync_error_synergia_not_activated);
|
||||
default:
|
||||
case CODE_MULTIACCOUNT_SETUP:
|
||||
case CODE_OTHER:
|
||||
return errorText != null ? errorText : context.getString(R.string.sync_error_unknown);
|
||||
}
|
||||
}
|
||||
public static String stringErrorType(int errorCode)
|
||||
{
|
||||
switch (errorCode) {
|
||||
default:
|
||||
case CODE_OTHER: return "CODE_OTHER";
|
||||
case CODE_OK: return "CODE_OK";
|
||||
case CODE_NO_INTERNET: return "CODE_NO_INTERNET";
|
||||
case CODE_SSL_ERROR: return "CODE_SSL_ERROR";
|
||||
case CODE_ARCHIVED: return "CODE_ARCHIVED";
|
||||
case CODE_MAINTENANCE: return "CODE_MAINTENANCE";
|
||||
case CODE_LOGIN_ERROR: return "CODE_LOGIN_ERROR";
|
||||
case CODE_ACCOUNT_MISMATCH: return "CODE_ACCOUNT_MISMATCH";
|
||||
case CODE_APP_SERVER_ERROR: return "CODE_APP_SERVER_ERROR";
|
||||
case CODE_MULTIACCOUNT_SETUP: return "CODE_MULTIACCOUNT_SETUP";
|
||||
case CODE_TIMEOUT: return "CODE_TIMEOUT";
|
||||
case CODE_PROFILE_NOT_FOUND: return "CODE_PROFILE_NOT_FOUND";
|
||||
case CODE_INVALID_LOGIN: return "CODE_INVALID_LOGIN";
|
||||
case CODE_INVALID_SERVER_ADDRESS: return "CODE_INVALID_SERVER_ADDRESS";
|
||||
case CODE_INVALID_SCHOOL_NAME: return "CODE_INVALID_SCHOOL_NAME";
|
||||
case CODE_INVALID_DEVICE: return "CODE_INVALID_DEVICE";
|
||||
case CODE_OLD_PASSWORD: return "CODE_OLD_PASSWORD";
|
||||
case CODE_INVALID_TOKEN: return "CODE_INVALID_TOKEN";
|
||||
case CODE_EXPIRED_TOKEN: return "CODE_EXPIRED_TOKEN";
|
||||
case CODE_INVALID_SYMBOL: return "CODE_INVALID_SYMBOL";
|
||||
case CODE_INVALID_PIN: return "CODE_INVALID_PIN";
|
||||
case CODE_ATTACHMENT_NOT_AVAILABLE: return "CODE_ATTACHMENT_NOT_AVAILABLE";
|
||||
case CODE_LIBRUS_NOT_ACTIVATED: return "CODE_LIBRUS_NOT_ACTIVATED";
|
||||
case CODE_PROFILE_ARCHIVED: return "CODE_PROFILE_ARCHIVED";
|
||||
case CODE_LIBRUS_DISCONNECTED: return "CODE_LIBRUS_DISCONNECTED";
|
||||
case CODE_SYNERGIA_NOT_ACTIVATED: return "CODE_SYNERGIA_NOT_ACTIVATED";
|
||||
}
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2019-12-13
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.szkolny
|
||||
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull
|
||||
|
||||
class Szkolny(val app: App, val callback: EdziennikCallback) {
|
||||
|
||||
private val api = SzkolnyApi(app)
|
||||
|
||||
fun sync(profiles: List<ProfileFull>) {
|
||||
if (profiles.isNotEmpty()) {
|
||||
val events = api.getEvents(profiles)
|
||||
|
||||
if (events.isNotEmpty()) {
|
||||
app.db.eventDao().addAll(events)
|
||||
app.db.metadataDao().addAllIgnore(events.map { event ->
|
||||
Metadata(
|
||||
event.profileId,
|
||||
Metadata.TYPE_EVENT,
|
||||
event.id,
|
||||
event.seen,
|
||||
event.notified,
|
||||
event.addedDate
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
completed()
|
||||
}
|
||||
|
||||
fun shareEvent(event: EventFull) {
|
||||
api.shareEvent(event)
|
||||
completed()
|
||||
}
|
||||
|
||||
fun unshareEvent(event: EventFull) {
|
||||
api.unshareEvent(event)
|
||||
completed()
|
||||
}
|
||||
|
||||
private fun completed() {
|
||||
callback.onCompleted()
|
||||
}
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2019-12-8
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.szkolny
|
||||
|
||||
import android.os.Build
|
||||
import com.google.gson.GsonBuilder
|
||||
import okhttp3.OkHttpClient
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.BuildConfig
|
||||
import pl.szczodrzynski.edziennik.api.v2.szkolny.adapter.DateAdapter
|
||||
import pl.szczodrzynski.edziennik.api.v2.szkolny.adapter.TimeAdapter
|
||||
import pl.szczodrzynski.edziennik.api.v2.szkolny.interceptor.SignatureInterceptor
|
||||
import pl.szczodrzynski.edziennik.api.v2.szkolny.request.EventShareRequest
|
||||
import pl.szczodrzynski.edziennik.api.v2.szkolny.request.ServerSyncRequest
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import retrofit2.create
|
||||
import java.util.concurrent.TimeUnit.SECONDS
|
||||
|
||||
class SzkolnyApi(val app: App) {
|
||||
|
||||
private val api: SzkolnyService
|
||||
|
||||
init {
|
||||
val okHttpClient: OkHttpClient = app.http.newBuilder()
|
||||
.followRedirects(true)
|
||||
.callTimeout(30, SECONDS)
|
||||
.addInterceptor(SignatureInterceptor(app))
|
||||
.build()
|
||||
|
||||
val gsonConverterFactory = GsonConverterFactory.create(
|
||||
GsonBuilder()
|
||||
.setLenient()
|
||||
.registerTypeAdapter(Date::class.java, DateAdapter())
|
||||
.registerTypeAdapter(Time::class.java, TimeAdapter())
|
||||
.create())
|
||||
|
||||
val retrofit: Retrofit = Retrofit.Builder()
|
||||
.baseUrl("https://api.szkolny.eu/")
|
||||
.addConverterFactory(gsonConverterFactory)
|
||||
.client(okHttpClient)
|
||||
.build()
|
||||
|
||||
api = retrofit.create()
|
||||
}
|
||||
|
||||
fun getEvents(profiles: List<ProfileFull>): List<EventFull> {
|
||||
val teams = app.db.teamDao().allNow
|
||||
|
||||
val response = api.serverSync(ServerSyncRequest(
|
||||
deviceId = app.deviceId,
|
||||
device = ServerSyncRequest.Device(
|
||||
osType = "Android",
|
||||
osVersion = Build.VERSION.RELEASE,
|
||||
hardware = "${Build.MANUFACTURER} ${Build.MODEL}",
|
||||
pushToken = app.config.sync.tokenApp,
|
||||
appVersion = BuildConfig.VERSION_NAME,
|
||||
appType = BuildConfig.BUILD_TYPE,
|
||||
appVersionCode = BuildConfig.VERSION_CODE,
|
||||
syncInterval = app.config.sync.interval
|
||||
),
|
||||
userCodes = profiles.map { it.usernameId },
|
||||
users = profiles.map { profile ->
|
||||
ServerSyncRequest.User(
|
||||
profile.usernameId,
|
||||
profile.studentNameLong ?: "",
|
||||
profile.studentNameShort ?: "",
|
||||
profile.loginStoreType,
|
||||
teams.filter { it.profileId == profile.id }.map { it.code }
|
||||
)
|
||||
}
|
||||
)).execute().body()
|
||||
|
||||
val events = mutableListOf<EventFull>()
|
||||
|
||||
response?.data?.events?.forEach { event ->
|
||||
teams.filter { it.code == event.teamCode }.forEach { team ->
|
||||
val profile = profiles.firstOrNull { it.id == team.profileId }
|
||||
|
||||
events.add(event.apply {
|
||||
profileId = team.profileId
|
||||
teamId = team.id
|
||||
addedManually = true
|
||||
seen = profile?.empty ?: false
|
||||
notified = profile?.empty ?: false
|
||||
|
||||
if (profile?.usernameId == event.sharedBy) sharedBy = "self"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return events
|
||||
}
|
||||
|
||||
fun shareEvent(event: EventFull) {
|
||||
val team = app.db.teamDao().getByIdNow(event.profileId, event.teamId)
|
||||
|
||||
api.shareEvent(EventShareRequest(
|
||||
deviceId = app.deviceId,
|
||||
sharedByName = event.sharedByName,
|
||||
shareTeamCode = team.code,
|
||||
event = event
|
||||
)).execute()
|
||||
}
|
||||
|
||||
fun unshareEvent(event: EventFull) {
|
||||
val team = app.db.teamDao().getByIdNow(event.profileId, event.teamId)
|
||||
|
||||
api.shareEvent(EventShareRequest(
|
||||
deviceId = app.deviceId,
|
||||
sharedByName = event.sharedByName,
|
||||
unshareTeamCode = team.code,
|
||||
eventId = event.id
|
||||
)).execute()
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2019-12-8
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.szkolny
|
||||
|
||||
import pl.szczodrzynski.edziennik.api.v2.szkolny.request.EventShareRequest
|
||||
import pl.szczodrzynski.edziennik.api.v2.szkolny.request.ServerSyncRequest
|
||||
import pl.szczodrzynski.edziennik.api.v2.szkolny.response.ApiResponse
|
||||
import pl.szczodrzynski.edziennik.api.v2.szkolny.response.ServerSyncResponse
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.POST
|
||||
|
||||
interface SzkolnyService {
|
||||
|
||||
@POST("appSync")
|
||||
fun serverSync(@Body request: ServerSyncRequest): Call<ApiResponse<ServerSyncResponse>>
|
||||
|
||||
@POST("share")
|
||||
fun shareEvent(@Body request: EventShareRequest): Call<ApiResponse<Nothing>>
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2019-12-8
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.szkolny.response
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull
|
||||
|
||||
data class ServerSyncResponse(val events: List<EventFull>)
|
@ -1,90 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.task
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import androidx.core.app.NotificationCompat
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.Notifier.ID_NOTIFICATIONS
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.notification.getNotificationTitle
|
||||
import pl.szczodrzynski.edziennik.utils.models.Notification
|
||||
import kotlin.math.min
|
||||
|
||||
class NotifyTask : IApiTask(-1) {
|
||||
override fun prepare(app: App) {
|
||||
taskName = app.getString(R.string.edziennik_notification_api_notify_title)
|
||||
}
|
||||
|
||||
override fun cancel() {
|
||||
|
||||
}
|
||||
|
||||
fun run(app: App, taskCallback: EdziennikCallback) {
|
||||
val list = app.db.notificationDao().getNotPostedNow()
|
||||
val notificationList = list.subList(0, min(15, list.size))
|
||||
|
||||
var unreadCount = list.size
|
||||
|
||||
for (notification in notificationList) {
|
||||
val intent = Intent(app, MainActivity::class.java)
|
||||
notification.fillIntent(intent)
|
||||
val pendingIntent = PendingIntent.getActivity(app, notification.id, intent, 0)
|
||||
val notificationBuilder = NotificationCompat.Builder(app, app.notifier.notificationGroup)
|
||||
// title, text, type, date
|
||||
.setContentTitle(notification.profileName)
|
||||
.setContentText(notification.text)
|
||||
.setSubText(app.getNotificationTitle(notification.type))
|
||||
.setWhen(notification.addedDate)
|
||||
.setTicker(app.getString(R.string.notification_ticker_format, Notification.stringType(app, notification.type)))
|
||||
// icon, color, lights, priority
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setColor(app.notifier.notificationColor)
|
||||
.setLights(-0xff0001, 2000, 2000)
|
||||
.setPriority(app.notifier.notificationPriority)
|
||||
// channel, group, style
|
||||
.setChannelId(app.notifier.notificationGroup)
|
||||
.setGroup(app.notifier.notificationGroup)
|
||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
||||
.setStyle(NotificationCompat.BigTextStyle().bigText(notification.text))
|
||||
// intent, auto cancel
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true)
|
||||
if (!app.notifier.shouldBeQuiet()) {
|
||||
notificationBuilder.setDefaults(app.notifier.notificationDefaults)
|
||||
}
|
||||
app.notifier.notificationManager.notify(notification.id, notificationBuilder.build())
|
||||
}
|
||||
|
||||
if (notificationList.isNotEmpty() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
val intent = Intent(app, MainActivity::class.java)
|
||||
intent.action = "android.intent.action.MAIN"
|
||||
intent.putExtra("fragmentId", MainActivity.DRAWER_ITEM_NOTIFICATIONS)
|
||||
val pendingIntent = PendingIntent.getActivity(app, ID_NOTIFICATIONS,
|
||||
intent, 0)
|
||||
|
||||
val groupBuilder = NotificationCompat.Builder(app, app.notifier.notificationGroup)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setColor(app.notifier.notificationColor)
|
||||
.setContentTitle(app.getString(R.string.notification_new_notification_title_format, unreadCount))
|
||||
.setGroupSummary(true)
|
||||
.setAutoCancel(true)
|
||||
.setChannelId(app.notifier.notificationGroup)
|
||||
.setGroup(app.notifier.notificationGroup)
|
||||
.setLights(-0xff0001, 2000, 2000)
|
||||
.setPriority(app.notifier.notificationPriority)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setStyle(NotificationCompat.BigTextStyle())
|
||||
if (!app.notifier.shouldBeQuiet()) {
|
||||
groupBuilder.setDefaults(app.notifier.notificationDefaults)
|
||||
}
|
||||
app.notifier.notificationManager.notify(ID_NOTIFICATIONS, groupBuilder.build())
|
||||
}
|
||||
|
||||
app.db.notificationDao().setAllPosted()
|
||||
|
||||
taskCallback.onCompleted()
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-12-7.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.task
|
||||
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.api.v2.szkolny.Szkolny
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull
|
||||
|
||||
class SzkolnyTask(val request: Any) : IApiTask(-1) {
|
||||
companion object {
|
||||
private const val TAG = "SzkolnyTask"
|
||||
|
||||
fun sync(profiles: List<ProfileFull>) = SzkolnyTask(SyncRequest(profiles))
|
||||
fun shareEvent(event: EventFull) = SzkolnyTask(ShareEventRequest(event))
|
||||
fun unshareEvent(event: EventFull) = SzkolnyTask(UnshareEventRequest(event))
|
||||
}
|
||||
|
||||
private lateinit var szkolny: Szkolny
|
||||
|
||||
override fun prepare(app: App) {
|
||||
taskName = app.getString(R.string.edziennik_szkolny_api_sync_title)
|
||||
}
|
||||
|
||||
override fun cancel() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
internal fun run(app: App, taskCallback: EdziennikCallback) {
|
||||
szkolny = Szkolny(app, taskCallback)
|
||||
|
||||
when (request) {
|
||||
is SyncRequest -> szkolny.sync(request.profiles)
|
||||
is ShareEventRequest -> szkolny.shareEvent(request.event)
|
||||
is UnshareEventRequest -> szkolny.unshareEvent(request.event)
|
||||
}
|
||||
}
|
||||
|
||||
data class SyncRequest(val profiles: List<ProfileFull>)
|
||||
data class ShareEventRequest(val event: EventFull)
|
||||
data class UnshareEventRequest(val event: EventFull)
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.config
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
@ -15,12 +16,13 @@ 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.db.AppDb
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
|
||||
companion object {
|
||||
const val DATA_VERSION = 2
|
||||
const val DATA_VERSION = 10
|
||||
}
|
||||
|
||||
private val job = Job()
|
||||
@ -39,6 +41,25 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
|
||||
get() { mDataVersion = mDataVersion ?: values.get("dataVersion", 0); return mDataVersion ?: 0 }
|
||||
set(value) { set("dataVersion", value); mDataVersion = value }
|
||||
|
||||
private var mHash: String? = null
|
||||
var hash: String
|
||||
get() { mHash = mHash ?: values.get("hash", ""); return mHash ?: "" }
|
||||
set(value) { set("hash", value); mHash = value }
|
||||
|
||||
private var mLastProfileId: Int? = null
|
||||
var lastProfileId: Int
|
||||
get() { mLastProfileId = mLastProfileId ?: values.get("lastProfileId", 0); return mLastProfileId ?: 0 }
|
||||
set(value) { set("lastProfileId", value); mLastProfileId = value }
|
||||
|
||||
private var mUpdatesChannel: String? = null
|
||||
var updatesChannel: String
|
||||
get() { mUpdatesChannel = mUpdatesChannel ?: values.get("updatesChannel", "release"); return mUpdatesChannel ?: "release" }
|
||||
set(value) { set("updatesChannel", value); mUpdatesChannel = value }
|
||||
private var mUpdate: Update? = null
|
||||
var update: Update?
|
||||
get() { mUpdate = mUpdate ?: values.get("update", null as Update?); return mUpdate ?: null as Update? }
|
||||
set(value) { set("update", value); mUpdate = value }
|
||||
|
||||
private var mAppVersion: Int? = null
|
||||
var appVersion: Int
|
||||
get() { mAppVersion = mAppVersion ?: values.get("appVersion", BuildConfig.VERSION_CODE); return mAppVersion ?: BuildConfig.VERSION_CODE }
|
||||
@ -49,6 +70,11 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
|
||||
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 mDevModePassword: String? = null
|
||||
var devModePassword: String?
|
||||
get() { mDevModePassword = mDevModePassword ?: values.get("devModePassword", null as String?); return mDevModePassword }
|
||||
@ -64,6 +90,16 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
|
||||
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 rawEntries: List<ConfigEntry> = db.configDao().getAllNow()
|
||||
private val profileConfigs: HashMap<Int, ProfileConfig> = hashMapOf()
|
||||
init {
|
||||
@ -74,8 +110,11 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
|
||||
ConfigMigration(app, this)
|
||||
}
|
||||
fun getFor(profileId: Int): ProfileConfig {
|
||||
return profileConfigs[profileId] ?: ProfileConfig(db, profileId, rawEntries)
|
||||
return profileConfigs[profileId] ?: ProfileConfig(db, profileId, db.configDao().getAllNow(profileId)).also {
|
||||
profileConfigs[profileId] = it
|
||||
}
|
||||
}
|
||||
fun forProfile() = getFor(App.profileId)
|
||||
|
||||
fun setProfile(profileId: Int) {
|
||||
}
|
||||
|
@ -17,6 +17,6 @@ class ConfigGrades(private val config: Config) {
|
||||
|
||||
private var mOrderBy: Int? = null
|
||||
var orderBy: Int
|
||||
get() { mOrderBy = mOrderBy ?: config.values.get("gradesOrderBy", 0); return mOrderBy ?: 0 }
|
||||
get() { mOrderBy = mOrderBy ?: config.values.get("gradesOrderBy", 0); return mOrderBy ?: ORDER_BY_DATE_DESC }
|
||||
set(value) { config.set("gradesOrderBy", value); mOrderBy = value }
|
||||
}
|
@ -9,6 +9,11 @@ import pl.szczodrzynski.edziennik.config.utils.getIntList
|
||||
import pl.szczodrzynski.edziennik.config.utils.set
|
||||
|
||||
class ConfigSync(private val config: Config) {
|
||||
private var mDontShowAppManagerDialog: Boolean? = null
|
||||
var dontShowAppManagerDialog: Boolean
|
||||
get() { mDontShowAppManagerDialog = mDontShowAppManagerDialog ?: config.values.get("dontShowAppManagerDialog", false); return mDontShowAppManagerDialog ?: false }
|
||||
set(value) { config.set("dontShowAppManagerDialog", value); mDontShowAppManagerDialog = value }
|
||||
|
||||
private var mSyncEnabled: Boolean? = null
|
||||
var enabled: Boolean
|
||||
get() { mSyncEnabled = mSyncEnabled ?: config.values.get("syncEnabled", true); return mSyncEnabled ?: true }
|
||||
|
@ -45,13 +45,18 @@ class ConfigUI(private val config: Config) {
|
||||
get() { mOpenDrawerOnBackPressed = mOpenDrawerOnBackPressed ?: config.values.get("openDrawerOnBackPressed", false); return mOpenDrawerOnBackPressed ?: false }
|
||||
set(value) { config.set("openDrawerOnBackPressed", value); mOpenDrawerOnBackPressed = value }
|
||||
|
||||
private var mAgendaViewType: Int? = null
|
||||
var agendaViewType: Int
|
||||
get() { mAgendaViewType = mAgendaViewType ?: config.values.get("agendaViewType", 0); return mAgendaViewType ?: 0 }
|
||||
set(value) { config.set("agendaViewType", value); mAgendaViewType = 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 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 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 }
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import pl.szczodrzynski.edziennik.config.db.ConfigEntry
|
||||
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
|
||||
@ -27,6 +28,7 @@ class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEn
|
||||
val values: HashMap<String, String?> = hashMapOf()
|
||||
|
||||
val grades by lazy { ProfileConfigGrades(this) }
|
||||
val ui by lazy { ProfileConfigUI(this) }
|
||||
/*
|
||||
val sync by lazy { ConfigSync(this) }
|
||||
val timetable by lazy { ConfigTimetable(this) }
|
||||
@ -37,10 +39,15 @@ class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEn
|
||||
get() { mDataVersion = mDataVersion ?: values.get("dataVersion", 0); return mDataVersion ?: 0 }
|
||||
set(value) { set("dataVersion", value); mDataVersion = value }
|
||||
|
||||
private var mHash: String? = null
|
||||
var hash: String
|
||||
get() { mHash = mHash ?: values.get("hash", ""); return mHash ?: "" }
|
||||
set(value) { set("hash", value); mHash = value }
|
||||
|
||||
init {
|
||||
rawEntries.toHashMap(profileId, values)
|
||||
/*if (dataVersion < DATA_VERSION)
|
||||
ProfileConfigMigration(this)*/
|
||||
if (dataVersion < DATA_VERSION)
|
||||
ProfileConfigMigration(this)
|
||||
}
|
||||
|
||||
override fun set(key: String, value: String?) {
|
||||
|
@ -6,8 +6,8 @@ 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.modules.profiles.Profile.Companion.COLOR_MODE_WEIGHTED
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.Companion.YEAR_ALL_GRADES
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.COLOR_MODE_WEIGHTED
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.YEAR_ALL_GRADES
|
||||
|
||||
class ProfileConfigGrades(private val config: ProfileConfig) {
|
||||
private var mColorMode: Int? = null
|
||||
@ -24,4 +24,4 @@ class ProfileConfigGrades(private val config: ProfileConfig) {
|
||||
var countZeroToAvg: Boolean
|
||||
get() { mCountZeroToAvg = mCountZeroToAvg ?: config.values.get("countZeroToAvg", true); return mCountZeroToAvg ?: true }
|
||||
set(value) { config.set("countZeroToAvg", value); mCountZeroToAvg = value }
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-1-19.
|
||||
*/
|
||||
|
||||
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
|
||||
|
||||
class ProfileConfigUI(private val config: 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 }
|
||||
}
|
@ -22,4 +22,7 @@ interface ConfigDao {
|
||||
|
||||
@Query("SELECT * FROM config WHERE profileId = :profileId")
|
||||
fun getAllNow(profileId: Int): List<ConfigEntry>
|
||||
|
||||
@Query("DELETE FROM config WHERE profileId = :profileId")
|
||||
fun clear(profileId: Int)
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-1-19.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.config.utils
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import pl.szczodrzynski.edziennik.BuildConfig
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.config.Config
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_LIBRUS
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_MOBIDZIENNIK
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_VULCAN
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
class AppConfigMigrationV3(p: SharedPreferences, config: Config) {
|
||||
init { config.apply {
|
||||
val s = "app.appConfig"
|
||||
if (dataVersion < 1) {
|
||||
ui.theme = p.getString("$s.appTheme", null)?.toIntOrNull() ?: 1
|
||||
sync.enabled = p.getString("$s.registerSyncEnabled", null)?.toBoolean() ?: true
|
||||
sync.interval = p.getString("$s.registerSyncInterval", null)?.toIntOrNull() ?: 3600
|
||||
val oldButtons = p.getString("$s.miniDrawerButtonIds", null)?.let { str ->
|
||||
str.replace("[\\[\\]]*".toRegex(), "")
|
||||
.split(",\\s?".toRegex())
|
||||
.mapNotNull { it.toIntOrNull() }
|
||||
}
|
||||
ui.miniMenuButtons = oldButtons ?: 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
|
||||
)
|
||||
dataVersion = 1
|
||||
}
|
||||
if (dataVersion < 2) {
|
||||
devModePassword = p.getString("$s.devModePassword", null).fix()
|
||||
sync.tokenApp = p.getString("$s.fcmToken", null).fix()
|
||||
timetable.bellSyncMultiplier = p.getString("$s.bellSyncMultiplier", null)?.toIntOrNull() ?: 0
|
||||
sync.quietHoursStart = p.getString("$s.quietHoursStart", null)?.toLongOrNull() ?: 0
|
||||
appRateSnackbarTime = p.getString("$s.appRateSnackbarTime", null)?.toLongOrNull() ?: 0
|
||||
sync.quietHoursEnd = p.getString("$s.quietHoursEnd", null)?.toLongOrNull() ?: 0
|
||||
timetable.countInSeconds = p.getString("$s.countInSeconds", null)?.toBoolean() ?: false
|
||||
ui.headerBackground = p.getString("$s.headerBackground", null).fix()
|
||||
ui.appBackground = p.getString("$s.appBackground", null).fix()
|
||||
ui.language = p.getString("$s.language", null).fix()
|
||||
appVersion = p.getString("$s.lastAppVersion", null)?.toIntOrNull() ?: BuildConfig.VERSION_CODE
|
||||
appInstalledTime = p.getString("$s.appInstalledTime", null)?.toLongOrNull() ?: 0
|
||||
grades.orderBy = p.getString("$s.gradesOrderBy", null)?.toIntOrNull() ?: 0
|
||||
sync.quietDuringLessons = p.getString("$s.quietDuringLessons", null)?.toBoolean() ?: false
|
||||
ui.miniMenuVisible = p.getString("$s.miniDrawerVisible", null)?.toBoolean() ?: false
|
||||
loginFinished = p.getString("$s.loginFinished", null)?.toBoolean() ?: false
|
||||
sync.onlyWifi = p.getString("$s.registerSyncOnlyWifi", null)?.toBoolean() ?: false
|
||||
sync.notifyAboutUpdates = p.getString("$s.notifyAboutUpdates", null)?.toBoolean() ?: true
|
||||
timetable.bellSyncDiff = p.getString("$s.bellSyncDiff", null)?.let { Gson().fromJson(it, Time::class.java) }
|
||||
|
||||
sync.tokenMobidziennikList = listOf()
|
||||
sync.tokenVulcanList = listOf()
|
||||
sync.tokenLibrusList = listOf()
|
||||
val tokens = p.getString("$s.fcmTokens", null)?.let { Gson().fromJson<Map<Int, Pair<String, List<Int>>>>(it, object: TypeToken<Map<Int, Pair<String, List<Int>>>>(){}.type) }
|
||||
tokens?.forEach {
|
||||
val token = it.value.first
|
||||
when (it.key) {
|
||||
LOGIN_TYPE_MOBIDZIENNIK -> sync.tokenMobidziennik = token
|
||||
LOGIN_TYPE_VULCAN -> sync.tokenVulcan = token
|
||||
LOGIN_TYPE_LIBRUS -> sync.tokenLibrus = token
|
||||
}
|
||||
}
|
||||
dataVersion = 2
|
||||
}
|
||||
}}
|
||||
|
||||
private fun String?.fix(): String? {
|
||||
return this?.replace("\"", "")?.let { if (it == "null") null else it }
|
||||
}
|
||||
}
|
@ -37,6 +37,9 @@ fun AbstractConfig.set(key: String, value: JsonElement?) {
|
||||
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) })
|
||||
}
|
||||
@ -74,6 +77,9 @@ fun HashMap<String, String?>.get(key: String, default: JsonObject?): JsonObject?
|
||||
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
|
||||
|
@ -5,32 +5,32 @@
|
||||
package pl.szczodrzynski.edziennik.config.utils
|
||||
|
||||
import android.content.Context
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.BuildConfig
|
||||
import pl.szczodrzynski.edziennik.HOUR
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.api.v2.LOGIN_TYPE_LIBRUS
|
||||
import pl.szczodrzynski.edziennik.api.v2.LOGIN_TYPE_MOBIDZIENNIK
|
||||
import pl.szczodrzynski.edziennik.api.v2.LOGIN_TYPE_VULCAN
|
||||
import pl.szczodrzynski.edziennik.config.Config
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import pl.szczodrzynski.edziennik.config.ConfigGrades.Companion.ORDER_BY_DATE_DESC
|
||||
|
||||
class ConfigMigration(app: App, config: Config) {
|
||||
init { config.apply {
|
||||
val p = app.getSharedPreferences("pl.szczodrzynski.edziennik_profiles", Context.MODE_PRIVATE)
|
||||
val s = "app.appConfig"
|
||||
|
||||
if (dataVersion < 1) {
|
||||
ui.theme = p.getString("$s.appTheme", null)?.toIntOrNull() ?: 1
|
||||
sync.enabled = p.getString("$s.registerSyncEnabled", null)?.toBoolean() ?: true
|
||||
sync.interval = p.getString("$s.registerSyncEnabled", null)?.toIntOrNull() ?: 3600
|
||||
val oldButtons = p.getString("$s.miniDrawerButtonIds", null)?.let { str ->
|
||||
str.replace("[\\[\\]]*".toRegex(), "")
|
||||
.split(",\\s?".toRegex())
|
||||
.mapNotNull { it.toIntOrNull() }
|
||||
}
|
||||
ui.miniMenuButtons = oldButtons ?: listOf(
|
||||
val p = app.getSharedPreferences("pl.szczodrzynski.edziennik_profiles", Context.MODE_PRIVATE)
|
||||
if (p.contains("app.appConfig.appTheme")) {
|
||||
// migrate appConfig from app version 3.x and lower.
|
||||
// Updates dataVersion to level 2.
|
||||
AppConfigMigrationV3(p, config)
|
||||
}
|
||||
|
||||
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,
|
||||
@ -39,46 +39,36 @@ class ConfigMigration(app: App, config: Config) {
|
||||
MainActivity.DRAWER_ITEM_HOMEWORK,
|
||||
MainActivity.DRAWER_ITEM_SETTINGS
|
||||
)
|
||||
dataVersion = 1
|
||||
}
|
||||
if (dataVersion < 2) {
|
||||
devModePassword = p.getString("$s.devModePassword", null).fix()
|
||||
sync.tokenApp = p.getString("$s.fcmToken", null).fix()
|
||||
timetable.bellSyncMultiplier = p.getString("$s.bellSyncMultiplier", null)?.toIntOrNull() ?: 0
|
||||
sync.quietHoursStart = p.getString("$s.quietHoursStart", null)?.toLongOrNull() ?: 0
|
||||
appRateSnackbarTime = p.getString("$s.appRateSnackbarTime", null)?.toLongOrNull() ?: 0
|
||||
sync.quietHoursEnd = p.getString("$s.quietHoursEnd", null)?.toLongOrNull() ?: 0
|
||||
timetable.countInSeconds = p.getString("$s.countInSeconds", null)?.toBoolean() ?: false
|
||||
ui.headerBackground = p.getString("$s.headerBackground", null).fix()
|
||||
ui.appBackground = p.getString("$s.appBackground", null).fix()
|
||||
ui.language = p.getString("$s.language", null).fix()
|
||||
appVersion = p.getString("$s.lastAppVersion", null)?.toIntOrNull() ?: BuildConfig.VERSION_CODE
|
||||
appInstalledTime = p.getString("$s.appInstalledTime", null)?.toLongOrNull() ?: 0
|
||||
grades.orderBy = p.getString("$s.gradesOrderBy", null)?.toIntOrNull() ?: 0
|
||||
sync.quietDuringLessons = p.getString("$s.quietDuringLessons", null)?.toBoolean() ?: false
|
||||
ui.miniMenuVisible = p.getString("$s.miniDrawerVisible", null)?.toBoolean() ?: false
|
||||
loginFinished = p.getString("$s.loginFinished", null)?.toBoolean() ?: false
|
||||
sync.onlyWifi = p.getString("$s.registerSyncOnlyWifi", null)?.toBoolean() ?: false
|
||||
sync.notifyAboutUpdates = p.getString("$s.notifyAboutUpdates", null)?.toBoolean() ?: true
|
||||
timetable.bellSyncDiff = p.getString("$s.bellSyncDiff", null)?.let { Gson().fromJson(it, Time::class.java) }
|
||||
|
||||
sync.enabled = true
|
||||
sync.interval = 1*HOUR.toInt()
|
||||
sync.notifyAboutUpdates = true
|
||||
sync.onlyWifi = false
|
||||
sync.quietHoursStart = 0
|
||||
sync.quietHoursEnd = 0
|
||||
sync.quietDuringLessons = false
|
||||
sync.tokenApp = null
|
||||
sync.tokenMobidziennik = null
|
||||
sync.tokenMobidziennikList = listOf()
|
||||
sync.tokenVulcanList = listOf()
|
||||
sync.tokenLibrus = null
|
||||
sync.tokenLibrusList = listOf()
|
||||
val tokens = p.getString("$s.fcmTokens", null)?.let { Gson().fromJson<Map<Int, Pair<String, List<Int>>>>(it, object: TypeToken<Map<Int, Pair<String, List<Int>>>>(){}.type) }
|
||||
tokens?.forEach {
|
||||
val token = it.value.first
|
||||
when (it.key) {
|
||||
LOGIN_TYPE_MOBIDZIENNIK -> sync.tokenMobidziennik = token
|
||||
LOGIN_TYPE_VULCAN -> sync.tokenVulcan = token
|
||||
LOGIN_TYPE_LIBRUS -> sync.tokenLibrus = token
|
||||
}
|
||||
}
|
||||
sync.tokenVulcan = null
|
||||
sync.tokenVulcanList = listOf()
|
||||
timetable.bellSyncMultiplier = 0
|
||||
timetable.bellSyncDiff = null
|
||||
timetable.countInSeconds = false
|
||||
grades.orderBy = ORDER_BY_DATE_DESC
|
||||
|
||||
dataVersion = 2
|
||||
}
|
||||
}}
|
||||
|
||||
private fun String?.fix(): String? {
|
||||
return this?.replace("\"", "")?.let { if (it == "null") null else it }
|
||||
}
|
||||
}
|
||||
if (dataVersion < 10) {
|
||||
ui.openDrawerOnBackPressed = false
|
||||
ui.homeCards = listOf()
|
||||
ui.snowfall = false
|
||||
ui.bottomSheetOpened = false
|
||||
sync.dontShowAppManagerDialog = false
|
||||
|
||||
dataVersion = 10
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
@ -4,23 +4,21 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.config.utils
|
||||
|
||||
import android.content.Context
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.config.Config
|
||||
import pl.szczodrzynski.edziennik.config.ProfileConfig
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.AGENDA_DEFAULT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.COLOR_MODE_WEIGHTED
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.YEAR_ALL_GRADES
|
||||
|
||||
class ProfileConfigMigration(app: App, config: Config) {
|
||||
class ProfileConfigMigration(config: ProfileConfig) {
|
||||
init { config.apply {
|
||||
val p = app.getSharedPreferences("pl.szczodrzynski.edziennik_profiles", Context.MODE_PRIVATE)
|
||||
val s = "app.appConfig"
|
||||
|
||||
if (dataVersion < 1) {
|
||||
grades.colorMode = COLOR_MODE_WEIGHTED
|
||||
grades.countZeroToAvg = true
|
||||
grades.yearAverageMode = YEAR_ALL_GRADES
|
||||
ui.agendaViewType = AGENDA_DEFAULT
|
||||
|
||||
//dataVersion = 1
|
||||
}
|
||||
if (dataVersion < 2) {
|
||||
//gradesColorMode do profilu !
|
||||
//agendaViewType do profilu !
|
||||
// app.appConfig.dontCountZeroToAverage do profilu !
|
||||
dataVersion = 1
|
||||
}
|
||||
}}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-9-28.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2
|
||||
package pl.szczodrzynski.edziennik.data.api
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
@ -12,13 +12,16 @@ import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.requests.ServiceCloseRequest
|
||||
import pl.szczodrzynski.edziennik.api.v2.events.requests.TaskCancelRequest
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.api.v2.task.*
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.data.api.events.*
|
||||
import pl.szczodrzynski.edziennik.data.api.events.requests.ServiceCloseRequest
|
||||
import pl.szczodrzynski.edziennik.data.api.events.requests.TaskCancelRequest
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.api.task.ErrorReportTask
|
||||
import pl.szczodrzynski.edziennik.data.api.task.IApiTask
|
||||
import pl.szczodrzynski.edziennik.data.api.task.SzkolnyTask
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
@ -26,7 +29,7 @@ import kotlin.math.roundToInt
|
||||
class ApiService : Service() {
|
||||
companion object {
|
||||
const val TAG = "ApiService"
|
||||
const val NOTIFICATION_API_CHANNEL_ID = "pl.szczodrzynski.edziennik.GET_DATA"
|
||||
const val NOTIFICATION_API_CHANNEL_ID = "pl.szczodrzynski.edziennik.SYNC"
|
||||
fun start(context: Context) {
|
||||
context.startService(Intent(context, ApiService::class.java))
|
||||
}
|
||||
@ -38,16 +41,15 @@ class ApiService : Service() {
|
||||
|
||||
private val app by lazy { applicationContext as App }
|
||||
|
||||
private val syncingProfiles = mutableListOf<ProfileFull>()
|
||||
private val syncingProfiles = mutableListOf<Profile>()
|
||||
|
||||
private val finishingTaskQueue = mutableListOf(
|
||||
SzkolnyTask.sync(syncingProfiles),
|
||||
NotifyTask()
|
||||
)
|
||||
private var szkolnyTaskFinished = false
|
||||
private val allTaskRequestList = mutableListOf<Any>()
|
||||
private val taskQueue = mutableListOf<IApiTask>()
|
||||
private val errorList = mutableListOf<ApiError>()
|
||||
|
||||
private var serviceClosed = false
|
||||
set(value) { field = value; notification.serviceClosed = value }
|
||||
private var taskCancelled = false
|
||||
private var taskIsRunning = false
|
||||
private var taskRunning: IApiTask? = null // for debug purposes
|
||||
@ -58,7 +60,7 @@ class ApiService : Service() {
|
||||
private var taskProgress = -1f
|
||||
private var taskProgressText: String? = null
|
||||
|
||||
private val notification by lazy { EdziennikNotification(this) }
|
||||
private val notification by lazy { EdziennikNotification(app) }
|
||||
|
||||
private var lastEventTime = System.currentTimeMillis()
|
||||
private var taskCancelTries = 0
|
||||
@ -73,7 +75,7 @@ class ApiService : Service() {
|
||||
override fun onCompleted() {
|
||||
lastEventTime = System.currentTimeMillis()
|
||||
d(TAG, "Task $taskRunningId (profile $taskProfileId) - $taskProgressText - finished")
|
||||
EventBus.getDefault().post(ApiTaskFinishedEvent(taskProfileId))
|
||||
EventBus.getDefault().postSticky(ApiTaskFinishedEvent(taskProfileId))
|
||||
clearTask()
|
||||
|
||||
notification.setIdle().post()
|
||||
@ -84,7 +86,7 @@ class ApiService : Service() {
|
||||
lastEventTime = System.currentTimeMillis()
|
||||
d(TAG, "Task $taskRunningId threw an error - $apiError")
|
||||
apiError.profileId = taskProfileId
|
||||
EventBus.getDefault().post(ApiTaskErrorEvent(apiError))
|
||||
EventBus.getDefault().postSticky(ApiTaskErrorEvent(apiError))
|
||||
errorList.add(apiError)
|
||||
apiError.throwable?.printStackTrace()
|
||||
if (apiError.isCritical) {
|
||||
@ -130,15 +132,20 @@ class ApiService : Service() {
|
||||
checkIfTaskFrozen()
|
||||
if (taskIsRunning)
|
||||
return
|
||||
if (taskCancelled || serviceClosed || (taskQueue.isEmpty() && finishingTaskQueue.isEmpty())) {
|
||||
serviceClosed = false
|
||||
if (taskCancelled || serviceClosed || (taskQueue.isEmpty() && szkolnyTaskFinished)) {
|
||||
allCompleted()
|
||||
return
|
||||
}
|
||||
|
||||
lastEventTime = System.currentTimeMillis()
|
||||
|
||||
val task = if (taskQueue.isEmpty()) finishingTaskQueue.removeAt(0) else taskQueue.removeAt(0)
|
||||
val task = if (taskQueue.isNotEmpty()) {
|
||||
taskQueue.removeAt(0)
|
||||
} else {
|
||||
szkolnyTaskFinished = true
|
||||
SzkolnyTask(app, syncingProfiles)
|
||||
}
|
||||
|
||||
task.taskId = ++taskMaximumId
|
||||
task.prepare(app)
|
||||
taskIsRunning = true
|
||||
@ -161,9 +168,8 @@ class ApiService : Service() {
|
||||
try {
|
||||
when (task) {
|
||||
is EdziennikTask -> task.run(app, taskCallback)
|
||||
is NotifyTask -> task.run(app, taskCallback)
|
||||
is ErrorReportTask -> task.run(app, taskCallback, notification, errorList)
|
||||
is SzkolnyTask -> task.run(app, taskCallback)
|
||||
is SzkolnyTask -> task.run(taskCallback)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
taskCallback.onError(ApiError(TAG, EXCEPTION_API_TASK).withThrowable(e))
|
||||
@ -194,7 +200,7 @@ class ApiService : Service() {
|
||||
*/
|
||||
private fun stopIfTaskFrozen() {
|
||||
if (checkIfTaskFrozen()) {
|
||||
stopSelf()
|
||||
allCompleted()
|
||||
}
|
||||
}
|
||||
|
||||
@ -213,7 +219,8 @@ class ApiService : Service() {
|
||||
}
|
||||
|
||||
private fun allCompleted() {
|
||||
EventBus.getDefault().post(ApiTaskAllFinishedEvent())
|
||||
serviceClosed = true
|
||||
EventBus.getDefault().postSticky(ApiTaskAllFinishedEvent())
|
||||
stopSelf()
|
||||
}
|
||||
|
||||
@ -228,6 +235,13 @@ class ApiService : Service() {
|
||||
EventBus.getDefault().removeStickyEvent(task)
|
||||
d(TAG, task.toString())
|
||||
|
||||
if (task is EdziennikTask) {
|
||||
// fix for duplicated tasks, thank you EventBus
|
||||
if (task.request in allTaskRequestList)
|
||||
return
|
||||
allTaskRequestList += task.request
|
||||
}
|
||||
|
||||
if (task is EdziennikTask) {
|
||||
when (task.request) {
|
||||
is EdziennikTask.SyncRequest -> app.db.profileDao().idsForSyncNow.forEach {
|
||||
@ -270,7 +284,7 @@ class ApiService : Service() {
|
||||
serviceClosed = true
|
||||
taskCancelled = true
|
||||
taskRunning?.cancel()
|
||||
stopSelf()
|
||||
allCompleted()
|
||||
}
|
||||
|
||||
/* _____ _ _ _
|
||||
@ -287,11 +301,13 @@ class ApiService : Service() {
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
d(TAG, "Foreground service onStartCommand")
|
||||
startForeground(EdziennikNotification.NOTIFICATION_ID, notification.notification)
|
||||
startForeground(app.notifications.syncId, notification.notification)
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
d(TAG, "Service destroyed")
|
||||
serviceClosed = true
|
||||
EventBus.getDefault().unregister(this)
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-9-19.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2
|
||||
package pl.szczodrzynski.edziennik.data.api
|
||||
|
||||
import android.os.Build
|
||||
import pl.szczodrzynski.edziennik.BuildConfig
|
||||
@ -14,11 +14,11 @@ val SYSTEM_USER_AGENT = System.getProperty("http.agent") ?: "Dalvik/2.1.0 Androi
|
||||
|
||||
val SERVER_USER_AGENT = "Szkolny.eu/${BuildConfig.VERSION_NAME} $SYSTEM_USER_AGENT"
|
||||
|
||||
const val FAKE_LIBRUS_API = "http://librus.szkolny.eu/api"
|
||||
const val FAKE_LIBRUS_PORTAL = "http://librus.szkolny.eu"
|
||||
const val FAKE_LIBRUS_AUTHORIZE = "http://librus.szkolny.eu/authorize.php"
|
||||
const val FAKE_LIBRUS_LOGIN = "http://librus.szkolny.eu/login_action.php"
|
||||
const val FAKE_LIBRUS_TOKEN = "http://librus.szkolny.eu/access_token.php"
|
||||
const val FAKE_LIBRUS_API = "https://librus.szkolny.eu/api"
|
||||
const val FAKE_LIBRUS_PORTAL = "https://librus.szkolny.eu"
|
||||
const val FAKE_LIBRUS_AUTHORIZE = "https://librus.szkolny.eu/authorize.php"
|
||||
const val FAKE_LIBRUS_LOGIN = "https://librus.szkolny.eu/login_action.php"
|
||||
const val FAKE_LIBRUS_TOKEN = "https://librus.szkolny.eu/access_token.php"
|
||||
const val FAKE_LIBRUS_ACCOUNT = "/synergia_accounts_fresh.php?login="
|
||||
const val FAKE_LIBRUS_ACCOUNTS = "/synergia_accounts.php"
|
||||
|
||||
@ -69,6 +69,10 @@ const val IDZIENNIK_WEB_NOTICES = "mod_panelRodzica/uwagi/WS_uwagiUcznia.asmx/po
|
||||
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"
|
||||
|
||||
val IDZIENNIK_API_USER_AGENT = SYSTEM_USER_AGENT
|
||||
const val IDZIENNIK_API_URL = "https://iuczniowie.progman.pl/idziennik/api"
|
||||
@ -100,4 +104,7 @@ const val VULCAN_API_ENDPOINT_ATTENDANCE = "mobile-api/Uczen.v3.Uczen/Frekwencje
|
||||
const val VULCAN_API_ENDPOINT_MESSAGES_RECEIVED = "mobile-api/Uczen.v3.Uczen/WiadomosciOdebrane"
|
||||
const val VULCAN_API_ENDPOINT_MESSAGES_SENT = "mobile-api/Uczen.v3.Uczen/WiadomosciWyslane"
|
||||
const val VULCAN_API_ENDPOINT_MESSAGES_CHANGE_STATUS = "mobile-api/Uczen.v3.Uczen/ZmienStatusWiadomosci"
|
||||
const val VULCAN_API_ENDPOINT_MESSAGES_ADD = "mobile-api/Uczen.v3.Uczen/DodajWiadomosc"
|
||||
const val VULCAN_API_ENDPOINT_PUSH = "mobile-api/Uczen.v3.Uczen/UstawPushToken"
|
||||
|
||||
const val EDUDZIENNIK_USER_AGENT = "Szkolny.eu/${BuildConfig.VERSION_NAME}"
|
@ -2,7 +2,7 @@
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-10-1.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2
|
||||
package pl.szczodrzynski.edziennik.data.api
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationManager
|
||||
@ -11,19 +11,16 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationCompat.PRIORITY_MIN
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
||||
class EdziennikNotification(val context: Context) {
|
||||
companion object {
|
||||
const val NOTIFICATION_ID = 20191001
|
||||
}
|
||||
|
||||
private val notificationManager by lazy { context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager }
|
||||
class EdziennikNotification(val app: App) {
|
||||
private val notificationManager by lazy { app.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager }
|
||||
|
||||
private val notificationBuilder: NotificationCompat.Builder by lazy {
|
||||
NotificationCompat.Builder(context, ApiService.NOTIFICATION_API_CHANNEL_ID)
|
||||
NotificationCompat.Builder(app, ApiService.NOTIFICATION_API_CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setPriority(PRIORITY_MIN)
|
||||
.setOngoing(true)
|
||||
@ -35,39 +32,40 @@ class EdziennikNotification(val context: Context) {
|
||||
|
||||
private var errorCount = 0
|
||||
private var criticalErrorCount = 0
|
||||
var serviceClosed = false
|
||||
|
||||
private fun cancelPendingIntent(taskId: Int): PendingIntent {
|
||||
val intent = Intent("pl.szczodrzynski.edziennik.SZKOLNY_MAIN")
|
||||
intent.putExtra("task", "TaskCancelRequest")
|
||||
intent.putExtra("taskId", taskId)
|
||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT) as PendingIntent
|
||||
return PendingIntent.getBroadcast(app, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT) as PendingIntent
|
||||
}
|
||||
private val closePendingIntent: PendingIntent
|
||||
get() {
|
||||
val intent = Intent("pl.szczodrzynski.edziennik.SZKOLNY_MAIN")
|
||||
intent.putExtra("task", "ServiceCloseRequest")
|
||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT) as PendingIntent
|
||||
return PendingIntent.getBroadcast(app, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT) as PendingIntent
|
||||
}
|
||||
|
||||
private fun errorCountText(): String? {
|
||||
var result = ""
|
||||
if (criticalErrorCount > 0) {
|
||||
result += context.resources.getQuantityString(R.plurals.critical_errors_format, criticalErrorCount, criticalErrorCount)
|
||||
result += app.resources.getQuantityString(R.plurals.critical_errors_format, criticalErrorCount, criticalErrorCount)
|
||||
}
|
||||
if (criticalErrorCount > 0 && errorCount > 0) {
|
||||
result += ", "
|
||||
}
|
||||
if (errorCount > 0) {
|
||||
result += context.resources.getQuantityString(R.plurals.normal_errors_format, errorCount, errorCount)
|
||||
result += app.resources.getQuantityString(R.plurals.normal_errors_format, errorCount, errorCount)
|
||||
}
|
||||
return if (result.isEmpty()) null else result
|
||||
}
|
||||
|
||||
fun setIdle(): EdziennikNotification {
|
||||
notificationBuilder.setContentTitle(context.getString(R.string.edziennik_notification_api_title))
|
||||
notificationBuilder.setContentTitle(app.getString(R.string.edziennik_notification_api_title))
|
||||
notificationBuilder.setProgress(0, 0, false)
|
||||
notificationBuilder.apply {
|
||||
val str = context.getString(R.string.edziennik_notification_api_text)
|
||||
val str = app.getString(R.string.edziennik_notification_api_text)
|
||||
setStyle(NotificationCompat.BigTextStyle().bigText(str))
|
||||
setContentText(str)
|
||||
}
|
||||
@ -81,7 +79,7 @@ class EdziennikNotification(val context: Context) {
|
||||
}
|
||||
fun setCriticalError(): EdziennikNotification {
|
||||
criticalErrorCount++
|
||||
notificationBuilder.setContentTitle(context.getString(R.string.edziennik_notification_api_error_title))
|
||||
notificationBuilder.setContentTitle(app.getString(R.string.edziennik_notification_api_error_title))
|
||||
notificationBuilder.setProgress(0, 0, false)
|
||||
notificationBuilder.apply {
|
||||
val str = errorCountText()
|
||||
@ -118,7 +116,7 @@ class EdziennikNotification(val context: Context) {
|
||||
notificationBuilder.addAction(
|
||||
NotificationCompat.Action(
|
||||
R.drawable.ic_notification,
|
||||
context.getString(R.string.edziennik_notification_api_close),
|
||||
app.getString(R.string.edziennik_notification_api_close),
|
||||
closePendingIntent
|
||||
))
|
||||
return this
|
||||
@ -128,13 +126,15 @@ class EdziennikNotification(val context: Context) {
|
||||
notificationBuilder.addAction(
|
||||
NotificationCompat.Action(
|
||||
R.drawable.ic_notification,
|
||||
context.getString(R.string.edziennik_notification_api_cancel),
|
||||
app.getString(R.string.edziennik_notification_api_cancel),
|
||||
cancelPendingIntent(taskId)
|
||||
))
|
||||
}
|
||||
|
||||
fun post() {
|
||||
notificationManager.notify(NOTIFICATION_ID, notification)
|
||||
if (serviceClosed)
|
||||
return
|
||||
notificationManager.notify(app.notifications.syncId, notification)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2
|
||||
package pl.szczodrzynski.edziennik.data.api
|
||||
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.Data
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.Feature
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimer
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_NEVER
|
||||
import pl.szczodrzynski.edziennik.data.api.models.Data
|
||||
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.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_NEVER
|
||||
|
||||
fun Data.prepare(loginMethods: List<LoginMethod>, features: List<Feature>, featureIds: List<Int>, viewId: Int?) {
|
||||
val data = this
|
||||
@ -47,7 +47,8 @@ fun Data.prepare(loginMethods: List<LoginMethod>, features: List<Feature>, featu
|
||||
.onEach { feature ->
|
||||
feature.endpointIds.forEach { endpoint ->
|
||||
(data.endpointTimers
|
||||
.singleOrNull { it.endpointId == endpoint.first } ?: EndpointTimer(data.profile?.id ?: -1, endpoint.first))
|
||||
.singleOrNull { it.endpointId == endpoint.first } ?: EndpointTimer(data.profile?.id
|
||||
?: -1, endpoint.first))
|
||||
.let { timer ->
|
||||
if (timer.nextSync == SYNC_ALWAYS ||
|
||||
(viewId != null && timer.viewId == viewId) ||
|
||||
@ -80,4 +81,32 @@ fun Data.prepare(loginMethods: List<LoginMethod>, features: List<Feature>, featu
|
||||
|
||||
progressCount = targetLoginMethodIds.size + targetEndpointIds.size
|
||||
progressStep = if (progressCount <= 0) 0f else 100f / progressCount.toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
fun Data.prepareFor(loginMethods: List<LoginMethod>, loginMethodId: Int) {
|
||||
val possibleLoginMethods = this.loginMethods.toMutableList()
|
||||
|
||||
loginMethods.forEach {
|
||||
if (it.isPossible(profile, loginStore))
|
||||
possibleLoginMethods += it.loginMethodId
|
||||
}
|
||||
|
||||
targetLoginMethodIds.clear()
|
||||
|
||||
// check the login method for any dependencies
|
||||
var requiredLoginMethod: Int? = loginMethodId
|
||||
while (requiredLoginMethod != LOGIN_METHOD_NOT_NEEDED) {
|
||||
loginMethods.singleOrNull { it.loginMethodId == requiredLoginMethod }?.let {
|
||||
if (requiredLoginMethod != null)
|
||||
targetLoginMethodIds.add(requiredLoginMethod!!)
|
||||
requiredLoginMethod = it.requiredLoginMethod(profile, loginStore)
|
||||
}
|
||||
}
|
||||
|
||||
// sort and distinct every login method
|
||||
targetLoginMethodIds = targetLoginMethodIds.toHashSet().toMutableList()
|
||||
targetLoginMethodIds.sort()
|
||||
|
||||
progressCount = 0
|
||||
progressStep = 0f
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-9-21.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2
|
||||
package pl.szczodrzynski.edziennik.data.api
|
||||
|
||||
/*const val CODE_OTHER = 0
|
||||
const val CODE_OK = 1
|
||||
@ -31,6 +31,9 @@ const val CODE_SYNERGIA_NOT_ACTIVATED = 32
|
||||
const val CODE_LIBRUS_DISCONNECTED = 31
|
||||
const val CODE_PROFILE_ARCHIVED = 30*/
|
||||
|
||||
const val ERROR_APP_CRASH = 1
|
||||
const val ERROR_MESSAGE_NOT_SENT = 10
|
||||
|
||||
const val ERROR_REQUEST_FAILURE = 50
|
||||
const val ERROR_REQUEST_HTTP_400 = 51
|
||||
const val ERROR_REQUEST_HTTP_401 = 52
|
||||
@ -38,13 +41,17 @@ const val ERROR_REQUEST_HTTP_403 = 53
|
||||
const val ERROR_REQUEST_HTTP_404 = 54
|
||||
const val ERROR_REQUEST_HTTP_405 = 55
|
||||
const val ERROR_REQUEST_HTTP_410 = 56
|
||||
const val ERROR_REQUEST_HTTP_500 = 57
|
||||
const val ERROR_REQUEST_HTTP_424 = 57
|
||||
const val ERROR_REQUEST_HTTP_500 = 58
|
||||
const val ERROR_REQUEST_HTTP_503 = 59
|
||||
const val ERROR_REQUEST_FAILURE_HOSTNAME_NOT_FOUND = 60
|
||||
const val ERROR_REQUEST_FAILURE_TIMEOUT = 61
|
||||
const val ERROR_REQUEST_FAILURE_NO_INTERNET = 62
|
||||
const val ERROR_REQUEST_FAILURE_SSL_ERROR = 63
|
||||
const val ERROR_RESPONSE_EMPTY = 100
|
||||
const val ERROR_LOGIN_DATA_MISSING = 101
|
||||
const val ERROR_PROFILE_MISSING = 105
|
||||
const val ERROR_PROFILE_ARCHIVED = 106
|
||||
const val ERROR_INVALID_LOGIN_MODE = 110
|
||||
const val ERROR_LOGIN_METHOD_NOT_SATISFIED = 111
|
||||
const val ERROR_NOT_IMPLEMENTED = 112
|
||||
@ -123,6 +130,8 @@ const val ERROR_MOBIDZIENNIK_WEB_NO_SESSION_VALUE = 216
|
||||
const val ERROR_MOBIDZIENNIK_WEB_NO_SERVER_ID = 213
|
||||
const val ERROR_MOBIDZIENNIK_WEB_INVALID_RESPONSE = 214
|
||||
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_NO_SESSION_ID = 215
|
||||
const val ERROR_LOGIN_MOBIDZIENNIK_API2_INVALID_LOGIN = 216
|
||||
const val ERROR_LOGIN_MOBIDZIENNIK_API2_OTHER = 217
|
||||
|
||||
const val ERROR_LOGIN_VULCAN_INVALID_SYMBOL = 301
|
||||
const val ERROR_LOGIN_VULCAN_INVALID_TOKEN = 302
|
||||
@ -157,6 +166,15 @@ const val ERROR_LOGIN_IDZIENNIK_FIRST_NO_SCHOOL_YEAR = 440
|
||||
const val ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA = 441
|
||||
const val ERROR_IDZIENNIK_API_ACCESS_DENIED = 450
|
||||
const val ERROR_IDZIENNIK_API_OTHER = 451
|
||||
const val ERROR_IDZIENNIK_API_NO_REGISTER = 452
|
||||
const val ERROR_IDZIENNIK_WEB_RECIPIENT_LIST_NO_PERMISSION = 453
|
||||
|
||||
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_TEMPLATE_WEB_OTHER = 801
|
||||
|
||||
@ -175,5 +193,7 @@ const val EXCEPTION_LIBRUS_MESSAGES_REQUEST = 911
|
||||
const val EXCEPTION_IDZIENNIK_WEB_REQUEST = 912
|
||||
const val EXCEPTION_IDZIENNIK_WEB_API_REQUEST = 913
|
||||
const val EXCEPTION_IDZIENNIK_API_REQUEST = 914
|
||||
const val EXCEPTION_EDUDZIENNIK_WEB_REQUEST = 920
|
||||
const val EXCEPTION_EDUDZIENNIK_FILE_REQUEST = 921
|
||||
|
||||
const val LOGIN_NO_ARGUMENTS = 1201
|
@ -2,7 +2,7 @@
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-9-29.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2
|
||||
package pl.szczodrzynski.edziennik.data.api
|
||||
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ANNOUNCEMENTS
|
||||
@ -13,8 +13,8 @@ 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.modules.messages.Message.TYPE_RECEIVED
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_SENT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT
|
||||
|
||||
internal const val FEATURE_TIMETABLE = 1
|
||||
internal const val FEATURE_AGENDA = 2
|
||||
@ -41,6 +41,7 @@ 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,
|
||||
@ -82,4 +83,4 @@ object Features {
|
||||
else -> getAllFeatures()
|
||||
} + getAllNecessary()).sorted()
|
||||
}
|
||||
}
|
||||
}
|
@ -2,19 +2,21 @@
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-9-20.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2
|
||||
package pl.szczodrzynski.edziennik.data.api
|
||||
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik.login.IdziennikLoginApi
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik.login.IdziennikLoginWeb
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.librus.login.LibrusLoginApi
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.librus.login.LibrusLoginMessages
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.librus.login.LibrusLoginPortal
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.librus.login.LibrusLoginSynergia
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.mobidziennik.login.MobidziennikLoginWeb
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.template.login.TemplateLoginApi
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.template.login.TemplateLoginWeb
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.vulcan.login.VulcanLoginApi
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.login.EdudziennikLoginWeb
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login.IdziennikLoginApi
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login.IdziennikLoginWeb
|
||||
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.template.login.TemplateLoginApi
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.template.login.TemplateLoginWeb
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.VulcanLoginApi
|
||||
import pl.szczodrzynski.edziennik.data.api.models.LoginMethod
|
||||
|
||||
// librus
|
||||
// mobidziennik
|
||||
@ -22,7 +24,7 @@ import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod
|
||||
// vulcan
|
||||
// mobireg
|
||||
|
||||
const val SYNERGIA_API_ENABLED = true
|
||||
const val SYNERGIA_API_ENABLED = false
|
||||
|
||||
|
||||
|
||||
@ -85,11 +87,11 @@ 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 }/*,
|
||||
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED },
|
||||
|
||||
LoginMethod(LOGIN_TYPE_MOBIDZIENNIK, LOGIN_METHOD_MOBIDZIENNIK_API2, MobidziennikLoginApi2::class.java)
|
||||
.withIsPossible { _, loginStore -> loginStore.hasLoginData("email") }
|
||||
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }*/
|
||||
.withIsPossible { profile, _ -> profile?.getStudentData("email", null) != null }
|
||||
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }
|
||||
)
|
||||
|
||||
const val LOGIN_TYPE_VULCAN = 4
|
||||
@ -130,6 +132,14 @@ val idziennikLoginMethods = listOf(
|
||||
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_IDZIENNIK_WEB }
|
||||
)
|
||||
|
||||
const val LOGIN_TYPE_EDUDZIENNIK = 5
|
||||
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 }
|
||||
)
|
||||
|
||||
val templateLoginMethods = listOf(
|
||||
LoginMethod(LOGIN_TYPE_TEMPLATE, LOGIN_METHOD_TEMPLATE_WEB, TemplateLoginWeb::class.java)
|
||||
.withIsPossible { _, _ -> true }
|
175
app/src/main/java/pl/szczodrzynski/edziennik/data/api/Regexes.kt
Normal file
175
app/src/main/java/pl/szczodrzynski/edziennik/data/api/Regexes.kt
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-10-6.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api
|
||||
|
||||
import kotlin.text.RegexOption.DOT_MATCHES_ALL
|
||||
|
||||
object Regexes {
|
||||
val STYLE_CSS_COLOR by lazy {
|
||||
"""color: (\w+);?""".toRegex()
|
||||
}
|
||||
|
||||
|
||||
|
||||
val MOBIDZIENNIK_GRADES_SUBJECT_NAME by lazy {
|
||||
"""<div.*?>\n*\s*(.+?)\s*\n*(?:<.*?)??</div>""".toRegex(DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_GRADES_COLOR by lazy {
|
||||
"""background-color:([#A-Fa-f0-9]+);""".toRegex(DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_GRADES_CATEGORY by lazy {
|
||||
"""> (.+?):</span>""".toRegex(DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_GRADES_CLASS_AVERAGE by lazy {
|
||||
"""Średnia ocen:.*<strong>([0-9]*\.?[0-9]*)</strong>""".toRegex(DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_GRADES_ADDED_DATE by lazy {
|
||||
"""Wpisano:.*<strong>.+?,\s([0-9]+)\s(.+?)\s([0-9]{4}),\sgodzina\s([0-9:]+)</strong>""".toRegex(DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_GRADES_COUNT_TO_AVG by lazy {
|
||||
"""Liczona do średniej:.*?<strong>nie<br/?></strong>""".toRegex(DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_GRADES_DETAILS by lazy {
|
||||
"""<strong.*?>(.+?)</strong>.*?<sup>.+?</sup>.*?(?:<small>\((.+?)\)</small>.*?)?<span>.*?Wartość oceny:.*?<strong>([0-9.]+)</strong>.*?Wpisał\(a\):.*?<strong>(.+?)</strong>.*?(?:Komentarz:.*?<strong>(.+?)</strong>)?</span>""".toRegex(DOT_MATCHES_ALL)
|
||||
}
|
||||
|
||||
val MOBIDZIENNIK_EVENT_TYPE by lazy {
|
||||
"""\(([0-9A-ząęóżźńśłć]*?)\)$""".toRegex(DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_LUCKY_NUMBER by lazy {
|
||||
"""class="szczesliwy_numerek".*>0*([0-9]+)(?:/0*[0-9]+)*</a>""".toRegex(DOT_MATCHES_ALL)
|
||||
}
|
||||
val MOBIDZIENNIK_CLASS_CALENDAR by lazy {
|
||||
"""events: (.+),$""".toRegex(RegexOption.MULTILINE)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
val MOBIDZIENNIK_MESSAGE_SENT_READ_DATE by lazy {
|
||||
""".+?,\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_RECIPIENTS_JSON by lazy {
|
||||
"""odbiorcy: (\[.+?]),${'$'}""".toRegex(RegexOption.MULTILINE)
|
||||
}
|
||||
|
||||
val MOBIDZIENNIK_ACCOUNT_EMAIL by lazy {
|
||||
"""name="email" value="(.+?@.+?\..+?)"""".toRegex(DOT_MATCHES_ALL)
|
||||
}
|
||||
|
||||
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
|
||||
|
||||
val VULCAN_SHIFT_ANNOTATION by lazy {
|
||||
"""\(przeniesiona (z|na) lekcj[ię] ([0-9]+), (.+)\)""".toRegex()
|
||||
}
|
||||
|
||||
|
||||
|
||||
val LIBRUS_ATTACHMENT_KEY by lazy {
|
||||
"""singleUseKey=([0-9A-f_]+)""".toRegex()
|
||||
}
|
||||
|
||||
|
||||
|
||||
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_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)
|
||||
}
|
||||
}
|
@ -1,19 +1,28 @@
|
||||
package pl.szczodrzynski.edziennik.api.v2.task
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-1-16.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.api.v2.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik.Idziennik
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.librus.Librus
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.mobidziennik.Mobidziennik
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.template.Template
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.vulcan.Vulcan
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.Edudziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.Idziennik
|
||||
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.template.Template
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.Vulcan
|
||||
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.api.task.IApiTask
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
|
||||
open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTask(profileId) {
|
||||
companion object {
|
||||
@ -24,8 +33,11 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
||||
fun syncProfile(profileId: Int, viewIds: List<Pair<Int, Int>>? = null, arguments: JsonObject? = null) = EdziennikTask(profileId, SyncProfileRequest(viewIds, arguments))
|
||||
fun syncProfileList(profileList: List<Int>) = EdziennikTask(-1, SyncProfileListRequest(profileList))
|
||||
fun messageGet(profileId: Int, message: MessageFull) = EdziennikTask(profileId, MessageGetRequest(message))
|
||||
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 announcementGet(profileId: Int, announcement: AnnouncementFull) = EdziennikTask(profileId, AnnouncementGetRequest(announcement))
|
||||
fun attachmentGet(profileId: Int, message: Message, attachmentId: Long, attachmentName: String) = EdziennikTask(profileId, AttachmentGetRequest(message, attachmentId, attachmentName))
|
||||
fun recipientListGet(profileId: Int) = EdziennikTask(profileId, RecipientListGetRequest())
|
||||
}
|
||||
|
||||
private lateinit var loginStore: LoginStore
|
||||
@ -39,11 +51,8 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
||||
taskName = app.getString(R.string.edziennik_notification_api_first_login_title)
|
||||
} else {
|
||||
// get the requested profile and login store
|
||||
val profile = app.db.profileDao().getFullByIdNow(profileId)
|
||||
val profile = app.db.profileDao().getByIdNow(profileId) ?: return
|
||||
this.profile = profile
|
||||
if (profile == null) {
|
||||
return
|
||||
}
|
||||
val loginStore = app.db.loginStoreDao().getByIdNow(profile.loginStoreId) ?: return
|
||||
this.loginStore = loginStore
|
||||
// save the profile ID and name as the current task's
|
||||
@ -54,11 +63,16 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
||||
private var edziennikInterface: EdziennikInterface? = null
|
||||
|
||||
internal fun run(app: App, taskCallback: EdziennikCallback) {
|
||||
if (profile?.archived == true) {
|
||||
taskCallback.onError(ApiError(TAG, ERROR_PROFILE_ARCHIVED))
|
||||
return
|
||||
}
|
||||
edziennikInterface = when (loginStore.type) {
|
||||
LOGIN_TYPE_LIBRUS -> Librus(app, profile, loginStore, taskCallback)
|
||||
LOGIN_TYPE_MOBIDZIENNIK -> Mobidziennik(app, profile, loginStore, taskCallback)
|
||||
LOGIN_TYPE_VULCAN -> Vulcan(app, profile, loginStore, taskCallback)
|
||||
LOGIN_TYPE_IDZIENNIK -> Idziennik(app, profile, loginStore, taskCallback)
|
||||
LOGIN_TYPE_EDUDZIENNIK -> Edudziennik(app, profile, loginStore, taskCallback)
|
||||
LOGIN_TYPE_TEMPLATE -> Template(app, profile, loginStore, taskCallback)
|
||||
else -> null
|
||||
}
|
||||
@ -73,9 +87,12 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
||||
viewId = request.viewIds?.get(0)?.first,
|
||||
arguments = request.arguments)
|
||||
is MessageGetRequest -> edziennikInterface?.getMessage(request.message)
|
||||
is MessageSendRequest -> edziennikInterface?.sendMessage(request.recipients, request.subject, request.text)
|
||||
is FirstLoginRequest -> edziennikInterface?.firstLogin()
|
||||
is AnnouncementsReadRequest -> edziennikInterface?.markAllAnnouncementsAsRead()
|
||||
is AnnouncementGetRequest -> edziennikInterface?.getAnnouncement(request.announcement)
|
||||
is AttachmentGetRequest -> edziennikInterface?.getAttachment(request.message, request.attachmentId, request.attachmentName)
|
||||
is RecipientListGetRequest -> edziennikInterface?.getRecipientList()
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,6 +109,9 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
||||
data class SyncProfileRequest(val viewIds: List<Pair<Int, Int>>? = null, val arguments: JsonObject? = null)
|
||||
data class SyncProfileListRequest(val profileList: List<Int>)
|
||||
data class MessageGetRequest(val message: MessageFull)
|
||||
data class MessageSendRequest(val recipients: List<Teacher>, val subject: String, val text: String)
|
||||
class AnnouncementsReadRequest
|
||||
data class AnnouncementGetRequest(val announcement: AnnouncementFull)
|
||||
data class AttachmentGetRequest(val message: Message, val attachmentId: Long, val attachmentName: String)
|
||||
class RecipientListGetRequest
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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.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.Message
|
||||
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.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?, arguments: JsonObject?) {
|
||||
data.arguments = arguments
|
||||
data.prepare(edudziennikLoginMethods, EdudziennikFeatures, featureIds, viewId)
|
||||
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(message: Message, attachmentId: Long, attachmentName: String) {}
|
||||
override fun getRecipientList() {}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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))
|
||||
)
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
useEndpoint(data.targetEndpointIds.removeAt(0)) {
|
||||
data.progress(data.progressStep)
|
||||
nextEndpoint(onSuccess)
|
||||
}
|
||||
}
|
||||
|
||||
private fun useEndpoint(endpointId: Int, onSuccess: () -> Unit) {
|
||||
Utils.d(TAG, "Using endpoint $endpointId")
|
||||
when (endpointId) {
|
||||
ENDPOINT_EDUDZIENNIK_WEB_START -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_data)
|
||||
EdudziennikWebStart(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_EDUDZIENNIK_WEB_TEACHERS -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_teachers)
|
||||
EdudziennikWebTeachers(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_EDUDZIENNIK_WEB_GRADES -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_grades)
|
||||
EdudziennikWebGrades(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_EDUDZIENNIK_WEB_TIMETABLE -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_timetable)
|
||||
EdudziennikWebTimetable(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_EDUDZIENNIK_WEB_EXAMS -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_exams)
|
||||
EdudziennikWebExams(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_attendance)
|
||||
EdudziennikWebAttendance(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_EDUDZIENNIK_WEB_ANNOUNCEMENTS -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_announcements)
|
||||
EdudziennikWebAnnouncements(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_EDUDZIENNIK_WEB_HOMEWORK -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_homework)
|
||||
EdudziennikWebHomework(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_EDUDZIENNIK_WEB_EVENTS -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_events)
|
||||
EdudziennikWebEvents(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_EDUDZIENNIK_WEB_NOTES -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_notices)
|
||||
EdudziennikWebNotes(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_lucky_number)
|
||||
EdudziennikWebLuckyNumber(data, onSuccess)
|
||||
}
|
||||
else -> onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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 okhttp3.Cookie
|
||||
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) {
|
||||
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.getForDomain("dziennikel.appspot.com")
|
||||
val semesterCookie = cookies.firstOrNull { it.name() == "semester" }?.value()?.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.saveFromResponse(null, listOf(
|
||||
Cookie.Builder()
|
||||
.name("sessionid")
|
||||
.value(data.webSessionId!!)
|
||||
.domain("dziennikel.appspot.com")
|
||||
.secure().httpOnly().build()
|
||||
))
|
||||
|
||||
Request.builder()
|
||||
.url(url)
|
||||
.userAgent(EDUDZIENNIK_USER_AGENT)
|
||||
.apply {
|
||||
if (xhr) header("X-Requested-With", "XMLHttpRequest")
|
||||
}
|
||||
.get()
|
||||
.callback(callback)
|
||||
.build()
|
||||
.enqueue()
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.get
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class EdudziennikWebAnnouncements(override val data: DataEdudziennik,
|
||||
val onSuccess: () -> Unit) : EdudziennikWeb(data) {
|
||||
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,
|
||||
id,
|
||||
subject,
|
||||
null,
|
||||
startDate,
|
||||
null,
|
||||
teacher.id,
|
||||
longId
|
||||
)
|
||||
|
||||
data.announcementIgnoreList.add(announcementObject)
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_ANNOUNCEMENT,
|
||||
id,
|
||||
profile.empty,
|
||||
profile.empty,
|
||||
addedDate
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_ANNOUNCEMENTS, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
} ?: onSuccess() }
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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,
|
||||
val onSuccess: () -> Unit) : EdudziennikWeb(data) {
|
||||
companion object {
|
||||
private const val TAG = "EdudziennikWebAttendance"
|
||||
}
|
||||
|
||||
init { data.profile?.also { profile ->
|
||||
webGet(TAG, data.studentEndpoint + "Presence", semester = -1) { 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()
|
||||
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_CUSTOM
|
||||
}
|
||||
)
|
||||
} ?: 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().getForDateNow(profileId, date)
|
||||
val lesson = lessons.firstOrNull { it.lessonNumber == lessonNumber }
|
||||
|
||||
val id = "${date.stringY_m_d}:$lessonNumber:$attendanceSymbol".crc32()
|
||||
|
||||
val (_, name, type) = attendanceTypes.firstOrNull { (symbol, _, _) -> symbol == attendanceSymbol }
|
||||
?: return@forEach
|
||||
|
||||
val startTime = data.lessonRanges.singleOrNull { it.lessonNumber == lessonNumber }?.startTime
|
||||
?: return@forEach
|
||||
|
||||
val attendanceObject = Attendance(
|
||||
profileId,
|
||||
id,
|
||||
lesson?.displayTeacherId ?: -1,
|
||||
lesson?.displaySubjectId ?: -1,
|
||||
profile.currentSemester,
|
||||
name,
|
||||
date,
|
||||
lesson?.displayStartTime ?: startTime,
|
||||
type
|
||||
)
|
||||
|
||||
data.attendanceList.add(attendanceObject)
|
||||
if(type != Attendance.TYPE_PRESENT) {
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_ATTENDANCE,
|
||||
id,
|
||||
profile.empty,
|
||||
profile.empty,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
} ?: onSuccess() }
|
||||
}
|
@ -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.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.get
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class EdudziennikWebEvents(override val data: DataEdudziennik,
|
||||
val onSuccess: () -> Unit) : EdudziennikWeb(data) {
|
||||
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,
|
||||
id,
|
||||
date,
|
||||
null,
|
||||
title,
|
||||
-1,
|
||||
Event.TYPE_CLASS_EVENT,
|
||||
false,
|
||||
-1,
|
||||
-1,
|
||||
data.teamClass?.id ?: -1
|
||||
)
|
||||
|
||||
data.eventList.add(eventObject)
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_EVENT,
|
||||
id,
|
||||
profile.empty,
|
||||
profile.empty,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
|
||||
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_CLASS_EVENT))
|
||||
|
||||
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_EVENTS, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
} ?: onSuccess() }
|
||||
}
|
@ -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,
|
||||
val onSuccess: () -> Unit) : EdudziennikWeb(data) {
|
||||
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().getForDateNow(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,
|
||||
id,
|
||||
date,
|
||||
startTime,
|
||||
topic,
|
||||
-1,
|
||||
eventType.id,
|
||||
false,
|
||||
-1,
|
||||
subject.id,
|
||||
data.teamClass?.id ?: -1
|
||||
)
|
||||
|
||||
data.eventList.add(eventObject)
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_EVENT,
|
||||
id,
|
||||
profile.empty,
|
||||
profile.empty,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
|
||||
data.toRemove.add(DataRemoveModel.Events.futureExceptTypes(listOf(
|
||||
Event.TYPE_HOMEWORK,
|
||||
Event.TYPE_CLASS_EVENT
|
||||
)))
|
||||
|
||||
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_EXAMS, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
}}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,225 @@
|
||||
/*
|
||||
* 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.*
|
||||
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,
|
||||
val onSuccess: () -> Unit) : EdudziennikWeb(data) {
|
||||
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 ""
|
||||
|
||||
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,
|
||||
id,
|
||||
fullName,
|
||||
color,
|
||||
description,
|
||||
name,
|
||||
value,
|
||||
if (gradeCountToAverage) weight else 0f,
|
||||
semester,
|
||||
teacher.id,
|
||||
subject.id
|
||||
).apply {
|
||||
type = gradeType
|
||||
}
|
||||
|
||||
data.gradeList.add(gradeObject)
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_GRADE,
|
||||
id,
|
||||
profile.empty,
|
||||
profile.empty,
|
||||
addedDate
|
||||
))
|
||||
}
|
||||
|
||||
val proposed = subjectElement.select(".proposal").firstOrNull()?.text()?.trim()
|
||||
|
||||
if (proposed != null && proposed.isNotBlank()) {
|
||||
val proposedGradeObject = Grade(
|
||||
profileId,
|
||||
(-1 * subject.id) - 1,
|
||||
"",
|
||||
-1,
|
||||
"",
|
||||
proposed,
|
||||
proposed.toFloatOrNull() ?: 0f,
|
||||
0f,
|
||||
semester,
|
||||
-1,
|
||||
subject.id
|
||||
).apply {
|
||||
type = when (semester) {
|
||||
1 -> TYPE_SEMESTER1_PROPOSED
|
||||
else -> TYPE_SEMESTER2_PROPOSED
|
||||
}
|
||||
}
|
||||
|
||||
data.gradeList.add(proposedGradeObject)
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_GRADE,
|
||||
proposedGradeObject.id,
|
||||
profile.empty,
|
||||
profile.empty,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
|
||||
val final = subjectElement.select(".final").firstOrNull()?.text()?.trim()
|
||||
|
||||
if (final != null && final.isNotBlank()) {
|
||||
val finalGradeObject = Grade(
|
||||
profileId,
|
||||
(-1 * subject.id) - 2,
|
||||
"",
|
||||
-1,
|
||||
"",
|
||||
final,
|
||||
final.toFloatOrNull() ?: 0f,
|
||||
0f,
|
||||
semester,
|
||||
-1,
|
||||
subject.id
|
||||
).apply {
|
||||
type = when (semester) {
|
||||
1 -> TYPE_SEMESTER1_FINAL
|
||||
else -> TYPE_SEMESTER2_FINAL
|
||||
}
|
||||
}
|
||||
|
||||
data.gradeList.add(finalGradeObject)
|
||||
data.metadataList.add(Metadata(
|
||||
data.profileId,
|
||||
Metadata.TYPE_GRADE,
|
||||
finalGradeObject.id,
|
||||
profile.empty,
|
||||
profile.empty,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
} ?: onSuccess() }
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.get
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class EdudziennikWebHomework(override val data: DataEdudziennik,
|
||||
val onSuccess: () -> Unit) : EdudziennikWeb(data) {
|
||||
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 id = EDUDZIENNIK_HOMEWORK_ID.find(dateElement.attr("href"))?.get(1)?.crc32()
|
||||
?: return@forEach
|
||||
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().getForDateNow(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()
|
||||
|
||||
val eventObject = Event(
|
||||
profileId,
|
||||
id,
|
||||
date,
|
||||
startTime,
|
||||
topic,
|
||||
-1,
|
||||
Event.TYPE_HOMEWORK,
|
||||
false,
|
||||
teacher.id,
|
||||
subject.id,
|
||||
data.teamClass?.id ?: -1
|
||||
)
|
||||
|
||||
data.eventList.add(eventObject)
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_HOMEWORK,
|
||||
id,
|
||||
profile.empty,
|
||||
profile.empty,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_HOMEWORK))
|
||||
|
||||
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_HOMEWORK, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
} ?: onSuccess() }
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.LuckyNumber
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class EdudziennikWebLuckyNumber(override val data: DataEdudziennik,
|
||||
val onSuccess: () -> Unit) : EdudziennikWeb(data) {
|
||||
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,
|
||||
Date.getToday(),
|
||||
luckyNumber
|
||||
)
|
||||
|
||||
data.luckyNumberList.add(luckyNumberObject)
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_LUCKY_NUMBER,
|
||||
luckyNumberObject.date.value.toLong(),
|
||||
true,
|
||||
profile.empty,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
|
||||
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
} ?: onSuccess() }
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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,
|
||||
val onSuccess: () -> Unit) : EdudziennikWeb(data) {
|
||||
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,
|
||||
id,
|
||||
description,
|
||||
profile.currentSemester,
|
||||
Notice.TYPE_NEUTRAL,
|
||||
teacher.id
|
||||
)
|
||||
|
||||
data.noticeList.add(noticeObject)
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_NOTICE,
|
||||
id,
|
||||
profile.empty,
|
||||
profile.empty,
|
||||
addedDate
|
||||
))
|
||||
}
|
||||
|
||||
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_NOTES, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
} ?: onSuccess() }
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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,
|
||||
val onSuccess: () -> Unit) : EdudziennikWeb(data) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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,
|
||||
val onSuccess: () -> Unit) : EdudziennikWeb(data) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.LessonRange
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
|
||||
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,
|
||||
val onSuccess: () -> Unit) : EdudziennikWeb(data) {
|
||||
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,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
} ?: onSuccess() }
|
||||
}
|
@ -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)
|
||||
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().post(FirstLoginFinishedEvent(profileList, data.loginStore))
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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.clearForDomain("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.getForDomain("dziennikel.appspot.com")
|
||||
val sessionId = cookies.firstOrNull { it.name() == "sessionid" }?.value()
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
@ -2,18 +2,18 @@
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-10-25.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik
|
||||
|
||||
import androidx.core.util.set
|
||||
import okhttp3.Cookie
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_IDZIENNIK_API
|
||||
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_IDZIENNIK_WEB
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.Data
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject
|
||||
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_IDZIENNIK_API
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_IDZIENNIK_WEB
|
||||
import pl.szczodrzynski.edziennik.data.api.models.Data
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Subject
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||
|
||||
class DataIdziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) {
|
||||
|
||||
@ -41,6 +41,8 @@ class DataIdziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data(
|
||||
loginMethods += LOGIN_METHOD_IDZIENNIK_API
|
||||
}
|
||||
|
||||
override fun generateUserCode() = "$webSchoolName:$webUsername:$registerId"
|
||||
|
||||
private var mLoginExpiryTime: Long? = null
|
||||
var loginExpiryTime: Long
|
||||
get() { mLoginExpiryTime = mLoginExpiryTime ?: loginStore.getLoginData("loginExpiryTime", 0L); return mLoginExpiryTime ?: 0L }
|
||||
@ -128,7 +130,8 @@ class DataIdziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data(
|
||||
subjectList.singleOrNull { it.id == id }
|
||||
|
||||
if (subject == null) {
|
||||
subject = Subject(profileId, id ?: name.crc16().toLong(), name, shortName)
|
||||
subject = Subject(profileId, id
|
||||
?: name.crc16().toLong(), name, shortName)
|
||||
subjectList[subject.id] = subject
|
||||
}
|
||||
return subject
|
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-10-25.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikData
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebGetAttachment
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebGetMessage
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebGetRecipientList
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebSendMessage
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.firstlogin.IdziennikFirstLogin
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login.IdziennikLogin
|
||||
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.Message
|
||||
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.MessageFull
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
|
||||
companion object {
|
||||
private const val TAG = "Idziennik"
|
||||
}
|
||||
|
||||
val internalErrorList = mutableListOf<Int>()
|
||||
val data: DataIdziennik
|
||||
private var afterLogin: (() -> Unit)? = null
|
||||
|
||||
init {
|
||||
data = DataIdziennik(app, profile, loginStore).apply {
|
||||
callback = wrapCallback(this@Idziennik.callback)
|
||||
satisfyLoginMethods()
|
||||
}
|
||||
}
|
||||
|
||||
private fun completed() {
|
||||
data.saveData()
|
||||
callback.onCompleted()
|
||||
}
|
||||
|
||||
/* _______ _ _ _ _ _
|
||||
|__ __| | /\ | | (_) | | |
|
||||
| | | |__ ___ / \ | | __ _ ___ _ __ _| |_| |__ _ __ ___
|
||||
| | | '_ \ / _ \ / /\ \ | |/ _` |/ _ \| '__| | __| '_ \| '_ ` _ \
|
||||
| | | | | | __/ / ____ \| | (_| | (_) | | | | |_| | | | | | | | |
|
||||
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
|
||||
__/ |
|
||||
|__*/
|
||||
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
|
||||
data.arguments = arguments
|
||||
data.prepare(idziennikLoginMethods, IdziennikFeatures, featureIds, viewId)
|
||||
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(idziennikLoginMethods, it) }
|
||||
afterLogin?.let { this.afterLogin = it }
|
||||
IdziennikLogin(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() ?: IdziennikData(data) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMessage(message: MessageFull) {
|
||||
login(LOGIN_METHOD_IDZIENNIK_WEB) {
|
||||
IdziennikWebGetMessage(data, message) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun sendMessage(recipients: List<Teacher>, subject: String, text: String) {
|
||||
login(LOGIN_METHOD_IDZIENNIK_API) {
|
||||
IdziennikWebSendMessage(data, recipients, subject, text) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun markAllAnnouncementsAsRead() {}
|
||||
override fun getAnnouncement(announcement: AnnouncementFull) {}
|
||||
|
||||
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
|
||||
login(LOGIN_METHOD_IDZIENNIK_WEB) {
|
||||
IdziennikWebGetAttachment(data, message, attachmentId, attachmentName) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRecipientList() {
|
||||
login(LOGIN_METHOD_IDZIENNIK_WEB) {
|
||||
IdziennikWebGetRecipientList(data) {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun firstLogin() { IdziennikFirstLogin(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_LOGIN_IDZIENNIK_WEB_NO_SESSION,
|
||||
ERROR_LOGIN_IDZIENNIK_WEB_NO_AUTH,
|
||||
ERROR_LOGIN_IDZIENNIK_WEB_NO_BEARER,
|
||||
ERROR_IDZIENNIK_WEB_ACCESS_DENIED,
|
||||
ERROR_IDZIENNIK_API_ACCESS_DENIED -> {
|
||||
data.loginMethods.remove(LOGIN_METHOD_IDZIENNIK_WEB)
|
||||
data.prepareFor(idziennikLoginMethods, LOGIN_METHOD_IDZIENNIK_WEB)
|
||||
data.loginExpiryTime = 0
|
||||
login()
|
||||
}
|
||||
ERROR_IDZIENNIK_API_NO_REGISTER -> {
|
||||
data()
|
||||
}
|
||||
else -> callback.onError(apiError)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,10 +2,10 @@
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-10-25.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik
|
||||
|
||||
import pl.szczodrzynski.edziennik.api.v2.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.Feature
|
||||
import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.models.Feature
|
||||
|
||||
const val ENDPOINT_IDZIENNIK_WEB_TIMETABLE = 1030
|
||||
const val ENDPOINT_IDZIENNIK_WEB_GRADES = 1040
|
@ -2,7 +2,7 @@
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-10-29.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik.data
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data
|
||||
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
@ -11,9 +11,9 @@ import com.google.gson.JsonParser
|
||||
import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||
import pl.szczodrzynski.edziennik.api.v2.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik.DataIdziennik
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.getString
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import java.net.HttpURLConnection
|
||||
@ -55,6 +55,7 @@ open class IdziennikApi(open val data: DataIdziennik) {
|
||||
}
|
||||
error?.let { code ->
|
||||
when (code) {
|
||||
"Uczeń nie posiada aktywnej pozycji w dzienniku" -> ERROR_IDZIENNIK_API_NO_REGISTER
|
||||
"Authorization has been denied for this request." -> ERROR_IDZIENNIK_API_ACCESS_DENIED
|
||||
else -> ERROR_IDZIENNIK_API_OTHER
|
||||
}.let { errorCode ->
|
||||
@ -107,6 +108,7 @@ open class IdziennikApi(open val data: DataIdziennik) {
|
||||
}
|
||||
}
|
||||
}
|
||||
.allowErrorCode(HttpURLConnection.HTTP_BAD_REQUEST)
|
||||
.allowErrorCode(HttpURLConnection.HTTP_UNAUTHORIZED)
|
||||
.allowErrorCode(HttpURLConnection.HTTP_INTERNAL_ERROR)
|
||||
.callback(callback)
|
@ -2,14 +2,14 @@
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-10-25.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik.data
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data
|
||||
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik.data.api.IdziennikApiCurrentRegister
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik.data.api.IdziennikApiMessagesInbox
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik.data.api.IdziennikApiMessagesSent
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik.data.web.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.api.IdziennikApiCurrentRegister
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.api.IdziennikApiMessagesInbox
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.api.IdziennikApiMessagesSent
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.*
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
|
||||
class IdziennikData(val data: DataIdziennik, val onSuccess: () -> Unit) {
|
@ -2,18 +2,20 @@
|
||||
* Copyright (c) Kuba Szczodrzyński 2019-10-25.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik.data
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data
|
||||
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonObject
|
||||
import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.callback.FileCallbackHandler
|
||||
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
||||
import im.wangchao.mhttp.callback.TextCallbackHandler
|
||||
import pl.szczodrzynski.edziennik.api.v2.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.edziennik.idziennik.DataIdziennik
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import java.io.File
|
||||
import java.net.HttpURLConnection.HTTP_INTERNAL_ERROR
|
||||
import java.net.HttpURLConnection.HTTP_UNAUTHORIZED
|
||||
|
||||
@ -39,6 +41,13 @@ open class IdziennikWeb(open val data: DataIdziennik) {
|
||||
return
|
||||
}
|
||||
|
||||
if (response?.code() == HTTP_INTERNAL_ERROR && endpoint == IDZIENNIK_WEB_GET_RECIPIENT_LIST) {
|
||||
data.error(ApiError(tag, ERROR_IDZIENNIK_WEB_RECIPIENT_LIST_NO_PERMISSION)
|
||||
.withResponse(response)
|
||||
.withApiResponse(json))
|
||||
return
|
||||
}
|
||||
|
||||
when {
|
||||
response?.code() == HTTP_UNAUTHORIZED -> ERROR_IDZIENNIK_WEB_ACCESS_DENIED
|
||||
response?.code() == HTTP_INTERNAL_ERROR -> ERROR_IDZIENNIK_WEB_SERVER_ERROR
|
||||
@ -156,4 +165,54 @@ open class IdziennikWeb(open val data: DataIdziennik) {
|
||||
.build()
|
||||
.enqueue()
|
||||
}
|
||||
|
||||
fun webGetFile(tag: String, endpoint: String, targetFile: File, parameters: Map<String, Any>,
|
||||
onSuccess: (file: File) -> Unit, onProgress: (written: Long, total: Long) -> Unit) {
|
||||
|
||||
d(tag, "Request: Idziennik/Web - $IDZIENNIK_WEB_URL/$endpoint")
|
||||
|
||||
val callback = object : FileCallbackHandler(targetFile) {
|
||||
override fun onSuccess(file: File?, response: Response?) {
|
||||
if (file == null) {
|
||||
data.error(ApiError(TAG, ERROR_FILE_DOWNLOAD)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
onSuccess(file)
|
||||
} catch (e: Exception) {
|
||||
data.error(ApiError(tag, EXCEPTION_EDUDZIENNIK_FILE_REQUEST)
|
||||
.withResponse(response)
|
||||
.withThrowable(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onProgress(bytesWritten: Long, bytesTotal: Long) {
|
||||
try {
|
||||
onProgress(bytesWritten, bytesTotal)
|
||||
} catch (e: Exception) {
|
||||
data.error(ApiError(tag, EXCEPTION_EDUDZIENNIK_FILE_REQUEST)
|
||||
.withThrowable(e))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(response: Response?, throwable: Throwable?) {
|
||||
data.error(ApiError(tag, ERROR_REQUEST_FAILURE)
|
||||
.withResponse(response)
|
||||
.withThrowable(throwable))
|
||||
}
|
||||
}
|
||||
|
||||
Request.builder()
|
||||
.url("$IDZIENNIK_WEB_URL/$endpoint")
|
||||
.userAgent(IDZIENNIK_USER_AGENT)
|
||||
.apply {
|
||||
parameters.forEach { (k, v) -> addParameter(k, v) }
|
||||
}
|
||||
.post()
|
||||
.callback(callback)
|
||||
.build()
|
||||
.enqueue()
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user