Compare commits

...

91 Commits

Author SHA1 Message Date
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
de6b77baba [4.0-beta.1] It's here. Finally. 2020-01-02 20:05:14 +01:00
c8e3a3d258 [Messages/Compose] Change reply greeting text. Remove debugging toasts. 2020-01-02 20:03:52 +01:00
52ef24ae7b [Messages/Compose] Fix when adding duplicated recipient. 2020-01-02 20:02:48 +01:00
1553173300 [Messages/Compose] Fix deselecting recipients in dialog. 2020-01-02 19:57:48 +01:00
f5b2c24ee3 [Messages/Compose] Fix browsing Teacher category in dialog. 2020-01-02 19:28:51 +01:00
2ddbc6bbac [Gradle] Move java-json to app/libs. 2020-01-02 18:49:43 +01:00
eae7189981 [Messages] Add sending messages and fix stuff. 2020-01-02 18:27:58 +01:00
f292b3637d [API/Librus] Fix syncing past homework. 2020-01-02 15:38:43 +01:00
aff0b361a2 It's the new year bruh. 2020-01-02 09:02:15 +01:00
9f78b86c57 [API/Edudziennik] Add getting notes. 2020-01-01 19:41:52 +01:00
4950627850 [API/Librus] Fix null in homework description. 2020-01-01 18:58:29 +01:00
5265f3eb6a [API/Edudziennik] Add getting events. 2020-01-01 18:47:09 +01:00
3cca5e8e9a [DataRemoveModel/Events] Add removing future except list of types. 2020-01-01 18:46:43 +01:00
e9ca109c57 [Errors] Rewrite crash activity in Kotlin and add error reporting. 2019-12-31 18:48:06 +01:00
344da53888 [Errors] Add reporting the error ID. 2019-12-31 14:28:37 +01:00
62ae3c4c4b [Edudziennik] Add to changelog. Update Edudziennik feature list. 2019-12-31 12:45:57 +01:00
6f95eb3c3f [Errors] Update error details dialog, implement error reporting. 2019-12-31 12:45:57 +01:00
f350a86946 [UI/Changelog] Add new changelog dialog. Update for v4.0-beta.1 2019-12-31 12:45:57 +01:00
868e529e62 [API/Edudziennik] Add saving teachers login ids. 2019-12-31 12:23:38 +01:00
62d82c88a1 [API/Szkolny] Fix respecting registration status of profile in App Sync. 2019-12-30 22:44:45 +01:00
a86e995113 [API/Librus] Fix announcement mark as read request and fix observer in announcements. 2019-12-29 20:40:54 +01:00
5e2c7e89ab [Hotfix] Fix crashing in grades subject adapter. 2019-12-29 20:22:48 +01:00
4a38906194 [API/Edudziennik] Add getting homework. 2019-12-29 19:41:03 +01:00
cc3e6d97dd [Strings/Sync] Change all "Pobieram…" to "Pobieranie…" 2019-12-29 19:01:12 +01:00
90d6fb56d1 [API/Librus] Add rest of the grade types and categories... 2019-12-29 18:51:47 +01:00
9b5cf3f636 [API/Librus] Add getting descriptive grades and its categories. 2019-12-29 17:48:54 +01:00
3723abbbbb [API/Idziennik] Add getting attachments. 2019-12-28 17:02:43 +01:00
a626427788 [API/Idziennik] Add getting message. 2019-12-28 14:09:32 +01:00
35d88f8c78 [API/Librus] Add marking announcements as read. 2019-12-27 20:10:42 +01:00
c65872b29b [Errors] Add showing errors details. 2019-12-26 23:44:24 +01:00
e472d34f4d [Announcements] Add idString column and add getting attachments in Edudziennik. 2019-12-26 22:59:43 +01:00
1257596104 [API/Edudziennik] Change grade categories and descriptions and change back lucky number sync frequency to always. 2019-12-26 00:43:03 +01:00
5dd6519d27 [Attendance] Change TYPE_FREE to TYPE_DAY_FREE. 2019-12-26 00:10:14 +01:00
e607577407 [Attendance] Add free attendance type. 2019-12-25 23:51:52 +01:00
ade12e729f [Grades] Change point grade types names. 2019-12-25 22:50:21 +01:00
eee83ebb94 [API/Edudziennik] Fix getting grades and add support for point grades. 2019-12-25 22:46:29 +01:00
39ff47e866 [API/Edudziennik] Fix downloading timetable out of the school year. 2019-12-25 22:19:02 +01:00
6c81a506e9 [API/Edudziennik] Add error reasons and add grade description. 2019-12-25 13:15:03 +01:00
a24620de31 [API/Edudziennik] Add limited access basic handling and add setting next sync for lucky number. 2019-12-25 02:38:54 +01:00
70de47408a [API/Edudziennik] Add getting teachers and *change endpoint IDs.* 2019-12-25 02:17:20 +01:00
04103d1c84 [API/Edudziennik] Move getting grades to other endpoint and add getting subjects. 2019-12-25 01:49:37 +01:00
435 changed files with 10268 additions and 10981 deletions

45
.idea/jarRepositories.xml generated Normal file
View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven2" />
<option name="name" value="maven2" />
<option name="url" value="https://kotlin.bintray.com/kotlinx/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven3" />
<option name="name" value="maven3" />
<option name="url" value="https://dl.bintray.com/wulkanowy/wulkanowy" />
</remote-repository>
<remote-repository>
<option name="id" value="BintrayJCenter" />
<option name="name" value="BintrayJCenter" />
<option name="url" value="https://jcenter.bintray.com/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://jitpack.io" />
</remote-repository>
<remote-repository>
<option name="id" value="Google" />
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenRepo" />
<option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository>
</component>
</project>

BIN
app/libs/java-json.jar Normal file

Binary file not shown.

View File

@ -22,13 +22,13 @@
-keep class android.support.v7.widget.** { *; }
-keep class pl.szczodrzynski.edziennik.utils.models.** { *; }
-keep class pl.szczodrzynski.edziennik.data.db.modules.events.Event { *; }
-keep class pl.szczodrzynski.edziennik.data.db.modules.events.EventFull { *; }
-keep class pl.szczodrzynski.edziennik.data.db.entity.Event { *; }
-keep class pl.szczodrzynski.edziennik.data.db.full.EventFull { *; }
-keep class pl.szczodrzynski.edziennik.ui.modules.home.HomeCardModel { *; }
-keepclassmembers class pl.szczodrzynski.edziennik.widgets.WidgetConfig { public *; }
-keepnames class pl.szczodrzynski.edziennik.WidgetTimetable
-keepnames class pl.szczodrzynski.edziennik.notifications.WidgetNotifications
-keepnames class pl.szczodrzynski.edziennik.luckynumber.WidgetLuckyNumber
-keepclassmembers class pl.szczodrzynski.edziennik.ui.widgets.WidgetConfig { public *; }
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.timetable.WidgetTimetableProvider
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.notifications.WidgetNotificationsProvider
-keepnames class pl.szczodrzynski.edziennik.widgets.luckynumber.WidgetLuckyNumber
-keep class .R
-keep class **.R$* {
@ -58,4 +58,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

@ -0,0 +1,13 @@
<!--
~ Copyright (c) Kuba Szczodrzyński 2019-12-28.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M13.5,15.5H10V12.5H13.5A1.5,1.5 0,0 1,15 14A1.5,1.5 0,0 1,13.5 15.5M10,6.5H13A1.5,1.5 0,0 1,14.5 8A1.5,1.5 0,0 1,13 9.5H10M15.6,10.79C16.57,10.11 17.25,9 17.25,8C17.25,5.74 15.5,4 13.25,4H7V18H14.04C16.14,18 17.75,16.3 17.75,14.21C17.75,12.69 16.89,11.39 15.6,10.79Z"/>
</vector>

View File

@ -0,0 +1,13 @@
<!--
~ Copyright (c) Kuba Szczodrzyński 2019-12-28.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M10,4V7H12.21L8.79,15H6V18H14V15H11.79L15.21,7H18V4H10Z"/>
</vector>

View File

@ -0,0 +1,13 @@
<!--
~ Copyright (c) Kuba Szczodrzyński 2019-12-28.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M5,21H19V19H5V21M12,17A6,6 0,0 0,18 11V3H15.5V11A3.5,3.5 0,0 1,12 14.5A3.5,3.5 0,0 1,8.5 11V3H6V11A6,6 0,0 0,12 17Z"/>
</vector>

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,63 +47,7 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ui.modules.messages.MessagesComposeActivity"
android:configChanges="orientation|screenSize"
android:label="@string/messages_compose_title"
android:theme="@style/AppTheme.Black" />
<activity
android:name=".ui.modules.feedback.FeedbackActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:label="@string/app_name"
android:theme="@style/AppTheme" />
<activity
android:name=".ui.modules.login.LoginActivity"
android:configChanges="orientation|screenSize"
android:launchMode="singleTop"
android:theme="@style/AppTheme.Light" />
<activity
android:name=".ui.modules.intro.ChangelogIntroActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name"
android:theme="@style/Theme.Intro" />
<!--
______ _ _
| ____(_) | |
| |__ _ _ __ ___| |__ __ _ ___ ___
| __| | | '__/ _ \ '_ \ / _` / __|/ _ \
| | | | | | __/ |_) | (_| \__ \ __/
|_| |_|_| \___|_.__/ \__,_|___/\___/
-->
<activity
android:name=".ui.modules.base.CrashActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:process=":error_activity"
android:theme="@style/DeadTheme" />
<!--
_____ _ _ _ _ _
/ ____| | | | | (_) (_) |
| | _ __ __ _ ___| |__ __ _ ___| |_ ___ ___| |_ _ _
| | | '__/ _` / __| '_ \ / _` |/ __| __| \ \ / / | __| | | |
| |____| | | (_| \__ \ | | | | (_| | (__| |_| |\ V /| | |_| |_| |
\_____|_| \__,_|___/_| |_| \__,_|\___|\__|_| \_/ |_|\__|\__, |
__/ |
|___/
-->
<activity
android:name=".ui.modules.base.CrashGtfoActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:theme="@style/DeadTheme" />
<activity
android:name=".widgets.WidgetConfigActivity"
android:configChanges="orientation|keyboardHidden"
android:excludeFromRecents="true"
android:noHistory="true"
android:theme="@style/AppTheme.NoDisplay">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>
<!--
__ ___ _ _
\ \ / (_) | | | |
@ -97,71 +56,36 @@
\ /\ / | | (_| | (_| | __/ |_ \__ \
\/ \/ |_|\__,_|\__, |\___|\__||___/
__/ |
|_
|___/
-->
<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: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" />
@ -171,8 +95,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" />
@ -182,8 +108,58 @@
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.WebPushConfigActivity"
android:configChanges="orientation|keyboardHidden"
android:theme="@style/AppTheme.Dark" />
<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" />
@ -196,54 +172,53 @@
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
<receiver
android:name=".sync.FirebaseBroadcastReceiver"
<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=".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=".Notifier$GetDataRetryService" />
<service android:name=".data.api.ApiService" />
<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,53 +1,37 @@
<html>
<head>
<style type="text/css">
* {
word-wrap: break-word;
}
body {
background-color: #{bg-color}; color: #{text-color};
}
a {
color: #{link-color};
}
a:active {
color: #{link-color-active};
}
ol {
list-style-position: inside;
padding-left: 0;
padding-right: 0;
}
li:not(:first-child) {
padding-top: 8px;
}
</style>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<h3>Wersja 4.0, 2019-jeszcze-nie-wiem-kiedy</h3>
<h3>Wersja 4.0-beta.3, 2020-01-10</h3>
<ul>
<li>UWAGA. To jest wersja in-development. Wiele funkcji może nie działać prawidłowo (lub wcale), co oznacza tylko że nie zostały jeszcze przeniesione
z wersji 3.x. Proszę o cierpliwość oraz <b>nie udostępnianie</b> tej wersji <u>nikomu</u>.</li>
<li>Bardzo dużo zmian</li>
<li><b>Przebudowaliśmy cały moduł synchronizacji</b>, co oznacza większą stabilność aplikacji, szybkosć oraz poprawność pobieranych danych.</li>
<li><b><u>Wysyłanie wiadomości</u></b> - funkcja, na którą czekał każdy. Od teraz w Szkolnym można wysyłać oraz odpowiadać na wiadomości do nauczycieli &#x1F44F;</li>
<li>Udoskonalony wygląd Szkolnego - sprawi, że korzystanie z aplikacji będzie jeszcze przyjemniejsze</li>
<li>Nowa <b>Strona główna</b> - ładniejszy wygląd oraz możliwość przestawiania kart na każdym profilu</li>
<li>Nowy <b>Plan lekcji</b> - z doskonałą obsługą lekcji przesuniętych oraz dwóch lekcji o tej samej godzinie</li>
<li>Nowe okienka informacji o wydarzeniach oraz lekcjach</li>
<li>Ł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>Znaczna ilość błędów z poprzednich wersji już nie występuje</li>
</ul>
<!--<i>
<h3>Plany na następne wersje:</h3>
<ul>
<li>Widget kalendarza ze sprawdzianami, ulepszenie widoku kalendarza w aplikacji</li>
<li>Wsparcie dla systemu Synergia w jednostkach samorządu terytorialnego - aplikacja Nasze Szkoły</li>
<li>Wsparcie dla Librusa w systemie Oświata w Radomiu</li>
<li>EduDziennik</li>
<li>Mobireg</li>
<li>Możliwość edycji planu lekcji</li>
</ul>
</i>-->
</body>
<br>
<br>
<br>
<b>Uwaga.</b> Ponieważ to wersja <i>beta</i>, niektóre funkcje mogą nie działać prawidłowo.<br>
Staramy się usuwać takie przypadki, jednak na chwilę obecną mogą występować błędy w:
<ul>
<li>Wysyłanie wiadomości może czasami nie działać - proszę o zgłaszanie wszystkich błędów na naszym serwerze Discord</li>
<li>Terminarz - brak informacji o odwołanych lekcjach w dialogu</li>
</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] = {
0xc4, 0x14, 0x3c, 0x14, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
0x1e, 0xef, 0x4e, 0xc6, 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

@ -67,12 +67,12 @@ import me.leolin.shortcutbadger.ShortcutBadger;
import okhttp3.ConnectionSpec;
import okhttp3.OkHttpClient;
import okhttp3.TlsVersion;
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing;
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.data.db.entity.DebugLog;
import pl.szczodrzynski.edziennik.data.db.entity.Profile;
import pl.szczodrzynski.edziennik.network.NetworkUtils;
import pl.szczodrzynski.edziennik.network.TLSSocketFactory;
import pl.szczodrzynski.edziennik.sync.SyncWorker;
@ -83,7 +83,7 @@ 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;
import static pl.szczodrzynski.edziennik.data.db.entity.LoginStore.LOGIN_TYPE_MOBIDZIENNIK;
public class App extends androidx.multidex.MultiDexApplication implements Configuration.Provider {
private static final String TAG = "App";
@ -138,11 +138,10 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
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 Profile profile;
public Config config;
private static Config mConfig;
public static Config getConfig() {
@ -438,6 +437,11 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
"Vulcan"
);
if (config.getRunSync()) {
config.setRunSync(false);
EdziennikTask.Companion.sync().enqueue(this);
}
try {
final long startTime = System.currentTimeMillis();
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(instanceIdResult -> {
@ -610,15 +614,6 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
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);
@ -632,7 +627,7 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
return;
}*/
if (profile == null || profile.getId() != id) {
profile = db.profileDao().getFullByIdNow(id);
profile = db.profileDao().getByIdNow(id);
/*if (profile == null) {
profileLoadById(id);
return;
@ -652,12 +647,6 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
}
}
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);
@ -697,11 +686,13 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
}*/
public int profileFirstId() {
return db.profileDao().getFirstId();
Integer id = db.profileDao().getFirstId();
return id == null ? 1 : id;
}
public int profileLastId() {
return db.profileDao().getLastId();
Integer id = db.profileDao().getLastId();
return id == null ? 1 : id;
}

View File

@ -4,7 +4,9 @@ import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import android.content.res.ColorStateList
import android.content.res.Resources
import android.database.Cursor
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.Typeface
@ -15,16 +17,19 @@ 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.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
@ -38,18 +43,22 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import okhttp3.RequestBody
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.utils.models.Time
import pl.szczodrzynski.navlib.getColorFromRes
import java.io.PrintWriter
import java.io.StringWriter
import java.math.BigInteger
import java.nio.charset.Charset
import java.security.MessageDigest
import java.text.SimpleDateFormat
import java.util.*
import java.util.zip.CRC32
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import kotlin.Pair
fun List<Teacher>.byId(id: Long) = firstOrNull { it.id == id }
@ -65,6 +74,7 @@ fun JsonObject?.getString(key: String): String? = get(key)?.let { if (it.isJsonN
fun JsonObject?.getInt(key: String): Int? = get(key)?.let { if (it.isJsonNull) null else it.asInt }
fun JsonObject?.getLong(key: String): Long? = get(key)?.let { if (it.isJsonNull) null else it.asLong }
fun JsonObject?.getFloat(key: String): Float? = get(key)?.let { if(it.isJsonNull) null else it.asFloat }
fun JsonObject?.getChar(key: String): Char? = get(key)?.let { if(it.isJsonNull) null else it.asCharacter }
fun JsonObject?.getJsonObject(key: String): JsonObject? = get(key)?.let { if (it.isJsonNull) null else it.asJsonObject }
fun JsonObject?.getJsonArray(key: String): JsonArray? = get(key)?.let { if (it.isJsonNull) null else it.asJsonArray }
@ -73,15 +83,32 @@ fun JsonObject?.getString(key: String, defaultValue: String): String = get(key)?
fun JsonObject?.getInt(key: String, defaultValue: Int): Int = get(key)?.let { if (it.isJsonNull) defaultValue else it.asInt } ?: defaultValue
fun JsonObject?.getLong(key: String, defaultValue: Long): Long = get(key)?.let { if (it.isJsonNull) defaultValue else it.asLong } ?: defaultValue
fun JsonObject?.getFloat(key: String, defaultValue: Float): Float = get(key)?.let { if(it.isJsonNull) defaultValue else it.asFloat } ?: defaultValue
fun JsonObject?.getChar(key: String, defaultValue: Char): Char = get(key)?.let { if(it.isJsonNull) defaultValue else it.asCharacter } ?: defaultValue
fun JsonObject?.getJsonObject(key: String, defaultValue: JsonObject): JsonObject = get(key)?.let { if (it.isJsonNull) defaultValue else it.asJsonObject } ?: defaultValue
fun JsonObject?.getJsonArray(key: String, defaultValue: JsonArray): JsonArray = get(key)?.let { if (it.isJsonNull) defaultValue else it.asJsonArray } ?: defaultValue
fun JsonArray?.asJsonObjectList() = this?.map { it.asJsonObject }
operator fun JsonObject.set(key: String, value: JsonElement) = this.add(key, value)
operator fun JsonObject.set(key: String, value: Boolean) = this.addProperty(key, value)
operator fun JsonObject.set(key: String, value: String?) = this.addProperty(key, value)
operator fun JsonObject.set(key: String, value: Number) = this.addProperty(key, value)
operator fun JsonObject.set(key: String, value: Char) = this.addProperty(key, value)
operator fun Profile.set(key: String, value: JsonElement) = this.studentData.add(key, value)
operator fun Profile.set(key: String, value: Boolean) = this.studentData.addProperty(key, value)
operator fun Profile.set(key: String, value: String?) = this.studentData.addProperty(key, value)
operator fun Profile.set(key: String, value: Number) = this.studentData.addProperty(key, value)
operator fun Profile.set(key: String, value: Char) = this.studentData.addProperty(key, value)
fun JsonArray.asJsonObjectList() = this.mapNotNull { it.asJsonObject }
fun CharSequence?.isNotNullNorEmpty(): Boolean {
return this != null && this.isNotEmpty()
}
fun CharSequence?.isNotNullNorBlank(): Boolean {
return this != null && this.isNotBlank()
}
fun currentTimeUnix() = System.currentTimeMillis() / 1000
fun Bundle?.getInt(key: String, defaultValue: Int): Int {
@ -97,12 +124,31 @@ fun Bundle?.getString(key: String, defaultValue: String): String {
return this?.getString(key, defaultValue) ?: defaultValue
}
fun String.fixName(): String {
return this.fixWhiteSpaces().toProperCase()
/**
* ` The quick BROWN_fox Jumps OveR THE LAZy-DOG. `
*
* converts to
*
* `The Quick Brown_fox Jumps Over The Lazy-Dog.`
*/
fun String?.fixName(): String {
return this?.fixWhiteSpaces()?.toProperCase() ?: ""
}
/**
* `The quick BROWN_fox Jumps OveR THE LAZy-DOG.`
*
* converts to
*
* `The Quick Brown_fox Jumps Over The Lazy-Dog.`
*/
fun String.toProperCase(): String = changeStringCase(this)
/**
* `John Smith` -> `Smith John`
*
* `JOHN SMith` -> `SMith JOHN`
*/
fun String.swapFirstLastName(): String {
return this.split(" ").let {
if (it.size > 1)
@ -148,33 +194,50 @@ fun String.getShortName(): String {
}
}
fun List<String>.join(delimiter: String): String {
return this.joinToString(delimiter)
/**
* "John Smith" -> "JS"
*
* "JOHN SMith" -> "JS"
*
* "John" -> "J"
*
* "John " -> "J"
*
* "John Smith " -> "JS"
*
* " " -> ""
*
* " " -> ""
*/
fun String?.getNameInitials(): String {
if (this.isNullOrBlank()) return ""
return this.toUpperCase().fixWhiteSpaces().split(" ").take(2).map { it[0] }.joinToString("")
}
fun colorFromName(context: Context, name: String?): Int {
var crc = (name ?: "").crc16()
crc = (crc and 0xff) or (crc shr 8)
crc %= 16
val color = when (crc) {
13 -> R.color.md_red_500
4 -> R.color.md_pink_A400
2 -> R.color.md_purple_A400
9 -> R.color.md_deep_purple_A700
5 -> R.color.md_indigo_500
1 -> R.color.md_indigo_A700
6 -> R.color.md_cyan_A200
14 -> R.color.md_teal_400
15 -> R.color.md_green_500
7 -> R.color.md_yellow_A700
3 -> R.color.md_deep_orange_A400
8 -> R.color.md_deep_orange_A700
10 -> R.color.md_brown_500
12 -> R.color.md_grey_400
11 -> R.color.md_blue_grey_400
else -> R.color.md_light_green_A700
}
return context.getColorFromRes(color)
fun List<String>.join(delimiter: String): String {
return concat(delimiter).toString()
}
fun colorFromName(name: String?): Int {
val i = (name ?: "").crc32()
return when ((i / 10 % 16 + 1).toInt()) {
13 -> 0xffF44336
4 -> 0xffF50057
2 -> 0xffD500F9
9 -> 0xff6200EA
5 -> 0xffFFAB00
1 -> 0xff304FFE
6 -> 0xff40C4FF
14 -> 0xff26A69A
15 -> 0xff00C853
7 -> 0xffFFD600
3 -> 0xffFF3D00
8 -> 0xffDD2C00
10 -> 0xff795548
12 -> 0xff2979FF
11 -> 0xffFF6D00
else -> 0xff64DD17
}.toInt()
}
fun colorFromCssName(name: String): Int {
@ -191,10 +254,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) {
@ -232,6 +292,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 ->
@ -240,6 +307,36 @@ fun <T> SparseArray<T>.values(): List<T> {
return result
}
fun SparseIntArray.keys(): List<Int> {
val result = mutableListOf<Int>()
forEach { key, _ ->
result += key
}
return result
}
fun SparseIntArray.values(): List<Int> {
val result = mutableListOf<Int>()
forEach { _, value ->
result += value
}
return result
}
fun <K, V> List<Pair<K, V>>.keys(): List<K> {
val result = mutableListOf<K>()
forEach { pair ->
result += pair.first
}
return result
}
fun <K, V> List<Pair<K, V>>.values(): List<V> {
val result = mutableListOf<V>()
forEach { pair ->
result += pair.second
}
return result
}
fun <T> List<T>.toSparseArray(destination: SparseArray<T>, key: (T) -> Int) {
forEach {
destination.put(key(it), it)
@ -422,6 +519,29 @@ fun CharSequence?.asItalicSpannable(): Spannable {
spannable.setSpan(StyleSpan(Typeface.ITALIC), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
return spannable
}
fun CharSequence?.asBoldSpannable(): Spannable {
val spannable = SpannableString(this)
spannable.setSpan(StyleSpan(Typeface.BOLD), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
return spannable
}
fun CharSequence.asSpannable(vararg spans: Any, substring: String? = null, ignoreCase: Boolean = false): Spannable {
val spannable = SpannableString(this)
if (substring == null) {
spans.forEach {
spannable.setSpan(it, 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
}
else if (substring.isNotEmpty()) {
var index = indexOf(substring, ignoreCase = ignoreCase)
while (index >= 0) {
spans.forEach {
spannable.setSpan(it, index, index + substring.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
index = indexOf(substring, startIndex = index + 1, ignoreCase = ignoreCase)
}
}
return spannable
}
/**
* Returns a new read-only list only of those given elements, that are not empty.
@ -429,7 +549,7 @@ fun CharSequence?.asItalicSpannable(): Spannable {
*/
fun <T : CharSequence> listOfNotEmpty(vararg elements: T): List<T> = elements.filterNot { it.isEmpty() }
fun List<CharSequence?>.concat(delimiter: String? = null): CharSequence {
fun List<CharSequence?>.concat(delimiter: CharSequence? = null): CharSequence {
if (this.isEmpty()) {
return ""
}
@ -438,11 +558,13 @@ fun List<CharSequence?>.concat(delimiter: String? = null): CharSequence {
return this[0] ?: ""
}
var spanned = false
for (piece in this) {
if (piece is Spanned) {
spanned = true
break
var spanned = delimiter is Spanned
if (!spanned) {
for (piece in this) {
if (piece is Spanned) {
spanned = true
break
}
}
}
@ -490,8 +612,44 @@ fun JsonObject(vararg properties: Pair<String, Any?>): JsonObject {
}
}
fun JsonArray(vararg properties: Any?): JsonArray {
return JsonArray().apply {
for (property in properties) {
when (property) {
is JsonElement -> add(property as JsonElement?)
is String -> add(property as String?)
is Char -> add(property as Char?)
is Number -> add(property as Number?)
is Boolean -> add(property as Boolean?)
}
}
}
}
fun Bundle(vararg properties: Pair<String, Any?>): Bundle {
return Bundle().apply {
for (property in properties) {
when (property.second) {
is String -> putString(property.first, property.second as String?)
is Char -> putChar(property.first, property.second as Char)
is Int -> putInt(property.first, property.second as Int)
is Long -> putLong(property.first, property.second as Long)
is Float -> putFloat(property.first, property.second as Float)
is Short -> putShort(property.first, property.second as Short)
is Double -> putDouble(property.first, property.second as Double)
is Boolean -> putBoolean(property.first, property.second as Boolean)
}
}
}
}
fun JsonArray?.isNullOrEmpty(): Boolean = (this?.size() ?: 0) == 0
fun JsonArray.isEmpty(): Boolean = this.size() == 0
operator fun JsonArray.plusAssign(o: JsonElement) = this.add(o)
operator fun JsonArray.plusAssign(o: String) = this.add(o)
operator fun JsonArray.plusAssign(o: Char) = this.add(o)
operator fun JsonArray.plusAssign(o: Number) = this.add(o)
operator fun JsonArray.plusAssign(o: Boolean) = this.add(o)
@Suppress("UNCHECKED_CAST")
inline fun <T : View> T.onClick(crossinline onClickListener: (v: T) -> Unit) {
@ -682,3 +840,113 @@ val String.firstLettersName: String
}
return nameShort
}
val Throwable.stackTraceString: String
get() {
val sw = StringWriter()
printStackTrace(PrintWriter(sw))
return sw.toString()
}
inline fun <T> LongSparseArray<T>.filter(predicate: (T) -> Boolean): List<T> {
val destination = ArrayList<T>()
this.forEach { _, element -> if (predicate(element)) destination.add(element) }
return destination
}
fun CharSequence.replace(oldValue: String, newValue: CharSequence, ignoreCase: Boolean = false): CharSequence =
splitToSequence(oldValue, ignoreCase = ignoreCase).toList().concat(newValue)
fun Int.toColorStateList(): ColorStateList {
val states = arrayOf(
intArrayOf( android.R.attr.state_enabled ),
intArrayOf(-android.R.attr.state_enabled ),
intArrayOf(-android.R.attr.state_checked ),
intArrayOf( android.R.attr.state_pressed )
)
val colors = intArrayOf(
this,
this,
this,
this
)
return ColorStateList(states, colors)
}
fun SpannableStringBuilder.appendText(text: CharSequence): SpannableStringBuilder {
append(text)
return this
}
fun SpannableStringBuilder.appendSpan(text: CharSequence, what: Any, flags: Int): SpannableStringBuilder {
val start: Int = length
append(text)
setSpan(what, start, length, flags)
return this
}
fun joinNotNullStrings(delimiter: String = "", vararg parts: String?): String {
var first = true
val sb = StringBuilder()
for (part in parts) {
if (part == null)
continue
if (!first)
sb += delimiter
first = false
sb += part
}
return sb.toString()
}
fun String.notEmptyOrNull(): String? {
return if (isEmpty())
null
else
this
}
fun String.base64Encode(): String {
return encodeToString(toByteArray(), NO_WRAP)
}
fun ByteArray.base64Encode(): String {
return encodeToString(this, NO_WRAP)
}
fun String.base64Decode(): ByteArray {
return Base64.decode(this, Base64.DEFAULT)
}
fun String.base64DecodeToString(): String {
return Base64.decode(this, Base64.DEFAULT).toString(Charset.defaultCharset())
}
fun CheckBox.trigger() { isChecked = !isChecked }
fun Context.plural(@PluralsRes resId: Int, value: Int): String = resources.getQuantityString(resId, value, value)
fun Context.getNotificationTitle(type: Int): String {
return getString(when (type) {
Notification.TYPE_UPDATE -> R.string.notification_type_update
Notification.TYPE_ERROR -> R.string.notification_type_error
Notification.TYPE_TIMETABLE_CHANGED -> R.string.notification_type_timetable_change
Notification.TYPE_TIMETABLE_LESSON_CHANGE -> R.string.notification_type_timetable_lesson_change
Notification.TYPE_NEW_GRADE -> R.string.notification_type_new_grade
Notification.TYPE_NEW_EVENT -> R.string.notification_type_new_event
Notification.TYPE_NEW_HOMEWORK -> R.string.notification_type_new_homework
Notification.TYPE_NEW_SHARED_EVENT -> R.string.notification_type_new_shared_event
Notification.TYPE_NEW_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))

View File

@ -9,7 +9,10 @@ import android.content.IntentFilter
import android.content.pm.PackageManager
import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable
import android.os.*
import android.os.AsyncTask
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.Settings
import android.util.Log
import android.view.Gravity
@ -20,6 +23,7 @@ import androidx.appcompat.widget.PopupMenu
import androidx.core.graphics.ColorUtils
import androidx.lifecycle.Observer
import androidx.navigation.NavOptions
import androidx.recyclerview.widget.RecyclerView
import com.danimahardhika.cafebar.CafeBar
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.mikepenz.iconics.IconicsColor
@ -36,13 +40,12 @@ import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import pl.droidsonroids.gif.GifDrawable
import pl.szczodrzynski.edziennik.App.APP_URL
import pl.szczodrzynski.edziennik.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.Metadata.*
import pl.szczodrzynski.edziennik.databinding.ActivitySzkolnyBinding
import pl.szczodrzynski.edziennik.network.ServerRequest
import pl.szczodrzynski.edziennik.sync.AppManagerDetectedEvent
import pl.szczodrzynski.edziennik.sync.SyncWorker
import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog
@ -52,6 +55,7 @@ import pl.szczodrzynski.edziennik.ui.modules.agenda.AgendaFragment
import pl.szczodrzynski.edziennik.ui.modules.announcements.AnnouncementsFragment
import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceFragment
import pl.szczodrzynski.edziennik.ui.modules.base.DebugFragment
import pl.szczodrzynski.edziennik.ui.modules.base.MainSnackbar
import pl.szczodrzynski.edziennik.ui.modules.behaviour.BehaviourFragment
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar
import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackFragment
@ -62,11 +66,13 @@ import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment
import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity
import pl.szczodrzynski.edziennik.ui.modules.messages.MessageFragment
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesComposeFragment
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesListFragment
import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsFragment
import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsNewFragment
import pl.szczodrzynski.edziennik.ui.modules.timetable.v2.TimetableFragment
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment
import pl.szczodrzynski.edziennik.ui.modules.webpush.WebPushFragment
import pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoTouch
import pl.szczodrzynski.edziennik.utils.Themes
@ -119,6 +125,7 @@ class MainActivity : AppCompatActivity() {
const val TARGET_HELP = 502
const val TARGET_FEEDBACK = 120
const val TARGET_MESSAGES_DETAILS = 503
const val TARGET_MESSAGES_COMPOSE = 504
const val TARGET_WEB_PUSH = 140
const val HOME_ID = DRAWER_ITEM_HOME
@ -210,7 +217,8 @@ class MainActivity : AppCompatActivity() {
list += NavTarget(TARGET_GRADES_EDITOR, R.string.menu_grades_editor, GradesEditorFragment::class)
list += NavTarget(TARGET_HELP, R.string.menu_help, HelpFragment::class)
list += NavTarget(TARGET_FEEDBACK, R.string.menu_feedback, FeedbackFragment::class)
list += NavTarget(TARGET_MESSAGES_DETAILS, R.string.menu_message, MessageFragment::class)
list += NavTarget(TARGET_MESSAGES_DETAILS, R.string.menu_message, MessageFragment::class).withPopTo(DRAWER_ITEM_MESSAGES)
list += NavTarget(TARGET_MESSAGES_COMPOSE, R.string.menu_message_compose, MessagesComposeFragment::class)
list += NavTarget(TARGET_WEB_PUSH, R.string.menu_web_push, WebPushFragment::class)
list += NavTarget(DRAWER_ITEM_DEBUG, R.string.menu_debug, DebugFragment::class)
@ -222,6 +230,7 @@ class MainActivity : AppCompatActivity() {
val navView: NavView by lazy { b.navView }
val drawer: NavDrawer by lazy { navView.drawer }
val bottomSheet: NavBottomSheet by lazy { navView.bottomSheet }
val mainSnackbar: MainSnackbar by lazy { MainSnackbar(this) }
val errorSnackbar: ErrorSnackbar by lazy { ErrorSnackbar(this) }
val swipeRefreshLayout: SwipeRefreshLayoutNoTouch by lazy { b.swipeRefreshLayout }
@ -232,10 +241,11 @@ class MainActivity : AppCompatActivity() {
private val fragmentManager by lazy { supportFragmentManager }
private lateinit var navTarget: NavTarget
private var navArguments: Bundle? = null
val navTargetId
get() = navTarget.id
private val navBackStack = mutableListOf<NavTarget>()
private val navBackStack = mutableListOf<Pair<NavTarget, Bundle?>>()
private var navLoading = true
/* ____ _____ _
@ -257,6 +267,7 @@ class MainActivity : AppCompatActivity() {
Log.d(TAG, Signing.appPassword)
mainSnackbar.setCoordinator(b.navView.coordinator, b.navView.bottomBar)
errorSnackbar.setCoordinator(b.navView.coordinator, b.navView.bottomBar)
navLoading = true
@ -311,6 +322,10 @@ class MainActivity : AppCompatActivity() {
removeAllItems()
toggleGroupEnabled = false
textInputEnabled = false
onCloseListener = {
if (!app.config.ui.bottomSheetOpened)
app.config.ui.bottomSheetOpened = true
}
}
drawer.apply {
@ -318,7 +333,6 @@ class MainActivity : AppCompatActivity() {
drawerProfileListEmptyListener = {
app.config.loginFinished = false
app.saveConfig("loginFinished")
profileListEmptyListener()
}
drawerItemSelectedListener = { id, position, drawerItem ->
@ -358,10 +372,8 @@ class MainActivity : AppCompatActivity() {
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)
app.db.profileDao().all.observe(this, Observer { profiles ->
drawer.setProfileList(profiles.filter { it.id >= 0 }.toMutableList())
if (profileListEmpty) {
profileListEmpty = false
handleIntent(intent?.extras)
@ -426,16 +438,7 @@ class MainActivity : AppCompatActivity() {
// WHAT'S NEW DIALOG
if (app.config.appVersion < BuildConfig.VERSION_CODE) {
ServerRequest(app, app.requestScheme + APP_URL + "main.php?just_updated", "MainActivity/JU")
.run { e, result ->
Handler(Looper.getMainLooper()).post {
try {
ChangelogDialog().show(supportFragmentManager, "whats_new")
} catch (e2: Exception) {
e2.printStackTrace()
}
}
}
ChangelogDialog(this)
if (app.config.appVersion < 170) {
//Intent intent = new Intent(this, ChangelogIntroActivity.class);
//startActivity(intent);
@ -504,13 +507,6 @@ 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 = {
@ -519,9 +515,6 @@ class MainActivity : AppCompatActivity() {
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()
}
DRAWER_PROFILE_SYNC_ALL -> {
@ -560,7 +553,7 @@ class MainActivity : AppCompatActivity() {
).enqueue(this)
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onSyncStartedEvent(event: ApiTaskStartedEvent) {
fun onApiTaskStartedEvent(event: ApiTaskStartedEvent) {
swipeRefreshLayout.isRefreshing = true
if (event.profileId == App.profileId) {
navView.toolbar.apply {
@ -571,7 +564,7 @@ class MainActivity : AppCompatActivity() {
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onSyncProgressEvent(event: ApiTaskProgressEvent) {
fun onApiTaskProgressEvent(event: ApiTaskProgressEvent) {
if (event.profileId == App.profileId) {
navView.toolbar.apply {
subtitleFormat = null
@ -584,8 +577,8 @@ class MainActivity : AppCompatActivity() {
}
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onSyncProfileFinishedEvent(event: ApiTaskFinishedEvent) {
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onApiTaskFinishedEvent(event: ApiTaskFinishedEvent) {
if (event.profileId == App.profileId) {
navView.toolbar.apply {
subtitleFormat = R.string.toolbar_subtitle
@ -594,17 +587,19 @@ class MainActivity : AppCompatActivity() {
}
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onSyncFinishedEvent(event: ApiTaskAllFinishedEvent) {
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onApiTaskAllFinishedEvent(event: ApiTaskAllFinishedEvent) {
swipeRefreshLayout.isRefreshing = false
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onSyncErrorEvent(event: ApiTaskErrorEvent) {
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onApiTaskErrorEvent(event: ApiTaskErrorEvent) {
EventBus.getDefault().removeStickyEvent(event)
navView.toolbar.apply {
subtitleFormat = R.string.toolbar_subtitle
subtitleFormatWithUnread = R.plurals.toolbar_subtitle_with_unread
subtitle = "Gotowe"
}
mainSnackbar.dismiss()
errorSnackbar.addError(event.error).show()
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
@ -752,10 +747,12 @@ class MainActivity : AppCompatActivity() {
val filter = IntentFilter()
filter.addAction(Intent.ACTION_MAIN)
registerReceiver(intentReceiver, filter)
EventBus.getDefault().register(this)
super.onResume()
}
override fun onPause() {
unregisterReceiver(intentReceiver)
EventBus.getDefault().unregister(this)
super.onPause()
}
@ -808,10 +805,13 @@ class MainActivity : AppCompatActivity() {
}
AsyncTask.execute {
app.profileLoadById(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()
@ -833,7 +833,7 @@ class MainActivity : AppCompatActivity() {
loadId = DRAWER_ITEM_HOME
}
val target = navTargetList
.singleOrNull { it.id == loadId }
.firstOrNull { it.id == loadId }
if (target == null) {
Toast.makeText(this, getString(R.string.error_invalid_fragment, id), Toast.LENGTH_LONG).show()
loadTarget(navTargetList.first(), arguments)
@ -843,7 +843,7 @@ class MainActivity : AppCompatActivity() {
}
}
private fun loadTarget(target: NavTarget, arguments: Bundle? = null) {
d("NavDebug", "loadItem(id = ${target.id})")
d("NavDebug", "loadTarget(target = $target, arguments = $arguments)")
bottomSheet.close()
bottomSheet.removeAllContextual()
@ -870,7 +870,7 @@ class MainActivity : AppCompatActivity() {
)
}
else {
navBackStack.lastIndexOf(target).let {
navBackStack.keys().lastIndexOf(target).let {
if (it == -1)
return@let target
// pop the back stack up until that target
@ -901,8 +901,9 @@ class MainActivity : AppCompatActivity() {
R.anim.task_open_enter,
R.anim.task_open_exit
)
navBackStack.add(navTarget)
navBackStack.add(navTarget to arguments)
navTarget = target
navArguments = arguments
}
}
@ -917,7 +918,7 @@ class MainActivity : AppCompatActivity() {
d("NavDebug", "Current fragment ${navTarget.fragmentClass?.java?.simpleName}, pop to home ${navTarget.popToHome}, back stack:")
navBackStack.forEachIndexed { index, target2 ->
d("NavDebug", " - $index: ${target2.fragmentClass?.java?.simpleName}")
d("NavDebug", " - $index: ${target2.first.fragmentClass?.java?.simpleName}")
}
transaction.replace(R.id.fragment, fragment)
@ -942,11 +943,18 @@ class MainActivity : AppCompatActivity() {
return false
}
// TODO back stack argument support
if (navTarget.popToHome) {
loadTarget(HOME_ID)
}
else {
loadTarget(navBackStack.last())
when {
navTarget.popToHome -> {
loadTarget(HOME_ID)
}
navTarget.popTo != null -> {
loadTarget(navTarget.popTo ?: HOME_ID)
}
else -> {
navBackStack.last().let {
loadTarget(it.first, it.second)
}
}
}
return true
}
@ -961,6 +969,8 @@ class MainActivity : AppCompatActivity() {
* that something has changed in the bottom sheet.
*/
fun gainAttention() {
if (app.config.ui.bottomSheetOpened || true)
return
b.navView.postDelayed({
navView.gainAttentionOnBottomBar()
}, 2000)
@ -1084,4 +1094,8 @@ 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

@ -17,7 +17,6 @@ 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;
@ -221,10 +220,8 @@ public class Notifier {
app.appConfig.notifications.add(notification);
}
public void postAll(ProfileFull profile) {
public void postAll() {
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();

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
@ -39,6 +40,11 @@ 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 mAppVersion: Int? = null
var appVersion: Int
get() { mAppVersion = mAppVersion ?: values.get("appVersion", BuildConfig.VERSION_CODE); return mAppVersion ?: BuildConfig.VERSION_CODE }
@ -49,6 +55,11 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
get() { mLoginFinished = mLoginFinished ?: values.get("loginFinished", false); return mLoginFinished ?: false }
set(value) { set("loginFinished", value); mLoginFinished = value }
private var mPrivacyPolicyAccepted: Boolean? = null
var privacyPolicyAccepted: Boolean
get() { mPrivacyPolicyAccepted = mPrivacyPolicyAccepted ?: values.get("privacyPolicyAccepted", false); return mPrivacyPolicyAccepted ?: false }
set(value) { set("privacyPolicyAccepted", value); mPrivacyPolicyAccepted = value }
private var mDevModePassword: String? = null
var devModePassword: String?
get() { mDevModePassword = mDevModePassword ?: values.get("devModePassword", null as String?); return mDevModePassword }
@ -64,6 +75,16 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
get() { mAppRateSnackbarTime = mAppRateSnackbarTime ?: values.get("appRateSnackbarTime", 0L); return mAppRateSnackbarTime ?: 0L }
set(value) { set("appRateSnackbarTime", value); mAppRateSnackbarTime = value }
private var mRunSync: Boolean? = null
var runSync: Boolean
get() { mRunSync = mRunSync ?: values.get("runSync", false); return mRunSync ?: false }
set(value) { set("runSync", value); mRunSync = value }
private var mWidgetConfigs: JsonObject? = null
var widgetConfigs: JsonObject
get() { mWidgetConfigs = mWidgetConfigs ?: values.get("widgetConfigs", JsonObject()); return mWidgetConfigs ?: JsonObject() }
set(value) { set("widgetConfigs", value); mWidgetConfigs = value }
private var rawEntries: List<ConfigEntry> = db.configDao().getAllNow()
private val profileConfigs: HashMap<Int, ProfileConfig> = hashMapOf()
init {
@ -74,8 +95,11 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
ConfigMigration(app, this)
}
fun getFor(profileId: Int): ProfileConfig {
return profileConfigs[profileId] ?: ProfileConfig(db, profileId, rawEntries)
return profileConfigs[profileId] ?: ProfileConfig(db, profileId, db.configDao().getAllNow(profileId)).also {
profileConfigs[profileId] = it
}
}
fun forProfile() = getFor(App.profileId)
fun setProfile(profileId: Int) {
}

View File

@ -59,4 +59,9 @@ class ConfigUI(private val config: Config) {
var snowfall: Boolean
get() { mSnowfall = mSnowfall ?: config.values.get("snowfall", false); return mSnowfall ?: false }
set(value) { config.set("snowfall", value); mSnowfall = value }
private var mBottomSheetOpened: Boolean? = null
var bottomSheetOpened: Boolean
get() { mBottomSheetOpened = mBottomSheetOpened ?: config.values.get("bottomSheetOpened", false); return mBottomSheetOpened ?: false }
set(value) { config.set("bottomSheetOpened", value); mBottomSheetOpened = value }
}

View File

@ -37,6 +37,11 @@ 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)

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

@ -18,7 +18,7 @@ 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.db.entity.Profile
import pl.szczodrzynski.edziennik.utils.Utils.d
import kotlin.math.min
import kotlin.math.roundToInt
@ -38,12 +38,13 @@ 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 val taskQueue = mutableListOf<IApiTask>()
private val errorList = mutableListOf<ApiError>()
@ -73,7 +74,7 @@ class ApiService : Service() {
override fun onCompleted() {
lastEventTime = System.currentTimeMillis()
d(TAG, "Task $taskRunningId (profile $taskProfileId) - $taskProgressText - finished")
EventBus.getDefault().post(ApiTaskFinishedEvent(taskProfileId))
EventBus.getDefault().postSticky(ApiTaskFinishedEvent(taskProfileId))
clearTask()
notification.setIdle().post()
@ -84,7 +85,7 @@ class ApiService : Service() {
lastEventTime = System.currentTimeMillis()
d(TAG, "Task $taskRunningId threw an error - $apiError")
apiError.profileId = taskProfileId
EventBus.getDefault().post(ApiTaskErrorEvent(apiError))
EventBus.getDefault().postSticky(ApiTaskErrorEvent(apiError))
errorList.add(apiError)
apiError.throwable?.printStackTrace()
if (apiError.isCritical) {
@ -194,7 +195,7 @@ class ApiService : Service() {
*/
private fun stopIfTaskFrozen() {
if (checkIfTaskFrozen()) {
stopSelf()
allCompleted()
}
}
@ -213,7 +214,7 @@ class ApiService : Service() {
}
private fun allCompleted() {
EventBus.getDefault().post(ApiTaskAllFinishedEvent())
EventBus.getDefault().postSticky(ApiTaskAllFinishedEvent())
stopSelf()
}
@ -228,6 +229,11 @@ 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) {
when (task.request) {
is EdziennikTask.SyncRequest -> app.db.profileDao().idsForSyncNow.forEach {
@ -270,7 +276,7 @@ class ApiService : Service() {
serviceClosed = true
taskCancelled = true
taskRunning?.cancel()
stopSelf()
allCompleted()
}
/* _____ _ _ _

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"
@ -69,6 +69,10 @@ const val IDZIENNIK_WEB_NOTICES = "mod_panelRodzica/uwagi/WS_uwagiUcznia.asmx/po
const val IDZIENNIK_WEB_ATTENDANCE = "mod_panelRodzica/obecnosci/WS_obecnosciUcznia.asmx/pobierzObecnosciUcznia"
const val IDZIENNIK_WEB_ANNOUNCEMENTS = "mod_panelRodzica/tabOgl/WS_tablicaOgloszen.asmx/GetOgloszenia"
const val IDZIENNIK_WEB_MESSAGES_LIST = "mod_komunikator/WS_wiadomosci.asmx/PobierzListeWiadomosci"
const val IDZIENNIK_WEB_GET_MESSAGE = "mod_komunikator/WS_wiadomosci.asmx/PobierzWiadomosc"
const val IDZIENNIK_WEB_GET_RECIPIENT_LIST = "mod_komunikator/WS_wiadomosci.asmx/pobierzListeOdbiorcowPanelRodzic"
const val IDZIENNIK_WEB_SEND_MESSAGE = "mod_komunikator/WS_wiadomosci.asmx/WyslijWiadomosc"
const val IDZIENNIK_WEB_GET_ATTACHMENT = "mod_komunikator/Download.ashx"
val IDZIENNIK_API_USER_AGENT = SYSTEM_USER_AGENT
const val IDZIENNIK_API_URL = "https://iuczniowie.progman.pl/idziennik/api"
@ -100,6 +104,7 @@ const val VULCAN_API_ENDPOINT_ATTENDANCE = "mobile-api/Uczen.v3.Uczen/Frekwencje
const val VULCAN_API_ENDPOINT_MESSAGES_RECEIVED = "mobile-api/Uczen.v3.Uczen/WiadomosciOdebrane"
const val VULCAN_API_ENDPOINT_MESSAGES_SENT = "mobile-api/Uczen.v3.Uczen/WiadomosciWyslane"
const val VULCAN_API_ENDPOINT_MESSAGES_CHANGE_STATUS = "mobile-api/Uczen.v3.Uczen/ZmienStatusWiadomosci"
const val VULCAN_API_ENDPOINT_MESSAGES_ADD = "mobile-api/Uczen.v3.Uczen/DodajWiadomosc"
const val VULCAN_API_ENDPOINT_PUSH = "mobile-api/Uczen.v3.Uczen/UstawPushToken"
const val EDUDZIENNIK_USER_AGENT = "Szkolny.eu/${BuildConfig.VERSION_NAME}"

View File

@ -11,22 +11,22 @@ 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.data.db.entity.Attendance
import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.Grade.*
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Notice
import pl.szczodrzynski.edziennik.data.db.entity.Notification
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_LUCKY_NUMBER
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_ANNOUNCEMENT
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_ATTENDANCE
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_EVENT
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_GRADE
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_HOMEWORK
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_MESSAGE
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_NOTICE
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_TIMETABLE_LESSON_CHANGE
import pl.szczodrzynski.edziennik.getNotificationTitle
import pl.szczodrzynski.edziennik.utils.models.Date
class DataNotifications(val data: Data) {
@ -45,6 +45,9 @@ class DataNotifications(val data: Data) {
return@run
}
val today = Date.getToday()
val todayValue = today.value
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(
@ -90,10 +93,6 @@ class DataNotifications(val data: Data) {
).addExtra("eventId", event.id).addExtra("eventDate", event.eventDate.value.toLong())
}
val today = Date.getToday()
val todayValue = today.value
profile.currentSemester = profile.dateToSemester(today)
for (grade in app.db.gradeDao().getNotNotifiedNow(profileId)) {
val gradeName = when (grade.type) {
TYPE_SEMESTER1_PROPOSED, TYPE_SEMESTER2_PROPOSED -> app.getString(R.string.grade_semester_proposed_format_2, grade.name)

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) ||
@ -81,3 +82,32 @@ fun Data.prepare(loginMethods: List<LoginMethod>, features: List<Feature>, featu
progressCount = targetLoginMethodIds.size + targetEndpointIds.size
progressStep = if (progressCount <= 0) 0f else 100f / progressCount.toFloat()
}
fun Data.prepareFor(loginMethods: List<LoginMethod>, loginMethodId: Int) {
val possibleLoginMethods = this.loginMethods.toMutableList()
loginMethods.forEach {
if (it.isPossible(profile, loginStore))
possibleLoginMethods += it.loginMethodId
}
targetEndpointIds.clear()
targetLoginMethodIds.clear()
// check the login method for any dependencies
var requiredLoginMethod: Int? = loginMethodId
while (requiredLoginMethod != LOGIN_METHOD_NOT_NEEDED) {
loginMethods.singleOrNull { it.loginMethodId == requiredLoginMethod }?.let {
if (requiredLoginMethod != null)
targetLoginMethodIds.add(requiredLoginMethod!!)
requiredLoginMethod = it.requiredLoginMethod(profile, loginStore)
}
}
// sort and distinct every login method
targetLoginMethodIds = targetLoginMethodIds.toHashSet().toMutableList()
targetLoginMethodIds.sort()
progressCount = 0
progressStep = 0f
}

View File

@ -31,6 +31,9 @@ const val CODE_SYNERGIA_NOT_ACTIVATED = 32
const val CODE_LIBRUS_DISCONNECTED = 31
const val CODE_PROFILE_ARCHIVED = 30*/
const val ERROR_APP_CRASH = 1
const val ERROR_MESSAGE_NOT_SENT = 10
const val ERROR_REQUEST_FAILURE = 50
const val ERROR_REQUEST_HTTP_400 = 51
const val ERROR_REQUEST_HTTP_401 = 52
@ -38,10 +41,13 @@ const val ERROR_REQUEST_HTTP_403 = 53
const val ERROR_REQUEST_HTTP_404 = 54
const val ERROR_REQUEST_HTTP_405 = 55
const val ERROR_REQUEST_HTTP_410 = 56
const val ERROR_REQUEST_HTTP_500 = 57
const val ERROR_REQUEST_HTTP_424 = 57
const val ERROR_REQUEST_HTTP_500 = 58
const val ERROR_REQUEST_HTTP_503 = 59
const val ERROR_REQUEST_FAILURE_HOSTNAME_NOT_FOUND = 60
const val ERROR_REQUEST_FAILURE_TIMEOUT = 61
const val ERROR_REQUEST_FAILURE_NO_INTERNET = 62
const val ERROR_REQUEST_FAILURE_SSL_ERROR = 63
const val ERROR_RESPONSE_EMPTY = 100
const val ERROR_LOGIN_DATA_MISSING = 101
const val ERROR_PROFILE_MISSING = 105
@ -157,11 +163,14 @@ const val ERROR_LOGIN_IDZIENNIK_FIRST_NO_SCHOOL_YEAR = 440
const val ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA = 441
const val ERROR_IDZIENNIK_API_ACCESS_DENIED = 450
const val ERROR_IDZIENNIK_API_OTHER = 451
const val ERROR_IDZIENNIK_API_NO_REGISTER = 452
const val ERROR_IDZIENNIK_WEB_RECIPIENT_LIST_NO_PERMISSION = 453
const val ERROR_LOGIN_EDUDZIENNIK_WEB_INVALID_LOGIN = 501
const val ERROR_LOGIN_EDUDZIENNIK_WEB_OTHER = 510
const val ERROR_LOGIN_EDUDZIENNIK_WEB_NO_SESSION_ID = 511
const val ERROR_EDUDZIENNIK_WEB_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
const val ERROR_TEMPLATE_WEB_OTHER = 801
@ -182,5 +191,6 @@ const val EXCEPTION_IDZIENNIK_WEB_REQUEST = 912
const val EXCEPTION_IDZIENNIK_WEB_API_REQUEST = 913
const val EXCEPTION_IDZIENNIK_API_REQUEST = 914
const val EXCEPTION_EDUDZIENNIK_WEB_REQUEST = 920
const val EXCEPTION_EDUDZIENNIK_FILE_REQUEST = 921
const val LOGIN_NO_ARGUMENTS = 1201

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

View File

@ -23,7 +23,7 @@ import pl.szczodrzynski.edziennik.data.api.models.LoginMethod
// vulcan
// mobireg
const val SYNERGIA_API_ENABLED = true
const val SYNERGIA_API_ENABLED = false

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()
}
@ -55,6 +55,10 @@ object Regexes {
"""href="https://.+?\.mobidziennik.pl/.+?&(?:amp;)?zalacznik=([0-9]+)"(?:.+?<small.+?\(([0-9.]+)\s(M|K|G|)B\))*""".toRegex(DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_MESSAGE_RECIPIENTS_JSON by lazy {
"""odbiorcy: (\[.+?\]),${'$'}""".toRegex(RegexOption.MULTILINE)
}
val IDZIENNIK_LOGIN_HIDDEN_FIELDS by lazy {
@ -70,7 +74,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)
@ -78,6 +82,9 @@ object Regexes {
val IDZIENNIK_LOGIN_FIRST_STUDENT by lazy {
"""<option.*?value="([0-9]+)"\sdata-id-ucznia="([A-z0-9]+?)".*?>(.+?)\s(.+?)\s*\((.+?),\s*(.+?)\)</option>""".toRegex(DOT_MATCHES_ALL)
}
val IDZIENNIK_MESSAGES_RECIPIENT_PARENT by lazy {
"""(.+?)\s\((.+)\)""".toRegex()
}
@ -99,6 +106,9 @@ object Regexes {
val EDUDZIENNIK_ACCOUNT_NAME_START by lazy {
"""<span id='user_dn'>(.*?)</span>""".toRegex()
}
val EDUDZIENNIK_SUBJECTS_START by lazy {
"""<a class="menu-course" href="/Students/[\w-_]+?/Courses/([\w-_]+)/">(.+?)</a>""".toRegex()
}
val EDUDZIENNIK_ATTENDANCE_ENTRIES by lazy {
"""<td id="([\d-]+?):(\d+?)".*?>(.+?)</td>""".toRegex()
@ -110,6 +120,10 @@ object Regexes {
"""\((.+?)\) (.+)""".toRegex()
}
val EDUDZIENNIK_ANNOUNCEMENT_DESCRIPTION by lazy {
"""<div class="desc">.*?<p>(.*?)</p>""".toRegex(DOT_MATCHES_ALL)
}
val EDUDZIENNIK_SUBJECT_ID by lazy {
"""/Courses/([\w-_]+?)/""".toRegex()
}
@ -122,6 +136,21 @@ object Regexes {
val EDUDZIENNIK_EVENT_TYPE_ID by lazy {
"""/GradeLabels/([\w-_]+?)/""".toRegex()
}
val EDUDZIENNIK_ANNOUNCEMENT_ID by lazy {
"""/Announcement/([\w-_]+?)/""".toRegex()
}
val EDUDZIENNIK_HOMEWORK_ID by lazy {
"""/Homework/([\w-_]+?)/""".toRegex()
}
val EDUDZIENNIK_TEACHER_ID by lazy {
"""/Teachers/([\w-_]+?)/""".toRegex()
}
val EDUDZIENNIK_EVENT_ID by lazy {
"""/KlassEvent/([\w-_]+?)/""".toRegex()
}
val EDUDZIENNIK_NOTE_ID by lazy {
"""/RegistryNotes/([\w-_]+?)/""".toRegex()
}
val EDUDZIENNIK_SCHOOL_DETAIL_ID by lazy {
"""<a id="School_detail".*?/School/([\w-_]+?)/""".toRegex(DOT_MATCHES_ALL)
@ -135,4 +164,8 @@ object Regexes {
val EDUDZIENNIK_CLASS_DETAIL_NAME by lazy {
"""<a id="Klass_detail".*?>(.*?)</a>""".toRegex(DOT_MATCHES_ALL)
}
val EDUDZIENNIK_TEACHERS by lazy {
"""<div class="teacher">.*?<p>(.+?) (.+?)</p>""".toRegex(DOT_MATCHES_ALL)
}
}

View File

@ -7,11 +7,11 @@ 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.EventType
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
/**
* Use http://patorjk.com/software/taag/#p=display&f=Big for the ascii art
@ -29,6 +29,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,34 +78,43 @@ 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/"
val schoolEndpoint: String
get() = "Schools/$schoolId/"
val classStudentEndpoint: String
get() = "Class/$studentId/"
val schoolClassEndpoint: String
get() = "Schools/$classId/"
val studentAndClassEndpoint: String
get() = "Students/$studentId/Klass/$classId/"
val courseEndpoint: String
get() = "Course/$studentId/"
val studentAndClassesEndpoint: String
get() = "Students/$studentId/Classes/$classId/"
val timetableEndpoint: String
get() = "Plan/$studentId/"
val studentAndTeacherClassEndpoint: String
get() = "Students/$studentId/Teachers/$classId/"
val courseStudentEndpoint: String
get() = "Course/$studentId/"
fun getSubject(longId: String, name: String): Subject {
val id = longId.crc32()
return subjectList.singleOrNull { it.id == id } ?: run {
@ -113,20 +124,32 @@ class DataEdudziennik(app: App, profile: Profile?, loginStore: LoginStore) : Dat
}
}
fun getTeacher(firstName: String, lastName: String): Teacher {
fun getTeacher(firstName: String, lastName: String, longId: String? = null): Teacher {
val name = "$firstName $lastName".fixName()
val id = name.crc32()
return teacherList.singleOrNull { it.id == id } ?: run {
val teacher = Teacher(profileId, id, firstName, lastName)
return teacherList.singleOrNull { it.id == id }?.also {
if (longId != null && it.loginId == null) it.loginId = longId
} ?: run {
val teacher = Teacher(profileId, id, firstName, lastName, longId)
teacherList.put(id, teacher)
teacher
}
}
fun getTeacherByFirstLast(nameFirstLast: String, longId: String? = null): Teacher {
val nameParts = nameFirstLast.split(" ")
return getTeacher(nameParts[0], nameParts[1], longId)
}
fun getTeacherByLastFirst(nameLastFirst: String, longId: String? = null): Teacher {
val nameParts = nameLastFirst.split(" ")
return getTeacher(nameParts[1], nameParts[0], longId)
}
fun getEventType(longId: String, name: String): EventType {
val id = longId.crc16().toLong()
return eventTypes.singleOrNull { it.id == id } ?: run {
val eventType = EventType(profileId, id, name, colorFromName(app, name))
val eventType = EventType(profileId, id, name, colorFromName(name))
eventTypes.put(id, eventType)
eventType
}

View File

@ -6,20 +6,21 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik
import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.ERROR_EDUDZIENNIK_WEB_TIMETABLE_NOT_PUBLIC
import pl.szczodrzynski.edziennik.data.api.ERROR_LOGIN_EDUDZIENNIK_WEB_NO_SESSION_ID
import pl.szczodrzynski.edziennik.data.api.edudziennikLoginMethods
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikData
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web.EdudziennikWebGetAnnouncement
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.firstlogin.EdudziennikFirstLogin
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.login.EdudziennikLogin
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.login.EdudziennikLoginWeb
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.prepare
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.utils.Utils.d
class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
@ -29,6 +30,7 @@ class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStor
val internalErrorList = mutableListOf<Int>()
val data: DataEdudziennik
private var afterLogin: (() -> Unit)? = null
init {
data = DataEdudziennik(app, profile, loginStore).apply {
@ -55,21 +57,17 @@ class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStor
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
data.arguments = arguments
data.prepare(edudziennikLoginMethods, EdudziennikFeatures, featureIds, viewId)
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
EdudziennikLogin(data) {
EdudziennikData(data) {
completed()
}
}
login()
}
private fun login() {
private fun login(loginMethodId: Int? = null, afterLogin: (() -> Unit)? = null) {
d(TAG, "Trying to login with ${data.targetLoginMethodIds}")
if (internalErrorList.isNotEmpty()) {
d(TAG, " - Internal errors:")
internalErrorList.forEach { d(TAG, " - code $it") }
}
loginMethodId?.let { data.prepareFor(edudziennikLoginMethods, it) }
afterLogin?.let { this.afterLogin = it }
EdudziennikLogin(data) {
data()
}
@ -81,29 +79,27 @@ class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStor
d(TAG, " - Internal errors:")
internalErrorList.forEach { d(TAG, " - code $it") }
}
EdudziennikData(data) {
afterLogin?.invoke() ?: EdudziennikData(data) {
completed()
}
}
override fun getMessage(message: MessageFull) {
override fun getMessage(message: MessageFull) {}
override fun sendMessage(recipients: List<Teacher>, subject: String, text: String) {}
override fun markAllAnnouncementsAsRead() {}
}
override fun markAllAnnouncementsAsRead() {
}
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
}
override fun firstLogin() {
EdudziennikFirstLogin(data) {
completed()
override fun getAnnouncement(announcement: AnnouncementFull) {
EdudziennikLoginWeb(data) {
EdudziennikWebGetAnnouncement(data, announcement) {
completed()
}
}
}
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {}
override fun getRecipientList() {}
override fun firstLogin() { EdudziennikFirstLogin(data) { completed() } }
override fun cancel() {
d(TAG, "Cancelled")
data.cancel()
@ -111,18 +107,9 @@ class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStor
private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {
return object : EdziennikCallback {
override fun onCompleted() {
callback.onCompleted()
}
override fun onProgress(step: Float) {
callback.onProgress(step)
}
override fun onStartProgress(stringRes: Int) {
callback.onStartProgress(stringRes)
}
override fun onCompleted() { callback.onCompleted() }
override fun onProgress(step: Float) { callback.onProgress(step) }
override fun onStartProgress(stringRes: Int) { callback.onStartProgress(stringRes) }
override fun onError(apiError: ApiError) {
if (apiError.errorCode in internalErrorList) {
// finish immediately if the same error occurs twice during the same sync
@ -131,11 +118,13 @@ class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStor
}
internalErrorList.add(apiError.errorCode)
when (apiError.errorCode) {
ERROR_EDUDZIENNIK_WEB_SESSION_EXPIRED -> {
login()
}
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

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

View File

@ -11,7 +11,7 @@ import pl.szczodrzynski.edziennik.utils.Utils
class EdudziennikData(val data: DataEdudziennik, val onSuccess: () -> Unit) {
companion object {
private const val TAG = "TemplateData"
private const val TAG = "EdudziennikData"
}
init {
@ -40,6 +40,14 @@ class EdudziennikData(val data: DataEdudziennik, val onSuccess: () -> Unit) {
data.startProgress(R.string.edziennik_progress_endpoint_data)
EdudziennikWebStart(data, onSuccess)
}
ENDPOINT_EDUDZIENNIK_WEB_TEACHERS -> {
data.startProgress(R.string.edziennik_progress_endpoint_teachers)
EdudziennikWebTeachers(data, onSuccess)
}
ENDPOINT_EDUDZIENNIK_WEB_GRADES -> {
data.startProgress(R.string.edziennik_progress_endpoint_grades)
EdudziennikWebGrades(data, onSuccess)
}
ENDPOINT_EDUDZIENNIK_WEB_TIMETABLE -> {
data.startProgress(R.string.edziennik_progress_endpoint_timetable)
EdudziennikWebTimetable(data, onSuccess)
@ -52,6 +60,22 @@ class EdudziennikData(val data: DataEdudziennik, val onSuccess: () -> Unit) {
data.startProgress(R.string.edziennik_progress_endpoint_attendance)
EdudziennikWebAttendance(data, onSuccess)
}
ENDPOINT_EDUDZIENNIK_WEB_ANNOUNCEMENTS -> {
data.startProgress(R.string.edziennik_progress_endpoint_announcements)
EdudziennikWebAnnouncements(data, onSuccess)
}
ENDPOINT_EDUDZIENNIK_WEB_HOMEWORK -> {
data.startProgress(R.string.edziennik_progress_endpoint_homework)
EdudziennikWebHomework(data, onSuccess)
}
ENDPOINT_EDUDZIENNIK_WEB_EVENTS -> {
data.startProgress(R.string.edziennik_progress_endpoint_events)
EdudziennikWebEvents(data, onSuccess)
}
ENDPOINT_EDUDZIENNIK_WEB_NOTES -> {
data.startProgress(R.string.edziennik_progress_endpoint_notices)
EdudziennikWebNotes(data, onSuccess)
}
ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER -> {
data.startProgress(R.string.edziennik_progress_endpoint_lucky_number)
EdudziennikWebLuckyNumber(data, onSuccess)

View File

@ -8,10 +8,7 @@ import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import im.wangchao.mhttp.callback.TextCallbackHandler
import okhttp3.Cookie
import pl.szczodrzynski.edziennik.data.api.EDUDZIENNIK_USER_AGENT
import pl.szczodrzynski.edziennik.data.api.ERROR_REQUEST_FAILURE
import pl.szczodrzynski.edziennik.data.api.ERROR_RESPONSE_EMPTY
import pl.szczodrzynski.edziennik.data.api.EXCEPTION_EDUDZIENNIK_WEB_REQUEST
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
@ -54,7 +51,12 @@ open class EdudziennikWeb(open val data: DataEdudziennik) {
}
override fun onFailure(response: Response?, throwable: Throwable?) {
data.error(ApiError(tag, ERROR_REQUEST_FAILURE)
val error = when (response?.code()) {
402 -> ERROR_EDUDZIENNIK_WEB_LIMITED_ACCESS
403 -> ERROR_EDUDZIENNIK_WEB_SESSION_EXPIRED
else -> ERROR_REQUEST_FAILURE
}
data.error(ApiError(tag, error)
.withResponse(response)
.withThrowable(throwable))
}
@ -68,7 +70,7 @@ open class EdudziennikWeb(open val data: DataEdudziennik) {
.secure().httpOnly().build(),
Cookie.Builder()
.name("semester")
.value((profile?.currentSemester ?: 1).toString())
.value((data.currentSemester).toString())
.domain("dziennikel.appspot.com")
.secure().httpOnly().build()
))

View File

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

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.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Attendance
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.singleOrNull
import pl.szczodrzynski.edziennik.utils.models.Date
@ -40,8 +40,8 @@ class EdudziennikWebAttendance(override val data: DataEdudziennik,
"nieobecność" -> Attendance.TYPE_ABSENT
"spóźnienie" -> Attendance.TYPE_BELATED
"nieobecność usprawiedliwiona" -> Attendance.TYPE_ABSENT_EXCUSED
"dzień wolny" -> Attendance.TYPE_RELEASED
"brak zajęć" -> Attendance.TYPE_RELEASED
"dzień wolny" -> Attendance.TYPE_DAY_FREE
"brak zajęć" -> Attendance.TYPE_DAY_FREE
"oddelegowany" -> Attendance.TYPE_RELEASED
else -> Attendance.TYPE_CUSTOM
}
@ -69,7 +69,7 @@ class EdudziennikWebAttendance(override val data: DataEdudziennik,
id,
lesson?.displayTeacherId ?: -1,
lesson?.displaySubjectId ?: -1,
profile.currentSemester,
data.currentSemester,
name,
date,
lesson?.displayStartTime ?: startTime,

View File

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

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.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
@ -45,7 +45,7 @@ class EdudziennikWebExams(override val data: DataEdudziennik,
val date = Date.fromY_m_d(dateString)
val lessons = data.app.db.timetableDao().getForDateNow(profileId, date)
val startTime = lessons.firstOrNull { it.subjectId == subject.id }?.startTime
val startTime = lessons.firstOrNull { it.displaySubjectId == subject.id }?.displayStartTime
val eventTypeElement = examElement.child(3).child(0)
val eventTypeId = EDUDZIENNIK_EVENT_TYPE_ID.find(eventTypeElement.attr("href"))?.get(1)
@ -78,7 +78,10 @@ class EdudziennikWebExams(override val data: DataEdudziennik,
))
}
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_DEFAULT))
data.toRemove.add(DataRemoveModel.Events.futureExceptTypes(listOf(
Event.TYPE_HOMEWORK,
Event.TYPE_CLASS_EVENT
)))
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_EXAMS, SYNC_ALWAYS)
onSuccess()

View File

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

View File

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

View File

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

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,
@ -20,7 +20,7 @@ class EdudziennikWebLuckyNumber(override val data: DataEdudziennik,
init { data.profile?.also { profile ->
webGet(TAG, data.schoolEndpoint + "Lucky", xhr = true) { text ->
text.toIntOrNull()?.let { luckyNumber ->
text.toIntOrNull()?.also { luckyNumber ->
val luckyNumberObject = LuckyNumber(
profileId,
Date.getToday(),
@ -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

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

View File

@ -4,11 +4,18 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web
import org.jsoup.Jsoup
import pl.szczodrzynski.edziennik.MONTH
import pl.szczodrzynski.edziennik.crc32
import pl.szczodrzynski.edziennik.data.api.ERROR_EDUDZIENNIK_WEB_TEAM_MISSING
import pl.szczodrzynski.edziennik.data.api.Regexes
import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_SUBJECTS_START
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_START
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.Team
import pl.szczodrzynski.edziennik.firstLettersName
import pl.szczodrzynski.edziennik.get
class EdudziennikWebStart(override val data: DataEdudziennik,
val onSuccess: () -> Unit) : EdudziennikWeb(data) {
@ -18,13 +25,53 @@ class EdudziennikWebStart(override val data: DataEdudziennik,
init {
webGet(TAG, data.studentEndpoint + "start") { text ->
val doc = Jsoup.parse(text)
getSchoolAndTeam(text)
getSubjects(text)
EdudziennikWebStartInfo(data, text)
EdudziennikWebStartGrades(data, doc)
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_START, SYNC_ALWAYS)
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_START, MONTH)
onSuccess()
}
}
private fun getSchoolAndTeam(text: String) {
val schoolId = Regexes.EDUDZIENNIK_SCHOOL_DETAIL_ID.find(text)?.get(1)?.trim()
val schoolLongName = Regexes.EDUDZIENNIK_SCHOOL_DETAIL_NAME.find(text)?.get(1)?.trim()
data.schoolId = schoolId
val classId = Regexes.EDUDZIENNIK_CLASS_DETAIL_ID.find(text)?.get(1)?.trim()
val className = Regexes.EDUDZIENNIK_CLASS_DETAIL_NAME.find(text)?.get(1)?.trim()
data.classId = classId
if (classId == null || className == null || schoolId == null || schoolLongName == null) {
data.error(ApiError(TAG, ERROR_EDUDZIENNIK_WEB_TEAM_MISSING)
.withApiResponse(text))
return
}
val schoolName = schoolId.crc32().toString() + schoolLongName.firstLettersName + "_edu"
data.schoolName = schoolName
val teamId = classId.crc32()
val teamCode = "$schoolName:$className"
val teamObject = Team(
data.profileId,
teamId,
className,
Team.TYPE_CLASS,
teamCode,
-1
)
data.teamClass = teamObject
data.teamList.put(teamObject.id, teamObject)
}
private fun getSubjects(text: String) {
EDUDZIENNIK_SUBJECTS_START.findAll(text).forEach {
val id = it[1].trim()
val name = it[2].trim()
data.getSubject(id, name)
}
}
}

View File

@ -1,185 +0,0 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-24
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web
import android.graphics.Color
import org.jsoup.nodes.Document
import pl.szczodrzynski.edziennik.colorFromCssName
import pl.szczodrzynski.edziennik.crc32
import pl.szczodrzynski.edziennik.data.api.Regexes
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
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.get
import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.models.Date
class EdudziennikWebStartGrades(val data: DataEdudziennik, val doc: Document) {
companion object {
const val TAG = "EdudziennikWebStartGrades"
}
init {data.profile?.also { profile ->
val subjects = doc.select("#student_grades tbody").firstOrNull()?.children()
if (subjects.isNullOrEmpty()) return@also
subjects.forEach { subjectElement ->
if (subjectElement.id().isBlank()) return@forEach
val subjectId = subjectElement.id().trim()
val subjectName = subjectElement.child(0).text().trim()
val subject = data.getSubject(subjectId, subjectName)
val grades = subjectElement.select(".grade")
val gradesInfo = subjectElement.select(".grade-tip")
val gradeValues = 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()
Pair(value, weight)
}
grades.forEachIndexed { index, gradeElement ->
val id = Regexes.EDUDZIENNIK_GRADE_ID.find(gradeElement.attr("href"))?.get(1)?.crc32()
?: return@forEachIndexed
val (value, weight) = gradeValues[index]
val name = gradeElement.text().trim().let {
if (it.contains(',') || it.contains('.')) {
val replaced = it.replace(',', '.')
val float = replaced.toFloatOrNull()
if (float != null && float % 1 == 0f) float.toInt().toString()
else it
} else it
}
val info = gradesInfo[index]
val category = info.child(4).text().trim()
val (teacherLastName, teacherFirstName) = info.child(1).text().split(' ')
val teacher = data.getTeacher(teacherFirstName, teacherLastName)
val addedDate = info.child(2).text().split(' ').let {
val day = it[0].toInt()
val month = Utils.monthFromName(it[1])
val year = it[2].toInt()
Date(year, month, day).inMillis
}
val color = Regexes.STYLE_CSS_COLOR.find(gradeElement.attr("style"))?.get(1)?.let {
if (it.startsWith('#')) Color.parseColor(it)
else colorFromCssName(it)
} ?: -1
val gradeObject = Grade(
data.profileId,
id,
category,
color,
"",
name,
value,
weight,
profile.currentSemester,
teacher.id,
subject.id
)
data.gradeList.add(gradeObject)
data.metadataList.add(Metadata(
data.profileId,
Metadata.TYPE_GRADE,
id,
profile.empty,
profile.empty,
addedDate
))
}
val proposed = subjectElement.select(".proposal").firstOrNull()?.text()?.trim()
if (proposed != null && proposed.isNotBlank()) {
val proposedGradeObject = Grade(
data.profileId,
(-1 * subject.id) - 1,
"",
-1,
"",
proposed,
proposed.toFloatOrNull() ?: 0f,
0f,
profile.currentSemester,
-1,
subject.id
).apply {
type = when (semester) {
1 -> TYPE_SEMESTER1_PROPOSED
else -> TYPE_SEMESTER2_PROPOSED
}
}
data.gradeList.add(proposedGradeObject)
data.metadataList.add(Metadata(
data.profileId,
Metadata.TYPE_GRADE,
proposedGradeObject.id,
profile.empty,
profile.empty,
System.currentTimeMillis()
))
}
val final = subjectElement.select(".final").firstOrNull()?.text()?.trim()
if (final != null && final.isNotBlank()) {
val finalGradeObject = Grade(
data.profileId,
(-1 * subject.id) - 2,
"",
-1,
"",
final,
final.toFloatOrNull() ?: 0f,
0f,
profile.currentSemester,
-1,
subject.id
).apply {
type = when (semester) {
1 -> TYPE_SEMESTER1_FINAL
else -> TYPE_SEMESTER2_FINAL
}
}
data.gradeList.add(finalGradeObject)
data.metadataList.add(Metadata(
data.profileId,
Metadata.TYPE_GRADE,
finalGradeObject.id,
profile.empty,
profile.empty,
System.currentTimeMillis()
))
}
}
data.toRemove.addAll(listOf(
TYPE_NORMAL,
TYPE_SEMESTER1_PROPOSED,
TYPE_SEMESTER2_PROPOSED,
TYPE_SEMESTER1_FINAL,
TYPE_SEMESTER2_FINAL
).map {
DataRemoveModel.Grades.semesterWithType(profile.currentSemester, it)
})
}}
}

View File

@ -1,54 +0,0 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-24
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web
import pl.szczodrzynski.edziennik.crc32
import pl.szczodrzynski.edziennik.data.api.ERROR_EDUDZIENNIK_WEB_TEAM_MISSING
import pl.szczodrzynski.edziennik.data.api.Regexes
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.modules.teams.Team
import pl.szczodrzynski.edziennik.firstLettersName
import pl.szczodrzynski.edziennik.get
class EdudziennikWebStartInfo(val data: DataEdudziennik, val text: String) {
companion object {
const val TAG = "EdudziennikWebStartInfo"
}
init { run {
val schoolId = Regexes.EDUDZIENNIK_SCHOOL_DETAIL_ID.find(text)?.get(1)?.trim()
val schoolLongName = Regexes.EDUDZIENNIK_SCHOOL_DETAIL_NAME.find(text)?.get(1)?.trim()
data.schoolId = schoolId
val classId = Regexes.EDUDZIENNIK_CLASS_DETAIL_ID.find(text)?.get(1)?.trim()
val className = Regexes.EDUDZIENNIK_CLASS_DETAIL_NAME.find(text)?.get(1)?.trim()
data.classId = classId
if (classId == null || className == null || schoolId == null || schoolLongName == null) {
data.error(ApiError(TAG, ERROR_EDUDZIENNIK_WEB_TEAM_MISSING)
.withApiResponse(text))
return@run
}
val schoolName = schoolId.crc32().toString() + schoolLongName.firstLettersName + "_edu"
data.schoolName = schoolName
val teamId = classId.crc32()
val teamCode = "$schoolName:$className"
val teamObject = Team(
data.profileId,
teamId,
className,
Team.TYPE_CLASS,
teamCode,
-1
)
data.teamClass = teamObject
data.teamList.put(teamObject.id, teamObject)
}}
}

View File

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

View File

@ -5,21 +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
@ -56,90 +54,85 @@ class EdudziennikWebTimetable(override val data: DataEdudziennik,
val table = doc.select("#Schedule tbody").first()
if (table.text().trim() == "Brak planu lekcji.") {
data.error(ApiError(TAG, ERROR_EDUDZIENNIK_WEB_TIMETABLE_NOT_PUBLIC)
.withApiResponse(text))
onSuccess()
return@webGet
}
if (!table.text().contains("Brak planu lekcji.")) {
table.children().forEach { row ->
val rowElements = row.children()
table.children().forEach { row ->
val rowElements = row.children()
val lessonNumber = rowElements[0].text().toInt()
val lessonNumber = rowElements[0].text().toInt()
val times = rowElements[1].text().split('-')
val startTime = Time.fromH_m(times[0].trim())
val endTime = Time.fromH_m(times[1].trim())
val times = rowElements[1].text().split('-')
val startTime = Time.fromH_m(times[0].trim())
val endTime = Time.fromH_m(times[1].trim())
data.lessonRanges.singleOrNull {
it.lessonNumber == lessonNumber && it.startTime == startTime && it.endTime == endTime
} ?: run {
data.lessonRanges.put(lessonNumber, LessonRange(profileId, lessonNumber, startTime, endTime))
}
rowElements.subList(2, rowElements.size).forEachIndexed { index, lesson ->
val course = lesson.select(".course").firstOrNull() ?: return@forEachIndexed
val info = course.select("span > span")
if (info.isEmpty()) return@forEachIndexed
val type = when (course.hasClass("substitute")) {
true -> Lesson.TYPE_CHANGE
else -> Lesson.TYPE_NORMAL
data.lessonRanges.singleOrNull {
it.lessonNumber == lessonNumber && it.startTime == startTime && it.endTime == endTime
} ?: run {
data.lessonRanges.put(lessonNumber, LessonRange(profileId, lessonNumber, startTime, endTime))
}
/* Getting subject */
rowElements.subList(2, rowElements.size).forEachIndexed { index, lesson ->
val course = lesson.select(".course").firstOrNull() ?: return@forEachIndexed
val info = course.select("span > span")
val subjectElement = info[0].child(0)
val subjectId = EDUDZIENNIK_SUBJECT_ID.find(subjectElement.attr("href"))?.get(1)
?: return@forEachIndexed
val subjectName = subjectElement.text().trim()
val subject = data.getSubject(subjectId, subjectName)
if (info.isEmpty()) return@forEachIndexed
/* Getting teacher */
val type = when (course.hasClass("substitute")) {
true -> Lesson.TYPE_CHANGE
else -> Lesson.TYPE_NORMAL
}
val teacherId = if (info.size >= 2) {
val teacherElement = info[1].child(0)
val teacherName = teacherElement.text().trim()
teacherName.splitName()?.let { (teacherLastName, teacherFirstName) ->
data.getTeacher(teacherFirstName, teacherLastName)
}?.id ?: -1
} else -1
/* Getting subject */
val lessonObject = Lesson(profileId, -1).also {
it.type = type
it.date = weekStart.clone().stepForward(0, 0, index)
it.lessonNumber = lessonNumber
it.startTime = startTime
it.endTime = endTime
it.subjectId = subject.id
it.teacherId = teacherId
val subjectElement = info[0].child(0)
val subjectId = EDUDZIENNIK_SUBJECT_ID.find(subjectElement.attr("href"))?.get(1)
?: return@forEachIndexed
val subjectName = subjectElement.text().trim()
val subject = data.getSubject(subjectId, subjectName)
it.id = it.buildId()
}
/* Getting teacher */
data.lessonNewList.add(lessonObject)
dataDays.remove(lessonObject.date!!.value)
val teacherId = if (info.size >= 2) {
val teacherElement = info[1].child(0)
val teacherLongId = EDUDZIENNIK_TEACHER_ID.find(teacherElement.attr("href"))?.get(1)
val teacherName = teacherElement.text().trim()
data.getTeacherByLastFirst(teacherName, teacherLongId).id
} else null
if (type != Lesson.TYPE_NORMAL) {
val seen = profile.empty || lessonObject.date!! < Date.getToday()
val lessonObject = Lesson(profileId, -1).also {
it.type = type
it.date = weekStart.clone().stepForward(0, 0, index)
it.lessonNumber = lessonNumber
it.startTime = startTime
it.endTime = endTime
it.subjectId = subject.id
it.teacherId = teacherId
it.teamId = data.teamClass?.id
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_LESSON_CHANGE,
lessonObject.id,
seen,
seen,
System.currentTimeMillis()
))
it.id = it.buildId()
}
data.lessonList.add(lessonObject)
dataDays.remove(lessonObject.date!!.value)
if (type != Lesson.TYPE_NORMAL) {
val seen = profile.empty || lessonObject.date!! < Date.getToday()
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_LESSON_CHANGE,
lessonObject.id,
seen,
seen,
System.currentTimeMillis()
))
}
}
}
}
for (day in dataDays) {
val lessonDate = Date.fromValue(day)
data.lessonNewList += Lesson(profileId, lessonDate.value.toLong()).apply {
data.lessonList += Lesson(profileId, lessonDate.value.toLong()).apply {
type = Lesson.TYPE_NO_LESSONS
date = lessonDate
}
@ -147,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

@ -13,6 +13,7 @@ import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.getUnixDate
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
import pl.szczodrzynski.edziennik.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.models.Date
class EdudziennikLoginWeb(val data: DataEdudziennik, val onSuccess: () -> Unit) {
companion object {
@ -61,6 +62,7 @@ class EdudziennikLoginWeb(val data: DataEdudziennik, val onSuccess: () -> Unit)
val cookies = data.app.cookieJar.getForDomain("dziennikel.appspot.com")
val sessionId = cookies.firstOrNull { it.name() == "sessionid" }?.value()
val semester = cookies.firstOrNull { it.name() == "semester" }?.value()?.toIntOrNull()
if (sessionId == null) {
data.error(ApiError(TAG, ERROR_LOGIN_EDUDZIENNIK_WEB_NO_SESSION_ID)
@ -70,6 +72,14 @@ class EdudziennikLoginWeb(val data: DataEdudziennik, val onSuccess: () -> Unit)
}
data.webSessionId = sessionId
if (data.profile != null && semester != null) {
data.currentSemester = semester
if (semester == 2 && data.profile.dateSemester2Start > Date.getToday())
data.profile.dateSemester2Start = Date.getToday().stepForward(0, 0, -1)
}
data.webSessionIdExpiryTime = response.getUnixDate() + 45 * 60 /* 45 min */
onSuccess()
}

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

@ -6,19 +6,23 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik
import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.CODE_INTERNAL_LIBRUS_ACCOUNT_410
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikData
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebGetAttachment
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebGetMessage
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebGetRecipientList
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebSendMessage
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.firstlogin.IdziennikFirstLogin
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login.IdziennikLogin
import pl.szczodrzynski.edziennik.data.api.idziennikLoginMethods
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.prepare
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.utils.Utils.d
class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
@ -28,6 +32,7 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
val internalErrorList = mutableListOf<Int>()
val data: DataIdziennik
private var afterLogin: (() -> Unit)? = null
init {
data = DataIdziennik(app, profile, loginStore).apply {
@ -54,33 +59,69 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) {
data.arguments = arguments
data.prepare(idziennikLoginMethods, IdziennikFeatures, featureIds, viewId)
d(TAG, "LoginMethod IDs: ${data.targetLoginMethodIds}")
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
login()
}
private fun login(loginMethodId: Int? = null, afterLogin: (() -> Unit)? = null) {
d(TAG, "Trying to login with ${data.targetLoginMethodIds}")
if (internalErrorList.isNotEmpty()) {
d(TAG, " - Internal errors:")
internalErrorList.forEach { d(TAG, " - code $it") }
}
loginMethodId?.let { data.prepareFor(idziennikLoginMethods, it) }
afterLogin?.let { this.afterLogin = it }
IdziennikLogin(data) {
IdziennikData(data) {
data()
}
}
private fun data() {
d(TAG, "Endpoint IDs: ${data.targetEndpointIds}")
if (internalErrorList.isNotEmpty()) {
d(TAG, " - Internal errors:")
internalErrorList.forEach { d(TAG, " - code $it") }
}
afterLogin?.invoke() ?: IdziennikData(data) {
completed()
}
}
override fun getMessage(message: MessageFull) {
login(LOGIN_METHOD_IDZIENNIK_WEB) {
IdziennikWebGetMessage(data, message) {
completed()
}
}
}
override fun getMessage(message: MessageFull) {
}
override fun markAllAnnouncementsAsRead() {
}
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
}
override fun firstLogin() {
IdziennikFirstLogin(data) {
completed()
override fun sendMessage(recipients: List<Teacher>, subject: String, text: String) {
login(LOGIN_METHOD_IDZIENNIK_API) {
IdziennikWebSendMessage(data, recipients, subject, text) {
completed()
}
}
}
override fun markAllAnnouncementsAsRead() {}
override fun getAnnouncement(announcement: AnnouncementFull) {}
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
login(LOGIN_METHOD_IDZIENNIK_WEB) {
IdziennikWebGetAttachment(data, message, attachmentId, attachmentName) {
completed()
}
}
}
override fun getRecipientList() {
login(LOGIN_METHOD_IDZIENNIK_WEB) {
IdziennikWebGetRecipientList(data) {
completed()
}
}
}
override fun firstLogin() { IdziennikFirstLogin(data) { completed() } }
override fun cancel() {
d(TAG, "Cancelled")
data.cancel()
@ -88,28 +129,29 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {
return object : EdziennikCallback {
override fun onCompleted() {
callback.onCompleted()
}
override fun onProgress(step: Float) {
callback.onProgress(step)
}
override fun onStartProgress(stringRes: Int) {
callback.onStartProgress(stringRes)
}
override fun onCompleted() { callback.onCompleted() }
override fun onProgress(step: Float) { callback.onProgress(step) }
override fun onStartProgress(stringRes: Int) { callback.onStartProgress(stringRes) }
override fun onError(apiError: ApiError) {
if (apiError.errorCode in internalErrorList) {
// finish immediately if the same error occurs twice during the same sync
callback.onError(apiError)
return
}
internalErrorList.add(apiError.errorCode)
when (apiError.errorCode) {
in internalErrorList -> {
// finish immediately if the same error occurs twice during the same sync
callback.onError(apiError)
ERROR_LOGIN_IDZIENNIK_WEB_NO_SESSION,
ERROR_LOGIN_IDZIENNIK_WEB_NO_AUTH,
ERROR_LOGIN_IDZIENNIK_WEB_NO_BEARER,
ERROR_IDZIENNIK_WEB_ACCESS_DENIED,
ERROR_IDZIENNIK_API_ACCESS_DENIED -> {
data.loginMethods.remove(LOGIN_METHOD_IDZIENNIK_WEB)
data.prepareFor(idziennikLoginMethods, LOGIN_METHOD_IDZIENNIK_WEB)
data.loginExpiryTime = 0
login()
}
CODE_INTERNAL_LIBRUS_ACCOUNT_410 -> {
internalErrorList.add(apiError.errorCode)
loginStore.removeLoginData("refreshToken") // force a clean login
//loginLibrus()
ERROR_IDZIENNIK_API_NO_REGISTER -> {
data()
}
else -> callback.onError(apiError)
}

View File

@ -55,6 +55,7 @@ open class IdziennikApi(open val data: DataIdziennik) {
}
error?.let { code ->
when (code) {
"Uczeń nie posiada aktywnej pozycji w dzienniku" -> ERROR_IDZIENNIK_API_NO_REGISTER
"Authorization has been denied for this request." -> ERROR_IDZIENNIK_API_ACCESS_DENIED
else -> ERROR_IDZIENNIK_API_OTHER
}.let { errorCode ->
@ -107,6 +108,7 @@ open class IdziennikApi(open val data: DataIdziennik) {
}
}
}
.allowErrorCode(HttpURLConnection.HTTP_BAD_REQUEST)
.allowErrorCode(HttpURLConnection.HTTP_UNAUTHORIZED)
.allowErrorCode(HttpURLConnection.HTTP_INTERNAL_ERROR)
.callback(callback)

View File

@ -8,12 +8,14 @@ import com.google.gson.JsonArray
import com.google.gson.JsonObject
import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import im.wangchao.mhttp.callback.FileCallbackHandler
import im.wangchao.mhttp.callback.JsonCallbackHandler
import im.wangchao.mhttp.callback.TextCallbackHandler
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.utils.Utils.d
import java.io.File
import java.net.HttpURLConnection.HTTP_INTERNAL_ERROR
import java.net.HttpURLConnection.HTTP_UNAUTHORIZED
@ -39,6 +41,13 @@ open class IdziennikWeb(open val data: DataIdziennik) {
return
}
if (response?.code() == HTTP_INTERNAL_ERROR && endpoint == IDZIENNIK_WEB_GET_RECIPIENT_LIST) {
data.error(ApiError(tag, ERROR_IDZIENNIK_WEB_RECIPIENT_LIST_NO_PERMISSION)
.withResponse(response)
.withApiResponse(json))
return
}
when {
response?.code() == HTTP_UNAUTHORIZED -> ERROR_IDZIENNIK_WEB_ACCESS_DENIED
response?.code() == HTTP_INTERNAL_ERROR -> ERROR_IDZIENNIK_WEB_SERVER_ERROR
@ -156,4 +165,54 @@ open class IdziennikWeb(open val data: DataIdziennik) {
.build()
.enqueue()
}
fun webGetFile(tag: String, endpoint: String, targetFile: File, parameters: Map<String, Any>,
onSuccess: (file: File) -> Unit, onProgress: (written: Long, total: Long) -> Unit) {
d(tag, "Request: Idziennik/Web - $IDZIENNIK_WEB_URL/$endpoint")
val callback = object : FileCallbackHandler(targetFile) {
override fun onSuccess(file: File?, response: Response?) {
if (file == null) {
data.error(ApiError(TAG, ERROR_FILE_DOWNLOAD)
.withResponse(response))
return
}
try {
onSuccess(file)
} catch (e: Exception) {
data.error(ApiError(tag, EXCEPTION_EDUDZIENNIK_FILE_REQUEST)
.withResponse(response)
.withThrowable(e))
}
}
override fun onProgress(bytesWritten: Long, bytesTotal: Long) {
try {
onProgress(bytesWritten, bytesTotal)
} catch (e: Exception) {
data.error(ApiError(tag, EXCEPTION_EDUDZIENNIK_FILE_REQUEST)
.withThrowable(e))
}
}
override fun onFailure(response: Response?, throwable: Throwable?) {
data.error(ApiError(tag, ERROR_REQUEST_FAILURE)
.withResponse(response)
.withThrowable(throwable))
}
}
Request.builder()
.url("$IDZIENNIK_WEB_URL/$endpoint")
.userAgent(IDZIENNIK_USER_AGENT)
.apply {
parameters.forEach { (k, v) -> addParameter(k, v) }
}
.post()
.callback(callback)
.build()
.enqueue()
}
}

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

@ -5,17 +5,18 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.api
import com.google.gson.JsonArray
import pl.szczodrzynski.edziennik.asJsonObjectList
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.asJsonObjectList
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.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
@ -51,11 +52,18 @@ class IdziennikApiMessagesInbox(override val data: DataIdziennik,
val sentDate = Date.fromIso(jMessage.getString("dataWyslania"))
val sender = jMessage.getAsJsonObject("nadawca")
var firstName = sender.getString("imie")
var lastName = sender.getString("nazwisko")
if (firstName.isNullOrEmpty() || lastName.isNullOrEmpty()) {
firstName = "usunięty"
lastName = "użytkownik"
}
val rTeacher = data.getTeacher(
sender.getString("imie") ?: "",
sender.getString("nazwisko") ?: ""
firstName,
lastName
)
rTeacher.loginId = sender.getString("id") + ":" + sender.getString("usr")
rTeacher.loginId = /*sender.getString("id") + ":" + */sender.getString("usr")
rTeacher.setTeacherType(Teacher.TYPE_OTHER)
val message = Message(
profileId,
@ -77,7 +85,7 @@ class IdziennikApiMessagesInbox(override val data: DataIdziennik,
data.messageIgnoreList.add(message)
data.messageRecipientList.add(messageRecipient)
data.messageMetadataList.add(Metadata(
data.setSeenMetadataList.add(Metadata(
profileId,
Metadata.TYPE_MESSAGE,
message.id,

View File

@ -7,15 +7,15 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.api
import com.google.gson.JsonArray
import pl.szczodrzynski.edziennik.DAY
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES
import pl.szczodrzynski.edziennik.asJsonObjectList
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.asJsonObjectList
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
@ -62,7 +62,7 @@ class IdziennikApiMessagesSent(override val data: DataIdziennik,
lastName = "użytkownik"
}
val rTeacher = data.getTeacher(firstName, lastName)
rTeacher.loginId = recipient.get("id").asString + ":" + recipient.get("usr").asString
rTeacher.loginId = /*recipient.get("id").asString + ":" + */recipient.get("usr").asString
val messageRecipient = MessageRecipient(
profileId,

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
@ -54,7 +54,8 @@ class IdziennikWebAnnouncements(override val data: DataIdziennik,
jAnnouncement.get("Tresc").asString,
startDate,
null,
rTeacher.id
rTeacher.id,
null
)
data.announcementList.add(announcementObject)
data.metadataList.add(Metadata(

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

@ -0,0 +1,60 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-28
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
import org.greenrobot.eventbus.EventBus
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.entity.Message
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.utils.Utils
import java.io.File
class IdziennikWebGetAttachment(
override val data: DataIdziennik, val message: Message, val attachmentId: Long,
val attachmentName: String, val onSuccess: () -> Unit
) : IdziennikWeb(data) {
companion object {
const val TAG = "IdziennikWebGetAttachment"
}
init {
val messageId = "\\[META:([A-z0-9]+);([0-9-]+)]".toRegex().find(message.body ?: "")?.get(2) ?: -1
val targetFile = File(Utils.getStorageDir(), attachmentName)
webGetFile(TAG, IDZIENNIK_WEB_GET_ATTACHMENT, targetFile, mapOf(
"id" to messageId,
"fileName" to attachmentName
), { file ->
val event = AttachmentGetEvent(
profileId,
message.id,
attachmentId,
AttachmentGetEvent.TYPE_FINISHED,
file.absolutePath
)
val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.messageId}_${event.attachmentId}")
Utils.writeStringToFile(attachmentDataFile, event.fileName)
EventBus.getDefault().post(event)
onSuccess()
}) { written, _ ->
val event = AttachmentGetEvent(
profileId,
message.id,
attachmentId,
AttachmentGetEvent.TYPE_PROGRESS,
bytesWritten = written
)
EventBus.getDefault().post(event)
}
}
}

View File

@ -0,0 +1,102 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-28
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.*
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.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(
override val data: DataIdziennik,
private val message: MessageFull,
val onSuccess: () -> Unit
) : IdziennikWeb(data) {
companion object {
const val TAG = "IdziennikWebGetMessage"
}
init { data.profile?.also { profile ->
val metaPattern = "\\[META:([A-z0-9]+);([0-9-]+)]".toRegex()
val meta = metaPattern.find(message.body!!)
val messageIdString = meta?.get(1) ?: ""
webApiGet(TAG, IDZIENNIK_WEB_GET_MESSAGE, parameters = mapOf(
"idWiadomosci" to messageIdString,
"typWiadomosci" to if (message.type == TYPE_SENT) 1 else 0
)) { json ->
json.getJsonObject("d")?.getJsonObject("Wiadomosc")?.also {
val id = it.getLong("_recordId")
message.body = message.body?.replace(metaPattern, "[META:$messageIdString;$id]")
message.clearAttachments()
it.getJsonArray("ListaZal")?.asJsonObjectList()?.forEach { attachment ->
message.addAttachment(
attachment.getLong("Id") ?: return@forEach,
attachment.getString("Nazwa") ?: return@forEach,
-1
)
}
message.recipients?.clear()
when (message.type) {
TYPE_RECEIVED -> {
val recipientObject = MessageRecipientFull(profileId, -1, message.id)
val readDateString = it.getString("DataOdczytania")
recipientObject.readDate = if (readDateString.isNullOrBlank()) System.currentTimeMillis()
else Date.fromIso(readDateString)
recipientObject.fullName = profile.accountName ?: profile.studentNameLong
data.messageRecipientList.add(recipientObject)
message.addRecipient(recipientObject)
}
TYPE_SENT -> {
it.getJsonArray("ListaOdbiorcow")?.asJsonObjectList()?.forEach { recipient ->
val recipientName = recipient.getString("NazwaOdbiorcy") ?: return@forEach
val teacher = data.getTeacherByLastFirst(recipientName)
val recipientObject = MessageRecipientFull(profileId, teacher.id, message.id)
recipientObject.readDate = recipient.getLong("Status") ?: return@forEach
recipientObject.fullName = teacher.fullName
data.messageRecipientList.add(recipientObject)
message.addRecipient(recipientObject)
}
}
}
if (!message.seen) {
message.seen = true
data.setSeenMetadataList.add(Metadata(
profileId,
Metadata.TYPE_MESSAGE,
message.id,
message.seen,
message.notified,
message.addedDate
))
}
EventBus.getDefault().postSticky(MessageGetEvent(message))
data.messageList.add(message)
onSuccess()
}
}
} ?: onSuccess() }
}

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-12-30.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
import androidx.room.OnConflictStrategy
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_GET_RECIPIENT_LIST
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.events.RecipientListGetEvent
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
class IdziennikWebGetRecipientList(
override val data: DataIdziennik, val onSuccess: () -> Unit) : IdziennikWeb(data) {
companion object {
private const val TAG = "IdziennikWebGetRecipientList"
}
init {
webApiGet(TAG, IDZIENNIK_WEB_GET_RECIPIENT_LIST, mapOf(
"idP" to data.registerId
)) { result ->
val json = result.getJsonObject("d") ?: run {
data.error(ApiError(TAG, ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA)
.withApiResponse(result))
return@webApiGet
}
json.getJsonArray("ListK_Pracownicy")?.asJsonObjectList()?.forEach { recipient ->
val name = recipient.getString("ImieNazwisko") ?: ": "
val (fullName, subject) = name.split(": ").let {
Pair(it.getOrNull(0), it.getOrNull(1))
}
val guid = recipient.getString("Id") ?: ""
// get teacher by ID or create it
val teacher = data.getTeacherByFirstLast(fullName ?: " ")
teacher.loginId = guid
teacher.setTeacherType(Teacher.TYPE_TEACHER)
// unset OTHER that is automatically set in IdziennikApiMessages*
teacher.unsetTeacherType(Teacher.TYPE_OTHER)
teacher.typeDescription = subject
}
json.getJsonArray("ListK_Opiekunowie")?.asJsonObjectList()?.forEach { recipient ->
val name = recipient.getString("ImieNazwisko") ?: ": "
val (fullName, parentOf) = Regexes.IDZIENNIK_MESSAGES_RECIPIENT_PARENT.find(name)?.let {
Pair(it.groupValues.getOrNull(1), it.groupValues.getOrNull(2))
} ?: Pair(null, null)
val guid = recipient.getString("Id") ?: ""
// get teacher by ID or create it
val teacher = data.getTeacherByFirstLast(fullName ?: " ")
teacher.loginId = guid
teacher.setTeacherType(Teacher.TYPE_PARENT)
// unset OTHER that is automatically set in IdziennikApiMessages*
teacher.unsetTeacherType(Teacher.TYPE_OTHER)
teacher.typeDescription = parentOf
}
val event = RecipientListGetEvent(
data.profileId,
data.teacherList.filter { it.loginId != null }
)
profile?.lastReceiversSync = System.currentTimeMillis()
data.teacherOnConflictStrategy = OnConflictStrategy.REPLACE
EventBus.getDefault().postSticky(event)
onSuccess()
}
}
}

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

@ -0,0 +1,70 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-12-30.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_SEND_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.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.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import java.util.*
class IdziennikWebSendMessage(
override val data: DataIdziennik,
val recipients: List<Teacher>,
val subject: String,
val text: String,
val onSuccess: () -> Unit
) : IdziennikWeb(data) {
companion object {
private const val TAG = "IdziennikWebSendMessage"
}
init {
val recipientsArray = JsonArray()
for (teacher in recipients) {
teacher.loginId?.let {
recipientsArray += it
}
}
webApiGet(TAG, IDZIENNIK_WEB_SEND_MESSAGE, mapOf(
"Wiadomosc" to JsonObject(
"Tytul" to subject,
"Tresc" to text,
"Confirmation" to false,
"GuidMessage" to UUID.randomUUID().toString().toUpperCase(Locale.ROOT),
"Odbiorcy" to recipientsArray
)
)) { result ->
val json = result.getJsonObject("d") ?: run {
data.error(ApiError(TAG, ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA)
.withApiResponse(result))
return@webApiGet
}
if (json.getBoolean("CzyJestBlad") != false) {
// TODO error
return@webApiGet
}
IdziennikApiMessagesSent(data) {
val message = data.messageIgnoreList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject }
val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id }
val event = MessageSentEvent(data.profileId, message, metadata?.addedDate)
EventBus.getDefault().postSticky(event)
onSuccess()
}
}
}
}

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

@ -8,18 +8,23 @@ import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusData
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api.LibrusApiAnnouncementMarkAsRead
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesGetAttachment
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesGetMessage
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesGetRecipientList
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesSendMessage
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.LibrusSynergiaMarkAllAnnouncementsAsRead
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.firstlogin.LibrusFirstLogin
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.*
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.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.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.utils.Utils.d
class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
@ -29,6 +34,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
val internalErrorList = mutableListOf<Int>()
val data: DataLibrus
private var afterLogin: (() -> Unit)? = null
init {
data = DataLibrus(app, profile, loginStore).apply {
@ -58,12 +64,14 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
login()
}
private fun login() {
private fun login(loginMethodId: Int? = null, afterLogin: (() -> Unit)? = null) {
d(TAG, "Trying to login with ${data.targetLoginMethodIds}")
if (internalErrorList.isNotEmpty()) {
d(TAG, " - Internal errors:")
internalErrorList.forEach { d(TAG, " - code $it") }
}
loginMethodId?.let { data.prepareFor(librusLoginMethods, it) }
afterLogin?.let { this.afterLogin = it }
LibrusLogin(data) {
data()
}
@ -75,57 +83,60 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
d(TAG, " - Internal errors:")
internalErrorList.forEach { d(TAG, " - code $it") }
}
LibrusData(data) {
afterLogin?.invoke() ?: LibrusData(data) {
completed()
}
}
override fun getMessage(message: MessageFull) {
LibrusLoginPortal(data) {
LibrusLoginApi(data) {
LibrusLoginSynergia(data) {
LibrusLoginMessages(data) {
LibrusMessagesGetMessage(data, message) {
completed()
}
}
}
login(LOGIN_METHOD_LIBRUS_MESSAGES) {
LibrusMessagesGetMessage(data, message) {
completed()
}
}
}
override fun sendMessage(recipients: List<Teacher>, subject: String, text: String) {
login(LOGIN_METHOD_LIBRUS_MESSAGES) {
LibrusMessagesSendMessage(data, recipients, subject, text) {
completed()
}
}
}
override fun markAllAnnouncementsAsRead() {
LibrusLoginPortal(data) {
LibrusLoginApi(data) {
LibrusLoginSynergia(data) {
LibrusSynergiaMarkAllAnnouncementsAsRead(data) {
completed()
}
}
login(LOGIN_METHOD_LIBRUS_SYNERGIA) {
LibrusSynergiaMarkAllAnnouncementsAsRead(data) {
completed()
}
}
}
override fun getAnnouncement(announcement: AnnouncementFull) {
login(LOGIN_METHOD_LIBRUS_API) {
LibrusApiAnnouncementMarkAsRead(data, announcement) {
completed()
}
}
}
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
LibrusLoginPortal(data) {
LibrusLoginApi(data) {
LibrusLoginSynergia(data) {
LibrusLoginMessages(data) {
LibrusMessagesGetAttachment(data, message, attachmentId, attachmentName) {
completed()
}
}
}
login(LOGIN_METHOD_LIBRUS_MESSAGES) {
LibrusMessagesGetAttachment(data, message, attachmentId, attachmentName) {
completed()
}
}
}
override fun firstLogin() {
LibrusFirstLogin(data) {
completed()
override fun getRecipientList() {
login(LOGIN_METHOD_LIBRUS_MESSAGES) {
LibrusMessagesGetRecipientList(data) {
completed()
}
}
}
override fun firstLogin() { LibrusFirstLogin(data) { completed() } }
override fun cancel() {
d(TAG, "Cancelled")
data.cancel()
@ -133,18 +144,9 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
private fun wrapCallback(callback: EdziennikCallback): EdziennikCallback {
return object : EdziennikCallback {
override fun onCompleted() {
callback.onCompleted()
}
override fun onProgress(step: Float) {
callback.onProgress(step)
}
override fun onStartProgress(stringRes: Int) {
callback.onStartProgress(stringRes)
}
override fun onCompleted() { callback.onCompleted() }
override fun onProgress(step: Float) { callback.onProgress(step) }
override fun onStartProgress(stringRes: Int) { callback.onStartProgress(stringRes) }
override fun onError(apiError: ApiError) {
if (apiError.errorCode in internalErrorList) {
// finish immediately if the same error occurs twice during the same sync
@ -155,30 +157,26 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
when (apiError.errorCode) {
ERROR_LIBRUS_PORTAL_ACCESS_DENIED -> {
data.loginMethods.remove(LOGIN_METHOD_LIBRUS_PORTAL)
data.targetLoginMethodIds.add(LOGIN_METHOD_LIBRUS_PORTAL)
data.targetLoginMethodIds.sort()
data.prepareFor(librusLoginMethods, LOGIN_METHOD_LIBRUS_PORTAL)
data.portalTokenExpiryTime = 0
login()
}
ERROR_LIBRUS_API_ACCESS_DENIED,
ERROR_LIBRUS_API_TOKEN_EXPIRED -> {
data.loginMethods.remove(LOGIN_METHOD_LIBRUS_API)
data.targetLoginMethodIds.add(LOGIN_METHOD_LIBRUS_API)
data.targetLoginMethodIds.sort()
data.prepareFor(librusLoginMethods, LOGIN_METHOD_LIBRUS_API)
data.apiTokenExpiryTime = 0
login()
}
ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED -> {
data.loginMethods.remove(LOGIN_METHOD_LIBRUS_SYNERGIA)
data.targetLoginMethodIds.add(LOGIN_METHOD_LIBRUS_SYNERGIA)
data.targetLoginMethodIds.sort()
data.prepareFor(librusLoginMethods, LOGIN_METHOD_LIBRUS_SYNERGIA)
data.synergiaSessionIdExpiryTime = 0
login()
}
ERROR_LIBRUS_MESSAGES_ACCESS_DENIED -> {
data.loginMethods.remove(LOGIN_METHOD_LIBRUS_MESSAGES)
data.targetLoginMethodIds.add(LOGIN_METHOD_LIBRUS_MESSAGES)
data.targetLoginMethodIds.sort()
data.prepareFor(librusLoginMethods, LOGIN_METHOD_LIBRUS_MESSAGES)
data.messagesSessionIdExpiryTime = 0
login()
}

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")
@ -65,10 +65,12 @@ open class LibrusApi(open val data: DataLibrus) {
"Nieprawidłowy węzeł." -> ERROR_LIBRUS_API_INCORRECT_ENDPOINT
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)
@ -89,6 +93,19 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
data.startProgress(R.string.edziennik_progress_endpoint_grade_categories)
LibrusApiBehaviourGradeCategories(data, onSuccess)
}
ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADE_CATEGORIES -> {
data.startProgress(R.string.edziennik_progress_endpoint_grade_categories)
LibrusApiDescriptiveGradeCategories(data, onSuccess)
}
ENDPOINT_LIBRUS_API_TEXT_GRADE_CATEGORIES -> {
data.startProgress(R.string.edziennik_progress_endpoint_grade_categories)
LibrusApiTextGradeCategories(data, onSuccess)
}
ENDPOINT_LIBRUS_API_POINT_GRADE_CATEGORIES -> {
data.startProgress(R.string.edziennik_progress_endpoint_grade_categories)
LibrusApiPointGradeCategories(data, onSuccess)
}
ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS -> {
data.startProgress(R.string.edziennik_progress_endpoint_grade_comments)
LibrusApiGradeComments(data, onSuccess)
@ -97,6 +114,7 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
data.startProgress(R.string.edziennik_progress_endpoint_grade_comments)
LibrusApiBehaviourGradeComments(data, onSuccess)
}
ENDPOINT_LIBRUS_API_NORMAL_GRADES -> {
data.startProgress(R.string.edziennik_progress_endpoint_grades)
LibrusApiGrades(data, onSuccess)
@ -105,7 +123,18 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
data.startProgress(R.string.edziennik_progress_endpoint_behaviour_grades)
LibrusApiBehaviourGrades(data, onSuccess)
}
// TODO grades
ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES -> {
data.startProgress(R.string.edziennik_progress_endpoint_descriptive_grades)
LibrusApiDescriptiveGrades(data, onSuccess)
}
ENDPOINT_LIBRUS_API_TEXT_GRADES -> {
data.startProgress(R.string.edziennik_progress_endpoint_descriptive_grades)
LibrusApiTextGrades(data, onSuccess)
}
ENDPOINT_LIBRUS_API_POINT_GRADES -> {
data.startProgress(R.string.edziennik_progress_endpoint_point_grades)
LibrusApiPointGrades(data, onSuccess)
}
ENDPOINT_LIBRUS_API_EVENT_TYPES -> {
data.startProgress(R.string.edziennik_progress_endpoint_event_types)

View File

@ -5,6 +5,7 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import im.wangchao.mhttp.body.MediaTypeUtils
@ -12,6 +13,8 @@ import im.wangchao.mhttp.callback.FileCallbackHandler
import im.wangchao.mhttp.callback.JsonCallbackHandler
import im.wangchao.mhttp.callback.TextCallbackHandler
import okhttp3.Cookie
import org.json.JSONObject
import org.json.XML
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.parser.Parser
@ -38,10 +41,10 @@ open class LibrusMessages(open val data: DataLibrus) {
val profile
get() = data.profile
fun messagesGet(tag: String, endpoint: String, method: Int = POST,
fun messagesGet(tag: String, module: String, method: Int = POST,
parameters: Map<String, Any>? = null, onSuccess: (doc: Document) -> Unit) {
d(tag, "Request: Librus/Messages - $LIBRUS_MESSAGES_URL/$endpoint")
d(tag, "Request: Librus/Messages - $LIBRUS_MESSAGES_URL/$module")
val callback = object : TextCallbackHandler() {
override fun onSuccess(text: String?, response: Response?) {
@ -107,22 +110,92 @@ open class LibrusMessages(open val data: DataLibrus) {
transformer.transform(DOMSource(doc), StreamResult(stringWriter))
val requestXml = stringWriter.toString()
/*val requestXml = xml("service") {
"header" { }
"data" {
for ((key, value) in parameters.orEmpty()) {
key {
-value.toString()
Request.builder()
.url("$LIBRUS_MESSAGES_URL/$module")
.userAgent(SYNERGIA_USER_AGENT)
.setTextBody(requestXml, MediaTypeUtils.APPLICATION_XML)
.apply {
when (method) {
GET -> get()
POST -> post()
}
}
.callback(callback)
.build()
.enqueue()
}
fun messagesGetJson(tag: String, module: String, method: Int = POST,
parameters: Map<String, Any>? = null, onSuccess: (json: JsonObject?) -> Unit) {
d(tag, "Request: Librus/Messages - $LIBRUS_MESSAGES_URL/$module")
val callback = object : TextCallbackHandler() {
override fun onSuccess(text: String?, response: Response?) {
if (text.isNullOrEmpty()) {
data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY)
.withResponse(response))
return
}
when {
text.contains("<message>Niepoprawny login i/lub hasło.</message>") -> data.error(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN, response, text)
text.contains("stop.png") -> data.error(TAG, ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED, response, text)
text.contains("eAccessDeny") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text)
text.contains("OffLine") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_MAINTENANCE, response, text)
text.contains("<status>error</status>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ERROR, response, text)
text.contains("<type>eVarWhitThisNameNotExists</type>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text)
text.contains("<error>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_OTHER, response, text)
}
try {
val json: JSONObject? = XML.toJSONObject(text)
onSuccess(JsonParser().parse(json?.toString() ?: "{}")?.asJsonObject)
} catch (e: Exception) {
data.error(ApiError(tag, EXCEPTION_LIBRUS_MESSAGES_REQUEST)
.withResponse(response)
.withThrowable(e)
.withApiResponse(text))
}
}
}.toString(PrintOptions(
singleLineTextElements = true,
useSelfClosingTags = true
))*/
override fun onFailure(response: Response?, throwable: Throwable?) {
data.error(ApiError(tag, ERROR_REQUEST_FAILURE)
.withResponse(response)
.withThrowable(throwable))
}
}
data.app.cookieJar.saveFromResponse(null, listOf(
Cookie.Builder()
.name("DZIENNIKSID")
.value(data.messagesSessionId!!)
.domain("wiadomosci.librus.pl")
.secure().httpOnly().build()
))
val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
val doc = docBuilder.newDocument()
val serviceElement = doc.createElement("service")
val headerElement = doc.createElement("header")
val dataElement = doc.createElement("data")
for ((key, value) in parameters.orEmpty()) {
val element = doc.createElement(key)
element.appendChild(doc.createTextNode(value.toString()))
dataElement.appendChild(element)
}
serviceElement.appendChild(headerElement)
serviceElement.appendChild(dataElement)
doc.appendChild(serviceElement)
val transformer = TransformerFactory.newInstance().newTransformer()
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes")
val stringWriter = StringWriter()
transformer.transform(DOMSource(doc), StreamResult(stringWriter))
val requestXml = stringWriter.toString()
Request.builder()
.url("$LIBRUS_MESSAGES_URL/$endpoint")
.url("$LIBRUS_MESSAGES_URL/$module")
.userAgent(SYNERGIA_USER_AGENT)
.setTextBody(requestXml, MediaTypeUtils.APPLICATION_XML)
.apply {

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-27
*/
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.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.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
class LibrusApiAnnouncementMarkAsRead(
override val data: DataLibrus,
private val announcement: AnnouncementFull,
val onSuccess: () -> Unit
) : LibrusApi(data) {
companion object {
const val TAG = "LibrusApiAnnouncementMarkAsRead"
}
init {
apiGet(TAG, "SchoolNotices/MarkAsRead/${announcement.idString}", method = POST,
ignoreErrors = listOf(ERROR_LIBRUS_API_INVALID_REQUEST_PARAMS)) {
announcement.seen = true
EventBus.getDefault().postSticky(AnnouncementGetEvent(announcement))
data.setSeenMetadataList.add(Metadata(
profileId,
Metadata.TYPE_ANNOUNCEMENT,
announcement.id,
announcement.seen,
announcement.notified,
announcement.addedDate
))
onSuccess()
}
}
}

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,10 +21,11 @@ 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 id = announcement.getString("Id")?.crc32() ?: return@forEach
val longId = announcement.getString("Id") ?: return@forEach
val id = longId.crc32()
val subject = announcement.getString("Subject") ?: ""
val text = announcement.getString("Content") ?: ""
val startDate = Date.fromY_m_d(announcement.getString("StartDate"))
@ -41,11 +42,12 @@ class LibrusApiAnnouncements(override val data: DataLibrus,
text,
startDate,
endDate,
teacherId
teacherId,
longId
)
data.announcementList.add(announcementObject)
data.metadataList.add(Metadata(
data.setSeenMetadataList.add(Metadata(
profileId,
Metadata.TYPE_ANNOUNCEMENT,
id,

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,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_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.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Attendance
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.models.Date
@ -25,30 +25,35 @@ 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)
.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 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,
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
@ -40,7 +40,7 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
1,
-1,
1
).apply { type = Grade.TYPE_BEHAVIOUR }
).apply { type = Grade.TYPE_POINT_SUM }
data.gradeList.add(semester1StartGradeObject)
data.metadataList.add(Metadata(
@ -66,7 +66,7 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
2,
-1,
1
).apply { type = Grade.TYPE_BEHAVIOUR }
).apply { type = Grade.TYPE_POINT_SUM }
data.gradeList.add(semester2StartGradeObject)
data.metadataList.add(Metadata(
@ -133,7 +133,7 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
teacherId,
1
).apply {
type = Grade.TYPE_BEHAVIOUR
type = Grade.TYPE_POINT_SUM
valueMax = valueTo
}
@ -148,7 +148,7 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
))
}
data.toRemove.add(DataRemoveModel.Grades.semesterWithType(profile.currentSemester, Grade.TYPE_BEHAVIOUR))
data.toRemove.add(DataRemoveModel.Grades.semesterWithType(profile.currentSemester, Grade.TYPE_POINT_SUM))
data.setSyncNext(ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES, SYNC_ALWAYS)
onSuccess()
}

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

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-29
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import android.graphics.Color
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.entity.GradeCategory
class LibrusApiDescriptiveGradeCategories(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) {
companion object {
const val TAG = "LibrusApiDescriptiveGradeCategories"
}
init {
apiGet(TAG, "DescriptiveTextGrades/Skills") { json ->
json.getJsonArray("Skills")?.asJsonObjectList()?.forEach { category ->
val id = category.getLong("Id") ?: return@forEach
val name = category.getString("Name") ?: ""
val color = category.getJsonObject("Color")?.getInt("Id")
?.let { data.getColor(it) } ?: Color.BLUE
val gradeCategoryObject = GradeCategory(
profileId,
id,
-1f,
color,
name
).apply {
type = GradeCategory.TYPE_DESCRIPTIVE
}
data.gradeCategories.put(id, gradeCategoryObject)
}
data.setSyncNext(ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADE_CATEGORIES, 1 * DAY)
onSuccess()
}
}
}

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-29
*/
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_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.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,
val onSuccess: () -> Unit) : LibrusApi(data) {
companion object {
const val TAG = "LibrusApiDescriptiveGrades"
}
init { data.profile?.also { profile ->
apiGet(TAG, "BaseTextGrades") { json ->
json.getJsonArray("Grades")?.asJsonObjectList()?.forEach { grade ->
val id = grade.getLong("Id") ?: return@forEach
val teacherId = grade.getJsonObject("AddedBy")?.getLong("Id") ?: return@forEach
val semester = grade.getInt("Semester") ?: return@forEach
val subjectId = grade.getJsonObject("Subject")?.getLong("Id") ?: return@forEach
val description = grade.getString("Grade")
val categoryId = grade.getJsonObject("Skill")?.getLong("Id")
?: grade.getJsonObject("Category")?.getLong("Id")
?: return@forEach
val type = when (grade.getJsonObject("Category")) {
null -> TYPE_DESCRIPTIVE_TEXT
else -> TYPE_TEXT
}
val category = data.gradeCategories.singleOrNull {
it.categoryId == categoryId && it.type == when (type) {
TYPE_DESCRIPTIVE_TEXT -> GradeCategory.TYPE_DESCRIPTIVE
else -> GradeCategory.TYPE_NORMAL
}
}
val addedDate = Date.fromIso(grade.getString("AddDate") ?: return@forEach)
val gradeObject = Grade(
profileId,
id,
category?.text ?: "",
category?.color ?: -1,
description,
" ",
0f,
0f,
semester,
teacherId,
subjectId
).apply {
this.type = type
}
data.gradeList.add(gradeObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_GRADE,
id,
profile.empty,
profile.empty,
addedDate
))
}
data.toRemove.addAll(listOf(
TYPE_DESCRIPTIVE_TEXT,
TYPE_TEXT
).map {
DataRemoveModel.Grades.semesterWithType(profile.currentSemester, it)
})
data.setSyncNext(ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES, SYNC_ALWAYS)
onSuccess()
}
} ?: onSuccess() }
}

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.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
import pl.szczodrzynski.edziennik.utils.models.Time
@ -28,7 +28,7 @@ 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
@ -70,7 +70,10 @@ class LibrusApiEvents(override val data: DataLibrus,
))
}
data.toRemove.add(DataRemoveModel.Events.futureExceptType(Event.TYPE_HOMEWORK))
data.toRemove.add(DataRemoveModel.Events.futureExceptTypes(listOf(
Event.TYPE_HOMEWORK,
Event.TYPE_PT_MEETING
)))
data.setSyncNext(ENDPOINT_LIBRUS_API_EVENTS, SYNC_ALWAYS)
onSuccess()

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

@ -0,0 +1,50 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-29
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import android.graphics.Color
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.entity.GradeCategory
class LibrusApiPointGradeCategories(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) {
companion object {
const val TAG = "LibrusApiPointGradeCategories"
}
init {
apiGet(TAG, "PointGrades/Categories") { json ->
json.getJsonArray("Categories")?.asJsonObjectList()?.forEach { category ->
val id = category.getLong("Id") ?: return@forEach
val name = category.getString("Name") ?: ""
val color = category.getJsonObject("Color")?.getInt("Id")
?.let { data.getColor(it) } ?: Color.BLUE
val countToAverage = category.getBoolean("CountToTheAverage") ?: true
val weight = if (countToAverage) category.getFloat("Weight") ?: 0f else 0f
val valueFrom = category.getFloat("ValueFrom") ?: 0f
val valueTo = category.getFloat("ValueTo") ?: 0f
val gradeCategoryObject = GradeCategory(
profileId,
id,
weight,
color,
name
).apply {
type = GradeCategory.TYPE_POINT
setValueRange(valueFrom, valueTo)
}
data.gradeCategories.put(id, gradeCategoryObject)
}
data.setSyncNext(ENDPOINT_LIBRUS_API_POINT_GRADE_CATEGORIES, 1 * DAY)
onSuccess()
}
}
}

View File

@ -0,0 +1,78 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-29
*/
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_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.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,
val onSuccess: () -> Unit) : LibrusApi(data) {
companion object {
const val TAG = "LibrusApiPointGrades"
}
init { data.profile?.also { profile ->
apiGet(TAG, "PointGrades") { json ->
json.getJsonArray("Grades")?.asJsonObjectList()?.forEach { grade ->
val id = grade.getLong("Id") ?: return@forEach
val teacherId = grade.getJsonObject("AddedBy")?.getLong("Id") ?: return@forEach
val semester = grade.getInt("Semester") ?: return@forEach
val subjectId = grade.getJsonObject("Subject")?.getLong("Id") ?: return@forEach
val name = grade.getString("Grade") ?: return@forEach
val value = grade.getFloat("GradeValue") ?: 0f
val categoryId = grade.getJsonObject("Category")?.getLong("Id") ?: return@forEach
val category = data.gradeCategories.singleOrNull {
it.categoryId == categoryId && it.type == GradeCategory.TYPE_POINT
}
val addedDate = Date.fromIso(grade.getString("AddDate") ?: return@forEach)
val gradeObject = Grade(
profileId,
id,
category?.text ?: "",
category?.color ?: -1,
"",
name,
value,
category?.weight ?: 0f,
semester,
teacherId,
subjectId
).apply {
type = TYPE_POINT_AVG
valueMax = category?.valueTo ?: 0f
}
data.gradeList.add(gradeObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_GRADE,
id,
profile.empty,
profile.empty,
addedDate
))
}
data.toRemove.add(DataRemoveModel.Grades.semesterWithType(profile.currentSemester, TYPE_POINT_AVG))
data.setSyncNext(ENDPOINT_LIBRUS_API_POINT_GRADES, SYNC_ALWAYS)
onSuccess()
}
} ?: onSuccess() }
}

View File

@ -8,8 +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_PT_MEETINGS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
@ -21,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
@ -61,6 +62,8 @@ class LibrusApiPtMeetings(override val data: DataLibrus,
))
}
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_PT_MEETING))
data.setSyncNext(ENDPOINT_LIBRUS_API_PT_MEETINGS, 12*HOUR)
onSuccess()
}

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