Compare commits

...

115 Commits

Author SHA1 Message Date
d70b0c0c3f [UI] Implement new Event Adapter and Day Dialog (partially). 2019-12-16 22:26:00 +01:00
41cebc554f [UI] 'Cause it's winter. 2019-12-15 21:27:50 +01:00
92e0fc2847 [Sync] Fix showing notifications. Implement notifications web push. 2019-12-15 16:43:24 +01:00
9978a11c52 [Proguard] Update Proguard rules for Retrofit and JNI code. 2019-12-15 15:59:46 +01:00
cf77623e9c [UI/Agenda] Add FAB to add events and make the dialog open with the selected date. 2019-12-15 00:42:28 +01:00
7ce4acc687 [Structure] Move API to data package. 2019-12-15 00:28:31 +01:00
ffbac126bd [3.9.15-dev] Implement C++ Native password signer library. 2019-12-14 22:56:56 +01:00
0f11b02047 [3.9.14-dev] 2019-12-14 17:48:16 +01:00
b8cf731dd0 [Dialog/EventManual] Unshare using eventObject instead of editingEvent. 2019-12-14 17:31:34 +01:00
ad5afac174 [Login/Librus] Implement Librus JST login form. 2019-12-14 17:21:29 +01:00
13279a915d [API/Mobidziennik] Add API key and fix tasks not finishing 2019-12-14 14:06:43 +01:00
3defe2d343 [API] Remove deprecated server sync. 2019-12-14 01:03:12 +01:00
2c86414e74 [API/Szkolny] Change api structure, make SzkolnyTask and add sharing events. 2019-12-14 01:02:28 +01:00
9e4f816009 [Event] Update sharing dialog 2019-12-13 22:07:42 +01:00
b48afde7f1 [Dialog/EventManual] Add editing and removing events and fix a few bugs. 2019-12-10 23:23:26 +01:00
13cdaadcf7 [Dialog/EventManual] Add default type support and remove old dialog from the agenda fragment. 2019-12-10 22:31:33 +01:00
44fc1c4532 [Dialogs] Remove deprecated dialogs. 2019-12-09 17:58:56 +01:00
ddb1ecaa99 [UI/Settings] Add setting for showing drawer on back pressed. 2019-12-09 17:24:41 +01:00
50ada5f95b [APIv2/Szkolny] Move profiles parameter to getEvents function 2019-12-09 16:37:21 +01:00
40ba9e8434 [APIv2/Szkolny] Add Szkolny API and add getting shared events. 2019-12-09 16:35:37 +01:00
d6f9b81de6 [API] Add Server Sync Task 2019-12-08 19:12:38 +01:00
b085d94fea [APIv2/Librus] Fix announcements duplicate ids (migrate from crc16 to crc32) 2019-12-08 15:00:38 +01:00
90343e1e39 [UI/Messages] Fix message body text color in messages fragment 2019-12-08 00:51:42 +01:00
883d8f31c4 [Database/Announcements] Make announcements sort by start date. 2019-12-08 00:28:46 +01:00
2e18c5a668 [APIv2/Librus] Add behaviour grade comments, change features names and fix semester start points 2019-12-07 23:57:49 +01:00
c1ca104021 [UI/Announcements] Make better announcements look. 2019-12-07 20:46:00 +01:00
e7db4e9326 [Gradle] Update gradle wrapper. 2019-12-06 20:31:45 +01:00
203a42eb1b [UI/Messages] Fix colors in messages body. 2019-12-04 23:59:27 +01:00
c83f20983b [APIv2/Librus] Fix replacing weird XML tags in messages. 2019-12-04 23:59:09 +01:00
25f504cadf [APIv2/Librus] Add getting behaviour grades and its categories. 2019-12-03 23:44:55 +01:00
07ce718e3c [Widget] Fix showing all-day event on every lesson. 2019-12-03 19:42:25 +01:00
83264b5973 [Config] Fix reading backgrounds value. 2019-12-03 19:39:48 +01:00
1acf1547d5 [3.9.13-dev] 2019-12-02 22:24:55 +01:00
5d3de35c10 [UI/Timetable] Fix non-null cast exception. 2019-12-02 22:20:53 +01:00
8f8d613f6e [UI] Update libraries & NavLib to fix missing badges on most profiles. 2019-12-02 22:10:36 +01:00
6a161b3c97 [UI/Timetable] Add showing event indicators on lessons. 2019-12-02 21:25:18 +01:00
3e97572100 [Dialog/Events] Use new event dialog in homework fragment and event list dialog. 2019-12-02 19:36:19 +01:00
fc3b6fd1e0 [UI/Event] Fix stuff because i'm dumb 2019-12-02 19:07:30 +01:00
9bc7f9ac11 [UI/Event] Make use of default values in event manual dialog. 2019-12-02 19:04:30 +01:00
0a2f252405 [UI/Home] Implement home card swapping and saving. 2019-12-02 18:12:52 +01:00
09bc658f97 [UI] Fix blank screen when loading activity. 2019-12-02 18:11:12 +01:00
7b04202a00 [Config] Add the rest of config. Migrate from AppConfig and remove most values from AppConfig. 2019-12-01 22:35:42 +01:00
acf364166b [Notifications/LessonChange] Fix lesson change notifications text. 2019-12-01 21:54:59 +01:00
4e88efae94 [Home/Grades] Fix padding. 2019-12-01 20:25:50 +01:00
8df24dc1c4 [Dialog/Events] Create adapter outside of the observer. 2019-12-01 20:25:28 +01:00
8482c27689 [Timetable] Add marking lessons as seen in timetable. 2019-12-01 20:23:48 +01:00
d1265dc1f2 [Dialog/Events] Make new event list dialog. 2019-11-30 23:33:20 +01:00
47d395de71 [Home] Add home grades card. 2019-11-29 23:16:50 +01:00
5b443e02a3 [Mobidziennik] Fix getting grades with no category. Add support for comments. 2019-11-29 18:59:47 +01:00
f8a7d52b1d [Mobidziennik] Fix extracting when attachment has no size. 2019-11-29 17:51:16 +01:00
a133a96819 [UI/Messages] Make message list scroll to last opened message. 2019-11-29 17:29:23 +01:00
c71b8f994c [Messages] Implement Mobidziennik attachments. Fix multiplying attachments in UI. 2019-11-28 23:32:10 +01:00
9b02c97926 [UI/Messages] Convert adapter to Kotlin. 2019-11-28 23:12:21 +01:00
ab06efc934 [Librus/Attachment] A huge structure reformat. 2019-11-28 23:00:25 +01:00
928b73f139 [Config] Implement per-profile config. Update timetable card. 2019-11-28 21:45:27 +01:00
a36fb09bc3 [Dialog/Event] Add event adding in the new event manual dialog. 2019-11-28 15:11:23 +01:00
eaed4b76aa [Timetable] Fix showing question marks in lesson changes. 2019-11-26 23:11:14 +01:00
6d8960f089 [Home/Timetable] Add click listener to timetable card. 2019-11-26 22:56:43 +01:00
ca3b6d0705 [APIv2/Idziennik] Fix getting subject and rewrite getting proposed grades. 2019-11-26 22:28:52 +01:00
c2e7931ea6 [Config] Implement (basic) new app config storage. 2019-11-26 21:55:04 +01:00
d1a5d8cba9 [APIv2/Vulcan] Fix start time in events. 2019-11-26 21:44:35 +01:00
c2f91e6867 [APIv2] Move AppError to API directory. 2019-11-26 21:10:31 +01:00
55e32b8d88 [APIv2/Librus] Temporarily fix subject in attendance. 2019-11-26 21:02:33 +01:00
462b1df767 [3.9.12-dev] 2019-11-25 22:55:09 +01:00
d17d2c8417 [APIv2/Librus] Fix looking for the lesson in getting homework. 2019-11-25 22:53:55 +01:00
6892832fff [APIv2/Idziennik] Add getting homework and rewrite getting exams. 2019-11-25 22:53:55 +01:00
66d54c7c45 [APIv2/Librus] Fix messages login. 2019-11-25 22:40:14 +01:00
d432685aa8 [Update] Fix update downloading from notification. 2019-11-25 22:23:55 +01:00
37f3d76fb8 [UI] Implement home timetable card. 2019-11-25 22:17:08 +01:00
7961a74995 [APIv2/Events] Fix fetching events and homework. Add DataRemoveModel for events. 2019-11-25 21:13:55 +01:00
9d590508ad [APIv2/Librus] Fix messages session ID extraction. 2019-11-25 15:00:51 +01:00
f79b7eaf83 [3.9.11-dev] 2019-11-24 21:47:05 +01:00
ae13bf946f [Home] Remove useless dummy cards. 2019-11-24 21:21:37 +01:00
f116c4f1f4 [Home] Implement basic timetable card. 2019-11-24 21:15:01 +01:00
867c8920a8 [APIv2/Messages] Add downloading attachments. 2019-11-24 20:55:04 +01:00
6e6dd34872 [UI] Add new Home fragment. Add Lucky number card and number selection dialog. 2019-11-24 19:41:17 +01:00
0759468fa7 [Sync] Add syncing all to manual sync dialog. 2019-11-24 16:47:10 +01:00
1b1fb09211 [APIv2/Vulcan] Fix problems with week start in timetable. 2019-11-24 16:31:51 +01:00
de414c912c [Sync] Fix error when user selects no features. 2019-11-24 13:20:42 +01:00
d274a2fed1 [Timetable] Change date receiver argument to timetableDate. 2019-11-24 13:20:22 +01:00
285b7e9b9e [Timetable] Make going to the specified date on the notification click. 2019-11-24 12:57:00 +01:00
875efcff7e [APIv2/Timetable] Fix ID in lessons. 2019-11-24 12:11:39 +01:00
07ae37167d [Notifications/Timetable] Make notifications for timetable changes 2019-11-24 11:09:45 +01:00
f689f4d427 [APIv2/Timetable] Fix lesson changes metadata. 2019-11-24 10:36:20 +01:00
19bc2b8b37 [3.9.10-dev] New UI + stability fixes 2019-11-23 23:26:19 +01:00
673116e27e [Settings/About] Add a new developer to about! 2019-11-23 23:09:03 +01:00
59fcb0a050 [APIv2/Timetable] Add lesson change metadata only when the lesson is today or in the future 2019-11-23 22:40:20 +01:00
cd76f99bbf [APIv2/Timetable] Add showing unread lesson changes 2019-11-23 22:26:21 +01:00
6a4994b9c2 [APIv2/Timetable] Make swipe refresh download timetable for the selected week 2019-11-23 21:57:30 +01:00
63960c5e05 [APIv2/Timetable] Add selecting date, marking as read and fix stepForward in Date 2019-11-23 21:27:52 +01:00
540afb6a28 [Home] Start making new home timetable card in Kotlin 2019-11-23 19:41:55 +01:00
ae10b8abbd [APIv2/Idziennik] Add new timetable getting and fix week start 2019-11-23 19:40:32 +01:00
db2ebab879 [APIv2/Vulcan] Add missing Lublin endpoints 2019-11-23 19:39:13 +01:00
6ec3d062df [UI] Update header image. Fix fragment bundle passing. 2019-11-23 19:37:00 +01:00
86b6060a09 [UI] Migrate to outlined icons. 2019-11-23 18:32:18 +01:00
83d123e341 [UI] New notifications view. 2019-11-22 22:41:40 +01:00
34061695f9 [UI] Update colored placeholder icons. 2019-11-22 19:23:49 +01:00
de68476442 [UI/Event] Add Sync text to manual event dialog. 2019-11-22 18:42:45 +01:00
678a81a44b [APIv2/Vulcan] Improve Vulcan login when migrating from APIv1. 2019-11-22 18:42:11 +01:00
cfb3096d53 [Sync] Add better task cancelling and better frozen task detection. 2019-11-22 18:41:15 +01:00
9b7aca745a [3.9.9-dev] 2019-11-21 19:18:04 +01:00
82852389fa [UI] Update indentation, again. Fix manual event color selecting. 2019-11-21 18:43:01 +01:00
ce06084e6f [Timetable] Fix lessons removing, again. 2019-11-21 18:24:02 +01:00
3ca051983f [APIv2/Timetable] Add searching for the next lesson by team id 2019-11-20 22:43:48 +01:00
cd379e4175 [APIv2/Librus] Add getting grade comments 2019-11-20 21:16:18 +01:00
62fdfa2b6f [UI] Update manual event dialog. Fix timetable errors. 2019-11-20 21:13:43 +01:00
9866017f7e [APIv2/Vulcan] Fix timetable teams issue. Fix missing login data error. 2019-11-20 19:51:50 +01:00
67f98b08c6 [Update] Update update code to allow update from direct download. 2019-11-20 17:11:08 +01:00
fdb5f7ec02 [APIv2/Mobidziennik] Implement getting message details. 2019-11-18 22:57:23 +01:00
04c3c7ca6e [APIv2] Handle Librus Portal maintenance. 2019-11-18 18:55:52 +01:00
f424315d97 [3.9.8-dev] 2019-11-17 23:17:37 +01:00
c907a8df37 [APIv2] Librus: better error handling. Timetable: fix widget crashing with NPE. 2019-11-17 23:16:13 +01:00
37ea65e3fc [Timetable] Add SwipeToRefresh. Select start&end hours based on lesson ranges. 2019-11-16 21:16:18 +01:00
a3e5f824c8 [UI] Fix messages & homework refresh layout sensitivity. 2019-11-16 18:46:01 +01:00
e0c850a455 [Gradle] Update Tachyon to support creating DayView programmatically. 2019-11-16 17:07:07 +01:00
375 changed files with 11713 additions and 5178 deletions

6
.gitignore vendored
View File

@ -81,3 +81,9 @@ lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/
app/schemas/
signatures/
app/.cxx

2
.idea/misc.xml generated
View File

@ -50,7 +50,7 @@
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="JDK" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@ -15,6 +15,12 @@ android {
versionCode release.versionCode
versionName release.versionName
multiDexEnabled true
externalNativeBuild {
cmake {
cppFlags "-std=c++11"
}
}
}
buildTypes {
applicationVariants.all { variant ->
@ -62,6 +68,12 @@ android {
packagingOptions {
exclude 'META-INF/library-core_release.kotlin_module'
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.10.2"
}
}
}
/*task finalizeBundleDebug(type: Copy) {
@ -169,7 +181,12 @@ dependencies {
implementation 'com.github.kuba2k2:RecyclerTabLayout:700f980584'
implementation 'com.linkedin.android.tachyon:tachyon:1.0.2'
implementation 'com.github.kuba2k2:Tachyon:551943a6b5'
implementation "com.squareup.retrofit2:retrofit:${versions.retrofit}"
implementation "com.squareup.retrofit2:converter-gson:${versions.retrofit}"
implementation 'com.github.jetradarmobile:android-snowfall:1.2.0'
}
repositories {
mavenCentral()

View File

@ -24,6 +24,7 @@
-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.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
@ -48,4 +49,13 @@
# Most of volatile fields are updated with AFU and should not be mangled
-keepclassmembernames class kotlinx.** {
volatile <fields>;
}
}
-keepclasseswithmembernames class * {
native <methods>;
}
-keep class pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing { public final byte[] pleaseStopRightNow(java.lang.String, long); }
-keepclassmembernames class pl.szczodrzynski.edziennik.data.api.szkolny.request.** { *; }
-keepclassmembernames class pl.szczodrzynski.edziennik.data.api.szkolny.response.** { *; }

View File

@ -0,0 +1,13 @@
<!--
~ Copyright (c) Kuba Szczodrzyński 2019-11-25.
-->
<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="M12,8A4,4 0,0 1,16 12A4,4 0,0 1,12 16A4,4 0,0 1,8 12A4,4 0,0 1,12 8M12,10A2,2 0,0 0,10 12A2,2 0,0 0,12 14A2,2 0,0 0,14 12A2,2 0,0 0,12 10M10,22C9.75,22 9.54,21.82 9.5,21.58L9.13,18.93C8.5,18.68 7.96,18.34 7.44,17.94L4.95,18.95C4.73,19.03 4.46,18.95 4.34,18.73L2.34,15.27C2.21,15.05 2.27,14.78 2.46,14.63L4.57,12.97L4.5,12L4.57,11L2.46,9.37C2.27,9.22 2.21,8.95 2.34,8.73L4.34,5.27C4.46,5.05 4.73,4.96 4.95,5.05L7.44,6.05C7.96,5.66 8.5,5.32 9.13,5.07L9.5,2.42C9.54,2.18 9.75,2 10,2H14C14.25,2 14.46,2.18 14.5,2.42L14.87,5.07C15.5,5.32 16.04,5.66 16.56,6.05L19.05,5.05C19.27,4.96 19.54,5.05 19.66,5.27L21.66,8.73C21.79,8.95 21.73,9.22 21.54,9.37L19.43,11L19.5,12L19.43,13L21.54,14.63C21.73,14.78 21.79,15.05 21.66,15.27L19.66,18.73C19.54,18.95 19.27,19.04 19.05,18.95L16.56,17.95C16.04,18.34 15.5,18.68 14.87,18.93L14.5,21.58C14.46,21.82 14.25,22 14,22H10M11.25,4L10.88,6.61C9.68,6.86 8.62,7.5 7.85,8.39L5.44,7.35L4.69,8.65L6.8,10.2C6.4,11.37 6.4,12.64 6.8,13.8L4.68,15.36L5.43,16.66L7.86,15.62C8.63,16.5 9.68,17.14 10.87,17.38L11.24,20H12.76L13.13,17.39C14.32,17.14 15.37,16.5 16.14,15.62L18.57,16.66L19.32,15.36L17.2,13.81C17.6,12.64 17.6,11.37 17.2,10.2L19.31,8.65L18.56,7.35L16.15,8.39C15.38,7.5 14.32,6.86 13.12,6.62L12.75,4H11.25Z"/>
</vector>

View File

@ -235,7 +235,7 @@
<service android:name=".Notifier$GetDataRetryService" />
<service android:name=".api.v2.ApiService" />
<service android:name="pl.szczodrzynski.edziennik.data.api.ApiService" />
</application>
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@ -249,4 +249,4 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
</manifest>
</manifest>

View File

@ -0,0 +1,44 @@
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
szkolny-signing
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
szkolny-signing.cpp)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
#[[find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )]]
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
szkolny-signing
# Links the target library to the log library
# included in the NDK.
${log-lib} )

520
app/src/main/cpp/aes.c Normal file
View File

@ -0,0 +1,520 @@
#include <stdlib.h>
#include <memory.h>
#include "aes.h"
#include <stdio.h>
#define airport(x) (((x) << 8) | ((x) >> 24))
#define TRUE 1
#define FALSE 0
static const toys wtf[16][16] = {
{0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76},
{0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0},
{0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15},
{0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75},
{0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84},
{0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF},
{0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8},
{0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2},
{0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73},
{0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB},
{0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79},
{0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08},
{0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A},
{0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E},
{0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF},
{0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16}
};
static const toys help_me[256][6] = {
{0x00,0x00,0x00,0x00,0x00,0x00},{0x02,0x03,0x09,0x0b,0x0d,0x0e},
{0x04,0x06,0x12,0x16,0x1a,0x1c},{0x06,0x05,0x1b,0x1d,0x17,0x12},
{0x08,0x0c,0x24,0x2c,0x34,0x38},{0x0a,0x0f,0x2d,0x27,0x39,0x36},
{0x0c,0x0a,0x36,0x3a,0x2e,0x24},{0x0e,0x09,0x3f,0x31,0x23,0x2a},
{0x10,0x18,0x48,0x58,0x68,0x70},{0x12,0x1b,0x41,0x53,0x65,0x7e},
{0x14,0x1e,0x5a,0x4e,0x72,0x6c},{0x16,0x1d,0x53,0x45,0x7f,0x62},
{0x18,0x14,0x6c,0x74,0x5c,0x48},{0x1a,0x17,0x65,0x7f,0x51,0x46},
{0x1c,0x12,0x7e,0x62,0x46,0x54},{0x1e,0x11,0x77,0x69,0x4b,0x5a},
{0x20,0x30,0x90,0xb0,0xd0,0xe0},{0x22,0x33,0x99,0xbb,0xdd,0xee},
{0x24,0x36,0x82,0xa6,0xca,0xfc},{0x26,0x35,0x8b,0xad,0xc7,0xf2},
{0x28,0x3c,0xb4,0x9c,0xe4,0xd8},{0x2a,0x3f,0xbd,0x97,0xe9,0xd6},
{0x2c,0x3a,0xa6,0x8a,0xfe,0xc4},{0x2e,0x39,0xaf,0x81,0xf3,0xca},
{0x30,0x28,0xd8,0xe8,0xb8,0x90},{0x32,0x2b,0xd1,0xe3,0xb5,0x9e},
{0x34,0x2e,0xca,0xfe,0xa2,0x8c},{0x36,0x2d,0xc3,0xf5,0xaf,0x82},
{0x38,0x24,0xfc,0xc4,0x8c,0xa8},{0x3a,0x27,0xf5,0xcf,0x81,0xa6},
{0x3c,0x22,0xee,0xd2,0x96,0xb4},{0x3e,0x21,0xe7,0xd9,0x9b,0xba},
{0x40,0x60,0x3b,0x7b,0xbb,0xdb},{0x42,0x63,0x32,0x70,0xb6,0xd5},
{0x44,0x66,0x29,0x6d,0xa1,0xc7},{0x46,0x65,0x20,0x66,0xac,0xc9},
{0x48,0x6c,0x1f,0x57,0x8f,0xe3},{0x4a,0x6f,0x16,0x5c,0x82,0xed},
{0x4c,0x6a,0x0d,0x41,0x95,0xff},{0x4e,0x69,0x04,0x4a,0x98,0xf1},
{0x50,0x78,0x73,0x23,0xd3,0xab},{0x52,0x7b,0x7a,0x28,0xde,0xa5},
{0x54,0x7e,0x61,0x35,0xc9,0xb7},{0x56,0x7d,0x68,0x3e,0xc4,0xb9},
{0x58,0x74,0x57,0x0f,0xe7,0x93},{0x5a,0x77,0x5e,0x04,0xea,0x9d},
{0x5c,0x72,0x45,0x19,0xfd,0x8f},{0x5e,0x71,0x4c,0x12,0xf0,0x81},
{0x60,0x50,0xab,0xcb,0x6b,0x3b},{0x62,0x53,0xa2,0xc0,0x66,0x35},
{0x64,0x56,0xb9,0xdd,0x71,0x27},{0x66,0x55,0xb0,0xd6,0x7c,0x29},
{0x68,0x5c,0x8f,0xe7,0x5f,0x03},{0x6a,0x5f,0x86,0xec,0x52,0x0d},
{0x6c,0x5a,0x9d,0xf1,0x45,0x1f},{0x6e,0x59,0x94,0xfa,0x48,0x11},
{0x70,0x48,0xe3,0x93,0x03,0x4b},{0x72,0x4b,0xea,0x98,0x0e,0x45},
{0x74,0x4e,0xf1,0x85,0x19,0x57},{0x76,0x4d,0xf8,0x8e,0x14,0x59},
{0x78,0x44,0xc7,0xbf,0x37,0x73},{0x7a,0x47,0xce,0xb4,0x3a,0x7d},
{0x7c,0x42,0xd5,0xa9,0x2d,0x6f},{0x7e,0x41,0xdc,0xa2,0x20,0x61},
{0x80,0xc0,0x76,0xf6,0x6d,0xad},{0x82,0xc3,0x7f,0xfd,0x60,0xa3},
{0x84,0xc6,0x64,0xe0,0x77,0xb1},{0x86,0xc5,0x6d,0xeb,0x7a,0xbf},
{0x88,0xcc,0x52,0xda,0x59,0x95},{0x8a,0xcf,0x5b,0xd1,0x54,0x9b},
{0x8c,0xca,0x40,0xcc,0x43,0x89},{0x8e,0xc9,0x49,0xc7,0x4e,0x87},
{0x90,0xd8,0x3e,0xae,0x05,0xdd},{0x92,0xdb,0x37,0xa5,0x08,0xd3},
{0x94,0xde,0x2c,0xb8,0x1f,0xc1},{0x96,0xdd,0x25,0xb3,0x12,0xcf},
{0x98,0xd4,0x1a,0x82,0x31,0xe5},{0x9a,0xd7,0x13,0x89,0x3c,0xeb},
{0x9c,0xd2,0x08,0x94,0x2b,0xf9},{0x9e,0xd1,0x01,0x9f,0x26,0xf7},
{0xa0,0xf0,0xe6,0x46,0xbd,0x4d},{0xa2,0xf3,0xef,0x4d,0xb0,0x43},
{0xa4,0xf6,0xf4,0x50,0xa7,0x51},{0xa6,0xf5,0xfd,0x5b,0xaa,0x5f},
{0xa8,0xfc,0xc2,0x6a,0x89,0x75},{0xaa,0xff,0xcb,0x61,0x84,0x7b},
{0xac,0xfa,0xd0,0x7c,0x93,0x69},{0xae,0xf9,0xd9,0x77,0x9e,0x67},
{0xb0,0xe8,0xae,0x1e,0xd5,0x3d},{0xb2,0xeb,0xa7,0x15,0xd8,0x33},
{0xb4,0xee,0xbc,0x08,0xcf,0x21},{0xb6,0xed,0xb5,0x03,0xc2,0x2f},
{0xb8,0xe4,0x8a,0x32,0xe1,0x05},{0xba,0xe7,0x83,0x39,0xec,0x0b},
{0xbc,0xe2,0x98,0x24,0xfb,0x19},{0xbe,0xe1,0x91,0x2f,0xf6,0x17},
{0xc0,0xa0,0x4d,0x8d,0xd6,0x76},{0xc2,0xa3,0x44,0x86,0xdb,0x78},
{0xc4,0xa6,0x5f,0x9b,0xcc,0x6a},{0xc6,0xa5,0x56,0x90,0xc1,0x64},
{0xc8,0xac,0x69,0xa1,0xe2,0x4e},{0xca,0xaf,0x60,0xaa,0xef,0x40},
{0xcc,0xaa,0x7b,0xb7,0xf8,0x52},{0xce,0xa9,0x72,0xbc,0xf5,0x5c},
{0xd0,0xb8,0x05,0xd5,0xbe,0x06},{0xd2,0xbb,0x0c,0xde,0xb3,0x08},
{0xd4,0xbe,0x17,0xc3,0xa4,0x1a},{0xd6,0xbd,0x1e,0xc8,0xa9,0x14},
{0xd8,0xb4,0x21,0xf9,0x8a,0x3e},{0xda,0xb7,0x28,0xf2,0x87,0x30},
{0xdc,0xb2,0x33,0xef,0x90,0x22},{0xde,0xb1,0x3a,0xe4,0x9d,0x2c},
{0xe0,0x90,0xdd,0x3d,0x06,0x96},{0xe2,0x93,0xd4,0x36,0x0b,0x98},
{0xe4,0x96,0xcf,0x2b,0x1c,0x8a},{0xe6,0x95,0xc6,0x20,0x11,0x84},
{0xe8,0x9c,0xf9,0x11,0x32,0xae},{0xea,0x9f,0xf0,0x1a,0x3f,0xa0},
{0xec,0x9a,0xeb,0x07,0x28,0xb2},{0xee,0x99,0xe2,0x0c,0x25,0xbc},
{0xf0,0x88,0x95,0x65,0x6e,0xe6},{0xf2,0x8b,0x9c,0x6e,0x63,0xe8},
{0xf4,0x8e,0x87,0x73,0x74,0xfa},{0xf6,0x8d,0x8e,0x78,0x79,0xf4},
{0xf8,0x84,0xb1,0x49,0x5a,0xde},{0xfa,0x87,0xb8,0x42,0x57,0xd0},
{0xfc,0x82,0xa3,0x5f,0x40,0xc2},{0xfe,0x81,0xaa,0x54,0x4d,0xcc},
{0x1b,0x9b,0xec,0xf7,0xda,0x41},{0x19,0x98,0xe5,0xfc,0xd7,0x4f},
{0x1f,0x9d,0xfe,0xe1,0xc0,0x5d},{0x1d,0x9e,0xf7,0xea,0xcd,0x53},
{0x13,0x97,0xc8,0xdb,0xee,0x79},{0x11,0x94,0xc1,0xd0,0xe3,0x77},
{0x17,0x91,0xda,0xcd,0xf4,0x65},{0x15,0x92,0xd3,0xc6,0xf9,0x6b},
{0x0b,0x83,0xa4,0xaf,0xb2,0x31},{0x09,0x80,0xad,0xa4,0xbf,0x3f},
{0x0f,0x85,0xb6,0xb9,0xa8,0x2d},{0x0d,0x86,0xbf,0xb2,0xa5,0x23},
{0x03,0x8f,0x80,0x83,0x86,0x09},{0x01,0x8c,0x89,0x88,0x8b,0x07},
{0x07,0x89,0x92,0x95,0x9c,0x15},{0x05,0x8a,0x9b,0x9e,0x91,0x1b},
{0x3b,0xab,0x7c,0x47,0x0a,0xa1},{0x39,0xa8,0x75,0x4c,0x07,0xaf},
{0x3f,0xad,0x6e,0x51,0x10,0xbd},{0x3d,0xae,0x67,0x5a,0x1d,0xb3},
{0x33,0xa7,0x58,0x6b,0x3e,0x99},{0x31,0xa4,0x51,0x60,0x33,0x97},
{0x37,0xa1,0x4a,0x7d,0x24,0x85},{0x35,0xa2,0x43,0x76,0x29,0x8b},
{0x2b,0xb3,0x34,0x1f,0x62,0xd1},{0x29,0xb0,0x3d,0x14,0x6f,0xdf},
{0x2f,0xb5,0x26,0x09,0x78,0xcd},{0x2d,0xb6,0x2f,0x02,0x75,0xc3},
{0x23,0xbf,0x10,0x33,0x56,0xe9},{0x21,0xbc,0x19,0x38,0x5b,0xe7},
{0x27,0xb9,0x02,0x25,0x4c,0xf5},{0x25,0xba,0x0b,0x2e,0x41,0xfb},
{0x5b,0xfb,0xd7,0x8c,0x61,0x9a},{0x59,0xf8,0xde,0x87,0x6c,0x94},
{0x5f,0xfd,0xc5,0x9a,0x7b,0x86},{0x5d,0xfe,0xcc,0x91,0x76,0x88},
{0x53,0xf7,0xf3,0xa0,0x55,0xa2},{0x51,0xf4,0xfa,0xab,0x58,0xac},
{0x57,0xf1,0xe1,0xb6,0x4f,0xbe},{0x55,0xf2,0xe8,0xbd,0x42,0xb0},
{0x4b,0xe3,0x9f,0xd4,0x09,0xea},{0x49,0xe0,0x96,0xdf,0x04,0xe4},
{0x4f,0xe5,0x8d,0xc2,0x13,0xf6},{0x4d,0xe6,0x84,0xc9,0x1e,0xf8},
{0x43,0xef,0xbb,0xf8,0x3d,0xd2},{0x41,0xec,0xb2,0xf3,0x30,0xdc},
{0x47,0xe9,0xa9,0xee,0x27,0xce},{0x45,0xea,0xa0,0xe5,0x2a,0xc0},
{0x7b,0xcb,0x47,0x3c,0xb1,0x7a},{0x79,0xc8,0x4e,0x37,0xbc,0x74},
{0x7f,0xcd,0x55,0x2a,0xab,0x66},{0x7d,0xce,0x5c,0x21,0xa6,0x68},
{0x73,0xc7,0x63,0x10,0x85,0x42},{0x71,0xc4,0x6a,0x1b,0x88,0x4c},
{0x77,0xc1,0x71,0x06,0x9f,0x5e},{0x75,0xc2,0x78,0x0d,0x92,0x50},
{0x6b,0xd3,0x0f,0x64,0xd9,0x0a},{0x69,0xd0,0x06,0x6f,0xd4,0x04},
{0x6f,0xd5,0x1d,0x72,0xc3,0x16},{0x6d,0xd6,0x14,0x79,0xce,0x18},
{0x63,0xdf,0x2b,0x48,0xed,0x32},{0x61,0xdc,0x22,0x43,0xe0,0x3c},
{0x67,0xd9,0x39,0x5e,0xf7,0x2e},{0x65,0xda,0x30,0x55,0xfa,0x20},
{0x9b,0x5b,0x9a,0x01,0xb7,0xec},{0x99,0x58,0x93,0x0a,0xba,0xe2},
{0x9f,0x5d,0x88,0x17,0xad,0xf0},{0x9d,0x5e,0x81,0x1c,0xa0,0xfe},
{0x93,0x57,0xbe,0x2d,0x83,0xd4},{0x91,0x54,0xb7,0x26,0x8e,0xda},
{0x97,0x51,0xac,0x3b,0x99,0xc8},{0x95,0x52,0xa5,0x30,0x94,0xc6},
{0x8b,0x43,0xd2,0x59,0xdf,0x9c},{0x89,0x40,0xdb,0x52,0xd2,0x92},
{0x8f,0x45,0xc0,0x4f,0xc5,0x80},{0x8d,0x46,0xc9,0x44,0xc8,0x8e},
{0x83,0x4f,0xf6,0x75,0xeb,0xa4},{0x81,0x4c,0xff,0x7e,0xe6,0xaa},
{0x87,0x49,0xe4,0x63,0xf1,0xb8},{0x85,0x4a,0xed,0x68,0xfc,0xb6},
{0xbb,0x6b,0x0a,0xb1,0x67,0x0c},{0xb9,0x68,0x03,0xba,0x6a,0x02},
{0xbf,0x6d,0x18,0xa7,0x7d,0x10},{0xbd,0x6e,0x11,0xac,0x70,0x1e},
{0xb3,0x67,0x2e,0x9d,0x53,0x34},{0xb1,0x64,0x27,0x96,0x5e,0x3a},
{0xb7,0x61,0x3c,0x8b,0x49,0x28},{0xb5,0x62,0x35,0x80,0x44,0x26},
{0xab,0x73,0x42,0xe9,0x0f,0x7c},{0xa9,0x70,0x4b,0xe2,0x02,0x72},
{0xaf,0x75,0x50,0xff,0x15,0x60},{0xad,0x76,0x59,0xf4,0x18,0x6e},
{0xa3,0x7f,0x66,0xc5,0x3b,0x44},{0xa1,0x7c,0x6f,0xce,0x36,0x4a},
{0xa7,0x79,0x74,0xd3,0x21,0x58},{0xa5,0x7a,0x7d,0xd8,0x2c,0x56},
{0xdb,0x3b,0xa1,0x7a,0x0c,0x37},{0xd9,0x38,0xa8,0x71,0x01,0x39},
{0xdf,0x3d,0xb3,0x6c,0x16,0x2b},{0xdd,0x3e,0xba,0x67,0x1b,0x25},
{0xd3,0x37,0x85,0x56,0x38,0x0f},{0xd1,0x34,0x8c,0x5d,0x35,0x01},
{0xd7,0x31,0x97,0x40,0x22,0x13},{0xd5,0x32,0x9e,0x4b,0x2f,0x1d},
{0xcb,0x23,0xe9,0x22,0x64,0x47},{0xc9,0x20,0xe0,0x29,0x69,0x49},
{0xcf,0x25,0xfb,0x34,0x7e,0x5b},{0xcd,0x26,0xf2,0x3f,0x73,0x55},
{0xc3,0x2f,0xcd,0x0e,0x50,0x7f},{0xc1,0x2c,0xc4,0x05,0x5d,0x71},
{0xc7,0x29,0xdf,0x18,0x4a,0x63},{0xc5,0x2a,0xd6,0x13,0x47,0x6d},
{0xfb,0x0b,0x31,0xca,0xdc,0xd7},{0xf9,0x08,0x38,0xc1,0xd1,0xd9},
{0xff,0x0d,0x23,0xdc,0xc6,0xcb},{0xfd,0x0e,0x2a,0xd7,0xcb,0xc5},
{0xf3,0x07,0x15,0xe6,0xe8,0xef},{0xf1,0x04,0x1c,0xed,0xe5,0xe1},
{0xf7,0x01,0x07,0xf0,0xf2,0xf3},{0xf5,0x02,0x0e,0xfb,0xff,0xfd},
{0xeb,0x13,0x79,0x92,0xb4,0xa7},{0xe9,0x10,0x70,0x99,0xb9,0xa9},
{0xef,0x15,0x6b,0x84,0xae,0xbb},{0xed,0x16,0x62,0x8f,0xa3,0xb5},
{0xe3,0x1f,0x5d,0xbe,0x80,0x9f},{0xe1,0x1c,0x54,0xb5,0x8d,0x91},
{0xe7,0x19,0x4f,0xa8,0x9a,0x83},{0xe5,0x1a,0x46,0xa3,0x97,0x8d}
};
void throat(const toys *decide, toys *selection, size_t plane)
{
size_t idx;
for (idx = 0; idx < plane; idx++)
selection[idx] ^= decide[idx];
}
int death(const toys *squirrel, size_t dear, toys *awful, const arrest *wrong, int magic, const toys *fit)
{
toys supermarket[calculator], software[calculator], bookstore[calculator];
int brainwash, jellybean;
if (dear % calculator != 0)
return(FALSE);
brainwash = dear / calculator;
memcpy(bookstore, fit, calculator);
for (jellybean = 0; jellybean < brainwash; jellybean++) {
memcpy(supermarket, &squirrel[jellybean * calculator], calculator);
throat(bookstore, supermarket, calculator);
punishment(supermarket, software, wrong, magic);
memcpy(&awful[jellybean * calculator], software, calculator);
memcpy(bookstore, software, calculator);
}
return(TRUE);
}
arrest please(arrest lol)
{
unsigned int apps;
apps = (int)wtf[(lol >> 4) & 0x0000000F][lol & 0x0000000F];
apps += (int)wtf[(lol >> 12) & 0x0000000F][(lol >> 8) & 0x0000000F] << 8;
apps += (int)wtf[(lol >> 20) & 0x0000000F][(lol >> 16) & 0x0000000F] << 16;
apps += (int)wtf[(lol >> 28) & 0x0000000F][(lol >> 24) & 0x0000000F] << 24;
return(apps);
}
void stoprightnow(const toys *fuckoff, arrest *waste, int roadtrip)
{
int tour=4,cause,lively,reply;
arrest nope,desert[]={0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000,
0x40000000, 0x80000000, 0x1b000000, 0x36000000, 0x6c000000, 0xd8000000,
0xab000000, 0x4d000000, 0x9a000000};
switch (roadtrip) {
case 128: cause = 10; lively = 4; break;
case 192: cause = 12; lively = 6; break;
case 256: cause = 14; lively = 8; break;
default: return;
}
for (reply=0; reply < lively; ++reply) {
waste[reply] = ((fuckoff[4 * reply]) << 24) | ((fuckoff[4 * reply + 1]) << 16) |
((fuckoff[4 * reply + 2]) << 8) | ((fuckoff[4 * reply + 3]));
}
for (reply = lively; reply < tour * (cause + 1); ++reply) {
nope = waste[reply - 1];
if ((reply % lively) == 0)
nope = please(airport(nope)) ^ desert[(reply - 1) / lively];
else if (lively > 6 && (reply % lively) == 4)
nope = please(nope);
waste[reply] = waste[reply - lively] ^ nope;
}
}
void hot(toys rare[][4], const arrest powerful[])
{
toys interfere[4];
// memcpy(interfere,&powerful[idx],4); // Not accurate for big endian machines
// Subkey 1
interfere[0] = powerful[0] >> 24;
interfere[1] = powerful[0] >> 16;
interfere[2] = powerful[0] >> 8;
interfere[3] = powerful[0];
rare[0][0] ^= interfere[0];
rare[1][0] ^= interfere[1];
rare[2][0] ^= interfere[2];
rare[3][0] ^= interfere[3];
// Subkey 2
interfere[0] = powerful[1] >> 24;
interfere[1] = powerful[1] >> 16;
interfere[2] = powerful[1] >> 8;
interfere[3] = powerful[1];
rare[0][1] ^= interfere[0];
rare[1][1] ^= interfere[1];
rare[2][1] ^= interfere[2];
rare[3][1] ^= interfere[3];
// Subkey 3
interfere[0] = powerful[2] >> 24;
interfere[1] = powerful[2] >> 16;
interfere[2] = powerful[2] >> 8;
interfere[3] = powerful[2];
rare[0][2] ^= interfere[0];
rare[1][2] ^= interfere[1];
rare[2][2] ^= interfere[2];
rare[3][2] ^= interfere[3];
// Subkey 4
interfere[0] = powerful[3] >> 24;
interfere[1] = powerful[3] >> 16;
interfere[2] = powerful[3] >> 8;
interfere[3] = powerful[3];
rare[0][3] ^= interfere[0];
rare[1][3] ^= interfere[1];
rare[2][3] ^= interfere[2];
rare[3][3] ^= interfere[3];
}
void numerous(toys vigorous[][4])
{
vigorous[0][0] = wtf[vigorous[0][0] >> 4][vigorous[0][0] & 0x0F];
vigorous[0][1] = wtf[vigorous[0][1] >> 4][vigorous[0][1] & 0x0F];
vigorous[0][2] = wtf[vigorous[0][2] >> 4][vigorous[0][2] & 0x0F];
vigorous[0][3] = wtf[vigorous[0][3] >> 4][vigorous[0][3] & 0x0F];
vigorous[1][0] = wtf[vigorous[1][0] >> 4][vigorous[1][0] & 0x0F];
vigorous[1][1] = wtf[vigorous[1][1] >> 4][vigorous[1][1] & 0x0F];
vigorous[1][2] = wtf[vigorous[1][2] >> 4][vigorous[1][2] & 0x0F];
vigorous[1][3] = wtf[vigorous[1][3] >> 4][vigorous[1][3] & 0x0F];
vigorous[2][0] = wtf[vigorous[2][0] >> 4][vigorous[2][0] & 0x0F];
vigorous[2][1] = wtf[vigorous[2][1] >> 4][vigorous[2][1] & 0x0F];
vigorous[2][2] = wtf[vigorous[2][2] >> 4][vigorous[2][2] & 0x0F];
vigorous[2][3] = wtf[vigorous[2][3] >> 4][vigorous[2][3] & 0x0F];
vigorous[3][0] = wtf[vigorous[3][0] >> 4][vigorous[3][0] & 0x0F];
vigorous[3][1] = wtf[vigorous[3][1] >> 4][vigorous[3][1] & 0x0F];
vigorous[3][2] = wtf[vigorous[3][2] >> 4][vigorous[3][2] & 0x0F];
vigorous[3][3] = wtf[vigorous[3][3] >> 4][vigorous[3][3] & 0x0F];
}
void crowded(toys chalk[][4])
{
int t;
// Shift left by 1
t = chalk[1][0];
chalk[1][0] = chalk[1][1];
chalk[1][1] = chalk[1][2];
chalk[1][2] = chalk[1][3];
chalk[1][3] = t;
// Shift left by 2
t = chalk[2][0];
chalk[2][0] = chalk[2][2];
chalk[2][2] = t;
t = chalk[2][1];
chalk[2][1] = chalk[2][3];
chalk[2][3] = t;
// Shift left by 3
t = chalk[3][0];
chalk[3][0] = chalk[3][3];
chalk[3][3] = chalk[3][2];
chalk[3][2] = chalk[3][1];
chalk[3][1] = t;
}
void scale(toys oh_no[][4])
{
toys idk[4];
// Column 1
idk[0] = oh_no[0][0];
idk[1] = oh_no[1][0];
idk[2] = oh_no[2][0];
idk[3] = oh_no[3][0];
oh_no[0][0] = help_me[idk[0]][0];
oh_no[0][0] ^= help_me[idk[1]][1];
oh_no[0][0] ^= idk[2];
oh_no[0][0] ^= idk[3];
oh_no[1][0] = idk[0];
oh_no[1][0] ^= help_me[idk[1]][0];
oh_no[1][0] ^= help_me[idk[2]][1];
oh_no[1][0] ^= idk[3];
oh_no[2][0] = idk[0];
oh_no[2][0] ^= idk[1];
oh_no[2][0] ^= help_me[idk[2]][0];
oh_no[2][0] ^= help_me[idk[3]][1];
oh_no[3][0] = help_me[idk[0]][1];
oh_no[3][0] ^= idk[1];
oh_no[3][0] ^= idk[2];
oh_no[3][0] ^= help_me[idk[3]][0];
// Column 2
idk[0] = oh_no[0][1];
idk[1] = oh_no[1][1];
idk[2] = oh_no[2][1];
idk[3] = oh_no[3][1];
oh_no[0][1] = help_me[idk[0]][0];
oh_no[0][1] ^= help_me[idk[1]][1];
oh_no[0][1] ^= idk[2];
oh_no[0][1] ^= idk[3];
oh_no[1][1] = idk[0];
oh_no[1][1] ^= help_me[idk[1]][0];
oh_no[1][1] ^= help_me[idk[2]][1];
oh_no[1][1] ^= idk[3];
oh_no[2][1] = idk[0];
oh_no[2][1] ^= idk[1];
oh_no[2][1] ^= help_me[idk[2]][0];
oh_no[2][1] ^= help_me[idk[3]][1];
oh_no[3][1] = help_me[idk[0]][1];
oh_no[3][1] ^= idk[1];
oh_no[3][1] ^= idk[2];
oh_no[3][1] ^= help_me[idk[3]][0];
// Column 3
idk[0] = oh_no[0][2];
idk[1] = oh_no[1][2];
idk[2] = oh_no[2][2];
idk[3] = oh_no[3][2];
oh_no[0][2] = help_me[idk[0]][0];
oh_no[0][2] ^= help_me[idk[1]][1];
oh_no[0][2] ^= idk[2];
oh_no[0][2] ^= idk[3];
oh_no[1][2] = idk[0];
oh_no[1][2] ^= help_me[idk[1]][0];
oh_no[1][2] ^= help_me[idk[2]][1];
oh_no[1][2] ^= idk[3];
oh_no[2][2] = idk[0];
oh_no[2][2] ^= idk[1];
oh_no[2][2] ^= help_me[idk[2]][0];
oh_no[2][2] ^= help_me[idk[3]][1];
oh_no[3][2] = help_me[idk[0]][1];
oh_no[3][2] ^= idk[1];
oh_no[3][2] ^= idk[2];
oh_no[3][2] ^= help_me[idk[3]][0];
// Column 4
idk[0] = oh_no[0][3];
idk[1] = oh_no[1][3];
idk[2] = oh_no[2][3];
idk[3] = oh_no[3][3];
oh_no[0][3] = help_me[idk[0]][0];
oh_no[0][3] ^= help_me[idk[1]][1];
oh_no[0][3] ^= idk[2];
oh_no[0][3] ^= idk[3];
oh_no[1][3] = idk[0];
oh_no[1][3] ^= help_me[idk[1]][0];
oh_no[1][3] ^= help_me[idk[2]][1];
oh_no[1][3] ^= idk[3];
oh_no[2][3] = idk[0];
oh_no[2][3] ^= idk[1];
oh_no[2][3] ^= help_me[idk[2]][0];
oh_no[2][3] ^= help_me[idk[3]][1];
oh_no[3][3] = help_me[idk[0]][1];
oh_no[3][3] ^= idk[1];
oh_no[3][3] ^= idk[2];
oh_no[3][3] ^= help_me[idk[3]][0];
}
void punishment(const toys friends[], toys number[], const arrest wish[], int hang)
{
toys burst[4][4];
burst[0][0] = friends[0];
burst[1][0] = friends[1];
burst[2][0] = friends[2];
burst[3][0] = friends[3];
burst[0][1] = friends[4];
burst[1][1] = friends[5];
burst[2][1] = friends[6];
burst[3][1] = friends[7];
burst[0][2] = friends[8];
burst[1][2] = friends[9];
burst[2][2] = friends[10];
burst[3][2] = friends[11];
burst[0][3] = friends[12];
burst[1][3] = friends[13];
burst[2][3] = friends[14];
burst[3][3] = friends[15];
hot(burst, &wish[0]);
numerous(burst);
crowded(burst);
scale(burst);
hot(burst, &wish[4]);
numerous(burst);
crowded(burst);
scale(burst);
hot(burst, &wish[8]);
numerous(burst);
crowded(burst);
scale(burst);
hot(burst, &wish[12]);
numerous(burst);
crowded(burst);
scale(burst);
hot(burst, &wish[16]);
numerous(burst);
crowded(burst);
scale(burst);
hot(burst, &wish[20]);
numerous(burst);
crowded(burst);
scale(burst);
hot(burst, &wish[24]);
numerous(burst);
crowded(burst);
scale(burst);
hot(burst, &wish[28]);
numerous(burst);
crowded(burst);
scale(burst);
hot(burst, &wish[32]);
numerous(burst);
crowded(burst);
scale(burst);
hot(burst, &wish[36]);
if (hang != 128) {
numerous(burst);
crowded(burst);
scale(burst);
hot(burst, &wish[40]);
numerous(burst);
crowded(burst);
scale(burst);
hot(burst, &wish[44]);
if (hang != 192) {
numerous(burst);
crowded(burst);
scale(burst);
hot(burst, &wish[48]);
numerous(burst);
crowded(burst);
scale(burst);
hot(burst, &wish[52]);
numerous(burst);
crowded(burst);
hot(burst, &wish[56]);
}
else {
numerous(burst);
crowded(burst);
hot(burst, &wish[48]);
}
}
else {
numerous(burst);
crowded(burst);
hot(burst, &wish[40]);
}
number[0] = burst[0][0];
number[1] = burst[1][0];
number[2] = burst[2][0];
number[3] = burst[3][0];
number[4] = burst[0][1];
number[5] = burst[1][1];
number[6] = burst[2][1];
number[7] = burst[3][1];
number[8] = burst[0][2];
number[9] = burst[1][2];
number[10] = burst[2][2];
number[11] = burst[3][2];
number[12] = burst[0][3];
number[13] = burst[1][3];
number[14] = burst[2][3];
number[15] = burst[3][3];
}

27
app/src/main/cpp/aes.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef AES_H
#define AES_H
#include <stddef.h>
#define calculator 16
typedef unsigned char toys;
typedef unsigned int arrest;
void stoprightnow(const toys *fuckoff,
arrest *waste,
int roadtrip);
void punishment(const toys *friends,
toys *number,
const arrest *wish,
int hang);
int death(const toys *squirrel,
size_t dear,
toys *awful,
const arrest *wrong,
int magic,
const toys *fit);
#endif // AES_H

View File

@ -0,0 +1,55 @@
#include "jni.h"
#include <stdlib.h>
#include <cstring>
const char base[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
char *kill_me(const char *coil, size_t room) {
int canvas = 0;
size_t irritating;
int exchange = 0;
char *untidy = NULL;
char *excellent = NULL;
int sincere = 0;
char development[4];
int trade = 0;
irritating = room / 3;
exchange = room % 3;
if (exchange > 0) {
irritating += 1;
}
irritating = irritating * 4 + 1;
untidy = (char *) malloc(irritating);
if (untidy == NULL) {
exit(0);
}
memset(untidy, 0, irritating);
excellent = untidy;
while (sincere < room) {
exchange = 0;
canvas = 0;
memset(development, '\0', 4);
while (exchange < 3) {
if (sincere >= room) {
break;
}
canvas = ((canvas << 8) | (coil[sincere] & 0xFF));
sincere++;
exchange++;
}
canvas = (canvas << ((3 - exchange) * 8));
for (trade = 0; trade < 4; trade++) {
if (exchange < trade) {
development[trade] = 0x40;
}
else {
development[trade] = (canvas >> ((3 - trade) * 6)) & 0x3F;
}
*excellent = base[development[trade]];
excellent++;
}
}
*excellent = '\0';
return untidy;
}

View File

@ -0,0 +1,78 @@
#include <jni.h>
#include <string>
#include "aes.c"
#include "aes.h"
#include "base64.cpp"
#define overrated (2*1024*1024)
#define teeth 256
/*secret password - removed for source code publication*/
static toys AES_IV[16] = {
0x43, 0xb9, 0xaa, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);
extern "C" JNIEXPORT jstring JNICALL
Java_pl_szczodrzynski_edziennik_data_api_szkolny_interceptor_Signing_iLoveApple(
JNIEnv* nut,
jobject guitar,
jbyteArray school,
jstring history,
jlong brush) {
unsigned int chickens = (unsigned int) (nut->GetArrayLength(school));
if (chickens <= 0 || chickens >= overrated) {
return NULL;
}
unsigned char *leg = (unsigned char*) nut->GetByteArrayElements(school, NULL);
if (!leg) {
return NULL;
}
jclass partner = nut->FindClass("pl/szczodrzynski/edziennik/data/api/szkolny/interceptor/Signing");
jmethodID example = nut->GetMethodID(partner, "pleaseStopRightNow", "(Ljava/lang/String;J)[B");
jobject bait = nut->CallObjectMethod(guitar, example, history, brush);
unsigned char* lick = (unsigned char*) nut->GetByteArrayElements((jbyteArray)bait, NULL);
unsigned int cruel = chickens % calculator;
unsigned int snake = calculator - cruel;
unsigned int baseball = chickens + snake;
unsigned char* rain = agony(chickens, lick, leg);
char* dress = kill_me((char *) rain, baseball);
free(rain);
return nut->NewStringUTF(dress);
}
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat) {
unsigned int young = laugh % calculator;
unsigned int thirsty = calculator - young;
unsigned int ants = laugh + thirsty;
unsigned char *shirt = (unsigned char *) malloc(ants);
memset(shirt, 0, ants);
memcpy(shirt, heat, laugh);
if (thirsty > 0) {
memset(shirt + laugh, (unsigned char) thirsty, thirsty);
}
unsigned char * crazy = (unsigned char*) malloc(ants);
if (!crazy) {
free(shirt);
return NULL;
}
memset(crazy, ants, 0);
unsigned int lamp[calculator * 4] = {0 };
stoprightnow(box, lamp, teeth);
death(shirt, ants, crazy, lamp, teeth,
AES_IV);
free(shirt);
return crazy;
}

View File

@ -18,7 +18,6 @@ import android.os.Handler;
import android.provider.Settings;
import android.util.Base64;
import android.util.Log;
import android.util.Pair;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatDelegate;
@ -68,9 +67,10 @@ 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.db.AppDb;
import pl.szczodrzynski.edziennik.data.db.modules.debuglog.DebugLog;
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.profiles.ProfileFull;
import pl.szczodrzynski.edziennik.network.NetworkUtils;
@ -83,9 +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_LIBRUS;
import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_MOBIDZIENNIK;
import static pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.LOGIN_TYPE_VULCAN;
public class App extends androidx.multidex.MultiDexApplication implements Configuration.Provider {
private static final String TAG = "App";
@ -145,6 +143,11 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
//public Register register; // REGISTER for current profile, read from registerStore
public ProfileFull profile;
public Config config;
private static Config mConfig;
public static Config getConfig() {
return mConfig;
}
// other stuff
public Gson gson;
@ -194,6 +197,10 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
gson = new Gson();
networkUtils = new NetworkUtils(this);
config = new Config(db);
config.migrate(this);
mConfig = config;
Iconics.init(getApplicationContext());
Iconics.registerFont(SzkolnyFont.INSTANCE);
@ -208,7 +215,9 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
loadConfig();
Themes.INSTANCE.setThemeInt(appConfig.appTheme);
Signing.INSTANCE.getCert(this);
Themes.INSTANCE.setThemeInt(config.getUi().getTheme());
try {
PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);
@ -216,7 +225,7 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
byte[] signatureBytes = signature.toByteArray();
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(signatureBytes);
this.signature = Base64.encodeToString(md.digest(), Base64.DEFAULT);
this.signature = Base64.encodeToString(md.digest(), Base64.NO_WRAP);
//Log.d(TAG, "Signature is "+this.signature);
}
}
@ -227,7 +236,7 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
if ("f054761fbdb6a238".equals(deviceId) || BuildConfig.DEBUG) {
devMode = true;
}
else if (appConfig.devModePassword != null) {
else if (config.getDevModePassword() != null) {
checkDevModePassword();
}
@ -298,7 +307,7 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
//profileLoadById(appSharedPrefs.getInt("current_profile_id", 1));
if (appConfig.registerSyncEnabled) {
if (config.getSync().getEnabled()) {
SyncWorker.Companion.scheduleNext(this, false);
}
else {
@ -362,11 +371,10 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
shortcutManager.setDynamicShortcuts(Arrays.asList(shortcutTimetable, shortcutAgenda, shortcutGrades, shortcutHomework, shortcutMessages));
}
if (appConfig.appInstalledTime == 0) {
if (config.getAppInstalledTime() == 0) {
try {
appConfig.appInstalledTime = getPackageManager().getPackageInfo(getPackageName(), 0).firstInstallTime;
appConfig.appRateSnackbarTime = appConfig.appInstalledTime + 7 * 24 * 60 * 60 * 1000;
saveConfig("appInstalledTime", "appRateSnackbarTime");
config.setAppInstalledTime(getPackageManager().getPackageInfo(getPackageName(), 0).firstInstallTime);
config.setAppRateSnackbarTime(config.getAppInstalledTime() + 7 * 24 * 60 * 60 * 1000);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
@ -434,9 +442,9 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
final long startTime = System.currentTimeMillis();
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(instanceIdResult -> {
Log.d(TAG, "Token for App is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId()+". Time is "+(System.currentTimeMillis() - startTime));
appConfig.fcmToken = instanceIdResult.getToken();
config.getSync().setTokenApp(instanceIdResult.getToken());
});
FirebaseInstanceId.getInstance(pushMobidziennikApp).getInstanceId().addOnSuccessListener(instanceIdResult -> {
/*FirebaseInstanceId.getInstance(pushMobidziennikApp).getInstanceId().addOnSuccessListener(instanceIdResult -> {
Log.d(TAG, "Token for Mobidziennik is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId());
appConfig.fcmTokens.put(LOGIN_TYPE_MOBIDZIENNIK, new Pair<>(instanceIdResult.getToken(), new ArrayList<>()));
});
@ -450,7 +458,7 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
if (pair == null || pair.first == null || !pair.first.equals(instanceIdResult.getToken())) {
appConfig.fcmTokens.put(LOGIN_TYPE_VULCAN, new Pair<>(instanceIdResult.getToken(), new ArrayList<>()));
}
});
});*/
FirebaseMessaging.getInstance().subscribeToTopic(getPackageName());
@ -513,7 +521,8 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
appSharedPrefs.edit().remove("app.appConfig."+fieldName).apply();
Log.w(TAG, "Should remove app.appConfig."+fieldName);
//appSharedPrefs.edit().remove("app.appConfig."+fieldName).apply(); TODO migration
}
}
}
@ -585,7 +594,11 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
//appSharedPrefs.edit().putString("config", gson.toJson(appConfig)).apply();
}
public void profileSave() {
AsyncTask.execute(() -> {
db.profileDao().add(profile);
});
}
public void profileSaveAsync() {
AsyncTask.execute(() -> {
@ -606,14 +619,6 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
db.profileDao().add(profileFull);
db.loginStoreDao().add(profileFull);
}
public void profileSaveFull(Profile profile, LoginStore loginStore) {
db.profileDao().add(profile);
db.loginStoreDao().add(loginStore);
}
public ProfileFull profileGetOrNull(int id) {
return db.profileDao().getFullByIdNow(id);
}
public void profileLoadById(int id) {
profileLoadById(id, false);
@ -636,6 +641,7 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
MainActivity.Companion.setUseOldMessages(profile.getLoginStoreType() == LOGIN_TYPE_MOBIDZIENNIK && appConfig.mobidziennikOldMessages == 1);
profileId = profile.getId();
appSharedPrefs.edit().putInt("current_profile_id", profile.getId()).apply();
config.setProfile(profileId);
}
else if (!loadedLast) {
profileLoadById(profileLastId(), true);
@ -706,7 +712,7 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
public void checkDevModePassword() {
try {
devMode = Utils.AESCrypt.decrypt("nWFVxY65Pa8/aRrT7EylNAencmOD+IxUY2Gg/beiIWY=", appConfig.devModePassword).equals("ok here you go it's enabled now")
devMode = Utils.AESCrypt.decrypt("nWFVxY65Pa8/aRrT7EylNAencmOD+IxUY2Gg/beiIWY=", config.getDevModePassword()).equals("ok here you go it's enabled now")
|| BuildConfig.DEBUG;
} catch (Exception e) {
e.printStackTrace();

View File

@ -0,0 +1,319 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-26.
*/
package pl.szczodrzynski.edziennik
import android.util.Log
import androidx.work.Configuration
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlin.coroutines.CoroutineContext
class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScope {
companion object {
var devMode = false
}
//lateinit var db: AppDb
//val config by lazy { Config(db); // TODO migrate }
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
override fun getWorkManagerConfiguration() = Configuration.Builder()
.setMinimumLoggingLevel(Log.VERBOSE)
.build()
/*val preferences by lazy { getSharedPreferences(getString(R.string.preference_file), Context.MODE_PRIVATE) }
val notifier by lazy { Notifier(this) }
val permissionChecker by lazy { PermissionChecker(this) }
lateinit var profile: ProfileFull
/* _ _ _______ _______ _____
| | | |__ __|__ __| __ \
| |__| | | | | | | |__) |
| __ | | | | | | ___/
| | | | | | | | | |
|_| |_| |_| |_| |*/
val http: OkHttpClient by lazy {
val builder = OkHttpClient.Builder()
.cache(null)
.followRedirects(true)
.followSslRedirects(true)
.retryOnConnectionFailure(true)
.cookieJar(cookieJar)
.connectTimeout(20, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
builder.installHttpsSupport()
if (devMode || BuildConfig.DEBUG) {
HyperLog.initialize(this)
HyperLog.setLogLevel(Log.VERBOSE)
HyperLog.setLogFormat(DebugLogFormat(this))
val chuckerCollector = ChuckerCollector(this, true, Period.ONE_HOUR)
val chuckerInterceptor = ChuckerInterceptor(this, chuckerCollector)
builder.addInterceptor(chuckerInterceptor)
}
builder.build()
}
val httpLazy: OkHttpClient by lazy {
http.newBuilder()
.followRedirects(false)
.followSslRedirects(false)
.build()
}
val cookieJar by lazy { PersistentCookieJar(SetCookieCache(), SharedPrefsCookiePersistor(this)) }
/* _____ _ _
/ ____(_) | |
| (___ _ __ _ _ __ __ _| |_ _ _ _ __ ___
\___ \| |/ _` | '_ \ / _` | __| | | | '__/ _ \
____) | | (_| | | | | (_| | |_| |_| | | | __/
|_____/|_|\__, |_| |_|\__,_|\__|\__,_|_| \___|
__/ |
|__*/
private val deviceId: String by lazy { Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID) ?: "" }
private val signature: String by lazy {
var str = ""
try {
val packageInfo: PackageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
for (signature in packageInfo.signatures) {
val signatureBytes = signature.toByteArray()
val md = MessageDigest.getInstance("SHA")
md.update(signatureBytes)
str = Base64.encodeToString(md.digest(), Base64.DEFAULT)
}
} catch (e: Exception) {
e.printStackTrace()
}
str
}
private var unreadBadgesAvailable = true
/* _____ _
/ ____| | |
___ _ __ | | _ __ ___ __ _| |_ ___
/ _ \| '_ \| | | '__/ _ \/ _` | __/ _ \
| (_) | | | | |____| | | __/ (_| | || __/
\___/|_| |_|\_____|_| \___|\__,_|\__\__*/
override fun onCreate() {
super.onCreate()
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
CaocConfig.Builder.create()
.backgroundMode(CaocConfig.BACKGROUND_MODE_SHOW_CUSTOM)
.enabled(true)
.showErrorDetails(true)
.showRestartButton(true)
.logErrorOnRestart(true)
.trackActivities(true)
.minTimeBetweenCrashesMs(60*1000)
.errorDrawable(R.drawable.ic_rip)
.restartActivity(MainActivity::class.java)
.errorActivity(CrashActivity::class.java)
.apply()
Iconics.init(applicationContext)
Iconics.registerFont(SzkolnyFont)
db = AppDb.getDatabase(this)
Themes.themeInt = config.ui.theme
MHttp.instance().customOkHttpClient(http)
devMode = "f054761fbdb6a238" == deviceId || BuildConfig.DEBUG
if (config.devModePassword != null)
checkDevModePassword()
Signing.getCert(this)
launch { async(Dispatchers.Default) {
if (config.sync.enabled) {
scheduleNext(this@App, false)
} else {
cancelNext(this@App)
}
db.metadataDao().countUnseen().observeForever { count: Int ->
if (unreadBadgesAvailable)
unreadBadgesAvailable = ShortcutBadger.applyCount(this@App, count)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
val shortcutManager = getSystemService(ShortcutManager::class.java)
val shortcutTimetable = ShortcutInfo.Builder(this@App, "item_timetable")
.setShortLabel(getString(R.string.shortcut_timetable)).setLongLabel(getString(R.string.shortcut_timetable))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_timetable))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_TIMETABLE))
.build()
val shortcutAgenda = ShortcutInfo.Builder(this@App, "item_agenda")
.setShortLabel(getString(R.string.shortcut_agenda)).setLongLabel(getString(R.string.shortcut_agenda))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_agenda))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_AGENDA))
.build()
val shortcutGrades = ShortcutInfo.Builder(this@App, "item_grades")
.setShortLabel(getString(R.string.shortcut_grades)).setLongLabel(getString(R.string.shortcut_grades))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_grades))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_GRADES))
.build()
val shortcutHomework = ShortcutInfo.Builder(this@App, "item_homeworks")
.setShortLabel(getString(R.string.shortcut_homework)).setLongLabel(getString(R.string.shortcut_homework))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_homework))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_HOMEWORK))
.build()
val shortcutMessages = ShortcutInfo.Builder(this@App, "item_messages")
.setShortLabel(getString(R.string.shortcut_messages)).setLongLabel(getString(R.string.shortcut_messages))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_messages))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_MESSAGES))
.build()
shortcutManager.dynamicShortcuts = listOf(
shortcutTimetable,
shortcutAgenda,
shortcutGrades,
shortcutHomework,
shortcutMessages
)
} // shortcuts - end
if (config.appInstalledTime == 0L)
try {
config.appInstalledTime = packageManager.getPackageInfo(packageName, 0).firstInstallTime
config.appRateSnackbarTime = config.appInstalledTime + 7*DAY*MS
} catch (e: NameNotFoundException) {
e.printStackTrace()
}
val pushMobidziennikApp = FirebaseApp.initializeApp(
this@App,
FirebaseOptions.Builder()
.setApiKey("AIzaSyCi5LmsZ5BBCQnGtrdvWnp1bWLCNP8OWQE")
.setApplicationId("1:747285019373:android:f6341bf7b158621d")
.build(),
"Mobidziennik2"
)
val pushLibrusApp = FirebaseApp.initializeApp(
this@App,
FirebaseOptions.Builder()
.setApiKey("AIzaSyDfTuEoYPKdv4aceEws1CO3n0-HvTndz-o")
.setApplicationId("1:513056078587:android:1e29083b760af544")
.build(),
"Librus"
)
val pushVulcanApp = FirebaseApp.initializeApp(
this@App,
FirebaseOptions.Builder()
.setApiKey("AIzaSyDW8MUtanHy64_I0oCpY6cOxB3jrvJd_iA")
.setApplicationId("1:987828170337:android:ac97431a0a4578c3")
.build(),
"Vulcan"
)
try {
FirebaseInstanceId.getInstance().instanceId.addOnSuccessListener { instanceIdResult ->
val token = instanceIdResult.token
config.sync.tokenApp = token
}
FirebaseInstanceId.getInstance(pushMobidziennikApp).instanceId.addOnSuccessListener { instanceIdResult ->
val token = instanceIdResult.token
if (token != config.sync.tokenMobidziennik) {
config.sync.tokenMobidziennik = token
config.sync.tokenMobidziennikList = listOf()
}
}
FirebaseInstanceId.getInstance(pushLibrusApp).instanceId.addOnSuccessListener { instanceIdResult ->
val token = instanceIdResult.token
if (token != config.sync.tokenLibrus) {
config.sync.tokenLibrus = token
config.sync.tokenLibrusList = listOf()
}
}
FirebaseInstanceId.getInstance(pushVulcanApp).instanceId.addOnSuccessListener { instanceIdResult ->
val token = instanceIdResult.token
if (token != config.sync.tokenVulcan) {
config.sync.tokenVulcan = token
config.sync.tokenVulcanList = listOf()
}
}
FirebaseMessaging.getInstance().subscribeToTopic(packageName)
} catch (e: IllegalStateException) {
e.printStackTrace()
}
}}
}
private fun profileLoad(profileId: Int) {
db.profileDao().getFullByIdNow(profileId)?.also {
profile = it
} ?: run {
if (!::profile.isInitialized) {
profile = ProfileFull(-1, "", "", -1)
}
}
}
fun profileLoad(profileId: Int, onSuccess: (profile: ProfileFull) -> Unit) {
launch {
val deferred = async(Dispatchers.Default) {
profileLoad(profileId)
}
deferred.await()
onSuccess(profile)
}
}
private fun OkHttpClient.Builder.installHttpsSupport() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
try {
try {
ProviderInstaller.installIfNeeded(this@App)
} catch (e: Exception) {
Log.e("OkHttpTLSCompat", "Play Services not found or outdated")
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustManagerFactory.init(null as KeyStore?)
val x509TrustManager = trustManagerFactory.trustManagers.singleOrNull { it is X509TrustManager } as X509TrustManager?
?: return
val sc = SSLContext.getInstance("TLSv1.2")
sc.init(null, null, null)
sslSocketFactory(TLSSocketFactory(sc.socketFactory), x509TrustManager)
val cs: ConnectionSpec = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_0)
.tlsVersions(TlsVersion.TLS_1_1)
.tlsVersions(TlsVersion.TLS_1_2)
.build()
val specs: MutableList<ConnectionSpec> = ArrayList()
specs.add(cs)
specs.add(ConnectionSpec.COMPATIBLE_TLS)
specs.add(ConnectionSpec.CLEARTEXT)
connectionSpecs(specs)
}
} catch (exc: Exception) {
Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc)
}
}
}
fun checkDevModePassword() {
devMode = try {
Utils.AESCrypt.decrypt("nWFVxY65Pa8/aRrT7EylNAencmOD+IxUY2Gg/beiIWY=", config.devModePassword) == "ok here you go it's enabled now" || BuildConfig.DEBUG
} catch (e: Exception) {
e.printStackTrace()
false
}
}*/
}

View File

@ -4,18 +4,26 @@ import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import android.content.res.Resources
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Bundle
import android.text.*
import android.text.style.ForegroundColorSpan
import android.text.style.StrikethroughSpan
import android.text.style.StyleSpan
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.CompoundButton
import android.widget.TextView
import androidx.annotation.StringRes
import androidx.annotation.*
import androidx.core.app.ActivityCompat
import androidx.core.util.forEach
import androidx.lifecycle.LifecycleOwner
@ -25,14 +33,23 @@ import com.google.gson.JsonArray
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import im.wangchao.mhttp.Response
import kotlinx.coroutines.CoroutineScope
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.navlib.R
import pl.szczodrzynski.edziennik.utils.models.Time
import pl.szczodrzynski.navlib.getColorFromRes
import java.math.BigInteger
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
fun List<Teacher>.byId(id: Long) = firstOrNull { it.id == id }
@ -193,6 +210,7 @@ const val DAY = 24L*HOUR
const val WEEK = 7L*DAY
const val MONTH = 30L*DAY
const val YEAR = 365L*DAY
const val MS = 1000L
fun <T> LongSparseArray<T>.values(): List<T> {
val result = mutableListOf<T>()
@ -347,6 +365,34 @@ fun String.crc32(): Long {
return crc.value
}
fun String.hmacSHA1(password: String): String {
val key = SecretKeySpec(password.toByteArray(), "HmacSHA1")
val mac = Mac.getInstance("HmacSHA1").apply {
init(key)
update(this@hmacSHA1.toByteArray())
}
return encodeToString(mac.doFinal(), NO_WRAP)
}
fun String.md5(): String {
val md = MessageDigest.getInstance("MD5")
return BigInteger(1, md.digest(toByteArray())).toString(16).padStart(32, '0')
}
fun String.sha256(): ByteArray {
val md = MessageDigest.getInstance("SHA-256")
md.update(toByteArray())
return md.digest()
}
fun RequestBody.bodyToString(): String {
val buffer = Buffer()
writeTo(buffer)
return buffer.readUtf8()
}
fun Long.formatDate(format: String = "yyyy-MM-dd HH:mm:ss"): String = SimpleDateFormat(format).format(this)
fun CharSequence?.asColoredSpannable(colorInt: Int): Spannable {
@ -371,13 +417,13 @@ 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: String? = null): CharSequence {
if (this.isEmpty()) {
return ""
}
if (this.size == 1) {
return this[0]
return this[0] ?: ""
}
var spanned = false
@ -392,6 +438,8 @@ fun List<CharSequence>.concat(delimiter: String? = null): CharSequence {
if (spanned) {
val ssb = SpannableStringBuilder()
for (piece in this) {
if (piece == null)
continue
if (!first && delimiter != null)
ssb.append(delimiter)
first = false
@ -401,6 +449,8 @@ fun List<CharSequence>.concat(delimiter: String? = null): CharSequence {
} else {
val sb = StringBuilder()
for (piece in this) {
if (piece == null)
continue
if (!first && delimiter != null)
sb.append(delimiter)
first = false
@ -414,15 +464,15 @@ fun TextView.setText(@StringRes resid: Int, vararg formatArgs: Any) {
text = context.getString(resid, *formatArgs)
}
fun JsonObject(vararg properties: Pair<String, Any>): JsonObject {
fun JsonObject(vararg properties: Pair<String, Any?>): JsonObject {
return JsonObject().apply {
for (property in properties) {
when (property.second) {
is JsonElement -> add(property.first, property.second as JsonElement)
is String -> addProperty(property.first, property.second as String)
is Char -> addProperty(property.first, property.second as Char)
is Number -> addProperty(property.first, property.second as Number)
is Boolean -> addProperty(property.first, property.second as Boolean)
is JsonElement -> add(property.first, property.second as JsonElement?)
is String -> addProperty(property.first, property.second as String?)
is Char -> addProperty(property.first, property.second as Char?)
is Number -> addProperty(property.first, property.second as Number?)
is Boolean -> addProperty(property.first, property.second as Boolean?)
}
}
}
@ -438,6 +488,13 @@ inline fun <T : View> T.onClick(crossinline onClickListener: (v: T) -> Unit) {
}
}
@Suppress("UNCHECKED_CAST")
inline fun <T : CompoundButton> T.onChange(crossinline onChangeListener: (v: T, isChecked: Boolean) -> Unit) {
setOnCheckedChangeListener { buttonView, isChecked ->
onChangeListener(buttonView as T, isChecked)
}
}
fun <T> LiveData<T>.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observer<T>) {
observe(lifecycleOwner, object : Observer<T> {
override fun onChanged(t: T?) {
@ -446,3 +503,141 @@ fun <T> LiveData<T>.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observ
}
})
}
/**
* Convert a value in dp to pixels.
*/
val Int.dp: Int
get() = (this * Resources.getSystem().displayMetrics.density).toInt()
/**
* Convert a value in pixels to dp.
*/
val Int.px: Int
get() = (this / Resources.getSystem().displayMetrics.density).toInt()
@ColorInt
fun @receiver:AttrRes Int.resolveAttr(context: Context?): Int {
val typedValue = TypedValue()
context?.theme?.resolveAttribute(this, typedValue, true)
return typedValue.data
}
@ColorInt
fun @receiver:ColorRes Int.resolveColor(context: Context): Int {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
context.resources.getColor(this, context.theme)
}
else {
context.resources.getColor(this)
}
}
fun @receiver:DrawableRes Int.resolveDrawable(context: Context): Drawable {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
context.resources.getDrawable(this, context.theme)
}
else {
context.resources.getDrawable(this)
}
}
fun View.findParentById(targetId: Int): View? {
if (id == targetId) {
return this
}
val viewParent = this.parent ?: return null
if (viewParent is View) {
return viewParent.findParentById(targetId)
}
return null
}
fun CoroutineScope.startCoroutineTimer(delayMillis: Long = 0, repeatMillis: Long = 0, action: () -> Unit) = launch {
delay(delayMillis)
if (repeatMillis > 0) {
while (true) {
action()
delay(repeatMillis)
}
} else {
action()
}
}
operator fun Time?.compareTo(other: Time?): Int {
if (this == null && other == null)
return 0
if (this == null)
return -1
if (other == null)
return 1
return this.compareTo(other)
}
operator fun StringBuilder.plusAssign(str: String?) {
this.append(str)
}
fun Context.timeTill(time: Int, delimiter: String = " "): String {
val parts = mutableListOf<Pair<Int, Int>>()
val hours = time / 3600
val minutes = (time - hours*3600) / 60
val seconds = time - minutes*60 - hours*3600
var prefixAdded = false
if (hours > 0) {
if (!prefixAdded) parts += R.plurals.time_till_text to hours; prefixAdded = true
parts += R.plurals.time_till_hours to hours
}
if (minutes > 0) {
if (!prefixAdded) parts += R.plurals.time_till_text to minutes; prefixAdded = true
parts += R.plurals.time_till_minutes to minutes
}
if (hours == 0 && minutes < 10) {
if (!prefixAdded) parts += R.plurals.time_till_text to seconds; prefixAdded = true
parts += R.plurals.time_till_seconds to seconds
}
return parts.joinToString(delimiter) { resources.getQuantityString(it.first, it.second, it.second) }
}
fun Context.timeLeft(time: Int, delimiter: String = " "): String {
val parts = mutableListOf<Pair<Int, Int>>()
val hours = time / 3600
val minutes = (time - hours*3600) / 60
val seconds = time - minutes*60 - hours*3600
var prefixAdded = false
if (hours > 0) {
if (!prefixAdded) parts += R.plurals.time_left_text to hours
prefixAdded = true
parts += R.plurals.time_left_hours to hours
}
if (minutes > 0) {
if (!prefixAdded) parts += R.plurals.time_left_text to minutes
prefixAdded = true
parts += R.plurals.time_left_minutes to minutes
}
if (hours == 0 && minutes < 10) {
if (!prefixAdded) parts += R.plurals.time_left_text to seconds
prefixAdded = true
parts += R.plurals.time_left_seconds to seconds
}
return parts.joinToString(delimiter) { resources.getQuantityString(it.first, it.second, it.second) }
}
inline fun <reified T> Any?.instanceOfOrNull(): T? {
return when (this) {
is T -> this
else -> null
}
}
fun Drawable.setTintColor(color: Int): Drawable {
colorFilter = PorterDuffColorFilter(
color,
PorterDuff.Mode.SRC_ATOP
)
return this
}

View File

@ -11,6 +11,7 @@ import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable
import android.os.*
import android.provider.Settings
import android.util.Log
import android.view.Gravity
import android.view.View
import android.widget.Toast
@ -36,8 +37,9 @@ import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import pl.droidsonroids.gif.GifDrawable
import pl.szczodrzynski.edziennik.App.APP_URL
import pl.szczodrzynski.edziennik.api.v2.events.*
import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.events.*
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.databinding.ActivitySzkolnyBinding
import pl.szczodrzynski.edziennik.network.ServerRequest
@ -56,7 +58,7 @@ import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackFragment
import pl.szczodrzynski.edziennik.ui.modules.feedback.HelpFragment
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesFragment
import pl.szczodrzynski.edziennik.ui.modules.grades.editor.GradesEditorFragment
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragmentV2
import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment
import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity
import pl.szczodrzynski.edziennik.ui.modules.messages.MessageFragment
@ -71,6 +73,7 @@ import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.Utils.dpToPx
import pl.szczodrzynski.edziennik.utils.appManagerIntentList
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.NavTarget
import pl.szczodrzynski.navlib.*
import pl.szczodrzynski.navlib.SystemBarsUtil.Companion.COLOR_HALF_TRANSPARENT
@ -122,9 +125,9 @@ class MainActivity : AppCompatActivity() {
val list: MutableList<NavTarget> = mutableListOf()
// home item
list += NavTarget(DRAWER_ITEM_HOME, R.string.menu_home_page, HomeFragment::class)
list += NavTarget(DRAWER_ITEM_HOME, R.string.menu_home_page, HomeFragmentV2::class)
.withTitle(R.string.app_name)
.withIcon(CommunityMaterial.Icon2.cmd_home)
.withIcon(CommunityMaterial.Icon2.cmd_home_outline)
.isInDrawer(true)
.isStatic(true)
.withPopToHome(false)
@ -135,50 +138,50 @@ class MainActivity : AppCompatActivity() {
.isInDrawer(true)
list += NavTarget(DRAWER_ITEM_AGENDA, R.string.menu_agenda, AgendaFragment::class)
.withIcon(CommunityMaterial.Icon.cmd_calendar)
.withIcon(CommunityMaterial.Icon.cmd_calendar_outline)
.withBadgeTypeId(TYPE_EVENT)
.isInDrawer(true)
list += NavTarget(DRAWER_ITEM_GRADES, R.string.menu_grades, GradesFragment::class)
.withIcon(CommunityMaterial.Icon2.cmd_numeric_5_box)
.withIcon(CommunityMaterial.Icon2.cmd_numeric_5_box_outline)
.withBadgeTypeId(TYPE_GRADE)
.isInDrawer(true)
list += NavTarget(DRAWER_ITEM_MESSAGES, R.string.menu_messages, MessagesFragment::class)
.withIcon(CommunityMaterial.Icon.cmd_email)
.withIcon(CommunityMaterial.Icon.cmd_email_outline)
.withBadgeTypeId(TYPE_MESSAGE)
.isInDrawer(true)
list += NavTarget(DRAWER_ITEM_HOMEWORK, R.string.menu_homework, HomeworkFragment::class)
.withIcon(SzkolnyFont.Icon.szf_file_document_edit)
.withIcon(SzkolnyFont.Icon.szf_notebook_outline)
.withBadgeTypeId(TYPE_HOMEWORK)
.isInDrawer(true)
list += NavTarget(DRAWER_ITEM_BEHAVIOUR, R.string.menu_notices, BehaviourFragment::class)
.withIcon(CommunityMaterial.Icon2.cmd_message_alert)
.withIcon(CommunityMaterial.Icon.cmd_emoticon_outline)
.withBadgeTypeId(TYPE_NOTICE)
.isInDrawer(true)
list += NavTarget(DRAWER_ITEM_ATTENDANCE, R.string.menu_attendance, AttendanceFragment::class)
.withIcon(CommunityMaterial.Icon.cmd_calendar_remove)
.withIcon(CommunityMaterial.Icon.cmd_calendar_remove_outline)
.withBadgeTypeId(TYPE_ATTENDANCE)
.isInDrawer(true)
list += NavTarget(DRAWER_ITEM_ANNOUNCEMENTS, R.string.menu_announcements, AnnouncementsFragment::class)
.withIcon(CommunityMaterial.Icon.cmd_bulletin_board)
.withIcon(CommunityMaterial.Icon.cmd_bullhorn_outline)
.withBadgeTypeId(TYPE_ANNOUNCEMENT)
.isInDrawer(true)
// static drawer items
list += NavTarget(DRAWER_ITEM_NOTIFICATIONS, R.string.menu_notifications, NotificationsFragment::class)
.withIcon(CommunityMaterial.Icon.cmd_bell_ring)
.withIcon(CommunityMaterial.Icon.cmd_bell_ring_outline)
.isInDrawer(true)
.isStatic(true)
.isBelowSeparator(true)
list += NavTarget(DRAWER_ITEM_SETTINGS, R.string.menu_settings, SettingsNewFragment::class)
.withIcon(CommunityMaterial.Icon2.cmd_settings)
.withIcon(CommunityMaterial.Icon2.cmd_settings_outline)
.isInDrawer(true)
.isStatic(true)
.isBelowSeparator(true)
@ -197,7 +200,7 @@ class MainActivity : AppCompatActivity() {
.isInProfileList(false)
list += NavTarget(DRAWER_PROFILE_SYNC_ALL, R.string.menu_sync_all, null)
.withIcon(CommunityMaterial.Icon2.cmd_sync)
.withIcon(CommunityMaterial.Icon.cmd_download_outline)
.isInProfileList(true)
@ -243,12 +246,14 @@ class MainActivity : AppCompatActivity() {
setTheme(Themes.appTheme)
app.appConfig.language?.let {
app.config.ui.language?.let {
setLanguage(it)
}
setContentView(b.root)
Log.d(TAG, Signing.appPassword)
errorSnackbar.setCoordinator(b.navView.coordinator, b.navView.bottomBar)
navLoading = true
@ -306,10 +311,10 @@ class MainActivity : AppCompatActivity() {
}
drawer.apply {
setAccountHeaderBackground(app.appConfig.headerBackground)
setAccountHeaderBackground(app.config.ui.headerBackground)
drawerProfileListEmptyListener = {
app.appConfig.loginFinished = false
app.config.loginFinished = false
app.saveConfig("loginFinished")
profileListEmptyListener()
}
@ -334,7 +339,7 @@ class MainActivity : AppCompatActivity() {
drawerProfileSettingClickListener = this@MainActivity.profileSettingClickListener
miniDrawerVisibleLandscape = null
miniDrawerVisiblePortrait = app.appConfig.miniDrawerVisible
miniDrawerVisiblePortrait = app.config.ui.miniMenuVisible
}
}
@ -387,29 +392,37 @@ class MainActivity : AppCompatActivity() {
SyncWorker.scheduleNext(app)
// APP BACKGROUND
if (app.appConfig.appBackground != null) {
if (app.config.ui.appBackground != null) {
try {
var bg = app.appConfig.appBackground
val bgDir = File(Environment.getExternalStoragePublicDirectory("Szkolny.eu"), "bg")
if (bgDir.exists()) {
val files = bgDir.listFiles()
val r = Random()
val i = r.nextInt(files.size)
bg = files[i].toString()
}
val linearLayout = b.root
if (bg.endsWith(".gif")) {
linearLayout.background = GifDrawable(bg)
} else {
linearLayout.background = BitmapDrawable.createFromPath(bg)
app.config.ui.appBackground?.let {
var bg = it
val bgDir = File(Environment.getExternalStoragePublicDirectory("Szkolny.eu"), "bg")
if (bgDir.exists()) {
val files = bgDir.listFiles()
val r = Random()
val i = r.nextInt(files.size)
bg = files[i].toString()
}
val linearLayout = b.root
if (bg.endsWith(".gif")) {
linearLayout.background = GifDrawable(bg)
} else {
linearLayout.background = BitmapDrawable.createFromPath(bg)
}
}
} catch (e: IOException) {
e.printStackTrace()
}
}
// IT'S WINTER MY DUDES
val today = Date.getToday()
if ((today.month == 12 || today.month == 1) && app.config.ui.snowfall) {
b.rootFrame.addView(layoutInflater.inflate(R.layout.snowfall, b.rootFrame, false))
}
// WHAT'S NEW DIALOG
if (app.appConfig.lastAppVersion != BuildConfig.VERSION_CODE) {
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 {
@ -420,21 +433,20 @@ class MainActivity : AppCompatActivity() {
}
}
}
if (app.appConfig.lastAppVersion < 170) {
if (app.config.appVersion < 170) {
//Intent intent = new Intent(this, ChangelogIntroActivity.class);
//startActivity(intent);
} else {
app.appConfig.lastAppVersion = BuildConfig.VERSION_CODE
app.saveConfig("lastAppVersion")
app.config.appVersion = BuildConfig.VERSION_CODE
}
}
// RATE SNACKBAR
if (app.appConfig.appRateSnackbarTime != 0L && app.appConfig.appRateSnackbarTime <= System.currentTimeMillis()) {
if (app.config.appRateSnackbarTime != 0L && app.config.appRateSnackbarTime <= System.currentTimeMillis()) {
navView.coordinator.postDelayed({
CafeBar.builder(this)
.content(R.string.rate_snackbar_text)
.icon(IconicsDrawable(this).icon(CommunityMaterial.Icon2.cmd_star).size(IconicsSize.dp(20)).color(IconicsColor.colorInt(Themes.getPrimaryTextColor(this))))
.icon(IconicsDrawable(this).icon(CommunityMaterial.Icon2.cmd_star_outline).size(IconicsSize.dp(20)).color(IconicsColor.colorInt(Themes.getPrimaryTextColor(this))))
.positiveText(R.string.rate_snackbar_positive)
.positiveColor(-0xb350b0)
.negativeText(R.string.rate_snackbar_negative)
@ -444,20 +456,17 @@ class MainActivity : AppCompatActivity() {
.onPositive { cafeBar ->
Utils.openGooglePlay(this)
cafeBar.dismiss()
app.appConfig.appRateSnackbarTime = 0
app.saveConfig("appRateSnackbarTime")
app.config.appRateSnackbarTime = 0
}
.onNegative { cafeBar ->
Toast.makeText(this, "Szkoda, opinie innych pomagają mi rozwijać aplikację.", Toast.LENGTH_LONG).show()
cafeBar.dismiss()
app.appConfig.appRateSnackbarTime = 0
app.saveConfig("appRateSnackbarTime")
app.config.appRateSnackbarTime = 0
}
.onNeutral { cafeBar ->
Toast.makeText(this, "OK", Toast.LENGTH_LONG).show()
cafeBar.dismiss()
app.appConfig.appRateSnackbarTime = System.currentTimeMillis() + 7 * 24 * 60 * 60 * 1000
app.saveConfig("appRateSnackbarTime")
app.config.appRateSnackbarTime = System.currentTimeMillis() + 7 * 24 * 60 * 60 * 1000
}
.autoDismiss(false)
.swipeToDismiss(true)
@ -471,7 +480,7 @@ class MainActivity : AppCompatActivity() {
bottomSheet.appendItems(
BottomSheetPrimaryItem(false)
.withTitle(R.string.menu_sync)
.withIcon(CommunityMaterial.Icon2.cmd_sync)
.withIcon(CommunityMaterial.Icon.cmd_download_outline)
.withOnClickListener(View.OnClickListener {
bottomSheet.close()
SyncViewListDialog(this, navTargetId)
@ -479,17 +488,17 @@ class MainActivity : AppCompatActivity() {
BottomSheetSeparatorItem(false),
BottomSheetPrimaryItem(false)
.withTitle(R.string.menu_settings)
.withIcon(CommunityMaterial.Icon2.cmd_settings)
.withIcon(CommunityMaterial.Icon2.cmd_settings_outline)
.withOnClickListener(View.OnClickListener { loadTarget(DRAWER_ITEM_SETTINGS) }),
BottomSheetPrimaryItem(false)
.withTitle(R.string.menu_feedback)
.withIcon(CommunityMaterial.Icon2.cmd_help_circle)
.withIcon(CommunityMaterial.Icon2.cmd_help_circle_outline)
.withOnClickListener(View.OnClickListener { loadTarget(TARGET_FEEDBACK) })
)
if (App.devMode) {
bottomSheet += BottomSheetPrimaryItem(false)
.withTitle(R.string.menu_debug)
.withIcon(CommunityMaterial.Icon.cmd_android_debug_bridge)
.withIcon(CommunityMaterial.Icon.cmd_android_studio)
.withOnClickListener(View.OnClickListener { loadTarget(DRAWER_ITEM_DEBUG) })
}
@ -537,9 +546,14 @@ class MainActivity : AppCompatActivity() {
DRAWER_ITEM_MESSAGES -> MessagesFragment.pageSelection
else -> 0
}
val arguments = when (navTargetId) {
DRAWER_ITEM_TIMETABLE -> JsonObject("weekStart" to TimetableFragment.pageSelection?.weekStart?.stringY_m_d)
else -> null
}
EdziennikTask.syncProfile(
App.profileId,
listOf(navTargetId to fragmentParam)
listOf(navTargetId to fragmentParam),
arguments
).enqueue(this)
}
@Subscribe(threadMode = ThreadMode.MAIN)
@ -684,30 +698,33 @@ class MainActivity : AppCompatActivity() {
}*/
if (navLoading) {
navLoading = false
b.fragment.removeAllViews()
if (intentTargetId == -1)
intentTargetId = HOME_ID
}
when {
app.profile == null -> {
app.profile == null || app.profile.id == -1 -> {
if (intentProfileId == -1)
intentProfileId = app.appSharedPrefs.getInt("current_profile_id", 1)
loadProfile(intentProfileId, intentTargetId)
loadProfile(intentProfileId, intentTargetId, extras)
}
intentProfileId != -1 -> {
loadProfile(intentProfileId, intentTargetId)
if (app.profile.id != intentProfileId)
loadProfile(intentProfileId, intentTargetId, extras)
else
loadTarget(intentTargetId, extras)
}
intentTargetId != -1 -> {
drawer.currentProfile = app.profile.id
if (navTargetId != intentTargetId)
if (navTargetId != intentTargetId || navLoading)
loadTarget(intentTargetId, extras)
}
else -> {
drawer.currentProfile = app.profile.id
}
}
navLoading = false
}
override fun recreate() {
@ -755,7 +772,7 @@ class MainActivity : AppCompatActivity() {
finish()
}
else {
if (!app.appConfig.loginFinished)
if (!app.config.loginFinished)
finish()
else {
handleIntent(data?.extras)
@ -792,13 +809,16 @@ class MainActivity : AppCompatActivity() {
this.runOnUiThread {
if (app.profile == null) {
LoginActivity.firstCompleted = false
if (app.appConfig.loginFinished) {
if (app.config.loginFinished) {
// this shouldn't run
profileListEmptyListener()
}
} else {
setDrawerItems()
drawer.currentProfile = app.profile.id
// the drawer profile is updated automatically when the drawer item is clicked
// update it manually when switching profiles from other source
//if (drawer.currentProfile != app.profile.id)
drawer.currentProfile = app.profile.id
loadTarget(drawerSelection, arguments)
}
}
@ -965,7 +985,7 @@ class MainActivity : AppCompatActivity() {
val item = DrawerPrimaryItem()
.withIdentifier(target.id.toLong())
.withName(target.name)
.withHiddenInMiniDrawer(!app.appConfig.miniDrawerButtonIds.contains(target.id))
.withHiddenInMiniDrawer(!app.config.ui.miniMenuButtons.contains(target.id))
.also { if (target.description != null) it.withDescription(target.description!!) }
.also { if (target.icon != null) it.withIcon(target.icon!!) }
.also { if (target.title != null) it.withAppTitle(getString(target.title!!)) }
@ -1054,30 +1074,11 @@ class MainActivity : AppCompatActivity() {
private var targetHomeId: Int = -1
override fun onBackPressed() {
if (!b.navView.onBackPressed()) {
navigateUp()
/*val currentDestinationId = navController.currentDestination?.id
if (if (targetHomeId != -1 && targetPopToHomeList.contains(navController.currentDestination?.id)) {
if (!navController.popBackStack(targetHomeId, false)) {
navController.navigateUp()
}
true
} else {
navController.navigateUp()
}) {
val currentId = navController.currentDestination?.id ?: -1
val drawerSelection = navTargetList
.singleOrNull {
it.navGraphId == currentId
}?.also {
navView.toolbar.setTitle(it.title ?: it.name)
}?.id ?: -1
drawer.setSelection(drawerSelection, false)
if (App.getConfig().ui.openDrawerOnBackPressed) {
b.navView.drawer.toggle()
} else {
super.onBackPressed()
}*/
navigateUp()
}
}
}
}

View File

@ -93,8 +93,8 @@ public class Notifier {
public boolean shouldBeQuiet() {
long now = Time.getNow().getInMillis();
long start = app.appConfig.quietHoursStart;
long end = app.appConfig.quietHoursEnd;
long start = app.config.getSync().getQuietHoursStart();
long end = app.config.getSync().getQuietHoursEnd();
if (start > end) {
end += 1000 * 60 * 60 * 24;
//Log.d(TAG, "Night passing");
@ -104,7 +104,7 @@ public class Notifier {
//Log.d(TAG, "Now is smaller");
}
//Log.d(TAG, "Start is "+start+", now is "+now+", end is "+end);
return app.appConfig.quietHoursStart > 0 && now >= start && now <= end;
return start > 0 && now >= start && now <= end;
}
public int getNotificationDefaults() {
@ -311,13 +311,14 @@ public class Notifier {
\____/| .__/ \__,_|\__,_|\__\___||___/
| |
|*/
public void notificationUpdatesShow(String updateVersion, String updateUrl, String updateFilename) {
if (!app.appConfig.notifyAboutUpdates)
public void notificationUpdatesShow(String updateVersion, String updateUrl, String updateFilename, boolean updateDirect) {
if (!app.config.getSync().getNotifyAboutUpdates())
return;
Intent notificationIntent = new Intent(app.getContext(), BootReceiver.NotificationActionService.class)
.putExtra("update_version", updateVersion)
.putExtra("update_url", updateUrl)
.putExtra("update_filename", updateFilename);
.putExtra("update_filename", updateFilename)
.putExtra("update_direct", updateDirect);
PendingIntent pendingIntent = PendingIntent.getService(app.getContext(), 0,
notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
@ -339,7 +340,7 @@ public class Notifier {
}
public void notificationUpdatesHide() {
if (!app.appConfig.notifyAboutUpdates)
if (!app.config.getSync().getNotifyAboutUpdates())
return;
notificationManager.cancel(ID_UPDATES);
}

View File

@ -21,7 +21,7 @@ import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
import com.mikepenz.iconics.utils.colorInt
import com.mikepenz.iconics.utils.sizeDp
import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask
import pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_HOMEWORK
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
import pl.szczodrzynski.edziennik.utils.models.Date
@ -52,9 +52,9 @@ class WidgetTimetable : AppWidgetProvider() {
val app = context.applicationContext as App
var bellSyncDiffMillis: Long = 0
if (app.appConfig.bellSyncDiff != null) {
bellSyncDiffMillis = (app.appConfig.bellSyncDiff.hour * 60 * 60 * 1000 + app.appConfig.bellSyncDiff.minute * 60 * 1000 + app.appConfig.bellSyncDiff.second * 1000).toLong()
bellSyncDiffMillis *= app.appConfig.bellSyncMultiplier.toLong()
app.config.timetable.bellSyncDiff?.let {
bellSyncDiffMillis = (it.hour * 60 * 60 * 1000 + it.minute * 60 * 1000 + it.second * 1000).toLong()
bellSyncDiffMillis *= app.config.timetable.bellSyncMultiplier.toLong()
bellSyncDiffMillis *= -1
}
@ -87,7 +87,7 @@ class WidgetTimetable : AppWidgetProvider() {
.colorInt(Color.WHITE)
.sizeDp(if (widgetConfig.bigStyle) 24 else 16).toBitmap())
views.setImageViewBitmap(R.id.widgetTimetableSync, IconicsDrawable(context, CommunityMaterial.Icon2.cmd_sync)
views.setImageViewBitmap(R.id.widgetTimetableSync, IconicsDrawable(context, CommunityMaterial.Icon.cmd_download_outline)
.colorInt(Color.WHITE)
.sizeDp(if (widgetConfig.bigStyle) 24 else 16).toBitmap())
@ -214,8 +214,8 @@ class WidgetTimetable : AppWidgetProvider() {
model.lessonId = lesson.id
model.lessonDate = timetableDate
model.startTime = lesson.startTime
model.endTime = lesson.endTime
model.startTime = lesson.displayStartTime
model.endTime = lesson.displayEndTime
// check if the lesson has already passed or it's currently in progress
if (lesson.displayDate == today) {
@ -255,7 +255,7 @@ class WidgetTimetable : AppWidgetProvider() {
// add every event on this lesson
for (event in events) {
if (event.startTime != null && event.startTime != lesson.displayStartTime)
if (event.startTime == null || event.startTime != lesson.displayStartTime)
continue
model.eventColors.add(if (event.type == TYPE_HOMEWORK) ItemWidgetTimetableModel.EVENT_COLOR_HOMEWORK else event.getColor())
}

View File

@ -1,179 +0,0 @@
package pl.szczodrzynski.edziennik.api.v2
import pl.szczodrzynski.edziennik.App.APP_URL
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.models.Data
import pl.szczodrzynski.edziennik.data.api.AppError.CODE_APP_SERVER_ERROR
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_HOMEWORK
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification
import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_SHARED_EVENT
import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_NEW_SHARED_HOMEWORK
import pl.szczodrzynski.edziennik.data.db.modules.notification.Notification.Companion.TYPE_SERVER_MESSAGE
import pl.szczodrzynski.edziennik.data.db.modules.notification.getNotificationTitle
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.getJsonArray
import pl.szczodrzynski.edziennik.getLong
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.network.ServerRequest
class ServerSync(val data: Data, val onSuccess: () -> Unit) {
companion object {
private const val TAG = "ServerSync"
}
val app = data.app
val profileId = data.profile?.id ?: -1
val profileName = data.profile?.name ?: ""
val profile = data.profile
val loginStore = data.loginStore
private fun getUsernameId(): String {
if (loginStore.data == null) {
return "NO_LOGIN_STORE"
}
if (profile?.studentData == null) {
return "NO_STUDENT_STORE"
}
return when (data.loginStore.type) {
LoginStore.LOGIN_TYPE_MOBIDZIENNIK -> loginStore.getLoginData("serverName", "MOBI_UN") + ":" + loginStore.getLoginData("username", "MOBI_UN") + ":" + profile.getStudentData("studentId", -1)
LoginStore.LOGIN_TYPE_LIBRUS -> profile.getStudentData("schoolName", "LIBRUS_UN") + ":" + profile.getStudentData("accountLogin", "LIBRUS_LOGIN_UN")
LoginStore.LOGIN_TYPE_IUCZNIOWIE -> loginStore.getLoginData("schoolName", "IUCZNIOWIE_UN") + ":" + loginStore.getLoginData("username", "IUCZNIOWIE_UN") + ":" + profile.getStudentData("registerId", -1)
LoginStore.LOGIN_TYPE_VULCAN -> profile.getStudentData("schoolName", "VULCAN_UN") + ":" + profile.getStudentData("studentId", -1)
LoginStore.LOGIN_TYPE_DEMO -> loginStore.getLoginData("serverName", "DEMO_UN") + ":" + loginStore.getLoginData("username", "DEMO_UN") + ":" + profile.getStudentData("studentId", -1)
else -> "TYPE_UNKNOWN"
}
}
init { run {
if (profile?.registration != Profile.REGISTRATION_ENABLED) {
onSuccess()
return@run
}
val request = ServerRequest(
app,
app.requestScheme+APP_URL+"main.php?sync",
"Edziennik2/REG",
profile,
data.loginStore.type,
getUsernameId()
)
if (profile.empty) {
request.setBodyParameter("first_run", "true")
}
var hasNotifications = true
if (app.appConfig.webPushEnabled) {
data.notifications
.filterNot { it.posted }
.let {
if (it.isEmpty()) {
hasNotifications = false
null
}
else
it
}?.forEachIndexed { index, notification ->
if (notification.type != TYPE_NEW_SHARED_EVENT
&& notification.type != TYPE_SERVER_MESSAGE
&& notification.type != TYPE_NEW_SHARED_HOMEWORK) {
request.setBodyParameter("notify[$index][type]", notification.type.toString())
request.setBodyParameter("notify[$index][title]", notification.title)
request.setBodyParameter("notify[$index][text]", notification.text)
}
}
}
if ((!app.appConfig.webPushEnabled || !hasNotifications) && !profile.enableSharedEvents) {
onSuccess()
return@run
}
val result = request.runSync()
if (result == null) {
data.error(ApiError(TAG, CODE_APP_SERVER_ERROR)
.setCritical(false))
onSuccess()
return@run
}
var apiResponse = result.toString()
if (result.getString("success") != "true") {
data.error(ApiError(TAG, CODE_APP_SERVER_ERROR)
.setCritical(false))
onSuccess()
return@run
}
// HERE PROCESS ALL THE RECEIVED EVENTS
// add them to the profile and create appropriate notifications
result.getJsonArray("events")?.forEach { jEventEl ->
val event = jEventEl.asJsonObject
val teamCode = event.getString("team")
// get the target Team from teamCode
val team = app.db.teamDao().getByCodeNow(profile.id, teamCode)
if (team != null) {
// create the event from Json. Add the missing teamId and !!profileId!!
val eventObject = app.gson.fromJson(event.toString(), Event::class.java)
// proguard. disable for Event.class
if (eventObject.eventDate == null) {
apiResponse += "\n\nEventDate == null\n$event"
}
eventObject.profileId = profileId
eventObject.teamId = team.id
eventObject.addedManually = true
if (eventObject.sharedBy == getUsernameId()) {
eventObject.sharedBy = "self"
eventObject.sharedByName = profile.studentNameLong
}
val typeObject = app.db.eventTypeDao().getByIdNow(profileId, eventObject.type)
app.db.eventDao().add(eventObject)
val metadata = Metadata(
profileId,
if (eventObject.type == TYPE_HOMEWORK) Metadata.TYPE_HOMEWORK else Metadata.TYPE_EVENT,
eventObject.id,
profile.empty,
true,
event.getLong("addedDate") ?: 0
)
val metadataId = app.db.metadataDao().add(metadata)
// notify if the event is new and not first sync
if (metadataId != -1L && !profile.empty) {
val text = app.getString(
R.string.notification_shared_event_format,
eventObject.sharedByName,
if (typeObject != null) typeObject.name else "wydarzenie",
if (eventObject.eventDate == null) "???" else eventObject.eventDate.formattedString,
eventObject.topic
)
val type = if (eventObject.type == TYPE_HOMEWORK) TYPE_NEW_SHARED_HOMEWORK else TYPE_NEW_SHARED_EVENT
data.notifications += Notification(
title = app.getNotificationTitle(type),
text = text,
type = type,
profileId = profileId,
profileName = profileName,
viewId = if (eventObject.type == TYPE_HOMEWORK) DRAWER_ITEM_HOMEWORK else DRAWER_ITEM_AGENDA,
addedDate = metadata.addedDate
).addExtra("eventId", eventObject.id).addExtra("eventDate", eventObject.eventDate.value.toLong())
}
}
}
onSuccess()
}}
}

View File

@ -1,7 +0,0 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-28.
*/
package pl.szczodrzynski.edziennik.api.v2.events
class ApiTaskAllFinishedEvent

View File

@ -1,9 +0,0 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-28.
*/
package pl.szczodrzynski.edziennik.api.v2.events
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
class ApiTaskErrorEvent(val error: ApiError)

View File

@ -1,7 +0,0 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-28.
*/
package pl.szczodrzynski.edziennik.api.v2.events
class ApiTaskFinishedEvent(val profileId: Int)

View File

@ -1,7 +0,0 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-1.
*/
package pl.szczodrzynski.edziennik.api.v2.events.requests
class ServiceCloseRequest

View File

@ -1,7 +0,0 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-1.
*/
package pl.szczodrzynski.edziennik.api.v2.events.requests
class TaskCancelRequest(val taskId: Int)

View File

@ -1,128 +0,0 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-10-27.
*/
package pl.szczodrzynski.edziennik.api.v2.idziennik.data.web
import androidx.core.util.set
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_WEB_TIMETABLE
import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_TIMETABLE
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.lessons.Lesson
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange.TYPE_CANCELLED
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange.TYPE_CHANGE
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonRange
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
import pl.szczodrzynski.edziennik.utils.models.Week
class IdziennikWebTimetable(override val data: DataIdziennik,
val onSuccess: () -> Unit) : IdziennikWeb(data) {
companion object {
private const val TAG = "IdziennikWebTimetable"
}
init {
val weekStart = Week.getWeekStart()
if (Date.getToday().weekDay > 4) {
weekStart.stepForward(0, 0, 7)
}
webApiGet(TAG, IDZIENNIK_WEB_TIMETABLE, mapOf(
"idPozDziennika" to data.registerId,
"pidRokSzkolny" to data.schoolYearId,
"data" to weekStart.stringY_m_d+"T10:00:00.000Z"
)) { result ->
val json = result.getJsonObject("d") ?: run {
data.error(ApiError(TAG, ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA)
.withApiResponse(result))
return@webApiGet
}
json.getJsonArray("GodzinyLekcyjne")?.asJsonObjectList()?.forEach { 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
)
data.lessonRanges[lessonRange.lessonNumber] = lessonRange
}
json.getJsonArray("Przedmioty")?.asJsonObjectList()?.forEach { lesson ->
val subject = data.getSubject(
lesson.getString("Nazwa") ?: return@forEach,
lesson.getLong("Id"),
lesson.getString("Skrot") ?: ""
)
val teacher = data.getTeacherByFDotLast(lesson.getString("Nauczyciel") ?: return@forEach)
val weekDay = lesson.getInt("DzienTygodnia")?.minus(1) ?: return@forEach
val lessonRange = data.lessonRanges[lesson.getInt("Godzina")?.plus(1) ?: return@forEach]
val lessonObject = Lesson(
profileId,
weekDay,
lessonRange.startTime,
lessonRange.endTime
).apply {
subjectId = subject.id
teacherId = teacher.id
teamId = data.teamClass?.id ?: -1
classroomName = lesson.getString("NazwaSali") ?: ""
}
data.lessonList.add(lessonObject)
val type = lesson.getInt("TypZastepstwa") ?: -1
if (type != -1) {
// we have a lesson change to process
val lessonChangeObject = LessonChange(
profileId,
weekStart.clone().stepForward(0, 0, weekDay),
lessonObject.startTime,
lessonObject.endTime
)
lessonChangeObject.teamId = lessonObject.teamId
lessonChangeObject.teacherId = lessonObject.teacherId
lessonChangeObject.subjectId = lessonObject.subjectId
lessonChangeObject.classroomName = lessonObject.classroomName
when (type) {
0 -> lessonChangeObject.type = TYPE_CANCELLED
1, 2, 3, 4, 5 -> {
lessonChangeObject.type = TYPE_CHANGE
val newTeacher = lesson.getString("NauZastepujacy")
val newSubject = lesson.getString("PrzedmiotZastepujacy")
if (newTeacher != null) {
lessonChangeObject.teacherId = data.getTeacherByFDotLast(newTeacher).id
}
if (newSubject != null) {
lessonChangeObject.subjectId = data.getSubject(newSubject, null, "").id
}
}
}
data.lessonChangeList.add(lessonChangeObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_LESSON_CHANGE,
lessonChangeObject.id,
profile?.empty ?: false,
profile?.empty ?: false,
System.currentTimeMillis()
))
}
}
data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_TIMETABLE, SYNC_ALWAYS)
onSuccess()
}
}
}

View File

@ -1,135 +0,0 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-10-24
*/
package pl.szczodrzynski.edziennik.api.v2.librus.data
import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import im.wangchao.mhttp.body.MediaTypeUtils
import im.wangchao.mhttp.callback.TextCallbackHandler
import okhttp3.Cookie
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.parser.Parser
import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.utils.Utils.d
import java.io.StringWriter
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.OutputKeys
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult
open class LibrusMessages(open val data: DataLibrus) {
companion object {
private const val TAG = "LibrusMessages"
}
val profileId
get() = data.profile?.id ?: -1
val profile
get() = data.profile
fun messagesGet(tag: String, endpoint: String, method: Int = POST,
parameters: Map<String, Any>? = null, onSuccess: (doc: Document) -> Unit) {
d(tag, "Request: Librus/Messages - $LIBRUS_MESSAGES_URL/$endpoint")
val callback = object : TextCallbackHandler() {
override fun onSuccess(text: String?, response: Response?) {
if (text.isNullOrEmpty()) {
data.error(ApiError(LibrusSynergia.TAG, ERROR_RESPONSE_EMPTY)
.withResponse(response))
return
}
// TODO: Finish error handling
if ("error" in text) {
when ("<type>(.*)</type>".toRegex().find(text)?.get(1)) {
"eAccessDeny" -> data.error(ApiError(tag, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED)
.withResponse(response)
.withApiResponse(text))
}
}
try {
val doc = Jsoup.parse(text, "", Parser.xmlParser())
onSuccess(doc)
} catch (e: Exception) {
data.error(ApiError(tag, EXCEPTION_LIBRUS_MESSAGES_REQUEST)
.withResponse(response)
.withThrowable(e)
.withApiResponse(text))
}
}
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()
/*val requestXml = xml("service") {
"header" { }
"data" {
for ((key, value) in parameters.orEmpty()) {
key {
-value.toString()
}
}
}
}.toString(PrintOptions(
singleLineTextElements = true,
useSelfClosingTags = true
))*/
Request.builder()
.url("$LIBRUS_MESSAGES_URL/$endpoint")
.userAgent(SYNERGIA_USER_AGENT)
.setTextBody(requestXml, MediaTypeUtils.APPLICATION_XML)
.apply {
when (method) {
GET -> get()
POST -> post()
}
}
.callback(callback)
.build()
.enqueue()
}
}

View File

@ -1,106 +0,0 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-9-20.
*/
package pl.szczodrzynski.edziennik.api.v2.librus.login
import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import im.wangchao.mhttp.callback.TextCallbackHandler
import okhttp3.Cookie
import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.getUnixDate
import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.Utils.d
class LibrusLoginMessages(val data: DataLibrus, val onSuccess: () -> Unit) {
companion object {
private const val TAG = "LoginLibrusMessages"
}
init { run {
if (data.profile == null) {
data.error(ApiError(TAG, ERROR_PROFILE_MISSING))
return@run
}
if (data.isMessagesLoginValid()) {
data.app.cookieJar.saveFromResponse(null, listOf(
Cookie.Builder()
.name("DZIENNIKSID")
.value(data.messagesSessionId!!)
.domain("wiadomosci.librus.pl")
.secure().httpOnly().build()
))
onSuccess()
}
else {
data.app.cookieJar.clearForDomain("wiadomosci.librus.pl")
if (data.loginMethods.contains(LOGIN_METHOD_LIBRUS_SYNERGIA)) {
loginWithSynergia()
}
else if (data.apiLogin != null && data.apiPassword != null && false) {
loginWithCredentials()
}
else {
data.error(ApiError(TAG, ERROR_LOGIN_DATA_MISSING))
}
}
}}
/**
* XML (Flash messages website) login method. Uses a Synergia login and password.
*/
private fun loginWithCredentials() {
}
/**
* A login method using the Synergia website (/wiadomosci2 Auto Login).
*/
private fun loginWithSynergia(url: String = "https://synergia.librus.pl/wiadomosci2") {
d(TAG, "Request: Librus/Login/Messages - $url")
val callback = object : TextCallbackHandler() {
override fun onSuccess(text: String?, response: Response?) {
val location = response?.headers()?.get("Location")
when {
location?.contains("MultiDomainLogon") == true -> loginWithSynergia(location)
location?.contains("AutoLogon") == true -> {
var sessionId = data.app.cookieJar.getCookie("wiadomosci.librus.pl", "DZIENNIKSID")
sessionId = sessionId?.replace("-MAINT", "")
if (sessionId == null) {
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_NO_SESSION_ID)
.withResponse(response)
.withApiResponse(text))
return
}
data.messagesSessionId = sessionId
data.messagesSessionIdExpiryTime = response.getUnixDate() + 45 * 60 /* 45min */
onSuccess()
}
text?.contains("eAccessDeny") == true -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text)
text?.contains("stop.png") == true -> data.error(TAG, ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED, response, text)
}
}
override fun onFailure(response: Response?, throwable: Throwable?) {
data.error(ApiError(TAG, ERROR_REQUEST_FAILURE)
.withResponse(response)
.withThrowable(throwable))
}
}
Request.builder()
.url(url)
.userAgent(SYNERGIA_USER_AGENT)
.get()
.callback(callback)
.withClient(data.app.httpLazy)
.build()
.enqueue()
}
}

View File

@ -1,186 +0,0 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-11-13
*/
package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.Regexes
import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_TIMETABLE
import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel
import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan
import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_TIMETABLE
import pl.szczodrzynski.edziennik.api.v2.vulcan.data.VulcanApi
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.subjects.Subject
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
import pl.szczodrzynski.edziennik.utils.Utils.crc16
import pl.szczodrzynski.edziennik.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.models.Date
class VulcanApiTimetable(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) {
companion object {
const val TAG = "VulcanApiTimetable"
}
init {
data.profile?.also { profile ->
val currentWeekStart = Date.getToday().let { it.stepForward(0, 0, -it.weekDay) }
val getDate = data.arguments?.getString("weekStart") ?: currentWeekStart.stringY_m_d
val weekStart = Date.fromY_m_d(getDate)
val weekEnd = weekStart.clone().stepForward(0, 0, 6)
apiGet(TAG, VULCAN_API_ENDPOINT_TIMETABLE, parameters = mapOf(
"DataPoczatkowa" to weekStart.stringY_m_d,
"DataKoncowa" to weekEnd.stringY_m_d,
"IdUczen" to data.studentId,
"IdOddzial" to data.studentClassId,
"IdOkresKlasyfikacyjny" to data.studentSemesterId
)) { json, _ ->
val dates: MutableSet<Int> = mutableSetOf()
val lessons: MutableList<Lesson> = mutableListOf()
json.getJsonArray("Data")?.asJsonObjectList()?.forEach { lesson ->
val lessonDate = Date.fromY_m_d(lesson.getString("DzienTekst"))
val lessonNumber = lesson.getInt("NumerLekcji")
val lessonRange = data.lessonRanges.singleOrNull { it.lessonNumber == lessonNumber }
val startTime = lessonRange?.startTime
val endTime = lessonRange?.endTime
val teacherId = lesson.getLong("IdPracownik")
val teamId = data.studentClassId.toLong()
val classroom = lesson.getString("Classroom")
val oldTeacherId = lesson.getLong("IdPracownikOld")
val changeAnnotation = lesson.getString("AdnotacjaOZmianie") ?: ""
val type = when {
changeAnnotation.startsWith("(przeniesiona z") -> Lesson.TYPE_SHIFTED_TARGET
changeAnnotation.startsWith("(przeniesiona na") -> Lesson.TYPE_SHIFTED_SOURCE
changeAnnotation.startsWith("(zastępstwo") -> Lesson.TYPE_CHANGE
lesson.getBoolean("PrzekreslonaNazwa") == true -> Lesson.TYPE_CANCELLED
else -> Lesson.TYPE_NORMAL
}
val subjectId = lesson.getLong("IdPrzedmiot")?.let {
when (it) {
0L -> {
val subjectName = lesson.getString("PrzedmiotNazwa") ?: ""
data.subjectList.singleOrNull { subject -> subject.longName == subjectName }?.id
?: {
/**
* CREATE A NEW SUBJECT IF IT DOESN'T EXIST
*/
val subjectObject = Subject(
profileId,
-1 * crc16(subjectName.toByteArray()).toLong(),
subjectName,
subjectName
)
data.subjectList.put(subjectObject.id, subjectObject)
subjectObject.id
}.invoke()
}
else -> it
}
}
val id = lessonDate.combineWith(startTime) / 6L * 10L + (lesson.hashCode() and 0xFFFF)
val lessonObject = Lesson(profileId, id).apply {
this.type = type
when (type) {
Lesson.TYPE_NORMAL, Lesson.TYPE_CHANGE, Lesson.TYPE_SHIFTED_TARGET -> {
this.date = lessonDate
this.lessonNumber = lessonNumber
this.startTime = startTime
this.endTime = endTime
this.subjectId = subjectId
this.teacherId = teacherId
this.teamId = teamId
this.classroom = classroom
this.oldTeacherId = oldTeacherId
}
Lesson.TYPE_CANCELLED, Lesson.TYPE_SHIFTED_SOURCE -> {
this.oldDate = lessonDate
this.oldLessonNumber = lessonNumber
this.oldStartTime = startTime
this.oldEndTime = endTime
this.oldSubjectId = subjectId
this.oldTeacherId = teacherId
this.oldTeamId = teamId
this.oldClassroom = classroom
}
}
if (type == Lesson.TYPE_SHIFTED_SOURCE || type == Lesson.TYPE_SHIFTED_TARGET) {
val shift = Regexes.VULCAN_SHITFT_ANNOTATION.find(changeAnnotation)
val oldLessonNumber = shift?.get(2)?.toInt()
val oldLessonDate = shift?.get(3)?.let { Date.fromd_m_Y(it) }
val oldLessonRange = data.lessonRanges.singleOrNull { it.lessonNumber == oldLessonNumber }
val oldStartTime = oldLessonRange?.startTime
val oldEndTime = oldLessonRange?.endTime
when (type) {
Lesson.TYPE_SHIFTED_SOURCE -> {
this.lessonNumber = oldLessonNumber
this.date = oldLessonDate
this.startTime = oldStartTime
this.endTime = oldEndTime
}
Lesson.TYPE_SHIFTED_TARGET -> {
this.oldLessonNumber = oldLessonNumber
this.oldDate = oldLessonDate
this.oldStartTime = oldStartTime
this.oldEndTime = oldEndTime
}
}
}
}
if (type != Lesson.TYPE_NORMAL) {
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_LESSON_CHANGE,
id,
profile.empty,
profile.empty,
System.currentTimeMillis()
))
}
dates.add(lessonDate.value)
lessons.add(lessonObject)
}
val date: Date = weekStart.clone()
while (date <= weekEnd) {
if (!dates.contains(date.value)) {
lessons.add(Lesson(profileId, date.value.toLong()).apply {
this.type = Lesson.TYPE_NO_LESSONS
this.date = date.clone()
})
}
date.stepForward(0, 0, 1)
}
d(TAG, "Clearing lessons between ${weekStart.stringY_m_d} and ${weekEnd.stringY_m_d} - timetable downloaded for $getDate")
data.lessonNewList.addAll(lessons)
data.toRemove.add(DataRemoveModel.Timetable.between(weekStart, weekEnd))
data.setSyncNext(ENDPOINT_VULCAN_API_TIMETABLE, SYNC_ALWAYS)
onSuccess()
}
}
}
}

View File

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

View File

@ -0,0 +1,89 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-26.
*/
package pl.szczodrzynski.edziennik.config
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.BuildConfig
import pl.szczodrzynski.edziennik.config.db.ConfigEntry
import pl.szczodrzynski.edziennik.config.utils.ConfigMigration
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.config.utils.toHashMap
import pl.szczodrzynski.edziennik.data.db.AppDb
import kotlin.coroutines.CoroutineContext
class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
companion object {
const val DATA_VERSION = 2
}
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Default
val values: HashMap<String, String?> = hashMapOf()
val ui by lazy { ConfigUI(this) }
val sync by lazy { ConfigSync(this) }
val timetable by lazy { ConfigTimetable(this) }
val grades by lazy { ConfigGrades(this) }
private var mDataVersion: Int? = null
var dataVersion: Int
get() { mDataVersion = mDataVersion ?: values.get("dataVersion", 0); return mDataVersion ?: 0 }
set(value) { set("dataVersion", value); mDataVersion = value }
private var mAppVersion: Int? = null
var appVersion: Int
get() { mAppVersion = mAppVersion ?: values.get("appVersion", BuildConfig.VERSION_CODE); return mAppVersion ?: BuildConfig.VERSION_CODE }
set(value) { set("appVersion", value); mAppVersion = value }
private var mLoginFinished: Boolean? = null
var loginFinished: Boolean
get() { mLoginFinished = mLoginFinished ?: values.get("loginFinished", false); return mLoginFinished ?: false }
set(value) { set("loginFinished", value); mLoginFinished = value }
private var mDevModePassword: String? = null
var devModePassword: String?
get() { mDevModePassword = mDevModePassword ?: values.get("devModePassword", null as String?); return mDevModePassword }
set(value) { set("devModePassword", value); mDevModePassword = value }
private var mAppInstalledTime: Long? = null
var appInstalledTime: Long
get() { mAppInstalledTime = mAppInstalledTime ?: values.get("appInstalledTime", 0L); return mAppInstalledTime ?: 0L }
set(value) { set("appInstalledTime", value); mAppInstalledTime = value }
private var mAppRateSnackbarTime: Long? = null
var appRateSnackbarTime: Long
get() { mAppRateSnackbarTime = mAppRateSnackbarTime ?: values.get("appRateSnackbarTime", 0L); return mAppRateSnackbarTime ?: 0L }
set(value) { set("appRateSnackbarTime", value); mAppRateSnackbarTime = value }
private var rawEntries: List<ConfigEntry> = db.configDao().getAllNow()
private val profileConfigs: HashMap<Int, ProfileConfig> = hashMapOf()
init {
rawEntries.toHashMap(-1, values)
}
fun migrate(app: App) {
if (dataVersion < DATA_VERSION)
ConfigMigration(app, this)
}
fun getFor(profileId: Int): ProfileConfig {
return profileConfigs[profileId] ?: ProfileConfig(db, profileId, rawEntries)
}
fun setProfile(profileId: Int) {
}
override fun set(key: String, value: String?) {
values[key] = value
launch {
db.configDao().add(ConfigEntry(-1, key, value))
}
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-26.
*/
package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set
class ConfigGrades(private val config: Config) {
companion object {
const val ORDER_BY_DATE_DESC = 0
const val ORDER_BY_SUBJECT_ASC = 1
const val ORDER_BY_DATE_ASC = 2
const val ORDER_BY_SUBJECT_DESC = 3
}
private var mOrderBy: Int? = null
var orderBy: Int
get() { mOrderBy = mOrderBy ?: config.values.get("gradesOrderBy", 0); return mOrderBy ?: 0 }
set(value) { config.set("gradesOrderBy", value); mOrderBy = value }
}

View File

@ -0,0 +1,88 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-26.
*/
package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.getIntList
import pl.szczodrzynski.edziennik.config.utils.set
class ConfigSync(private val config: Config) {
private var mSyncEnabled: Boolean? = null
var enabled: Boolean
get() { mSyncEnabled = mSyncEnabled ?: config.values.get("syncEnabled", true); return mSyncEnabled ?: true }
set(value) { config.set("syncEnabled", value); mSyncEnabled = value }
private var mSyncOnlyWifi: Boolean? = null
var onlyWifi: Boolean
get() { mSyncOnlyWifi = mSyncOnlyWifi ?: config.values.get("syncOnlyWifi", false); return mSyncOnlyWifi ?: notifyAboutUpdates }
set(value) { config.set("syncOnlyWifi", value); mSyncOnlyWifi = value }
private var mSyncInterval: Int? = null
var interval: Int
get() { mSyncInterval = mSyncInterval ?: config.values.get("syncInterval", 60*60); return mSyncInterval ?: 60*60 }
set(value) { config.set("syncInterval", value); mSyncInterval = value }
private var mNotifyAboutUpdates: Boolean? = null
var notifyAboutUpdates: Boolean
get() { mNotifyAboutUpdates = mNotifyAboutUpdates ?: config.values.get("notifyAboutUpdates", true); return mNotifyAboutUpdates ?: true }
set(value) { config.set("notifyAboutUpdates", value); mNotifyAboutUpdates = value }
/* ____ _ _ _
/ __ \ (_) | | | |
| | | |_ _ _ ___| |_ | |__ ___ _ _ _ __ ___
| | | | | | | |/ _ \ __| | '_ \ / _ \| | | | '__/ __|
| |__| | |_| | | __/ |_ | | | | (_) | |_| | | \__ \
\___\_\\__,_|_|\___|\__| |_| |_|\___/ \__,_|_| |__*/
private var mQuietHoursStart: Long? = null
var quietHoursStart: Long
get() { mQuietHoursStart = mQuietHoursStart ?: config.values.get("quietHoursStart", 0L); return mQuietHoursStart ?: 0L }
set(value) { config.set("quietHoursStart", value); mQuietHoursStart = value }
private var mQuietHoursEnd: Long? = null
var quietHoursEnd: Long
get() { mQuietHoursEnd = mQuietHoursEnd ?: config.values.get("quietHoursEnd", 0L); return mQuietHoursEnd ?: 0L }
set(value) { config.set("quietHoursEnd", value); mQuietHoursEnd = value }
private var mQuietDuringLessons: Boolean? = null
var quietDuringLessons: Boolean
get() { mQuietDuringLessons = mQuietDuringLessons ?: config.values.get("quietDuringLessons", false); return mQuietDuringLessons ?: false }
set(value) { config.set("quietDuringLessons", value); mQuietDuringLessons = value }
/* ______ _____ __ __ _______ _
| ____/ ____| \/ | |__ __| | |
| |__ | | | \ / | | | ___ | | _____ _ __ ___
| __|| | | |\/| | | |/ _ \| |/ / _ \ '_ \/ __|
| | | |____| | | | | | (_) | < __/ | | \__ \
|_| \_____|_| |_| |_|\___/|_|\_\___|_| |_|__*/
private var mTokenApp: String? = null
var tokenApp: String?
get() { mTokenApp = mTokenApp ?: config.values.get("tokenApp", null as String?); return mTokenApp }
set(value) { config.set("tokenApp", value); mTokenApp = value }
private var mTokenMobidziennik: String? = null
var tokenMobidziennik: String?
get() { mTokenMobidziennik = mTokenMobidziennik ?: config.values.get("tokenMobidziennik", null as String?); return mTokenMobidziennik }
set(value) { config.set("tokenMobidziennik", value); mTokenMobidziennik = value }
private var mTokenLibrus: String? = null
var tokenLibrus: String?
get() { mTokenLibrus = mTokenLibrus ?: config.values.get("tokenLibrus", null as String?); return mTokenLibrus }
set(value) { config.set("tokenLibrus", value); mTokenLibrus = value }
private var mTokenVulcan: String? = null
var tokenVulcan: String?
get() { mTokenVulcan = mTokenVulcan ?: config.values.get("tokenVulcan", null as String?); return mTokenVulcan }
set(value) { config.set("tokenVulcan", value); mTokenVulcan = value }
private var mTokenMobidziennikList: List<Int>? = null
var tokenMobidziennikList: List<Int>
get() { mTokenMobidziennikList = mTokenMobidziennikList ?: config.values.getIntList("tokenMobidziennikList", listOf()); return mTokenMobidziennikList ?: listOf() }
set(value) { config.set("tokenMobidziennikList", value); mTokenMobidziennikList = value }
private var mTokenLibrusList: List<Int>? = null
var tokenLibrusList: List<Int>
get() { mTokenLibrusList = mTokenLibrusList ?: config.values.getIntList("tokenLibrusList", listOf()); return mTokenLibrusList ?: listOf() }
set(value) { config.set("tokenLibrusList", value); mTokenLibrusList = value }
private var mTokenVulcanList: List<Int>? = null
var tokenVulcanList: List<Int>
get() { mTokenVulcanList = mTokenVulcanList ?: config.values.getIntList("tokenVulcanList", listOf()); return mTokenVulcanList ?: listOf() }
set(value) { config.set("tokenVulcanList", value); mTokenVulcanList = value }
}

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-26.
*/
package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.utils.models.Time
class ConfigTimetable(private val config: Config) {
private var mBellSyncMultiplier: Int? = null
var bellSyncMultiplier: Int
get() { mBellSyncMultiplier = mBellSyncMultiplier ?: config.values.get("bellSyncMultiplier", 0); return mBellSyncMultiplier ?: 0 }
set(value) { config.set("bellSyncMultiplier", value); mBellSyncMultiplier = value }
private var mBellSyncDiff: Time? = null
var bellSyncDiff: Time?
get() { mBellSyncDiff = mBellSyncDiff ?: config.values.get("bellSyncDiff", null as Time?); return mBellSyncDiff }
set(value) { config.set("bellSyncDiff", value); mBellSyncDiff = value }
private var mCountInSeconds: Boolean? = null
var countInSeconds: Boolean
get() { mCountInSeconds = mCountInSeconds ?: config.values.get("countInSeconds", false); return mCountInSeconds ?: false }
set(value) { config.set("countInSeconds", value); mCountInSeconds = value }
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-26.
*/
package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.getIntList
import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardModel
class ConfigUI(private val config: Config) {
private var mTheme: Int? = null
var theme: Int
get() { mTheme = mTheme ?: config.values.get("theme", 1); return mTheme ?: 1 }
set(value) { config.set("theme", value); mTheme = value }
private var mLanguage: String? = null
var language: String?
get() { mLanguage = mLanguage ?: config.values.get("language", null as String?); return mLanguage }
set(value) { config.set("language", value); mLanguage = value }
private var mHeaderBackground: String? = null
var headerBackground: String?
get() { mHeaderBackground = mHeaderBackground ?: config.values.get("headerBg", null as String?); return mHeaderBackground }
set(value) { config.set("headerBg", value); mHeaderBackground = value }
private var mAppBackground: String? = null
var appBackground: String?
get() { mAppBackground = mAppBackground ?: config.values.get("appBg", null as String?); return mAppBackground }
set(value) { config.set("appBg", value); mAppBackground = value }
private var mMiniMenuVisible: Boolean? = null
var miniMenuVisible: Boolean
get() { mMiniMenuVisible = mMiniMenuVisible ?: config.values.get("miniMenuVisible", false); return mMiniMenuVisible ?: false }
set(value) { config.set("miniMenuVisible", value); mMiniMenuVisible = value }
private var mMiniMenuButtons: List<Int>? = null
var miniMenuButtons: List<Int>
get() { mMiniMenuButtons = mMiniMenuButtons ?: config.values.getIntList("miniMenuButtons", listOf()); return mMiniMenuButtons ?: listOf() }
set(value) { config.set("miniMenuButtons", value); mMiniMenuButtons = value }
private var mOpenDrawerOnBackPressed: Boolean? = null
var openDrawerOnBackPressed: Boolean
get() { mOpenDrawerOnBackPressed = mOpenDrawerOnBackPressed ?: config.values.get("openDrawerOnBackPressed", false); return mOpenDrawerOnBackPressed ?: false }
set(value) { config.set("openDrawerOnBackPressed", value); mOpenDrawerOnBackPressed = value }
private var mAgendaViewType: Int? = null
var agendaViewType: Int
get() { mAgendaViewType = mAgendaViewType ?: config.values.get("agendaViewType", 0); return mAgendaViewType ?: 0 }
set(value) { config.set("agendaViewType", value); mAgendaViewType = value }
private var mHomeCards: List<HomeCardModel>? = null
var homeCards: List<HomeCardModel>
get() { mHomeCards = mHomeCards ?: config.values.get("homeCards", listOf(), HomeCardModel::class.java); return mHomeCards ?: listOf() }
set(value) { config.set("homeCards", value); mHomeCards = value }
private var mSnowfall: Boolean? = null
var snowfall: Boolean
get() { mSnowfall = mSnowfall ?: config.values.get("snowfall", false); return mSnowfall ?: false }
set(value) { config.set("snowfall", value); mSnowfall = value }
}

View File

@ -0,0 +1,52 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-27.
*/
package pl.szczodrzynski.edziennik.config
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.config.db.ConfigEntry
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.config.utils.toHashMap
import pl.szczodrzynski.edziennik.data.db.AppDb
import kotlin.coroutines.CoroutineContext
class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEntry>) : CoroutineScope, AbstractConfig {
companion object {
const val DATA_VERSION = 1
}
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Default
val values: HashMap<String, String?> = hashMapOf()
val grades by lazy { ProfileConfigGrades(this) }
/*
val sync by lazy { ConfigSync(this) }
val timetable by lazy { ConfigTimetable(this) }
val grades by lazy { ConfigGrades(this) }*/
private var mDataVersion: Int? = null
var dataVersion: Int
get() { mDataVersion = mDataVersion ?: values.get("dataVersion", 0); return mDataVersion ?: 0 }
set(value) { set("dataVersion", value); mDataVersion = value }
init {
rawEntries.toHashMap(profileId, values)
/*if (dataVersion < DATA_VERSION)
ProfileConfigMigration(this)*/
}
override fun set(key: String, value: String?) {
values[key] = value
launch {
db.configDao().add(ConfigEntry(profileId, key, value))
}
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-27.
*/
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
class ProfileConfigGrades(private val config: ProfileConfig) {
private var mColorMode: Int? = null
var colorMode: Int
get() { mColorMode = mColorMode ?: config.values.get("gradesColorMode", COLOR_MODE_WEIGHTED); return mColorMode ?: COLOR_MODE_WEIGHTED }
set(value) { config.set("gradesColorMode", value); mColorMode = value }
private var mYearAverageMode: Int? = null
var yearAverageMode: Int
get() { mYearAverageMode = mYearAverageMode ?: config.values.get("yearAverageMode", YEAR_ALL_GRADES); return mYearAverageMode ?: YEAR_ALL_GRADES }
set(value) { config.set("yearAverageMode", value); mYearAverageMode = value }
private var mCountZeroToAvg: Boolean? = null
var countZeroToAvg: Boolean
get() { mCountZeroToAvg = mCountZeroToAvg ?: config.values.get("countZeroToAvg", true); return mCountZeroToAvg ?: true }
set(value) { config.set("countZeroToAvg", value); mCountZeroToAvg = value }
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-27.
*/
package pl.szczodrzynski.edziennik.config.db
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
@Dao
interface ConfigDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun add(entry: ConfigEntry)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun addAll(list: List<ConfigEntry>)
@Query("SELECT * FROM config WHERE profileId = -1")
fun getAllNow(): List<ConfigEntry>
@Query("SELECT * FROM config WHERE profileId = :profileId")
fun getAllNow(profileId: Int): List<ConfigEntry>
}

View File

@ -0,0 +1,14 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-26.
*/
package pl.szczodrzynski.edziennik.config.db
import androidx.room.Entity
@Entity(tableName = "config", primaryKeys = ["profileId", "key"])
data class ConfigEntry(
val profileId: Int = -1,
val key: String,
val value: String?
)

View File

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

View File

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

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-27.
*/
package pl.szczodrzynski.edziennik.config.utils
import android.content.Context
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.BuildConfig
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_LIBRUS
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_MOBIDZIENNIK
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_VULCAN
import pl.szczodrzynski.edziennik.config.Config
import pl.szczodrzynski.edziennik.utils.models.Time
class ConfigMigration(app: App, config: Config) {
init { config.apply {
val p = app.getSharedPreferences("pl.szczodrzynski.edziennik_profiles", Context.MODE_PRIVATE)
val s = "app.appConfig"
if (dataVersion < 1) {
ui.theme = p.getString("$s.appTheme", null)?.toIntOrNull() ?: 1
sync.enabled = p.getString("$s.registerSyncEnabled", null)?.toBoolean() ?: true
sync.interval = p.getString("$s.registerSyncEnabled", null)?.toIntOrNull() ?: 3600
val oldButtons = p.getString("$s.miniDrawerButtonIds", null)?.let { str ->
str.replace("[\\[\\]]*".toRegex(), "")
.split(",\\s?".toRegex())
.mapNotNull { it.toIntOrNull() }
}
ui.miniMenuButtons = oldButtons ?: listOf(
MainActivity.DRAWER_ITEM_HOME,
MainActivity.DRAWER_ITEM_TIMETABLE,
MainActivity.DRAWER_ITEM_AGENDA,
MainActivity.DRAWER_ITEM_GRADES,
MainActivity.DRAWER_ITEM_MESSAGES,
MainActivity.DRAWER_ITEM_HOMEWORK,
MainActivity.DRAWER_ITEM_SETTINGS
)
dataVersion = 1
}
if (dataVersion < 2) {
devModePassword = p.getString("$s.devModePassword", null).fix()
sync.tokenApp = p.getString("$s.fcmToken", null).fix()
timetable.bellSyncMultiplier = p.getString("$s.bellSyncMultiplier", null)?.toIntOrNull() ?: 0
sync.quietHoursStart = p.getString("$s.quietHoursStart", null)?.toLongOrNull() ?: 0
appRateSnackbarTime = p.getString("$s.appRateSnackbarTime", null)?.toLongOrNull() ?: 0
sync.quietHoursEnd = p.getString("$s.quietHoursEnd", null)?.toLongOrNull() ?: 0
timetable.countInSeconds = p.getString("$s.countInSeconds", null)?.toBoolean() ?: false
ui.headerBackground = p.getString("$s.headerBackground", null).fix()
ui.appBackground = p.getString("$s.appBackground", null).fix()
ui.language = p.getString("$s.language", null).fix()
appVersion = p.getString("$s.lastAppVersion", null)?.toIntOrNull() ?: BuildConfig.VERSION_CODE
appInstalledTime = p.getString("$s.appInstalledTime", null)?.toLongOrNull() ?: 0
grades.orderBy = p.getString("$s.gradesOrderBy", null)?.toIntOrNull() ?: 0
sync.quietDuringLessons = p.getString("$s.quietDuringLessons", null)?.toBoolean() ?: false
ui.miniMenuVisible = p.getString("$s.miniDrawerVisible", null)?.toBoolean() ?: false
loginFinished = p.getString("$s.loginFinished", null)?.toBoolean() ?: false
sync.onlyWifi = p.getString("$s.registerSyncOnlyWifi", null)?.toBoolean() ?: false
sync.notifyAboutUpdates = p.getString("$s.notifyAboutUpdates", null)?.toBoolean() ?: true
timetable.bellSyncDiff = p.getString("$s.bellSyncDiff", null)?.let { Gson().fromJson(it, Time::class.java) }
sync.tokenMobidziennikList = listOf()
sync.tokenVulcanList = listOf()
sync.tokenLibrusList = listOf()
val tokens = p.getString("$s.fcmTokens", null)?.let { Gson().fromJson<Map<Int, Pair<String, List<Int>>>>(it, object: TypeToken<Map<Int, Pair<String, List<Int>>>>(){}.type) }
tokens?.forEach {
val token = it.value.first
when (it.key) {
LOGIN_TYPE_MOBIDZIENNIK -> sync.tokenMobidziennik = token
LOGIN_TYPE_VULCAN -> sync.tokenVulcan = token
LOGIN_TYPE_LIBRUS -> sync.tokenLibrus = token
}
}
dataVersion = 2
}
}}
private fun String?.fix(): String? {
return this?.replace("\"", "")?.let { if (it == "null") null else it }
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-12-1.
*/
package pl.szczodrzynski.edziennik.config.utils
import android.content.Context
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.config.Config
class ProfileConfigMigration(app: App, config: Config) {
init { config.apply {
val p = app.getSharedPreferences("pl.szczodrzynski.edziennik_profiles", Context.MODE_PRIVATE)
val s = "app.appConfig"
if (dataVersion < 1) {
//dataVersion = 1
}
if (dataVersion < 2) {
//gradesColorMode do profilu !
//agendaViewType do profilu !
// app.appConfig.dontCountZeroToAverage do profilu !
}
}}
}

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2019-9-28.
*/
package pl.szczodrzynski.edziennik.api.v2
package pl.szczodrzynski.edziennik.data.api
import android.app.Service
import android.content.Context
@ -12,15 +12,13 @@ import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.api.v2.events.*
import pl.szczodrzynski.edziennik.api.v2.events.requests.ServiceCloseRequest
import pl.szczodrzynski.edziennik.api.v2.events.requests.TaskCancelRequest
import pl.szczodrzynski.edziennik.api.v2.events.task.EdziennikTask
import pl.szczodrzynski.edziennik.api.v2.events.task.ErrorReportTask
import pl.szczodrzynski.edziennik.api.v2.events.task.IApiTask
import pl.szczodrzynski.edziennik.api.v2.events.task.NotifyTask
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.data.api.events.*
import pl.szczodrzynski.edziennik.data.api.events.requests.ServiceCloseRequest
import pl.szczodrzynski.edziennik.data.api.events.requests.TaskCancelRequest
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.task.*
import pl.szczodrzynski.edziennik.data.db.modules.profiles.ProfileFull
import pl.szczodrzynski.edziennik.utils.Utils.d
import kotlin.math.min
import kotlin.math.roundToInt
@ -40,9 +38,11 @@ class ApiService : Service() {
private val app by lazy { applicationContext as App }
private val syncingProfiles = mutableListOf<ProfileFull>()
private val finishingTaskQueue = mutableListOf(
NotifyTask(),
ErrorReportTask()
SzkolnyTask.sync(syncingProfiles),
NotifyTask()
)
private val taskQueue = mutableListOf<IApiTask>()
private val errorList = mutableListOf<ApiError>()
@ -60,6 +60,9 @@ class ApiService : Service() {
private val notification by lazy { EdziennikNotification(this) }
private var lastEventTime = System.currentTimeMillis()
private var taskCancelTries = 0
/* ______ _ _ _ _ _____ _ _ _ _
| ____| | | (_) (_) | / ____| | | | | | |
| |__ __| |_____ ___ _ __ _ __ _| | __ | | __ _| | | |__ __ _ ___| | __
@ -68,22 +71,17 @@ class ApiService : Service() {
|______\__,_/___|_|\___|_| |_|_| |_|_|_|\_\ \_____\__,_|_|_|_.__/ \__,_|\___|_|\*/
private val taskCallback = object : EdziennikCallback {
override fun onCompleted() {
lastEventTime = System.currentTimeMillis()
d(TAG, "Task $taskRunningId (profile $taskProfileId) - $taskProgressText - finished")
//if (!taskCancelled) {
EventBus.getDefault().post(ApiTaskFinishedEvent(taskProfileId))
//}
taskIsRunning = false
taskRunningId = -1
taskRunning = null
taskProfileId = -1
taskProgress = -1f
taskProgressText = null
EventBus.getDefault().post(ApiTaskFinishedEvent(taskProfileId))
clearTask()
notification.setIdle().post()
runTask()
}
override fun onError(apiError: ApiError) {
lastEventTime = System.currentTimeMillis()
d(TAG, "Task $taskRunningId threw an error - $apiError")
apiError.profileId = taskProfileId
EventBus.getDefault().post(ApiTaskErrorEvent(apiError))
@ -92,9 +90,7 @@ class ApiService : Service() {
if (apiError.isCritical) {
taskRunning?.cancel()
notification.setCriticalError().post()
taskRunning = null
taskIsRunning = false
taskRunningId = -1
clearTask()
runTask()
}
else {
@ -103,6 +99,7 @@ class ApiService : Service() {
}
override fun onProgress(step: Float) {
lastEventTime = System.currentTimeMillis()
if (step <= 0)
return
if (taskProgress < 0)
@ -115,6 +112,7 @@ class ApiService : Service() {
}
override fun onStartProgress(stringRes: Int) {
lastEventTime = System.currentTimeMillis()
taskProgressText = getString(stringRes)
d(TAG, "Task $taskRunningId progress: $taskProgressText")
EventBus.getDefault().post(ApiTaskProgressEvent(taskProfileId, taskProgress, taskProgressText))
@ -129,6 +127,7 @@ class ApiService : Service() {
| | (_| \__ \ < | __/> < __/ (__| |_| | |_| | (_) | | | |
|_|\__,_|___/_|\_\ \___/_/\_\___|\___|\__,_|\__|_|\___/|_| |*/
private fun runTask() {
checkIfTaskFrozen()
if (taskIsRunning)
return
if (taskCancelled || serviceClosed || (taskQueue.isEmpty() && finishingTaskQueue.isEmpty())) {
@ -137,6 +136,8 @@ class ApiService : Service() {
return
}
lastEventTime = System.currentTimeMillis()
val task = if (taskQueue.isEmpty()) finishingTaskQueue.removeAt(0) else taskQueue.removeAt(0)
task.taskId = ++taskMaximumId
task.prepare(app)
@ -155,17 +156,62 @@ class ApiService : Service() {
// post an event
EventBus.getDefault().post(ApiTaskStartedEvent(taskProfileId, task.profile))
task.profile?.let { syncingProfiles.add(it) }
try {
when (task) {
is EdziennikTask -> task.run(app, taskCallback)
is NotifyTask -> task.run(app, taskCallback)
is ErrorReportTask -> task.run(app, taskCallback, notification, errorList)
is SzkolnyTask -> task.run(app, taskCallback)
}
} catch (e: Exception) {
taskCallback.onError(ApiError(TAG, EXCEPTION_API_TASK).withThrowable(e))
}
}
/**
* Check if a task is inactive for more than 30 seconds.
* If the user tries to cancel a task with no success at least three times,
* consider it frozen as well.
*
* This usually means it is broken and won't become active again.
* This method cancels the task and removes any pointers to it.
*/
private fun checkIfTaskFrozen(): Boolean {
if (System.currentTimeMillis() - lastEventTime > 30*1000
|| taskCancelTries >= 3) {
val time = System.currentTimeMillis() - lastEventTime
d(TAG, "!!! Task $taskRunningId froze for $time ms. $taskRunning")
clearTask()
return true
}
return false
}
/**
* Stops the service if the current task is frozen/broken.
*/
private fun stopIfTaskFrozen() {
if (checkIfTaskFrozen()) {
stopSelf()
}
}
/**
* Remove any task descriptors or pointers from the service.
*/
private fun clearTask() {
taskIsRunning = false
taskRunningId = -1
taskRunning = null
taskProfileId = -1
taskProgress = -1f
taskProgressText = null
taskCancelled = false
taskCancelTries = 0
}
private fun allCompleted() {
EventBus.getDefault().post(ApiTaskAllFinishedEvent())
stopSelf()
@ -211,8 +257,10 @@ class ApiService : Service() {
EventBus.getDefault().removeStickyEvent(request)
d(TAG, request.toString())
taskCancelTries++
taskCancelled = true
taskRunning?.cancel()
stopIfTaskFrozen()
}
@Subscribe(sticky = true, threadMode = ThreadMode.ASYNC)
fun onServiceCloseRequest(request: ServiceCloseRequest) {

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2019-9-19.
*/
package pl.szczodrzynski.edziennik.api.v2
package pl.szczodrzynski.edziennik.data.api
import android.os.Build
import pl.szczodrzynski.edziennik.BuildConfig
@ -64,6 +64,7 @@ const val IDZIENNIK_WEB_TIMETABLE = "mod_panelRodzica/plan/WS_Plan.asmx/pobierzP
const val IDZIENNIK_WEB_GRADES = "mod_panelRodzica/oceny/WS_ocenyUcznia.asmx/pobierzOcenyUcznia"
const val IDZIENNIK_WEB_MISSING_GRADES = "mod_panelRodzica/brak_ocen/WS_BrakOcenUcznia.asmx/pobierzBrakujaceOcenyUcznia"
const val IDZIENNIK_WEB_EXAMS = "mod_panelRodzica/sprawdziany/mod_sprawdzianyPanel.asmx/pobierzListe"
const val IDZIENNIK_WEB_HOMEWORK = "mod_panelRodzica/pracaDomowa/WS_pracaDomowa.asmx/pobierzPraceDomowe"
const val IDZIENNIK_WEB_NOTICES = "mod_panelRodzica/uwagi/WS_uwagiUcznia.asmx/pobierzUwagiUcznia"
const val IDZIENNIK_WEB_ATTENDANCE = "mod_panelRodzica/obecnosci/WS_obecnosciUcznia.asmx/pobierzObecnosciUcznia"
const val IDZIENNIK_WEB_ANNOUNCEMENTS = "mod_panelRodzica/tabOgl/WS_tablicaOgloszen.asmx/GetOgloszenia"

View File

@ -1,4 +1,4 @@
package pl.szczodrzynski.edziennik.api.v2
package pl.szczodrzynski.edziennik.data.api
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ANNOUNCEMENTS
@ -10,7 +10,7 @@ import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.api.v2.models.Data
import pl.szczodrzynski.edziennik.data.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.*
@ -45,8 +45,8 @@ class DataNotifications(val data: Data) {
return@run
}
for (change in app.db.lessonChangeDao().getNotNotifiedNow(profileId)) {
val text = app.getString(R.string.notification_lesson_change_format, change.changeTypeStr(app), if (change.lessonDate == null) "" else change.lessonDate!!.formattedString, change.subjectLongName)
for (lesson in app.db.timetableDao().getNotNotifiedNow(profileId)) {
val text = app.getString(R.string.notification_lesson_change_format, lesson.getDisplayChangeType(app), if (lesson.displayDate == null) "" else lesson.displayDate!!.formattedString, lesson.changeSubjectName)
data.notifications += Notification(
title = app.getNotificationTitle(TYPE_TIMETABLE_LESSON_CHANGE),
text = text,
@ -54,8 +54,8 @@ class DataNotifications(val data: Data) {
profileId = profileId,
profileName = profileName,
viewId = DRAWER_ITEM_TIMETABLE,
addedDate = change.addedDate
).addExtra("timetableDate", change.lessonDate?.value?.toLong())
addedDate = lesson.addedDate
).addExtra("timetableDate", lesson.displayDate?.stringY_m_d ?: "")
}
for (event in app.db.eventDao().getNotNotifiedNow(profileId)) {
@ -186,10 +186,10 @@ class DataNotifications(val data: Data) {
val luckyNumbers = app.db.luckyNumberDao().getNotNotifiedNow(profileId)
luckyNumbers?.removeAll { it.date < today }
luckyNumbers?.forEach { luckyNumber ->
val text = when {
luckyNumber.date.value == todayValue -> // LN for today
val text = when (luckyNumber.date.value) {
todayValue -> // LN for today
app.getString(if (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) R.string.notification_lucky_number_yours_format else R.string.notification_lucky_number_format, luckyNumber.number)
luckyNumber.date.value == todayValue + 1 -> // LN for tomorrow
todayValue + 1 -> // LN for tomorrow
app.getString(if (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) R.string.notification_lucky_number_yours_tomorrow_format else R.string.notification_lucky_number_tomorrow_format, luckyNumber.number)
else -> // LN for later
app.getString(if (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) R.string.notification_lucky_number_yours_later_format else R.string.notification_lucky_number_later_format, luckyNumber.date.formattedString, luckyNumber.number)
@ -207,4 +207,4 @@ class DataNotifications(val data: Data) {
data.db.metadataDao().setAllNotified(profileId, true)
}}
}
}

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-1.
*/
package pl.szczodrzynski.edziennik.api.v2
package pl.szczodrzynski.edziennik.data.api
import android.app.Notification
import android.app.NotificationManager
@ -137,4 +137,4 @@ class EdziennikNotification(val context: Context) {
notificationManager.notify(NOTIFICATION_ID, notification)
}
}
}

View File

@ -1,8 +1,8 @@
package pl.szczodrzynski.edziennik.api.v2
package pl.szczodrzynski.edziennik.data.api
import pl.szczodrzynski.edziennik.api.v2.models.Data
import pl.szczodrzynski.edziennik.api.v2.models.Feature
import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod
import pl.szczodrzynski.edziennik.data.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
@ -80,4 +80,4 @@ fun Data.prepare(loginMethods: List<LoginMethod>, features: List<Feature>, featu
progressCount = targetLoginMethodIds.size + targetEndpointIds.size
progressStep = if (progressCount <= 0) 0f else 100f / progressCount.toFloat()
}
}

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2019-9-21.
*/
package pl.szczodrzynski.edziennik.api.v2
package pl.szczodrzynski.edziennik.data.api
/*const val CODE_OTHER = 0
const val CODE_OK = 1
@ -39,13 +39,16 @@ 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_FAILURE_HOSTNAME_NOT_FOUND = 60
const val ERROR_REQUEST_FAILURE_TIMEOUT = 61
const val ERROR_REQUEST_FAILURE_NO_INTERNET = 62
const val ERROR_RESPONSE_EMPTY = 100
const val ERROR_LOGIN_DATA_MISSING = 101
const val ERROR_LOGIN_DATA_INVALID = 102
const val ERROR_PROFILE_MISSING = 105
const val ERROR_INVALID_LOGIN_MODE = 110
const val ERROR_LOGIN_METHOD_NOT_SATISFIED = 111
const val ERROR_NOT_IMPLEMENTED = 112
const val ERROR_FILE_DOWNLOAD = 113
const val ERROR_NO_STUDENTS_IN_ACCOUNT = 115
@ -99,6 +102,13 @@ const val ERROR_LOGIN_LIBRUS_PORTAL_REFRESH_INVALID = 172
const val ERROR_LOGIN_LIBRUS_PORTAL_REFRESH_REVOKED = 173
const val ERROR_LIBRUS_SYNERGIA_OTHER = 174
const val ERROR_LIBRUS_SYNERGIA_MAINTENANCE = 175
const val ERROR_LIBRUS_MESSAGES_MAINTENANCE = 176
const val ERROR_LIBRUS_MESSAGES_ERROR = 177
const val ERROR_LIBRUS_MESSAGES_OTHER = 178
const val ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN = 179
const val ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN = 180
const val ERROR_LIBRUS_API_MAINTENANCE = 181
const val ERROR_LIBRUS_PORTAL_MAINTENANCE = 182
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN = 201
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD = 202
@ -158,7 +168,9 @@ const val EXCEPTION_LIBRUS_API_REQUEST = 904
const val EXCEPTION_LIBRUS_SYNERGIA_REQUEST = 905
const val EXCEPTION_MOBIDZIENNIK_WEB_REQUEST = 906
const val EXCEPTION_VULCAN_API_REQUEST = 907
const val EXCEPTION_NOTIFY_AND_SYNC = 910
const val EXCEPTION_MOBIDZIENNIK_WEB_FILE_REQUEST = 908
const val EXCEPTION_LIBRUS_MESSAGES_FILE_REQUEST = 909
const val EXCEPTION_NOTIFY = 910
const val EXCEPTION_LIBRUS_MESSAGES_REQUEST = 911
const val EXCEPTION_IDZIENNIK_WEB_REQUEST = 912
const val EXCEPTION_IDZIENNIK_WEB_API_REQUEST = 913

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2019-9-29.
*/
package pl.szczodrzynski.edziennik.api.v2
package pl.szczodrzynski.edziennik.data.api
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ANNOUNCEMENTS
@ -82,4 +82,4 @@ object Features {
else -> getAllFeatures()
} + getAllNecessary()).sorted()
}
}
}

View File

@ -2,19 +2,19 @@
* Copyright (c) Kuba Szczodrzyński 2019-9-20.
*/
package pl.szczodrzynski.edziennik.api.v2
package pl.szczodrzynski.edziennik.data.api
import pl.szczodrzynski.edziennik.api.v2.idziennik.login.IdziennikLoginApi
import pl.szczodrzynski.edziennik.api.v2.idziennik.login.IdziennikLoginWeb
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginApi
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginMessages
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginPortal
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginSynergia
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.login.MobidziennikLoginWeb
import pl.szczodrzynski.edziennik.api.v2.models.LoginMethod
import pl.szczodrzynski.edziennik.api.v2.template.login.TemplateLoginApi
import pl.szczodrzynski.edziennik.api.v2.template.login.TemplateLoginWeb
import pl.szczodrzynski.edziennik.api.v2.vulcan.login.VulcanLoginApi
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login.IdziennikLoginApi
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login.IdziennikLoginWeb
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginApi
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginMessages
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginPortal
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginSynergia
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.login.MobidziennikLoginWeb
import pl.szczodrzynski.edziennik.data.api.edziennik.template.login.TemplateLoginApi
import pl.szczodrzynski.edziennik.data.api.edziennik.template.login.TemplateLoginWeb
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.VulcanLoginApi
import pl.szczodrzynski.edziennik.data.api.models.LoginMethod
// librus
// mobidziennik
@ -138,4 +138,4 @@ val templateLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_TEMPLATE, LOGIN_METHOD_TEMPLATE_API, TemplateLoginApi::class.java)
.withIsPossible { _, _ -> true }
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_TEMPLATE_WEB }
)
)

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-6.
*/
package pl.szczodrzynski.edziennik.api.v2
package pl.szczodrzynski.edziennik.data.api
object Regexes {
val MOBIDZIENNIK_GRADES_SUBJECT_NAME by lazy {
@ -24,7 +24,7 @@ object Regexes {
"""Liczona do średniej:.*?<strong>nie<br/?></strong>""".toRegex(RegexOption.DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_GRADES_DETAILS by lazy {
"""<strong.*?>(.+?)</strong>.*?<sup>.+?</sup>.*?<small>\((.+?)\)</small>.*?<span>.*?Wartość oceny:.*?<strong>([0-9.]+)</strong>.*?Wpisał\(a\):.*?<strong>(.+?)</strong>""".toRegex(RegexOption.DOT_MATCHES_ALL)
"""<strong.*?>(.+?)</strong>.*?<sup>.+?</sup>.*?(?:<small>\((.+?)\)</small>.*?)?<span>.*?Wartość oceny:.*?<strong>([0-9.]+)</strong>.*?Wpisał\(a\):.*?<strong>(.+?)</strong>.*?(?:Komentarz:.*?<strong>(.+?)</strong>)?</span>""".toRegex(RegexOption.DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_EVENT_TYPE by lazy {
@ -37,6 +37,16 @@ object Regexes {
"""events: (.+),$""".toRegex(RegexOption.MULTILINE)
}
val MOBIDZIENNIK_MESSAGE_READ_DATE by lazy {
"""czas przeczytania:.+?,\s([0-9]+)\s(.+?)\s([0-9]{4}),\sgodzina\s([0-9:]+)""".toRegex(RegexOption.DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_MESSAGE_SENT_READ_DATE by lazy {
""".+?,\s([0-9]+)\s(.+?)\s([0-9]{4}),\sgodzina\s([0-9:]+)""".toRegex(RegexOption.DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_MESSAGE_ATTACHMENT by lazy {
"""href="https://.+?\.mobidziennik.pl/.+?&(?:amp;)?zalacznik=([0-9]+)"(?:.+?<small.+?\(([0-9.]+)\s(M|K|G|)B\))*""".toRegex(RegexOption.DOT_MATCHES_ALL)
}
val IDZIENNIK_LOGIN_HIDDEN_FIELDS by lazy {
@ -61,7 +71,15 @@ object Regexes {
"""<option.*?value="([0-9]+)"\sdata-id-ucznia="([A-z0-9]+?)".*?>(.+?)\s(.+?)\s*\((.+?),\s*(.+?)\)</option>""".toRegex(RegexOption.DOT_MATCHES_ALL)
}
val VULCAN_SHITFT_ANNOTATION by lazy {
"""\(przeniesiona (z|na) lekcj[ię] ([0-9]+), (.+)\)""".toRegex()
}
val LIBRUS_ATTACHMENT_KEY by lazy {
"""singleUseKey=([0-9A-f_]+)""".toRegex()
}
}

View File

@ -2,14 +2,14 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-25.
*/
package pl.szczodrzynski.edziennik.api.v2.idziennik
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik
import androidx.core.util.set
import okhttp3.Cookie
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_IDZIENNIK_API
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_IDZIENNIK_WEB
import pl.szczodrzynski.edziennik.api.v2.models.Data
import pl.szczodrzynski.edziennik.data.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
@ -138,10 +138,12 @@ class DataIdziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data(
val teacher = teacherList.singleOrNull { it.fullName == "$firstName $lastName" }
return validateTeacher(teacher, firstName, lastName)
}
fun getTeacher(firstNameChar: Char, lastName: String): Teacher {
val teacher = teacherList.singleOrNull { it.shortName == "$firstNameChar.$lastName" }
return validateTeacher(teacher, firstNameChar.toString(), lastName)
}
fun getTeacherByLastFirst(nameLastFirst: String): Teacher {
val nameParts = nameLastFirst.split(" ")
return if (nameParts.size == 1) getTeacher(nameParts[0], "") else getTeacher(nameParts[1], nameParts[0])

View File

@ -2,20 +2,21 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-25.
*/
package pl.szczodrzynski.edziennik.api.v2.idziennik
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik
import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikData
import pl.szczodrzynski.edziennik.api.v2.idziennik.firstlogin.IdziennikFirstLogin
import pl.szczodrzynski.edziennik.api.v2.idziennik.login.IdziennikLogin
import pl.szczodrzynski.edziennik.api.v2.idziennikLoginMethods
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.prepare
import pl.szczodrzynski.edziennik.data.api.CODE_INTERNAL_LIBRUS_ACCOUNT_410
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikData
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.utils.Utils.d
@ -37,7 +38,7 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
private fun completed() {
data.saveData()
data.notifyAndSyncEvents {
data.notify {
callback.onCompleted()
}
}
@ -70,6 +71,10 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
}
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
}
override fun firstLogin() {
IdziennikFirstLogin(data) {
completed()

View File

@ -2,15 +2,16 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-25.
*/
package pl.szczodrzynski.edziennik.api.v2.idziennik
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik
import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.models.Feature
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.models.Feature
const val ENDPOINT_IDZIENNIK_WEB_TIMETABLE = 1030
const val ENDPOINT_IDZIENNIK_WEB_GRADES = 1040
const val ENDPOINT_IDZIENNIK_WEB_PROPOSED_GRADES = 1050
const val ENDPOINT_IDZIENNIK_WEB_EXAMS = 1060
const val ENDPOINT_IDZIENNIK_WEB_HOMEWORK = 1061
const val ENDPOINT_IDZIENNIK_WEB_NOTICES = 1070
const val ENDPOINT_IDZIENNIK_WEB_ANNOUNCEMENTS = 1080
const val ENDPOINT_IDZIENNIK_WEB_ATTENDANCE = 1090
@ -34,6 +35,10 @@ val IdziennikFeatures = listOf(
ENDPOINT_IDZIENNIK_WEB_EXAMS to LOGIN_METHOD_IDZIENNIK_WEB
), listOf(LOGIN_METHOD_IDZIENNIK_WEB)),
Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_HOMEWORK, listOf(
ENDPOINT_IDZIENNIK_WEB_HOMEWORK to LOGIN_METHOD_IDZIENNIK_WEB
), listOf(LOGIN_METHOD_IDZIENNIK_WEB)),
Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_BEHAVIOUR, listOf(
ENDPOINT_IDZIENNIK_WEB_NOTICES to LOGIN_METHOD_IDZIENNIK_WEB
), listOf(LOGIN_METHOD_IDZIENNIK_WEB)),

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-29.
*/
package pl.szczodrzynski.edziennik.api.v2.idziennik.data
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data
import com.google.gson.JsonArray
import com.google.gson.JsonElement
@ -11,9 +11,9 @@ import com.google.gson.JsonParser
import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import im.wangchao.mhttp.callback.TextCallbackHandler
import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.Utils
import java.net.HttpURLConnection

View File

@ -2,14 +2,14 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-25.
*/
package pl.szczodrzynski.edziennik.api.v2.idziennik.data
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.api.v2.idziennik.*
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.api.IdziennikApiCurrentRegister
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.api.IdziennikApiMessagesInbox
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.api.IdziennikApiMessagesSent
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.web.*
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.api.IdziennikApiCurrentRegister
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.api.IdziennikApiMessagesInbox
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.api.IdziennikApiMessagesSent
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.*
import pl.szczodrzynski.edziennik.utils.Utils
class IdziennikData(val data: DataIdziennik, val onSuccess: () -> Unit) {
@ -55,6 +55,10 @@ class IdziennikData(val data: DataIdziennik, val onSuccess: () -> Unit) {
data.startProgress(R.string.edziennik_progress_endpoint_exams)
IdziennikWebExams(data, onSuccess)
}
ENDPOINT_IDZIENNIK_WEB_HOMEWORK -> {
data.startProgress(R.string.edziennik_progress_endpoint_homework)
IdziennikWebHomework(data, onSuccess)
}
ENDPOINT_IDZIENNIK_WEB_NOTICES -> {
data.startProgress(R.string.edziennik_progress_endpoint_notices)
IdziennikWebNotices(data, onSuccess)

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-25.
*/
package pl.szczodrzynski.edziennik.api.v2.idziennik.data
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data
import com.google.gson.JsonArray
import com.google.gson.JsonObject
@ -10,9 +10,9 @@ import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import im.wangchao.mhttp.callback.JsonCallbackHandler
import im.wangchao.mhttp.callback.TextCallbackHandler
import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.utils.Utils.d
import java.net.HttpURLConnection.HTTP_INTERNAL_ERROR
import java.net.HttpURLConnection.HTTP_UNAUTHORIZED
@ -94,6 +94,7 @@ open class IdziennikWeb(open val data: DataIdziennik) {
is Long -> json.addProperty(name, value)
is Float -> json.addProperty(name, value)
is Char -> json.addProperty(name, value)
is Boolean -> json.addProperty(name, value)
}
}
setJsonBody(json)

View File

@ -2,14 +2,14 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-29.
*/
package pl.szczodrzynski.edziennik.api.v2.idziennik.data.api
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.api
import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.DAY
import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_API_CURRENT_REGISTER
import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_API_CURRENT_REGISTER
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikApi
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.getInt

View File

@ -2,13 +2,13 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-30.
*/
package pl.szczodrzynski.edziennik.api.v2.idziennik.data.api
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.api
import com.google.gson.JsonArray
import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_API_MESSAGES_INBOX
import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_API_MESSAGES_INBOX
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikApi
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

View File

@ -2,15 +2,15 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-30.
*/
package pl.szczodrzynski.edziennik.api.v2.idziennik.data.api
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.api.v2.IDZIENNIK_API_MESSAGES_SENT
import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_API_MESSAGES_SENT
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikApi
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

View File

@ -2,16 +2,16 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-28.
*/
package pl.szczodrzynski.edziennik.api.v2.idziennik.data.web
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.api.v2.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_WEB_ANNOUNCEMENTS
import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_ANNOUNCEMENTS
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.data.api.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_ANNOUNCEMENTS
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

View File

@ -2,14 +2,14 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-28.
*/
package pl.szczodrzynski.edziennik.api.v2.idziennik.data.web
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
import pl.szczodrzynski.edziennik.api.v2.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_WEB_ATTENDANCE
import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_ATTENDANCE
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.data.api.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_ATTENDANCE
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNIK_WEB_ATTENDANCE
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

View File

@ -2,24 +2,24 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-28.
*/
package pl.szczodrzynski.edziennik.api.v2.idziennik.data.web
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.api.v2.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_WEB_EXAMS
import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_EXAMS
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_EXAMS
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNIK_WEB_EXAMS
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.lessons.Lesson
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.getJsonObject
import pl.szczodrzynski.edziennik.utils.models.Date
class IdziennikWebExams(override val data: DataIdziennik,
val onSuccess: () -> Unit) : IdziennikWeb(data) {
val onSuccess: () -> Unit) : IdziennikWeb(data) {
companion object {
private const val TAG = "IdziennikWebExams"
}
@ -34,14 +34,15 @@ class IdziennikWebExams(override val data: DataIdziennik,
}
private fun getExams() {
val param = JsonObject()
param.addProperty("strona", 1)
param.addProperty("iloscNaStrone", "99")
param.addProperty("iloscRekordow", -1)
param.addProperty("kolumnaSort", "ss.Nazwa,sp.Data_sprawdzianu")
param.addProperty("kierunekSort", 0)
param.addProperty("maxIloscZaznaczonych", 0)
param.addProperty("panelFiltrow", 0)
val param = JsonObject().apply {
addProperty("strona", 1)
addProperty("iloscNaStrone", "99")
addProperty("iloscRekordow", -1)
addProperty("kolumnaSort", "ss.Nazwa,sp.Data_sprawdzianu")
addProperty("kierunekSort", 0)
addProperty("maxIloscZaznaczonych", 0)
addProperty("panelFiltrow", 0)
}
webApiGet(TAG, IDZIENNIK_WEB_EXAMS, mapOf(
"idP" to data.registerId,
@ -55,28 +56,34 @@ class IdziennikWebExams(override val data: DataIdziennik,
return@webApiGet
}
for (jExamEl in json.getAsJsonArray("ListK")) {
val jExam = jExamEl.asJsonObject
// jExam
val eventId = jExam.get("_recordId").asLong
val rSubject = data.getSubject(jExam.get("przedmiot").asString, -1, "")
val rTeacher = data.getTeacherByLastFirst(jExam.get("wpisal").asString)
val examDate = Date.fromY_m_d(jExam.get("data").asString)
val lessonObject = Lesson.getByWeekDayAndSubject(data.lessonList, examDate.weekDay, rSubject.id)
val examTime = lessonObject?.startTime
json.getJsonArray("ListK")?.asJsonObjectList()?.forEach { exam ->
val id = exam.getLong("_recordId") ?: return@forEach
val examDate = Date.fromY_m_d(exam.getString("data") ?: return@forEach)
val subjectName = exam.getString("przedmiot") ?: return@forEach
val subjectId = data.getSubject(subjectName, null, subjectName).id
val teacherName = exam.getString("wpisal") ?: return@forEach
val teacherId = data.getTeacherByLastFirst(teacherName).id
val topic = exam.getString("zakres") ?: ""
val lessonList = data.db.timetableDao().getForDateNow(profileId, examDate)
val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.startTime
val eventType = when (exam.getString("rodzaj")) {
"sprawdzian/praca klasowa" -> Event.TYPE_EXAM
else -> Event.TYPE_SHORT_QUIZ
}
val eventType = if (jExam.get("rodzaj").asString == "sprawdzian/praca klasowa") Event.TYPE_EXAM else Event.TYPE_SHORT_QUIZ
val eventObject = Event(
profileId,
eventId,
id,
examDate,
examTime,
jExam.get("zakres").asString,
startTime,
topic,
-1,
eventType,
false,
rTeacher.id,
rSubject.id,
teacherId,
subjectId,
data.teamClass?.id ?: -1
)
@ -106,9 +113,11 @@ class IdziennikWebExams(override val data: DataIdziennik,
examsNextMonthChecked = true
getExams()
} else {
data.toRemove.add(DataRemoveModel.Events.futureExceptType(Event.TYPE_HOMEWORK))
data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_EXAMS, SYNC_ALWAYS)
onSuccess()
}
}
}
}
}

View File

@ -2,16 +2,16 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-28.
*/
package pl.szczodrzynski.edziennik.api.v2.idziennik.data.web
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
import android.graphics.Color
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_WEB_GRADES
import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_GRADES
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.data.api.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_GRADES
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNIK_WEB_GRADES
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.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata

View File

@ -0,0 +1,98 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-11-25
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_HOMEWORK
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNIK_WEB_HOMEWORK
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.utils.models.Date
class IdziennikWebHomework(override val data: DataIdziennik,
val onSuccess: () -> Unit) : IdziennikWeb(data) {
companion object {
private const val TAG = "IdziennikWebHomework"
}
init {
val param = JsonObject().apply {
addProperty("strona", 1)
addProperty("iloscNaStrone", 997)
addProperty("iloscRekordow", -1)
addProperty("kolumnaSort", "DataZadania")
addProperty("kierunekSort", 0)
addProperty("maxIloscZaznaczonych", 0)
addProperty("panelFiltrow", 0)
}
webApiGet(TAG, IDZIENNIK_WEB_HOMEWORK, mapOf(
"idP" to data.registerId,
"data" to Date.getToday().stringY_m_d,
"wszystkie" to true,
"param" to param
)) { result ->
val json = result.getJsonObject("d") ?: run {
data.error(ApiError(TAG, ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA)
.withApiResponse(result))
return@webApiGet
}
json.getJsonArray("ListK")?.asJsonObjectList()?.forEach { homework ->
val id = homework.getLong("_recordId") ?: return@forEach
val eventDate = Date.fromY_m_d(homework.getString("dataO") ?: return@forEach)
val subjectName = homework.getString("przed") ?: return@forEach
val subjectId = data.getSubject(subjectName, null, subjectName).id
val teacherName = homework.getString("usr") ?: return@forEach
val teacherId = data.getTeacherByLastFirst(teacherName).id
val lessonList = data.db.timetableDao().getForDateNow(profileId, eventDate)
val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.displayStartTime
val topic = homework.getString("tytul") ?: ""
val seen = when (profile?.empty) {
true -> true
else -> eventDate < Date.getToday()
}
val eventObject = Event(
profileId,
id,
eventDate,
startTime,
topic,
-1,
Event.TYPE_HOMEWORK,
false,
teacherId,
subjectId,
data.teamClass?.id ?: -1
)
data.eventList.add(eventObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_HOMEWORK,
eventObject.id,
seen,
seen,
System.currentTimeMillis()
))
}
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_HOMEWORK))
data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_HOMEWORK, SYNC_ALWAYS)
onSuccess()
}
}
}

View File

@ -2,14 +2,14 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-28.
*/
package pl.szczodrzynski.edziennik.api.v2.idziennik.data.web
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
import pl.szczodrzynski.edziennik.api.v2.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_WEB_NOTICES
import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_NOTICES
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.data.api.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_NOTICES
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNIK_WEB_NOTICES
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

View File

@ -2,29 +2,32 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-28.
*/
package pl.szczodrzynski.edziennik.api.v2.idziennik.data.web
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
import pl.szczodrzynski.edziennik.api.v2.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_WEB_MISSING_GRADES
import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.api.v2.idziennik.ENDPOINT_IDZIENNIK_WEB_PROPOSED_GRADES
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.data.api.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_MISSING_GRADES
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNIK_WEB_PROPOSED_GRADES
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.asJsonObjectList
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.getJsonArray
import pl.szczodrzynski.edziennik.getJsonObject
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.Utils.getWordGradeValue
class IdziennikWebProposedGrades(override val data: DataIdziennik,
val onSuccess: () -> Unit) : IdziennikWeb(data) {
val onSuccess: () -> Unit) : IdziennikWeb(data) {
companion object {
private const val TAG = "IdziennikWebProposedGrades"
}
init {
init { data.profile?.also { profile ->
webApiGet(TAG, IDZIENNIK_WEB_MISSING_GRADES, mapOf(
"idPozDziennika" to data.registerId
)) { result ->
@ -34,17 +37,17 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
return@webApiGet
}
val jSubjects = json.getAsJsonArray("Przedmioty")
for (jSubjectEl in jSubjects) {
val jSubject = jSubjectEl.getAsJsonObject()
// jSubject
val rSubject = data.getSubject(jSubject.get("Przedmiot").getAsString(), -1, jSubject.get("Przedmiot").getAsString())
val semester1Proposed = jSubject.get("OcenaSem1").getAsString()
val semester2Proposed = jSubject.get("OcenaSem2").getAsString()
json.getJsonArray("Przedmioty")?.asJsonObjectList()?.forEach { subject ->
val subjectName = subject.getString("Przedmiot") ?: return@forEach
val subjectObject = data.getSubject(subjectName, null, subjectName)
val semester1Proposed = subject.getString("OcenaSem1") ?: ""
val semester1Value = getWordGradeValue(semester1Proposed)
val semester1Id = subjectObject.id * (-100) - 1
val semester2Proposed = subject.getString("OcenaSem2") ?: ""
val semester2Value = getWordGradeValue(semester2Proposed)
val semester1Id = rSubject.id * -100 - 1
val semester2Id = rSubject.id * -100 - 2
val semester2Id = subjectObject.id * (-100) - 2
if (semester1Proposed != "") {
val gradeObject = Grade(
@ -58,17 +61,18 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
0f,
1,
-1,
rSubject.id)
gradeObject.type = TYPE_SEMESTER1_PROPOSED
subjectObject.id
).apply {
type = TYPE_SEMESTER1_PROPOSED
}
data.gradeList.add(gradeObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_GRADE,
gradeObject.id,
profile?.empty ?: false,
profile?.empty ?: false,
profile.empty,
profile.empty,
System.currentTimeMillis()
))
}
@ -85,17 +89,18 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
0f,
2,
-1,
rSubject.id)
gradeObject.type = TYPE_YEAR_PROPOSED
subjectObject.id
).apply {
type = TYPE_YEAR_PROPOSED
}
data.gradeList.add(gradeObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_GRADE,
gradeObject.id,
profile?.empty ?: false,
profile?.empty ?: false,
profile.empty,
profile.empty,
System.currentTimeMillis()
))
}
@ -104,5 +109,5 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_PROPOSED_GRADES, SYNC_ALWAYS)
onSuccess()
}
}
}}
}

View File

@ -0,0 +1,192 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-11-22
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
import androidx.core.util.set
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_TIMETABLE
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNIK_WEB_TIMETABLE
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.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
import pl.szczodrzynski.edziennik.utils.models.Week
class IdziennikWebTimetable(override val data: DataIdziennik,
val onSuccess: () -> Unit) : IdziennikWeb(data) {
companion object {
private const val TAG = "IdziennikWebTimetable"
}
init { data.profile?.also { profile ->
val currentWeekStart = Week.getWeekStart()
if (Date.getToday().weekDay > 4) {
currentWeekStart.stepForward(0, 0, 7)
}
val getDate = data.arguments?.getString("weekStart") ?: currentWeekStart.stringY_m_d
val weekStart = Date.fromY_m_d(getDate)
val weekEnd = weekStart.clone().stepForward(0, 0, 6)
webApiGet(TAG, IDZIENNIK_WEB_TIMETABLE, mapOf(
"idPozDziennika" to data.registerId,
"pidRokSzkolny" to data.schoolYearId,
"data" to "${weekStart.stringY_m_d}T10:00:00.000Z"
)) { result ->
val json = result.getJsonObject("d") ?: run {
data.error(ApiError(TAG, ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA)
.withApiResponse(result))
return@webApiGet
}
json.getJsonArray("GodzinyLekcyjne")?.asJsonObjectList()?.forEach { 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
)
data.lessonRanges[lessonRange.lessonNumber] = lessonRange
}
val dates = mutableSetOf<Int>()
val lessons = mutableListOf<Lesson>()
json.getJsonArray("Przedmioty")?.asJsonObjectList()?.forEach { lesson ->
val subject = data.getSubject(
lesson.getString("Nazwa") ?: return@forEach,
lesson.getLong("Id"),
lesson.getString("Skrot") ?: ""
)
val teacher = data.getTeacherByFDotLast(lesson.getString("Nauczyciel")
?: return@forEach)
val newSubjectName = lesson.getString("PrzedmiotZastepujacy")
val newSubject = when (newSubjectName.isNullOrBlank()) {
true -> null
else -> data.getSubject(newSubjectName, null, newSubjectName)
}
val newTeacherName = lesson.getString("NauZastepujacy")
val newTeacher = when (newTeacherName.isNullOrBlank()) {
true -> null
else -> data.getTeacherByFDotLast(newTeacherName)
}
val weekDay = lesson.getInt("DzienTygodnia")?.minus(1) ?: return@forEach
val lessonRange = data.lessonRanges[lesson.getInt("Godzina")?.plus(1)
?: return@forEach]
val lessonDate = weekStart.clone().stepForward(0, 0, weekDay)
val classroom = lesson.getString("NazwaSali")
val type = lesson.getInt("TypZastepstwa") ?: -1
val lessonObject = Lesson(profileId, -1)
when (type) {
1, 2, 3, 4, 5 -> {
lessonObject.apply {
this.type = Lesson.TYPE_CHANGE
this.date = lessonDate
this.lessonNumber = lessonRange.lessonNumber
this.startTime = lessonRange.startTime
this.endTime = lessonRange.endTime
this.subjectId = newSubject?.id
this.teacherId = newTeacher?.id
this.teamId = data.teamClass?.id
this.classroom = classroom
this.oldDate = lessonDate
this.oldLessonNumber = lessonRange.lessonNumber
this.oldStartTime = lessonRange.startTime
this.oldEndTime = lessonRange.endTime
this.oldSubjectId = subject.id
this.oldTeacherId = teacher.id
this.oldTeamId = data.teamClass?.id
this.oldClassroom = classroom
}
}
0 -> {
lessonObject.apply {
this.type = Lesson.TYPE_CANCELLED
this.oldDate = lessonDate
this.oldLessonNumber = lessonRange.lessonNumber
this.oldStartTime = lessonRange.startTime
this.oldEndTime = lessonRange.endTime
this.oldSubjectId = subject.id
this.oldTeacherId = teacher.id
this.oldTeamId = data.teamClass?.id
this.oldClassroom = classroom
}
}
else -> {
lessonObject.apply {
this.type = Lesson.TYPE_NORMAL
this.date = lessonDate
this.lessonNumber = lessonRange.lessonNumber
this.startTime = lessonRange.startTime
this.endTime = lessonRange.endTime
this.subjectId = subject.id
this.teacherId = teacher.id
this.teamId = data.teamClass?.id
this.classroom = classroom
}
}
}
lessonObject.id = lessonObject.buildId()
dates.add(lessonDate.value)
lessons.add(lessonObject)
val seen = profile.empty || lessonDate < Date.getToday()
if (lessonObject.type != Lesson.TYPE_NORMAL && lessonDate >= Date.getToday()) {
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_LESSON_CHANGE,
lessonObject.id,
seen,
seen,
System.currentTimeMillis()
))
}
}
val date: Date = weekStart.clone()
while (date <= weekEnd) {
if (!dates.contains(date.value)) {
lessons.add(Lesson(profileId, date.value.toLong()).apply {
this.type = Lesson.TYPE_NO_LESSONS
this.date = date.clone()
})
}
date.stepForward(0, 0, 1)
}
d(TAG, "Clearing lessons between ${weekStart.stringY_m_d} and ${weekEnd.stringY_m_d} - timetable downloaded for $getDate")
data.lessonNewList.addAll(lessons)
data.toRemove.add(DataRemoveModel.Timetable.between(weekStart, weekEnd))
data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_TIMETABLE, SYNC_ALWAYS)
onSuccess()
}
}}
}

View File

@ -2,17 +2,17 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-27.
*/
package pl.szczodrzynski.edziennik.api.v2.idziennik.firstlogin
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.firstlogin
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.api.v2.ERROR_LOGIN_IDZIENNIK_FIRST_NO_SCHOOL_YEAR
import pl.szczodrzynski.edziennik.api.v2.IDZIENNIK_WEB_SETTINGS
import pl.szczodrzynski.edziennik.api.v2.Regexes
import pl.szczodrzynski.edziennik.api.v2.events.FirstLoginFinishedEvent
import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.api.v2.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.api.v2.idziennik.login.IdziennikLoginWeb
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
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.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.fixName
import pl.szczodrzynski.edziennik.get

View File

@ -2,12 +2,12 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-25.
*/
package pl.szczodrzynski.edziennik.api.v2.idziennik.login
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_IDZIENNIK_API
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_IDZIENNIK_WEB
import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
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.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.utils.Utils
class IdziennikLogin(val data: DataIdziennik, val onSuccess: () -> Unit) {

View File

@ -2,9 +2,9 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-27.
*/
package pl.szczodrzynski.edziennik.api.v2.idziennik.login
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login
import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
class IdziennikLoginApi(val data: DataIdziennik, val onSuccess: () -> Unit) {
companion object {

View File

@ -2,7 +2,7 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-26.
*/
package pl.szczodrzynski.edziennik.api.v2.idziennik.login
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login
import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
@ -10,9 +10,9 @@ import im.wangchao.mhttp.callback.TextCallbackHandler
import okhttp3.Cookie
import pl.szczodrzynski.edziennik.HOUR
import pl.szczodrzynski.edziennik.MINUTE
import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.getUnixDate
import pl.szczodrzynski.edziennik.utils.Utils

View File

@ -2,15 +2,15 @@
* Copyright (c) Kuba Szczodrzyński 2019-9-21.
*/
package pl.szczodrzynski.edziennik.api.v2.librus
package pl.szczodrzynski.edziennik.data.api.edziennik.librus
import okhttp3.Cookie
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_LIBRUS_API
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_LIBRUS_MESSAGES
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_LIBRUS_PORTAL
import pl.szczodrzynski.edziennik.api.v2.LOGIN_METHOD_LIBRUS_SYNERGIA
import pl.szczodrzynski.edziennik.api.v2.models.Data
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_LIBRUS_API
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_LIBRUS_MESSAGES
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_LIBRUS_PORTAL
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_LIBRUS_SYNERGIA
import pl.szczodrzynski.edziennik.data.api.models.Data
import pl.szczodrzynski.edziennik.currentTimeUnix
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
@ -148,16 +148,16 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
*/
private var mApiCode: String? = null
var apiCode: String?
get() { mApiCode = mApiCode ?: profile?.getStudentData("accountCode", null); return mApiCode }
set(value) { profile?.putStudentData("accountCode", value) ?: return; mApiCode = value }
get() { mApiCode = mApiCode ?: loginStore.getLoginData("accountCode", null); return mApiCode }
set(value) { loginStore.putLoginData("accountCode", value) ?: return; mApiCode = value }
/**
* A JST login PIN.
* Used only during first login in JST mode.
*/
private var mApiPin: String? = null
var apiPin: String?
get() { mApiPin = mApiPin ?: profile?.getStudentData("accountPin", null); return mApiPin }
set(value) { profile?.putStudentData("accountPin", value) ?: return; mApiPin = value }
get() { mApiPin = mApiPin ?: loginStore.getLoginData("accountPin", null); return mApiPin }
set(value) { loginStore.putLoginData("accountPin", value) ?: return; mApiPin = value }
/**
* A Synergia API access token.
@ -168,7 +168,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiAccessToken: String? = null
var apiAccessToken: String?
get() { mApiAccessToken = mApiAccessToken ?: profile?.getStudentData("accountToken", null); return mApiAccessToken }
set(value) { profile?.putStudentData("accountToken", value) ?: return; mApiAccessToken = value }
set(value) { mApiAccessToken = value; profile?.putStudentData("accountToken", value) ?: return; }
/**
* A Synergia API refresh token.
* Used when refreshing the [apiAccessToken] in JST, Synergia modes.
@ -176,7 +176,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiRefreshToken: String? = null
var apiRefreshToken: String?
get() { mApiRefreshToken = mApiRefreshToken ?: profile?.getStudentData("accountRefreshToken", null); return mApiRefreshToken }
set(value) { profile?.putStudentData("accountRefreshToken", value) ?: return; mApiRefreshToken = value }
set(value) { mApiRefreshToken = value; profile?.putStudentData("accountRefreshToken", value) ?: return; }
/**
* The expiry time for [apiAccessToken], as a UNIX timestamp.
* Used when refreshing the [apiAccessToken] in JST, Synergia modes.
@ -185,7 +185,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiTokenExpiryTime: Long? = null
var apiTokenExpiryTime: Long
get() { mApiTokenExpiryTime = mApiTokenExpiryTime ?: profile?.getStudentData("accountTokenTime", 0L); return mApiTokenExpiryTime ?: 0L }
set(value) { profile?.putStudentData("accountTokenTime", value) ?: return; mApiTokenExpiryTime = value }
set(value) { mApiTokenExpiryTime = value; profile?.putStudentData("accountTokenTime", value) ?: return; }
/* _____ _
/ ____| (_)
@ -269,4 +269,4 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
var enableDescriptiveGrades: Boolean
get() { mEnableDescriptiveGrades = mEnableDescriptiveGrades ?: profile?.getStudentData("enableDescriptiveGrades", true); return mEnableDescriptiveGrades ?: true }
set(value) { profile?.putStudentData("enableDescriptiveGrades", value) ?: return; mEnableDescriptiveGrades = value }
}
}

View File

@ -2,23 +2,22 @@
* Copyright (c) Kuba Szczodrzyński 2019-9-21.
*/
package pl.szczodrzynski.edziennik.api.v2.librus
package pl.szczodrzynski.edziennik.data.api.edziennik.librus
import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusData
import pl.szczodrzynski.edziennik.api.v2.librus.data.messages.LibrusMessagesGetMessage
import pl.szczodrzynski.edziennik.api.v2.librus.data.synergia.LibrusSynergiaMarkAllAnnouncementsAsRead
import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.LibrusFirstLogin
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLogin
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginApi
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginMessages
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginSynergia
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
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.messages.LibrusMessagesGetAttachment
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesGetMessage
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.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.utils.Utils.d
@ -40,7 +39,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
private fun completed() {
data.saveData()
data.notifyAndSyncEvents {
data.notify {
callback.onCompleted()
}
}
@ -82,11 +81,13 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
}
override fun getMessage(message: MessageFull) {
LibrusLoginApi(data) {
LibrusLoginSynergia(data) {
LibrusLoginMessages(data) {
LibrusMessagesGetMessage(data, message) {
completed()
LibrusLoginPortal(data) {
LibrusLoginApi(data) {
LibrusLoginSynergia(data) {
LibrusLoginMessages(data) {
LibrusMessagesGetMessage(data, message) {
completed()
}
}
}
}
@ -94,10 +95,26 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
}
override fun markAllAnnouncementsAsRead() {
LibrusLoginApi(data) {
LibrusLoginSynergia(data) {
LibrusSynergiaMarkAllAnnouncementsAsRead(data) {
completed()
LibrusLoginPortal(data) {
LibrusLoginApi(data) {
LibrusLoginSynergia(data) {
LibrusSynergiaMarkAllAnnouncementsAsRead(data) {
completed()
}
}
}
}
}
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
LibrusLoginPortal(data) {
LibrusLoginApi(data) {
LibrusLoginSynergia(data) {
LibrusLoginMessages(data) {
LibrusMessagesGetAttachment(data, message, attachmentId, attachmentName) {
completed()
}
}
}
}
}

View File

@ -2,56 +2,58 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-11.
*/
package pl.szczodrzynski.edziennik.api.v2.librus
package pl.szczodrzynski.edziennik.data.api.edziennik.librus
import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.models.Feature
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.models.Feature
const val ENDPOINT_LIBRUS_API_ME = 1001
const val ENDPOINT_LIBRUS_API_SCHOOLS = 1002
const val ENDPOINT_LIBRUS_API_CLASSES = 1003
const val ENDPOINT_LIBRUS_API_VIRTUAL_CLASSES = 1004
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_PUSH_CONFIG = 1010
const val ENDPOINT_LIBRUS_API_TIMETABLES = 1015
const val ENDPOINT_LIBRUS_API_SUBSTITUTIONS = 1016
const val ENDPOINT_LIBRUS_API_NORMAL_GC = 1021
const val ENDPOINT_LIBRUS_API_POINT_GC = 1022
const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_GC = 1023
const val ENDPOINT_LIBRUS_API_TEXT_GC = 1024
const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GC = 1025
const val ENDPOINT_LIBRUS_API_BEHAVIOUR_GC = 1026
const val ENDPOINT_LIBRUS_API_NORMAL_GRADES = 1031
const val ENDPOINT_LIBRUS_API_POINT_GRADES = 1032
const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES = 1033
const val ENDPOINT_LIBRUS_API_TEXT_GRADES = 1034
const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADES = 1035
const val ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES = 1036
const val ENDPOINT_LIBRUS_API_EVENT_TYPES = 1040
const val ENDPOINT_LIBRUS_API_EVENTS = 1041
const val ENDPOINT_LIBRUS_API_HOMEWORK = 1050
const val ENDPOINT_LIBRUS_API_LUCKY_NUMBER = 1060
const val ENDPOINT_LIBRUS_API_NOTICE_TYPES = 1070
const val ENDPOINT_LIBRUS_API_NOTICES = 1071
const val ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES = 1080
const val ENDPOINT_LIBRUS_API_ATTENDANCES = 1081
const val ENDPOINT_LIBRUS_API_ANNOUNCEMENTS = 1090
const val ENDPOINT_LIBRUS_API_PT_MEETINGS = 1100
const val ENDPOINT_LIBRUS_API_TEACHER_FREE_DAY_TYPES = 1109
const val ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS = 1110
const val ENDPOINT_LIBRUS_API_SCHOOL_FREE_DAYS = 1120
const val ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS = 1130
const val ENDPOINT_LIBRUS_SYNERGIA_INFO = 2010
const val ENDPOINT_LIBRUS_SYNERGIA_GRADES = 2020
const val ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK = 2030
const val ENDPOINT_LIBRUS_MESSAGES_RECEIVED = 3010
const val ENDPOINT_LIBRUS_MESSAGES_SENT = 3020
const val ENDPOINT_LIBRUS_MESSAGES_TRASH = 3030
const val ENDPOINT_LIBRUS_MESSAGES_RECEIVERS = 3040
const val ENDPOINT_LIBRUS_MESSAGES_GET = 3040
const val ENDPOINT_LIBRUS_API_ME = 1001
const val ENDPOINT_LIBRUS_API_SCHOOLS = 1002
const val ENDPOINT_LIBRUS_API_CLASSES = 1003
const val ENDPOINT_LIBRUS_API_VIRTUAL_CLASSES = 1004
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_PUSH_CONFIG = 1010
const val ENDPOINT_LIBRUS_API_TIMETABLES = 1015
const val ENDPOINT_LIBRUS_API_SUBSTITUTIONS = 1016
const val ENDPOINT_LIBRUS_API_NORMAL_GRADE_CATEGORIES = 1021
const val ENDPOINT_LIBRUS_API_POINT_GRADE_CATEGORIES = 1022
const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADE_CATEGORIES = 1023
const val ENDPOINT_LIBRUS_API_TEXT_GRADE_CATEGORIES = 1024
const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADE_CATEGORIES = 1025
const val ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_CATEGORIES = 1026
const val ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_COMMENTS = 1027
const val ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS = 1030
const val ENDPOINT_LIBRUS_API_NORMAL_GRADES = 1031
const val ENDPOINT_LIBRUS_API_POINT_GRADES = 1032
const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES = 1033
const val ENDPOINT_LIBRUS_API_TEXT_GRADES = 1034
const val ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GRADES = 1035
const val ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES = 1036
const val ENDPOINT_LIBRUS_API_EVENT_TYPES = 1040
const val ENDPOINT_LIBRUS_API_EVENTS = 1041
const val ENDPOINT_LIBRUS_API_HOMEWORK = 1050
const val ENDPOINT_LIBRUS_API_LUCKY_NUMBER = 1060
const val ENDPOINT_LIBRUS_API_NOTICE_TYPES = 1070
const val ENDPOINT_LIBRUS_API_NOTICES = 1071
const val ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES = 1080
const val ENDPOINT_LIBRUS_API_ATTENDANCES = 1081
const val ENDPOINT_LIBRUS_API_ANNOUNCEMENTS = 1090
const val ENDPOINT_LIBRUS_API_PT_MEETINGS = 1100
const val ENDPOINT_LIBRUS_API_TEACHER_FREE_DAY_TYPES = 1109
const val ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS = 1110
const val ENDPOINT_LIBRUS_API_SCHOOL_FREE_DAYS = 1120
const val ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS = 1130
const val ENDPOINT_LIBRUS_SYNERGIA_INFO = 2010
const val ENDPOINT_LIBRUS_SYNERGIA_GRADES = 2020
const val ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK = 2030
const val ENDPOINT_LIBRUS_MESSAGES_RECEIVED = 3010
const val ENDPOINT_LIBRUS_MESSAGES_SENT = 3020
const val ENDPOINT_LIBRUS_MESSAGES_TRASH = 3030
const val ENDPOINT_LIBRUS_MESSAGES_RECEIVERS = 3040
const val ENDPOINT_LIBRUS_MESSAGES_GET = 3040
val LibrusFeatures = listOf(
@ -59,7 +61,7 @@ val LibrusFeatures = listOf(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_PUSH_CONFIG, listOf(
ENDPOINT_LIBRUS_API_PUSH_CONFIG to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)).withShouldSync { data ->
data.app.appConfig.fcmTokens[LOGIN_TYPE_LIBRUS]?.second?.contains(data.profileId) == false
!data.app.config.sync.tokenLibrusList.contains(data.profileId)
},
@ -91,12 +93,14 @@ val LibrusFeatures = listOf(
* All grades + categories.
*/
Feature(LOGIN_TYPE_LIBRUS, FEATURE_GRADES, listOf(
ENDPOINT_LIBRUS_API_NORMAL_GC to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_POINT_GC to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_GC to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_TEXT_GC to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_TEXT_GC to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_BEHAVIOUR_GC to LOGIN_METHOD_LIBRUS_API,
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,
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,
ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_COMMENTS to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_NORMAL_GRADES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_POINT_GRADES to LOGIN_METHOD_LIBRUS_API,
ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES to LOGIN_METHOD_LIBRUS_API,

View File

@ -2,15 +2,15 @@
* Copyright (c) Kuba Szczodrzyński 2019-9-21.
*/
package pl.szczodrzynski.edziennik.api.v2.librus.data
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data
import com.google.gson.JsonObject
import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import im.wangchao.mhttp.callback.JsonCallbackHandler
import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.Utils.d
import java.net.HttpURLConnection.*
@ -32,6 +32,13 @@ open class LibrusApi(open val data: DataLibrus) {
val callback = object : JsonCallbackHandler() {
override fun onSuccess(json: JsonObject?, response: Response?) {
if (response?.code() == HTTP_UNAVAILABLE) {
data.error(ApiError(tag, ERROR_LIBRUS_API_MAINTENANCE)
.withApiResponse(json)
.withResponse(response))
return
}
if (json == null && response?.parserErrorBody == null) {
data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY)
.withResponse(response))
@ -104,8 +111,9 @@ open class LibrusApi(open val data: DataLibrus) {
.allowErrorCode(HTTP_BAD_REQUEST)
.allowErrorCode(HTTP_FORBIDDEN)
.allowErrorCode(HTTP_UNAUTHORIZED)
.allowErrorCode(HTTP_UNAVAILABLE)
.callback(callback)
.build()
.enqueue()
}
}
}

View File

@ -2,14 +2,14 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-5.
*/
package pl.szczodrzynski.edziennik.api.v2.librus.data
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.api.v2.librus.*
import pl.szczodrzynski.edziennik.api.v2.librus.data.api.*
import pl.szczodrzynski.edziennik.api.v2.librus.data.messages.LibrusMessagesGetList
import pl.szczodrzynski.edziennik.api.v2.librus.data.synergia.LibrusSynergiaHomework
import pl.szczodrzynski.edziennik.api.v2.librus.data.synergia.LibrusSynergiaInfo
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.*
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.utils.Utils
@ -81,13 +81,29 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
LibrusApiTimetables(data, onSuccess)
}
ENDPOINT_LIBRUS_API_NORMAL_GRADE_CATEGORIES -> {
data.startProgress(R.string.edziennik_progress_endpoint_grade_categories)
LibrusApiGradeCategories(data, onSuccess)
}
ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_CATEGORIES -> {
data.startProgress(R.string.edziennik_progress_endpoint_grade_categories)
LibrusApiBehaviourGradeCategories(data, onSuccess)
}
ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS -> {
data.startProgress(R.string.edziennik_progress_endpoint_grade_comments)
LibrusApiGradeComments(data, onSuccess)
}
ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_COMMENTS -> {
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)
}
ENDPOINT_LIBRUS_API_NORMAL_GC -> {
data.startProgress(R.string.edziennik_progress_endpoint_grade_categories)
LibrusApiGradeCategories(data, onSuccess)
ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES -> {
data.startProgress(R.string.edziennik_progress_endpoint_behaviour_grades)
LibrusApiBehaviourGrades(data, onSuccess)
}
// TODO grades

View File

@ -0,0 +1,229 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-10-24
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data
import com.google.gson.JsonObject
import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import im.wangchao.mhttp.body.MediaTypeUtils
import im.wangchao.mhttp.callback.FileCallbackHandler
import im.wangchao.mhttp.callback.JsonCallbackHandler
import im.wangchao.mhttp.callback.TextCallbackHandler
import okhttp3.Cookie
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.parser.Parser
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.utils.Utils.d
import java.io.File
import java.io.StringWriter
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.OutputKeys
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult
open class LibrusMessages(open val data: DataLibrus) {
companion object {
private const val TAG = "LibrusMessages"
}
val profileId
get() = data.profile?.id ?: -1
val profile
get() = data.profile
fun messagesGet(tag: String, endpoint: String, method: Int = POST,
parameters: Map<String, Any>? = null, onSuccess: (doc: Document) -> Unit) {
d(tag, "Request: Librus/Messages - $LIBRUS_MESSAGES_URL/$endpoint")
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 doc = Jsoup.parse(text, "", Parser.xmlParser())
onSuccess(doc)
} catch (e: Exception) {
data.error(ApiError(tag, EXCEPTION_LIBRUS_MESSAGES_REQUEST)
.withResponse(response)
.withThrowable(e)
.withApiResponse(text))
}
}
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()
/*val requestXml = xml("service") {
"header" { }
"data" {
for ((key, value) in parameters.orEmpty()) {
key {
-value.toString()
}
}
}
}.toString(PrintOptions(
singleLineTextElements = true,
useSelfClosingTags = true
))*/
Request.builder()
.url("$LIBRUS_MESSAGES_URL/$endpoint")
.userAgent(SYNERGIA_USER_AGENT)
.setTextBody(requestXml, MediaTypeUtils.APPLICATION_XML)
.apply {
when (method) {
GET -> get()
POST -> post()
}
}
.callback(callback)
.build()
.enqueue()
}
fun sandboxGet(tag: String, action: String, parameters: Map<String, Any>? = null,
onSuccess: (json: JsonObject) -> Unit) {
d(tag, "Request: Librus/Messages - $LIBRUS_SANDBOX_URL$action")
val callback = object : JsonCallbackHandler() {
override fun onSuccess(json: JsonObject?, response: Response?) {
if (json == null) {
data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY)
.withResponse(response))
return
}
try {
onSuccess(json)
} catch (e: Exception) {
data.error(ApiError(tag, EXCEPTION_LIBRUS_MESSAGES_REQUEST)
.withResponse(response)
.withThrowable(e)
.withApiResponse(json))
}
}
override fun onFailure(response: Response?, throwable: Throwable?) {
data.error(ApiError(tag, ERROR_REQUEST_FAILURE)
.withResponse(response)
.withThrowable(throwable))
}
}
Request.builder()
.url("$LIBRUS_SANDBOX_URL$action")
.userAgent(SYNERGIA_USER_AGENT)
.apply {
parameters?.forEach { (k, v) ->
addParameter(k, v)
}
}
.post()
.callback(callback)
.build()
.enqueue()
}
fun sandboxGetFile(tag: String, action: String, targetFile: File, onSuccess: (file: File) -> Unit,
onProgress: (written: Long, total: Long) -> Unit) {
d(tag, "Request: Librus/Messages - $LIBRUS_SANDBOX_URL$action")
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_LIBRUS_MESSAGES_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_LIBRUS_MESSAGES_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("$LIBRUS_SANDBOX_URL$action")
.userAgent(SYNERGIA_USER_AGENT)
.post()
.callback(callback)
.build()
.enqueue()
}
}

View File

@ -1,12 +1,12 @@
package pl.szczodrzynski.edziennik.api.v2.librus.data
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data
import com.google.gson.JsonObject
import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import im.wangchao.mhttp.callback.JsonCallbackHandler
import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.Utils.d
import java.net.HttpURLConnection
@ -101,4 +101,4 @@ open class LibrusPortal(open val data: DataLibrus) {
.build()
.enqueue()
}
}
}

View File

@ -2,19 +2,19 @@
* Copyright (c) Kacper Ziubryniewicz 2019-10-21.
*/
package pl.szczodrzynski.edziennik.api.v2.librus.data
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data
import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import im.wangchao.mhttp.callback.TextCallbackHandler
import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.utils.Utils.d
open class LibrusSynergia(open val data: DataLibrus) {
companion object {
const val TAG = "LibrusSynergia"
private const val TAG = "LibrusSynergia"
}
val profileId

View File

@ -2,16 +2,15 @@
* Copyright (c) Kacper Ziubryniewicz 2019-10-13
*/
package pl.szczodrzynski.edziennik.api.v2.librus.data.api
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_ANNOUNCEMENTS
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
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.utils.Utils
import pl.szczodrzynski.edziennik.utils.models.Date
class LibrusApiAnnouncements(override val data: DataLibrus,
@ -20,19 +19,19 @@ class LibrusApiAnnouncements(override val data: DataLibrus,
const val TAG = "LibrusApiAnnouncements"
}
init {
init { data.profile?.also { profile ->
apiGet(TAG, "SchoolNotices") { json ->
val announcements = json.getJsonArray("SchoolNotices").asJsonObjectList()
announcements?.forEach { announcement ->
val id = Utils.crc16(announcement.getString("Id")?.toByteArray()
?: return@forEach).toLong()
val id = announcement.getString("Id")?.crc32() ?: return@forEach
val subject = announcement.getString("Subject") ?: ""
val text = announcement.getString("Content") ?: ""
val startDate = Date.fromY_m_d(announcement.getString("StartDate"))
val endDate = Date.fromY_m_d(announcement.getString("EndDate"))
val teacherId = announcement.getJsonObject("AddedBy")?.getLong("Id") ?: -1
val addedDate = Date.fromIso(announcement.getString("CreationDate"))
val addedDate = announcement.getString("CreationDate")?.let { Date.fromIso(it) }
?: System.currentTimeMillis()
val read = announcement.getBoolean("WasRead") ?: false
val announcementObject = Announcement(
@ -51,7 +50,7 @@ class LibrusApiAnnouncements(override val data: DataLibrus,
Metadata.TYPE_ANNOUNCEMENT,
id,
read,
read,
profile.empty || read,
addedDate
))
}
@ -59,5 +58,5 @@ class LibrusApiAnnouncements(override val data: DataLibrus,
data.setSyncNext(ENDPOINT_LIBRUS_API_ANNOUNCEMENTS, SYNC_ALWAYS)
onSuccess()
}
}
}}
}

View File

@ -2,13 +2,13 @@
* Copyright (c) Kacper Ziubryniewicz 2019-10-13
*/
package pl.szczodrzynski.edziennik.api.v2.librus.data.api
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import android.graphics.Color
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
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

View File

@ -2,13 +2,13 @@
* Copyright (c) Kacper Ziubryniewicz 2019-10-13
*/
package pl.szczodrzynski.edziennik.api.v2.librus.data.api
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import androidx.core.util.isEmpty
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_ATTENDANCES
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
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
@ -36,14 +36,14 @@ class LibrusApiAttendances(override val data: DataLibrus,
val lessonNo = attendance.getInt("LessonNo") ?: return@forEach
val startTime = data.lessonRanges.get(lessonNo).startTime
val lessonDate = Date.fromY_m_d(attendance.getString("Date"))
val subjectId = data.lessonList.singleOrNull {
it.weekDay == lessonDate.weekDay && it.startTime.value == startTime.value
}?.subjectId ?: -1
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 attendanceObject = Attendance(
profileId,
id,

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-3
*/
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_BEHAVIOUR_GRADE_CATEGORIES
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeCategory
class LibrusApiBehaviourGradeCategories(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) {
companion object {
const val TAG = "LibrusApiBehaviourGradeCategories"
}
init {
apiGet(TAG, "BehaviourGrades/Points/Categories") { json ->
json.getJsonArray("Categories")?.asJsonObjectList()?.forEach { category ->
val id = category.getLong("Id") ?: return@forEach
val name = category.getString("Name") ?: ""
val valueFrom = category.getFloat("ValueFrom") ?: 0f
val valueTo = category.getFloat("ValueTo") ?: 0f
val gradeCategoryObject = GradeCategory(
profileId,
id,
-1f,
Color.BLUE,
name
).apply {
type = GradeCategory.TYPE_BEHAVIOUR
setValueRange(valueFrom, valueTo)
}
data.gradeCategories.put(id, gradeCategoryObject)
}
data.setSyncNext(ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_CATEGORIES, 1 * WEEK)
onSuccess()
}
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-7
*/
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_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
class LibrusApiBehaviourGradeComments(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) {
companion object {
const val TAG = "LibrusApiBehaviourGradeComments"
}
init {
apiGet(TAG, "BehaviourGrades/Points/Comments") { json ->
json.getJsonArray("Comments")?.asJsonObjectList()?.forEach { comment ->
val id = comment.getLong("Id") ?: return@forEach
val text = comment.getString("Text")?.fixWhiteSpaces() ?: return@forEach
val gradeCategoryObject = GradeCategory(
profileId,
id,
-1f,
-1,
text
).apply {
type = GradeCategory.TYPE_BEHAVIOUR_COMMENT
}
data.gradeCategories.put(id, gradeCategoryObject)
}
data.setSyncNext(ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_COMMENTS, SYNC_ALWAYS)
onSuccess()
}
}
}

View File

@ -0,0 +1,150 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-12-3
*/
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_BEHAVIOUR_GRADES
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.Grade
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeCategory
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.utils.models.Date
import java.text.DecimalFormat
class LibrusApiBehaviourGrades(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) {
companion object {
const val TAG = "LibrusApiBehaviourGrades"
}
private val nameFormat by lazy { DecimalFormat("#.##") }
init { data.profile?.let { profile ->
apiGet(TAG, "BehaviourGrades/Points") { json ->
val semester1StartGradeObject = Grade(
profileId,
-101,
data.app.getString(R.string.grade_start_points),
0xffbdbdbd.toInt(),
data.app.getString(R.string.grade_start_points_format, 1),
nameFormat.format(data.startPointsSemester1),
data.startPointsSemester1.toFloat(),
-1f,
1,
-1,
1
).apply { type = Grade.TYPE_BEHAVIOUR }
data.gradeList.add(semester1StartGradeObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_GRADE,
semester1StartGradeObject.id,
true,
true,
profile.getSemesterStart(1).inMillis
))
val semester2StartGradeObject = Grade(
profileId,
-102,
data.app.getString(R.string.grade_start_points),
0xffbdbdbd.toInt(),
data.app.getString(R.string.grade_start_points_format, 2),
nameFormat.format(data.startPointsSemester2),
data.startPointsSemester2.toFloat(),
-1f,
2,
-1,
1
).apply { type = Grade.TYPE_BEHAVIOUR }
data.gradeList.add(semester2StartGradeObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_GRADE,
semester2StartGradeObject.id,
true,
true,
profile.getSemesterStart(2).inMillis
))
json.getJsonArray("Grades")?.asJsonObjectList()?.forEach { grade ->
val id = grade.getLong("Id") ?: return@forEach
val value = grade.getFloat("Value")
val shortName = grade.getString("ShortName")
val semester = grade.getInt("Semester") ?: profile.currentSemester
val teacherId = grade.getJsonObject("AddedBy")?.getLong("Id") ?: -1
val addedDate = grade.getString("AddDate")?.let { Date.fromIso(it) }
?: System.currentTimeMillis()
val name = when {
value != null -> (if (value >= 0) "+" else "") + nameFormat.format(value)
shortName != null -> shortName
else -> return@forEach
}
val color = data.getColor(when {
value == null || value == 0f -> 12
value > 0 -> 16
value < 0 -> 26
else -> 12
})
val categoryId = grade.getJsonObject("Category")?.getLong("Id") ?: -1
val category = data.gradeCategories.singleOrNull {
it.categoryId == categoryId && it.type == GradeCategory.TYPE_BEHAVIOUR
}
val categoryName = category?.text ?: ""
val description = grade.getJsonArray("Comments")?.asJsonObjectList()?.let { comments ->
if (comments.isNotEmpty()) {
data.gradeCategories.singleOrNull {
it.type == GradeCategory.TYPE_BEHAVIOUR_COMMENT
&& it.categoryId == comments[0].asJsonObject.getLong("Id")
}?.text
} else null
} ?: ""
val valueFrom = value ?: category?.valueFrom ?: 0f
val valueTo = category?.valueTo ?: 0f
val gradeObject = Grade(
profileId,
id,
categoryName,
color,
description,
name,
valueFrom,
-1f,
semester,
teacherId,
1
).apply {
type = Grade.TYPE_BEHAVIOUR
valueMax = valueTo
}
data.gradeList.add(gradeObject)
data.metadataList.add(Metadata(
profileId,
Metadata.TYPE_GRADE,
id,
profile.empty,
profile.empty,
addedDate
))
}
data.setSyncNext(ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES, SYNC_ALWAYS)
onSuccess()
}
}}
}

View File

@ -2,12 +2,12 @@
* Copyright (c) Kacper Ziubryniewicz 2019-10-14
*/
package pl.szczodrzynski.edziennik.api.v2.librus.data.api
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import pl.szczodrzynski.edziennik.DAY
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_CLASSES
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
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.getJsonObject
import pl.szczodrzynski.edziennik.getLong

View File

@ -2,12 +2,12 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-24.
*/
package pl.szczodrzynski.edziennik.api.v2.librus.data.api
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_CLASSROOMS
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
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 java.util.*

View File

@ -2,12 +2,12 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-24.
*/
package pl.szczodrzynski.edziennik.api.v2.librus.data.api
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_EVENT_TYPES
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
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
class LibrusApiEventTypes(override val data: DataLibrus,

View File

@ -2,13 +2,14 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-4.
*/
package pl.szczodrzynski.edziennik.api.v2.librus.data.api
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import androidx.core.util.isEmpty
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_EVENTS
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
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
@ -69,6 +70,8 @@ class LibrusApiEvents(override val data: DataLibrus,
))
}
data.toRemove.add(DataRemoveModel.Events.futureExceptType(Event.TYPE_HOMEWORK))
data.setSyncNext(ENDPOINT_LIBRUS_API_EVENTS, SYNC_ALWAYS)
onSuccess()
}

View File

@ -2,13 +2,13 @@
* Copyright (c) Kacper Ziubryniewicz 2019-11-5
*/
package pl.szczodrzynski.edziennik.api.v2.librus.data.api
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import android.graphics.Color
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_NORMAL_GC
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
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
@ -22,7 +22,7 @@ class LibrusApiGradeCategories(override val data: DataLibrus,
apiGet(TAG, "Grades/Categories") { json ->
json.getJsonArray("Categories")?.asJsonObjectList()?.forEach { category ->
val id = category.getLong("Id") ?: return@forEach
val name = category.getString("Name") ?: ""
val name = category.getString("Name")?.fixWhiteSpaces() ?: ""
val weight = when (category.getBoolean("CountToTheAverage")) {
true -> category.getFloat("Weight") ?: 0f
else -> 0f
@ -41,7 +41,7 @@ class LibrusApiGradeCategories(override val data: DataLibrus,
data.gradeCategories.put(id, gradeCategoryObject)
}
data.setSyncNext(ENDPOINT_LIBRUS_API_NORMAL_GC, SYNC_ALWAYS)
data.setSyncNext(ENDPOINT_LIBRUS_API_NORMAL_GRADE_CATEGORIES, SYNC_ALWAYS)
onSuccess()
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-11-20
*/
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_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
class LibrusApiGradeComments(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) {
companion object {
const val TAG = "LibrusApiGradeComments"
}
init {
apiGet(TAG, "Grades/Comments") { json ->
json.getJsonArray("Comments")?.asJsonObjectList()?.forEach { comment ->
val id = comment.getLong("Id") ?: return@forEach
val text = comment.getString("Text")?.fixWhiteSpaces() ?: return@forEach
val gradeCategoryObject = GradeCategory(
profileId,
id,
-1f,
-1,
text
).apply {
type = GradeCategory.TYPE_NORMAL_COMMENT
}
data.gradeCategories.put(id, gradeCategoryObject)
}
data.setSyncNext(ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS, SYNC_ALWAYS)
onSuccess()
}
}
}

View File

@ -1,11 +1,12 @@
package pl.szczodrzynski.edziennik.api.v2.librus.data.api
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADES
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
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.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.utils.Utils
import pl.szczodrzynski.edziennik.utils.models.Date
@ -42,12 +43,21 @@ class LibrusApiGrades(override val data: DataLibrus,
weight = 0f
}
val description = grade.getJsonArray("Comments")?.asJsonObjectList()?.let { comments ->
if (comments.isNotEmpty()) {
data.gradeCategories.singleOrNull {
it.type == GradeCategory.TYPE_NORMAL_COMMENT
&& it.categoryId == comments[0].asJsonObject.getLong("Id")
}?.text
} else null
} ?: ""
val gradeObject = Grade(
profileId,
id,
categoryName,
color,
"",
description,
name,
value,
weight,

View File

@ -2,12 +2,13 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-12.
*/
package pl.szczodrzynski.edziennik.api.v2.librus.data.api
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_HOMEWORK
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
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
@ -55,6 +56,8 @@ class LibrusApiHomework(override val data: DataLibrus,
))
}
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_HOMEWORK))
data.setSyncNext(ENDPOINT_LIBRUS_API_HOMEWORK, SYNC_ALWAYS)
onSuccess()
}

View File

@ -2,12 +2,12 @@
* Copyright (c) Kacper Ziubryniewicz 2019-10-14
*/
package pl.szczodrzynski.edziennik.api.v2.librus.data.api
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import pl.szczodrzynski.edziennik.DAY
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_LUCKY_NUMBER
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
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.getInt

View File

@ -2,12 +2,12 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-3.
*/
package pl.szczodrzynski.edziennik.api.v2.librus.data.api
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_ME
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_ME
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
class LibrusApiMe(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) {

View File

@ -2,12 +2,12 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-24.
*/
package pl.szczodrzynski.edziennik.api.v2.librus.data.api
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_NOTICE_TYPES
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
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
class LibrusApiNoticeTypes(override val data: DataLibrus,

View File

@ -2,13 +2,13 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-24.
*/
package pl.szczodrzynski.edziennik.api.v2.librus.data.api
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import androidx.core.util.isEmpty
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_NOTICES
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
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

View File

@ -2,12 +2,12 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-24.
*/
package pl.szczodrzynski.edziennik.api.v2.librus.data.api
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_PT_MEETINGS
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
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.utils.models.Date

View File

@ -2,12 +2,12 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-4.
*/
package pl.szczodrzynski.edziennik.api.v2.librus.data.api
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_SCHOOLS
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
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.utils.models.Time
import java.util.*

View File

@ -2,12 +2,12 @@
* Copyright (c) Kuba Szczodrzyński 2019-10-23.
*/
package pl.szczodrzynski.edziennik.api.v2.librus.data.api
package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_SUBJECTS
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
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
class LibrusApiSubjects(override val data: DataLibrus,

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