Compare commits

...

126 Commits

Author SHA1 Message Date
c8c1fe5367 [4.0-beta.8] Update build.gradle and signing. 2020-02-14 22:39:13 +01:00
71128e0244 [Messages/Compose] Fix text layout jumping and scrolling off-screen when typing a long message. 2020-02-14 22:28:58 +01:00
453bcaa1f6 [Dialog/Day] Show lesson changes and teacher absences in the day dialog. 2020-02-13 23:04:29 +01:00
48898ab1d4 [Widgets] Fix profile separator text color. 2020-02-13 13:44:20 +01:00
a095520d0d [UI/Agenda] Fix subject, teacher and time display in all day events. 2020-02-13 13:33:04 +01:00
2e0c6fa6a5 [Errors] Add request body in error reporting. 2020-02-13 09:57:59 +01:00
bfbc0861df [Notifications] Disable notifications about past events & timetable changes. 2020-02-12 23:05:03 +01:00
3a500f3f28 [API/Librus] Fix student name not normalized, short name not having a trailing dot (remove legacy code). 2020-02-12 19:20:39 +01:00
df8094c39c [Dialog/LessonChanges] Add a new lesson changes dialog. 2020-02-11 16:34:40 +01:00
448fd0e884 [API/Librus] Fix marking removed announcements as read. 2020-02-10 23:53:04 +01:00
4717b4549e [Feedback] Fix crashing when null message is received. 2020-02-09 23:03:37 +01:00
57a8d72f1c [Feedback] Fix received messages not displaying for user. 2020-02-09 23:00:01 +01:00
7e57617e04 [Feedback] Update proguard rules for feedback message entity. 2020-02-09 22:03:41 +01:00
37ddd643ac [Feedback] Hide notification when feedback is open. Fix mixing messages when a thread is open. 2020-02-09 15:10:03 +01:00
bcf3fef303 [Widget/Timetable] Fix no lessons text not legible on dark background. 2020-02-09 14:34:38 +01:00
7ac4d24106 [4.0-beta.7] Update build.gradle, signing and changelog. 2020-02-08 23:16:54 +01:00
93e5bce778 [Feedback] Fix showing wrong names, improve messages filtering by device id. 2020-02-08 23:12:32 +01:00
d48beba307 [Notifications] Fix timetable notification not having subject name. 2020-02-08 23:11:59 +01:00
760338496c [Dialog/GenerateBlockTimetable] Add option for showing teacher names. 2020-02-08 01:33:34 +01:00
b52e7a3078 [Database] Remove unnecessary migration. 2020-02-04 20:59:13 +01:00
78c5b6b2a5 [Database] Fix migrations from 3.2.1 to 4.0-beta. 2020-02-04 00:31:56 +01:00
60a3c38951 [API/Vulcan] Add automatic semester date and ID updating. 2020-02-01 21:41:09 +01:00
4763033f24 [4.0-beta.6] Update build.gradle, signing and changelog. 2020-01-28 22:53:52 +01:00
3b0570d21c Revert "[Sync] Lower the priority of sync notification."
This reverts commit 1677be9e6fa30478564df94201ea0f8193d6e5b8.
2020-01-28 22:47:59 +01:00
16bf478d1a [UI/Agenda] Rewrite agenda in Kotlin and add lesson change counters. 2020-01-26 22:20:46 +01:00
5bf181b6d1 [Feedback] Implement notifications. 2020-01-26 22:03:20 +01:00
21b2e5d194 [Feedback] Add new feedback fragment and API. 2020-01-26 20:05:32 +01:00
759afcf3ca [Database/Migrations] Move migrations to files. 2020-01-25 17:23:47 +01:00
d48c7844a4 [UI/Settings] Add grades config dialog to settings fragment. 2020-01-25 13:31:40 +01:00
7d8caa8df7 [API/Librus] Use added by teacher id instead of lesson teacher in attendance. 2020-01-25 12:42:03 +01:00
62f53930da [Dialog/GradesConfig] Add grades view config dialog. 2020-01-22 23:09:07 +01:00
9a45cbb679 [UI] Add keepScreenOn in counter activity and bell sync dialog. 2020-01-22 23:08:23 +01:00
8e5a10f6d8 [API/Mobidziennik] Implement getting email for push registration. 2020-01-21 20:44:49 +01:00
10c57d2272 [Sync] Lower the priority of sync notification. 2020-01-20 23:10:35 +01:00
67d4d0f898 [Sync] Fix doubled and dead notifications during sync. 2020-01-20 21:36:53 +01:00
97e0d04842 [API] Partially revert "Include device object in each request." 2020-01-20 21:34:22 +01:00
3ba30ede92 [Sync] Fix sync notification crashing on Oreo+. 2020-01-20 20:26:52 +01:00
1035e411ab [API] Include device object in each request. 2020-01-20 19:43:14 +01:00
d5ae4b7ec9 [Home/Grades] Remove filtering grades by semester. 2020-01-20 19:30:20 +01:00
111d040cf9 [Updates] Fix no update toast not visible. 2020-01-20 19:27:59 +01:00
8cc594d170 [Widget/Timetable] Fix widget crashing with NO_LESSONS item. 2020-01-20 19:27:06 +01:00
d8a8bed68d [4.0-beta.5] Update build.gradle and signing. 2020-01-19 22:42:29 +01:00
eedbd954bd [Updates] Add toast for error while checking and for no updates. 2020-01-19 22:35:12 +01:00
0eb8366027 [Changelog] Fix changelog dialog appearance on Android N+. 2020-01-19 22:29:34 +01:00
894135104b [4.0-beta.4] Update build.gradle, signing and changelog. 2020-01-19 22:18:20 +01:00
7b2e408efc [Sync] Make sync not possible for archived profiles. Translate some error codes. 2020-01-19 22:17:57 +01:00
e4115c122e [Errors] Add reporting app version name along with the error. 2020-01-19 21:57:17 +01:00
537b16949e [Updates] Fix running update worker in Java. 2020-01-19 21:52:08 +01:00
ca60ceb2a7 [Firebase] Implement handling app updates. 2020-01-19 21:49:58 +01:00
0fad12fea5 [Updates] Change update channel to beta. 2020-01-19 21:39:28 +01:00
6cd2c23aac [Firebase] Implement handling server messages. 2020-01-19 21:31:37 +01:00
512baaa43f [Errors] Include parser error body when reporting HTTP errors. 2020-01-19 20:34:10 +01:00
d097fcc973 [API/Librus] Fix classrooms name short extraction when name contains two spaces. 2020-01-19 20:22:41 +01:00
621dbd459c [API/Vulcan] Fix marking messages as seen. 2020-01-19 20:03:25 +01:00
840ab4b0c4 [Models] Remove unused models and classes. 2020-01-19 19:30:38 +01:00
904be34a87 [Notifications] Fix showing an empty notification where the list is empty. 2020-01-19 19:19:30 +01:00
b7fc6fcc38 [Structure] Refactor App class to Kotlin. Rewrite SzkolnyTask and posting notifications. Remove dependency on AppConfig. Update libraries and gradle. 2020-01-19 19:07:27 +01:00
55c6e40d6d [DB] Convert AppDb to Kotlin. 2020-01-19 18:44:57 +01:00
4dfb015057 [Firebase/Librus] Implement basic push integration. 2020-01-19 01:17:33 +01:00
e40a0ba2bb [Strings] Add missing translations. 2020-01-18 00:44:13 +01:00
fd48f10df9 [Dialog/EventDetails] Show toast when calendar app not found instead of crashing. 2020-01-18 00:26:26 +01:00
6a54e7fef7 [Firebase] Implement Mobidziennik push service. 2020-01-16 09:27:30 +01:00
5c4d6ed140 [API/Edudziennik] Fix getting attendances. 2020-01-15 23:19:33 +01:00
9ed1be3594 [UI/DrawerProfiles] Add button for marking everything as read in every profile. 2020-01-15 22:58:22 +01:00
c5ce582678 [API/Edudziennik] Fix fix for semesters and getting grades on first login. 2020-01-15 22:26:38 +01:00
2050083bce [API/Edudziennik] Fix semesters and getting grades on first login. 2020-01-15 21:19:48 +01:00
92e6bdb562 [API/iDziennik] Fix regex getting school year in first login. 2020-01-13 22:42:30 +01:00
93e70c38b7 [Firebase] Implement base for per-register FCM tasks. 2020-01-12 21:39:06 +01:00
45b96179a5 [DB] Fix timetable migration crashing app. 2020-01-12 21:33:46 +01:00
a29a534a40 [API/Edudziennik] Fix showing notifications for presence attendances. 2020-01-12 19:45:20 +01:00
8e2297359c [Firebase] Implement new custom FCM service. 2020-01-11 19:07:25 +01:00
92ba7248ef [UI] Fix vector drawables crashing on API < 21. 2020-01-11 13:56:09 +01:00
f657d37cbd [DB/Timetable] Fix migration fixing wrong primary key columns. 2020-01-10 22:23:14 +01:00
9e312f60bf [APIService] Fix showing notification with no service running. 2020-01-10 21:34:55 +01:00
85f72b78f7 [DB/Timetable] Fix wrong primary key columns. 2020-01-10 21:33:59 +01:00
40acb67ceb [Errors] Fix Timeout error detection (SocketTimeoutException inherits from InterruptedIOException). 2020-01-10 16:44:54 +01:00
3ae8100bda [4.0-beta.3] Update build.gradle, signing and changelog. 2020-01-10 11:17:42 +01:00
1a3dc41edf [API/Idziennik] Fix error when historical grade has no color. 2020-01-10 11:14:43 +01:00
b111d33b04 [Widget/Config] Disable Unified lucky number widget in config activity. 2020-01-09 21:11:43 +01:00
ea5720d1c8 [API/Edudziennik] Fix grades colors. 2020-01-08 21:12:10 +01:00
53675122c6 [Dialog/EventDetails] Add feature for saving events in calendar app. 2020-01-08 20:51:06 +01:00
4ba7997bc1 [Database] Fix bug with teacher and subject id when sharing event. 2020-01-07 21:37:58 +01:00
19c446d267 [Widgets/LuckyNumber] Implement a widget. Not knowing if it works or not, sorry. 2020-01-07 11:42:51 +01:00
1abb9ac378 [API] Fix config not reading from DB. Do not sync device if not changed. 2020-01-07 10:45:21 +01:00
f9c7492726 [Widget/Timetable] Fix past lessons (today) not displayed. 2020-01-07 10:14:43 +01:00
6ece6ca52a [UI/Counter] Add Bell Sync option in counter activity. 2020-01-07 09:48:24 +01:00
f6a8e9d2fa [4.0-beta.2] Update build.gradle, signing and changelog. 2020-01-06 22:34:46 +01:00
878de34546 [Widgets] Implement new Notifications widget. 2020-01-06 22:26:54 +01:00
7b97ef316d [Structure] Change database file structure. Rewrite converters to Kotlin. 2020-01-06 21:25:34 +01:00
aafa87c661 [Deprecated] Remove deprecated home fragment. 2020-01-06 19:17:28 +01:00
26eb2e4381 [Announcements/Liburs] Fix error on marking as read and make announcement show even when there's no Internet connection. 2020-01-06 18:24:43 +01:00
4b08ea7a89 [Dialog/GenerateBlockTimetable] Fix not showing cancelled lessons. 2020-01-06 17:47:57 +01:00
7f1f2d0039 [API/Mobidziennik] Implement Lesson Ranges. Make Cancelled lesson use old* variables. 2020-01-06 17:30:38 +01:00
0227762ddc [API/DataRemoveModel] Make DAO not remove TYPE_NO_LESSON entries. 2020-01-06 16:54:35 +01:00
ff0de8afc2 [UI/Dialogs] Remove debug toasts. Remove old Event List Dialog. 2020-01-06 16:43:31 +01:00
ddf66ef061 [UI/Dialogs] Fix "No events" text alignment. 2020-01-06 16:36:48 +01:00
52ecfba0a5 [API/Librus] Add Lessons endpoint and showing correct attendance subjects. 2020-01-06 16:27:37 +01:00
e123ff1bec [Timetable] Fix Timetable crashing with Swipe Refresh Layout and no parent. 2020-01-06 15:57:15 +01:00
45753583ee [UI/Login] Fix back button not working on Login Chooser. 2020-01-06 15:56:23 +01:00
5a77c481a2 [Extensions] Make asJsonObjectList return non-nullable JsonObjects. 2020-01-06 15:05:33 +01:00
4e796542d7 [ZXing] Try to fix QR code scanner crashing (Proguard). 2020-01-06 14:53:51 +01:00
ae42c227a8 [API/Idziennik] Add showing error on getting recipient list with no permissions. Translate some error codes. 2020-01-06 14:53:51 +01:00
fc58035bbf [UI/Timetable] Temporarily fix page scrolling issues. 2020-01-06 14:53:51 +01:00
834c4fc5f4 [UI/Attendance] Disable Mobidziennik sync reminder. 2020-01-06 14:53:51 +01:00
33fcffd2bd [API/Vulcan] Fix incorrect Attendance type. 2020-01-06 14:53:51 +01:00
18b83e2ed8 [Database] Remove deprecated Lesson and LessonChange. 2020-01-06 13:32:32 +01:00
f05b39736c [Dialogs/GenerateBlockTimetable] Add new dialog. 2020-01-06 00:11:03 +01:00
31a293c5c0 [Extensions] Add trigger extension instead of using performClick on checkboxes. 2020-01-05 23:42:35 +01:00
1e6952c86a [Utils] Fix time diff. 2020-01-05 23:39:48 +01:00
21ad38d33f [UI/Login] Fix login summary list not showing all profiles. 2020-01-05 18:29:49 +01:00
1589a05a37 [Colors] Update default profile image colors (colorFromName). 2020-01-05 18:29:49 +01:00
3f19e5d465 [Messages] Fix Messages Fragment crash. 2020-01-05 18:29:49 +01:00
5e9bd98bba [API/LuckyNumber] Always set lucky number seen metadata to true. 2020-01-05 16:39:05 +01:00
d626d98421 [API/Edudziennik] Fix error when limited access. Remove timetable not public. 2020-01-05 15:30:15 +01:00
bce74a408c [API/Edudziennik] Save cookie with semester in student data. Change semester date on semester change. 2020-01-04 23:05:36 +01:00
30c5b2d1c9 Refactor Profiles, Login Stores and Login activity (hoping it works). 2020-01-04 22:08:56 +01:00
95a150f7d8 [API/Librus] Show map value in descriptive grades. 2020-01-04 13:43:49 +01:00
45d31d2358 [Grades] Count only proposal and final grades to average with value greater than 0. 2020-01-04 13:43:17 +01:00
fb59dfc677 [API/FakeLibrus] Change http to https protocol. 2020-01-04 13:42:20 +01:00
30303f50ac [API/Edudziennik] Use new getTeacher methods. Add teamId to lessons. 2020-01-04 00:47:03 +01:00
a2fa133831 [Home/TimetableCard] Fix downloading timetable for a specific week. 2020-01-04 00:25:56 +01:00
d735dcea05 [API/Mobidziennik] Use toIntOrNull instead of try catch in the lucky number extractor. 2020-01-04 00:15:56 +01:00
a96fcabba5 [API/Edudziennik] Fix getting grades with null value. 2020-01-03 23:51:36 +01:00
21fd59c196 [API/Edudziennik] Save and use semester cookie instead of currentSemester in profile. 2020-01-03 23:48:50 +01:00
15f126416f [API/Librus] Use normal grade categories in text grades. 2020-01-03 15:23:37 +01:00
7f1f9f81a6 [API/Librus] Remove text grade categories from features. 2020-01-02 21:29:27 +01:00
561 changed files with 11498 additions and 14185 deletions

View File

@ -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'

View File

@ -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
@ -103,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}"
@ -187,6 +188,8 @@ dependencies {
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()

View File

@ -22,13 +22,14 @@
-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.data.db.entity.FeedbackMessage { *; }
-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$* {
@ -58,4 +59,4 @@
-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.** { *; }
-keepclassmembernames class pl.szczodrzynski.edziennik.data.api.szkolny.response.** { *; }

5
app/proguard/zxing.pro Normal file
View 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);
}

View File

@ -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"
@ -15,12 +26,16 @@
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,58 +47,7 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<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>
<!--
__ ___ _ _
\ \ / (_) | | | |
@ -92,71 +56,37 @@
\ /\ / | | (_| | (_| | __/ |_ \__ \
\/ \/ |_|\__,_|\__, |\___|\__||___/
__/ |
|_
|___/
-->
<activity android:name=".widgets.timetable.LessonDialogActivity"
<activity android:name=".ui.widgets.WidgetConfigActivity"
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.CounterActivityOld"
android:theme="@style/AppTheme.Black" />
<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" />
@ -166,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" />
@ -177,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="pl.szczodrzynski.edziennik.data.api.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>

View File

@ -1,4 +1,4 @@
<h3>Wersja 4.0-beta.1, 2020-01-02</h3>
<h3>Wersja 4.0-beta.7, 2020-02-08</h3>
<ul>
<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 &#x1F44F;</li>
@ -6,14 +6,18 @@
<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>
<br>
<br>
@ -22,17 +26,11 @@
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>Widget powiadomień</li>
<li>Terminarz - brak informacji o odwołanych lekcjach w dialogu</li>
<li>Brak generowania blokowego planu lekcji</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>&copy; Kuba Szczodrzyński, Kacper Ziubryniewicz 2020</i>

View File

@ -9,7 +9,7 @@
/*secret password - removed for source code publication*/
static toys AES_IV[16] = {
0x6c, 0x53, 0xa9, 0x71, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
0x89, 0x62, 0x5d, 0x4d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);

View File

@ -1,729 +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.api.szkolny.interceptor.Signing;
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask;
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();
Signing.INSTANCE.getCert(this);
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"
);
if (config.getRunSync()) {
config.setRunSync(false);
EdziennikTask.Companion.sync().enqueue(this);
}
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;
}
}
}

View File

@ -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 { async(Dispatchers.Default) {
if (config.sync.enabled) {
scheduleNext(this@App, false)
} else {
cancelNext(this@App)
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()
}
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
}
}*/
}
}

View File

@ -3,9 +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
@ -16,21 +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
@ -38,22 +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 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 }
@ -69,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 }
@ -77,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 {
@ -196,25 +230,23 @@ fun List<String>.join(delimiter: String): String {
}
fun colorFromName(name: String?): Int {
var crc = (name ?: "").crc16()
crc = (crc and 0xff) or (crc shr 8)
crc %= 16
return when (crc) {
val i = (name ?: "").crc32()
return when ((i / 10 % 16 + 1).toInt()) {
13 -> 0xffF44336
4 -> 0xffF50057
2 -> 0xffD500F9
9 -> 0xff6200EA
5 -> 0xff3F51B5
5 -> 0xffFFAB00
1 -> 0xff304FFE
6 -> 0xff18FFFF
6 -> 0xff40C4FF
14 -> 0xff26A69A
15 -> 0xff4CAF50
15 -> 0xff00C853
7 -> 0xffFFD600
3 -> 0xffFF3D00
8 -> 0xffDD2C00
10 -> 0xff795548
12 -> 0xffBDBDBD
11 -> 0xff78909C
12 -> 0xff2979FF
11 -> 0xffFF6D00
else -> 0xff64DD17
}.toInt()
}
@ -233,10 +265,7 @@ fun colorFromCssName(name: String): Int {
}.toInt()
}
fun MutableList<Profile>.filterOutArchived(): MutableList<Profile> {
this.removeAll { it.archived }
return this
}
fun List<Profile>.filterOutArchived() = this.filter { !it.archived }
fun Activity.isStoragePermissionGranted(): Boolean {
return if (Build.VERSION.SDK_INT >= 23) {
@ -274,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 ->
@ -282,6 +318,21 @@ 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 ->
@ -497,7 +548,7 @@ fun CharSequence.asSpannable(vararg spans: Any, substring: String? = null, ignor
spans.forEach {
spannable.setSpan(it, index, index + substring.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
index = indexOf(substring, startIndex = index + 1, ignoreCase = ignoreCase);
index = indexOf(substring, startIndex = index + 1, ignoreCase = ignoreCase)
}
}
return spannable
@ -602,6 +653,34 @@ fun Bundle(vararg properties: Pair<String, Any?>): Bundle {
}
}
}
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
@ -832,7 +911,7 @@ fun Int.toColorStateList(): ColorStateList {
this
)
return ColorStateList(states, colors);
return ColorStateList(states, colors)
}
fun SpannableStringBuilder.appendText(text: CharSequence): SpannableStringBuilder {
@ -879,3 +958,81 @@ fun String.base64Decode(): ByteArray {
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) }

View File

@ -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,7 +8,6 @@ import android.content.IntentFilter
import android.content.pm.PackageManager
import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable
import android.os.AsyncTask
import android.os.Build
import android.os.Bundle
import android.os.Environment
@ -36,17 +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.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.api.task.EdziennikTask
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata.*
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.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
@ -71,7 +74,7 @@ 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
@ -92,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
@ -107,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
@ -207,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)
@ -225,6 +234,10 @@ class MainActivity : AppCompatActivity() {
}
}
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 }
@ -256,12 +269,21 @@ 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)
@ -321,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)
@ -358,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 {
@ -403,6 +414,7 @@ class MainActivity : AppCompatActivity() {
isStoragePermissionGranted()
SyncWorker.scheduleNext(app)
UpdateWorker.scheduleNext(app)
// APP BACKGROUND
if (app.config.ui.appBackground != null) {
@ -505,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)
}
@ -572,6 +582,13 @@ class MainActivity : AppCompatActivity() {
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
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 {
@ -587,6 +604,7 @@ class MainActivity : AppCompatActivity() {
}
@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
@ -597,10 +615,12 @@ class MainActivity : AppCompatActivity() {
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onApiTaskAllFinishedEvent(event: ApiTaskAllFinishedEvent) {
EventBus.getDefault().removeStickyEvent(event)
swipeRefreshLayout.isRefreshing = false
}
@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
@ -611,7 +631,8 @@ class MainActivity : AppCompatActivity() {
}
@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)
@ -633,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()
@ -676,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) {
@ -709,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 -> {
@ -750,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)
@ -773,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)
}
}
}
@ -802,35 +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) {
@ -975,7 +1014,7 @@ class MainActivity : AppCompatActivity() {
* that something has changed in the bottom sheet.
*/
fun gainAttention() {
if (app.config.ui.bottomSheetOpened || true)
if (app.config.ui.bottomSheetOpened)
return
b.navView.postDelayed({
navView.gainAttentionOnBottomBar()
@ -1024,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()
@ -1093,7 +1131,7 @@ 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()
@ -1101,6 +1139,7 @@ class MainActivity : AppCompatActivity() {
}
}
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()
}

View File

@ -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);
}
}
}

View File

@ -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 }
@ -69,6 +95,11 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
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 {
@ -79,11 +110,11 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
ConfigMigration(app, this)
}
fun getFor(profileId: Int): ProfileConfig {
return profileConfigs[profileId] ?: ProfileConfig(db, profileId, rawEntries)
}
fun forProfile(): ProfileConfig {
return profileConfigs[App.profileId] ?: ProfileConfig(db, App.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) {
}

View File

@ -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 }
}

View File

@ -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 }

View File

@ -45,11 +45,6 @@ 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() }

View File

@ -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?) {

View File

@ -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 }
}
}

View File

@ -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 }
}

View File

@ -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)
}

View File

@ -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 }
}
}

View File

@ -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

View File

@ -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.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.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
}
}}
}

View File

@ -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
}
}}
}

View File

@ -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.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.*
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull
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,17 +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 val allTaskList = mutableListOf<IApiTask>()
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
@ -59,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
@ -131,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
@ -162,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))
@ -214,6 +219,7 @@ class ApiService : Service() {
}
private fun allCompleted() {
serviceClosed = true
EventBus.getDefault().postSticky(ApiTaskAllFinishedEvent())
stopSelf()
}
@ -229,10 +235,12 @@ class ApiService : Service() {
EventBus.getDefault().removeStickyEvent(task)
d(TAG, task.toString())
// fix for duplicated tasks, thank you EventBus
if (task in allTaskList)
return
allTaskList += task
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) {
@ -293,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)
}

View File

@ -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"

View File

@ -1,210 +0,0 @@
package pl.szczodrzynski.edziennik.data.api
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ANNOUNCEMENTS
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ATTENDANCE
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_BEHAVIOUR
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_GRADES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOME
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.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
}
val today = Date.getToday()
val todayValue = today.value
profile.currentSemester = profile.dateToSemester(today)
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())
}
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)
}}
}

View File

@ -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)
}
}

View File

@ -3,9 +3,9 @@ package pl.szczodrzynski.edziennik.data.api
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.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.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) ||
@ -90,7 +91,6 @@ fun Data.prepareFor(loginMethods: List<LoginMethod>, loginMethodId: Int) {
possibleLoginMethods += it.loginMethodId
}
targetEndpointIds.clear()
targetLoginMethodIds.clear()
// check the login method for any dependencies

View File

@ -51,6 +51,7 @@ 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
@ -115,6 +116,7 @@ const val ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN = 179
const val ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN = 180
const val ERROR_LIBRUS_API_MAINTENANCE = 181
const val ERROR_LIBRUS_PORTAL_MAINTENANCE = 182
const val ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM = 183
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN = 201
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD = 202
@ -129,6 +131,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
@ -164,11 +168,11 @@ 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_TIMETABLE_NOT_PUBLIC = 520
const val ERROR_EDUDZIENNIK_WEB_LIMITED_ACCESS = 521
const val ERROR_EDUDZIENNIK_WEB_SESSION_EXPIRED = 522
const val ERROR_EDUDZIENNIK_WEB_TEAM_MISSING = 530

View File

@ -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,

View File

@ -11,6 +11,7 @@ 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
@ -23,7 +24,7 @@ import pl.szczodrzynski.edziennik.data.api.models.LoginMethod
// vulcan
// mobireg
const val SYNERGIA_API_ENABLED = true
const val SYNERGIA_API_ENABLED = false
@ -86,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

View File

@ -8,7 +8,7 @@ import kotlin.text.RegexOption.DOT_MATCHES_ALL
object Regexes {
val STYLE_CSS_COLOR by lazy {
"""color: \w+?;?"?""".toRegex()
"""color: (\w+);?""".toRegex()
}
@ -56,7 +56,11 @@ object Regexes {
}
val MOBIDZIENNIK_MESSAGE_RECIPIENTS_JSON by lazy {
"""odbiorcy: (\[.+?\]),${'$'}""".toRegex(RegexOption.MULTILINE)
"""odbiorcy: (\[.+?]),${'$'}""".toRegex(RegexOption.MULTILINE)
}
val MOBIDZIENNIK_ACCOUNT_EMAIL by lazy {
"""name="email" value="(.+?@.+?\..+?)"""".toRegex(DOT_MATCHES_ALL)
}
@ -74,7 +78,7 @@ object Regexes {
"""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(DOT_MATCHES_ALL)
"""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)

View File

@ -1,4 +1,8 @@
package pl.szczodrzynski.edziennik.data.api.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
@ -12,11 +16,13 @@ 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.db.modules.announcements.AnnouncementFull
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.teachers.Teacher
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 {
@ -45,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
@ -60,6 +63,10 @@ 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)

View File

@ -7,11 +7,7 @@ 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.modules.events.EventType
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.db.entity.*
/**
* Use http://patorjk.com/software/taag/#p=display&f=Big for the ascii art
@ -29,6 +25,8 @@ class DataEdudziennik(app: App, profile: Profile?, loginStore: LoginStore) : Dat
}
}
override fun generateUserCode() = "$schoolName:$loginEmail:${studentId?.crc32()}"
private var mLoginEmail: String? = null
var loginEmail: String?
get() { mLoginEmail = mLoginEmail ?: loginStore.getLoginData("email", null); return mLoginEmail }
@ -76,16 +74,16 @@ class DataEdudziennik(app: App, profile: Profile?, loginStore: LoginStore) : Dat
| | | | __| '_ \ / _ \ '__|
| |__| | |_| | | | __/ |
\____/ \__|_| |_|\___|*/
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 }
private var mTimetableNotPublic: Boolean? = null
var timetableNotPublic: Boolean
get() { mTimetableNotPublic = mTimetableNotPublic ?: profile?.getStudentData("timetableNotPublic", false); return mTimetableNotPublic ?: false }
set(value) { profile?.putStudentData("timetableNotPublic", value) ?: return; mTimetableNotPublic = value }
val studentEndpoint: String
get() = "Students/$studentId/"

View File

@ -15,12 +15,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.login.Edudzienn
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.modules.announcements.AnnouncementFull
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.data.db.modules.teachers.Teacher
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 {
@ -41,9 +41,7 @@ class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStor
private fun completed() {
data.saveData()
data.notify {
callback.onCompleted()
}
callback.onCompleted()
}
/* _______ _ _ _ _ _
@ -124,8 +122,7 @@ class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStor
ERROR_LOGIN_EDUDZIENNIK_WEB_NO_SESSION_ID -> {
login()
}
ERROR_EDUDZIENNIK_WEB_TIMETABLE_NOT_PUBLIC -> {
data.timetableNotPublic = true
ERROR_EDUDZIENNIK_WEB_LIMITED_ACCESS -> {
data()
}
else -> callback.onError(apiError)

View File

@ -12,6 +12,7 @@ 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 {
@ -24,11 +25,11 @@ open class EdudziennikWeb(open val data: DataEdudziennik) {
val profile
get() = data.profile
fun webGet(tag: String, endpoint: String, xhr: Boolean = false, onSuccess: (text: String) -> Unit) {
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")
@ -40,6 +41,18 @@ open class EdudziennikWeb(open val data: DataEdudziennik) {
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) {
@ -67,11 +80,6 @@ open class EdudziennikWeb(open val data: DataEdudziennik) {
.name("sessionid")
.value(data.webSessionId!!)
.domain("dziennikel.appspot.com")
.secure().httpOnly().build(),
Cookie.Builder()
.name("semester")
.value((profile?.currentSemester ?: 1).toString())
.domain("dziennikel.appspot.com")
.secure().httpOnly().build()
))

View File

@ -10,11 +10,10 @@ 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.modules.announcements.Announcement
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
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.splitName
import pl.szczodrzynski.edziennik.utils.models.Date
class EdudziennikWebAnnouncements(override val data: DataEdudziennik,
@ -37,9 +36,7 @@ class EdudziennikWebAnnouncements(override val data: DataEdudziennik,
val subject = titleElement.text()
val teacherName = announcementElement.child(1).text()
val teacher = teacherName.splitName()?.let { (teacherFirstName, teacherLastName) ->
data.getTeacher(teacherFirstName, teacherLastName)
} ?: return@forEach
val teacher = data.getTeacherByFirstLast(teacherName)
val dateString = announcementElement.getElementsByClass("datetime").first().text()
val startDate = Date.fromY_m_d(dateString)

View File

@ -11,9 +11,9 @@ 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.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
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
@ -26,7 +26,7 @@ class EdudziennikWebAttendance(override val data: DataEdudziennik,
}
init { data.profile?.also { profile ->
webGet(TAG, data.studentEndpoint + "Presence") { text ->
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())
@ -77,14 +77,16 @@ class EdudziennikWebAttendance(override val data: DataEdudziennik,
)
data.attendanceList.add(attendanceObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_ATTENDANCE,
id,
profile.empty,
profile.empty,
System.currentTimeMillis()
))
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)

View File

@ -11,9 +11,9 @@ 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.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
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

View File

@ -13,9 +13,9 @@ 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.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
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
@ -60,7 +60,7 @@ class EdudziennikWebExams(override val data: DataEdudziennik,
startTime,
topic,
-1,
eventType.id.toInt(),
eventType.id,
false,
-1,
subject.id,

View File

@ -9,7 +9,7 @@ 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.modules.announcements.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.get
class EdudziennikWebGetAnnouncement(

View File

@ -13,10 +13,10 @@ 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.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.*
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
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
@ -27,10 +27,18 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
private const val TAG = "EdudziennikWebGrades"
}
init { data.profile?.also { profile ->
webGet(TAG, data.studentEndpoint + "start") { text ->
val doc = Jsoup.parse(text)
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 ->
@ -54,10 +62,10 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
subjects.select(".avg-$subjectId .grade-tip > p").first()
.text().split('+').map {
val split = it.split('*')
val weight = split[0].trim().toFloat()
val value = split[1].trim().toFloat()
val value = split[1].trim().toFloatOrNull()
val weight = value?.let { split[0].trim().toFloatOrNull() } ?: 0f
Pair(value, weight)
Pair(value ?: 0f, weight)
}
} else emptyList()
@ -82,8 +90,8 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
val description = columnName + if (comment.isNotBlank()) " - $comment" else ""
val (teacherLastName, teacherFirstName) = info.child(1).text().split(' ')
val teacher = data.getTeacher(teacherFirstName, teacherLastName)
val teacherName = info.child(1).text()
val teacher = data.getTeacherByLastFirst(teacherName)
val addedDate = info.child(2).text().split(' ').let {
val day = it[0].toInt()
@ -107,7 +115,7 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
name,
value,
if (gradeCountToAverage) weight else 0f,
profile.currentSemester,
semester,
teacher.id,
subject.id
).apply {
@ -137,7 +145,7 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
proposed,
proposed.toFloatOrNull() ?: 0f,
0f,
profile.currentSemester,
semester,
-1,
subject.id
).apply {
@ -170,7 +178,7 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
final,
final.toFloatOrNull() ?: 0f,
0f,
profile.currentSemester,
semester,
-1,
subject.id
).apply {
@ -201,12 +209,17 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
TYPE_SEMESTER1_FINAL,
TYPE_SEMESTER2_FINAL
).map {
DataRemoveModel.Grades.semesterWithType(profile.currentSemester, it)
DataRemoveModel.Grades.semesterWithType(semester, it)
})
}
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_GRADES, SYNC_ALWAYS)
onSuccess()
if (profile.empty && requestSemester == 1 && data.currentSemester == 2) {
requestSemester = null
getGrades()
} else {
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_GRADES, SYNC_ALWAYS)
onSuccess()
}
}
}}
} ?: onSuccess() }
}

View File

@ -12,11 +12,10 @@ 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.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
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.splitName
import pl.szczodrzynski.edziennik.utils.models.Date
class EdudziennikWebHomework(override val data: DataEdudziennik,
@ -46,9 +45,7 @@ class EdudziennikWebHomework(override val data: DataEdudziennik,
val startTime = lessons.firstOrNull { it.subjectId == subject.id }?.displayStartTime
val teacherName = homeworkElement.child(2).text()
val teacher = teacherName.splitName()?.let { (teacherFirstName, teacherLastName) ->
data.getTeacher(teacherFirstName, teacherLastName)
} ?: return@forEach
val teacher = data.getTeacherByFirstLast(teacherName)
val topic = homeworkElement.child(4).text()

View File

@ -7,9 +7,9 @@ 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.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.luckynumber.LuckyNumber
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
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,
@ -32,7 +32,7 @@ class EdudziennikWebLuckyNumber(override val data: DataEdudziennik,
profileId,
Metadata.TYPE_LUCKY_NUMBER,
luckyNumberObject.date.value.toLong(),
profile.empty,
true,
profile.empty,
System.currentTimeMillis()
))

View File

@ -10,9 +10,9 @@ 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.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice
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

View File

@ -13,7 +13,7 @@ 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.modules.teams.Team
import pl.szczodrzynski.edziennik.data.db.entity.Team
import pl.szczodrzynski.edziennik.firstLettersName
import pl.szczodrzynski.edziennik.get

View File

@ -5,22 +5,19 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web
import org.jsoup.Jsoup
import pl.szczodrzynski.edziennik.data.api.ERROR_EDUDZIENNIK_WEB_TIMETABLE_NOT_PUBLIC
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.ApiError
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonRange
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
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.splitName
import pl.szczodrzynski.edziennik.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
@ -57,17 +54,7 @@ class EdudziennikWebTimetable(override val data: DataEdudziennik,
val table = doc.select("#Schedule tbody").first()
if (table.text().trim() == "Brak planu lekcji.") {
val today = Date.getToday()
val schoolYearStart = if (today.month >= 9) today.year else today.year - 1
if (weekStart >= Date(schoolYearStart, 9, 1)) {
data.error(ApiError(TAG, ERROR_EDUDZIENNIK_WEB_TIMETABLE_NOT_PUBLIC)
.withApiResponse(text))
onSuccess()
return@webGet
}
} else {
if (!table.text().contains("Brak planu lekcji.")) {
table.children().forEach { row ->
val rowElements = row.children()
@ -108,10 +95,8 @@ class EdudziennikWebTimetable(override val data: DataEdudziennik,
val teacherElement = info[1].child(0)
val teacherLongId = EDUDZIENNIK_TEACHER_ID.find(teacherElement.attr("href"))?.get(1)
val teacherName = teacherElement.text().trim()
teacherName.splitName()?.let { (teacherLastName, teacherFirstName) ->
data.getTeacher(teacherFirstName, teacherLastName, teacherLongId)
}?.id ?: -1
} else -1
data.getTeacherByLastFirst(teacherName, teacherLongId).id
} else null
val lessonObject = Lesson(profileId, -1).also {
it.type = type
@ -121,11 +106,12 @@ class EdudziennikWebTimetable(override val data: DataEdudziennik,
it.endTime = endTime
it.subjectId = subject.id
it.teacherId = teacherId
it.teamId = data.teamClass?.id
it.id = it.buildId()
}
data.lessonNewList.add(lessonObject)
data.lessonList.add(lessonObject)
dataDays.remove(lessonObject.date!!.value)
if (type != Lesson.TYPE_NORMAL) {
@ -146,7 +132,7 @@ class EdudziennikWebTimetable(override val data: DataEdudziennik,
for (day in dataDays) {
val lessonDate = Date.fromValue(day)
data.lessonNewList += Lesson(profileId, lessonDate.value.toLong()).apply {
data.lessonList += Lesson(profileId, lessonDate.value.toLong()).apply {
type = Lesson.TYPE_NO_LESSONS
date = lessonDate
}
@ -154,8 +140,6 @@ class EdudziennikWebTimetable(override val data: DataEdudziennik,
d(TAG, "Clearing lessons between ${weekStart.stringY_m_d} and ${weekEnd.stringY_m_d} - timetable downloaded for $getDate")
if (data.timetableNotPublic) data.timetableNotPublic = false
data.toRemove.add(DataRemoveModel.Timetable.between(weekStart, weekEnd))
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_TIMETABLE, SYNC_ALWAYS)
onSuccess()

View File

@ -5,17 +5,18 @@
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.modules.profiles.Profile
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.utils.Utils
import pl.szczodrzynski.edziennik.set
class EdudziennikFirstLogin(val data: DataEdudziennik, val onSuccess: () -> Unit) {
companion object {
@ -26,25 +27,35 @@ class EdudziennikFirstLogin(val data: DataEdudziennik, val onSuccess: () -> Unit
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 accountName = EDUDZIENNIK_ACCOUNT_NAME_START.find(text)?.get(1)?.fixName()
val accountNameLong = EDUDZIENNIK_ACCOUNT_NAME_START.find(text)?.get(1)?.fixName()
EDUDZIENNIK_STUDENTS_START.findAll(text).forEach {
val studentId = it[1]
val studentName = it[2].fixName()
val studentNameLong = it[2].fixName()
if (studentId.isBlank() || studentName.isBlank()) return@forEach
if (studentId.isBlank() || studentNameLong.isBlank()) return@forEach
val profile = Profile()
profile.studentNameLong = studentName
profile.studentNameShort = studentName.getShortName()
profile.accountNameLong = if (studentName == accountName) null else accountName
profile.studentSchoolYear = Utils.getCurrentSchoolYear()
profile.name = studentName
profile.subname = data.loginEmail
profile.empty = true
profile.putStudentData("studentId", studentId)
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)
}

View File

@ -10,10 +10,10 @@ import pl.szczodrzynski.edziennik.*
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.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.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

View File

@ -17,12 +17,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login.IdziennikLo
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.modules.announcements.AnnouncementFull
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.data.db.modules.teachers.Teacher
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 {
@ -43,9 +43,7 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
private fun completed() {
data.saveData()
data.notify {
callback.onCompleted()
}
callback.onCompleted()
}
/* _______ _ _ _ _ _

View File

@ -41,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

View File

@ -10,8 +10,8 @@ import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_API_CURRENT_REGISTER
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNIK_API_CURRENT_REGISTER
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikApi
import pl.szczodrzynski.edziennik.data.db.modules.luckynumber.LuckyNumber
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.LuckyNumber
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.getInt
import pl.szczodrzynski.edziennik.getJsonObject
import pl.szczodrzynski.edziennik.getString
@ -25,9 +25,6 @@ class IdziennikApiCurrentRegister(override val data: DataIdziennik,
}
init {
data.profile?.luckyNumber = -1
data.profile?.luckyNumberDate = null
apiGet(TAG, IDZIENNIK_API_CURRENT_REGISTER) { json ->
if (json !is JsonObject) {
onSuccess()
@ -37,9 +34,9 @@ class IdziennikApiCurrentRegister(override val data: DataIdziennik,
var nextSync = System.currentTimeMillis() + 14*DAY*1000
val settings = json.getJsonObject("ustawienia")?.apply {
profile?.dateSemester1Start = getString("poczatekSemestru1")?.let { Date.fromY_m_d(it) }
profile?.dateSemester2Start = getString("koniecSemestru1")?.let { Date.fromY_m_d(it).stepForward(0, 0, 1) }
profile?.dateYearEnd = getString("koniecSemestru2")?.let { Date.fromY_m_d(it) }
getString("poczatekSemestru1")?.let { profile?.dateSemester1Start = Date.fromY_m_d(it) }
getString("koniecSemestru1")?.let { profile?.dateSemester2Start = Date.fromY_m_d(it).stepForward(0, 0, 1) }
getString("koniecSemestru2")?.let { profile?.dateYearEnd = Date.fromY_m_d(it) }
}
json.getInt("szczesliwyNumerek")?.let { luckyNumber ->
@ -80,7 +77,7 @@ class IdziennikApiCurrentRegister(override val data: DataIdziennik,
profileId,
Metadata.TYPE_LUCKY_NUMBER,
luckyNumberObject.date.value.toLong(),
data.profile?.empty ?: false,
true,
data.profile?.empty ?: false,
System.currentTimeMillis()
))

View File

@ -10,13 +10,13 @@ import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_API_MESSAGES_INBOX
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNIK_API_MESSAGES_INBOX
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikApi
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_DELETED
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_DELETED
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.getBoolean
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.Utils.crc32

View File

@ -12,10 +12,10 @@ import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_API_MESSAGES_SENT
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNIK_API_MESSAGES_SENT
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikApi
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipient
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.utils.Utils.crc32
import pl.szczodrzynski.edziennik.utils.models.Date

View File

@ -12,9 +12,9 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNIK_WEB_ANNOUNCEMENTS
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.modules.announcements.Announcement
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
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.getJsonObject
import pl.szczodrzynski.edziennik.utils.models.Date

View File

@ -11,10 +11,10 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNI
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.crc16
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance
import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance.*
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Attendance
import pl.szczodrzynski.edziennik.data.db.entity.Attendance.*
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.getJsonObject
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time

View File

@ -13,9 +13,9 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNI
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
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.utils.models.Date
class IdziennikWebExams(override val data: DataIdziennik,

View File

@ -9,7 +9,7 @@ import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_GET_ATTACHMENT
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.utils.Utils
import java.io.File

View File

@ -10,11 +10,11 @@ import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_GET_MESSAGE
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
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.modules.messages.MessageFull
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageRecipientFull
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.data.db.full.MessageRecipientFull
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.utils.models.Date
class IdziennikWebGetMessage(
@ -57,7 +57,7 @@ class IdziennikWebGetMessage(
recipientObject.readDate = if (readDateString.isNullOrBlank()) System.currentTimeMillis()
else Date.fromIso(readDateString)
recipientObject.fullName = profile.accountNameLong ?: profile.studentNameLong
recipientObject.fullName = profile.accountName ?: profile.studentNameLong
data.messageRecipientList.add(recipientObject)
message.addRecipient(recipientObject)

View File

@ -14,7 +14,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.events.RecipientListGetEvent
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
class IdziennikWebGetRecipientList(
override val data: DataIdziennik, val onSuccess: () -> Unit) : IdziennikWeb(data) {

View File

@ -13,9 +13,9 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNI
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
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.utils.models.Date
class IdziennikWebGrades(override val data: DataIdziennik,
@ -89,11 +89,17 @@ class IdziennikWebGrades(override val data: DataIdziennik,
count += weight
}
val historyColor = historyItem.getString("Kolor") ?: ""
colorInt = 0xff2196f3.toInt()
if (historyColor.isNotEmpty()) {
colorInt = Color.parseColor("#$historyColor")
}
val historyObject = Grade(
profileId,
gradeObject.id * -1,
historyItem.get("Kategoria").asString,
Color.parseColor("#" + historyItem.get("Kolor").asString),
colorInt,
historyItem.get("Uzasadnienie").asString,
historyItem.get("Ocena").asString,
value,

View File

@ -13,9 +13,9 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNI
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
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.utils.models.Date
class IdziennikWebHomework(override val data: DataIdziennik,

View File

@ -11,10 +11,10 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNI
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.crc16
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice
import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice.*
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Notice
import pl.szczodrzynski.edziennik.data.db.entity.Notice.*
import pl.szczodrzynski.edziennik.getJsonObject
import pl.szczodrzynski.edziennik.utils.models.Date

View File

@ -12,11 +12,11 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNI
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_SEMESTER1_PROPOSED
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_YEAR_PROPOSED
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_SEMESTER1_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_YEAR_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.getJsonArray
import pl.szczodrzynski.edziennik.getJsonObject
import pl.szczodrzynski.edziennik.getString

View File

@ -13,9 +13,9 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.api.IdziennikApiMessagesSent
import pl.szczodrzynski.edziennik.data.api.events.MessageSentEvent
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import java.util.*
class IdziennikWebSendMessage(

View File

@ -13,10 +13,10 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNI
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonRange
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
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.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
@ -51,12 +51,13 @@ class IdziennikWebTimetable(override val data: DataIdziennik,
return@webApiGet
}
json.getJsonArray("GodzinyLekcyjne")?.asJsonObjectList()?.forEach { range ->
json.getJsonArray("GodzinyLekcyjne")?.asJsonObjectList()?.forEachIndexed { index, range ->
val lessonRange = LessonRange(
profileId,
range.getInt("LiczbaP") ?: return@forEach,
range.getString("Poczatek")?.let { Time.fromH_m(it) } ?: return@forEach,
range.getString("Koniec")?.let { Time.fromH_m(it) } ?: return@forEach
index + 1,
range.getString("Poczatek")?.let { Time.fromH_m(it) }
?: return@forEachIndexed,
range.getString("Koniec")?.let { Time.fromH_m(it) } ?: return@forEachIndexed
)
data.lessonRanges[lessonRange.lessonNumber] = lessonRange
}
@ -182,7 +183,7 @@ class IdziennikWebTimetable(override val data: DataIdziennik,
d(TAG, "Clearing lessons between ${weekStart.stringY_m_d} and ${weekEnd.stringY_m_d} - timetable downloaded for $getDate")
data.lessonNewList.addAll(lessons)
data.lessonList.addAll(lessons)
data.toRemove.add(DataRemoveModel.Timetable.between(weekStart, weekEnd))
data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_TIMETABLE, SYNC_ALWAYS)

View File

@ -7,15 +7,17 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.firstlogin
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.ERROR_LOGIN_IDZIENNIK_FIRST_NO_SCHOOL_YEAR
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_SETTINGS
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_IDZIENNIK
import pl.szczodrzynski.edziennik.data.api.Regexes
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login.IdziennikLoginWeb
import pl.szczodrzynski.edziennik.data.api.events.FirstLoginFinishedEvent
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.fixName
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.set
import pl.szczodrzynski.edziennik.swapFirstLastName
class IdziennikFirstLogin(val data: DataIdziennik, val onSuccess: () -> Unit) {
@ -27,6 +29,10 @@ class IdziennikFirstLogin(val data: DataIdziennik, val onSuccess: () -> Unit) {
private val profileList = mutableListOf<Profile>()
init {
val loginStoreId = data.loginStore.id
val loginStoreType = LOGIN_TYPE_IDZIENNIK
var firstProfileId = loginStoreId
IdziennikLoginWeb(data) {
web.webGet(TAG, IDZIENNIK_WEB_SETTINGS) { text ->
//val accounts = json.getJsonArray("accounts")
@ -34,12 +40,15 @@ class IdziennikFirstLogin(val data: DataIdziennik, val onSuccess: () -> Unit) {
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
else null
var schoolYearStart: Int? = null
var schoolYearEnd: Int? = null
var schoolYearName: String? = null
val schoolYear = Regexes.IDZIENNIK_LOGIN_FIRST_SCHOOL_YEAR.find(text)?.let {
schoolYearName = it[2]
val schoolYearId = Regexes.IDZIENNIK_LOGIN_FIRST_SCHOOL_YEAR.find(text)?.let {
schoolYearName = it[2]+"/"+it[3]
schoolYearStart = it[2].toIntOrNull()
schoolYearEnd = it[3].toIntOrNull()
it[1].toIntOrNull()
} ?: run {
data.error(ApiError(TAG, ERROR_LOGIN_IDZIENNIK_FIRST_NO_SCHOOL_YEAR)
@ -57,18 +66,26 @@ class IdziennikFirstLogin(val data: DataIdziennik, val onSuccess: () -> Unit) {
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)
val studentNameLong = "$firstName $lastName".fixName()
val studentNameShort = "$firstName ${lastName[0]}.".fixName()
val accountName = if (accountNameLong == studentNameLong) null else accountNameLong
val profile = Profile(
firstProfileId++,
loginStoreId,
loginStoreType,
studentNameLong,
data.webUsername,
studentNameLong,
studentNameShort,
accountName
).apply {
schoolYearStart?.let { studentSchoolYearStart = it }
studentClassName = className
studentData["studentId"] = studentId
studentData["registerId"] = registerId
studentData["schoolYearId"] = schoolYearId
}
profileList.add(profile)
}

View File

@ -12,8 +12,8 @@ import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_LIBRUS_MESSAGES
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_LIBRUS_PORTAL
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_LIBRUS_SYNERGIA
import pl.szczodrzynski.edziennik.data.api.models.Data
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app, profile, loginStore) {
@ -51,6 +51,8 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
}
}
override fun generateUserCode() = "$schoolName:$apiLogin"
fun getColor(id: Int?): Int {
return when (id) {
1 -> 0xFFF0E68C

View File

@ -19,12 +19,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLogin
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.modules.announcements.AnnouncementFull
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.data.db.modules.teachers.Teacher
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 Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
@ -45,9 +45,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
private fun completed() {
data.saveData()
data.notify {
callback.onCompleted()
}
callback.onCompleted()
}
/* _______ _ _ _ _ _

View File

@ -15,6 +15,7 @@ const val ENDPOINT_LIBRUS_API_UNITS = 1005
const val ENDPOINT_LIBRUS_API_USERS = 1006
const val ENDPOINT_LIBRUS_API_SUBJECTS = 1007
const val ENDPOINT_LIBRUS_API_CLASSROOMS = 1008
const val ENDPOINT_LIBRUS_API_LESSONS = 1009
const val ENDPOINT_LIBRUS_API_PUSH_CONFIG = 1010
const val ENDPOINT_LIBRUS_API_TIMETABLES = 1015
const val ENDPOINT_LIBRUS_API_SUBSTITUTIONS = 1016
@ -57,6 +58,10 @@ const val ENDPOINT_LIBRUS_MESSAGES_GET = 3040
val LibrusFeatures = listOf(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_ALWAYS_NEEDED, listOf(
ENDPOINT_LIBRUS_API_LESSONS to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)),
// push config
Feature(LOGIN_TYPE_LIBRUS, FEATURE_PUSH_CONFIG, listOf(
ENDPOINT_LIBRUS_API_PUSH_CONFIG to LOGIN_METHOD_LIBRUS_API
@ -96,7 +101,8 @@ val LibrusFeatures = listOf(
ENDPOINT_LIBRUS_API_NORMAL_GRADE_CATEGORIES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_POINT_GRADE_CATEGORIES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADE_CATEGORIES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_TEXT_GRADE_CATEGORIES to LOGIN_METHOD_LIBRUS_API,
// Commented out, because TextGrades/Categories is the same as Grades/Categories
/* ENDPOINT_LIBRUS_API_TEXT_GRADE_CATEGORIES to LOGIN_METHOD_LIBRUS_API, */
ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADE_CATEGORIES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_CATEGORIES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS to LOGIN_METHOD_LIBRUS_API,

View File

@ -26,7 +26,7 @@ open class LibrusApi(open val data: DataLibrus) {
val profile
get() = data.profile
fun apiGet(tag: String, endpoint: String, method: Int = GET, payload: JsonObject? = null, onSuccess: (json: JsonObject) -> Unit) {
fun apiGet(tag: String, endpoint: String, method: Int = GET, payload: JsonObject? = null, ignoreErrors: List<Int> = emptyList(), onSuccess: (json: JsonObject) -> Unit) {
d(tag, "Request: Librus/Api - ${if (data.fakeLogin) FAKE_LIBRUS_API else LIBRUS_API_URL}/$endpoint")
@ -63,12 +63,15 @@ open class LibrusApi(open val data: DataLibrus) {
"NotesIsNotActive" -> ERROR_LIBRUS_API_NOTES_NOT_ACTIVE
"InvalidRequest" -> ERROR_LIBRUS_API_INVALID_REQUEST_PARAMS
"Nieprawidłowy węzeł." -> ERROR_LIBRUS_API_INCORRECT_ENDPOINT
"NoticeboardProblem" -> ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM
else -> ERROR_LIBRUS_API_OTHER
}.let { errorCode ->
data.error(ApiError(tag, errorCode)
.withApiResponse(json)
.withResponse(response))
return
if (errorCode !in ignoreErrors) {
data.error(ApiError(tag, errorCode)
.withApiResponse(json)
.withResponse(response))
return
}
}
}

View File

@ -10,7 +10,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesGetList
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.LibrusSynergiaHomework
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.LibrusSynergiaInfo
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.utils.Utils
class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
@ -75,6 +75,10 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
data.startProgress(R.string.edziennik_progress_endpoint_classrooms)
LibrusApiClassrooms(data, onSuccess)
}
ENDPOINT_LIBRUS_API_LESSONS -> {
data.startProgress(R.string.edziennik_progress_endpoint_lessons)
LibrusApiLessons(data, onSuccess)
}
// TODO push config
ENDPOINT_LIBRUS_API_TIMETABLES -> {
data.startProgress(R.string.edziennik_progress_endpoint_timetable)

View File

@ -5,12 +5,14 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.ERROR_LIBRUS_API_INVALID_REQUEST_PARAMS
import pl.szczodrzynski.edziennik.data.api.ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM
import pl.szczodrzynski.edziennik.data.api.POST
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.api.events.AnnouncementGetEvent
import pl.szczodrzynski.edziennik.data.db.modules.announcements.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
class LibrusApiAnnouncementMarkAsRead(
override val data: DataLibrus,
@ -22,7 +24,11 @@ class LibrusApiAnnouncementMarkAsRead(
}
init {
apiGet(TAG, "SchoolNotices/MarkAsRead/${announcement.idString}", method = POST) {
apiGet(TAG, "SchoolNotices/MarkAsRead/${announcement.idString}", method = POST,
ignoreErrors = listOf(
ERROR_LIBRUS_API_INVALID_REQUEST_PARAMS,
ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM
)) {
announcement.seen = true
EventBus.getDefault().postSticky(AnnouncementGetEvent(announcement))

View File

@ -8,9 +8,9 @@ import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_ANNOUNCEMENTS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.modules.announcements.Announcement
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
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.utils.models.Date
class LibrusApiAnnouncements(override val data: DataLibrus,
@ -21,7 +21,7 @@ class LibrusApiAnnouncements(override val data: DataLibrus,
init { data.profile?.also { profile ->
apiGet(TAG, "SchoolNotices") { json ->
val announcements = json.getJsonArray("SchoolNotices").asJsonObjectList()
val announcements = json.getJsonArray("SchoolNotices")?.asJsonObjectList()
announcements?.forEach { announcement ->
val longId = announcement.getString("Id") ?: return@forEach

View File

@ -9,8 +9,8 @@ import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance
import pl.szczodrzynski.edziennik.data.db.modules.attendance.AttendanceType
import pl.szczodrzynski.edziennik.data.db.entity.Attendance
import pl.szczodrzynski.edziennik.data.db.entity.AttendanceType
class LibrusApiAttendanceTypes(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) {
@ -20,7 +20,7 @@ class LibrusApiAttendanceTypes(override val data: DataLibrus,
init {
apiGet(TAG, "Attendances/Types") { json ->
val attendanceTypes = json.getJsonArray("Types").asJsonObjectList()
val attendanceTypes = json.getJsonArray("Types")?.asJsonObjectList()
attendanceTypes?.forEach { attendanceType ->
val id = attendanceType.getLong("Id") ?: return@forEach

View File

@ -9,10 +9,9 @@ import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_ATTENDANCES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.utils.Utils
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.utils.models.Date
class LibrusApiAttendances(override val data: DataLibrus,
@ -25,30 +24,36 @@ class LibrusApiAttendances(override val data: DataLibrus,
if (data.attendanceTypes.isEmpty()) {
data.db.attendanceTypeDao().getAllNow(profileId).toSparseArray(data.attendanceTypes) { it.id }
}
if (data.librusLessons.isEmpty()) {
data.db.librusLessonDao().getAllNow(profileId).toSparseArray(data.librusLessons) { it.lessonId }
}
apiGet(TAG, "Attendances") { json ->
val attendances = json.getJsonArray("Attendances").asJsonObjectList()
val attendances = json.getJsonArray("Attendances")?.asJsonObjectList()
attendances?.forEach { attendance ->
val id = Utils.strToInt((attendance.getString("Id") ?: return@forEach)
val id = ((attendance.getString("Id") ?: return@forEach)
.replace("[^\\d.]".toRegex(), "")).toLong()
val teacherId = attendance.getJsonObject("AddedBy")?.getLong("Id") ?: -1
val lessonId = attendance.getJsonObject("Lesson")?.getLong("Id") ?: -1
val lessonNo = attendance.getInt("LessonNo") ?: return@forEach
val startTime = data.lessonRanges.get(lessonNo).startTime
val lessonDate = Date.fromY_m_d(attendance.getString("Date"))
val teacherId = attendance.getJsonObject("AddedBy")?.getLong("Id")
val semester = attendance.getInt("Semester") ?: return@forEach
val type = attendance.getJsonObject("Type")?.getLong("Id") ?: return@forEach
val typeObject = data.attendanceTypes.get(type)
val topic = typeObject?.name ?: ""
val lessonList = data.db.timetableDao().getForDateNow(profileId, lessonDate)
val subjectId = lessonList.firstOrNull { it.startTime == startTime }?.subjectId ?: -1
val startTime = data.lessonRanges.get(lessonNo).startTime
val lesson = if (lessonId != -1L)
data.librusLessons.singleOrNull { it.lessonId == lessonId }
else null
val attendanceObject = Attendance(
profileId,
id,
teacherId,
subjectId,
teacherId ?: lesson?.teacherId ?: -1,
lesson?.subjectId ?: -1,
semester,
topic,
lessonDate,

View File

@ -9,7 +9,7 @@ import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_CATEGORIES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
class LibrusApiBehaviourGradeCategories(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) {

View File

@ -8,8 +8,8 @@ import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_COMMENTS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
class LibrusApiBehaviourGradeComments(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) {

View File

@ -9,10 +9,10 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeCategory
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.utils.models.Date
import java.text.DecimalFormat

View File

@ -8,7 +8,7 @@ import pl.szczodrzynski.edziennik.DAY
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_CLASSES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.modules.teams.Team
import pl.szczodrzynski.edziennik.data.db.entity.Team
import pl.szczodrzynski.edziennik.getJsonObject
import pl.szczodrzynski.edziennik.getLong
import pl.szczodrzynski.edziennik.getString
@ -38,6 +38,8 @@ class LibrusApiClasses(override val data: DataLibrus,
teacherId
)
data.profile?.studentClassName = name
data.teamList.put(id, teamObject)
data.unitId = studentClass.getJsonObject("Unit").getLong("Id") ?: 0L

View File

@ -8,7 +8,7 @@ import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_CLASSROOMS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.modules.classrooms.Classroom
import pl.szczodrzynski.edziennik.data.db.entity.Classroom
import java.util.*
class LibrusApiClassrooms(override val data: DataLibrus,
@ -19,15 +19,16 @@ class LibrusApiClassrooms(override val data: DataLibrus,
init {
apiGet(TAG, "Classrooms") { json ->
val classrooms = json.getJsonArray("Classrooms").asJsonObjectList()
val classrooms = json.getJsonArray("Classrooms")?.asJsonObjectList()
classrooms?.forEach { classroom ->
val id = classroom.getLong("Id") ?: return@forEach
val name = classroom.getString("Name")?.toLowerCase(Locale.getDefault()) ?: ""
val symbol = classroom.getString("Symbol")?.toLowerCase(Locale.getDefault()) ?: ""
val nameShort = name.split(" ").onEach { it[0] }.joinToString()
val nameShort = name.fixWhiteSpaces().split(" ").onEach { it[0] }.joinToString()
val symbolParts = symbol.fixWhiteSpaces().split(" ")
val friendlyName = if (name != symbol && !name.contains(symbol) && !nameShort.contains(symbol)) {
val friendlyName = if (name != symbol && !name.contains(symbol) && !name.containsAll(symbolParts) && !nameShort.contains(symbol)) {
classroom.getString("Symbol") + " " + classroom.getString("Name")
}
else {

View File

@ -9,7 +9,7 @@ import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADE_CATEGORIES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
class LibrusApiDescriptiveGradeCategories(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) {

View File

@ -9,12 +9,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_DESCRIPTIVE_TEXT
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_TEXT
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeCategory
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_DESCRIPTIVE_TEXT
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_TEXT
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.utils.models.Date
class LibrusApiDescriptiveGrades(override val data: DataLibrus,
@ -44,7 +44,7 @@ class LibrusApiDescriptiveGrades(override val data: DataLibrus,
val category = data.gradeCategories.singleOrNull {
it.categoryId == categoryId && it.type == when (type) {
TYPE_DESCRIPTIVE_TEXT -> GradeCategory.TYPE_DESCRIPTIVE
else -> GradeCategory.TYPE_TEXT
else -> GradeCategory.TYPE_NORMAL
}
}

View File

@ -8,7 +8,7 @@ import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_EVENT_TYPES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.modules.events.EventType
import pl.szczodrzynski.edziennik.data.db.entity.EventType
class LibrusApiEventTypes(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) {
@ -18,7 +18,7 @@ class LibrusApiEventTypes(override val data: DataLibrus,
init {
apiGet(TAG, "HomeWorks/Categories") { json ->
val eventTypes = json.getJsonArray("Categories").asJsonObjectList()
val eventTypes = json.getJsonArray("Categories")?.asJsonObjectList()
eventTypes?.forEach { eventType ->
val id = eventType.getLong("Id") ?: return@forEach

View File

@ -10,9 +10,9 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_EVENTS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
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.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
@ -28,13 +28,13 @@ class LibrusApiEvents(override val data: DataLibrus,
}
apiGet(TAG, "HomeWorks") { json ->
val events = json.getJsonArray("HomeWorks").asJsonObjectList()
val events = json.getJsonArray("HomeWorks")?.asJsonObjectList()
events?.forEach { event ->
val id = event.getLong("Id") ?: return@forEach
val eventDate = Date.fromY_m_d(event.getString("Date"))
val topic = event.getString("Content") ?: ""
val type = event.getJsonObject("Category")?.getInt("Id") ?: -1
val type = event.getJsonObject("Category")?.getLong("Id") ?: -1
val teacherId = event.getJsonObject("CreatedBy")?.getLong("Id") ?: -1
val subjectId = event.getJsonObject("Subject")?.getLong("Id") ?: -1
val teamId = event.getJsonObject("Class")?.getLong("Id") ?: -1

View File

@ -9,8 +9,8 @@ import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADE_CATEGORIES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
class LibrusApiGradeCategories(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) {

View File

@ -8,8 +8,8 @@ import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
class LibrusApiGradeComments(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) {

View File

@ -5,11 +5,11 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.*
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeCategory
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.*
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.models.Date
@ -21,7 +21,7 @@ class LibrusApiGrades(override val data: DataLibrus,
init { data.profile?.also { profile ->
apiGet(TAG, "Grades") { json ->
val grades = json.getJsonArray("Grades").asJsonObjectList()
val grades = json.getJsonArray("Grades")?.asJsonObjectList()
grades?.forEach { grade ->
val id = grade.getLong("Id") ?: return@forEach
@ -32,19 +32,16 @@ class LibrusApiGrades(override val data: DataLibrus,
val subjectId = grade.getJsonObject("Subject")?.getLong("Id") ?: -1
val addedDate = Date.fromIso(grade.getString("AddDate"))
val category = data.gradeCategories.singleOrNull { it.categoryId == categoryId }
val categoryName = category?.text ?: ""
val color = category?.color ?: -1
var weight = category?.weight ?: 0f
val value = Utils.getGradeValue(name)
if (name == "-" || name == "+"
|| name.equals("np", ignoreCase = true)
|| name.equals("bz", ignoreCase = true)) {
weight = 0f
val category = data.gradeCategories.singleOrNull {
it.categoryId == categoryId && it.type == GradeCategory.TYPE_NORMAL
}
val value = Utils.getGradeValue(name)
val weight = if (name == "-" || name == "+"
|| name.equals("np", ignoreCase = true)
|| name.equals("bz", ignoreCase = true)) 0f
else category?.weight ?: 0f
val description = grade.getJsonArray("Comments")?.asJsonObjectList()?.let { comments ->
if (comments.isNotEmpty()) {
data.gradeCategories.singleOrNull {
@ -57,8 +54,8 @@ class LibrusApiGrades(override val data: DataLibrus,
val gradeObject = Grade(
profileId,
id,
categoryName,
color,
category?.text ?: "",
category?.color ?: -1,
description,
name,
value,

View File

@ -9,9 +9,9 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_HOMEWORK
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
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.utils.models.Date
class LibrusApiHomework(override val data: DataLibrus,
@ -22,7 +22,7 @@ class LibrusApiHomework(override val data: DataLibrus,
init {
apiGet(TAG, "HomeWorkAssignments") { json ->
val homeworkList = json.getJsonArray("HomeWorkAssignments").asJsonObjectList()
val homeworkList = json.getJsonArray("HomeWorkAssignments")?.asJsonObjectList()
homeworkList?.forEach { homework ->
val id = homework.getLong("Id") ?: return@forEach

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-1-6.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_LESSONS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.entity.LibrusLesson
class LibrusApiLessons(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) {
companion object {
const val TAG = "LibrusApiLessons"
}
init {
apiGet(TAG, "Lessons") { json ->
val lessons = json.getJsonArray("Lessons")?.asJsonObjectList()
lessons?.forEach { lesson ->
val id = lesson.getLong("Id") ?: return@forEach
val teacherId = lesson.getJsonObject("Teacher")?.getLong("Id") ?: return@forEach
val subjectId = lesson.getJsonObject("Subject")?.getLong("Id") ?: return@forEach
val teamId = lesson.getJsonObject("Class")?.getLong("Id")
val librusLesson = LibrusLesson(
profileId,
id,
teacherId,
subjectId,
teamId
)
data.librusLessons.put(id, librusLesson)
}
data.setSyncNext(ENDPOINT_LIBRUS_API_LESSONS, 4*DAY)
onSuccess()
}
}
}

View File

@ -8,8 +8,8 @@ import pl.szczodrzynski.edziennik.DAY
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_LUCKY_NUMBER
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.modules.luckynumber.LuckyNumber
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.LuckyNumber
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.getInt
import pl.szczodrzynski.edziennik.getJsonObject
import pl.szczodrzynski.edziennik.getString
@ -23,9 +23,6 @@ class LibrusApiLuckyNumber(override val data: DataLibrus,
}
init {
data.profile?.luckyNumber = -1
data.profile?.luckyNumberDate = null
var nextSync = System.currentTimeMillis() + 2*DAY*1000
apiGet(TAG, "LuckyNumbers") { json ->
@ -52,7 +49,7 @@ class LibrusApiLuckyNumber(override val data: DataLibrus,
profileId,
Metadata.TYPE_LUCKY_NUMBER,
luckyNumberObject.date.value.toLong(),
profile?.empty ?: false,
true,
profile?.empty ?: false,
System.currentTimeMillis()
))

View File

@ -24,7 +24,7 @@ class LibrusApiMe(override val data: DataLibrus,
data.isPremium = account?.getBoolean("IsPremium") == true || account?.getBoolean("IsPremiumDemo") == true
val isParent = account?.getInt("GroupId") == 5
data.profile?.accountNameLong =
data.profile?.accountName =
if (isParent)
buildFullName(account?.getString("FirstName"), account?.getString("LastName"))
else null

View File

@ -8,7 +8,7 @@ import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_NOTICE_TYPES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.modules.notices.NoticeType
import pl.szczodrzynski.edziennik.data.db.entity.NoticeType
class LibrusApiNoticeTypes(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) {
@ -18,7 +18,7 @@ class LibrusApiNoticeTypes(override val data: DataLibrus,
init {
apiGet(TAG, "Notes/Categories") { json ->
val noticeTypes = json.getJsonArray("Categories").asJsonObjectList()
val noticeTypes = json.getJsonArray("Categories")?.asJsonObjectList()
noticeTypes?.forEach { noticeType ->
val id = noticeType.getLong("Id") ?: return@forEach

View File

@ -9,9 +9,9 @@ import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_NOTICES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Notice
import pl.szczodrzynski.edziennik.utils.models.Date
class LibrusApiNotices(override val data: DataLibrus,
@ -26,7 +26,7 @@ class LibrusApiNotices(override val data: DataLibrus,
}
apiGet(TAG, "Notes") { json ->
val notes = json.getJsonArray("Notes").asJsonObjectList()
val notes = json.getJsonArray("Notes")?.asJsonObjectList()
notes?.forEach { note ->
val id = note.getLong("Id") ?: return@forEach
@ -46,7 +46,7 @@ class LibrusApiNotices(override val data: DataLibrus,
val noticeObject = Notice(
profileId,
id,
categoryText+"\n"+text,
categoryText + "\n" + text,
semester,
type,
teacherId

View File

@ -9,7 +9,7 @@ import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_POINT_GRADE_CATEGORIES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
class LibrusApiPointGradeCategories(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) {

View File

@ -9,11 +9,11 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_POINT_GRADES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade.TYPE_POINT_AVG
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeCategory
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_POINT_AVG
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.utils.models.Date
class LibrusApiPointGrades(override val data: DataLibrus,

View File

@ -9,8 +9,8 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_PT_MEETINGS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
@ -22,7 +22,7 @@ class LibrusApiPtMeetings(override val data: DataLibrus,
init {
apiGet(TAG, "ParentTeacherConferences") { json ->
val ptMeetings = json.getJsonArray("ParentTeacherConferences").asJsonObjectList()
val ptMeetings = json.getJsonArray("ParentTeacherConferences")?.asJsonObjectList()
ptMeetings?.forEach { meeting ->
val id = meeting.getLong("Id") ?: return@forEach

View File

@ -8,7 +8,7 @@ import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_SCHOOLS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonRange
import pl.szczodrzynski.edziennik.data.db.entity.LessonRange
import pl.szczodrzynski.edziennik.utils.models.Time
import java.util.*

View File

@ -8,7 +8,7 @@ import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_SUBJECTS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject
import pl.szczodrzynski.edziennik.data.db.entity.Subject
class LibrusApiSubjects(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) {
@ -18,7 +18,7 @@ class LibrusApiSubjects(override val data: DataLibrus,
init {
apiGet(TAG, "Subjects") { json ->
val subjects = json.getJsonArray("Subjects").asJsonObjectList()
val subjects = json.getJsonArray("Subjects")?.asJsonObjectList()
subjects?.forEach { subject ->
val id = subject.getLong("Id") ?: return@forEach

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