Compare commits

...

282 Commits

Author SHA1 Message Date
46407f9647 [4.4.3] Update build.gradle, signing and changelog. 2020-10-16 23:54:48 +02:00
6ecb97b87e [Login/Podlasie] Add option to logout other devices. 2020-10-16 23:50:44 +02:00
ecdaaeae65 [API/Vulcan] Add KO1 routing rule. 2020-10-16 18:06:34 +02:00
a0c302b663 [App] Swap devMode with debugMode. Fix hiding sticks from old. 2020-10-16 17:18:19 +02:00
b31039ecd9 [API/Mobidziennik] Fix getting recipient list. 2020-10-16 16:57:00 +02:00
5c84086f42 [Settings/Grades] Add hiding sticks from old. 2020-10-14 22:26:47 +02:00
752cdfa8d6 Implement wear module base. 2020-09-17 18:05:17 +02:00
8e3d404352 [4.4.2] Update build.gradle, signing and changelog. 2020-09-05 19:14:11 +02:00
810cfd8092 [API] Rename response parameters to fix compatibility. 2020-09-05 19:13:37 +02:00
bd2a9524c6 [UI] Use HtmlCompat instead of Html. Fix a typo. 2020-09-05 18:47:30 +02:00
d780d5118d [Proguard] Add rules to fix API responses. 2020-09-05 18:38:53 +02:00
f1570b8eb9 [4.4.1] Update build.gradle, signing and changelog. 2020-09-04 15:44:26 +02:00
de0f29a09e [API/Mobidziennik] Fix getting attendance when a day has no lessons. 2020-09-04 00:04:23 +02:00
c0d11c91e3 [API/Mobidziennik] Fix trimming subject name in timetable. 2020-09-03 23:51:56 +02:00
22c540a3d4 [UI] Improve register unavailable dialog and card. 2020-09-03 23:50:53 +02:00
b7e35d0322 [4.4] Update build.gradle, signing and changelog. 2020-09-03 14:08:54 +02:00
7bcd6bf038 [Sync] Implement checking register availability. Improve app updates. 2020-09-03 13:39:46 +02:00
ea4591144b [4.3.1] Update build.gradle, signing and changelog. 2020-08-29 00:00:38 +02:00
7627d184a2 [API/Librus] Update client parameters. 2020-08-28 23:47:28 +02:00
076b485fda [API] Enable back sync before school year. 2020-08-28 23:27:34 +02:00
09cb97e367 [4.3] Update build.gradle, signing and changelog. 2020-08-28 15:26:42 +02:00
4e1f2ed41a [UI] Update date in about card subtext. Make gradlew executable. 2020-08-27 12:07:07 +02:00
281b6a95ef [API] Fix for syncing new profiles after archiving. 2020-08-27 00:10:19 +02:00
e40871c0d0 [UI] Update register names, again. 2020-08-26 22:15:48 +02:00
b74eeed994 [UI] Update register names. 2020-08-26 21:36:30 +02:00
ccde482364 [UI] Update register names. 2020-08-25 23:48:51 +02:00
a02033d0f3 [API] Fix archiving compatibility for older app versions. 2020-08-25 22:25:21 +02:00
6c6bc89f57 [UI] Improve archive-related UI. Add archived info home card. 2020-08-25 19:14:11 +02:00
1e3da45340 [UI] Remove unused home cards. 2020-08-25 17:02:12 +02:00
0d366adddb [UI] Implement showing archived profiles in drawer. 2020-08-25 16:01:11 +02:00
2c24eba46d [UI] Show bottom bar badge in debug versions. 2020-08-25 12:05:58 +02:00
7c6dbca986 [API] Implement basic profile archiving. 2020-08-25 10:46:50 +02:00
33a8fa2a1e [API] Fix doubled sync on API error. 2020-08-24 18:56:26 +02:00
300e2c4bc2 [API/Librus] Fix doubled sync on reCaptcha timeout. 2020-08-24 18:16:47 +02:00
f883318bd2 [Gradle] Fix compilation issues in latest Android Studio. 2020-08-24 17:42:53 +02:00
5460c1e2a0 [4.2.1] Update build.gradle, signing and changelog. 2020-05-21 23:12:25 +02:00
137c975e81 [API/Vulcan] Add getting Firebase token from server. 2020-05-21 22:07:31 +02:00
001de4a88c [Firebase] Fix getting FCM tokens and try to fix Vulcan registering. 2020-05-20 22:04:39 +02:00
5dcb3fd580 [Data] Fix setting correct time zone in ISO date parsing. 2020-05-18 12:22:21 +02:00
f13995aa5c [API/Mobidziennik] Fix lucky number extraction. 2020-05-18 11:42:58 +02:00
e23deb5ca6 [API/Podlasie] Fix security token generation. 2020-05-18 11:41:27 +02:00
d688b379a2 [4.2] Update build.gradle, signing and changelog. 2020-05-16 21:27:56 +02:00
d6a796e25e [Login] Crop Podlasie logo. Add Feedback activity, remove help icon. 2020-05-16 20:59:28 +02:00
e02d3e571d [API/Librus] Fix some more captcha errors. 2020-05-16 20:52:16 +02:00
907b75b22d [Proguard] Add rule for app login platform. 2020-05-16 20:46:54 +02:00
c3660b5f80 [Login] Change Librus logo. Disable Synergia & Vulcan e-mail login. 2020-05-16 20:44:56 +02:00
7ff10df70c [Login] Fix not showing sync fragment. 2020-05-16 20:33:37 +02:00
83e1b21ec3 [API/Podlasie] Add downloading attachments in homework. 2020-05-14 18:54:37 +02:00
deb54e4b24 [API/Podlasie] Add getting homework. 2020-05-14 12:06:52 +02:00
48873caecc [DB/Grades] Fix DataRemoveModel deleting models instead of marking as don't keep. 2020-05-14 11:43:22 +02:00
cadd1a3dbd Merge branch 'feature/prymus' into develop 2020-05-13 23:16:34 +02:00
f09f069b2c [API/Podlasie] Move event description to homework body. 2020-05-13 22:56:22 +02:00
1fb5aaed5d [UI/Podlasie] Show homework fragment in drawer. 2020-05-13 22:55:45 +02:00
65ba330d5f [API/Podlasie] Fix encoding in events topic. 2020-05-13 22:49:01 +02:00
795317f13f [API/Podlasie] Add getting teachers. 2020-05-13 22:47:05 +02:00
031cc05209 [API/Podlasie] Add getting events. 2020-05-13 22:17:50 +02:00
0ac8e1d9c1 [API/Podlasie] Add getting the lucky number. 2020-05-13 19:56:41 +02:00
4389dc9d79 [API/Podlasie] Add getting grades proposals. 2020-05-13 19:37:24 +02:00
b13257cb78 [API/Podlasie] Add getting grades. 2020-05-13 17:33:42 +02:00
fcffa2afeb [API/Podlasie] Fix saving class team, add main endpoint and getting the timetable. 2020-05-13 16:56:42 +02:00
3c2f85f263 Merge branch 'develop' into feature/prymus 2020-05-12 20:37:15 +02:00
0a2323acf3 [API/Podlasie] Implement first login and login page. 2020-05-12 20:25:45 +02:00
45c2948ed1 [Login] Fix not showing errors after one successful login. 2020-05-12 18:23:29 +02:00
f72a6103b5 Merge branch 'feature/new-login' into develop 2020-05-12 18:20:08 +02:00
9261848369 [API] Improve Lab fragment. Fix OkHttp crashing on API <21. 2020-05-12 13:41:40 +02:00
7f4e45c57c Merge branch 'develop' into feature/prymus 2020-05-11 21:06:18 +02:00
180154b684 Merge branch 'new-login' into develop 2020-05-11 20:59:38 +02:00
a4f58eb19b [API/Podlasie] Implement basic Podlasie e-register support. 2020-05-11 20:41:13 +02:00
fada483d55 [Login] Add missing e-registers. 2020-05-11 20:37:25 +02:00
3ae9ba3d61 Merge branch 'develop' into feature/new-login
# Conflicts:
#	app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/VulcanLoginApi.kt
#	app/src/main/res/values/strings.xml
2020-05-11 19:06:07 +02:00
15102fe818 [Hotfix] Suppress ConvertSecondaryConstructorToPrimary in LibrusLoginApi. 2020-05-11 18:14:15 +02:00
8864bb2a5e [UI] Add new app icon and splash logo. 2020-05-11 13:42:30 +02:00
ef1cdd5b20 [API/Librus] Use Synergia message module when messages module login fails. 2020-05-10 19:44:44 +02:00
35f4f34342 [API/Vulcan] Add syncing first semester. Disable counting releases in attendance. 2020-05-09 23:01:42 +02:00
1a8134459a [API/Librus] Implement downloading messages using Synergia endpoints. 2020-05-09 20:11:09 +02:00
a6ce3a5068 [API/Librus] Make downloading attachments use Synergia. 2020-05-09 20:11:09 +02:00
328b20f78b [Fix] Increase HTTP client timeout duration. 2020-05-09 20:11:09 +02:00
771712da99 [UI/Attendance] Fix counting issues. Add attendance details dialog. 2020-05-09 19:18:18 +02:00
65e0e10db6 [4.1] Update build.gradle, signing and changelog. 2020-05-09 15:23:44 +02:00
62e0e53354 [DB] Force attendance sync in models migration. 2020-05-09 10:59:25 +02:00
a5461a488a [UI] Fix attendance type list crash. 2020-05-09 10:53:19 +02:00
192dd0c4c7 [UI] Add listing attendance by type. 2020-05-08 22:19:55 +02:00
c49755c0eb [API/Librus] Fix messages login when ReCaptcha is needed. 2020-05-08 20:46:51 +02:00
c8c758958d [API/Mobidziennik] Fix web attendance without lesson topic. 2020-05-07 15:48:16 +02:00
e068f1944f [UI] Add attendance summary page. Disable presence notifications. 2020-05-05 22:57:24 +02:00
97412a3736 [UI] Add German translation. 2020-05-05 21:46:18 +02:00
9167d53a1a [UI] Add new attendance UI module. 2020-05-04 22:47:27 +02:00
6436a17036 [API] Fix signatures for nightly versions. 2020-05-04 09:28:39 +02:00
5ab5dbe940 [UI] Show nightly version badge in main activity. 2020-05-03 13:11:04 +02:00
d68ab0d010 [Fix] Suppress i18n warning in GradesConfigDialog. 2020-05-02 23:55:20 +02:00
f70a1f5730 [DB] Fix homework added date migration. 2020-05-01 22:03:13 +02:00
85106a01d7 [API/Librus] Add online lesson URL to events description. 2020-04-30 21:41:42 +02:00
90e99e241a [UI/Home] Show textual period grades in grades card. 2020-04-29 15:23:30 +02:00
3f61ab8299 [DB] Refactor database and entities. 2020-04-29 15:14:45 +02:00
f685a4dceb [API/Vulcan] Implement Vulcan lucky numbers. 2020-04-22 20:05:36 +02:00
e8dad29e5d Merge branch 'develop' into feature/new-login 2020-04-20 19:05:05 +02:00
13a3f66db3 Merge branch 'api-v2' into develop
# Conflicts:
#	.gitignore
#	app/build.gradle
#	app/src/main/assets/pl-changelog.html
#	app/src/main/java/pl/szczodrzynski/edziennik/App.java
#	app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt
#	app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt
#	app/src/main/java/pl/szczodrzynski/edziennik/data/api/Librus.java
#	app/src/main/java/pl/szczodrzynski/edziennik/data/api/Mobidziennik.java
#	app/src/main/java/pl/szczodrzynski/edziennik/data/api/Vulcan.java
#	app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java
#	app/src/main/java/pl/szczodrzynski/edziennik/utils/models/AppConfig.java
#	app/src/main/res/values-en/strings.xml
#	app/src/main/res/values/strings.xml
#	build.gradle
#	gradle/wrapper/gradle-wrapper.properties
2020-04-20 19:00:53 +02:00
dc9e6081c5 [4.0] Update build.gradle, signing and changelog. 2020-04-19 22:06:19 +02:00
26f8c03570 [UI] Show class name and school year in subname. Fix setting language. 2020-04-19 22:01:29 +02:00
97e0f36f09 [UI] Fix semester grades overlapping when scrolling. 2020-04-19 21:37:39 +02:00
27e49b10fd [API] Implement draft Vulcan Web login. 2020-04-19 19:27:27 +02:00
97dc8d12f1 [Login] Add new login user interface. 2020-04-16 11:01:53 +02:00
9b13552b73 [API/Mobidziennik] Fix downloading attachments. 2020-04-16 09:45:28 +02:00
d8559637a5 [Strings] Fix HTML tags missing in translations. 2020-04-13 22:43:26 +02:00
00a90a14dc [API] Fix downloading attachments with different name. Handle UTF encoded download names. 2020-04-07 13:57:12 +02:00
d56afb034b [API] Add Vulcan OneDrive attachment downloading. Add asking for permissions on demand. 2020-04-07 12:16:48 +02:00
0327ba37f1 [Strings] Update English translation. 2020-04-07 09:10:38 +02:00
12a54e58b5 [API] Add Vulcan message & homework attachments. Fix Mobidziennik homework attachments. 2020-04-06 22:50:00 +02:00
238250e8c9 [API/Mobidziennik] Fix homework attachment downloading. 2020-04-06 19:28:04 +02:00
041bfc6cc0 [4.0-rc.5] Update build.gradle, signing and changelog. 2020-04-05 23:59:13 +02:00
8a4866cb62 [Messages] Add new attachments view. Allow replying to deleted messages. 2020-04-05 23:56:56 +02:00
0e4d609bbf [Messages] Fix search in sent messages. Implement removing messages. 2020-04-05 23:39:32 +02:00
f07b12bd87 [Grades] Fix marking yearly grades as seen. 2020-04-05 22:04:23 +02:00
0413dbffa2 [Messages] Implement saving list position on message open. 2020-04-05 21:40:12 +02:00
c214b48409 [Messages] Add a search bar. Fix Grades not loading. 2020-04-05 20:06:35 +02:00
91a6366548 [Messages] Fix opening messages. 2020-04-04 23:14:40 +02:00
03c9932b8c [API/Edudziennik] Add getting whole homework body. 2020-04-04 22:24:15 +02:00
ea4919a25d [API/Librus] Add handling missing attachment error. 2020-04-04 22:23:31 +02:00
f98b174857 [UI] Refactor Messages fragment. 2020-04-04 21:31:14 +02:00
c0aeb0d2f3 [UI] Add showing unseen events. Add Lab fragment. 2020-04-03 20:58:51 +02:00
fcb627aac6 [Messages/Compose] Fix underlined word under the caret. 2020-04-02 15:38:16 +02:00
7f0aea29cd [API/Librus] Fix HTML to string conversion in homework. Add force download homework button in debug mode. 2020-04-02 12:56:24 +02:00
f6dcbb6594 [API/Librus] Disable Messages login when downloading homework attachment. 2020-04-02 11:19:44 +02:00
b790421693 [API/Idziennik] Correct showing proposed descriptive grades. Clarify some connection errors. 2020-04-02 11:17:47 +02:00
f5a7799924 [API/Librus] Fix getting homework without attachments. 2020-04-02 09:37:22 +02:00
b052b5bd66 [API/Librus] Add getting homework body and downloading homework attachments. 2020-04-01 22:55:20 +02:00
12d8de1def [API] Implement Idziennik homework attachment downloading. Change attachment events to sticky. 2020-04-01 21:49:05 +02:00
9303483470 [API] Implement Mobidziennik homework attachment downloading. Modify the interface a bit. Show attachments in UI. 2020-04-01 17:07:50 +02:00
f8adc86a0e [Events] Fix for duplicated events when metadata is doubled. 2020-04-01 17:04:06 +02:00
db57c258c5 [API] Add trimming whitespaces from events' titles. 2020-04-01 16:56:47 +02:00
ddb2760c16 [Events] Add Mobidziennik event attachment listing. 2020-03-31 20:04:32 +02:00
14d267a95a [API] Fix task cancelling with the notification. [UI] Add event downloading progress bar. 2020-03-31 18:20:24 +02:00
a6c4053896 [API] Add interface method to get event details. 2020-03-31 15:18:34 +02:00
949a68ec1d [Homework] Add mark as done confirmation dialog. Refactor code a bit. 2020-03-31 09:06:32 +02:00
93333a8c48 [Homework] Fix showing done homework on every profile. 2020-03-31 08:34:08 +02:00
da48c059ec [4.0-rc.4] Update build.gradle, signing and changelog. 2020-03-30 23:29:34 +02:00
ee5566d1ef [Events] Add toast hint to mark as done button. 2020-03-30 23:28:50 +02:00
b794b30346 [UI] Fix disabling pull to refresh when changing page using tab layout. 2020-03-30 23:16:35 +02:00
0db6393bb0 [Events] Add showing green check when event is done. Hide done events from homework current list. 2020-03-30 23:02:19 +02:00
fcc3c55110 [Events] Fix preserving isDone value. Improve DataRemoveModel method of keeping items. 2020-03-30 22:37:48 +02:00
328c07eaf4 [Messages] Fix Librus attachment downloading. Add option to force (re)download an attachment. 2020-03-30 19:48:56 +02:00
b004ec048e [UI] Refactor Grades, Notifications, Homework fragments to better match unified templates. 2020-03-30 18:55:28 +02:00
b9f83875a0 [Event] Add isDone attribute and marking events as done. 2020-03-30 18:19:19 +02:00
8c869d082b [UI] Add pager fragment templates. Move all templates to 'template' module. Fix swipe to refresh with pager fragments. 2020-03-30 12:50:21 +02:00
043f8210ba [UI] Add lazy loading to fragments with view pager. 2020-03-29 23:11:17 +02:00
41a79caf83 [API/Mobidziennik] Change data remove model to include only possible types. 2020-03-29 21:06:39 +02:00
0427fa6087 [Events] Add support for selective updates and upserting. 2020-03-29 18:05:56 +02:00
2f3c912dbe [Config] Disable teacher absence notifications by default. Add missing migration values. 2020-03-29 16:27:05 +02:00
219a7443c0 [4.0-rc.3] Update build.gradle, signing and changelog. 2020-03-29 15:31:49 +02:00
6deb408d80 [API/Librus] Fix attachment downloading, once again. 2020-03-29 15:30:30 +02:00
c6e1ff2164 [Events] Fix event sorting. Fix showing event teacher name. 2020-03-29 15:26:48 +02:00
bc0918a115 [API/Librus] Fix attachment downloading. 2020-03-29 15:16:35 +02:00
55ff9173be [API/Liburs] Fix unseen teacher absence metadata and add notifications for new teacher absences. 2020-03-28 17:08:36 +01:00
d4d548846f [Refactor] Refactor EventDao class. 2020-03-28 11:17:39 +01:00
ef4527f140 [Refactor] Rewrite events to Kotlin. 2020-03-27 18:51:56 +01:00
0b1e7242bb [API/Mobidziennik] Fix some errors. 2020-03-27 14:05:03 +01:00
30b6ac2a06 [4.0-rc.2] Update build.gradle, singing and changelog. 2020-03-26 20:46:03 +01:00
a7fa7cb5e4 [API/Librus] Fix a typo. 2020-03-26 20:45:46 +01:00
f3e87f9016 [API/Librus] Fix missing login data error. 2020-03-26 20:42:58 +01:00
a983af6c28 [4.0-rc.1] Update build.gradle, singing and changelog. 2020-03-26 20:40:00 +01:00
114c841f0c [API/Liburs] Fix Librus API push config endpoint. 2020-03-26 20:34:59 +01:00
e271048577 [UI] Clarify some errors. Fix deselecting the mini drawer. 2020-03-26 18:48:54 +01:00
b8c5925e82 [API/Librus] Fix attendance NPE. 2020-03-26 17:55:39 +01:00
9bda6c8869 [API/Librus] Disable push config with no premium. Disable API homework. Enable Synergia homework. 2020-03-26 16:31:11 +01:00
d1608d308c [API/Librus] Disable login with credentials in Messages. 2020-03-26 15:57:13 +01:00
b8e1e1d33a [Event/Manual] Fix dropdowns not showing any data. 2020-03-25 17:36:22 +01:00
8099a037e7 [Proguard] Update proguard rules to fix BetterLink and MiniDrawer. 2020-03-24 21:03:19 +01:00
af23c932a6 [API] Change the Cookie jar to fix most cookie problems. 2020-03-24 20:43:41 +01:00
4edabbb186 [Messages/Compose] Enable HTML messages for Idziennik. 2020-03-24 16:31:46 +01:00
37a5bea79b [Libraries] Update gradle, NavLib and Firebase. 2020-03-24 16:29:45 +01:00
40cdc7d713 [4.0-beta.14] Update build.gradle, signing and changelog. 2020-03-24 12:54:42 +01:00
49825aca48 [API/Librus] Fix message attachment downloading. 2020-03-24 12:51:49 +01:00
1d57c4e705 [Settings] Replace hardcoded Discord invite link with a redirect. 2020-03-21 16:57:01 +01:00
87ae5787ee [UX] Fix app quiting in home when back button opens drawer function active. 2020-03-20 21:39:38 +01:00
20f16c25a3 [API/Liburs] Fix getting wrong homework description in Synergia. 2020-03-20 15:47:12 +01:00
6f1ec79d9b [UX] Fix back button opens drawer function always opening the drawer. 2020-03-20 14:21:13 +01:00
18c7eea89c [API/Mobidziennik] Fix messages exception when no table found. Handle server problem maintenance error. 2020-03-19 23:25:01 +01:00
f73060aeb6 [API/Idziennik] Fix announcements error. 2020-03-19 23:04:23 +01:00
2f653b83b6 [Errors] Clarify some HTTP error explanations. 2020-03-19 23:00:39 +01:00
445dec907d [Home] Change card swipe direction to left. Add config dialog to bottom sheet. 2020-03-19 22:29:42 +01:00
927316d24b [Firebase] Disable sync by firebase when profile is excluded from auto sync. 2020-03-19 20:18:52 +01:00
3957453ed6 [UI] Fix displaying lists for correct profile in event manual dialog. 2020-03-19 20:09:35 +01:00
0296c704cb [UI] Update dialog NoDisplay theme. 2020-03-19 20:02:50 +01:00
1e7fe972de [API/Librus] Fix setting messages as read. 2020-03-19 19:49:55 +01:00
c95bc656ea [UI] Add context menus in messages and events to quickly run an action. 2020-03-19 17:55:12 +01:00
a082d95b04 [Events/Manual] Remove saving progress toasts. 2020-03-18 14:39:21 +01:00
6866dd4801 [Widgets/Timetable] Fix "no lessons" and "no timetable" texts overlapping. 2020-03-18 12:48:04 +01:00
2186da416e [Timetable] Fix displaying "no lessons" when all lessons in next 7 days are cancelled. 2020-03-18 12:45:53 +01:00
22d859fcde [UI] Set main snackbar dismiss timeout to 7 seconds. 2020-03-18 12:44:57 +01:00
39514b69b3 [API/Vulcan] Fix API url slash issue when migrating from 3.x. 2020-03-18 12:44:28 +01:00
c384736840 [Grades] Fix counting average without weight. 2020-03-17 16:06:22 +01:00
507657f273 [UI/Messages] Improve HTML lists presentation. 2020-03-17 16:05:21 +01:00
60641742ed [4.0-beta.13] Update build.gradle, signing and changelog. 2020-03-15 23:05:03 +01:00
0fc6f07986 [Homework] Fix homework list sorting. 2020-03-15 22:20:35 +01:00
1b2bdc0580 [Login] Add QR scanner to Vulcan & Librus JST login. Implement incorrect token error in Librus JST. 2020-03-15 21:46:46 +01:00
9bac239f77 [API/Librus] Add handling message not found error. Fix for duplicated errors and exceptions. 2020-03-15 20:15:10 +01:00
371acb2d2a [Events] Disable shared notification for past events. 2020-03-15 20:01:23 +01:00
454f82e139 [Events] Disable shared notification with registration disabled. Add registration enable prompt when sharing events. 2020-03-15 19:59:48 +01:00
e8da249353 [UI] Fix date dropdown selecting wrong month. Refactor event dialogs a bit. 2020-03-15 14:54:26 +01:00
c7950c53da [API/Vulcan] Fix adding unknown subject in timetable. Fix selecting correct TeamClass in timetable. 2020-03-15 12:12:00 +01:00
b5502478e4 [Dialog/EventManual] Add process dialog and fix some things. 2020-03-14 23:27:16 +01:00
4480a7e486 [API/Librus] Fix indicating parent account during first login. 2020-03-13 16:37:30 +01:00
7c7dff743b [API] Optimize App Sync a bit. 2020-03-13 16:22:43 +01:00
c568cd3f2e [Messages] Replace hardcoded message colors with brightened/darkened versions instead of white/black. 2020-03-12 18:46:16 +01:00
6ec2bc6f21 [API/Mobidziennik] Fix duplicated line breaks when getting message. 2020-03-12 13:55:02 +01:00
af3b6f3a97 [UI] Replace material date pickers with the DatePickerDialog. Add time picker to time dropdown. 2020-03-11 21:11:35 +01:00
d855118610 [Attendance] Revert changing attendance item font. 2020-03-11 19:58:56 +01:00
c9992d9fe8 [UI] Make fragments disable pull to refresh when not scrolled to the top. 2020-03-11 19:18:24 +01:00
85fe2636cc [Home] Disable pull to refresh while swiping a card. 2020-03-11 18:41:37 +01:00
35f4a31a76 [Home] Implement dismissing, adding and removing cards. Remove debug card. 2020-03-11 18:25:28 +01:00
1e494ebb70 [Feedback] Implement feedback fragment in feedback activity. 2020-03-11 17:36:41 +01:00
ed93627505 [Grades] Implement not counting selected grades to average. 2020-03-11 16:57:12 +01:00
b9b4b0036f [Grades] Update fonts and colors a bit. 2020-03-11 16:25:54 +01:00
4aa31424d6 [4.0-beta.12] Update build.gradle, signing and changelog. 2020-03-10 23:50:24 +01:00
8a825227cb [Timetable] Disable setting metadata for normal type lessons. 2020-03-10 23:45:40 +01:00
cc1b581d7e [Grades] Show custom plus/minus value annotation in GradeDetailsDialog. 2020-03-10 23:44:02 +01:00
9936d90ae2 [Dropdown/Date] Clarify strings a bit (makes sense during weekends). 2020-03-10 22:04:13 +01:00
df1a241b2b [Timetable] Fix showing "no timetable" when all nearest lessons are cancelled. Fix a crash in timetable fragment. 2020-03-10 22:01:30 +01:00
ae89b33fb7 [Events/Manual] Implement syncing timetable when no lessons for the selected date. 2020-03-10 21:49:02 +01:00
e05b483f5c [Grades] Disable counting grade value when custom values not specified. 2020-03-10 21:45:38 +01:00
715f536b23 [MainActivity] Fix some critical errors. 2020-03-10 20:57:04 +01:00
930813fb8a [Agenda] Try to fix agenda fragment not attached crashes. 2020-03-10 20:25:14 +01:00
acd5e9b998 [Timetable] Implement lazy day loading. Introduce TimetableManager class. 2020-03-10 19:27:18 +01:00
06011bf4ae [Grades] Add grades config and mark as read menu items. 2020-03-10 18:38:28 +01:00
30e15b813c [HotFix/Timers] Change timers intervals from 1s to 500ms. 2020-03-09 22:03:44 +01:00
fcd7a7f349 [Grades] Make home card use GradeView. Update GradeDetailsDialog text color. Remove deprecated items. 2020-03-09 20:39:48 +01:00
42ef40439e [Grades] Implement showing unseen badges and marking as seen. Change default "hide improved" config value. 2020-03-09 20:18:11 +01:00
098beb14fe [Timetable/Generate] Add automatic timetable sync when no timetable for the selected week. 2020-03-09 14:57:14 +01:00
0b186a754a [API/Librus] Implement behaviour grades with types. Use optional "Phrase" in text grades. 2020-03-08 20:12:37 +01:00
d00963b53d [Grades] Implement getting correct grade colors. 2020-03-08 19:39:23 +01:00
e282af0e80 [Grades] Add option to hide improved grades. Make counting average without weight configurable. 2020-03-08 17:57:44 +01:00
630361849c [Notifications] Implement Quiet hours. Add missing timetable manual strings. 2020-03-08 17:22:14 +01:00
88a1de50ca [Changelog] Update the changelog a bit. 2020-03-07 20:14:05 +01:00
d8263d0b6a [Timetable/Manual] Add database migration to implement new model. 2020-03-07 20:09:22 +01:00
611ab0f100 [Events/Manual] Create custom views for dropdowns. Simplify dialog code. Fix wrong start time saving. 2020-03-07 20:03:47 +01:00
70c307b796 [UI/Grades] Change some fonts. 2020-03-07 11:54:47 +01:00
054a233ad6 [API/Librus] Handle some more maintenance cases. 2020-03-07 09:45:45 +01:00
55268f1c43 [4.0-beta.11] Update build.gradle, signing and changelog. 2020-03-06 23:23:33 +01:00
1bec6d281c [Grades] Implement Grades editor. 2020-03-06 21:24:01 +01:00
f17a02be54 [Grades] Implement new Grades module (UI & API changes). 2020-03-06 21:09:05 +01:00
4e8fdd2225 [API/Idziennik] Fix incorrect exam type. 2020-03-06 09:25:35 +01:00
59819b4a96 [Base] Update TemplateFragment. 2020-03-04 19:09:53 +01:00
673378d8d9 [UI/Home] Improve no data text font in home cards. 2020-03-04 19:02:50 +01:00
30044d6b21 [Timetable] Ignore last lessons if cancelled and jump to the next day. 2020-03-03 18:06:55 +01:00
ee43d40680 [API/Librus] Fix device not registered error in push config. 2020-03-03 10:35:48 +01:00
1354faf8c7 [Dialog/GenerateBlockTimetable] Make better dialog layout. 2020-02-29 00:43:38 +01:00
1bfb3781ab [UI/Lists] Add missing item dividers. Try to improve attendance & grades design. 2020-02-28 23:45:46 +01:00
d7d0c6f822 [UI/Events] Add button tooltips in dialogs. Add showing weekday in home card. Add 'go to timetable' button in details dialog. 2020-02-28 23:01:38 +01:00
2bea18dc3c [Home/Events] Add new card to home fragment. Disable debug card swapping. 2020-02-28 22:38:03 +01:00
f998f2d956 [Home/Timetable] Remove "?" lessons from timetable card. 2020-02-28 21:10:03 +01:00
faa77ee5fb [Widgets/Timetable] Show crossed out classroom in lesson change if no new classroom specified. 2020-02-28 21:10:03 +01:00
88ec463284 [Gradle] Update gradle. 2020-02-28 18:33:28 +01:00
b7df71d7d9 [API/Grades] Fix proposed/final grades added date in Mobidziennik, Idziennik. 2020-02-27 23:41:41 +01:00
6a28dbd2c4 [API/Idziennik] Add changing the selected student/register (web) to get grades in some cases. 2020-02-27 23:36:41 +01:00
010f7fa1fe [API/Idziennik] Add getting lucky number from website. Fix API lucky number date. 2020-02-27 23:01:47 +01:00
209f98594f [Widgets/Timetable] Show lessons date in unified timetable widget. 2020-02-27 22:32:02 +01:00
54121c99a3 [Login/Captcha] Update captcha to fit smaller screens. Fix Librus invalid login error with captcha. 2020-02-26 21:27:03 +01:00
f6f1370edf [Debug] Add new debug mode. Include hidden Chucker in release. 2020-02-26 20:37:55 +01:00
d5863485f9 [API/Edudziennik] Fix getting attendance for the second semester. 2020-02-25 19:28:53 +01:00
afc88d316b [4.0-beta.10] Update build.gradle, signing and changelog. 2020-02-24 18:27:21 +01:00
b141279811 [API/Librus] Update Client ID. Add handling of invalid Client ID error. 2020-02-24 18:06:53 +01:00
3c8afb0609 [3.2.2] Update build.gradle and changelog. Update Librus Client ID and user agent. 2020-02-24 16:55:24 +01:00
1997ea25d5 [Gradle] Update gradle and libraries. 2020-02-24 15:29:18 +01:00
f4b49eecd4 [UI] Update theme accent colors. 2020-02-24 15:29:18 +01:00
a4493ec964 [Notifications] Add filtering notifications to show during sync. 2020-02-24 15:29:18 +01:00
af8bda9e92 [Dialog/Day] Add showing lessons count and length. 2020-02-23 23:17:28 +01:00
06d252e4ca [Notifications] Fix chucker notifications throwing an error toast. 2020-02-23 17:40:02 +01:00
67be456bb0 [Firebase/Librus] Implement Librus push registration and receiving. Fix not passing lastSync to endpoint. 2020-02-21 22:49:24 +01:00
aa5e225148 [Firebase/Vulcan] Fix not converting received string to JsonObject. 2020-02-21 22:32:50 +01:00
367f46fac8 [API/Librus] Fix captcha showing as incorrect login error. Add handling CSRF error. 2020-02-21 21:32:06 +01:00
d2f14093ec [API] Fix sync error in case of an internal, handled error. 2020-02-21 20:41:57 +01:00
43ed621879 [Errors] Fix error reporting from snackbar. 2020-02-21 20:35:18 +01:00
15c8134d13 [Firebase/Vulcan] Implement push notifications sync. 2020-02-20 21:20:51 +01:00
27f9b8a04e Update gradle. Add Chucker. 2020-01-10 09:30:02 +01:00
86669a491a Update .gitignore 2020-01-09 21:14:08 +01:00
6a2c863fcc [3.2.1] Update build.gradle and changelog 2019-12-15 19:05:22 +01:00
cf69273de1 [APIv1/Mobidziennik] Add API key. 2019-12-14 14:17:37 +01:00
fa99b7fd11 [3.2] Update build.gradle and changelog 2019-10-31 17:57:41 +01:00
9c5653b52e [Gradle] Fix AgendaCalendarView dependencies. 2019-10-31 17:20:07 +01:00
88ad8523a0 [UI] Disable bottom menu gain attention ripple. 2019-10-31 17:19:25 +01:00
a15f59fbd1 [API/Vulcan] Add edu.lublin.eu register support. 2019-10-31 15:40:59 +01:00
188470a043 [UI/Settings] Add option to change app language. 2019-10-28 16:33:25 +01:00
704 changed files with 29092 additions and 14554 deletions

3
.gitignore vendored
View File

@ -86,4 +86,5 @@ app/schemas/
signatures/ signatures/
app/.cxx app/.cxx
/i18n/

17
.idea/compiler.xml generated Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="1.7">
<module name="annotation" target="1.7" />
<module name="codegen" target="1.7" />
<module name="Szkolny.eu.agendacalendarview" target="1.8" />
<module name="Szkolny.eu.app" target="1.8" />
<module name="Szkolny.eu.cafebar" target="1.8" />
<module name="Szkolny.eu.material-about-library" target="1.8" />
<module name="Szkolny.eu.mhttp" target="1.8" />
<module name="Szkolny.eu.nachos" target="1.8" />
<module name="Szkolny.eu.szkolny-font" target="1.8" />
<module name="Szkolny.eu.wear" target="1.8" />
</bytecodeTargetLevel>
</component>
</project>

View File

@ -41,5 +41,15 @@
<option name="name" value="MavenRepo" /> <option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" /> <option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository> </remote-repository>
<remote-repository>
<option name="id" value="maven4" />
<option name="name" value="maven4" />
<option name="url" value="https://dl.bintray.com/undervoid/Powerpermission" />
</remote-repository>
<remote-repository>
<option name="id" value="maven4" />
<option name="name" value="maven4" />
<option name="url" value="https://dl.bintray.com/undervoid/PowerPermission" />
</remote-repository>
</component> </component>
</project> </project>

3
.idea/misc.xml generated
View File

@ -11,7 +11,6 @@
<item index="1" class="java.lang.String" itemvalue="org.greenrobot.eventbus.Subscribe" /> <item index="1" class="java.lang.String" itemvalue="org.greenrobot.eventbus.Subscribe" />
</list> </list>
</component> </component>
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="NullableNotNullManager"> <component name="NullableNotNullManager">
<option name="myDefaultNullable" value="org.jetbrains.annotations.Nullable" /> <option name="myDefaultNullable" value="org.jetbrains.annotations.Nullable" />
<option name="myDefaultNotNull" value="androidx.annotation.RecentlyNonNull" /> <option name="myDefaultNotNull" value="androidx.annotation.RecentlyNonNull" />
@ -51,7 +50,7 @@
</value> </value>
</option> </option>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="false" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

1
annotation/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

29
annotation/build.gradle Normal file
View File

@ -0,0 +1,29 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-28.
*/
apply plugin: 'java-library'
apply plugin: 'kotlin'
apply plugin: 'kotlin-kapt'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
sourceCompatibility = "7"
targetCompatibility = "7"
repositories {
mavenCentral()
}
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}

View File

@ -0,0 +1,14 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-28.
*/
package pl.szczodrzynski.edziennik.annotation
import kotlin.reflect.KClass
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
annotation class SelectiveDao(
val db: KClass<*>
)

View File

@ -0,0 +1,13 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-3-28.
*/
package pl.szczodrzynski.edziennik.annotation
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
annotation class UpdateSelective(
val primaryKeys: Array<String>,
val skippedColumns: Array<String> = []
)

View File

@ -128,6 +128,7 @@ dependencies {
implementation "com.mikepenz:iconics-core:${versions.iconics}" implementation "com.mikepenz:iconics-core:${versions.iconics}"
implementation "com.mikepenz:iconics-views:${versions.iconics}" implementation "com.mikepenz:iconics-views:${versions.iconics}"
implementation "com.mikepenz:community-material-typeface:${versions.font_cmd}@aar" implementation "com.mikepenz:community-material-typeface:${versions.font_cmd}@aar"
implementation "com.mikepenz:materialize:1.2.1"
implementation "com.github.kuba2k2:NavLib:${versions.navlib}" implementation "com.github.kuba2k2:NavLib:${versions.navlib}"
@ -144,7 +145,11 @@ dependencies {
implementation("com.github.ozodrukh:CircularReveal:2.0.1@aar") {transitive = true} implementation("com.github.ozodrukh:CircularReveal:2.0.1@aar") {transitive = true}
implementation "com.heinrichreimersoftware:material-intro:1.5.8" // do not update implementation "com.heinrichreimersoftware:material-intro:1.5.8" // do not update
implementation "com.jaredrummler:colorpicker:1.0.2" implementation "com.jaredrummler:colorpicker:1.0.2"
implementation "com.squareup.okhttp3:okhttp:3.12.2" implementation("com.squareup.okhttp3:okhttp") {
version {
strictly "3.12.2"
}
}
implementation "com.theartofdev.edmodo:android-image-cropper:2.8.0" // do not update implementation "com.theartofdev.edmodo:android-image-cropper:2.8.0" // do not update
implementation "com.wdullaer:materialdatetimepicker:4.1.2" implementation "com.wdullaer:materialdatetimepicker:4.1.2"
implementation "com.yuyh.json:jsonviewer:1.0.6" implementation "com.yuyh.json:jsonviewer:1.0.6"
@ -166,8 +171,8 @@ dependencies {
//implementation project(":Navigation") //implementation project(":Navigation")
implementation project(":szkolny-font") implementation project(":szkolny-font")
debugImplementation "com.github.ChuckerTeam.Chucker:library:3.0.1" implementation "com.github.ChuckerTeam.Chucker:library:3.0.1"
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:3.0.1" //releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:3.0.1"
//implementation 'com.github.wulkanowy:uonet-request-signer:master-SNAPSHOT' //implementation 'com.github.wulkanowy:uonet-request-signer:master-SNAPSHOT'
//implementation 'com.github.kuba2k2.uonet-request-signer:android:master-63f094b14a-1' //implementation 'com.github.kuba2k2.uonet-request-signer:android:master-63f094b14a-1'
@ -190,6 +195,21 @@ dependencies {
implementation 'com.github.jetradarmobile:android-snowfall:1.2.0' implementation 'com.github.jetradarmobile:android-snowfall:1.2.0'
implementation "io.coil-kt:coil:0.9.2" implementation "io.coil-kt:coil:0.9.2"
implementation 'com.github.kuba2k2:NumberSlidingPicker:2921225f76'
implementation project(":annotation")
kapt project(":codegen")
implementation 'com.google.android:flexbox:2.0.1'
implementation 'com.qifan.powerpermission:powerpermission:1.3.0'
implementation 'com.qifan.powerpermission:powerpermission-coroutines:1.3.0'
implementation 'com.github.kuba2k2.FSLogin:lib:master-SNAPSHOT'
implementation 'pl.droidsonroids:jspoon:1.3.2'
implementation "com.squareup.retrofit2:converter-scalars:2.8.1"
implementation "pl.droidsonroids.retrofit2:converter-jspoon:1.3.2"
} }
repositories { repositories {
mavenCentral() mavenCentral()

View File

@ -31,6 +31,12 @@
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.notifications.WidgetNotificationsProvider -keepnames class pl.szczodrzynski.edziennik.ui.widgets.notifications.WidgetNotificationsProvider
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.luckynumber.WidgetLuckyNumberProvider -keepnames class pl.szczodrzynski.edziennik.ui.widgets.luckynumber.WidgetLuckyNumberProvider
-keepnames class androidx.appcompat.view.menu.MenuBuilder { setHeaderTitleInt(java.lang.CharSequence); }
-keepclassmembernames class androidx.appcompat.view.menu.StandardMenuPopup { private *; }
-keepnames class androidx.appcompat.view.menu.MenuPopupHelper { showPopup(int, int, boolean, boolean); }
-keepclassmembernames class com.mikepenz.materialdrawer.widget.MiniDrawerSliderView { private *; }
-keep class .R -keep class .R
-keep class **.R$* { -keep class **.R$* {
<fields>; <fields>;
@ -58,5 +64,6 @@
-keep class pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing { public final byte[] pleaseStopRightNow(java.lang.String, long); } -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.** { *; } -keepclassmembers class pl.szczodrzynski.edziennik.data.api.szkolny.request.** { *; }
-keepclassmembernames class pl.szczodrzynski.edziennik.data.api.szkolny.response.** { *; } -keepclassmembers class pl.szczodrzynski.edziennik.data.api.szkolny.response.** { *; }
-keepclassmembernames class pl.szczodrzynski.edziennik.ui.modules.login.LoginInfo.Platform { *; }

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -14,6 +14,9 @@
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- PowerPermission uses minSdk 21, it's safe to override as it is used only in >= 23 -->
<uses-sdk tools:overrideLibrary="com.qifan.powerpermission.coroutines, com.qifan.powerpermission.core" />
<application <application
android:name=".App" android:name=".App"
android:allowBackup="true" android:allowBackup="true"
@ -21,7 +24,6 @@
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config" android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme.Dark" android:theme="@style/AppTheme.Dark"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
@ -62,7 +64,7 @@
android:configChanges="orientation|keyboardHidden" android:configChanges="orientation|keyboardHidden"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:noHistory="true" android:noHistory="true"
android:theme="@style/AppTheme.NoDisplay"> android:theme="@style/AppTheme.Dark.NoDisplay">
<intent-filter> <intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" /> <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter> </intent-filter>
@ -84,7 +86,7 @@
android:configChanges="orientation|keyboardHidden" android:configChanges="orientation|keyboardHidden"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:noHistory="true" android:noHistory="true"
android:theme="@style/AppTheme.NoDisplay" /> android:theme="@style/AppTheme.Dark.NoDisplay" />
<!-- NOTIFICATIONS --> <!-- NOTIFICATIONS -->
<receiver android:name=".ui.widgets.notifications.WidgetNotificationsProvider" <receiver android:name=".ui.widgets.notifications.WidgetNotificationsProvider"
android:label="@string/widget_notifications_title"> android:label="@string/widget_notifications_title">
@ -132,9 +134,6 @@
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:launchMode="singleTop" android:launchMode="singleTop"
android:theme="@style/AppTheme.Light" /> android:theme="@style/AppTheme.Light" />
<activity android:name=".ui.modules.login.LoginLibrusCaptchaActivity"
android:theme="@android:style/Theme.Dialog"
android:excludeFromRecents="true"/>
<activity android:name=".ui.modules.home.CounterActivity" <activity android:name=".ui.modules.home.CounterActivity"
android:theme="@style/AppTheme.Black" /> android:theme="@style/AppTheme.Black" />
<activity android:name=".ui.modules.feedback.FeedbackActivity" <activity android:name=".ui.modules.feedback.FeedbackActivity"

View File

@ -1,35 +1,10 @@
<h3>Wersja 4.0-beta.9, 2020-02-19</h3> <h3>Wersja 4.4.3, 2020-10-16</h3>
<ul> <ul>
<li><b>Przebudowaliśmy cały moduł synchronizacji</b>, co oznacza większą stabilność aplikacji, szybkosć oraz poprawność pobieranych danych.</li> <li>Mobidziennik: naprawione wysyłanie wiadomości.</li>
<li><b><u>Wysyłanie wiadomości</u></b> - funkcja, na którą czekał każdy. Od teraz w Szkolnym można wysyłać oraz odpowiadać na wiadomości do nauczycieli &#x1F44F;</li> <li>Vulcan: naprawione logowanie dla dzienników w Koszalinie.</li>
<li>Udoskonalony wygląd Szkolnego - sprawi, że korzystanie z aplikacji będzie jeszcze przyjemniejsze</li> <li>PPE: opcja wylogowania innych urządzeń przy logowaniu.</li>
<li>Nowa <b>Strona główna</b> - ładniejszy wygląd oraz możliwość przestawiania kart na każdym profilu</li>
<li>Nowy <b>Plan lekcji</b> - z doskonałą obsługą lekcji przesuniętych oraz dwóch lekcji o tej samej godzinie</li>
<li>Nowe okienka informacji o wydarzeniach oraz lekcjach</li>
<li>Nowe, przyjemniejsze powiadomienia</li>
<li>Łatwiejsze dodawanie własnych wydarzeń</li>
<li>Dużo poprawek w widoku <b>Wiadomości</b> oraz <b>Ogłoszeń</b></li>
<li>Częściowa <b>Obsługa dziennika EduDziennik</b></li>
<li>Librus: opcja logowania w dziennikach <b>Jednostek Samorządu Terytorialnego</b> oraz <b>Oświata w Radomiu</b></li>
<li>Librus: <b>poprawione obliczanie frekwencji</b></li>
<li>Librus: obsługa Zadań domowych bez posiadania Mobilnych dodatków (przez system Synergia)</li>
<li>Lepsze <b>przekazywanie powiadomień na komputer</b> oraz łatwiejsze parowanie</li>
<li>Poprawiliśmy synchronizację w tle na niektórych telefonach</li>
<li>Usunąłem denerwujący brak zaznaczenia w lewym menu</li>
<li>Znaczna ilość błędów z poprzednich wersji już nie występuje</li>
<li><strike>Występują natomiast nowe błędy, dlatego proszę o ich zgłaszanie :)</strike></li>
</ul> </ul>
<br> <br>
<br> <br>
<br>
<b>Uwaga.</b> Ponieważ to wersja <i>beta</i>, niektóre funkcje mogą nie działać prawidłowo.<br>
Staramy się usuwać takie przypadki, jednak na chwilę obecną mogą występować błędy w:
<ul>
<li>Wysyłanie wiadomości może nie działać w pełni prawidłowo - proszę o zgłaszanie wszystkich błędów na naszym serwerze Discord</li>
<li>Cisza nocna w powiadomieniach jeszcze nie działa.</li>
</ul>
<br>
<br>
<br>
Dzięki za korzystanie ze Szkolnego!<br> Dzięki za korzystanie ze Szkolnego!<br>
<i>&copy; Kuba Szczodrzyński, Kacper Ziubryniewicz 2020</i> <i>&copy; Kuba Szczodrzyński, Kacper Ziubryniewicz 2020</i>

View File

@ -9,7 +9,7 @@
/*secret password - removed for source code publication*/ /*secret password - removed for source code publication*/
static toys AES_IV[16] = { static toys AES_IV[16] = {
0xe3, 0x65, 0x9e, 0xe5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 0xaa, 0x6d, 0x87, 0x46, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat); unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);

View File

@ -28,9 +28,6 @@ import com.hypertrack.hyperlog.HyperLog
import com.mikepenz.iconics.Iconics import com.mikepenz.iconics.Iconics
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
import im.wangchao.mhttp.MHttp import im.wangchao.mhttp.MHttp
import im.wangchao.mhttp.internal.cookie.PersistentCookieJar
import im.wangchao.mhttp.internal.cookie.cache.SetCookieCache
import im.wangchao.mhttp.internal.cookie.persistence.SharedPrefsCookiePersistor
import kotlinx.coroutines.* import kotlinx.coroutines.*
import me.leolin.shortcutbadger.ShortcutBadger import me.leolin.shortcutbadger.ShortcutBadger
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -41,12 +38,13 @@ import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing
import pl.szczodrzynski.edziennik.data.db.AppDb import pl.szczodrzynski.edziennik.data.db.AppDb
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.network.NetworkUtils import pl.szczodrzynski.edziennik.network.NetworkUtils
import pl.szczodrzynski.edziennik.network.cookie.DumbCookieJar
import pl.szczodrzynski.edziennik.sync.SyncWorker import pl.szczodrzynski.edziennik.sync.SyncWorker
import pl.szczodrzynski.edziennik.sync.UpdateWorker import pl.szczodrzynski.edziennik.sync.UpdateWorker
import pl.szczodrzynski.edziennik.ui.modules.base.CrashActivity import pl.szczodrzynski.edziennik.ui.modules.base.CrashActivity
import pl.szczodrzynski.edziennik.utils.* import pl.szczodrzynski.edziennik.utils.*
import pl.szczodrzynski.edziennik.utils.managers.NotificationChannelsManager import pl.szczodrzynski.edziennik.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.managers.UserActionManager import pl.szczodrzynski.edziennik.utils.managers.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
@ -59,11 +57,17 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
val profileId val profileId
get() = profile.id get() = profile.id
var debugMode = false
var devMode = false var devMode = false
} }
val notificationChannelsManager by lazy { NotificationChannelsManager(this) } val notificationChannelsManager by lazy { NotificationChannelsManager(this) }
val userActionManager by lazy { UserActionManager(this) } val userActionManager by lazy { UserActionManager(this) }
val gradesManager by lazy { GradesManager(this) }
val timetableManager by lazy { TimetableManager(this) }
val eventManager by lazy { EventManager(this) }
val permissionManager by lazy { PermissionManager(this) }
val attendanceManager by lazy { AttendanceManager(this) }
val db val db
get() = App.db get() = App.db
@ -98,9 +102,9 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
.followSslRedirects(true) .followSslRedirects(true)
.retryOnConnectionFailure(true) .retryOnConnectionFailure(true)
.cookieJar(cookieJar) .cookieJar(cookieJar)
.connectTimeout(20, TimeUnit.SECONDS) .connectTimeout(15, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS)
builder.installHttpsSupport(this) builder.installHttpsSupport(this)
if (devMode || BuildConfig.DEBUG) { if (devMode || BuildConfig.DEBUG) {
@ -120,7 +124,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
.followSslRedirects(false) .followSslRedirects(false)
.build() .build()
} }
val cookieJar by lazy { PersistentCookieJar(SetCookieCache(), SharedPrefsCookiePersistor(this)) } val cookieJar by lazy { DumbCookieJar(this) }
/* _____ _ _ /* _____ _ _
/ ____(_) | | / ____(_) | |
@ -158,13 +162,20 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
Iconics.registerFont(SzkolnyFont) Iconics.registerFont(SzkolnyFont)
App.db = AppDb(this) App.db = AppDb(this)
Themes.themeInt = config.ui.theme Themes.themeInt = config.ui.theme
devMode = config.debugMode
MHttp.instance().customOkHttpClient(http) MHttp.instance().customOkHttpClient(http)
if (!profileLoadById(config.lastProfileId)) { if (!profileLoadById(config.lastProfileId)) {
db.profileDao().firstId?.let { profileLoadById(it) } db.profileDao().firstId?.let { profileLoadById(it) }
} }
devMode = "f054761fbdb6a238" == deviceId || BuildConfig.DEBUG config.ui.language?.let {
setLanguage(it)
}
debugMode = BuildConfig.DEBUG
if (BuildConfig.DEBUG)
devMode = true
Signing.getCert(this) Signing.getCert(this)
@ -174,6 +185,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
if (config.devModePassword != null) if (config.devModePassword != null)
checkDevModePassword() checkDevModePassword()
devMode = debugMode || config.debugMode
if (config.sync.enabled) if (config.sync.enabled)
SyncWorker.scheduleNext(this@App, false) SyncWorker.scheduleNext(this@App, false)
@ -246,6 +258,10 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
val pushMobidziennikApp = FirebaseApp.initializeApp( val pushMobidziennikApp = FirebaseApp.initializeApp(
this@App, this@App,
FirebaseOptions.Builder() FirebaseOptions.Builder()
.setProjectId("mobidziennik")
.setStorageBucket("mobidziennik.appspot.com")
.setDatabaseUrl("https://mobidziennik.firebaseio.com")
.setGcmSenderId("747285019373")
.setApiKey("AIzaSyCi5LmsZ5BBCQnGtrdvWnp1bWLCNP8OWQE") .setApiKey("AIzaSyCi5LmsZ5BBCQnGtrdvWnp1bWLCNP8OWQE")
.setApplicationId("1:747285019373:android:f6341bf7b158621d") .setApplicationId("1:747285019373:android:f6341bf7b158621d")
.build(), .build(),
@ -255,6 +271,10 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
val pushLibrusApp = FirebaseApp.initializeApp( val pushLibrusApp = FirebaseApp.initializeApp(
this@App, this@App,
FirebaseOptions.Builder() FirebaseOptions.Builder()
.setProjectId("synergiadru")
.setStorageBucket("synergiadru.appspot.com")
.setDatabaseUrl("https://synergiadru.firebaseio.com")
.setGcmSenderId("513056078587")
.setApiKey("AIzaSyDfTuEoYPKdv4aceEws1CO3n0-HvTndz-o") .setApiKey("AIzaSyDfTuEoYPKdv4aceEws1CO3n0-HvTndz-o")
.setApplicationId("1:513056078587:android:1e29083b760af544") .setApplicationId("1:513056078587:android:1e29083b760af544")
.build(), .build(),
@ -264,6 +284,10 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
val pushVulcanApp = FirebaseApp.initializeApp( val pushVulcanApp = FirebaseApp.initializeApp(
this@App, this@App,
FirebaseOptions.Builder() FirebaseOptions.Builder()
.setProjectId("dzienniczekplus")
.setStorageBucket("dzienniczekplus.appspot.com")
.setDatabaseUrl("https://dzienniczekplus.firebaseio.com")
.setGcmSenderId("987828170337")
.setApiKey("AIzaSyDW8MUtanHy64_I0oCpY6cOxB3jrvJd_iA") .setApiKey("AIzaSyDW8MUtanHy64_I0oCpY6cOxB3jrvJd_iA")
.setApplicationId("1:987828170337:android:ac97431a0a4578c3") .setApplicationId("1:987828170337:android:ac97431a0a4578c3")
.build(), .build(),
@ -273,10 +297,12 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
try { try {
FirebaseInstanceId.getInstance().instanceId.addOnSuccessListener { instanceIdResult -> FirebaseInstanceId.getInstance().instanceId.addOnSuccessListener { instanceIdResult ->
val token = instanceIdResult.token val token = instanceIdResult.token
d("Firebase", "Got App token: $token")
config.sync.tokenApp = token config.sync.tokenApp = token
} }
FirebaseInstanceId.getInstance(pushMobidziennikApp).instanceId.addOnSuccessListener { instanceIdResult -> FirebaseInstanceId.getInstance(pushMobidziennikApp).instanceId.addOnSuccessListener { instanceIdResult ->
val token = instanceIdResult.token val token = instanceIdResult.token
d("Firebase", "Got Mobidziennik2 token: $token")
if (token != config.sync.tokenMobidziennik) { if (token != config.sync.tokenMobidziennik) {
config.sync.tokenMobidziennik = token config.sync.tokenMobidziennik = token
config.sync.tokenMobidziennikList = listOf() config.sync.tokenMobidziennikList = listOf()
@ -284,6 +310,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
} }
FirebaseInstanceId.getInstance(pushLibrusApp).instanceId.addOnSuccessListener { instanceIdResult -> FirebaseInstanceId.getInstance(pushLibrusApp).instanceId.addOnSuccessListener { instanceIdResult ->
val token = instanceIdResult.token val token = instanceIdResult.token
d("Firebase", "Got Librus token: $token")
if (token != config.sync.tokenLibrus) { if (token != config.sync.tokenLibrus) {
config.sync.tokenLibrus = token config.sync.tokenLibrus = token
config.sync.tokenLibrusList = listOf() config.sync.tokenLibrusList = listOf()
@ -291,6 +318,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
} }
FirebaseInstanceId.getInstance(pushVulcanApp).instanceId.addOnSuccessListener { instanceIdResult -> FirebaseInstanceId.getInstance(pushVulcanApp).instanceId.addOnSuccessListener { instanceIdResult ->
val token = instanceIdResult.token val token = instanceIdResult.token
d("Firebase", "Got Vulcan token: $token")
if (token != config.sync.tokenVulcan) { if (token != config.sync.tokenVulcan) {
config.sync.tokenVulcan = token config.sync.tokenVulcan = token
config.sync.tokenVulcanList = listOf() config.sync.tokenVulcanList = listOf()
@ -336,6 +364,9 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
if (!success) { if (!success) {
EventBus.getDefault().post(ProfileListEmptyEvent()) EventBus.getDefault().post(ProfileListEmptyEvent())
} }
else {
onSuccess(profile)
}
} }
} }
fun profileSave() = profileSave(profile) fun profileSave() = profileSave(profile)

View File

@ -1,21 +0,0 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-11.
*/
package pl.szczodrzynski.edziennik;
import android.graphics.Paint;
import android.widget.TextView;
import androidx.databinding.BindingAdapter;
public class Binding {
@BindingAdapter("strikeThrough")
public static void strikeThrough(TextView textView, Boolean strikeThrough) {
if (strikeThrough) {
textView.setPaintFlags(textView.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
} else {
textView.setPaintFlags(textView.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
}
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-11.
*/
package pl.szczodrzynski.edziennik
import android.graphics.Paint
import android.widget.TextView
import androidx.databinding.BindingAdapter
object Binding {
@JvmStatic
@BindingAdapter("strikeThrough")
fun strikeThrough(textView: TextView, strikeThrough: Boolean) {
if (strikeThrough) {
textView.paintFlags = textView.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
} else {
textView.paintFlags = textView.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
}
}
}

View File

@ -2,6 +2,8 @@ package pl.szczodrzynski.edziennik
import android.Manifest import android.Manifest
import android.app.Activity import android.app.Activity
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
@ -10,6 +12,7 @@ import android.content.res.Resources
import android.database.Cursor import android.database.Cursor
import android.graphics.PorterDuff import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter import android.graphics.PorterDuffColorFilter
import android.graphics.Rect
import android.graphics.Typeface import android.graphics.Typeface
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Build import android.os.Build
@ -23,10 +26,8 @@ import android.util.Base64
import android.util.Base64.NO_WRAP import android.util.Base64.NO_WRAP
import android.util.Base64.encodeToString import android.util.Base64.encodeToString
import android.view.View import android.view.View
import android.widget.CheckBox import android.view.WindowManager
import android.widget.CompoundButton import android.widget.*
import android.widget.RadioButton
import android.widget.TextView
import androidx.annotation.* import androidx.annotation.*
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.database.getIntOrNull import androidx.core.database.getIntOrNull
@ -36,10 +37,16 @@ import androidx.core.util.forEach
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import androidx.viewpager.widget.ViewPager
import com.google.android.gms.security.ProviderInstaller import com.google.android.gms.security.ProviderInstaller
import com.google.android.material.button.MaterialButton
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.gson.JsonArray import com.google.gson.JsonArray
import com.google.gson.JsonElement import com.google.gson.JsonElement
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonParser
import im.wangchao.mhttp.Response import im.wangchao.mhttp.Response
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -50,6 +57,7 @@ import okhttp3.RequestBody
import okhttp3.TlsVersion import okhttp3.TlsVersion
import okio.Buffer import okio.Buffer
import pl.szczodrzynski.edziennik.data.api.* import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApiException import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApiException
import pl.szczodrzynski.edziennik.data.api.szkolny.response.ApiResponse import pl.szczodrzynski.edziennik.data.api.szkolny.response.ApiResponse
import pl.szczodrzynski.edziennik.data.db.entity.Notification import pl.szczodrzynski.edziennik.data.db.entity.Notification
@ -94,8 +102,8 @@ fun JsonObject?.getInt(key: String): Int? = get(key)?.let { if (it.isJsonNull) n
fun JsonObject?.getLong(key: String): Long? = get(key)?.let { if (it.isJsonNull) null else it.asLong } fun JsonObject?.getLong(key: String): Long? = get(key)?.let { if (it.isJsonNull) null else it.asLong }
fun JsonObject?.getFloat(key: String): Float? = get(key)?.let { if(it.isJsonNull) null else it.asFloat } fun JsonObject?.getFloat(key: String): Float? = get(key)?.let { if(it.isJsonNull) null else it.asFloat }
fun JsonObject?.getChar(key: String): Char? = get(key)?.let { if(it.isJsonNull) null else it.asCharacter } fun JsonObject?.getChar(key: String): Char? = get(key)?.let { if(it.isJsonNull) null else it.asCharacter }
fun JsonObject?.getJsonObject(key: String): JsonObject? = get(key)?.let { if (it.isJsonNull) null else it.asJsonObject } fun JsonObject?.getJsonObject(key: String): JsonObject? = get(key)?.let { if (it.isJsonObject) it.asJsonObject else null }
fun JsonObject?.getJsonArray(key: String): JsonArray? = get(key)?.let { if (it.isJsonNull) null else it.asJsonArray } fun JsonObject?.getJsonArray(key: String): JsonArray? = get(key)?.let { if (it.isJsonArray) it.asJsonArray else null }
fun JsonObject?.getBoolean(key: String, defaultValue: Boolean): Boolean = get(key)?.let { if (it.isJsonNull) defaultValue else it.asBoolean } ?: defaultValue fun JsonObject?.getBoolean(key: String, defaultValue: Boolean): Boolean = get(key)?.let { if (it.isJsonNull) defaultValue else it.asBoolean } ?: defaultValue
fun JsonObject?.getString(key: String, defaultValue: String): String = get(key)?.let { if (it.isJsonNull) defaultValue else it.asString } ?: defaultValue fun JsonObject?.getString(key: String, defaultValue: String): String = get(key)?.let { if (it.isJsonNull) defaultValue else it.asString } ?: defaultValue
@ -103,8 +111,19 @@ fun JsonObject?.getInt(key: String, defaultValue: Int): Int = get(key)?.let { if
fun JsonObject?.getLong(key: String, defaultValue: Long): Long = get(key)?.let { if (it.isJsonNull) defaultValue else it.asLong } ?: defaultValue fun JsonObject?.getLong(key: String, defaultValue: Long): Long = get(key)?.let { if (it.isJsonNull) defaultValue else it.asLong } ?: defaultValue
fun JsonObject?.getFloat(key: String, defaultValue: Float): Float = get(key)?.let { if(it.isJsonNull) defaultValue else it.asFloat } ?: defaultValue fun JsonObject?.getFloat(key: String, defaultValue: Float): Float = get(key)?.let { if(it.isJsonNull) defaultValue else it.asFloat } ?: defaultValue
fun JsonObject?.getChar(key: String, defaultValue: Char): Char = get(key)?.let { if(it.isJsonNull) defaultValue else it.asCharacter } ?: defaultValue fun JsonObject?.getChar(key: String, defaultValue: Char): Char = get(key)?.let { if(it.isJsonNull) defaultValue else it.asCharacter } ?: defaultValue
fun JsonObject?.getJsonObject(key: String, defaultValue: JsonObject): JsonObject = get(key)?.let { if (it.isJsonNull) defaultValue else it.asJsonObject } ?: defaultValue fun JsonObject?.getJsonObject(key: String, defaultValue: JsonObject): JsonObject = get(key)?.let { if (it.isJsonObject) it.asJsonObject else defaultValue } ?: defaultValue
fun JsonObject?.getJsonArray(key: String, defaultValue: JsonArray): JsonArray = get(key)?.let { if (it.isJsonNull) defaultValue else it.asJsonArray } ?: defaultValue fun JsonObject?.getJsonArray(key: String, defaultValue: JsonArray): JsonArray = get(key)?.let { if (it.isJsonArray) it.asJsonArray else defaultValue } ?: defaultValue
fun JsonArray.getBoolean(key: Int): Boolean? = if (key >= size()) null else get(key)?.let { if (it.isJsonNull) null else it.asBoolean }
fun JsonArray.getString(key: Int): String? = if (key >= size()) null else get(key)?.let { if (it.isJsonNull) null else it.asString }
fun JsonArray.getInt(key: Int): Int? = if (key >= size()) null else get(key)?.let { if (it.isJsonNull) null else it.asInt }
fun JsonArray.getLong(key: Int): Long? = if (key >= size()) null else get(key)?.let { if (it.isJsonNull) null else it.asLong }
fun JsonArray.getFloat(key: Int): Float? = if (key >= size()) null else get(key)?.let { if(it.isJsonNull) null else it.asFloat }
fun JsonArray.getChar(key: Int): Char? = if (key >= size()) null else get(key)?.let { if(it.isJsonNull) null else it.asCharacter }
fun JsonArray.getJsonObject(key: Int): JsonObject? = if (key >= size()) null else get(key)?.let { if (it.isJsonObject) it.asJsonObject else null }
fun JsonArray.getJsonArray(key: Int): JsonArray? = if (key >= size()) null else get(key)?.let { if (it.isJsonArray) it.asJsonArray else null }
fun String.toJsonObject(): JsonObject? = try { JsonParser().parse(this).asJsonObject } catch (ignore: Exception) { null }
operator fun JsonObject.set(key: String, value: JsonElement) = this.add(key, value) operator fun JsonObject.set(key: String, value: JsonElement) = this.add(key, value)
operator fun JsonObject.set(key: String, value: Boolean) = this.addProperty(key, value) operator fun JsonObject.set(key: String, value: Boolean) = this.addProperty(key, value)
@ -124,6 +143,10 @@ fun CharSequence?.isNotNullNorEmpty(): Boolean {
return this != null && this.isNotEmpty() return this != null && this.isNotEmpty()
} }
fun <T> Collection<T>?.isNotNullNorEmpty(): Boolean {
return this != null && this.isNotEmpty()
}
fun CharSequence?.isNotNullNorBlank(): Boolean { fun CharSequence?.isNotNullNorBlank(): Boolean {
return this != null && this.isNotBlank() return this != null && this.isNotBlank()
} }
@ -143,6 +166,13 @@ fun Bundle?.getString(key: String, defaultValue: String): String {
return this?.getString(key, defaultValue) ?: defaultValue return this?.getString(key, defaultValue) ?: defaultValue
} }
fun Bundle?.getIntOrNull(key: String): Int? {
return this?.get(key) as? Int
}
fun <T : Any> Bundle?.get(key: String): T? {
return this?.get(key) as? T?
}
/** /**
* ` The quick BROWN_fox Jumps OveR THE LAZy-DOG. ` * ` The quick BROWN_fox Jumps OveR THE LAZy-DOG. `
* *
@ -269,7 +299,7 @@ fun colorFromCssName(name: String): Int {
"orange" -> 0xffffa500 "orange" -> 0xffffa500
"black" -> 0xff000000 "black" -> 0xff000000
"white" -> 0xffffffff "white" -> 0xffffffff
else -> -1 else -> -1L
}.toInt() }.toInt()
} }
@ -425,7 +455,7 @@ operator fun MatchResult.get(group: Int): String {
return groupValues[group] return groupValues[group]
} }
fun Activity.setLanguage(language: String) { fun Context.setLanguage(language: String) {
val locale = Locale(language.toLowerCase(Locale.ROOT)) val locale = Locale(language.toLowerCase(Locale.ROOT))
val configuration = resources.configuration val configuration = resources.configuration
Locale.setDefault(locale) Locale.setDefault(locale)
@ -434,7 +464,6 @@ fun Activity.setLanguage(language: String) {
} }
configuration.locale = locale configuration.locale = locale
resources.updateConfiguration(configuration, resources.displayMetrics) resources.updateConfiguration(configuration, resources.displayMetrics)
baseContext.resources.updateConfiguration(configuration, baseContext.resources.displayMetrics)
} }
/* /*
@ -543,7 +572,7 @@ fun CharSequence?.asBoldSpannable(): Spannable {
spannable.setSpan(StyleSpan(Typeface.BOLD), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) spannable.setSpan(StyleSpan(Typeface.BOLD), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
return spannable return spannable
} }
fun CharSequence.asSpannable(vararg spans: Any, substring: String? = null, ignoreCase: Boolean = false): Spannable { fun CharSequence.asSpannable(vararg spans: Any, substring: String? = null, ignoreCase: Boolean = false, ignoreDiacritics: Boolean = false): Spannable {
val spannable = SpannableString(this) val spannable = SpannableString(this)
if (substring == null) { if (substring == null) {
spans.forEach { spans.forEach {
@ -551,17 +580,44 @@ fun CharSequence.asSpannable(vararg spans: Any, substring: String? = null, ignor
} }
} }
else if (substring.isNotEmpty()) { else if (substring.isNotEmpty()) {
var index = indexOf(substring, ignoreCase = ignoreCase) val string =
if (ignoreDiacritics)
this.cleanDiacritics()
else this
var index = string.indexOf(substring, ignoreCase = ignoreCase)
.takeIf { it != -1 } ?: indexOf(substring, ignoreCase = ignoreCase)
while (index >= 0) { while (index >= 0) {
spans.forEach { spans.forEach {
spannable.setSpan(it, index, index + substring.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) spannable.setSpan(it, index, index + substring.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
} }
index = indexOf(substring, startIndex = index + 1, ignoreCase = ignoreCase) index = string.indexOf(substring, startIndex = index + 1, ignoreCase = ignoreCase)
.takeIf { it != -1 } ?: indexOf(substring, startIndex = index + 1, ignoreCase = ignoreCase)
} }
} }
return spannable return spannable
} }
fun CharSequence.cleanDiacritics(): String {
val nameClean = StringBuilder()
forEach {
val ch = when (it) {
'ż' -> 'z'
'ó' -> 'o'
'ł' -> 'l'
'ć' -> 'c'
'ę' -> 'e'
'ś' -> 's'
'ą' -> 'a'
'ź' -> 'z'
'ń' -> 'n'
else -> it
}
nameClean.append(ch)
}
return nameClean.toString()
}
/** /**
* Returns a new read-only list only of those given elements, that are not empty. * Returns a new read-only list only of those given elements, that are not empty.
* Applies for CharSequence and descendants. * Applies for CharSequence and descendants.
@ -617,6 +673,16 @@ fun TextView.setText(@StringRes resid: Int, vararg formatArgs: Any) {
text = context.getString(resid, *formatArgs) text = context.getString(resid, *formatArgs)
} }
fun MaterialAlertDialogBuilder.setTitle(@StringRes resid: Int, vararg formatArgs: Any): MaterialAlertDialogBuilder {
setTitle(context.getString(resid, *formatArgs))
return this
}
fun MaterialAlertDialogBuilder.setMessage(@StringRes resid: Int, vararg formatArgs: Any): MaterialAlertDialogBuilder {
setMessage(context.getString(resid, *formatArgs))
return this
}
fun JsonObject(vararg properties: Pair<String, Any?>): JsonObject { fun JsonObject(vararg properties: Pair<String, Any?>): JsonObject {
return JsonObject().apply { return JsonObject().apply {
for (property in properties) { for (property in properties) {
@ -705,6 +771,13 @@ inline fun <T : View> T.onClick(crossinline onClickListener: (v: T) -> Unit) {
} }
} }
@Suppress("UNCHECKED_CAST")
inline fun <T : View> T.onLongClick(crossinline onLongClickListener: (v: T) -> Boolean) {
setOnLongClickListener { v: View ->
onLongClickListener(v as T)
}
}
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
inline fun <T : CompoundButton> T.onChange(crossinline onChangeListener: (v: T, isChecked: Boolean) -> Unit) { inline fun <T : CompoundButton> T.onChange(crossinline onChangeListener: (v: T, isChecked: Boolean) -> Unit) {
setOnCheckedChangeListener { buttonView, isChecked -> setOnCheckedChangeListener { buttonView, isChecked ->
@ -712,6 +785,19 @@ inline fun <T : CompoundButton> T.onChange(crossinline onChangeListener: (v: T,
} }
} }
@Suppress("UNCHECKED_CAST")
inline fun <T : MaterialButton> T.onChange(crossinline onChangeListener: (v: T, isChecked: Boolean) -> Unit) {
clearOnCheckedChangeListeners()
addOnCheckedChangeListener { buttonView, isChecked ->
onChangeListener(buttonView as T, isChecked)
}
}
fun View.attachToastHint(stringRes: Int) = onLongClick {
Toast.makeText(it.context, stringRes, Toast.LENGTH_SHORT).show()
true
}
fun <T> LiveData<T>.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observer<T>) { fun <T> LiveData<T>.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observer<T>) {
observe(lifecycleOwner, object : Observer<T> { observe(lifecycleOwner, object : Observer<T> {
override fun onChanged(t: T?) { override fun onChanged(t: T?) {
@ -767,7 +853,7 @@ fun View.findParentById(targetId: Int): View? {
return null return null
} }
fun CoroutineScope.startCoroutineTimer(delayMillis: Long = 0, repeatMillis: Long = 0, action: () -> Unit) = launch { fun CoroutineScope.startCoroutineTimer(delayMillis: Long = 0, repeatMillis: Long = 0, action: suspend CoroutineScope.() -> Unit) = launch {
delay(delayMillis) delay(delayMillis)
if (repeatMillis > 0) { if (repeatMillis > 0) {
while (true) { while (true) {
@ -991,6 +1077,7 @@ fun Context.getNotificationTitle(type: Int): String {
Notification.TYPE_FEEDBACK_MESSAGE -> R.string.notification_type_feedback_message Notification.TYPE_FEEDBACK_MESSAGE -> R.string.notification_type_feedback_message
Notification.TYPE_NEW_ANNOUNCEMENT -> R.string.notification_type_new_announcement Notification.TYPE_NEW_ANNOUNCEMENT -> R.string.notification_type_new_announcement
Notification.TYPE_AUTO_ARCHIVING -> R.string.notification_type_auto_archiving Notification.TYPE_AUTO_ARCHIVING -> R.string.notification_type_auto_archiving
Notification.TYPE_TEACHER_ABSENCE -> R.string.notification_type_new_teacher_absence
Notification.TYPE_GENERAL -> R.string.notification_type_general Notification.TYPE_GENERAL -> R.string.notification_type_general
else -> R.string.notification_type_general else -> R.string.notification_type_general
}) })
@ -1068,3 +1155,106 @@ fun Throwable.toErrorCode() = when (this) {
private fun ApiResponse.Error.toErrorCode() = when (this.code) { private fun ApiResponse.Error.toErrorCode() = when (this.code) {
else -> ERROR_API_EXCEPTION else -> ERROR_API_EXCEPTION
} }
fun Throwable.toApiError(tag: String) = ApiError.fromThrowable(tag, this)
inline fun <A, B, R> ifNotNull(a: A?, b: B?, code: (A, B) -> R): R? {
if (a != null && b != null) {
return code(a, b)
}
return null
}
@kotlin.jvm.JvmName("averageOrNullOfInt")
fun Iterable<Int>.averageOrNull() = this.average().let { if (it.isNaN()) null else it }
@kotlin.jvm.JvmName("averageOrNullOfFloat")
fun Iterable<Float>.averageOrNull() = this.average().let { if (it.isNaN()) null else it }
fun String.copyToClipboard(context: Context) {
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clipData = ClipData.newPlainText("Tekst", this)
clipboard.primaryClip = clipData
}
fun TextView.getTextPosition(range: IntRange): Rect {
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
// Initialize global value
var parentTextViewRect = Rect()
// Initialize values for the computing of clickedText position
//val completeText = parentTextView.text as SpannableString
val textViewLayout = this.layout
val startOffsetOfClickedText = range.first//completeText.getSpanStart(clickedText)
val endOffsetOfClickedText = range.last//completeText.getSpanEnd(clickedText)
var startXCoordinatesOfClickedText = textViewLayout.getPrimaryHorizontal(startOffsetOfClickedText)
var endXCoordinatesOfClickedText = textViewLayout.getPrimaryHorizontal(endOffsetOfClickedText)
// Get the rectangle of the clicked text
val currentLineStartOffset = textViewLayout.getLineForOffset(startOffsetOfClickedText)
val currentLineEndOffset = textViewLayout.getLineForOffset(endOffsetOfClickedText)
val keywordIsInMultiLine = currentLineStartOffset != currentLineEndOffset
textViewLayout.getLineBounds(currentLineStartOffset, parentTextViewRect)
// Update the rectangle position to his real position on screen
val parentTextViewLocation = intArrayOf(0, 0)
this.getLocationOnScreen(parentTextViewLocation)
val parentTextViewTopAndBottomOffset = (parentTextViewLocation[1] - this.scrollY + this.compoundPaddingTop)
parentTextViewRect.top += parentTextViewTopAndBottomOffset
parentTextViewRect.bottom += parentTextViewTopAndBottomOffset
// In the case of multi line text, we have to choose what rectangle take
if (keywordIsInMultiLine) {
val screenHeight = windowManager.defaultDisplay.height
val dyTop = parentTextViewRect.top
val dyBottom = screenHeight - parentTextViewRect.bottom
val onTop = dyTop > dyBottom
if (onTop) {
endXCoordinatesOfClickedText = textViewLayout.getLineRight(currentLineStartOffset);
} else {
parentTextViewRect = Rect()
textViewLayout.getLineBounds(currentLineEndOffset, parentTextViewRect);
parentTextViewRect.top += parentTextViewTopAndBottomOffset;
parentTextViewRect.bottom += parentTextViewTopAndBottomOffset;
startXCoordinatesOfClickedText = textViewLayout.getLineLeft(currentLineEndOffset);
}
}
parentTextViewRect.left += (
parentTextViewLocation[0] +
startXCoordinatesOfClickedText +
this.compoundPaddingLeft -
this.scrollX
).toInt()
parentTextViewRect.right = (
parentTextViewRect.left +
endXCoordinatesOfClickedText -
startXCoordinatesOfClickedText
).toInt()
return parentTextViewRect
}
inline fun ViewPager.addOnPageSelectedListener(crossinline block: (position: Int) -> Unit) = addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) {}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) { block(position) }
})
val SwipeRefreshLayout.onScrollListener: RecyclerView.OnScrollListener
get() = object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
if (recyclerView.canScrollVertically(-1))
this@onScrollListener.isEnabled = false
if (!recyclerView.canScrollVertically(-1) && newState == RecyclerView.SCROLL_STATE_IDLE)
this@onScrollListener.isEnabled = true
}
}
operator fun <K, V> Iterable<Pair<K, V>>.get(key: K): V? {
return firstOrNull { it.first == key }?.second
}
fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) }

View File

@ -12,16 +12,15 @@ import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Environment import android.os.Environment
import android.provider.Settings import android.provider.Settings
import android.util.Log
import android.view.Gravity import android.view.Gravity
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.graphics.ColorUtils import androidx.core.graphics.ColorUtils
import androidx.core.view.isVisible
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.navigation.NavOptions import androidx.navigation.NavOptions
import androidx.recyclerview.widget.RecyclerView
import com.danimahardhika.cafebar.CafeBar import com.danimahardhika.cafebar.CafeBar
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.mikepenz.iconics.IconicsColor import com.mikepenz.iconics.IconicsColor
@ -32,8 +31,8 @@ import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
import com.mikepenz.materialdrawer.model.DividerDrawerItem import com.mikepenz.materialdrawer.model.DividerDrawerItem
import com.mikepenz.materialdrawer.model.ProfileDrawerItem import com.mikepenz.materialdrawer.model.ProfileDrawerItem
import com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem import com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem import com.mikepenz.materialdrawer.model.interfaces.*
import com.mikepenz.materialdrawer.model.interfaces.IProfile import com.mikepenz.materialdrawer.model.utils.withIsHiddenInMiniDrawer
import kotlinx.coroutines.* import kotlinx.coroutines.*
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
@ -42,36 +41,41 @@ import pl.droidsonroids.gif.GifDrawable
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.events.* import pl.szczodrzynski.edziennik.data.api.events.*
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Metadata.* import pl.szczodrzynski.edziennik.data.db.entity.Metadata.*
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.databinding.ActivitySzkolnyBinding import pl.szczodrzynski.edziennik.databinding.ActivitySzkolnyBinding
import pl.szczodrzynski.edziennik.sync.AppManagerDetectedEvent import pl.szczodrzynski.edziennik.sync.AppManagerDetectedEvent
import pl.szczodrzynski.edziennik.sync.SyncWorker import pl.szczodrzynski.edziennik.sync.SyncWorker
import pl.szczodrzynski.edziennik.sync.UpdateWorker import pl.szczodrzynski.edziennik.sync.UpdateWorker
import pl.szczodrzynski.edziennik.ui.dialogs.RegisterUnavailableDialog
import pl.szczodrzynski.edziennik.ui.dialogs.ServerMessageDialog import pl.szczodrzynski.edziennik.ui.dialogs.ServerMessageDialog
import pl.szczodrzynski.edziennik.ui.dialogs.UpdateAvailableDialog
import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog
import pl.szczodrzynski.edziennik.ui.dialogs.settings.ProfileRemoveDialog import pl.szczodrzynski.edziennik.ui.dialogs.settings.ProfileRemoveDialog
import pl.szczodrzynski.edziennik.ui.dialogs.sync.SyncViewListDialog import pl.szczodrzynski.edziennik.ui.dialogs.sync.SyncViewListDialog
import pl.szczodrzynski.edziennik.ui.modules.agenda.AgendaFragment import pl.szczodrzynski.edziennik.ui.modules.agenda.AgendaFragment
import pl.szczodrzynski.edziennik.ui.modules.announcements.AnnouncementsFragment import pl.szczodrzynski.edziennik.ui.modules.announcements.AnnouncementsFragment
import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceFragment import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceFragment
import pl.szczodrzynski.edziennik.ui.modules.base.DebugFragment
import pl.szczodrzynski.edziennik.ui.modules.base.MainSnackbar import pl.szczodrzynski.edziennik.ui.modules.base.MainSnackbar
import pl.szczodrzynski.edziennik.ui.modules.behaviour.BehaviourFragment import pl.szczodrzynski.edziennik.ui.modules.behaviour.BehaviourFragment
import pl.szczodrzynski.edziennik.ui.modules.debug.DebugFragment
import pl.szczodrzynski.edziennik.ui.modules.debug.LabFragment
import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar import pl.szczodrzynski.edziennik.ui.modules.error.ErrorSnackbar
import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackFragment import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackFragment
import pl.szczodrzynski.edziennik.ui.modules.feedback.HelpFragment import pl.szczodrzynski.edziennik.ui.modules.feedback.HelpFragment
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesFragment import pl.szczodrzynski.edziennik.ui.modules.grades.GradesListFragment
import pl.szczodrzynski.edziennik.ui.modules.grades.editor.GradesEditorFragment import pl.szczodrzynski.edziennik.ui.modules.grades.editor.GradesEditorFragment
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment
import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment
import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity
import pl.szczodrzynski.edziennik.ui.modules.messages.MessageFragment import pl.szczodrzynski.edziennik.ui.modules.messages.MessageFragment
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesComposeFragment
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesListFragment import pl.szczodrzynski.edziennik.ui.modules.messages.compose.MessagesComposeFragment
import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsFragment import pl.szczodrzynski.edziennik.ui.modules.notifications.NotificationsListFragment
import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment import pl.szczodrzynski.edziennik.ui.modules.settings.ProfileManagerFragment
import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsNewFragment import pl.szczodrzynski.edziennik.ui.modules.settings.SettingsNewFragment
import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment import pl.szczodrzynski.edziennik.ui.modules.timetable.TimetableFragment
@ -91,7 +95,6 @@ import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem
import pl.szczodrzynski.navlib.drawer.NavDrawer import pl.szczodrzynski.navlib.drawer.NavDrawer
import pl.szczodrzynski.navlib.drawer.items.DrawerPrimaryItem import pl.szczodrzynski.navlib.drawer.items.DrawerPrimaryItem
import pl.szczodrzynski.navlib.drawer.items.withAppTitle
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.util.* import java.util.*
@ -131,6 +134,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
const val TARGET_MESSAGES_DETAILS = 503 const val TARGET_MESSAGES_DETAILS = 503
const val TARGET_MESSAGES_COMPOSE = 504 const val TARGET_MESSAGES_COMPOSE = 504
const val TARGET_WEB_PUSH = 140 const val TARGET_WEB_PUSH = 140
const val TARGET_LAB = 1000
const val HOME_ID = DRAWER_ITEM_HOME const val HOME_ID = DRAWER_ITEM_HOME
@ -155,7 +159,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
.withBadgeTypeId(TYPE_EVENT) .withBadgeTypeId(TYPE_EVENT)
.isInDrawer(true) .isInDrawer(true)
list += NavTarget(DRAWER_ITEM_GRADES, R.string.menu_grades, GradesFragment::class) list += NavTarget(DRAWER_ITEM_GRADES, R.string.menu_grades, GradesListFragment::class)
.withIcon(CommunityMaterial.Icon2.cmd_numeric_5_box_outline) .withIcon(CommunityMaterial.Icon2.cmd_numeric_5_box_outline)
.withBadgeTypeId(TYPE_GRADE) .withBadgeTypeId(TYPE_GRADE)
.isInDrawer(true) .isInDrawer(true)
@ -187,7 +191,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
// static drawer items // static drawer items
list += NavTarget(DRAWER_ITEM_NOTIFICATIONS, R.string.menu_notifications, NotificationsFragment::class) list += NavTarget(DRAWER_ITEM_NOTIFICATIONS, R.string.menu_notifications, NotificationsListFragment::class)
.withIcon(CommunityMaterial.Icon.cmd_bell_ring_outline) .withIcon(CommunityMaterial.Icon.cmd_bell_ring_outline)
.isInDrawer(true) .isInDrawer(true)
.isStatic(true) .isStatic(true)
@ -228,7 +232,14 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
list += NavTarget(TARGET_MESSAGES_DETAILS, R.string.menu_message, MessageFragment::class).withPopTo(DRAWER_ITEM_MESSAGES) list += NavTarget(TARGET_MESSAGES_DETAILS, R.string.menu_message, MessageFragment::class).withPopTo(DRAWER_ITEM_MESSAGES)
list += NavTarget(TARGET_MESSAGES_COMPOSE, R.string.menu_message_compose, MessagesComposeFragment::class) list += NavTarget(TARGET_MESSAGES_COMPOSE, R.string.menu_message_compose, MessagesComposeFragment::class)
list += NavTarget(TARGET_WEB_PUSH, R.string.menu_web_push, WebPushFragment::class) list += NavTarget(TARGET_WEB_PUSH, R.string.menu_web_push, WebPushFragment::class)
list += NavTarget(DRAWER_ITEM_DEBUG, R.string.menu_debug, DebugFragment::class) if (App.devMode) {
list += NavTarget(DRAWER_ITEM_DEBUG, R.string.menu_debug, DebugFragment::class)
list += NavTarget(TARGET_LAB, R.string.menu_lab, LabFragment::class)
.withIcon(CommunityMaterial.Icon.cmd_flask_outline)
.isInDrawer(true)
.isBelowSeparator(true)
.isStatic(true)
}
list list
} }
@ -286,11 +297,25 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
setContentView(b.root) setContentView(b.root)
Log.d(TAG, Signing.appPassword)
mainSnackbar.setCoordinator(b.navView.coordinator, b.navView.bottomBar) mainSnackbar.setCoordinator(b.navView.coordinator, b.navView.bottomBar)
errorSnackbar.setCoordinator(b.navView.coordinator, b.navView.bottomBar) errorSnackbar.setCoordinator(b.navView.coordinator, b.navView.bottomBar)
when {
BuildConfig.VERSION_NAME.contains("nightly") -> {
b.nightlyText.isVisible = true
b.nightlyText.text = "Nightly\n"+BuildConfig.VERSION_NAME.substringAfterLast(".")
}
BuildConfig.VERSION_NAME.contains("daily") -> {
b.nightlyText.isVisible = true
b.nightlyText.text = "Daily\n"+BuildConfig.VERSION_NAME.substringAfterLast(".")
}
BuildConfig.DEBUG -> {
b.nightlyText.isVisible = true
b.nightlyText.text = "Debug\n"+BuildConfig.VERSION_NAME
}
else -> b.nightlyText.isVisible = false
}
navLoading = true navLoading = true
b.navView.apply { b.navView.apply {
@ -364,7 +389,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
false false
} }
drawerProfileLongClickListener = { _, profile, _, view -> drawerProfileLongClickListener = { _, profile, _, view ->
if (profile is ProfileDrawerItem) { if (view != null && profile is ProfileDrawerItem) {
showProfileContextMenu(profile, view) showProfileContextMenu(profile, view)
true true
} }
@ -388,7 +413,20 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
} }
app.db.profileDao().all.observe(this, Observer { profiles -> app.db.profileDao().all.observe(this, Observer { profiles ->
drawer.setProfileList(profiles.filter { it.id >= 0 }.toMutableList()) val allArchived = profiles.all { it.archived }
drawer.setProfileList(profiles.filter { it.id >= 0 && (!it.archived || allArchived) }.toMutableList())
//prepend the archived profile if loaded
if (app.profile.archived && !allArchived) {
drawer.prependProfile(Profile(
id = app.profile.id,
loginStoreId = app.profile.loginStoreId,
loginStoreType = app.profile.loginStoreType,
name = app.profile.name,
subname = "Archiwum - ${app.profile.subname}"
).also {
it.archived = true
})
}
drawer.currentProfile = App.profileId drawer.currentProfile = App.profileId
}) })
@ -404,18 +442,33 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
}) })
b.swipeRefreshLayout.isEnabled = true b.swipeRefreshLayout.isEnabled = true
b.swipeRefreshLayout.setOnRefreshListener { this.syncCurrentFeature() } b.swipeRefreshLayout.setOnRefreshListener { launch { syncCurrentFeature() } }
b.swipeRefreshLayout.setColorSchemeResources( b.swipeRefreshLayout.setColorSchemeResources(
R.color.md_blue_500, R.color.md_blue_500,
R.color.md_amber_500, R.color.md_amber_500,
R.color.md_green_500 R.color.md_green_500
) )
isStoragePermissionGranted()
SyncWorker.scheduleNext(app) SyncWorker.scheduleNext(app)
UpdateWorker.scheduleNext(app) UpdateWorker.scheduleNext(app)
// if loaded profile is archived, switch to the up-to-date version of it
if (app.profile.archived) {
launch {
if (app.profile.archiveId != null) {
val profile = withContext(Dispatchers.IO) {
app.db.profileDao().getNotArchivedOf(app.profile.archiveId!!)
}
if (profile != null)
loadProfile(profile)
else
loadProfile(0)
} else {
loadProfile(0)
}
}
}
// APP BACKGROUND // APP BACKGROUND
if (app.config.ui.appBackground != null) { if (app.config.ui.appBackground != null) {
try { try {
@ -448,6 +501,8 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
// WHAT'S NEW DIALOG // WHAT'S NEW DIALOG
if (app.config.appVersion < BuildConfig.VERSION_CODE) { if (app.config.appVersion < BuildConfig.VERSION_CODE) {
// force an AppSync after update
app.config.sync.lastAppSync = 0L
ChangelogDialog(this) ChangelogDialog(this)
if (app.config.appVersion < 170) { if (app.config.appVersion < 170) {
//Intent intent = new Intent(this, ChangelogIntroActivity.class); //Intent intent = new Intent(this, ChangelogIntroActivity.class);
@ -553,7 +608,66 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|_____/ \__, |_| |_|\___| |_____/ \__, |_| |_|\___|
__/ | __/ |
|__*/ |__*/
fun syncCurrentFeature() { suspend fun syncCurrentFeature() {
if (app.profile.archived) {
MaterialAlertDialogBuilder(this)
.setTitle(R.string.profile_archived_title)
.setMessage(
R.string.profile_archived_text,
app.profile.studentSchoolYearStart,
app.profile.studentSchoolYearStart + 1
)
.setPositiveButton(R.string.ok, null)
.show()
swipeRefreshLayout.isRefreshing = false
return
}
if (app.profile.shouldArchive()) {
MaterialAlertDialogBuilder(this)
.setTitle(R.string.profile_archiving_title)
.setMessage(
R.string.profile_archiving_format,
app.profile.dateYearEnd.formattedString
)
.setPositiveButton(R.string.ok, null)
.show()
}
if (app.profile.isBeforeYear()) {
MaterialAlertDialogBuilder(this)
.setTitle(R.string.profile_year_not_started_title)
.setMessage(
R.string.profile_year_not_started_format,
app.profile.dateSemester1Start.formattedString
)
.setPositiveButton(R.string.ok, null)
.show()
swipeRefreshLayout.isRefreshing = false
return
}
app.profile.registerName?.let { registerName ->
var status = app.config.sync.registerAvailability[registerName]
if (status == null || status.nextCheckAt < currentTimeUnix()) {
withContext(Dispatchers.IO) {
val api = SzkolnyApi(app)
api.runCatching(this@MainActivity) {
val availability = getRegisterAvailability()
app.config.sync.registerAvailability = availability
status = availability[registerName]
}
}
}
if (status?.available != true
|| status?.minVersionCode ?: BuildConfig.VERSION_CODE > BuildConfig.VERSION_CODE) {
swipeRefreshLayout.isRefreshing = false
loadTarget(DRAWER_ITEM_HOME)
if (status != null)
RegisterUnavailableDialog(this, status!!)
return
}
}
swipeRefreshLayout.isRefreshing = true swipeRefreshLayout.isRefreshing = true
Toast.makeText(this, fragmentToSyncName(navTargetId), Toast.LENGTH_SHORT).show() Toast.makeText(this, fragmentToSyncName(navTargetId), Toast.LENGTH_SHORT).show()
val fragmentParam = when (navTargetId) { val fragmentParam = when (navTargetId) {
@ -567,9 +681,23 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
EdziennikTask.syncProfile( EdziennikTask.syncProfile(
App.profileId, App.profileId,
listOf(navTargetId to fragmentParam), listOf(navTargetId to fragmentParam),
arguments arguments = arguments
).enqueue(this) ).enqueue(this)
} }
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onUpdateEvent(event: Update) {
EventBus.getDefault().removeStickyEvent(event)
UpdateAvailableDialog(this, event)
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onRegisterAvailabilityEvent(event: RegisterAvailabilityEvent) {
EventBus.getDefault().removeStickyEvent(event)
app.profile.registerName?.let { registerName ->
event.data[registerName]?.let {
RegisterUnavailableDialog(this, it)
}
}
}
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)
fun onApiTaskStartedEvent(event: ApiTaskStartedEvent) { fun onApiTaskStartedEvent(event: ApiTaskStartedEvent) {
swipeRefreshLayout.isRefreshing = true swipeRefreshLayout.isRefreshing = true
@ -725,6 +853,15 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
) )
true true
} }
"createManualEvent" -> {
val date = extras.getString("eventDate")?.let { Date.fromY_m_d(it) } ?: Date.getToday()
EventManualDialog(
this,
App.profileId,
defaultDate = date
)
true
}
else -> false else -> false
} }
if (handled && !navLoading) { if (handled && !navLoading) {
@ -865,26 +1002,51 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
fun loadProfile(id: Int) = loadProfile(id, navTargetId) fun loadProfile(id: Int) = loadProfile(id, navTargetId)
fun loadProfile(id: Int, arguments: Bundle?) = loadProfile(id, navTargetId, arguments) fun loadProfile(id: Int, arguments: Bundle?) = loadProfile(id, navTargetId, arguments)
fun loadProfile(id: Int, drawerSelection: Int, arguments: Bundle? = null) { fun loadProfile(profile: Profile) = loadProfile(
profile,
navTargetId,
null,
if (app.profile.archived) app.profile.id else null
)
private fun loadProfile(id: Int, drawerSelection: Int, arguments: Bundle? = null) {
if (App.profileId == id) { if (App.profileId == id) {
drawer.currentProfile = app.profile.id drawer.currentProfile = app.profile.id
loadTarget(drawerSelection, arguments) loadTarget(drawerSelection, arguments)
return return
} }
val previousArchivedId = if (app.profile.archived) app.profile.id else null
app.profileLoad(id) { app.profileLoad(id) {
MessagesFragment.pageSelection = -1 loadProfile(it, drawerSelection, arguments, previousArchivedId)
MessagesListFragment.tapPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
MessagesListFragment.topPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
MessagesListFragment.bottomPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
setDrawerItems()
// the drawer profile is updated automatically when the drawer item is clicked
// update it manually when switching profiles from other source
//if (drawer.currentProfile != app.profile.id)
drawer.currentProfile = app.profileId
loadTarget(drawerSelection, arguments)
} }
} }
private fun loadProfile(profile: Profile, drawerSelection: Int, arguments: Bundle?, previousArchivedId: Int?) {
App.profile = profile
MessagesFragment.pageSelection = -1
setDrawerItems()
if (previousArchivedId != null) {
// prevents accidentally removing the first item if the archived profile is not shown
drawer.removeProfileById(previousArchivedId)
}
if (profile.archived) {
drawer.prependProfile(Profile(
id = profile.id,
loginStoreId = profile.loginStoreId,
loginStoreType = profile.loginStoreType,
name = profile.name,
subname = "Archiwum - ${profile.subname}"
).also {
it.archived = true
})
}
// the drawer profile is updated automatically when the drawer item is clicked
// update it manually when switching profiles from other source
//if (drawer.currentProfile != app.profile.id)
drawer.currentProfile = app.profileId
loadTarget(drawerSelection, arguments)
}
fun loadTarget(id: Int, arguments: Bundle? = null) { fun loadTarget(id: Int, arguments: Bundle? = null) {
var loadId = id var loadId = id
if (loadId == -1) { if (loadId == -1) {
@ -900,15 +1062,16 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
loadTarget(target, arguments) loadTarget(target, arguments)
} }
} }
private fun loadTarget(target: NavTarget, arguments: Bundle? = null) { private fun loadTarget(target: NavTarget, args: Bundle? = null) {
d("NavDebug", "loadTarget(target = $target, arguments = $arguments)") d("NavDebug", "loadTarget(target = $target, args = $args)")
val arguments = args ?: navBackStack.firstOrNull { it.first.id == target.id }?.second ?: Bundle()
bottomSheet.close() bottomSheet.close()
bottomSheet.removeAllContextual() bottomSheet.removeAllContextual()
bottomSheet.toggleGroupEnabled = false bottomSheet.toggleGroupEnabled = false
bottomSheet.onCloseListener = null
drawer.close() drawer.close()
drawer.setSelection(target.id, fireOnClick = false) if (drawer.getSelection() != target.id)
drawer.setSelection(target.id, fireOnClick = false)
navView.toolbar.setTitle(target.title ?: target.name) navView.toolbar.setTitle(target.title ?: target.name)
navView.bottomBar.fabEnable = false navView.bottomBar.fabEnable = false
navView.bottomBar.fabExtended = false navView.bottomBar.fabExtended = false
@ -950,6 +1113,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
navBackStack.removeAt(navBackStack.lastIndex) navBackStack.removeAt(navBackStack.lastIndex)
} }
navTarget = target navTarget = target
navArguments = arguments
return@let null return@let null
}?.let { }?.let {
@ -959,7 +1123,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
R.anim.task_open_enter, R.anim.task_open_enter,
R.anim.task_open_exit R.anim.task_open_exit
) )
navBackStack.add(navTarget to arguments) navBackStack.add(navTarget to navArguments)
navTarget = target navTarget = target
navArguments = arguments navArguments = arguments
} }
@ -1056,11 +1220,12 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
val item = DrawerPrimaryItem() val item = DrawerPrimaryItem()
.withIdentifier(target.id.toLong()) .withIdentifier(target.id.toLong())
.withName(target.name) .withName(target.name)
.withHiddenInMiniDrawer(!app.config.ui.miniMenuButtons.contains(target.id)) .withIsHiddenInMiniDrawer(!app.config.ui.miniMenuButtons.contains(target.id))
.also { if (target.description != null) it.withDescription(target.description!!) } .also { if (target.description != null) it.withDescription(target.description!!) }
.also { if (target.icon != null) it.withIcon(target.icon!!) } .also { if (target.icon != null) it.withIcon(target.icon!!) }
.also { if (target.title != null) it.withAppTitle(getString(target.title!!)) } .also { if (target.title != null) it.withAppTitle(getString(target.title!!)) }
.also { if (target.badgeTypeId != null) it.withBadgeStyle(drawer.badgeStyle)} .also { if (target.badgeTypeId != null) it.withBadgeStyle(drawer.badgeStyle)}
.withSelectedBackgroundAnimated(false)
if (target.badgeTypeId != null) if (target.badgeTypeId != null)
drawer.addUnreadCounterType(target.badgeTypeId!!, target.id) drawer.addUnreadCounterType(target.badgeTypeId!!, target.id)
@ -1120,7 +1285,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
drawer.addProfileSettings(*drawerProfiles.toTypedArray()) drawer.addProfileSettings(*drawerProfiles.toTypedArray())
} }
private fun showProfileContextMenu(profile: IProfile<*>, view: View) { private fun showProfileContextMenu(profile: IProfile, view: View) {
val profileId = profile.identifier.toInt() val profileId = profile.identifier.toInt()
val popupMenu = PopupMenu(this, view) val popupMenu = PopupMenu(this, view)
popupMenu.menu.add(0, 1, 1, R.string.profile_menu_open_settings) popupMenu.menu.add(0, 1, 1, R.string.profile_menu_open_settings)
@ -1133,7 +1298,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
} }
loadTarget(DRAWER_ITEM_SETTINGS, null) loadTarget(DRAWER_ITEM_SETTINGS, null)
} else if (item.itemId == 2) { } else if (item.itemId == 2) {
ProfileRemoveDialog(this, profileId, profile.name?.getText(this)?.toString() ?: "?") ProfileRemoveDialog(this, profileId, profile.name?.getText(this) ?: "?")
} }
true true
} }
@ -1144,7 +1309,8 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
private var targetHomeId: Int = -1 private var targetHomeId: Int = -1
override fun onBackPressed() { override fun onBackPressed() {
if (!b.navView.onBackPressed()) { if (!b.navView.onBackPressed()) {
if (App.config.ui.openDrawerOnBackPressed) { if (App.config.ui.openDrawerOnBackPressed && ((navTarget.popTo == null && navTarget.popToHome)
|| navTarget.id == DRAWER_ITEM_HOME)) {
b.navView.drawer.toggle() b.navView.drawer.toggle()
} else { } else {
navigateUp() navigateUp()

View File

@ -22,7 +22,7 @@ import kotlin.coroutines.CoroutineContext
class Config(val db: AppDb) : CoroutineScope, AbstractConfig { class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
companion object { companion object {
const val DATA_VERSION = 10 const val DATA_VERSION = 12
} }
private val job = Job() private val job = Job()
@ -75,6 +75,11 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
get() { mPrivacyPolicyAccepted = mPrivacyPolicyAccepted ?: values.get("privacyPolicyAccepted", false); return mPrivacyPolicyAccepted ?: false } get() { mPrivacyPolicyAccepted = mPrivacyPolicyAccepted ?: values.get("privacyPolicyAccepted", false); return mPrivacyPolicyAccepted ?: false }
set(value) { set("privacyPolicyAccepted", value); mPrivacyPolicyAccepted = value } set(value) { set("privacyPolicyAccepted", value); mPrivacyPolicyAccepted = value }
private var mDebugMode: Boolean? = null
var debugMode: Boolean
get() { mDebugMode = mDebugMode ?: values.get("debugMode", false); return mDebugMode ?: false }
set(value) { set("debugMode", value); mDebugMode = value }
private var mDevModePassword: String? = null private var mDevModePassword: String? = null
var devModePassword: String? var devModePassword: String?
get() { mDevModePassword = mDevModePassword ?: values.get("devModePassword", null as String?); return mDevModePassword } get() { mDevModePassword = mDevModePassword ?: values.get("devModePassword", null as String?); return mDevModePassword }
@ -100,11 +105,6 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
get() { mWidgetConfigs = mWidgetConfigs ?: values.get("widgetConfigs", JsonObject()); return mWidgetConfigs ?: JsonObject() } get() { mWidgetConfigs = mWidgetConfigs ?: values.get("widgetConfigs", JsonObject()); return mWidgetConfigs ?: JsonObject() }
set(value) { set("widgetConfigs", value); mWidgetConfigs = value } set(value) { set("widgetConfigs", value); mWidgetConfigs = value }
private var mLastAppSync: Long? = null
var lastAppSync: Long
get() { mLastAppSync = mLastAppSync ?: values.get("lastAppSync", 0L); return mLastAppSync ?: 0L }
set(value) { set("lastAppSync", value); mLastAppSync = value }
private var rawEntries: List<ConfigEntry> = db.configDao().getAllNow() private var rawEntries: List<ConfigEntry> = db.configDao().getAllNow()
private val profileConfigs: HashMap<Int, ProfileConfig> = hashMapOf() private val profileConfigs: HashMap<Int, ProfileConfig> = hashMapOf()
init { init {

View File

@ -6,17 +6,11 @@ package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.utils.managers.GradesManager
class ConfigGrades(private val config: Config) { 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 private var mOrderBy: Int? = null
var orderBy: Int var orderBy: Int
get() { mOrderBy = mOrderBy ?: config.values.get("gradesOrderBy", 0); return mOrderBy ?: ORDER_BY_DATE_DESC } get() { mOrderBy = mOrderBy ?: config.values.get("gradesOrderBy", 0); return mOrderBy ?: GradesManager.ORDER_BY_DATE_DESC }
set(value) { config.set("gradesOrderBy", value); mOrderBy = value } set(value) { config.set("gradesOrderBy", value); mOrderBy = value }
} }

View File

@ -4,11 +4,18 @@
package pl.szczodrzynski.edziennik.config package pl.szczodrzynski.edziennik.config
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import pl.szczodrzynski.edziennik.config.utils.get import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.getIntList import pl.szczodrzynski.edziennik.config.utils.getIntList
import pl.szczodrzynski.edziennik.config.utils.set import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.config.utils.setMap
import pl.szczodrzynski.edziennik.data.api.szkolny.response.RegisterAvailabilityStatus
import pl.szczodrzynski.edziennik.utils.models.Time
class ConfigSync(private val config: Config) { class ConfigSync(private val config: Config) {
private val gson = Gson()
private var mDontShowAppManagerDialog: Boolean? = null private var mDontShowAppManagerDialog: Boolean? = null
var dontShowAppManagerDialog: Boolean var dontShowAppManagerDialog: Boolean
get() { mDontShowAppManagerDialog = mDontShowAppManagerDialog ?: config.values.get("dontShowAppManagerDialog", false); return mDontShowAppManagerDialog ?: false } get() { mDontShowAppManagerDialog = mDontShowAppManagerDialog ?: config.values.get("dontShowAppManagerDialog", false); return mDontShowAppManagerDialog ?: false }
@ -19,6 +26,11 @@ class ConfigSync(private val config: Config) {
get() { mSyncEnabled = mSyncEnabled ?: config.values.get("syncEnabled", true); return mSyncEnabled ?: true } get() { mSyncEnabled = mSyncEnabled ?: config.values.get("syncEnabled", true); return mSyncEnabled ?: true }
set(value) { config.set("syncEnabled", value); mSyncEnabled = value } set(value) { config.set("syncEnabled", value); mSyncEnabled = value }
private var mWebPushEnabled: Boolean? = null
var webPushEnabled: Boolean
get() { mWebPushEnabled = mWebPushEnabled ?: config.values.get("webPushEnabled", true); return mWebPushEnabled ?: true }
set(value) { config.set("webPushEnabled", value); mWebPushEnabled = value }
private var mSyncOnlyWifi: Boolean? = null private var mSyncOnlyWifi: Boolean? = null
var onlyWifi: Boolean var onlyWifi: Boolean
get() { mSyncOnlyWifi = mSyncOnlyWifi ?: config.values.get("syncOnlyWifi", false); return mSyncOnlyWifi ?: notifyAboutUpdates } get() { mSyncOnlyWifi = mSyncOnlyWifi ?: config.values.get("syncOnlyWifi", false); return mSyncOnlyWifi ?: notifyAboutUpdates }
@ -34,20 +46,30 @@ class ConfigSync(private val config: Config) {
get() { mNotifyAboutUpdates = mNotifyAboutUpdates ?: config.values.get("notifyAboutUpdates", true); return mNotifyAboutUpdates ?: true } get() { mNotifyAboutUpdates = mNotifyAboutUpdates ?: config.values.get("notifyAboutUpdates", true); return mNotifyAboutUpdates ?: true }
set(value) { config.set("notifyAboutUpdates", value); mNotifyAboutUpdates = value } set(value) { config.set("notifyAboutUpdates", value); mNotifyAboutUpdates = value }
private var mLastAppSync: Long? = null
var lastAppSync: Long
get() { mLastAppSync = mLastAppSync ?: config.values.get("lastAppSync", 0L); return mLastAppSync ?: 0L }
set(value) { config.set("lastAppSync", value); mLastAppSync = value }
/* ____ _ _ _ /* ____ _ _ _
/ __ \ (_) | | | | / __ \ (_) | | | |
| | | |_ _ _ ___| |_ | |__ ___ _ _ _ __ ___ | | | |_ _ _ ___| |_ | |__ ___ _ _ _ __ ___
| | | | | | | |/ _ \ __| | '_ \ / _ \| | | | '__/ __| | | | | | | | |/ _ \ __| | '_ \ / _ \| | | | '__/ __|
| |__| | |_| | | __/ |_ | | | | (_) | |_| | | \__ \ | |__| | |_| | | __/ |_ | | | | (_) | |_| | | \__ \
\___\_\\__,_|_|\___|\__| |_| |_|\___/ \__,_|_| |__*/ \___\_\\__,_|_|\___|\__| |_| |_|\___/ \__,_|_| |__*/
private var mQuietHoursStart: Long? = null private var mQuietHoursEnabled: Boolean? = null
var quietHoursStart: Long var quietHoursEnabled: Boolean
get() { mQuietHoursStart = mQuietHoursStart ?: config.values.get("quietHoursStart", 0L); return mQuietHoursStart ?: 0L } get() { mQuietHoursEnabled = mQuietHoursEnabled ?: config.values.get("quietHoursEnabled", false); return mQuietHoursEnabled ?: false }
set(value) { config.set("quietHoursEnabled", value); mQuietHoursEnabled = value }
private var mQuietHoursStart: Time? = null
var quietHoursStart: Time?
get() { mQuietHoursStart = mQuietHoursStart ?: config.values.get("quietHoursStart", null as Time?); return mQuietHoursStart }
set(value) { config.set("quietHoursStart", value); mQuietHoursStart = value } set(value) { config.set("quietHoursStart", value); mQuietHoursStart = value }
private var mQuietHoursEnd: Long? = null private var mQuietHoursEnd: Time? = null
var quietHoursEnd: Long var quietHoursEnd: Time?
get() { mQuietHoursEnd = mQuietHoursEnd ?: config.values.get("quietHoursEnd", 0L); return mQuietHoursEnd ?: 0L } get() { mQuietHoursEnd = mQuietHoursEnd ?: config.values.get("quietHoursEnd", null as Time?); return mQuietHoursEnd }
set(value) { config.set("quietHoursEnd", value); mQuietHoursEnd = value } set(value) { config.set("quietHoursEnd", value); mQuietHoursEnd = value }
private var mQuietDuringLessons: Boolean? = null private var mQuietDuringLessons: Boolean? = null
@ -90,4 +112,9 @@ class ConfigSync(private val config: Config) {
var tokenVulcanList: List<Int> var tokenVulcanList: List<Int>
get() { mTokenVulcanList = mTokenVulcanList ?: config.values.getIntList("tokenVulcanList", listOf()); return mTokenVulcanList ?: listOf() } get() { mTokenVulcanList = mTokenVulcanList ?: config.values.getIntList("tokenVulcanList", listOf()); return mTokenVulcanList ?: listOf() }
set(value) { config.set("tokenVulcanList", value); mTokenVulcanList = value } set(value) { config.set("tokenVulcanList", value); mTokenVulcanList = value }
}
private var mRegisterAvailability: Map<String, RegisterAvailabilityStatus>? = null
var registerAvailability: Map<String, RegisterAvailabilityStatus>
get() { mRegisterAvailability = mRegisterAvailability ?: config.values.get("registerAvailability", null as String?)?.let { it -> gson.fromJson<Map<String, RegisterAvailabilityStatus>>(it, object: TypeToken<Map<String, RegisterAvailabilityStatus>>(){}.type) }; return mRegisterAvailability ?: mapOf() }
set(value) { config.setMap("registerAvailability", value); mRegisterAvailability = value }
}

View File

@ -7,7 +7,6 @@ package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.getIntList import pl.szczodrzynski.edziennik.config.utils.getIntList
import pl.szczodrzynski.edziennik.config.utils.set import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardModel
class ConfigUI(private val config: Config) { class ConfigUI(private val config: Config) {
private var mTheme: Int? = null private var mTheme: Int? = null
@ -45,11 +44,6 @@ class ConfigUI(private val config: Config) {
get() { mOpenDrawerOnBackPressed = mOpenDrawerOnBackPressed ?: config.values.get("openDrawerOnBackPressed", false); return mOpenDrawerOnBackPressed ?: false } get() { mOpenDrawerOnBackPressed = mOpenDrawerOnBackPressed ?: config.values.get("openDrawerOnBackPressed", false); return mOpenDrawerOnBackPressed ?: false }
set(value) { config.set("openDrawerOnBackPressed", value); mOpenDrawerOnBackPressed = value } set(value) { config.set("openDrawerOnBackPressed", value); mOpenDrawerOnBackPressed = 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 private var mSnowfall: Boolean? = null
var snowfall: Boolean var snowfall: Boolean
get() { mSnowfall = mSnowfall ?: config.values.get("snowfall", false); return mSnowfall ?: false } get() { mSnowfall = mSnowfall ?: config.values.get("snowfall", false); return mSnowfall ?: false }

View File

@ -18,7 +18,7 @@ import kotlin.coroutines.CoroutineContext
class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEntry>) : CoroutineScope, AbstractConfig { class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEntry>) : CoroutineScope, AbstractConfig {
companion object { companion object {
const val DATA_VERSION = 1 const val DATA_VERSION = 2
} }
private val job = Job() private val job = Job()
@ -29,8 +29,9 @@ class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEn
val grades by lazy { ProfileConfigGrades(this) } val grades by lazy { ProfileConfigGrades(this) }
val ui by lazy { ProfileConfigUI(this) } val ui by lazy { ProfileConfigUI(this) }
val sync by lazy { ProfileConfigSync(this) }
val attendance by lazy { ProfileConfigAttendance(this) }
/* /*
val sync by lazy { ConfigSync(this) }
val timetable by lazy { ConfigTimetable(this) } val timetable by lazy { ConfigTimetable(this) }
val grades by lazy { ConfigGrades(this) }*/ val grades by lazy { ConfigGrades(this) }*/
@ -56,4 +57,4 @@ class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEn
db.configDao().add(ConfigEntry(profileId, key, value)) db.configDao().add(ConfigEntry(profileId, key, value))
} }
} }
} }

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-4-29.
*/
package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set
class ProfileConfigAttendance(private val config: ProfileConfig) {
private var mAttendancePageSelection: Int? = null
var attendancePageSelection: Int
get() { mAttendancePageSelection = mAttendancePageSelection ?: config.values.get("attendancePageSelection", 1); return mAttendancePageSelection ?: 1 }
set(value) { config.set("attendancePageSelection", value); mAttendancePageSelection = value }
private var mUseSymbols: Boolean? = null
var useSymbols: Boolean
get() { mUseSymbols = mUseSymbols ?: config.values.get("useSymbols", false); return mUseSymbols ?: false }
set(value) { config.set("useSymbols", value); mUseSymbols = value }
private var mGroupConsecutiveDays: Boolean? = null
var groupConsecutiveDays: Boolean
get() { mGroupConsecutiveDays = mGroupConsecutiveDays ?: config.values.get("groupConsecutiveDays", true); return mGroupConsecutiveDays ?: true }
set(value) { config.set("groupConsecutiveDays", value); mGroupConsecutiveDays = value }
private var mShowPresenceInMonth: Boolean? = null
var showPresenceInMonth: Boolean
get() { mShowPresenceInMonth = mShowPresenceInMonth ?: config.values.get("showPresenceInMonth", false); return mShowPresenceInMonth ?: false }
set(value) { config.set("showPresenceInMonth", value); mShowPresenceInMonth = value }
}

View File

@ -5,9 +5,10 @@
package pl.szczodrzynski.edziennik.config package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.getFloat
import pl.szczodrzynski.edziennik.config.utils.set import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.COLOR_MODE_WEIGHTED import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_WEIGHTED
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.YEAR_ALL_GRADES import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES
class ProfileConfigGrades(private val config: ProfileConfig) { class ProfileConfigGrades(private val config: ProfileConfig) {
private var mColorMode: Int? = null private var mColorMode: Int? = null
@ -20,8 +21,37 @@ class ProfileConfigGrades(private val config: ProfileConfig) {
get() { mYearAverageMode = mYearAverageMode ?: config.values.get("yearAverageMode", YEAR_ALL_GRADES); return mYearAverageMode ?: YEAR_ALL_GRADES } get() { mYearAverageMode = mYearAverageMode ?: config.values.get("yearAverageMode", YEAR_ALL_GRADES); return mYearAverageMode ?: YEAR_ALL_GRADES }
set(value) { config.set("yearAverageMode", value); mYearAverageMode = value } set(value) { config.set("yearAverageMode", value); mYearAverageMode = value }
private var mCountZeroToAvg: Boolean? = null private var mHideImproved: Boolean? = null
var countZeroToAvg: Boolean var hideImproved: Boolean
get() { mCountZeroToAvg = mCountZeroToAvg ?: config.values.get("countZeroToAvg", true); return mCountZeroToAvg ?: true } get() { mHideImproved = mHideImproved ?: config.values.get("hideImproved", false); return mHideImproved ?: false }
set(value) { config.set("countZeroToAvg", value); mCountZeroToAvg = value } set(value) { config.set("hideImproved", value); mHideImproved = value }
private var mAverageWithoutWeight: Boolean? = null
var averageWithoutWeight: Boolean
get() { mAverageWithoutWeight = mAverageWithoutWeight ?: config.values.get("averageWithoutWeight", true); return mAverageWithoutWeight ?: true }
set(value) { config.set("averageWithoutWeight", value); mAverageWithoutWeight = value }
private var mPlusValue: Float? = null
var plusValue: Float?
get() { mPlusValue = mPlusValue ?: config.values.getFloat("plusValue"); return mPlusValue }
set(value) { config.set("plusValue", value); mPlusValue = value }
private var mMinusValue: Float? = null
var minusValue: Float?
get() { mMinusValue = mMinusValue ?: config.values.getFloat("minusValue"); return mMinusValue }
set(value) { config.set("minusValue", value); mMinusValue = value }
private var mDontCountEnabled: Boolean? = null
var dontCountEnabled: Boolean
get() { mDontCountEnabled = mDontCountEnabled ?: config.values.get("dontCountEnabled", false); return mDontCountEnabled ?: false }
set(value) { config.set("dontCountEnabled", value); mDontCountEnabled = value }
private var mDontCountGrades: List<String>? = null
var dontCountGrades: List<String>
get() { mDontCountGrades = mDontCountGrades ?: config.values.get("dontCountGrades", listOf()); return mDontCountGrades ?: listOf() }
set(value) { config.set("dontCountGrades", value); mDontCountGrades = value }
private var mHideSticksFromOld: Boolean? = null
var hideSticksFromOld: Boolean
get() { mHideSticksFromOld = mHideSticksFromOld ?: config.values.get("hideSticksFromOld", false); return mHideSticksFromOld ?: false }
set(value) { config.set("hideSticksFromOld", value); mHideSticksFromOld = value }
} }

View File

@ -0,0 +1,15 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-2-21.
*/
package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set
class ProfileConfigSync(private val config: ProfileConfig) {
private var mNotificationFilter: List<Int>? = null
var notificationFilter: List<Int>
get() { mNotificationFilter = mNotificationFilter ?: config.values.get("notificationFilter", listOf()); return mNotificationFilter ?: listOf() }
set(value) { config.set("notificationFilter", value); mNotificationFilter = value }
}

View File

@ -7,10 +7,16 @@ package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.AGENDA_DEFAULT import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.AGENDA_DEFAULT
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardModel
class ProfileConfigUI(private val config: ProfileConfig) { class ProfileConfigUI(private val config: ProfileConfig) {
private var mAgendaViewType: Int? = null private var mAgendaViewType: Int? = null
var agendaViewType: Int var agendaViewType: Int
get() { mAgendaViewType = mAgendaViewType ?: config.values.get("agendaViewType", 0); return mAgendaViewType ?: AGENDA_DEFAULT } get() { mAgendaViewType = mAgendaViewType ?: config.values.get("agendaViewType", 0); return mAgendaViewType ?: AGENDA_DEFAULT }
set(value) { config.set("agendaViewType", value); mAgendaViewType = value } 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 }
} }

View File

@ -14,6 +14,7 @@ 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_MOBIDZIENNIK
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_VULCAN import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_VULCAN
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
import kotlin.math.abs
class AppConfigMigrationV3(p: SharedPreferences, config: Config) { class AppConfigMigrationV3(p: SharedPreferences, config: Config) {
init { config.apply { init { config.apply {
@ -42,9 +43,7 @@ class AppConfigMigrationV3(p: SharedPreferences, config: Config) {
devModePassword = p.getString("$s.devModePassword", null).fix() devModePassword = p.getString("$s.devModePassword", null).fix()
sync.tokenApp = p.getString("$s.fcmToken", null).fix() sync.tokenApp = p.getString("$s.fcmToken", null).fix()
timetable.bellSyncMultiplier = p.getString("$s.bellSyncMultiplier", null)?.toIntOrNull() ?: 0 timetable.bellSyncMultiplier = p.getString("$s.bellSyncMultiplier", null)?.toIntOrNull() ?: 0
sync.quietHoursStart = p.getString("$s.quietHoursStart", null)?.toLongOrNull() ?: 0
appRateSnackbarTime = p.getString("$s.appRateSnackbarTime", 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 timetable.countInSeconds = p.getString("$s.countInSeconds", null)?.toBoolean() ?: false
ui.headerBackground = p.getString("$s.headerBackground", null).fix() ui.headerBackground = p.getString("$s.headerBackground", null).fix()
ui.appBackground = p.getString("$s.appBackground", null).fix() ui.appBackground = p.getString("$s.appBackground", null).fix()
@ -59,6 +58,22 @@ class AppConfigMigrationV3(p: SharedPreferences, config: Config) {
sync.notifyAboutUpdates = p.getString("$s.notifyAboutUpdates", null)?.toBoolean() ?: true sync.notifyAboutUpdates = p.getString("$s.notifyAboutUpdates", null)?.toBoolean() ?: true
timetable.bellSyncDiff = p.getString("$s.bellSyncDiff", null)?.let { Gson().fromJson(it, Time::class.java) } timetable.bellSyncDiff = p.getString("$s.bellSyncDiff", null)?.let { Gson().fromJson(it, Time::class.java) }
val startMillis = p.getString("$s.quietHoursStart", null)?.toLongOrNull() ?: 0
val endMillis = p.getString("$s.quietHoursEnd", null)?.toLongOrNull() ?: 0
if (startMillis > 0) {
try {
sync.quietHoursStart = Time.fromMillis(abs(startMillis))
sync.quietHoursEnd = Time.fromMillis(abs(endMillis))
sync.quietHoursEnabled = true
}
catch (_: Exception) {}
}
else {
sync.quietHoursEnabled = false
sync.quietHoursStart = null
sync.quietHoursEnd = null
}
sync.tokenMobidziennikList = listOf() sync.tokenMobidziennikList = listOf()
sync.tokenVulcanList = listOf() sync.tokenVulcanList = listOf()
sync.tokenLibrusList = listOf() sync.tokenLibrusList = listOf()
@ -78,4 +93,4 @@ class AppConfigMigrationV3(p: SharedPreferences, config: Config) {
private fun String?.fix(): String? { private fun String?.fix(): String? {
return this?.replace("\"", "")?.let { if (it == "null") null else it } return this?.replace("\"", "")?.let { if (it == "null") null else it }
} }
} }

View File

@ -49,6 +49,9 @@ fun AbstractConfig.setIntList(key: String, value: List<Int>?) {
fun AbstractConfig.setLongList(key: String, value: List<Long>?) { fun AbstractConfig.setLongList(key: String, value: List<Long>?) {
set(key, value?.let { gson.toJson(it) }) set(key, value?.let { gson.toJson(it) })
} }
fun <K, V> AbstractConfig.setMap(key: String, value: Map<K, V>?) {
set(key, value?.let { gson.toJson(it) })
}
fun HashMap<String, String?>.get(key: String, default: String?): String? { fun HashMap<String, String?>.get(key: String, default: String?): String? {
return this[key] ?: default return this[key] ?: default
@ -94,10 +97,14 @@ fun HashMap<String, String?>.getLongList(key: String, default: List<Long>?): Lis
return this[key]?.let { gson.fromJson<List<Long>>(it, object: TypeToken<List<Long>>(){}.type) } ?: default return this[key]?.let { gson.fromJson<List<Long>>(it, object: TypeToken<List<Long>>(){}.type) } ?: default
} }
fun HashMap<String, String?>.getFloat(key: String): Float? {
return this[key]?.toFloatOrNull()
}
fun List<ConfigEntry>.toHashMap(profileId: Int, map: HashMap<String, String?>) { fun List<ConfigEntry>.toHashMap(profileId: Int, map: HashMap<String, String?>) {
map.clear() map.clear()
forEach { forEach {
if (it.profileId == profileId) if (it.profileId == profileId)
map[it.key] = it.value map[it.key] = it.value
} }
} }

View File

@ -10,7 +10,9 @@ import pl.szczodrzynski.edziennik.BuildConfig
import pl.szczodrzynski.edziennik.HOUR import pl.szczodrzynski.edziennik.HOUR
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.config.Config import pl.szczodrzynski.edziennik.config.Config
import pl.szczodrzynski.edziennik.config.ConfigGrades.Companion.ORDER_BY_DATE_DESC import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.ORDER_BY_DATE_DESC
import pl.szczodrzynski.edziennik.utils.models.Time
import kotlin.math.abs
class ConfigMigration(app: App, config: Config) { class ConfigMigration(app: App, config: Config) {
init { config.apply { init { config.apply {
@ -43,8 +45,9 @@ class ConfigMigration(app: App, config: Config) {
sync.interval = 1*HOUR.toInt() sync.interval = 1*HOUR.toInt()
sync.notifyAboutUpdates = true sync.notifyAboutUpdates = true
sync.onlyWifi = false sync.onlyWifi = false
sync.quietHoursStart = 0 sync.quietHoursEnabled = false
sync.quietHoursEnd = 0 sync.quietHoursStart = null
sync.quietHoursEnd = null
sync.quietDuringLessons = false sync.quietDuringLessons = false
sync.tokenApp = null sync.tokenApp = null
sync.tokenMobidziennik = null sync.tokenMobidziennik = null
@ -61,14 +64,47 @@ class ConfigMigration(app: App, config: Config) {
dataVersion = 2 dataVersion = 2
} }
if (dataVersion < 3) {
update = null
privacyPolicyAccepted = false
debugMode = false
devModePassword = null
appInstalledTime = 0L
appRateSnackbarTime = 0L
dataVersion = 3
}
if (dataVersion < 10) { if (dataVersion < 10) {
ui.openDrawerOnBackPressed = false ui.openDrawerOnBackPressed = false
ui.homeCards = listOf()
ui.snowfall = false ui.snowfall = false
ui.bottomSheetOpened = false ui.bottomSheetOpened = false
sync.dontShowAppManagerDialog = false sync.dontShowAppManagerDialog = false
sync.webPushEnabled = true
sync.lastAppSync = 0L
dataVersion = 10 dataVersion = 10
} }
if (dataVersion < 11) {
val startMillis = config.values.get("quietHoursStart", 0L)
val endMillis = config.values.get("quietHoursEnd", 0L)
if (startMillis > 0) {
try {
sync.quietHoursStart = Time.fromMillis(abs(startMillis))
sync.quietHoursEnd = Time.fromMillis(abs(endMillis))
sync.quietHoursEnabled = true
}
catch (_: Exception) {}
}
else {
sync.quietHoursEnabled = false
sync.quietHoursStart = null
sync.quietHoursEnd = null
}
dataVersion = 11
}
}} }}
} }

View File

@ -5,20 +5,33 @@
package pl.szczodrzynski.edziennik.config.utils package pl.szczodrzynski.edziennik.config.utils
import pl.szczodrzynski.edziennik.config.ProfileConfig import pl.szczodrzynski.edziennik.config.ProfileConfig
import pl.szczodrzynski.edziennik.data.db.entity.Notification
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.AGENDA_DEFAULT import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.AGENDA_DEFAULT
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.COLOR_MODE_WEIGHTED import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_WEIGHTED
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.YEAR_ALL_GRADES import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES
class ProfileConfigMigration(config: ProfileConfig) { class ProfileConfigMigration(config: ProfileConfig) {
init { config.apply { init { config.apply {
if (dataVersion < 1) { if (dataVersion < 1) {
grades.colorMode = COLOR_MODE_WEIGHTED grades.colorMode = COLOR_MODE_WEIGHTED
grades.countZeroToAvg = true
grades.yearAverageMode = YEAR_ALL_GRADES grades.yearAverageMode = YEAR_ALL_GRADES
grades.hideImproved = false
grades.averageWithoutWeight = true
grades.plusValue = null
grades.minusValue = null
grades.dontCountEnabled = false
grades.dontCountGrades = listOf()
ui.agendaViewType = AGENDA_DEFAULT ui.agendaViewType = AGENDA_DEFAULT
// no migration for ui.homeCards
dataVersion = 1 dataVersion = 1
} }
if (dataVersion < 2) {
sync.notificationFilter = sync.notificationFilter + Notification.TYPE_TEACHER_ABSENCE
dataVersion = 2
}
}} }}
} }

View File

@ -22,6 +22,7 @@ import pl.szczodrzynski.edziennik.data.api.task.ErrorReportTask
import pl.szczodrzynski.edziennik.data.api.task.IApiTask import pl.szczodrzynski.edziennik.data.api.task.IApiTask
import pl.szczodrzynski.edziennik.data.api.task.SzkolnyTask import pl.szczodrzynski.edziennik.data.api.task.SzkolnyTask
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.toApiError
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
import kotlin.math.min import kotlin.math.min
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -37,6 +38,9 @@ class ApiService : Service() {
context.startService(Intent(context, ApiService::class.java)) context.startService(Intent(context, ApiService::class.java))
EventBus.getDefault().postSticky(request) EventBus.getDefault().postSticky(request)
} }
var lastEventTime = System.currentTimeMillis()
var taskCancelTries = 0
} }
private val app by lazy { applicationContext as App } private val app by lazy { applicationContext as App }
@ -63,9 +67,6 @@ class ApiService : Service() {
private val notification by lazy { EdziennikNotification(app) } private val notification by lazy { EdziennikNotification(app) }
private var lastEventTime = System.currentTimeMillis()
private var taskCancelTries = 0
/* ______ _ _ _ _ _____ _ _ _ _ /* ______ _ _ _ _ _____ _ _ _ _
| ____| | | (_) (_) | / ____| | | | | | | | ____| | | (_) (_) | / ____| | | | | | |
| |__ __| |_____ ___ _ __ _ __ _| | __ | | __ _| | | |__ __ _ ___| | __ | |__ __| |_____ ___ _ __ _ __ _| | __ | | __ _| | | |__ __ _ ___| | __
@ -181,7 +182,7 @@ class ApiService : Service() {
is SzkolnyTask -> task.run(taskCallback) is SzkolnyTask -> task.run(taskCallback)
} }
} catch (e: Exception) { } catch (e: Exception) {
taskCallback.onError(ApiError(TAG, EXCEPTION_API_TASK).withThrowable(e)) taskCallback.onError(e.toApiError(TAG))
} }
} }

View File

@ -22,16 +22,16 @@ const val FAKE_LIBRUS_TOKEN = "https://librus.szkolny.eu/access_token.php"
const val FAKE_LIBRUS_ACCOUNT = "/synergia_accounts_fresh.php?login=" const val FAKE_LIBRUS_ACCOUNT = "/synergia_accounts_fresh.php?login="
const val FAKE_LIBRUS_ACCOUNTS = "/synergia_accounts.php" const val FAKE_LIBRUS_ACCOUNTS = "/synergia_accounts.php"
val LIBRUS_USER_AGENT = "$SYSTEM_USER_AGENT LibrusMobileApp" val LIBRUS_USER_AGENT = "${SYSTEM_USER_AGENT}LibrusMobileApp"
const val SYNERGIA_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Gecko/20100101 Firefox/62.0" const val SYNERGIA_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Gecko/20100101 Firefox/62.0"
const val LIBRUS_CLIENT_ID = "wmSyUMo8llDAs4y9tJVYY92oyZ6h4lAt7KCuy0Gv" const val LIBRUS_CLIENT_ID = "0RbsDOkV9tyKEQYzlLv5hs3DM1ukrynFI4p6C1Yc"
const val LIBRUS_REDIRECT_URL = "http://localhost/bar" const val LIBRUS_REDIRECT_URL = "app://librus"
const val LIBRUS_AUTHORIZE_URL = "https://portal.librus.pl/oauth2/authorize?client_id=$LIBRUS_CLIENT_ID&redirect_uri=$LIBRUS_REDIRECT_URL&response_type=code" const val LIBRUS_AUTHORIZE_URL = "https://portal.librus.pl/oauth2/authorize?client_id=$LIBRUS_CLIENT_ID&redirect_uri=$LIBRUS_REDIRECT_URL&response_type=code"
const val LIBRUS_LOGIN_URL = "https://portal.librus.pl/rodzina/login/action" const val LIBRUS_LOGIN_URL = "https://portal.librus.pl/rodzina/login/action"
const val LIBRUS_TOKEN_URL = "https://portal.librus.pl/oauth2/access_token" const val LIBRUS_TOKEN_URL = "https://portal.librus.pl/oauth2/access_token"
const val LIBRUS_ACCOUNT_URL = "/v2/SynergiaAccounts/fresh/" // + login const val LIBRUS_ACCOUNT_URL = "/v3/SynergiaAccounts/fresh/" // + login
const val LIBRUS_ACCOUNTS_URL = "/v2/SynergiaAccounts" const val LIBRUS_ACCOUNTS_URL = "/v3/SynergiaAccounts"
/** https://api.librus.pl/2.0 */ /** https://api.librus.pl/2.0 */
const val LIBRUS_API_URL = "https://api.librus.pl/2.0" const val LIBRUS_API_URL = "https://api.librus.pl/2.0"
@ -56,10 +56,14 @@ const val LIBRUS_SYNERGIA_TOKEN_LOGIN_URL = "https://synergia.librus.pl/loguj/to
const val LIBRUS_MESSAGES_URL = "https://wiadomosci.librus.pl/module" const val LIBRUS_MESSAGES_URL = "https://wiadomosci.librus.pl/module"
const val LIBRUS_SANDBOX_URL = "https://sandbox.librus.pl/index.php?action=" const val LIBRUS_SANDBOX_URL = "https://sandbox.librus.pl/index.php?action="
const val LIBRUS_SYNERGIA_HOMEWORK_ATTACHMENT_URL = "https://synergia.librus.pl/homework/downloadFile"
const val LIBRUS_SYNERGIA_MESSAGES_ATTACHMENT_URL = "https://synergia.librus.pl/wiadomosci/pobierz_zalacznik"
const val IDZIENNIK_USER_AGENT = SYNERGIA_USER_AGENT const val IDZIENNIK_USER_AGENT = SYNERGIA_USER_AGENT
const val IDZIENNIK_WEB_URL = "https://iuczniowie.progman.pl/idziennik" const val IDZIENNIK_WEB_URL = "https://iuczniowie.progman.pl/idziennik"
const val IDZIENNIK_WEB_LOGIN = "login.aspx" const val IDZIENNIK_WEB_LOGIN = "login.aspx"
const val IDZIENNIK_WEB_SETTINGS = "mod_panelRodzica/Ustawienia.aspx" const val IDZIENNIK_WEB_SETTINGS = "mod_panelRodzica/Ustawienia.aspx"
const val IDZIENNIK_WEB_HOME = "mod_panelRodzica/StronaGlowna.aspx"
const val IDZIENNIK_WEB_TIMETABLE = "mod_panelRodzica/plan/WS_Plan.asmx/pobierzPlanZajec" const val IDZIENNIK_WEB_TIMETABLE = "mod_panelRodzica/plan/WS_Plan.asmx/pobierzPlanZajec"
const val IDZIENNIK_WEB_GRADES = "mod_panelRodzica/oceny/WS_ocenyUcznia.asmx/pobierzOcenyUcznia" 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_MISSING_GRADES = "mod_panelRodzica/brak_ocen/WS_BrakOcenUcznia.asmx/pobierzBrakujaceOcenyUcznia"
@ -73,6 +77,8 @@ const val IDZIENNIK_WEB_GET_MESSAGE = "mod_komunikator/WS_wiadomosci.asmx/Pobier
const val IDZIENNIK_WEB_GET_RECIPIENT_LIST = "mod_komunikator/WS_wiadomosci.asmx/pobierzListeOdbiorcowPanelRodzic" const val IDZIENNIK_WEB_GET_RECIPIENT_LIST = "mod_komunikator/WS_wiadomosci.asmx/pobierzListeOdbiorcowPanelRodzic"
const val IDZIENNIK_WEB_SEND_MESSAGE = "mod_komunikator/WS_wiadomosci.asmx/WyslijWiadomosc" const val IDZIENNIK_WEB_SEND_MESSAGE = "mod_komunikator/WS_wiadomosci.asmx/WyslijWiadomosc"
const val IDZIENNIK_WEB_GET_ATTACHMENT = "mod_komunikator/Download.ashx" const val IDZIENNIK_WEB_GET_ATTACHMENT = "mod_komunikator/Download.ashx"
const val IDZIENNIK_WEB_GET_HOMEWORK = "mod_panelRodzica/pracaDomowa/WS_pracaDomowa.asmx/pobierzJednaPraceDomowa"
const val IDZIENNIK_WEB_GET_HOMEWORK_ATTACHMENT = "mod_panelRodzica/pracaDomowa.aspx"
val IDZIENNIK_API_USER_AGENT = SYSTEM_USER_AGENT val IDZIENNIK_API_USER_AGENT = SYSTEM_USER_AGENT
const val IDZIENNIK_API_URL = "https://iuczniowie.progman.pl/idziennik/api" const val IDZIENNIK_API_URL = "https://iuczniowie.progman.pl/idziennik/api"
@ -86,7 +92,7 @@ val MOBIDZIENNIK_USER_AGENT = SYSTEM_USER_AGENT
const val VULCAN_API_USER_AGENT = "MobileUserAgent" const val VULCAN_API_USER_AGENT = "MobileUserAgent"
const val VULCAN_API_APP_NAME = "VULCAN-Android-ModulUcznia" const val VULCAN_API_APP_NAME = "VULCAN-Android-ModulUcznia"
const val VULCAN_API_APP_VERSION = "19.4.1.436" const val VULCAN_API_APP_VERSION = "20.5.1.470"
const val VULCAN_API_PASSWORD = "CE75EA598C7743AD9B0B7328DED85B06" const val VULCAN_API_PASSWORD = "CE75EA598C7743AD9B0B7328DED85B06"
const val VULCAN_API_PASSWORD_FAKELOG = "012345678901234567890123456789AB" const val VULCAN_API_PASSWORD_FAKELOG = "012345678901234567890123456789AB"
val VULCAN_API_DEVICE_NAME = "Szkolny.eu ${Build.MODEL}" val VULCAN_API_DEVICE_NAME = "Szkolny.eu ${Build.MODEL}"
@ -106,5 +112,14 @@ const val VULCAN_API_ENDPOINT_MESSAGES_SENT = "mobile-api/Uczen.v3.Uczen/Wiadomo
const val VULCAN_API_ENDPOINT_MESSAGES_CHANGE_STATUS = "mobile-api/Uczen.v3.Uczen/ZmienStatusWiadomosci" const val VULCAN_API_ENDPOINT_MESSAGES_CHANGE_STATUS = "mobile-api/Uczen.v3.Uczen/ZmienStatusWiadomosci"
const val VULCAN_API_ENDPOINT_MESSAGES_ADD = "mobile-api/Uczen.v3.Uczen/DodajWiadomosc" const val VULCAN_API_ENDPOINT_MESSAGES_ADD = "mobile-api/Uczen.v3.Uczen/DodajWiadomosc"
const val VULCAN_API_ENDPOINT_PUSH = "mobile-api/Uczen.v3.Uczen/UstawPushToken" const val VULCAN_API_ENDPOINT_PUSH = "mobile-api/Uczen.v3.Uczen/UstawPushToken"
const val VULCAN_API_ENDPOINT_MESSAGES_ATTACHMENTS = "mobile-api/Uczen.v3.Uczen/WiadomosciZalacznik"
const val VULCAN_API_ENDPOINT_HOMEWORK_ATTACHMENTS = "mobile-api/Uczen.v3.Uczen/ZadaniaDomoweZalacznik"
const val VULCAN_WEB_ENDPOINT_LUCKY_NUMBER = "Start.mvc/GetKidsLuckyNumbers"
const val VULCAN_WEB_ENDPOINT_REGISTER_DEVICE = "RejestracjaUrzadzeniaToken.mvc/Get"
const val EDUDZIENNIK_USER_AGENT = "Szkolny.eu/${BuildConfig.VERSION_NAME}" const val EDUDZIENNIK_USER_AGENT = "Szkolny.eu/${BuildConfig.VERSION_NAME}"
const val PODLASIE_API_VERSION = "1.0.62"
const val PODLASIE_API_URL = "https://cpdklaser.zeto.bialystok.pl/api"
const val PODLASIE_API_USER_ENDPOINT = "/pobierzDaneUcznia"
const val PODLASIE_API_LOGOUT_DEVICES_ENDPOINT = "/wyczyscUrzadzenia"

View File

@ -8,11 +8,12 @@ import android.app.Notification
import android.app.NotificationManager import android.app.NotificationManager
import android.app.PendingIntent import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.PRIORITY_MIN import androidx.core.app.NotificationCompat.PRIORITY_MIN
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.Bundle
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.receivers.SzkolnyReceiver
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -35,16 +36,18 @@ class EdziennikNotification(val app: App) {
var serviceClosed = false var serviceClosed = false
private fun cancelPendingIntent(taskId: Int): PendingIntent { private fun cancelPendingIntent(taskId: Int): PendingIntent {
val intent = Intent("pl.szczodrzynski.edziennik.SZKOLNY_MAIN") val intent = SzkolnyReceiver.getIntent(app, Bundle(
intent.putExtra("task", "TaskCancelRequest") "task" to "TaskCancelRequest",
intent.putExtra("taskId", taskId) "taskId" to taskId
return PendingIntent.getBroadcast(app, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT) as PendingIntent ))
return PendingIntent.getBroadcast(app, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) as PendingIntent
} }
private val closePendingIntent: PendingIntent private val closePendingIntent: PendingIntent
get() { get() {
val intent = Intent("pl.szczodrzynski.edziennik.SZKOLNY_MAIN") val intent = SzkolnyReceiver.getIntent(app, Bundle(
intent.putExtra("task", "ServiceCloseRequest") "task" to "ServiceCloseRequest"
return PendingIntent.getBroadcast(app, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT) as PendingIntent ))
return PendingIntent.getBroadcast(app, 0, intent, 0) as PendingIntent
} }
private fun errorCountText(): String? { private fun errorCountText(): String? {

View File

@ -7,7 +7,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.EndpointTimer
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_NEVER import pl.szczodrzynski.edziennik.data.db.entity.SYNC_NEVER
fun Data.prepare(loginMethods: List<LoginMethod>, features: List<Feature>, featureIds: List<Int>, viewId: Int?) { fun Data.prepare(loginMethods: List<LoginMethod>, features: List<Feature>, featureIds: List<Int>, viewId: Int?, onlyEndpoints: List<Int>?) {
val data = this val data = this
val possibleLoginMethods = data.loginMethods.toMutableList() val possibleLoginMethods = data.loginMethods.toMutableList()
@ -46,13 +46,18 @@ fun Data.prepare(loginMethods: List<LoginMethod>, features: List<Feature>, featu
// add all endpoint IDs and required login methods, filtering using timers // add all endpoint IDs and required login methods, filtering using timers
.onEach { feature -> .onEach { feature ->
feature.endpointIds.forEach { endpoint -> feature.endpointIds.forEach { endpoint ->
if (onlyEndpoints?.contains(endpoint.first) == false)
return@forEach
(data.endpointTimers (data.endpointTimers
.singleOrNull { it.endpointId == endpoint.first } ?: EndpointTimer(data.profile?.id .singleOrNull { it.endpointId == endpoint.first } ?: EndpointTimer(data.profile?.id
?: -1, endpoint.first)) ?: -1, endpoint.first))
.let { timer -> .let { timer ->
if (timer.nextSync == SYNC_ALWAYS || if (
(viewId != null && timer.viewId == viewId) || onlyEndpoints?.contains(endpoint.first) == true ||
(timer.nextSync != SYNC_NEVER && timer.nextSync < timestamp)) { timer.nextSync == SYNC_ALWAYS ||
viewId != null && timer.viewId == viewId ||
timer.nextSync != SYNC_NEVER && timer.nextSync < timestamp
) {
data.targetEndpointIds[endpoint.first] = timer.lastSync data.targetEndpointIds[endpoint.first] = timer.lastSync
requiredLoginMethods.add(endpoint.second) requiredLoginMethods.add(endpoint.second)
} }

View File

@ -122,6 +122,12 @@ const val ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN = 180
const val ERROR_LIBRUS_API_MAINTENANCE = 181 const val ERROR_LIBRUS_API_MAINTENANCE = 181
const val ERROR_LIBRUS_PORTAL_MAINTENANCE = 182 const val ERROR_LIBRUS_PORTAL_MAINTENANCE = 182
const val ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM = 183 const val ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM = 183
const val ERROR_LOGIN_LIBRUS_PORTAL_CSRF_EXPIRED = 184
const val ERROR_LIBRUS_API_DEVICE_REGISTERED = 185
const val ERROR_LIBRUS_MESSAGES_NOT_FOUND = 186
const val ERROR_LOGIN_LIBRUS_API_INVALID_REQUEST = 187
const val ERROR_LIBRUS_MESSAGES_ATTACHMENT_NOT_FOUND = 188
const val ERROR_LOGIN_LIBRUS_MESSAGES_TIMEOUT = 189
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN = 201 const val ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN = 201
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD = 202 const val ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD = 202
@ -138,6 +144,7 @@ const val ERROR_MOBIDZIENNIK_WEB_INVALID_RESPONSE = 214
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_NO_SESSION_ID = 215 const val ERROR_LOGIN_MOBIDZIENNIK_WEB_NO_SESSION_ID = 215
const val ERROR_LOGIN_MOBIDZIENNIK_API2_INVALID_LOGIN = 216 const val ERROR_LOGIN_MOBIDZIENNIK_API2_INVALID_LOGIN = 216
const val ERROR_LOGIN_MOBIDZIENNIK_API2_OTHER = 217 const val ERROR_LOGIN_MOBIDZIENNIK_API2_OTHER = 217
const val ERROR_MOBIDZIENNIK_WEB_SERVER_PROBLEM = 218
const val ERROR_LOGIN_VULCAN_INVALID_SYMBOL = 301 const val ERROR_LOGIN_VULCAN_INVALID_SYMBOL = 301
const val ERROR_LOGIN_VULCAN_INVALID_TOKEN = 302 const val ERROR_LOGIN_VULCAN_INVALID_TOKEN = 302
@ -152,6 +159,17 @@ const val ERROR_LOGIN_VULCAN_NO_PUPILS = 331
const val ERROR_VULCAN_API_MAINTENANCE = 340 const val ERROR_VULCAN_API_MAINTENANCE = 340
const val ERROR_VULCAN_API_BAD_REQUEST = 341 const val ERROR_VULCAN_API_BAD_REQUEST = 341
const val ERROR_VULCAN_API_OTHER = 342 const val ERROR_VULCAN_API_OTHER = 342
const val ERROR_VULCAN_ATTACHMENT_DOWNLOAD = 343
const val ERROR_VULCAN_WEB_DATA_MISSING = 344
const val ERROR_VULCAN_WEB_429 = 345
const val ERROR_VULCAN_WEB_OTHER = 346
const val ERROR_VULCAN_WEB_NO_CERTIFICATE = 347
const val ERROR_VULCAN_WEB_NO_REGISTER = 348
const val ERROR_VULCAN_WEB_CERTIFICATE_EXPIRED = 349
const val ERROR_VULCAN_WEB_LOGGED_OUT = 350
const val ERROR_VULCAN_WEB_CERTIFICATE_POST_FAILED = 351
const val ERROR_VULCAN_WEB_GRADUATE_ACCOUNT = 352
const val ERROR_VULCAN_WEB_NO_SCHOOLS = 353
const val ERROR_LOGIN_IDZIENNIK_WEB_INVALID_LOGIN = 401 const val ERROR_LOGIN_IDZIENNIK_WEB_INVALID_LOGIN = 401
const val ERROR_LOGIN_IDZIENNIK_WEB_INVALID_SCHOOL_NAME = 402 const val ERROR_LOGIN_IDZIENNIK_WEB_INVALID_SCHOOL_NAME = 402
@ -182,6 +200,12 @@ const val ERROR_EDUDZIENNIK_WEB_LIMITED_ACCESS = 521
const val ERROR_EDUDZIENNIK_WEB_SESSION_EXPIRED = 522 const val ERROR_EDUDZIENNIK_WEB_SESSION_EXPIRED = 522
const val ERROR_EDUDZIENNIK_WEB_TEAM_MISSING = 530 const val ERROR_EDUDZIENNIK_WEB_TEAM_MISSING = 530
const val ERROR_LOGIN_PODLASIE_API_INVALID_TOKEN = 601
const val ERROR_LOGIN_PODLASIE_API_DEVICE_LIMIT = 602
const val ERROR_PODLASIE_API_NO_TOKEN = 630
const val ERROR_PODLASIE_API_OTHER = 631
const val ERROR_PODLASIE_API_DATA_MISSING = 632
const val ERROR_TEMPLATE_WEB_OTHER = 801 const val ERROR_TEMPLATE_WEB_OTHER = 801
const val EXCEPTION_API_TASK = 900 const val EXCEPTION_API_TASK = 900
@ -201,5 +225,9 @@ const val EXCEPTION_IDZIENNIK_WEB_API_REQUEST = 913
const val EXCEPTION_IDZIENNIK_API_REQUEST = 914 const val EXCEPTION_IDZIENNIK_API_REQUEST = 914
const val EXCEPTION_EDUDZIENNIK_WEB_REQUEST = 920 const val EXCEPTION_EDUDZIENNIK_WEB_REQUEST = 920
const val EXCEPTION_EDUDZIENNIK_FILE_REQUEST = 921 const val EXCEPTION_EDUDZIENNIK_FILE_REQUEST = 921
const val ERROR_ONEDRIVE_DOWNLOAD = 930
const val EXCEPTION_VULCAN_WEB_LOGIN = 931
const val EXCEPTION_VULCAN_WEB_REQUEST = 932
const val EXCEPTION_PODLASIE_API_REQUEST = 940
const val LOGIN_NO_ARGUMENTS = 1201 const val LOGIN_NO_ARGUMENTS = 1201

View File

@ -13,8 +13,8 @@ import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOME
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_SENT
internal const val FEATURE_TIMETABLE = 1 internal const val FEATURE_TIMETABLE = 1
internal const val FEATURE_AGENDA = 2 internal const val FEATURE_AGENDA = 2

View File

@ -13,9 +13,11 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginPor
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginSynergia import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginSynergia
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.login.MobidziennikLoginApi2 import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.login.MobidziennikLoginApi2
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.login.MobidziennikLoginWeb import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.login.MobidziennikLoginWeb
import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.login.PodlasieLoginApi
import pl.szczodrzynski.edziennik.data.api.edziennik.template.login.TemplateLoginApi import pl.szczodrzynski.edziennik.data.api.edziennik.template.login.TemplateLoginApi
import pl.szczodrzynski.edziennik.data.api.edziennik.template.login.TemplateLoginWeb 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.edziennik.vulcan.login.VulcanLoginApi
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.login.VulcanLoginWebMain
import pl.szczodrzynski.edziennik.data.api.models.LoginMethod import pl.szczodrzynski.edziennik.data.api.models.LoginMethod
// librus // librus
@ -27,7 +29,6 @@ import pl.szczodrzynski.edziennik.data.api.models.LoginMethod
const val SYNERGIA_API_ENABLED = false const val SYNERGIA_API_ENABLED = false
const val LOGIN_TYPE_IDZIENNIK = 3 const val LOGIN_TYPE_IDZIENNIK = 3
const val LOGIN_TYPE_TEMPLATE = 21 const val LOGIN_TYPE_TEMPLATE = 21
@ -70,13 +71,13 @@ val librusLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_SYNERGIA, LibrusLoginSynergia::class.java) LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_SYNERGIA, LibrusLoginSynergia::class.java)
.withIsPossible { _, loginStore -> !loginStore.hasLoginData("fakeLogin") } .withIsPossible { _, loginStore -> !loginStore.hasLoginData("fakeLogin") }
.withRequiredLoginMethod { profile, _ -> .withRequiredLoginMethod { profile, _ ->
if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_API else LOGIN_METHOD_NOT_NEEDED if (profile?.hasStudentData("accountPassword") == false || true) LOGIN_METHOD_LIBRUS_API else LOGIN_METHOD_NOT_NEEDED
}, },
LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_MESSAGES, LibrusLoginMessages::class.java) LoginMethod(LOGIN_TYPE_LIBRUS, LOGIN_METHOD_LIBRUS_MESSAGES, LibrusLoginMessages::class.java)
.withIsPossible { _, loginStore -> !loginStore.hasLoginData("fakeLogin") } .withIsPossible { _, loginStore -> !loginStore.hasLoginData("fakeLogin") }
.withRequiredLoginMethod { profile, _ -> .withRequiredLoginMethod { profile, _ ->
if (profile?.hasStudentData("accountPassword") == false) LOGIN_METHOD_LIBRUS_SYNERGIA else LOGIN_METHOD_NOT_NEEDED if (profile?.hasStudentData("accountPassword") == false || true) LOGIN_METHOD_LIBRUS_SYNERGIA else LOGIN_METHOD_NOT_NEEDED
} }
) )
@ -103,11 +104,11 @@ const val LOGIN_METHOD_VULCAN_WEB_OLD = 300
const val LOGIN_METHOD_VULCAN_WEB_MESSAGES = 400 const val LOGIN_METHOD_VULCAN_WEB_MESSAGES = 400
const val LOGIN_METHOD_VULCAN_API = 500 const val LOGIN_METHOD_VULCAN_API = 500
val vulcanLoginMethods = listOf( val vulcanLoginMethods = listOf(
/*LoginMethod(LOGIN_TYPE_VULCAN, LOGIN_METHOD_VULCAN_WEB_MAIN, VulcanLoginWebMain::class.java) LoginMethod(LOGIN_TYPE_VULCAN, LOGIN_METHOD_VULCAN_WEB_MAIN, VulcanLoginWebMain::class.java)
.withIsPossible { _, _ -> false } .withIsPossible { _, loginStore -> loginStore.hasLoginData("webHost") }
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }, .withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED },
LoginMethod(LOGIN_TYPE_VULCAN, LOGIN_METHOD_VULCAN_WEB_NEW, VulcanLoginWebNew::class.java) /*LoginMethod(LOGIN_TYPE_VULCAN, LOGIN_METHOD_VULCAN_WEB_NEW, VulcanLoginWebNew::class.java)
.withIsPossible { _, _ -> false } .withIsPossible { _, _ -> false }
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_VULCAN_WEB_MAIN }, .withRequiredLoginMethod { _, _ -> LOGIN_METHOD_VULCAN_WEB_MAIN },
@ -118,7 +119,7 @@ val vulcanLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_VULCAN, LOGIN_METHOD_VULCAN_API, VulcanLoginApi::class.java) LoginMethod(LOGIN_TYPE_VULCAN, LOGIN_METHOD_VULCAN_API, VulcanLoginApi::class.java)
.withIsPossible { _, _ -> true } .withIsPossible { _, _ -> true }
.withRequiredLoginMethod { _, loginStore -> .withRequiredLoginMethod { _, loginStore ->
if (loginStore.mode == LOGIN_MODE_VULCAN_WEB) LOGIN_METHOD_VULCAN_WEB_NEW else LOGIN_METHOD_NOT_NEEDED if (loginStore.mode == LOGIN_MODE_VULCAN_WEB) LOGIN_METHOD_VULCAN_WEB_MAIN else LOGIN_METHOD_NOT_NEEDED
} }
) )
@ -133,6 +134,7 @@ val idziennikLoginMethods = listOf(
) )
const val LOGIN_TYPE_EDUDZIENNIK = 5 const val LOGIN_TYPE_EDUDZIENNIK = 5
const val LOGIN_MODE_EDUDZIENNIK_WEB = 0
const val LOGIN_METHOD_EDUDZIENNIK_WEB = 100 const val LOGIN_METHOD_EDUDZIENNIK_WEB = 100
val edudziennikLoginMethods = listOf( val edudziennikLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_EDUDZIENNIK, LOGIN_METHOD_EDUDZIENNIK_WEB, EdudziennikLoginWeb::class.java) LoginMethod(LOGIN_TYPE_EDUDZIENNIK, LOGIN_METHOD_EDUDZIENNIK_WEB, EdudziennikLoginWeb::class.java)
@ -140,6 +142,15 @@ val edudziennikLoginMethods = listOf(
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED } .withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }
) )
const val LOGIN_TYPE_PODLASIE = 6
const val LOGIN_MODE_PODLASIE_API = 0
const val LOGIN_METHOD_PODLASIE_API = 100
val podlasieLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_PODLASIE, LOGIN_METHOD_PODLASIE_API, PodlasieLoginApi::class.java)
.withIsPossible { _, _ -> true }
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }
)
val templateLoginMethods = listOf( val templateLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_TEMPLATE, LOGIN_METHOD_TEMPLATE_WEB, TemplateLoginWeb::class.java) LoginMethod(LOGIN_TYPE_TEMPLATE, LOGIN_METHOD_TEMPLATE_WEB, TemplateLoginWeb::class.java)
.withIsPossible { _, _ -> true } .withIsPossible { _, _ -> true }

View File

@ -5,12 +5,17 @@
package pl.szczodrzynski.edziennik.data.api package pl.szczodrzynski.edziennik.data.api
import kotlin.text.RegexOption.DOT_MATCHES_ALL import kotlin.text.RegexOption.DOT_MATCHES_ALL
import kotlin.text.RegexOption.IGNORE_CASE
object Regexes { object Regexes {
val STYLE_CSS_COLOR by lazy { val STYLE_CSS_COLOR by lazy {
"""color: (\w+);?""".toRegex() """color: (\w+);?""".toRegex()
} }
val NOT_DIGITS by lazy {
"""[^0-9]""".toRegex()
}
val MOBIDZIENNIK_GRADES_SUBJECT_NAME by lazy { val MOBIDZIENNIK_GRADES_SUBJECT_NAME by lazy {
@ -39,7 +44,7 @@ object Regexes {
"""\(([0-9A-ząęóżźńśłć]*?)\)$""".toRegex(DOT_MATCHES_ALL) """\(([0-9A-ząęóżźńśłć]*?)\)$""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_LUCKY_NUMBER by lazy { val MOBIDZIENNIK_LUCKY_NUMBER by lazy {
"""class="szczesliwy_numerek".*>0*([0-9]+)(?:/0*[0-9]+)*</a>""".toRegex(DOT_MATCHES_ALL) """class="szczesliwy_numerek".*?>0?([0-9]+)/?0?([0-9]+)?</a>""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_CLASS_CALENDAR by lazy { val MOBIDZIENNIK_CLASS_CALENDAR by lazy {
"""events: (.+),$""".toRegex(RegexOption.MULTILINE) """events: (.+),$""".toRegex(RegexOption.MULTILINE)
@ -67,6 +72,9 @@ object Regexes {
} }
val MOBIDZIENNIK_ATTENDANCE_TYPES by lazy {
"""Legenda:.+?normal;">(.+?)</span>""".toRegex(DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_ATTENDANCE_TABLE by lazy { val MOBIDZIENNIK_ATTENDANCE_TABLE by lazy {
"""<table .+?id="obecnosci_tabela">(.+?)</table>""".toRegex(DOT_MATCHES_ALL) """<table .+?id="obecnosci_tabela">(.+?)</table>""".toRegex(DOT_MATCHES_ALL)
} }
@ -76,11 +84,36 @@ object Regexes {
val MOBIDZIENNIK_ATTENDANCE_ENTRIES by lazy { val MOBIDZIENNIK_ATTENDANCE_ENTRIES by lazy {
"""font-size:.+?class=".*?">(.*?)</td>""".toRegex(DOT_MATCHES_ALL) """font-size:.+?class=".*?">(.*?)</td>""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_ATTENDANCE_COLUMNS by lazy {
"""<tr><td class="border-right1".+?/td>(.+?)</tr>""".toRegex(DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_ATTENDANCE_COLUMN by lazy {
"""(<td.+?>)(.*?)</td>""".toRegex(DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_ATTENDANCE_COLUMN_SPAN by lazy {
"""colspan="(\d+)"""".toRegex()
}
val MOBIDZIENNIK_ATTENDANCE_RANGE by lazy { val MOBIDZIENNIK_ATTENDANCE_RANGE by lazy {
"""<span>([0-9:]+) - .+? (.+?)</span></a>""".toRegex(DOT_MATCHES_ALL) """<span>([0-9:]+) - .+? (.+?)</span></a>""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_ATTENDANCE_LESSON by lazy { val MOBIDZIENNIK_ATTENDANCE_LESSON by lazy {
"""<strong>(.+?) - (.*?)</strong>.+?<small>.+?\((.+?), .+?(.+?)\)""".toRegex(DOT_MATCHES_ALL) """<strong>(.+?)</strong>\s*<small>\s*\((.+?),\s*(.+?)\)""".toRegex(DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_HOMEWORK_ROW by lazy {
"""class="rowRolling">(.+?</div>\s*</td>)""".toRegex(DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_HOMEWORK_ITEM by lazy {
"""<p><b>(.+?):</b>\s*(.+?)\s*</p>""".toRegex(DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_HOMEWORK_BODY by lazy {
"""Treść:</b>(.+?)<p><b>""".toRegex(DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_HOMEWORK_ID by lazy {
"""zadanieFormularz\(([0-9]+),""".toRegex(DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_HOMEWORK_ATTACHMENT by lazy {
"""zalacznik(_zadania)?=([0-9]+)'.+?word-break">(.+?)</td>""".toRegex(DOT_MATCHES_ALL)
} }
@ -109,17 +142,33 @@ object Regexes {
val IDZIENNIK_MESSAGES_RECIPIENT_PARENT by lazy { val IDZIENNIK_MESSAGES_RECIPIENT_PARENT by lazy {
"""(.+?)\s\((.+)\)""".toRegex() """(.+?)\s\((.+)\)""".toRegex()
} }
/*<span id="ctl00_spanSzczesliwyLos">Szczęśliwy los na dzisiaj to <b>19</b>. Los na jutro to <b>22</b></span>*/
val IDZIENNIK_WEB_LUCKY_NUMBER by lazy {
"""dzisiaj to <b>([0-9]+)</b>""".toRegex()
}
val IDZIENNIK_WEB_SELECTED_REGISTER by lazy {
"""selected="selected" value="([0-9]+)" data-id-ucznia""".toRegex()
}
val VULCAN_SHIFT_ANNOTATION by lazy { val VULCAN_SHIFT_ANNOTATION by lazy {
"""\(przeniesiona (z|na) lekcj[ię] ([0-9]+), (.+)\)""".toRegex() """\(przeniesiona (z|na) lekcj[ię] ([0-9]+), (.+)\)""".toRegex()
} }
val VULCAN_WEB_PERMISSIONS by lazy {
"""permissions: '([A-z0-9/=+\-_]+?)'""".toRegex()
}
val VULCAN_WEB_SYMBOL_VALIDATE by lazy {
"""[A-z0-9]+""".toRegex(IGNORE_CASE)
}
val LIBRUS_ATTACHMENT_KEY by lazy { val LIBRUS_ATTACHMENT_KEY by lazy {
"""singleUseKey=([0-9A-f_]+)""".toRegex() """singleUseKey=([0-9A-z_]+)""".toRegex()
}
val LIBRUS_MESSAGE_ID by lazy {
"""/wiadomosci/[0-9]+/[0-9]+/([0-9]+?)/""".toRegex()
} }
@ -147,6 +196,9 @@ object Regexes {
val EDUDZIENNIK_ANNOUNCEMENT_DESCRIPTION by lazy { val EDUDZIENNIK_ANNOUNCEMENT_DESCRIPTION by lazy {
"""<div class="desc">.*?<p>(.*?)</p>""".toRegex(DOT_MATCHES_ALL) """<div class="desc">.*?<p>(.*?)</p>""".toRegex(DOT_MATCHES_ALL)
} }
val EDUDZIENNIK_HOMEWORK_DESCRIPTION by lazy {
"""<div class="desc">(.*?)</div>""".toRegex(DOT_MATCHES_ALL)
}
val EDUDZIENNIK_SUBJECT_ID by lazy { val EDUDZIENNIK_SUBJECT_ID by lazy {
"""/Courses/([\w-_]+?)/""".toRegex() """/Courses/([\w-_]+?)/""".toRegex()
@ -192,4 +244,20 @@ object Regexes {
val EDUDZIENNIK_TEACHERS by lazy { val EDUDZIENNIK_TEACHERS by lazy {
"""<div class="teacher">.*?<p>(.+?) (.+?)</p>""".toRegex(DOT_MATCHES_ALL) """<div class="teacher">.*?<p>(.+?) (.+?)</p>""".toRegex(DOT_MATCHES_ALL)
} }
val LINKIFY_DATE_YMD by lazy {
"""(1\d{3}|20\d{2})[\-./](1[0-2]|0?\d)[\-./]([1-2]\d|3[0-1]|0?\d)""".toRegex()
}
val LINKIFY_DATE_DMY by lazy {
"""(?<![\d\-./])([1-2]\d|3[0-1]|0?\d)[\-./](1[0-2]|0?\d)(?:[\-./](1\d{3}|2?0?\d{2}))?(?![\d\-/])""".toRegex()
}
val LINKIFY_DATE_ABSOLUTE by lazy {
"""([1-3][0-9]|[1-9])\s(sty|lut|mar|kwi|maj|cze|lip|sie|wrz|paź|lis|gru).*?\s(1[0-9]{3}|20[0-9]{2})?""".toRegex(IGNORE_CASE)
}
val LINKIFY_DATE_RELATIVE by lazy {
"""za\s([0-9]+)?\s?(dni|dzień|tydzień|tygodnie)""".toRegex(IGNORE_CASE)
}
} }

View File

@ -5,39 +5,48 @@
package pl.szczodrzynski.edziennik.data.api.edziennik package pl.szczodrzynski.edziennik.data.api.edziennik
import com.google.gson.JsonObject import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.App import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.* import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.Edudziennik import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.Edudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.Idziennik import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.Idziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.Librus import pl.szczodrzynski.edziennik.data.api.edziennik.librus.Librus
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.Mobidziennik import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.Mobidziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.podlasie.Podlasie
import pl.szczodrzynski.edziennik.data.api.edziennik.template.Template import pl.szczodrzynski.edziennik.data.api.edziennik.template.Template
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.Vulcan import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.Vulcan
import pl.szczodrzynski.edziennik.data.api.events.RegisterAvailabilityEvent
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
import pl.szczodrzynski.edziennik.data.api.task.IApiTask import pl.szczodrzynski.edziennik.data.api.task.IApiTask
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.utils.Utils.d
open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTask(profileId) { open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTask(profileId) {
companion object { companion object {
private const val TAG = "EdziennikTask" private const val TAG = "EdziennikTask"
var profile: Profile? = null
var loginStore: LoginStore? = null
fun firstLogin(loginStore: LoginStore) = EdziennikTask(-1, FirstLoginRequest(loginStore)) fun firstLogin(loginStore: LoginStore) = EdziennikTask(-1, FirstLoginRequest(loginStore))
fun sync() = EdziennikTask(-1, SyncRequest()) fun sync() = EdziennikTask(-1, SyncRequest())
fun syncProfile(profileId: Int, viewIds: List<Pair<Int, Int>>? = null, arguments: JsonObject? = null) = EdziennikTask(profileId, SyncProfileRequest(viewIds, arguments)) fun syncProfile(profileId: Int, viewIds: List<Pair<Int, Int>>? = null, onlyEndpoints: List<Int>? = null, arguments: JsonObject? = null) = EdziennikTask(profileId, SyncProfileRequest(viewIds, onlyEndpoints, arguments))
fun syncProfileList(profileList: List<Int>) = EdziennikTask(-1, SyncProfileListRequest(profileList)) fun syncProfileList(profileList: List<Int>) = EdziennikTask(-1, SyncProfileListRequest(profileList))
fun messageGet(profileId: Int, message: MessageFull) = EdziennikTask(profileId, MessageGetRequest(message)) fun messageGet(profileId: Int, message: MessageFull) = EdziennikTask(profileId, MessageGetRequest(message))
fun messageSend(profileId: Int, recipients: List<Teacher>, subject: String, text: String) = EdziennikTask(profileId, MessageSendRequest(recipients, subject, text)) fun messageSend(profileId: Int, recipients: List<Teacher>, subject: String, text: String) = EdziennikTask(profileId, MessageSendRequest(recipients, subject, text))
fun announcementsRead(profileId: Int) = EdziennikTask(profileId, AnnouncementsReadRequest()) fun announcementsRead(profileId: Int) = EdziennikTask(profileId, AnnouncementsReadRequest())
fun announcementGet(profileId: Int, announcement: AnnouncementFull) = EdziennikTask(profileId, AnnouncementGetRequest(announcement)) fun announcementGet(profileId: Int, announcement: AnnouncementFull) = EdziennikTask(profileId, AnnouncementGetRequest(announcement))
fun attachmentGet(profileId: Int, message: Message, attachmentId: Long, attachmentName: String) = EdziennikTask(profileId, AttachmentGetRequest(message, attachmentId, attachmentName)) fun attachmentGet(profileId: Int, owner: Any, attachmentId: Long, attachmentName: String) = EdziennikTask(profileId, AttachmentGetRequest(owner, attachmentId, attachmentName))
fun recipientListGet(profileId: Int) = EdziennikTask(profileId, RecipientListGetRequest()) fun recipientListGet(profileId: Int) = EdziennikTask(profileId, RecipientListGetRequest())
fun eventGet(profileId: Int, event: EventFull) = EdziennikTask(profileId, EventGetRequest(event))
} }
private lateinit var loginStore: LoginStore private lateinit var loginStore: LoginStore
@ -58,21 +67,65 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
// save the profile ID and name as the current task's // save the profile ID and name as the current task's
taskName = app.getString(R.string.edziennik_notification_api_sync_title_format, profile.name) taskName = app.getString(R.string.edziennik_notification_api_sync_title_format, profile.name)
} }
EdziennikTask.profile = this.profile
EdziennikTask.loginStore = this.loginStore
} }
private var edziennikInterface: EdziennikInterface? = null private var edziennikInterface: EdziennikInterface? = null
internal fun run(app: App, taskCallback: EdziennikCallback) { internal fun run(app: App, taskCallback: EdziennikCallback) {
if (profile?.archived == true) { profile?.let { profile ->
taskCallback.onError(ApiError(TAG, ERROR_PROFILE_ARCHIVED)) if (profile.archived) {
return d(TAG, "The profile $profileId is archived")
taskCallback.onError(ApiError(TAG, ERROR_PROFILE_ARCHIVED))
return
}
else if (profile.shouldArchive()) {
d(TAG, "The profile $profileId's year ended on ${profile.dateYearEnd}, archiving")
ProfileArchiver(app, profile)
}
if (profile.isBeforeYear()) {
d(TAG, "The profile $profileId's school year has not started yet; aborting sync")
cancel()
taskCallback.onCompleted()
return
}
profile.registerName?.let { registerName ->
var status = app.config.sync.registerAvailability[registerName]
if (status == null || status.nextCheckAt < currentTimeUnix()) {
val api = SzkolnyApi(app)
api.runCatching({
val availability = getRegisterAvailability()
app.config.sync.registerAvailability = availability
status = availability[registerName]
}, onError = {
taskCallback.onError(it.toApiError(TAG))
return
})
}
if (status?.available != true
|| status?.minVersionCode ?: BuildConfig.VERSION_CODE > BuildConfig.VERSION_CODE) {
if (EventBus.getDefault().hasSubscriberForEvent(RegisterAvailabilityEvent::class.java)) {
EventBus.getDefault().postSticky(
RegisterAvailabilityEvent(app.config.sync.registerAvailability)
)
}
cancel()
taskCallback.onCompleted()
return
}
}
} }
edziennikInterface = when (loginStore.type) { edziennikInterface = when (loginStore.type) {
LOGIN_TYPE_LIBRUS -> Librus(app, profile, loginStore, taskCallback) LOGIN_TYPE_LIBRUS -> Librus(app, profile, loginStore, taskCallback)
LOGIN_TYPE_MOBIDZIENNIK -> Mobidziennik(app, profile, loginStore, taskCallback) LOGIN_TYPE_MOBIDZIENNIK -> Mobidziennik(app, profile, loginStore, taskCallback)
LOGIN_TYPE_VULCAN -> Vulcan(app, profile, loginStore, taskCallback) LOGIN_TYPE_VULCAN -> Vulcan(app, profile, loginStore, taskCallback)
LOGIN_TYPE_IDZIENNIK -> Idziennik(app, profile, loginStore, taskCallback) LOGIN_TYPE_IDZIENNIK -> Idziennik(app, profile, loginStore, taskCallback)
LOGIN_TYPE_EDUDZIENNIK -> Edudziennik(app, profile, loginStore, taskCallback) LOGIN_TYPE_EDUDZIENNIK -> Edudziennik(app, profile, loginStore, taskCallback)
LOGIN_TYPE_PODLASIE -> Podlasie(app, profile, loginStore, taskCallback)
LOGIN_TYPE_TEMPLATE -> Template(app, profile, loginStore, taskCallback) LOGIN_TYPE_TEMPLATE -> Template(app, profile, loginStore, taskCallback)
else -> null else -> null
} }
@ -85,18 +138,21 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
featureIds = request.viewIds?.flatMap { Features.getIdsByView(it.first, it.second) } featureIds = request.viewIds?.flatMap { Features.getIdsByView(it.first, it.second) }
?: Features.getAllIds(), ?: Features.getAllIds(),
viewId = request.viewIds?.get(0)?.first, viewId = request.viewIds?.get(0)?.first,
onlyEndpoints = request.onlyEndpoints,
arguments = request.arguments) arguments = request.arguments)
is MessageGetRequest -> edziennikInterface?.getMessage(request.message) is MessageGetRequest -> edziennikInterface?.getMessage(request.message)
is MessageSendRequest -> edziennikInterface?.sendMessage(request.recipients, request.subject, request.text) is MessageSendRequest -> edziennikInterface?.sendMessage(request.recipients, request.subject, request.text)
is FirstLoginRequest -> edziennikInterface?.firstLogin() is FirstLoginRequest -> edziennikInterface?.firstLogin()
is AnnouncementsReadRequest -> edziennikInterface?.markAllAnnouncementsAsRead() is AnnouncementsReadRequest -> edziennikInterface?.markAllAnnouncementsAsRead()
is AnnouncementGetRequest -> edziennikInterface?.getAnnouncement(request.announcement) is AnnouncementGetRequest -> edziennikInterface?.getAnnouncement(request.announcement)
is AttachmentGetRequest -> edziennikInterface?.getAttachment(request.message, request.attachmentId, request.attachmentName) is AttachmentGetRequest -> edziennikInterface?.getAttachment(request.owner, request.attachmentId, request.attachmentName)
is RecipientListGetRequest -> edziennikInterface?.getRecipientList() is RecipientListGetRequest -> edziennikInterface?.getRecipientList()
is EventGetRequest -> edziennikInterface?.getEvent(request.event)
} }
} }
override fun cancel() { override fun cancel() {
d(TAG, "Task ${toString()} cancelling...")
edziennikInterface?.cancel() edziennikInterface?.cancel()
} }
@ -106,12 +162,13 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
data class FirstLoginRequest(val loginStore: LoginStore) data class FirstLoginRequest(val loginStore: LoginStore)
class SyncRequest class SyncRequest
data class SyncProfileRequest(val viewIds: List<Pair<Int, Int>>? = null, val arguments: JsonObject? = null) data class SyncProfileRequest(val viewIds: List<Pair<Int, Int>>? = null, val onlyEndpoints: List<Int>? = null, val arguments: JsonObject? = null)
data class SyncProfileListRequest(val profileList: List<Int>) data class SyncProfileListRequest(val profileList: List<Int>)
data class MessageGetRequest(val message: MessageFull) data class MessageGetRequest(val message: MessageFull)
data class MessageSendRequest(val recipients: List<Teacher>, val subject: String, val text: String) data class MessageSendRequest(val recipients: List<Teacher>, val subject: String, val text: String)
class AnnouncementsReadRequest class AnnouncementsReadRequest
data class AnnouncementGetRequest(val announcement: AnnouncementFull) data class AnnouncementGetRequest(val announcement: AnnouncementFull)
data class AttachmentGetRequest(val message: Message, val attachmentId: Long, val attachmentName: String) data class AttachmentGetRequest(val owner: Any, val attachmentId: Long, val attachmentName: String)
class RecipientListGetRequest class RecipientListGetRequest
data class EventGetRequest(val event: EventFull)
} }

View File

@ -0,0 +1,97 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-8-25.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik
import android.content.Intent
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.Intent
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.models.Date
class ProfileArchiver(val app: App, val profile: Profile) {
companion object {
private const val TAG = "ProfileArchiver"
}
init {
if (profile.archiveId == null)
profile.archiveId = profile.id
d(TAG, "Processing ${profile.name}#${profile.id}, archiveId = ${profile.archiveId}")
profile.archived = true
app.db.profileDao().add(profile)
//app.db.metadataDao().setAllSeen(profile.id, true)
app.db.notificationDao().clear(profile.id)
app.db.endpointTimerDao().clear(profile.id)
d(TAG, "Archived profile ${profile.id} saved")
profile.archived = false
// guess the nearest school year
val today = Date.getToday()
profile.studentSchoolYearStart = when {
today.month <= profile.dateYearEnd.month -> today.year - 1
else -> today.year
}
// set default semester dates
profile.dateSemester1Start = Date(profile.studentSchoolYearStart, 9, 1)
profile.dateSemester2Start = Date(profile.studentSchoolYearStart + 1, 2, 1)
profile.dateYearEnd = Date(profile.studentSchoolYearStart + 1, 6, 30)
val oldId = profile.id
val newId = (app.db.profileDao().lastId ?: profile.id) + 1
profile.id = newId
profile.subname = "Nowy rok szkolny - ${profile.studentSchoolYearStart}"
profile.studentClassName = null
d(TAG, "New profile ID for ${profile.name}: ${profile.id}")
when (profile.loginStoreType) {
LOGIN_TYPE_LIBRUS -> {
profile.removeStudentData("isPremium")
profile.removeStudentData("pushDeviceId")
profile.removeStudentData("startPointsSemester1")
profile.removeStudentData("startPointsSemester2")
profile.removeStudentData("enablePointGrades")
profile.removeStudentData("enableDescriptiveGrades")
}
LOGIN_TYPE_MOBIDZIENNIK -> {
}
LOGIN_TYPE_VULCAN -> {
// DataVulcan.isApiLoginValid() returns false so it will update the semester
profile.removeStudentData("currentSemesterEndDate")
profile.removeStudentData("studentSemesterId")
profile.removeStudentData("studentSemesterNumber")
profile.removeStudentData("semester1Id")
profile.removeStudentData("semester2Id")
profile.removeStudentData("studentClassId")
}
LOGIN_TYPE_IDZIENNIK -> {
profile.removeStudentData("schoolYearId")
}
LOGIN_TYPE_EDUDZIENNIK -> {
}
LOGIN_TYPE_PODLASIE -> {
}
}
d(TAG, "Processed student data: ${profile.studentData}")
app.db.profileDao().add(profile)
if (app.profileId == oldId) {
val intent = Intent(
Intent.ACTION_MAIN,
"profileId" to newId
)
app.sendBroadcast(intent)
}
}
}

View File

@ -9,6 +9,7 @@ import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.* import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikData import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikData
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web.EdudziennikWebGetAnnouncement import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web.EdudziennikWebGetAnnouncement
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web.EdudziennikWebGetHomework
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.firstlogin.EdudziennikFirstLogin import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.firstlogin.EdudziennikFirstLogin
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.login.EdudziennikLogin import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.login.EdudziennikLogin
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.login.EdudziennikLoginWeb import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.login.EdudziennikLoginWeb
@ -16,10 +17,10 @@ import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
@ -52,9 +53,9 @@ class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStor
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_| |_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
__/ | __/ |
|__*/ |__*/
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) { override fun sync(featureIds: List<Int>, viewId: Int?, onlyEndpoints: List<Int>?, arguments: JsonObject?) {
data.arguments = arguments data.arguments = arguments
data.prepare(edudziennikLoginMethods, EdudziennikFeatures, featureIds, viewId) data.prepare(edudziennikLoginMethods, EdudziennikFeatures, featureIds, viewId, onlyEndpoints)
login() login()
} }
@ -94,9 +95,17 @@ class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStor
} }
} }
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {} override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) {}
override fun getRecipientList() {} override fun getRecipientList() {}
override fun getEvent(eventFull: EventFull) {
EdudziennikLoginWeb(data) {
EdudziennikWebGetHomework(data, eventFull) {
completed()
}
}
}
override fun firstLogin() { EdudziennikFirstLogin(data) { completed() } } override fun firstLogin() { EdudziennikFirstLogin(data) { completed() } }
override fun cancel() { override fun cancel() {
d(TAG, "Cancelled") d(TAG, "Cancelled")

View File

@ -27,15 +27,15 @@ class EdudziennikData(val data: DataEdudziennik, val onSuccess: () -> Unit) {
onSuccess() onSuccess()
return return
} }
useEndpoint(data.targetEndpointIds.firstKey()) { endpointId -> val id = data.targetEndpointIds.firstKey()
data.targetEndpointIds.remove(endpointId) val lastSync = data.targetEndpointIds.remove(id)
useEndpoint(id, lastSync) { endpointId ->
data.progress(data.progressStep) data.progress(data.progressStep)
nextEndpoint(onSuccess) nextEndpoint(onSuccess)
} }
} }
private fun useEndpoint(endpointId: Int, onSuccess: (endpointId: Int) -> Unit) { private fun useEndpoint(endpointId: Int, lastSync: Long?, onSuccess: (endpointId: Int) -> Unit) {
val lastSync = data.targetEndpointIds[endpointId]
Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync") Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync")
when (endpointId) { when (endpointId) {
ENDPOINT_EDUDZIENNIK_WEB_START -> { ENDPOINT_EDUDZIENNIK_WEB_START -> {

View File

@ -7,7 +7,6 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data
import im.wangchao.mhttp.Request import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response import im.wangchao.mhttp.Response
import im.wangchao.mhttp.callback.TextCallbackHandler import im.wangchao.mhttp.callback.TextCallbackHandler
import okhttp3.Cookie
import pl.szczodrzynski.edziennik.data.api.* import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
@ -43,8 +42,8 @@ open class EdudziennikWeb(open val data: DataEdudziennik, open val lastSync: Lon
if (semester == null && url.contains("start")) { if (semester == null && url.contains("start")) {
profile?.also { profile -> profile?.also { profile ->
val cookies = data.app.cookieJar.getForDomain("dziennikel.appspot.com") val cookies = data.app.cookieJar.getAll("dziennikel.appspot.com")
val semesterCookie = cookies.firstOrNull { it.name() == "semester" }?.value()?.toIntOrNull() val semesterCookie = cookies["semester"]?.toIntOrNull()
semesterCookie?.let { data.currentSemester = it } semesterCookie?.let { data.currentSemester = it }
@ -75,13 +74,7 @@ open class EdudziennikWeb(open val data: DataEdudziennik, open val lastSync: Lon
} }
} }
data.app.cookieJar.saveFromResponse(null, listOf( data.app.cookieJar.set("dziennikel.appspot.com", "sessionid", data.webSessionId)
Cookie.Builder()
.name("sessionid")
.value(data.webSessionId!!)
.domain("dziennikel.appspot.com")
.secure().httpOnly().build()
))
Request.builder() Request.builder()
.url(url) .url(url)

View File

@ -45,24 +45,25 @@ class EdudziennikWebAnnouncements(override val data: DataEdudziennik,
val addedDate = Date.fromIsoHm(dateString) val addedDate = Date.fromIsoHm(dateString)
val announcementObject = Announcement( val announcementObject = Announcement(
profileId, profileId = profileId,
id, id = id,
subject, subject = subject,
null, text = null,
startDate, startDate = startDate,
null, endDate = null,
teacher.id, teacherId = teacher.id,
longId addedDate = addedDate
) ).also {
it.idString = longId
}
data.announcementIgnoreList.add(announcementObject) data.announcementList.add(announcementObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
profileId, profileId,
Metadata.TYPE_ANNOUNCEMENT, Metadata.TYPE_ANNOUNCEMENT,
id, id,
profile.empty, profile.empty,
profile.empty, profile.empty
addedDate
)) ))
} }
} }

View File

@ -27,17 +27,24 @@ class EdudziennikWebAttendance(override val data: DataEdudziennik,
private const val TAG = "EdudziennikWebAttendance" private const val TAG = "EdudziennikWebAttendance"
} }
init { data.profile?.also { profile -> private var requestSemester: Int? = null
webGet(TAG, data.studentEndpoint + "Presence", semester = -1) { text ->
init {
if (profile?.empty == true && data.currentSemester == 2) requestSemester = 1
getAttendances()
}
private fun getAttendances() { data.profile?.also { profile ->
webGet(TAG, data.studentEndpoint + "Presence", semester = requestSemester) { text ->
val attendanceTypes = EDUDZIENNIK_ATTENDANCE_TYPES.find(text)?.get(1)?.split(',')?.map { val attendanceTypes = EDUDZIENNIK_ATTENDANCE_TYPES.find(text)?.get(1)?.split(',')?.map {
val type = EDUDZIENNIK_ATTENDANCE_TYPE.find(it.trim()) val type = EDUDZIENNIK_ATTENDANCE_TYPE.find(it.trim())
val symbol = type?.get(1)?.trim() val symbol = type?.get(1)?.trim() ?: "?"
val name = type?.get(2)?.trim() val name = type?.get(2)?.trim() ?: "nieznany rodzaj"
return@map Triple( return@map Triple(
symbol, symbol,
name, name,
when (name?.toLowerCase(Locale.ROOT)) { when (name.toLowerCase(Locale.ROOT)) {
"obecność" -> Attendance.TYPE_PRESENT "obecność" -> Attendance.TYPE_PRESENT
"nieobecność" -> Attendance.TYPE_ABSENT "nieobecność" -> Attendance.TYPE_ABSENT
"spóźnienie" -> Attendance.TYPE_BELATED "spóźnienie" -> Attendance.TYPE_BELATED
@ -45,7 +52,7 @@ class EdudziennikWebAttendance(override val data: DataEdudziennik,
"dzień wolny" -> Attendance.TYPE_DAY_FREE "dzień wolny" -> Attendance.TYPE_DAY_FREE
"brak zajęć" -> Attendance.TYPE_DAY_FREE "brak zajęć" -> Attendance.TYPE_DAY_FREE
"oddelegowany" -> Attendance.TYPE_RELEASED "oddelegowany" -> Attendance.TYPE_RELEASED
else -> Attendance.TYPE_CUSTOM else -> Attendance.TYPE_UNKNOWN
} }
) )
} ?: emptyList() } ?: emptyList()
@ -55,44 +62,53 @@ class EdudziennikWebAttendance(override val data: DataEdudziennik,
val lessonNumber = attendanceElement[2].toInt() val lessonNumber = attendanceElement[2].toInt()
val attendanceSymbol = attendanceElement[3] val attendanceSymbol = attendanceElement[3]
val lessons = data.app.db.timetableDao().getForDateNow(profileId, date) val lessons = data.app.db.timetableDao().getAllForDateNow(profileId, date)
val lesson = lessons.firstOrNull { it.lessonNumber == lessonNumber } val lesson = lessons.firstOrNull { it.lessonNumber == lessonNumber }
val id = "${date.stringY_m_d}:$lessonNumber:$attendanceSymbol".crc32() val id = "${date.stringY_m_d}:$lessonNumber:$attendanceSymbol".crc32()
val (_, name, type) = attendanceTypes.firstOrNull { (symbol, _, _) -> symbol == attendanceSymbol } val (typeSymbol, typeName, baseType) = attendanceTypes.firstOrNull { (symbol, _, _) -> symbol == attendanceSymbol }
?: return@forEach ?: return@forEach
val startTime = data.lessonRanges.singleOrNull { it.lessonNumber == lessonNumber }?.startTime val startTime = data.lessonRanges.singleOrNull { it.lessonNumber == lessonNumber }?.startTime
?: return@forEach ?: return@forEach
val attendanceObject = Attendance( val attendanceObject = Attendance(
profileId, profileId = profileId,
id, id = id,
lesson?.displayTeacherId ?: -1, baseType = baseType,
lesson?.displaySubjectId ?: -1, typeName = typeName,
profile.currentSemester, typeShort = data.app.attendanceManager.getTypeShort(baseType),
name, typeSymbol = typeSymbol,
date, typeColor = null,
lesson?.displayStartTime ?: startTime, date = date,
type startTime = lesson?.displayStartTime ?: startTime,
) semester = profile.currentSemester,
teacherId = lesson?.displayTeacherId ?: -1,
subjectId = lesson?.displaySubjectId ?: -1
).also {
it.lessonNumber = lessonNumber
}
data.attendanceList.add(attendanceObject) data.attendanceList.add(attendanceObject)
if(type != Attendance.TYPE_PRESENT) { if (baseType != Attendance.TYPE_PRESENT) {
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
profileId, profileId,
Metadata.TYPE_ATTENDANCE, Metadata.TYPE_ATTENDANCE,
id, id,
profile.empty, profile.empty || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == Attendance.TYPE_UNKNOWN,
profile.empty, profile.empty || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == Attendance.TYPE_UNKNOWN
System.currentTimeMillis()
)) ))
} }
} }
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE, SYNC_ALWAYS) if (profile.empty && requestSemester == 1 && data.currentSemester == 2) {
onSuccess(ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE) requestSemester = null
getAttendances()
} else {
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE, SYNC_ALWAYS)
onSuccess(ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE)
}
} }
} ?: onSuccess(ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE) } } ?: onSuccess(ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE) }
} }

View File

@ -39,17 +39,16 @@ class EdudziennikWebEvents(override val data: DataEdudziennik,
?: return@forEach ?: return@forEach
val eventObject = Event( val eventObject = Event(
profileId, profileId = profileId,
id, id = id,
date, date = date,
null, time = null,
title, topic = title,
-1, color = null,
Event.TYPE_CLASS_EVENT, type = Event.TYPE_CLASS_EVENT,
false, teacherId = -1,
-1, subjectId = -1,
-1, teamId = data.teamClass?.id ?: -1
data.teamClass?.id ?: -1
) )
data.eventList.add(eventObject) data.eventList.add(eventObject)
@ -58,8 +57,7 @@ class EdudziennikWebEvents(override val data: DataEdudziennik,
Metadata.TYPE_EVENT, Metadata.TYPE_EVENT,
id, id,
profile.empty, profile.empty,
profile.empty, profile.empty
System.currentTimeMillis()
)) ))
} }

View File

@ -46,7 +46,7 @@ class EdudziennikWebExams(override val data: DataEdudziennik,
if (dateString.isBlank()) return@forEach if (dateString.isBlank()) return@forEach
val date = Date.fromY_m_d(dateString) val date = Date.fromY_m_d(dateString)
val lessons = data.app.db.timetableDao().getForDateNow(profileId, date) val lessons = data.app.db.timetableDao().getAllForDateNow(profileId, date)
val startTime = lessons.firstOrNull { it.displaySubjectId == subject.id }?.displayStartTime val startTime = lessons.firstOrNull { it.displaySubjectId == subject.id }?.displayStartTime
val eventTypeElement = examElement.child(3).child(0) val eventTypeElement = examElement.child(3).child(0)
@ -56,17 +56,16 @@ class EdudziennikWebExams(override val data: DataEdudziennik,
val eventType = data.getEventType(eventTypeId, eventTypeName) val eventType = data.getEventType(eventTypeId, eventTypeName)
val eventObject = Event( val eventObject = Event(
profileId, profileId = profileId,
id, id = id,
date, date = date,
startTime, time = startTime,
topic, topic = topic,
-1, color = null,
eventType.id, type = eventType.id,
false, teacherId = -1,
-1, subjectId = subject.id,
subject.id, teamId = data.teamClass?.id ?: -1
data.teamClass?.id ?: -1
) )
data.eventList.add(eventObject) data.eventList.add(eventObject)
@ -75,8 +74,7 @@ class EdudziennikWebExams(override val data: DataEdudziennik,
Metadata.TYPE_EVENT, Metadata.TYPE_EVENT,
id, id,
profile.empty, profile.empty,
profile.empty, profile.empty
System.currentTimeMillis()
)) ))
} }

View File

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

View File

@ -14,7 +14,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZI
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Grade import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.* import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_NORMAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_POINT_SUM
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.get import pl.szczodrzynski.edziennik.get
@ -90,7 +95,7 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
val columnName = info.child(4).text().trim() val columnName = info.child(4).text().trim()
val comment = info.ownText() val comment = info.ownText()
val description = columnName + if (comment.isNotBlank()) " - $comment" else "" val description = columnName + if (comment.isNotBlank()) " - $comment" else null
val teacherName = info.child(1).text() val teacherName = info.child(1).text()
val teacher = data.getTeacherByLastFirst(teacherName) val teacher = data.getTeacherByLastFirst(teacherName)
@ -109,20 +114,21 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
} ?: -1 } ?: -1
val gradeObject = Grade( val gradeObject = Grade(
profileId, profileId = profileId,
id, id = id,
fullName, name = name,
color, type = gradeType,
description, value = value,
name, weight = if (gradeCountToAverage) weight else 0f,
value, color = color,
if (gradeCountToAverage) weight else 0f, category = fullName,
semester, description = description,
teacher.id, comment = null,
subject.id semester = semester,
).apply { teacherId = teacher.id,
type = gradeType subjectId = subject.id,
} addedDate = addedDate
)
data.gradeList.add(gradeObject) data.gradeList.add(gradeObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
@ -130,8 +136,7 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
Metadata.TYPE_GRADE, Metadata.TYPE_GRADE,
id, id,
profile.empty, profile.empty,
profile.empty, profile.empty
addedDate
)) ))
} }
@ -139,23 +144,23 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
if (proposed != null && proposed.isNotBlank()) { if (proposed != null && proposed.isNotBlank()) {
val proposedGradeObject = Grade( val proposedGradeObject = Grade(
profileId, profileId = profileId,
(-1 * subject.id) - 1, id = (-1 * subject.id) - 1,
"", name = proposed,
-1, type = when (semester) {
"", 1 -> TYPE_SEMESTER1_PROPOSED
proposed, else -> TYPE_SEMESTER2_PROPOSED
proposed.toFloatOrNull() ?: 0f, },
0f, value = proposed.toFloatOrNull() ?: 0f,
semester, weight = 0f,
-1, color = -1,
subject.id category = null,
).apply { description = null,
type = when (semester) { comment = null,
1 -> TYPE_SEMESTER1_PROPOSED semester = semester,
else -> TYPE_SEMESTER2_PROPOSED teacherId = -1,
} subjectId = subject.id
} )
data.gradeList.add(proposedGradeObject) data.gradeList.add(proposedGradeObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
@ -163,8 +168,7 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
Metadata.TYPE_GRADE, Metadata.TYPE_GRADE,
proposedGradeObject.id, proposedGradeObject.id,
profile.empty, profile.empty,
profile.empty, profile.empty
System.currentTimeMillis()
)) ))
} }
@ -172,23 +176,23 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
if (final != null && final.isNotBlank()) { if (final != null && final.isNotBlank()) {
val finalGradeObject = Grade( val finalGradeObject = Grade(
profileId, profileId = profileId,
(-1 * subject.id) - 2, id = (-1 * subject.id) - 2,
"", name = final,
-1, type = when (semester) {
"", 1 -> TYPE_SEMESTER1_FINAL
final, else -> TYPE_SEMESTER2_FINAL
final.toFloatOrNull() ?: 0f, },
0f, value = final.toFloatOrNull() ?: 0f,
semester, weight = 0f,
-1, color = -1,
subject.id category = null,
).apply { description = null,
type = when (semester) { comment = null,
1 -> TYPE_SEMESTER1_FINAL semester = semester,
else -> TYPE_SEMESTER2_FINAL teacherId = -1,
} subjectId = subject.id
} )
data.gradeList.add(finalGradeObject) data.gradeList.add(finalGradeObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
@ -196,8 +200,7 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
Metadata.TYPE_GRADE, Metadata.TYPE_GRADE,
finalGradeObject.id, finalGradeObject.id,
profile.empty, profile.empty,
profile.empty, profile.empty
System.currentTimeMillis()
)) ))
} }
} }

View File

@ -33,8 +33,8 @@ class EdudziennikWebHomework(override val data: DataEdudziennik,
if (doc.getElementsByClass("message").text().trim() != "Brak prac domowych") { if (doc.getElementsByClass("message").text().trim() != "Brak prac domowych") {
doc.getElementsByTag("tr").forEach { homeworkElement -> doc.getElementsByTag("tr").forEach { homeworkElement ->
val dateElement = homeworkElement.getElementsByClass("date").first().child(0) val dateElement = homeworkElement.getElementsByClass("date").first().child(0)
val id = EDUDZIENNIK_HOMEWORK_ID.find(dateElement.attr("href"))?.get(1)?.crc32() val idStr = EDUDZIENNIK_HOMEWORK_ID.find(dateElement.attr("href"))?.get(1) ?: return@forEach
?: return@forEach val id = idStr.crc32()
val date = Date.fromY_m_d(dateElement.text()) val date = Date.fromY_m_d(dateElement.text())
val subjectElement = homeworkElement.child(1).child(0) val subjectElement = homeworkElement.child(1).child(0)
@ -43,36 +43,36 @@ class EdudziennikWebHomework(override val data: DataEdudziennik,
val subjectName = subjectElement.text() val subjectName = subjectElement.text()
val subject = data.getSubject(subjectId, subjectName) val subject = data.getSubject(subjectId, subjectName)
val lessons = data.app.db.timetableDao().getForDateNow(profileId, date) val lessons = data.app.db.timetableDao().getAllForDateNow(profileId, date)
val startTime = lessons.firstOrNull { it.subjectId == subject.id }?.displayStartTime val startTime = lessons.firstOrNull { it.subjectId == subject.id }?.displayStartTime
val teacherName = homeworkElement.child(2).text() val teacherName = homeworkElement.child(2).text()
val teacher = data.getTeacherByFirstLast(teacherName) val teacher = data.getTeacherByFirstLast(teacherName)
val topic = homeworkElement.child(4).text() val topic = homeworkElement.child(4).text()?.trim()
val eventObject = Event( val eventObject = Event(
profileId, profileId = profileId,
id, id = id,
date, date = date,
startTime, time = startTime,
topic, topic = topic ?: "",
-1, color = null,
Event.TYPE_HOMEWORK, type = Event.TYPE_HOMEWORK,
false, teacherId = teacher.id,
teacher.id, subjectId = subject.id,
subject.id, teamId = data.teamClass?.id ?: -1
data.teamClass?.id ?: -1
) )
eventObject.attachmentNames = mutableListOf(idStr)
data.eventList.add(eventObject) data.eventList.add(eventObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
profileId, profileId,
Metadata.TYPE_HOMEWORK, Metadata.TYPE_HOMEWORK,
id, id,
profile.empty, profile.empty,
profile.empty, profile.empty
System.currentTimeMillis()
)) ))
} }
} }

View File

@ -24,9 +24,9 @@ class EdudziennikWebLuckyNumber(override val data: DataEdudziennik,
webGet(TAG, data.schoolEndpoint + "Lucky", xhr = true) { text -> webGet(TAG, data.schoolEndpoint + "Lucky", xhr = true) { text ->
text.toIntOrNull()?.also { luckyNumber -> text.toIntOrNull()?.also { luckyNumber ->
val luckyNumberObject = LuckyNumber( val luckyNumberObject = LuckyNumber(
profileId, profileId = profileId,
Date.getToday(), date = Date.getToday(),
luckyNumber number = luckyNumber
) )
data.luckyNumberList.add(luckyNumberObject) data.luckyNumberList.add(luckyNumberObject)
@ -35,8 +35,7 @@ class EdudziennikWebLuckyNumber(override val data: DataEdudziennik,
Metadata.TYPE_LUCKY_NUMBER, Metadata.TYPE_LUCKY_NUMBER,
luckyNumberObject.date.value.toLong(), luckyNumberObject.date.value.toLong(),
true, true,
profile.empty, profile.empty
System.currentTimeMillis()
)) ))
} }

View File

@ -41,12 +41,15 @@ class EdudziennikWebNotes(override val data: DataEdudziennik,
val description = noteElement.child(3).text() val description = noteElement.child(3).text()
val noticeObject = Notice( val noticeObject = Notice(
profileId, profileId = profileId,
id, id = id,
description, type = Notice.TYPE_NEUTRAL,
profile.currentSemester, semester = profile.currentSemester,
Notice.TYPE_NEUTRAL, text = description,
teacher.id category = null,
points = null,
teacherId = teacher.id,
addedDate = addedDate
) )
data.noticeList.add(noticeObject) data.noticeList.add(noticeObject)
@ -55,8 +58,7 @@ class EdudziennikWebNotes(override val data: DataEdudziennik,
Metadata.TYPE_NOTICE, Metadata.TYPE_NOTICE,
id, id,
profile.empty, profile.empty,
profile.empty, profile.empty
addedDate
)) ))
} }

View File

@ -124,8 +124,7 @@ class EdudziennikWebTimetable(override val data: DataEdudziennik,
Metadata.TYPE_LESSON_CHANGE, Metadata.TYPE_LESSON_CHANGE,
lessonObject.id, lessonObject.id,
seen, seen,
seen, seen
System.currentTimeMillis()
)) ))
} }
} }

View File

@ -59,7 +59,7 @@ class EdudziennikFirstLogin(val data: DataEdudziennik, val onSuccess: () -> Unit
profileList.add(profile) profileList.add(profile)
} }
EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore)) EventBus.getDefault().postSticky(FirstLoginFinishedEvent(profileList, data.loginStore))
onSuccess() onSuccess()
} }
} }

View File

@ -24,7 +24,7 @@ class EdudziennikLoginWeb(val data: DataEdudziennik, val onSuccess: () -> Unit)
onSuccess() onSuccess()
} }
else { else {
data.app.cookieJar.clearForDomain("dziennikel.appspot.com") data.app.cookieJar.clear("dziennikel.appspot.com")
if (data.loginEmail.isNotNullNorEmpty() && data.loginPassword.isNotNullNorEmpty()) { if (data.loginEmail.isNotNullNorEmpty() && data.loginPassword.isNotNullNorEmpty()) {
loginWithCredentials() loginWithCredentials()
} }
@ -59,8 +59,8 @@ class EdudziennikLoginWeb(val data: DataEdudziennik, val onSuccess: () -> Unit)
} }
} }
val cookies = data.app.cookieJar.getForDomain("dziennikel.appspot.com") val cookies = data.app.cookieJar.getAll("dziennikel.appspot.com")
val sessionId = cookies.firstOrNull { it.name() == "sessionid" }?.value() val sessionId = cookies["sessionid"]
if (sessionId == null) { if (sessionId == null) {
data.error(ApiError(TAG, ERROR_LOGIN_EDUDZIENNIK_WEB_NO_SESSION_ID) data.error(ApiError(TAG, ERROR_LOGIN_EDUDZIENNIK_WEB_NO_SESSION_ID)

View File

@ -0,0 +1,70 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2020-5-14
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.helper
import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import im.wangchao.mhttp.callback.FileCallbackHandler
import pl.szczodrzynski.edziennik.data.api.ERROR_FILE_DOWNLOAD
import pl.szczodrzynski.edziennik.data.api.ERROR_REQUEST_FAILURE
import pl.szczodrzynski.edziennik.data.api.SYSTEM_USER_AGENT
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.utils.Utils
import java.io.File
class DownloadAttachment(
fileUrl: String,
val onSuccess: (file: File) -> Unit,
val onProgress: (written: Long, total: Long) -> Unit,
val onError: (apiError: ApiError) -> Unit
) {
companion object {
private const val TAG = "DownloadAttachment"
}
init {
val targetFile = Utils.getStorageDir()
val callback = object : FileCallbackHandler(targetFile) {
override fun onSuccess(file: File?, response: Response?) {
if (file == null) {
onError(ApiError(TAG, ERROR_FILE_DOWNLOAD)
.withResponse(response))
return
}
try {
onSuccess(file)
} catch (e: Exception) {
onError(ApiError(TAG, ERROR_FILE_DOWNLOAD)
.withResponse(response)
.withThrowable(e))
}
}
override fun onProgress(bytesWritten: Long, bytesTotal: Long) {
try {
this@DownloadAttachment.onProgress(bytesWritten, bytesTotal)
} catch (e: Exception) {
onError(ApiError(TAG, ERROR_FILE_DOWNLOAD)
.withThrowable(e))
}
}
override fun onFailure(response: Response?, throwable: Throwable?) {
onError(ApiError(TAG, ERROR_REQUEST_FAILURE)
.withResponse(response)
.withThrowable(throwable))
}
}
Request.builder()
.url(fileUrl)
.userAgent(SYSTEM_USER_AGENT)
.callback(callback)
.build()
.enqueue()
}
}

View File

@ -0,0 +1,105 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-4-7.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.helper
import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response
import im.wangchao.mhttp.callback.FileCallbackHandler
import im.wangchao.mhttp.callback.TextCallbackHandler
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.ERROR_ONEDRIVE_DOWNLOAD
import pl.szczodrzynski.edziennik.data.api.ERROR_REQUEST_FAILURE
import pl.szczodrzynski.edziennik.data.api.SYSTEM_USER_AGENT
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.utils.Utils
import java.io.File
class OneDriveDownloadAttachment(
app: App,
fileUrl: String,
val onSuccess: (file: File) -> Unit,
val onProgress: (written: Long, total: Long) -> Unit,
val onError: (apiError: ApiError) -> Unit
) {
companion object {
private const val TAG = "OneDriveDownloadAttachment"
}
init {
Request.builder()
.url(fileUrl)
.userAgent(SYSTEM_USER_AGENT)
.withClient(app.httpLazy)
.callback(object : TextCallbackHandler() {
override fun onSuccess(text: String, response: Response) {
val location = response.headers().get("Location")
// https://onedrive.live.com/redir?resid=D75496A2EB87531C!706&authkey=!ABjZeh3pHMqj11Q
if (location?.contains("onedrive.live.com/redir?resid=") != true) {
onError(ApiError(TAG, ERROR_ONEDRIVE_DOWNLOAD)
.withApiResponse(text)
.withResponse(response))
return
}
val url = location
.replace("onedrive.live.com/redir?resid=", "storage.live.com/items/")
.replace("?", "&")
.replaceFirst("&", "?")
downloadFile(url)
}
override fun onFailure(response: Response, throwable: Throwable) {
onError(ApiError(TAG, ERROR_REQUEST_FAILURE)
.withResponse(response)
.withThrowable(throwable))
}
})
.build()
.enqueue()
}
private fun downloadFile(url: String) {
val targetFile = Utils.getStorageDir()
val callback = object : FileCallbackHandler(targetFile) {
override fun onSuccess(file: File?, response: Response?) {
if (file == null) {
onError(ApiError(TAG, ERROR_ONEDRIVE_DOWNLOAD)
.withResponse(response))
return
}
try {
onSuccess(file)
} catch (e: Exception) {
onError(ApiError(TAG, ERROR_ONEDRIVE_DOWNLOAD)
.withResponse(response)
.withThrowable(e))
}
}
override fun onProgress(bytesWritten: Long, bytesTotal: Long) {
try {
this@OneDriveDownloadAttachment.onProgress(bytesWritten, bytesTotal)
} catch (e: Exception) {
onError(ApiError(TAG, ERROR_ONEDRIVE_DOWNLOAD)
.withThrowable(e))
}
}
override fun onFailure(response: Response?, throwable: Throwable?) {
onError(ApiError(TAG, ERROR_REQUEST_FAILURE)
.withResponse(response)
.withThrowable(throwable))
}
}
Request.builder()
.url(url)
.userAgent(SYSTEM_USER_AGENT)
.callback(callback)
.build()
.enqueue()
}
}

View File

@ -5,7 +5,6 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik
import androidx.core.util.set import androidx.core.util.set
import okhttp3.Cookie
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_IDZIENNIK_API 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.LOGIN_METHOD_IDZIENNIK_WEB
@ -24,18 +23,8 @@ class DataIdziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data(
loginMethods.clear() loginMethods.clear()
if (isWebLoginValid()) { if (isWebLoginValid()) {
loginMethods += LOGIN_METHOD_IDZIENNIK_WEB loginMethods += LOGIN_METHOD_IDZIENNIK_WEB
app.cookieJar.saveFromResponse(null, listOf( app.cookieJar.set("iuczniowie.progman.pl", "ASP.NET_SessionId_iDziennik", webSessionId)
Cookie.Builder() app.cookieJar.set("iuczniowie.progman.pl", ".ASPXAUTH", webAuth)
.name("ASP.NET_SessionId_iDziennik")
.value(webSessionId!!)
.domain("iuczniowie.progman.pl")
.secure().httpOnly().build(),
Cookie.Builder()
.name(".ASPXAUTH")
.value(webAuth!!)
.domain("iuczniowie.progman.pl")
.secure().httpOnly().build()
))
} }
if (isApiLoginValid()) if (isApiLoginValid())
loginMethods += LOGIN_METHOD_IDZIENNIK_API loginMethods += LOGIN_METHOD_IDZIENNIK_API
@ -81,6 +70,11 @@ class DataIdziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data(
get() { mWebAuth = mWebAuth ?: loginStore.getLoginData("webAuth", null); return mWebAuth } get() { mWebAuth = mWebAuth ?: loginStore.getLoginData("webAuth", null); return mWebAuth }
set(value) { loginStore.putLoginData("webAuth", value); mWebAuth = value } set(value) { loginStore.putLoginData("webAuth", value); mWebAuth = value }
private var mWebSelectedRegister: Int? = null
var webSelectedRegister: Int
get() { mWebSelectedRegister = mWebSelectedRegister ?: loginStore.getLoginData("webSelectedRegister", 0); return mWebSelectedRegister ?: 0 }
set(value) { loginStore.putLoginData("webSelectedRegister", value); mWebSelectedRegister = value }
/* _ /* _
/\ (_) /\ (_)
/ \ _ __ _ / \ _ __ _

View File

@ -8,20 +8,15 @@ import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.* import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikData import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikData
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebGetAttachment import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.*
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebGetMessage
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebGetRecipientList
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebSendMessage
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.firstlogin.IdziennikFirstLogin import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.firstlogin.IdziennikFirstLogin
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login.IdziennikLogin import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login.IdziennikLogin
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.*
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
@ -54,9 +49,9 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_| |_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
__/ | __/ |
|__*/ |__*/
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) { override fun sync(featureIds: List<Int>, viewId: Int?, onlyEndpoints: List<Int>?, arguments: JsonObject?) {
data.arguments = arguments data.arguments = arguments
data.prepare(idziennikLoginMethods, IdziennikFeatures, featureIds, viewId) data.prepare(idziennikLoginMethods, IdziennikFeatures, featureIds, viewId, onlyEndpoints)
login() login()
} }
@ -103,10 +98,17 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
override fun markAllAnnouncementsAsRead() {} override fun markAllAnnouncementsAsRead() {}
override fun getAnnouncement(announcement: AnnouncementFull) {} override fun getAnnouncement(announcement: AnnouncementFull) {}
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) { override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) {
login(LOGIN_METHOD_IDZIENNIK_WEB) { login(LOGIN_METHOD_IDZIENNIK_WEB) {
IdziennikWebGetAttachment(data, message, attachmentId, attachmentName) { if (owner is Message) {
completed() IdziennikWebGetAttachment(data, owner, attachmentId, attachmentName) {
completed()
}
}
else if (owner is Event) {
IdziennikWebGetHomeworkAttachment(data, owner, attachmentId, attachmentName) {
completed()
}
} }
} }
} }
@ -119,6 +121,14 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
} }
} }
override fun getEvent(eventFull: EventFull) {
login(LOGIN_METHOD_IDZIENNIK_WEB) {
IdziennikWebGetHomework(data, eventFull) {
completed()
}
}
}
override fun firstLogin() { IdziennikFirstLogin(data) { completed() } } override fun firstLogin() { IdziennikFirstLogin(data) { completed() } }
override fun cancel() { override fun cancel() {
d(TAG, "Cancelled") d(TAG, "Cancelled")

View File

@ -30,15 +30,15 @@ class IdziennikData(val data: DataIdziennik, val onSuccess: () -> Unit) {
onSuccess() onSuccess()
return return
} }
useEndpoint(data.targetEndpointIds.firstKey()) { endpointId -> val id = data.targetEndpointIds.firstKey()
data.targetEndpointIds.remove(endpointId) val lastSync = data.targetEndpointIds.remove(id)
useEndpoint(id, lastSync) { endpointId ->
data.progress(data.progressStep) data.progress(data.progressStep)
nextEndpoint(onSuccess) nextEndpoint(onSuccess)
} }
} }
private fun useEndpoint(endpointId: Int, onSuccess: (endpointId: Int) -> Unit) { private fun useEndpoint(endpointId: Int, lastSync: Long?, onSuccess: (endpointId: Int) -> Unit) {
val lastSync = data.targetEndpointIds[endpointId]
Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync") Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync")
when (endpointId) { when (endpointId) {
ENDPOINT_IDZIENNIK_WEB_TIMETABLE -> { ENDPOINT_IDZIENNIK_WEB_TIMETABLE -> {

View File

@ -13,6 +13,7 @@ import im.wangchao.mhttp.callback.JsonCallbackHandler
import im.wangchao.mhttp.callback.TextCallbackHandler import im.wangchao.mhttp.callback.TextCallbackHandler
import pl.szczodrzynski.edziennik.data.api.* import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebSwitchRegister
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
import java.io.File import java.io.File
@ -48,6 +49,17 @@ open class IdziennikWeb(open val data: DataIdziennik, open val lastSync: Long?)
return return
} }
if (response?.code() == HTTP_INTERNAL_ERROR && endpoint == IDZIENNIK_WEB_GRADES) {
// special override for accounts where displaying grades
// for another student requires switching it manually
if (data.registerId != data.webSelectedRegister) {
IdziennikWebSwitchRegister(data, data.registerId) {
webApiGet(tag, endpoint, parameters, onSuccess)
}
return
}
}
when { when {
response?.code() == HTTP_UNAUTHORIZED -> ERROR_IDZIENNIK_WEB_ACCESS_DENIED response?.code() == HTTP_UNAUTHORIZED -> ERROR_IDZIENNIK_WEB_ACCESS_DENIED
response?.code() == HTTP_INTERNAL_ERROR -> ERROR_IDZIENNIK_WEB_SERVER_ERROR response?.code() == HTTP_INTERNAL_ERROR -> ERROR_IDZIENNIK_WEB_SERVER_ERROR
@ -115,7 +127,7 @@ open class IdziennikWeb(open val data: DataIdziennik, open val lastSync: Long?)
.enqueue() .enqueue()
} }
fun webGet(tag: String, endpoint: String, onSuccess: (text: String) -> Unit) { fun webGet(tag: String, endpoint: String, parameters: Map<String, Any> = emptyMap(), onSuccess: (text: String) -> Unit) {
d(tag, "Request: Idziennik/Web - $IDZIENNIK_WEB_URL/$endpoint") d(tag, "Request: Idziennik/Web - $IDZIENNIK_WEB_URL/$endpoint")
val callback = object : TextCallbackHandler() { val callback = object : TextCallbackHandler() {
@ -160,7 +172,14 @@ open class IdziennikWeb(open val data: DataIdziennik, open val lastSync: Long?)
Request.builder() Request.builder()
.url("$IDZIENNIK_WEB_URL/$endpoint") .url("$IDZIENNIK_WEB_URL/$endpoint")
.userAgent(IDZIENNIK_USER_AGENT) .userAgent(IDZIENNIK_USER_AGENT)
.get() .apply {
if (parameters.isEmpty()) get()
else post()
parameters.map { (name, value) ->
addParameter(name, value)
}
}
.callback(callback) .callback(callback)
.build() .build()
.enqueue() .enqueue()
@ -210,6 +229,7 @@ open class IdziennikWeb(open val data: DataIdziennik, open val lastSync: Long?)
.apply { .apply {
parameters.forEach { (k, v) -> addParameter(k, v) } parameters.forEach { (k, v) -> addParameter(k, v) }
} }
.contentType("application/x-www-form-urlencoded")
.post() .post()
.callback(callback) .callback(callback)
.build() .build()

View File

@ -68,9 +68,9 @@ class IdziennikApiCurrentRegister(override val data: DataIdziennik,
val luckyNumberObject = LuckyNumber( val luckyNumberObject = LuckyNumber(
data.profileId, profileId = data.profileId,
Date.getToday(), date = luckyNumberDate,
luckyNumber number = luckyNumber
) )
data.luckyNumberList.add(luckyNumberObject) data.luckyNumberList.add(luckyNumberObject)
@ -80,8 +80,7 @@ class IdziennikApiCurrentRegister(override val data: DataIdziennik,
Metadata.TYPE_LUCKY_NUMBER, Metadata.TYPE_LUCKY_NUMBER,
luckyNumberObject.date.value.toLong(), luckyNumberObject.date.value.toLong(),
true, true,
data.profile?.empty ?: false, data.profile?.empty ?: false
System.currentTimeMillis()
)) ))
} }

View File

@ -11,8 +11,8 @@ 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.ENDPOINT_IDZIENNIK_API_MESSAGES_INBOX
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikApi import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikApi
import pl.szczodrzynski.edziennik.data.db.entity.* import pl.szczodrzynski.edziennik.data.db.entity.*
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_DELETED import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_DELETED
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.getBoolean import pl.szczodrzynski.edziennik.getBoolean
import pl.szczodrzynski.edziennik.getString import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.Utils.crc32 import pl.szczodrzynski.edziennik.utils.Utils.crc32
@ -33,11 +33,11 @@ class IdziennikApiMessagesInbox(override val data: DataIdziennik,
return@apiGet return@apiGet
} }
json.asJsonObjectList()?.forEach { jMessage -> json.asJsonObjectList().forEach { jMessage ->
val subject = jMessage.getString("tytul") val subject = jMessage.getString("tytul") ?: ""
if (subject?.contains("(") == true && subject.startsWith("iDziennik - ")) if (subject.contains("(") && subject.startsWith("iDziennik - "))
return@forEach return@forEach
if (subject?.startsWith("Uwaga dla ucznia (klasa:") == true) if (subject.startsWith("Uwaga dla ucznia (klasa:"))
return@forEach return@forEach
val messageIdStr = jMessage.getString("id") val messageIdStr = jMessage.getString("id")
@ -64,13 +64,13 @@ class IdziennikApiMessagesInbox(override val data: DataIdziennik,
rTeacher.setTeacherType(Teacher.TYPE_OTHER) rTeacher.setTeacherType(Teacher.TYPE_OTHER)
val message = Message( val message = Message(
profileId, profileId = profileId,
messageId, id = messageId,
subject, type = if (jMessage.getBoolean("rekordUsuniety") == true) TYPE_DELETED else TYPE_RECEIVED,
body, subject = subject,
if (jMessage.getBoolean("rekordUsuniety") == true) TYPE_DELETED else TYPE_RECEIVED, body = body,
rTeacher.id, senderId = rTeacher.id,
-1 addedDate = sentDate
) )
val messageRecipient = MessageRecipient( val messageRecipient = MessageRecipient(
@ -81,15 +81,14 @@ class IdziennikApiMessagesInbox(override val data: DataIdziennik,
/*messageId*/ messageId /*messageId*/ messageId
) )
data.messageIgnoreList.add(message) data.messageList.add(message)
data.messageRecipientList.add(messageRecipient) data.messageRecipientList.add(messageRecipient)
data.setSeenMetadataList.add(Metadata( data.setSeenMetadataList.add(Metadata(
profileId, profileId,
Metadata.TYPE_MESSAGE, Metadata.TYPE_MESSAGE,
message.id, message.id,
readDate > 0, readDate > 0,
readDate > 0 || profile?.empty ?: false, readDate > 0 || profile?.empty ?: false
sentDate
)) ))
} }

View File

@ -13,7 +13,7 @@ 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.ENDPOINT_IDZIENNIK_API_MESSAGES_SENT
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikApi import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikApi
import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.utils.Utils.crc32 import pl.szczodrzynski.edziennik.utils.Utils.crc32
@ -46,13 +46,13 @@ class IdziennikApiMessagesSent(override val data: DataIdziennik,
val sentDate = Date.fromIso(jMessage.get("dataWyslania").asString) val sentDate = Date.fromIso(jMessage.get("dataWyslania").asString)
val message = Message( val message = Message(
profileId, profileId = profileId,
messageId, id = messageId,
subject, type = TYPE_SENT,
body, subject = subject,
TYPE_SENT, body = body,
-1, senderId = null,
-1 addedDate = sentDate
) )
for (recipientEl in jMessage.getAsJsonArray("odbiorcy")) { for (recipientEl in jMessage.getAsJsonArray("odbiorcy")) {
@ -76,8 +76,8 @@ class IdziennikApiMessagesSent(override val data: DataIdziennik,
data.messageRecipientIgnoreList.add(messageRecipient) data.messageRecipientIgnoreList.add(messageRecipient)
} }
data.messageIgnoreList.add(message) data.messageList.add(message)
data.metadataList.add(Metadata(profileId, Metadata.TYPE_MESSAGE, message.id, true, true, sentDate)) data.metadataList.add(Metadata(profileId, Metadata.TYPE_MESSAGE, message.id, true, true))
} }
data.setSyncNext(ENDPOINT_IDZIENNIK_API_MESSAGES_SENT, DAY, DRAWER_ITEM_MESSAGES) data.setSyncNext(ENDPOINT_IDZIENNIK_API_MESSAGES_SENT, DAY, DRAWER_ITEM_MESSAGES)

View File

@ -16,6 +16,8 @@ import pl.szczodrzynski.edziennik.data.db.entity.Announcement
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.getJsonObject import pl.szczodrzynski.edziennik.getJsonObject
import pl.szczodrzynski.edziennik.getLong
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class IdziennikWebAnnouncements(override val data: DataIdziennik, class IdziennikWebAnnouncements(override val data: DataIdziennik,
@ -43,21 +45,21 @@ class IdziennikWebAnnouncements(override val data: DataIdziennik,
for (jAnnouncementEl in json.getAsJsonArray("ListK")) { for (jAnnouncementEl in json.getAsJsonArray("ListK")) {
val jAnnouncement = jAnnouncementEl.asJsonObject val jAnnouncement = jAnnouncementEl.asJsonObject
// jAnnouncement // jAnnouncement
val announcementId = jAnnouncement.get("Id").asLong val announcementId = jAnnouncement.getLong("Id") ?: -1
val rTeacher = data.getTeacherByFirstLast(jAnnouncement.get("Autor").asString) val rTeacher = data.getTeacherByFirstLast(jAnnouncement.getString("Autor") ?: "")
val addedDate = java.lang.Long.parseLong(jAnnouncement.get("DataDodania").asString.replace("[^\\d]".toRegex(), "")) val addedDate = jAnnouncement.getString("DataDodania")?.replace("[^\\d]".toRegex(), "")?.toLongOrNull() ?: System.currentTimeMillis()
val startDate = Date.fromMillis(java.lang.Long.parseLong(jAnnouncement.get("DataWydarzenia").asString.replace("[^\\d]".toRegex(), ""))) val startDate = jAnnouncement.getString("DataWydarzenia")?.replace("[^\\d]".toRegex(), "")?.toLongOrNull()?.let { Date.fromMillis(it) }
val announcementObject = Announcement( val announcementObject = Announcement(
profileId, profileId = profileId,
announcementId, id = announcementId,
jAnnouncement.get("Temat").asString, subject = jAnnouncement.get("Temat").asString,
jAnnouncement.get("Tresc").asString, text = jAnnouncement.get("Tresc").asString,
startDate, startDate = startDate,
null, endDate = null,
rTeacher.id, teacherId = rTeacher.id,
null addedDate = addedDate
) )
data.announcementList.add(announcementObject) data.announcementList.add(announcementObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
@ -65,8 +67,7 @@ class IdziennikWebAnnouncements(override val data: DataIdziennik,
Metadata.TYPE_ANNOUNCEMENT, Metadata.TYPE_ANNOUNCEMENT,
announcementObject.id, announcementObject.id,
profile?.empty ?: false, profile?.empty ?: false,
profile?.empty ?: false, profile?.empty ?: false
addedDate
)) ))
} }

View File

@ -12,10 +12,18 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNI
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.Attendance import pl.szczodrzynski.edziennik.data.db.entity.Attendance
import pl.szczodrzynski.edziennik.data.db.entity.Attendance.* import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_ABSENT
import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_ABSENT_EXCUSED
import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_BELATED
import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_PRESENT
import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_PRESENT_CUSTOM
import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_RELEASED
import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_UNKNOWN
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.getInt
import pl.szczodrzynski.edziennik.getJsonObject import pl.szczodrzynski.edziennik.getJsonObject
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
@ -51,71 +59,97 @@ class IdziennikWebAttendance(override val data: DataIdziennik,
for (jAttendanceEl in json.getAsJsonArray("Obecnosci")) { for (jAttendanceEl in json.getAsJsonArray("Obecnosci")) {
val jAttendance = jAttendanceEl.asJsonObject val jAttendance = jAttendanceEl.asJsonObject
// jAttendance // jAttendance
val attendanceTypeIdziennik = jAttendance.get("TypObecnosci").asInt val type = jAttendance.get("TypObecnosci").asInt
if (attendanceTypeIdziennik == 5 || attendanceTypeIdziennik == 7)
continue // skip "zajęcia nie odbyły się" and "Ferie"
val attendanceDate = Date.fromY_m_d(jAttendance.get("Data").asString) if (type == 5 || type == 7)
val attendanceTime = Time.fromH_m(jAttendance.get("OdDoGodziny").asString)
if (attendanceDate.combineWith(attendanceTime) > System.currentTimeMillis())
continue continue
val attendanceId = jAttendance.get("IdLesson").asString.crc16().toLong() val date = Date.fromY_m_d(jAttendance.get("Data").asString)
val time = Time.fromH_m(jAttendance.get("OdDoGodziny").asString)
if (date.combineWith(time) > System.currentTimeMillis())
continue
val id = jAttendance.get("IdLesson").asString.crc16().toLong()
val rSubject = data.getSubject(jAttendance.get("Przedmiot").asString, jAttendance.get("IdPrzedmiot").asLong, "") val rSubject = data.getSubject(jAttendance.get("Przedmiot").asString, jAttendance.get("IdPrzedmiot").asLong, "")
val rTeacher = data.getTeacherByFDotSpaceLast(jAttendance.get("PrzedmiotNauczyciel").asString) val rTeacher = data.getTeacherByFDotSpaceLast(jAttendance.get("PrzedmiotNauczyciel").asString)
var attendanceName = "obecność" var baseType = TYPE_UNKNOWN
var attendanceType = Attendance.TYPE_CUSTOM var typeName = "nieznany rodzaj"
var typeSymbol: String? = null
var typeColor: Long? = null
when (attendanceTypeIdziennik) { /* https://iuczniowie.progman.pl/idziennik/mod_panelRodzica/obecnosci/obecnosciUcznia_lmt637231494660000000.js */
1 /* nieobecność usprawiedliwiona */ -> { /* https://iuczniowie.progman.pl/idziennik/mod_panelRodzica/obecnosci/obecnosci_lmt637231494660000000.css */
attendanceName = "nieobecność usprawiedliwiona" when (type) {
attendanceType = TYPE_ABSENT_EXCUSED 1 -> {
baseType = TYPE_ABSENT_EXCUSED
typeName = "nieobecność usprawiedliwiona"
typeColor = 0xffffe099
} }
2 /* spóźnienie */ -> { 2 -> {
attendanceName = "spóźnienie" baseType = TYPE_BELATED
attendanceType = TYPE_BELATED typeName = "spóźnienie"
typeColor = 0xffffffaa
} }
3 /* nieobecność nieusprawiedliwiona */ -> { 3 -> {
attendanceName = "nieobecność nieusprawiedliwiona" baseType = TYPE_ABSENT
attendanceType = TYPE_ABSENT typeName = "nieobecność nieusprawiedliwiona"
typeColor = 0xffffad99
} }
4 /* zwolnienie */, 9 /* zwolniony / obecny */ -> { 4, 9 -> {
attendanceType = TYPE_RELEASED baseType = TYPE_RELEASED
if (attendanceTypeIdziennik == 4) if (type == 4) {
attendanceName = "zwolnienie" typeName = "zwolnienie"
if (attendanceTypeIdziennik == 9) typeColor = 0xffa8beff
attendanceName = "zwolnienie / obecność" }
if (type == 9) {
typeName = "zwolniony / obecny"
typeSymbol = "zb"
typeColor = 0xffff69b4
}
} }
0 /* obecny */, 8 /* Wycieczka */ -> { 8 -> {
attendanceType = TYPE_PRESENT baseType = TYPE_PRESENT_CUSTOM
if (attendanceTypeIdziennik == 8) typeName = "wycieczka"
attendanceName = "wycieczka" typeSymbol = "w"
typeColor = null
}
0 -> {
baseType = TYPE_PRESENT
typeName = "obecny"
typeColor = 0xffccffcc
} }
} }
val semester = profile?.dateToSemester(attendanceDate) ?: 1 val semester = profile?.dateToSemester(date) ?: 1
val attendanceObject = Attendance( val attendanceObject = Attendance(
profileId, profileId = profileId,
attendanceId, id = id,
rTeacher.id, baseType = baseType,
rSubject.id, typeName = typeName,
semester, typeShort = typeSymbol ?: data.app.attendanceManager.getTypeShort(baseType),
attendanceName, typeSymbol = typeSymbol ?: data.app.attendanceManager.getTypeShort(baseType),
attendanceDate, typeColor = typeColor?.toInt(),
attendanceTime, date = date,
attendanceType startTime = time,
) semester = semester,
teacherId = rTeacher.id,
subjectId = rSubject.id
).also {
it.lessonTopic = jAttendance.getString("PrzedmiotTemat")
it.lessonNumber = jAttendance.getInt("Godzina")
}
data.attendanceList.add(attendanceObject) data.attendanceList.add(attendanceObject)
if (attendanceObject.type != TYPE_PRESENT) { if (attendanceObject.baseType != TYPE_PRESENT) {
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
profileId, profileId,
Metadata.TYPE_ATTENDANCE, Metadata.TYPE_ATTENDANCE,
attendanceObject.id, attendanceObject.id,
profile?.empty ?: false, profile?.empty ?: false || baseType == TYPE_PRESENT_CUSTOM || baseType == TYPE_UNKNOWN,
profile?.empty ?: false, profile?.empty ?: false || baseType == TYPE_PRESENT_CUSTOM || baseType == TYPE_UNKNOWN
System.currentTimeMillis()
)) ))
} }
} }

View File

@ -17,6 +17,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import java.util.*
class IdziennikWebExams(override val data: DataIdziennik, class IdziennikWebExams(override val data: DataIdziennik,
override val lastSync: Long?, override val lastSync: Long?,
@ -65,28 +66,30 @@ class IdziennikWebExams(override val data: DataIdziennik,
val subjectId = data.getSubject(subjectName, null, subjectName).id val subjectId = data.getSubject(subjectName, null, subjectName).id
val teacherName = exam.getString("wpisal") ?: return@forEach val teacherName = exam.getString("wpisal") ?: return@forEach
val teacherId = data.getTeacherByLastFirst(teacherName).id val teacherId = data.getTeacherByLastFirst(teacherName).id
val topic = exam.getString("zakres") ?: "" val topic = exam.getString("zakres")?.trim() ?: ""
val lessonList = data.db.timetableDao().getForDateNow(profileId, examDate) val lessonList = data.db.timetableDao().getAllForDateNow(profileId, examDate)
val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.startTime val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.startTime
val eventType = when (exam.getString("rodzaj")) { val eventType = when (exam.getString("rodzaj")?.toLowerCase(Locale.getDefault())) {
"sprawdzian/praca klasowa" -> Event.TYPE_EXAM "sprawdzian/praca klasowa",
else -> Event.TYPE_SHORT_QUIZ "sprawdzian",
"praca klasowa" -> Event.TYPE_EXAM
"kartkówka" -> Event.TYPE_SHORT_QUIZ
else -> Event.TYPE_EXAM
} }
val eventObject = Event( val eventObject = Event(
profileId, profileId = profileId,
id, id = id,
examDate, date = examDate,
startTime, time = startTime,
topic, topic = topic,
-1, color = null,
eventType, type = eventType,
false, teacherId = teacherId,
teacherId, subjectId = subjectId,
subjectId, teamId = data.teamClass?.id ?: -1
data.teamClass?.id ?: -1
) )
data.eventList.add(eventObject) data.eventList.add(eventObject)
@ -95,8 +98,7 @@ class IdziennikWebExams(override val data: DataIdziennik,
Metadata.TYPE_EVENT, Metadata.TYPE_EVENT,
eventObject.id, eventObject.id,
profile?.empty ?: false, profile?.empty ?: false,
profile?.empty ?: false, profile?.empty ?: false
System.currentTimeMillis()
)) ))
} }

View File

@ -15,7 +15,7 @@ import pl.szczodrzynski.edziennik.utils.Utils
import java.io.File import java.io.File
class IdziennikWebGetAttachment(override val data: DataIdziennik, class IdziennikWebGetAttachment(override val data: DataIdziennik,
val message: Message, val owner: Any,
val attachmentId: Long, val attachmentId: Long,
val attachmentName: String, val attachmentName: String,
val onSuccess: () -> Unit val onSuccess: () -> Unit
@ -25,6 +25,8 @@ class IdziennikWebGetAttachment(override val data: DataIdziennik,
} }
init { init {
val message = owner as Message
val messageId = "\\[META:([A-z0-9]+);([0-9-]+)]".toRegex().find(message.body ?: "")?.get(2) ?: -1 val messageId = "\\[META:([A-z0-9]+);([0-9-]+)]".toRegex().find(message.body ?: "")?.get(2) ?: -1
val targetFile = File(Utils.getStorageDir(), attachmentName) val targetFile = File(Utils.getStorageDir(), attachmentName)
@ -34,29 +36,29 @@ class IdziennikWebGetAttachment(override val data: DataIdziennik,
), { file -> ), { file ->
val event = AttachmentGetEvent( val event = AttachmentGetEvent(
profileId, profileId,
message.id, owner,
attachmentId, attachmentId,
AttachmentGetEvent.TYPE_FINISHED, AttachmentGetEvent.TYPE_FINISHED,
file.absolutePath file.absolutePath
) )
val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.messageId}_${event.attachmentId}") val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.ownerId}_${event.attachmentId}")
Utils.writeStringToFile(attachmentDataFile, event.fileName) Utils.writeStringToFile(attachmentDataFile, event.fileName)
EventBus.getDefault().post(event) EventBus.getDefault().postSticky(event)
onSuccess() onSuccess()
}) { written, _ -> }) { written, _ ->
val event = AttachmentGetEvent( val event = AttachmentGetEvent(
profileId, profileId,
message.id, owner,
attachmentId, attachmentId,
AttachmentGetEvent.TYPE_PROGRESS, AttachmentGetEvent.TYPE_PROGRESS,
bytesWritten = written bytesWritten = written
) )
EventBus.getDefault().post(event) EventBus.getDefault().postSticky(event)
} }
} }
} }

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-4-1.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_GET_HOMEWORK
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.events.EventGetEvent
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.getBoolean
import pl.szczodrzynski.edziennik.getJsonObject
import pl.szczodrzynski.edziennik.getString
class IdziennikWebGetHomework(override val data: DataIdziennik,
val event: EventFull,
val onSuccess: () -> Unit
) : IdziennikWeb(data, null) {
companion object {
private const val TAG = "IdziennikWebGetHomework"
}
init {
webApiGet(TAG, IDZIENNIK_WEB_GET_HOMEWORK, mapOf(
"idP" to data.registerId,
"idPD" to event.id
)) { result ->
val json = result.getJsonObject("d") ?: run {
data.error(ApiError(TAG, ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA)
.withApiResponse(result))
return@webApiGet
}
val homework = json.getJsonObject("praca") ?: return@webApiGet
if (homework.getBoolean("zalacznik", false)) {
event.attachmentIds = mutableListOf(event.id)
event.attachmentNames = mutableListOf("Załącznik do zadania")
}
else {
event.attachmentIds = mutableListOf()
event.attachmentNames = mutableListOf()
}
event.homeworkBody = homework.getString("tresc")
data.eventList.add(event)
data.eventListReplace = true
EventBus.getDefault().postSticky(EventGetEvent(event))
onSuccess()
}
}
}

View File

@ -0,0 +1,100 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-4-1.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
import com.google.gson.JsonObject
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_GET_HOMEWORK_ATTACHMENT
import pl.szczodrzynski.edziennik.data.api.Regexes
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent
import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.set
import pl.szczodrzynski.edziennik.utils.Utils
import java.io.File
class IdziennikWebGetHomeworkAttachment(override val data: DataIdziennik,
val owner: Any,
val attachmentId: Long,
val attachmentName: String,
val onSuccess: () -> Unit
) : IdziennikWeb(data, null) {
companion object {
const val TAG = "IdziennikWebGetHomeworkAttachment"
}
init {
val homework = owner as Event
/*val request = Request.Builder()
.url("")
.build()
data.app.http.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
data.error(ApiError(TAG, ERROR_REQUEST_FAILURE)
.withThrowable(e))
}
override fun onResponse(call: Call, response: Response) {
val filename = response.header("content-disposition")?.substringAfter("\"")?.substringBeforeLast("\"")
val file: File = File(Utils.getStorageDir(), filename)
val sink = file.sink().buffer()
response.body()?.source()?.let {
sink.writeAll(it)
}
sink.close()
}
})*/
webGet(TAG, IDZIENNIK_WEB_GET_HOMEWORK_ATTACHMENT) { text ->
val hiddenFields = JsonObject()
Regexes.IDZIENNIK_LOGIN_HIDDEN_FIELDS.findAll(text).forEach {
hiddenFields[it[1]] = it[2]
}
webGetFile(TAG, IDZIENNIK_WEB_GET_HOMEWORK_ATTACHMENT, Utils.getStorageDir(), mapOf(
"__VIEWSTATE" to hiddenFields.getString("__VIEWSTATE", ""),
"__VIEWSTATEGENERATOR" to hiddenFields.getString("__VIEWSTATEGENERATOR", ""),
"__EVENTVALIDATION" to hiddenFields.getString("__EVENTVALIDATION", ""),
"__EVENTTARGET" to "ctl00\$cphContent\$bt_pobraniePliku",
"ctl00\$dxComboUczniowie" to data.registerId,
"ctl00\$cphContent\$idPracyDomowej" to attachmentId
), { file ->
val event = AttachmentGetEvent(
profileId,
owner,
attachmentId,
AttachmentGetEvent.TYPE_FINISHED,
file.absolutePath
)
val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.ownerId}_${event.attachmentId}")
Utils.writeStringToFile(attachmentDataFile, event.fileName)
homework.attachmentNames = mutableListOf(file.name)
data.eventList.add(homework)
data.eventListReplace = true
EventBus.getDefault().postSticky(event)
onSuccess()
}) { written, _ ->
val event = AttachmentGetEvent(
profileId,
owner,
attachmentId,
AttachmentGetEvent.TYPE_PROGRESS,
bytesWritten = written
)
EventBus.getDefault().postSticky(event)
}
}
}
}

View File

@ -10,8 +10,8 @@ import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_GET_MESSAGE
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_SENT
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.data.db.full.MessageRecipientFull import pl.szczodrzynski.edziennik.data.db.full.MessageRecipientFull
@ -50,7 +50,11 @@ class IdziennikWebGetMessage(override val data: DataIdziennik,
message.recipients?.clear() message.recipients?.clear()
when (message.type) { when (message.type) {
TYPE_RECEIVED -> { TYPE_RECEIVED -> {
val recipientObject = MessageRecipientFull(profileId, -1, message.id) val recipientObject = MessageRecipientFull(
profileId = profileId,
id = -1,
messageId = message.id
)
val readDateString = it.getString("DataOdczytania") val readDateString = it.getString("DataOdczytania")
recipientObject.readDate = if (readDateString.isNullOrBlank()) System.currentTimeMillis() recipientObject.readDate = if (readDateString.isNullOrBlank()) System.currentTimeMillis()
@ -67,7 +71,11 @@ class IdziennikWebGetMessage(override val data: DataIdziennik,
val recipientName = recipient.getString("NazwaOdbiorcy") ?: return@forEach val recipientName = recipient.getString("NazwaOdbiorcy") ?: return@forEach
val teacher = data.getTeacherByLastFirst(recipientName) val teacher = data.getTeacherByLastFirst(recipientName)
val recipientObject = MessageRecipientFull(profileId, teacher.id, message.id) val recipientObject = MessageRecipientFull(
profileId = profileId,
id = teacher.id,
messageId = message.id
)
recipientObject.readDate = recipient.getLong("Status") ?: return@forEach recipientObject.readDate = recipient.getLong("Status") ?: return@forEach
recipientObject.fullName = teacher.fullName recipientObject.fullName = teacher.fullName
@ -86,14 +94,14 @@ class IdziennikWebGetMessage(override val data: DataIdziennik,
Metadata.TYPE_MESSAGE, Metadata.TYPE_MESSAGE,
message.id, message.id,
message.seen, message.seen,
message.notified, message.notified
message.addedDate
)) ))
} }
EventBus.getDefault().postSticky(MessageGetEvent(message))
data.messageList.add(message) data.messageList.add(message)
data.messageListReplace = true
EventBus.getDefault().postSticky(MessageGetEvent(message))
onSuccess() onSuccess()
} }
} }

View File

@ -14,6 +14,7 @@ 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.ApiError
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Grade import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_NORMAL
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
@ -62,18 +63,24 @@ class IdziennikWebGrades(override val data: DataIdziennik,
colorInt = Color.parseColor("#$gradeColor") colorInt = Color.parseColor("#$gradeColor")
} }
val addedDate = grade.getString("Data_wystaw")?.let { Date.fromY_m_d(it).inMillis } ?: System.currentTimeMillis()
val gradeObject = Grade( val gradeObject = Grade(
profileId, profileId = profileId,
id, id = id,
category, name = name,
colorInt, type = TYPE_NORMAL,
"", value = value,
name, weight = weight,
value, color = colorInt,
weight, category = category,
semester, description = null,
teacher.id, comment = null,
subject.id) semester = semester,
teacherId = teacher.id,
subjectId = subject.id,
addedDate = addedDate
)
when (grade.getInt("Typ")) { when (grade.getInt("Typ")) {
0 -> { 0 -> {
@ -97,30 +104,33 @@ class IdziennikWebGrades(override val data: DataIdziennik,
colorInt = Color.parseColor("#$historyColor") colorInt = Color.parseColor("#$historyColor")
} }
val historyObject = Grade(
profileId,
gradeObject.id * -1,
historyItem.get("Kategoria").asString,
colorInt,
historyItem.get("Uzasadnienie").asString,
historyItem.get("Ocena").asString,
value,
if (value > 0f && countToTheAverage) weight * -1f else 0f,
historyItem.get("Semestr").asInt,
teacher.id,
subject.id)
historyObject.parentId = gradeObject.id
val addedDate = historyItem.getString("Data_wystaw")?.let { Date.fromY_m_d(it).inMillis } ?: System.currentTimeMillis() val addedDate = historyItem.getString("Data_wystaw")?.let { Date.fromY_m_d(it).inMillis } ?: System.currentTimeMillis()
val historyObject = Grade(
profileId = profileId,
id = gradeObject.id * -1,
name = historyItem.getString("Ocena") ?: "",
type = TYPE_NORMAL,
value = value,
weight = if (value > 0f && countToTheAverage) weight * -1f else 0f,
color = colorInt,
category = historyItem.getString("Kategoria"),
description = historyItem.getString("Uzasadnienie"),
comment = null,
semester = historyItem.getInt("Semestr") ?: 1,
teacherId = teacher.id,
subjectId = subject.id,
addedDate = addedDate
)
historyObject.parentId = gradeObject.id
data.gradeList.add(historyObject) data.gradeList.add(historyObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
profileId, profileId,
Metadata.TYPE_GRADE, Metadata.TYPE_GRADE,
historyObject.id, historyObject.id,
true, true,
true, true
addedDate
)) ))
} }
// update the current grade's value with an average of all historical grades and itself // update the current grade's value with an average of all historical grades and itself
@ -142,8 +152,6 @@ class IdziennikWebGrades(override val data: DataIdziennik,
} }
} }
val addedDate = grade.getString("Data_wystaw")?.let { Date.fromY_m_d(it).inMillis } ?: System.currentTimeMillis()
data.gradeList.add(gradeObject) data.gradeList.add(gradeObject)
data.metadataList.add( data.metadataList.add(
Metadata( Metadata(
@ -151,8 +159,7 @@ class IdziennikWebGrades(override val data: DataIdziennik,
Metadata.TYPE_GRADE, Metadata.TYPE_GRADE,
id, id,
data.profile.empty, data.profile.empty,
data.profile.empty, data.profile.empty
addedDate
)) ))
} }
} }

View File

@ -52,13 +52,14 @@ class IdziennikWebHomework(override val data: DataIdziennik,
json.getJsonArray("ListK")?.asJsonObjectList()?.forEach { homework -> json.getJsonArray("ListK")?.asJsonObjectList()?.forEach { homework ->
val id = homework.getLong("_recordId") ?: return@forEach val id = homework.getLong("_recordId") ?: return@forEach
val eventDate = Date.fromY_m_d(homework.getString("dataO") ?: return@forEach) val eventDate = Date.fromY_m_d(homework.getString("dataO") ?: return@forEach)
val addedDate = Date.fromY_m_d(homework.getString("dataZ") ?: return@forEach)
val subjectName = homework.getString("przed") ?: return@forEach val subjectName = homework.getString("przed") ?: return@forEach
val subjectId = data.getSubject(subjectName, null, subjectName).id val subjectId = data.getSubject(subjectName, null, subjectName).id
val teacherName = homework.getString("usr") ?: return@forEach val teacherName = homework.getString("usr") ?: return@forEach
val teacherId = data.getTeacherByLastFirst(teacherName).id val teacherId = data.getTeacherByLastFirst(teacherName).id
val lessonList = data.db.timetableDao().getForDateNow(profileId, eventDate) val lessonList = data.db.timetableDao().getAllForDateNow(profileId, eventDate)
val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.displayStartTime val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.displayStartTime
val topic = homework.getString("tytul") ?: "" val topic = homework.getString("tytul")?.trim() ?: ""
val seen = when (profile?.empty) { val seen = when (profile?.empty) {
true -> true true -> true
@ -67,17 +68,17 @@ class IdziennikWebHomework(override val data: DataIdziennik,
val eventObject = Event( val eventObject = Event(
profileId, profileId = profileId,
id, id = id,
eventDate, date = eventDate,
startTime, time = startTime,
topic, topic = topic,
-1, color = null,
Event.TYPE_HOMEWORK, type = Event.TYPE_HOMEWORK,
false, teacherId = teacherId,
teacherId, subjectId = subjectId,
subjectId, teamId = data.teamClass?.id ?: -1,
data.teamClass?.id ?: -1 addedDate = addedDate.inMillis
) )
data.eventList.add(eventObject) data.eventList.add(eventObject)
@ -86,8 +87,7 @@ class IdziennikWebHomework(override val data: DataIdziennik,
Metadata.TYPE_HOMEWORK, Metadata.TYPE_HOMEWORK,
eventObject.id, eventObject.id,
seen, seen,
seen, seen
System.currentTimeMillis()
)) ))
} }

View File

@ -13,9 +13,12 @@ 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.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Notice import pl.szczodrzynski.edziennik.data.db.entity.Notice
import pl.szczodrzynski.edziennik.data.db.entity.Notice.* import pl.szczodrzynski.edziennik.data.db.entity.Notice.Companion.TYPE_NEGATIVE
import pl.szczodrzynski.edziennik.data.db.entity.Notice.Companion.TYPE_NEUTRAL
import pl.szczodrzynski.edziennik.data.db.entity.Notice.Companion.TYPE_POSITIVE
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.getJsonObject import pl.szczodrzynski.edziennik.getJsonObject
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class IdziennikWebNotices(override val data: DataIdziennik, class IdziennikWebNotices(override val data: DataIdziennik,
@ -53,20 +56,24 @@ class IdziennikWebNotices(override val data: DataIdziennik,
} }
val noticeObject = Notice( val noticeObject = Notice(
profileId, profileId = profileId,
noticeId, id = noticeId,
jNotice.get("Tresc").asString, type = nType,
jNotice.get("Semestr").asInt, semester = jNotice.get("Semestr").asInt,
nType, text = jNotice.getString("Tresc") ?: "",
rTeacher.id) category = null,
points = null,
teacherId = rTeacher.id,
addedDate = addedDate.inMillis
)
data.noticeList.add(noticeObject) data.noticeList.add(noticeObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
profileId, profileId,
Metadata.TYPE_NOTICE, Metadata.TYPE_NOTICE,
noticeObject.id, noticeObject.id,
profile?.empty ?: false, profile?.empty ?: false,
profile?.empty ?: false, profile?.empty ?: false
addedDate.inMillis
)) ))
} }

View File

@ -13,14 +13,14 @@ 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.ApiError
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Grade import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_SEMESTER1_PROPOSED import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_DESCRIPTIVE
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_YEAR_PROPOSED import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.getJsonArray import pl.szczodrzynski.edziennik.getJsonArray
import pl.szczodrzynski.edziennik.getJsonObject import pl.szczodrzynski.edziennik.getJsonObject
import pl.szczodrzynski.edziennik.getString import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.Utils.getWordGradeValue
class IdziennikWebProposedGrades(override val data: DataIdziennik, class IdziennikWebProposedGrades(override val data: DataIdziennik,
override val lastSync: Long?, override val lastSync: Long?,
@ -39,35 +39,64 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
.withApiResponse(result)) .withApiResponse(result))
return@webApiGet return@webApiGet
} }
val manager = data.app.gradesManager
json.getJsonArray("Przedmioty")?.asJsonObjectList()?.forEach { subject -> json.getJsonArray("Przedmioty")?.asJsonObjectList()?.forEach { subject ->
val subjectName = subject.getString("Przedmiot") ?: return@forEach val subjectName = subject.getString("Przedmiot") ?: return@forEach
val subjectObject = data.getSubject(subjectName, null, subjectName) val subjectObject = data.getSubject(subjectName, null, subjectName)
val semester1Proposed = subject.getString("OcenaSem1") ?: "" val semester1Proposed = subject.getString("OcenaSem1") ?: ""
val semester1Value = getWordGradeValue(semester1Proposed) val semester1Value = manager.getGradeValue(semester1Proposed)
val semester1Id = subjectObject.id * (-100) - 1 val semester1Id = subjectObject.id * (-100) - 1
val semester1Type =
if (semester1Value == 0f) TYPE_DESCRIPTIVE
else TYPE_SEMESTER1_PROPOSED
val semester1Name = when {
semester1Value == 0f -> " "
semester1Value % 1.0f == 0f -> semester1Value.toInt().toString()
else -> semester1Value.toString()
}
val semester1Color =
if (semester1Value == 0f) 0xff536dfe.toInt()
else -1
val semester2Proposed = subject.getString("OcenaSem2") ?: "" val semester2Proposed = subject.getString("OcenaSem2") ?: ""
val semester2Value = getWordGradeValue(semester2Proposed) val semester2Value = manager.getGradeValue(semester2Proposed)
val semester2Id = subjectObject.id * (-100) - 2 val semester2Id = subjectObject.id * (-100) - 2
val semester2Type =
if (semester2Value == 0f) TYPE_DESCRIPTIVE
else TYPE_YEAR_PROPOSED
val semester2Name = when {
semester2Value == 0f -> " "
semester2Value % 1.0f == 0f -> semester2Value.toInt().toString()
else -> semester2Value.toString()
}
val semester2Color =
if (semester2Value == 0f) 0xffff4081.toInt()
else -1
if (semester1Proposed != "") { if (semester1Proposed != "") {
val addedDate = if (data.profile.empty)
data.profile.dateSemester1Start.inMillis
else
System.currentTimeMillis()
val gradeObject = Grade( val gradeObject = Grade(
profileId, profileId = profileId,
semester1Id, id = semester1Id,
"", name = semester1Name,
-1, type = semester1Type,
"", value = semester1Value,
semester1Value.toString(), weight = 0f,
semester1Value.toFloat(), color = semester1Color,
0f, category = if (semester1Value == 0f) "Ocena opisowa semestralna" else null,
1, description = if (semester1Value == 0f) semester1Proposed else null,
-1, comment = null,
subjectObject.id semester = 1,
).apply { teacherId = -1,
type = TYPE_SEMESTER1_PROPOSED subjectId = subjectObject.id,
} addedDate = addedDate
)
data.gradeList.add(gradeObject) data.gradeList.add(gradeObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
@ -75,27 +104,32 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
Metadata.TYPE_GRADE, Metadata.TYPE_GRADE,
gradeObject.id, gradeObject.id,
profile.empty, profile.empty,
profile.empty, profile.empty
System.currentTimeMillis()
)) ))
} }
if (semester2Proposed != "") { if (semester2Proposed != "") {
val addedDate = if (data.profile.empty)
data.profile.dateSemester2Start.inMillis
else
System.currentTimeMillis()
val gradeObject = Grade( val gradeObject = Grade(
profileId, profileId = profileId,
semester2Id, id = semester2Id,
"", name = semester2Name,
-1, type = semester2Type,
"", value = semester2Value,
semester2Value.toString(), weight = 0f,
semester2Value.toFloat(), color = semester2Color,
0f, category = if (semester2Value == 0f) "Ocena opisowa końcoworoczna" else null,
2, description = if (semester2Value == 0f) semester2Proposed else null,
-1, comment = null,
subjectObject.id semester = 2,
).apply { teacherId = -1,
type = TYPE_YEAR_PROPOSED subjectId = subjectObject.id,
} addedDate = addedDate
)
data.gradeList.add(gradeObject) data.gradeList.add(gradeObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
@ -103,8 +137,7 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
Metadata.TYPE_GRADE, Metadata.TYPE_GRADE,
gradeObject.id, gradeObject.id,
profile.empty, profile.empty,
profile.empty, profile.empty
System.currentTimeMillis()
)) ))
} }
} }

View File

@ -57,9 +57,9 @@ class IdziennikWebSendMessage(override val data: DataIdziennik,
} }
IdziennikApiMessagesSent(data, null) { IdziennikApiMessagesSent(data, null) {
val message = data.messageIgnoreList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject } val message = data.messageList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject }
val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id } val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id }
val event = MessageSentEvent(data.profileId, message, metadata?.addedDate) val event = MessageSentEvent(data.profileId, message, message?.addedDate)
EventBus.getDefault().postSticky(event) EventBus.getDefault().postSticky(event)
onSuccess() onSuccess()

View File

@ -0,0 +1,36 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_HOME
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.get
import pl.szczodrzynski.edziennik.getString
class IdziennikWebSwitchRegister(override val data: DataIdziennik,
val registerId: Int,
val onSuccess: () -> Unit
) : IdziennikWeb(data, null) {
companion object {
private const val TAG = "IdziennikWebSwitchRegister"
}
init {
val hiddenFields = data.loginStore.getLoginData("hiddenFields", JsonObject())
// TODO error checking
webGet(TAG, IDZIENNIK_WEB_HOME, mapOf(
"__VIEWSTATE" to hiddenFields.getString("__VIEWSTATE", ""),
"__VIEWSTATEGENERATOR" to hiddenFields.getString("__VIEWSTATEGENERATOR", ""),
"__EVENTVALIDATION" to hiddenFields.getString("__EVENTVALIDATION", ""),
"ctl00\$dxComboUczniowie" to registerId
)) { text ->
Regexes.IDZIENNIK_WEB_SELECTED_REGISTER.find(text)?.let {
val registerId = it[1].toIntOrNull() ?: return@let
data.webSelectedRegister = registerId
}
onSuccess()
}
}
}

View File

@ -165,8 +165,7 @@ class IdziennikWebTimetable(override val data: DataIdziennik,
Metadata.TYPE_LESSON_CHANGE, Metadata.TYPE_LESSON_CHANGE,
lessonObject.id, lessonObject.id,
seen, seen,
seen, seen
System.currentTimeMillis()
)) ))
} }
} }

View File

@ -89,7 +89,7 @@ class IdziennikFirstLogin(val data: DataIdziennik, val onSuccess: () -> Unit) {
profileList.add(profile) profileList.add(profile)
} }
EventBus.getDefault().post(FirstLoginFinishedEvent(profileList, data.loginStore)) EventBus.getDefault().postSticky(FirstLoginFinishedEvent(profileList, data.loginStore))
onSuccess() onSuccess()
} }
} }

View File

@ -7,15 +7,14 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login
import im.wangchao.mhttp.Request import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response import im.wangchao.mhttp.Response
import im.wangchao.mhttp.callback.TextCallbackHandler import im.wangchao.mhttp.callback.TextCallbackHandler
import okhttp3.Cookie import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.HOUR
import pl.szczodrzynski.edziennik.MINUTE
import pl.szczodrzynski.edziennik.data.api.* import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.get import pl.szczodrzynski.edziennik.data.db.entity.LuckyNumber
import pl.szczodrzynski.edziennik.getUnixDate import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.models.Date
class IdziennikLoginWeb(val data: DataIdziennik, val onSuccess: () -> Unit) { class IdziennikLoginWeb(val data: DataIdziennik, val onSuccess: () -> Unit) {
companion object { companion object {
@ -24,22 +23,12 @@ class IdziennikLoginWeb(val data: DataIdziennik, val onSuccess: () -> Unit) {
init { run { init { run {
if (data.isWebLoginValid()) { if (data.isWebLoginValid()) {
data.app.cookieJar.saveFromResponse(null, listOf( data.app.cookieJar.set("iuczniowie.progman.pl", "ASP.NET_SessionId_iDziennik", data.webSessionId)
Cookie.Builder() data.app.cookieJar.set("iuczniowie.progman.pl", ".ASPXAUTH", data.webAuth)
.name("ASP.NET_SessionId_iDziennik")
.value(data.webSessionId!!)
.domain("iuczniowie.progman.pl")
.secure().httpOnly().build(),
Cookie.Builder()
.name(".ASPXAUTH")
.value(data.webAuth!!)
.domain("iuczniowie.progman.pl")
.secure().httpOnly().build()
))
onSuccess() onSuccess()
} }
else { else {
data.app.cookieJar.clearForDomain("iuczniowie.progman.pl") data.app.cookieJar.clear("iuczniowie.progman.pl")
if (data.webSchoolName != null && data.webUsername != null && data.webPassword != null) { if (data.webSchoolName != null && data.webUsername != null && data.webPassword != null) {
loginWithCredentials() loginWithCredentials()
} }
@ -62,13 +51,54 @@ class IdziennikLoginWeb(val data: DataIdziennik, val onSuccess: () -> Unit) {
// login succeeded: there is a start page // login succeeded: there is a start page
if (text.contains("czyWyswietlicDostepMobilny")) { if (text.contains("czyWyswietlicDostepMobilny")) {
val cookies = data.app.cookieJar.getForDomain("iuczniowie.progman.pl") val cookies = data.app.cookieJar.getAll("iuczniowie.progman.pl")
run { run {
data.webSessionId = cookies.singleOrNull { it.name() == "ASP.NET_SessionId_iDziennik" }?.value() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_SESSION data.webSessionId = cookies["ASP.NET_SessionId_iDziennik"] ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_SESSION
data.webAuth = cookies.singleOrNull { it.name() == ".ASPXAUTH" }?.value() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_AUTH data.webAuth = cookies[".ASPXAUTH"] ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_AUTH
data.apiBearer = cookies.singleOrNull { it.name() == "Bearer" }?.value() ?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_BEARER data.apiBearer = cookies["Bearer"]?: return@run ERROR_LOGIN_IDZIENNIK_WEB_NO_BEARER
data.loginExpiryTime = response.getUnixDate() + 30 * MINUTE /* after about 40 minutes the login didn't work already */ data.loginExpiryTime = response.getUnixDate() + 30 * MINUTE /* after about 40 minutes the login didn't work already */
data.apiExpiryTime = response.getUnixDate() + 12 * HOUR /* actually it expires after 24 hours but I'm not sure when does the token refresh. */ data.apiExpiryTime = response.getUnixDate() + 12 * HOUR /* actually it expires after 24 hours but I'm not sure when does the token refresh. */
val hiddenFields = JsonObject()
Regexes.IDZIENNIK_LOGIN_HIDDEN_FIELDS.findAll(text).forEach {
hiddenFields[it[1]] = it[2]
}
data.loginStore.putLoginData("hiddenFields", hiddenFields)
Regexes.IDZIENNIK_WEB_SELECTED_REGISTER.find(text)?.let {
val registerId = it[1].toIntOrNull() ?: return@let
data.webSelectedRegister = registerId
}
// for profiles created after archiving
data.schoolYearId = Regexes.IDZIENNIK_LOGIN_FIRST_SCHOOL_YEAR.find(text)?.let {
it[1].toIntOrNull()
} ?: data.schoolYearId
data.profile?.studentClassName = Regexes.IDZIENNIK_LOGIN_FIRST_STUDENT.findAll(text)
.firstOrNull { it[1].toIntOrNull() == data.registerId }
?.let { "${it[5]} ${it[6]}" } ?: data.profile?.studentClassName
data.profile?.let { profile ->
Regexes.IDZIENNIK_WEB_LUCKY_NUMBER.find(text)?.also {
val number = it[1].toIntOrNull() ?: return@also
val luckyNumberObject = LuckyNumber(
profileId = data.profileId,
date = Date.getToday(),
number = number
)
data.luckyNumberList.add(luckyNumberObject)
data.metadataList.add(
Metadata(
profile.id,
Metadata.TYPE_LUCKY_NUMBER,
luckyNumberObject.date.value.toLong(),
true,
profile.empty
))
}
}
return@run null return@run null
}?.let { errorCode -> }?.let { errorCode ->
data.error(ApiError(TAG, errorCode) data.error(ApiError(TAG, errorCode)

View File

@ -4,7 +4,6 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.librus package pl.szczodrzynski.edziennik.data.api.edziennik.librus
import okhttp3.Cookie
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.currentTimeUnix import pl.szczodrzynski.edziennik.currentTimeUnix
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_LIBRUS_API import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_LIBRUS_API
@ -31,23 +30,11 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
loginMethods += LOGIN_METHOD_LIBRUS_API loginMethods += LOGIN_METHOD_LIBRUS_API
if (isSynergiaLoginValid()) { if (isSynergiaLoginValid()) {
loginMethods += LOGIN_METHOD_LIBRUS_SYNERGIA loginMethods += LOGIN_METHOD_LIBRUS_SYNERGIA
app.cookieJar.saveFromResponse(null, listOf( app.cookieJar.set("synergia.librus.pl", "DZIENNIKSID", synergiaSessionId)
Cookie.Builder()
.name("DZIENNIKSID")
.value(synergiaSessionId!!)
.domain("synergia.librus.pl")
.secure().httpOnly().build()
))
} }
if (isMessagesLoginValid()) { if (isMessagesLoginValid()) {
loginMethods += LOGIN_METHOD_LIBRUS_MESSAGES loginMethods += LOGIN_METHOD_LIBRUS_MESSAGES
app.cookieJar.saveFromResponse(null, listOf( app.cookieJar.set("wiadomosci.librus.pl", "DZIENNIKSID", messagesSessionId)
Cookie.Builder()
.name("DZIENNIKSID")
.value(messagesSessionId!!)
.domain("wiadomosci.librus.pl")
.secure().httpOnly().build()
))
} }
} }
@ -133,7 +120,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiLogin: String? = null private var mApiLogin: String? = null
var apiLogin: String? var apiLogin: String?
get() { mApiLogin = mApiLogin ?: profile?.getStudentData("accountLogin", null); return mApiLogin } get() { mApiLogin = mApiLogin ?: profile?.getStudentData("accountLogin", null); return mApiLogin }
set(value) { profile?.putStudentData("accountLogin", value) ?: return; mApiLogin = value } set(value) { profile?.putStudentData("accountLogin", value); mApiLogin = value }
/** /**
* A Synergia password. * A Synergia password.
* Used: for login (API Login Method) in Synergia mode. * Used: for login (API Login Method) in Synergia mode.
@ -142,7 +129,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiPassword: String? = null private var mApiPassword: String? = null
var apiPassword: String? var apiPassword: String?
get() { mApiPassword = mApiPassword ?: profile?.getStudentData("accountPassword", null); return mApiPassword } get() { mApiPassword = mApiPassword ?: profile?.getStudentData("accountPassword", null); return mApiPassword }
set(value) { profile?.putStudentData("accountPassword", value) ?: return; mApiPassword = value } set(value) { profile?.putStudentData("accountPassword", value); mApiPassword = value }
/** /**
* A JST login Code. * A JST login Code.
@ -151,8 +138,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiCode: String? = null private var mApiCode: String? = null
var apiCode: String? var apiCode: String?
get() { mApiCode = mApiCode ?: loginStore.getLoginData("accountCode", null); return mApiCode } get() { mApiCode = mApiCode ?: loginStore.getLoginData("accountCode", null); return mApiCode }
set(value) { set(value) { profile?.putStudentData("accountCode", value); mApiCode = value }
loginStore.putLoginData("accountCode", value); mApiCode = value }
/** /**
* A JST login PIN. * A JST login PIN.
* Used only during first login in JST mode. * Used only during first login in JST mode.
@ -160,8 +146,7 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
private var mApiPin: String? = null private var mApiPin: String? = null
var apiPin: String? var apiPin: String?
get() { mApiPin = mApiPin ?: loginStore.getLoginData("accountPin", null); return mApiPin } get() { mApiPin = mApiPin ?: loginStore.getLoginData("accountPin", null); return mApiPin }
set(value) { set(value) { profile?.putStudentData("accountPin", value); mApiPin = value }
loginStore.putLoginData("accountPin", value); mApiPin = value }
/** /**
* A Synergia API access token. * A Synergia API access token.
@ -191,6 +176,16 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
get() { mApiTokenExpiryTime = mApiTokenExpiryTime ?: profile?.getStudentData("accountTokenTime", 0L); return mApiTokenExpiryTime ?: 0L } get() { mApiTokenExpiryTime = mApiTokenExpiryTime ?: profile?.getStudentData("accountTokenTime", 0L); return mApiTokenExpiryTime ?: 0L }
set(value) { mApiTokenExpiryTime = value; profile?.putStudentData("accountTokenTime", value) ?: return; } set(value) { mApiTokenExpiryTime = value; profile?.putStudentData("accountTokenTime", value) ?: return; }
/**
* A push device ID, generated by Librus when registering
* a FCM token. I don't really know if this has any use,
* but it may be worthy to save that ID.
*/
private var mPushDeviceId: Int? = null
var pushDeviceId: Int
get() { mPushDeviceId = mPushDeviceId ?: profile?.getStudentData("pushDeviceId", 0); return mPushDeviceId ?: 0 }
set(value) { mPushDeviceId = value; profile?.putStudentData("pushDeviceId", value) ?: return; }
/* _____ _ /* _____ _
/ ____| (_) / ____| (_)
| (___ _ _ _ __ ___ _ __ __ _ _ __ _ | (___ _ _ _ __ ___ _ __ __ _ _ __ _
@ -280,4 +275,10 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
var timetableNotPublic: Boolean var timetableNotPublic: Boolean
get() { mTimetableNotPublic = mTimetableNotPublic ?: profile?.getStudentData("timetableNotPublic", false); return mTimetableNotPublic ?: false } get() { mTimetableNotPublic = mTimetableNotPublic ?: profile?.getStudentData("timetableNotPublic", false); return mTimetableNotPublic ?: false }
set(value) { profile?.putStudentData("timetableNotPublic", value) ?: return; mTimetableNotPublic = value } set(value) { profile?.putStudentData("timetableNotPublic", value) ?: return; mTimetableNotPublic = value }
/**
* Set to false when Recaptcha helper doesn't provide a working token.
* When it's set to false uses Synergia for messages.
*/
var messagesLoginSuccessful: Boolean = true
} }

View File

@ -13,7 +13,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.Librus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesGetMessage import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesGetMessage
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesGetRecipientList import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesGetRecipientList
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesSendMessage import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesSendMessage
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.LibrusSynergiaMarkAllAnnouncementsAsRead import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.firstlogin.LibrusFirstLogin import pl.szczodrzynski.edziennik.data.api.edziennik.librus.firstlogin.LibrusFirstLogin
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLogin import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLogin
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
@ -24,6 +24,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
@ -56,9 +57,9 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_| |_| |_| |_|\___| /_/ \_\_|\__, |\___/|_| |_|\__|_| |_|_| |_| |_|
__/ | __/ |
|__*/ |__*/
override fun sync(featureIds: List<Int>, viewId: Int?, arguments: JsonObject?) { override fun sync(featureIds: List<Int>, viewId: Int?, onlyEndpoints: List<Int>?, arguments: JsonObject?) {
data.arguments = arguments data.arguments = arguments
data.prepare(librusLoginMethods, LibrusFeatures, featureIds, viewId) data.prepare(librusLoginMethods, LibrusFeatures, featureIds, viewId, onlyEndpoints)
login() login()
} }
@ -88,9 +89,8 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
override fun getMessage(message: MessageFull) { override fun getMessage(message: MessageFull) {
login(LOGIN_METHOD_LIBRUS_MESSAGES) { login(LOGIN_METHOD_LIBRUS_MESSAGES) {
LibrusMessagesGetMessage(data, message) { if (data.messagesLoginSuccessful) LibrusMessagesGetMessage(data, message) { completed() }
completed() else LibrusSynergiaGetMessage(data, message) { completed() }
}
} }
} }
@ -118,11 +118,22 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
} }
} }
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) { override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) {
login(LOGIN_METHOD_LIBRUS_MESSAGES) { when (owner) {
LibrusMessagesGetAttachment(data, message, attachmentId, attachmentName) { is Message -> {
completed() login(LOGIN_METHOD_LIBRUS_SYNERGIA) {
if (data.messagesLoginSuccessful) LibrusMessagesGetAttachment(data, owner, attachmentId, attachmentName) { completed() }
LibrusSynergiaGetAttachment(data, owner, attachmentId, attachmentName) { completed() }
}
} }
is EventFull -> {
login(LOGIN_METHOD_LIBRUS_SYNERGIA) {
LibrusSynergiaHomeworkGetAttachment(data, owner, attachmentId, attachmentName) {
completed()
}
}
}
else -> completed()
} }
} }
@ -134,6 +145,14 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
} }
} }
override fun getEvent(eventFull: EventFull) {
login(LOGIN_METHOD_LIBRUS_SYNERGIA) {
LibrusSynergiaGetHomework(data, eventFull) {
completed()
}
}
}
override fun firstLogin() { LibrusFirstLogin(data) { completed() } } override fun firstLogin() { LibrusFirstLogin(data) { completed() } }
override fun cancel() { override fun cancel() {
d(TAG, "Cancelled") d(TAG, "Cancelled")
@ -180,6 +199,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
} }
ERROR_LOGIN_LIBRUS_PORTAL_NO_CODE, ERROR_LOGIN_LIBRUS_PORTAL_NO_CODE,
ERROR_LOGIN_LIBRUS_PORTAL_CSRF_MISSING, ERROR_LOGIN_LIBRUS_PORTAL_CSRF_MISSING,
ERROR_LOGIN_LIBRUS_PORTAL_CSRF_EXPIRED,
ERROR_LOGIN_LIBRUS_PORTAL_CODE_REVOKED, ERROR_LOGIN_LIBRUS_PORTAL_CODE_REVOKED,
ERROR_LOGIN_LIBRUS_PORTAL_CODE_EXPIRED -> { ERROR_LOGIN_LIBRUS_PORTAL_CODE_EXPIRED -> {
login() login()
@ -198,7 +218,6 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
ERROR_LOGIN_LIBRUS_MESSAGES_NO_SESSION_ID -> { ERROR_LOGIN_LIBRUS_MESSAGES_NO_SESSION_ID -> {
login() login()
} }
// TODO PORTAL CAPTCHA
ERROR_LIBRUS_API_TIMETABLE_NOT_PUBLIC -> { ERROR_LIBRUS_API_TIMETABLE_NOT_PUBLIC -> {
data.timetableNotPublic = true data.timetableNotPublic = true
data() data()
@ -207,6 +226,11 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
ERROR_LIBRUS_API_NOTES_NOT_ACTIVE -> { ERROR_LIBRUS_API_NOTES_NOT_ACTIVE -> {
data() data()
} }
ERROR_LIBRUS_API_DEVICE_REGISTERED -> {
data.app.config.sync.tokenLibrusList =
data.app.config.sync.tokenLibrusList + data.profileId
data()
}
else -> callback.onError(apiError) else -> callback.onError(apiError)
} }
} }

View File

@ -50,11 +50,11 @@ const val ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS = 1130
const val ENDPOINT_LIBRUS_SYNERGIA_INFO = 2010 const val ENDPOINT_LIBRUS_SYNERGIA_INFO = 2010
const val ENDPOINT_LIBRUS_SYNERGIA_GRADES = 2020 const val ENDPOINT_LIBRUS_SYNERGIA_GRADES = 2020
const val ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK = 2030 const val ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK = 2030
const val ENDPOINT_LIBRUS_SYNERGIA_MESSAGES_RECEIVED = 2040
const val ENDPOINT_LIBRUS_SYNERGIA_MESSAGES_SENT = 2050
const val ENDPOINT_LIBRUS_MESSAGES_RECEIVED = 3010 const val ENDPOINT_LIBRUS_MESSAGES_RECEIVED = 3010
const val ENDPOINT_LIBRUS_MESSAGES_SENT = 3020 const val ENDPOINT_LIBRUS_MESSAGES_SENT = 3020
const val ENDPOINT_LIBRUS_MESSAGES_TRASH = 3030 const val ENDPOINT_LIBRUS_MESSAGES_TRASH = 3030
const val ENDPOINT_LIBRUS_MESSAGES_RECEIVERS = 3040
const val ENDPOINT_LIBRUS_MESSAGES_GET = 3040
val LibrusFeatures = listOf( val LibrusFeatures = listOf(
@ -66,7 +66,7 @@ val LibrusFeatures = listOf(
Feature(LOGIN_TYPE_LIBRUS, FEATURE_PUSH_CONFIG, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_PUSH_CONFIG, listOf(
ENDPOINT_LIBRUS_API_PUSH_CONFIG to LOGIN_METHOD_LIBRUS_API ENDPOINT_LIBRUS_API_PUSH_CONFIG to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)).withShouldSync { data -> ), listOf(LOGIN_METHOD_LIBRUS_API)).withShouldSync { data ->
!data.app.config.sync.tokenLibrusList.contains(data.profileId) (data as DataLibrus).isPremium && !data.app.config.sync.tokenLibrusList.contains(data.profileId)
}, },
@ -118,11 +118,11 @@ val LibrusFeatures = listOf(
* Homework - using API. * Homework - using API.
* Sync only if account has premium access. * Sync only if account has premium access.
*/ */
Feature(LOGIN_TYPE_LIBRUS, FEATURE_HOMEWORK, listOf( /*Feature(LOGIN_TYPE_LIBRUS, FEATURE_HOMEWORK, listOf(
ENDPOINT_LIBRUS_API_HOMEWORK to LOGIN_METHOD_LIBRUS_API ENDPOINT_LIBRUS_API_HOMEWORK to LOGIN_METHOD_LIBRUS_API
), listOf(LOGIN_METHOD_LIBRUS_API)).withShouldSync { data -> ), listOf(LOGIN_METHOD_LIBRUS_API)).withShouldSync { data ->
(data as DataLibrus).isPremium (data as DataLibrus).isPremium
}, },*/
/** /**
* Behaviour - using API. * Behaviour - using API.
*/ */
@ -229,9 +229,9 @@ val LibrusFeatures = listOf(
*/ */
Feature(LOGIN_TYPE_LIBRUS, FEATURE_HOMEWORK, listOf( Feature(LOGIN_TYPE_LIBRUS, FEATURE_HOMEWORK, listOf(
ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK to LOGIN_METHOD_LIBRUS_SYNERGIA ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK to LOGIN_METHOD_LIBRUS_SYNERGIA
), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA)).withShouldSync { data -> ), listOf(LOGIN_METHOD_LIBRUS_SYNERGIA))/*.withShouldSync { data ->
!(data as DataLibrus).isPremium !(data as DataLibrus).isPremium
}, }*/,
/** /**
* Messages inbox - using messages website. * Messages inbox - using messages website.

View File

@ -0,0 +1,60 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-5-8.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.librus
import android.content.Context
import android.webkit.WebView
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.startCoroutineTimer
import kotlin.coroutines.CoroutineContext
class LibrusRecaptchaHelper(
val context: Context,
url: String,
html: String,
val onSuccess: (url: String) -> Unit,
val onTimeout: () -> Unit
) : CoroutineScope {
companion object {
private const val TAG = "LibrusRecaptchaHelper"
}
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Default
private val webView by lazy {
WebView(context).also {
it.settings.javaScriptEnabled = true
it.webViewClient = WebViewClient()
}
}
private var timeout: Job? = null
private var timedOut = false
inner class WebViewClient : android.webkit.WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
timeout?.cancel()
if (!timedOut) {
onSuccess(url)
}
return true
}
}
init {
launch(Dispatchers.Main) {
webView.loadDataWithBaseURL(url, html, "text/html", "UTF-8", null)
}
timeout = startCoroutineTimer(delayMillis = 10000L) {
timedOut = true
onTimeout()
}
}
}

View File

@ -44,9 +44,12 @@ open class LibrusApi(open val data: DataLibrus, open val lastSync: Long?) {
.withResponse(response)) .withResponse(response))
return return
} }
/*
{"Status":"Error","Code":"DeviceRegistered","Message":"This device is alerdy registered.","Resources":{"..":{"Url":"https:\/\/api.librus.pl\/2.0\/Root"}},"Url":"https:\/\/api.librus.pl\/2.0\/ChangeRegister"}*/
val error = if (response?.code() == 200) null else val error = if (response?.code() == 200) null else
json.getString("Code") ?: json.getString("Code") ?:
json.getString("Message") ?: json.getString("Message") ?:
json.getString("Status") ?:
response?.parserErrorBody response?.parserErrorBody
error?.let { code -> error?.let { code ->
when (code) { when (code) {
@ -64,6 +67,8 @@ open class LibrusApi(open val data: DataLibrus, open val lastSync: Long?) {
"InvalidRequest" -> ERROR_LIBRUS_API_INVALID_REQUEST_PARAMS "InvalidRequest" -> ERROR_LIBRUS_API_INVALID_REQUEST_PARAMS
"Nieprawidłowy węzeł." -> ERROR_LIBRUS_API_INCORRECT_ENDPOINT "Nieprawidłowy węzeł." -> ERROR_LIBRUS_API_INCORRECT_ENDPOINT
"NoticeboardProblem" -> ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM "NoticeboardProblem" -> ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM
"DeviceRegistered" -> ERROR_LIBRUS_API_DEVICE_REGISTERED
"Maintenance" -> ERROR_LIBRUS_API_MAINTENANCE
else -> ERROR_LIBRUS_API_OTHER else -> ERROR_LIBRUS_API_OTHER
}.let { errorCode -> }.let { errorCode ->
if (errorCode !in ignoreErrors) { if (errorCode !in ignoreErrors) {
@ -115,6 +120,8 @@ open class LibrusApi(open val data: DataLibrus, open val lastSync: Long?) {
.allowErrorCode(HTTP_FORBIDDEN) .allowErrorCode(HTTP_FORBIDDEN)
.allowErrorCode(HTTP_UNAUTHORIZED) .allowErrorCode(HTTP_UNAUTHORIZED)
.allowErrorCode(HTTP_UNAVAILABLE) .allowErrorCode(HTTP_UNAVAILABLE)
.allowErrorCode(HTTP_NOT_FOUND)
.allowErrorCode(503)
.callback(callback) .callback(callback)
.build() .build()
.enqueue() .enqueue()

View File

@ -8,6 +8,7 @@ import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.* 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.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesGetList import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesGetList
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.LibrusSynergiaGetMessages
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.LibrusSynergiaHomework 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.api.edziennik.librus.data.synergia.LibrusSynergiaInfo
import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Message
@ -31,15 +32,15 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
onSuccess() onSuccess()
return return
} }
useEndpoint(data.targetEndpointIds.firstKey()) { endpointId -> val id = data.targetEndpointIds.firstKey()
data.targetEndpointIds.remove(endpointId) val lastSync = data.targetEndpointIds.remove(id)
useEndpoint(id, lastSync) { endpointId ->
data.progress(data.progressStep) data.progress(data.progressStep)
nextEndpoint(onSuccess) nextEndpoint(onSuccess)
} }
} }
private fun useEndpoint(endpointId: Int, onSuccess: (endpointId: Int) -> Unit) { private fun useEndpoint(endpointId: Int, lastSync: Long?, onSuccess: (endpointId: Int) -> Unit) {
val lastSync = data.targetEndpointIds[endpointId]
Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync") Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync")
when (endpointId) { when (endpointId) {
/** /**
@ -81,7 +82,10 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
data.startProgress(R.string.edziennik_progress_endpoint_lessons) data.startProgress(R.string.edziennik_progress_endpoint_lessons)
LibrusApiLessons(data, lastSync, onSuccess) LibrusApiLessons(data, lastSync, onSuccess)
} }
// TODO push config ENDPOINT_LIBRUS_API_PUSH_CONFIG -> {
data.startProgress(R.string.edziennik_progress_endpoint_push_config)
LibrusApiPushConfig(data, lastSync, onSuccess)
}
ENDPOINT_LIBRUS_API_TIMETABLES -> { ENDPOINT_LIBRUS_API_TIMETABLES -> {
data.startProgress(R.string.edziennik_progress_endpoint_timetable) data.startProgress(R.string.edziennik_progress_endpoint_timetable)
LibrusApiTimetables(data, lastSync, onSuccess) LibrusApiTimetables(data, lastSync, onSuccess)
@ -198,17 +202,27 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
data.startProgress(R.string.edziennik_progress_endpoint_student_info) data.startProgress(R.string.edziennik_progress_endpoint_student_info)
LibrusSynergiaInfo(data, lastSync, onSuccess) LibrusSynergiaInfo(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_SYNERGIA_MESSAGES_RECEIVED -> {
data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox)
LibrusSynergiaGetMessages(data, type = Message.TYPE_RECEIVED, lastSync = lastSync, onSuccess = onSuccess)
}
ENDPOINT_LIBRUS_SYNERGIA_MESSAGES_SENT -> {
data.startProgress(R.string.edziennik_progress_endpoint_messages_outbox)
LibrusSynergiaGetMessages(data, type = Message.TYPE_SENT, lastSync = lastSync, onSuccess = onSuccess)
}
/** /**
* MESSAGES * MESSAGES
*/ */
ENDPOINT_LIBRUS_MESSAGES_RECEIVED -> { ENDPOINT_LIBRUS_MESSAGES_RECEIVED -> {
data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox) data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox)
LibrusMessagesGetList(data, type = Message.TYPE_RECEIVED, lastSync = lastSync, onSuccess = onSuccess) if (data.messagesLoginSuccessful) LibrusMessagesGetList(data, type = Message.TYPE_RECEIVED, lastSync = lastSync, onSuccess = onSuccess)
else LibrusSynergiaGetMessages(data, type = Message.TYPE_RECEIVED, lastSync = lastSync, onSuccess = onSuccess)
} }
ENDPOINT_LIBRUS_MESSAGES_SENT -> { ENDPOINT_LIBRUS_MESSAGES_SENT -> {
data.startProgress(R.string.edziennik_progress_endpoint_messages_outbox) data.startProgress(R.string.edziennik_progress_endpoint_messages_outbox)
LibrusMessagesGetList(data, type = Message.TYPE_SENT, lastSync = lastSync, onSuccess = onSuccess) if (data.messagesLoginSuccessful) LibrusMessagesGetList(data, type = Message.TYPE_SENT, lastSync = lastSync, onSuccess = onSuccess)
else LibrusSynergiaGetMessages(data, type = Message.TYPE_SENT, lastSync = lastSync, onSuccess = onSuccess)
} }
else -> onSuccess(endpointId) else -> onSuccess(endpointId)

View File

@ -12,7 +12,6 @@ import im.wangchao.mhttp.body.MediaTypeUtils
import im.wangchao.mhttp.callback.FileCallbackHandler import im.wangchao.mhttp.callback.FileCallbackHandler
import im.wangchao.mhttp.callback.JsonCallbackHandler import im.wangchao.mhttp.callback.JsonCallbackHandler
import im.wangchao.mhttp.callback.TextCallbackHandler import im.wangchao.mhttp.callback.TextCallbackHandler
import okhttp3.Cookie
import org.json.JSONObject import org.json.JSONObject
import org.json.XML import org.json.XML
import org.jsoup.Jsoup import org.jsoup.Jsoup
@ -55,13 +54,20 @@ open class LibrusMessages(open val data: DataLibrus, open val lastSync: Long?) {
} }
when { when {
text.contains("<message>Niepoprawny login i/lub hasło.</message>") -> data.error(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN, response, text) text.contains("<message>Niepoprawny login i/lub hasło.</message>") -> ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN
text.contains("stop.png") -> data.error(TAG, ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED, response, text) text.contains("<message>Nie odnaleziono wiadomości.</message>") -> ERROR_LIBRUS_MESSAGES_NOT_FOUND
text.contains("eAccessDeny") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text) text.contains("stop.png") -> ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED
text.contains("OffLine") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_MAINTENANCE, response, text) text.contains("eAccessDeny") -> ERROR_LIBRUS_MESSAGES_ACCESS_DENIED
text.contains("<status>error</status>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ERROR, response, text) text.contains("OffLine") -> ERROR_LIBRUS_MESSAGES_MAINTENANCE
text.contains("<type>eVarWhitThisNameNotExists</type>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text) text.contains("<status>error</status>") -> ERROR_LIBRUS_MESSAGES_ERROR
text.contains("<error>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_OTHER, response, text) text.contains("<type>eVarWhitThisNameNotExists</type>") -> ERROR_LIBRUS_MESSAGES_ACCESS_DENIED
text.contains("<error>") -> ERROR_LIBRUS_MESSAGES_OTHER
else -> null
}?.let { errorCode ->
data.error(ApiError(tag, errorCode)
.withApiResponse(text)
.withResponse(response))
return
} }
try { try {
@ -82,14 +88,7 @@ open class LibrusMessages(open val data: DataLibrus, open val lastSync: Long?) {
} }
} }
data.app.cookieJar.saveFromResponse(null, listOf( data.app.cookieJar.set("wiadomosci.librus.pl", "DZIENNIKSID", data.messagesSessionId)
Cookie.Builder()
.name("DZIENNIKSID")
.value(data.messagesSessionId!!)
.domain("wiadomosci.librus.pl")
.secure().httpOnly().build()
))
val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder() val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
val doc = docBuilder.newDocument() val doc = docBuilder.newDocument()
@ -139,13 +138,20 @@ open class LibrusMessages(open val data: DataLibrus, open val lastSync: Long?) {
} }
when { when {
text.contains("<message>Niepoprawny login i/lub hasło.</message>") -> data.error(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN, response, text) text.contains("<message>Niepoprawny login i/lub hasło.</message>") -> ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN
text.contains("stop.png") -> data.error(TAG, ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED, response, text) text.contains("<message>Nie odnaleziono wiadomości.</message>") -> ERROR_LIBRUS_MESSAGES_NOT_FOUND
text.contains("eAccessDeny") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text) text.contains("stop.png") -> ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED
text.contains("OffLine") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_MAINTENANCE, response, text) text.contains("eAccessDeny") -> ERROR_LIBRUS_MESSAGES_ACCESS_DENIED
text.contains("<status>error</status>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ERROR, response, text) text.contains("OffLine") -> ERROR_LIBRUS_MESSAGES_MAINTENANCE
text.contains("<type>eVarWhitThisNameNotExists</type>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text) text.contains("<status>error</status>") -> ERROR_LIBRUS_MESSAGES_ERROR
text.contains("<error>") -> data.error(TAG, ERROR_LIBRUS_MESSAGES_OTHER, response, text) text.contains("<type>eVarWhitThisNameNotExists</type>") -> ERROR_LIBRUS_MESSAGES_ACCESS_DENIED
text.contains("<error>") -> ERROR_LIBRUS_MESSAGES_OTHER
else -> null
}?.let { errorCode ->
data.error(ApiError(tag, errorCode)
.withApiResponse(text)
.withResponse(response))
return
} }
try { try {
@ -166,14 +172,7 @@ open class LibrusMessages(open val data: DataLibrus, open val lastSync: Long?) {
} }
} }
data.app.cookieJar.saveFromResponse(null, listOf( data.app.cookieJar.set("wiadomosci.librus.pl", "DZIENNIKSID", data.messagesSessionId)
Cookie.Builder()
.name("DZIENNIKSID")
.value(data.messagesSessionId!!)
.domain("wiadomosci.librus.pl")
.secure().httpOnly().build()
))
val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder() val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
val doc = docBuilder.newDocument() val doc = docBuilder.newDocument()
@ -253,10 +252,11 @@ open class LibrusMessages(open val data: DataLibrus, open val lastSync: Long?) {
.enqueue() .enqueue()
} }
fun sandboxGetFile(tag: String, action: String, targetFile: File, onSuccess: (file: File) -> Unit, fun sandboxGetFile(tag: String, url: String, targetFile: File, onSuccess: (file: File) -> Unit,
method: Int = GET,
onProgress: (written: Long, total: Long) -> Unit) { onProgress: (written: Long, total: Long) -> Unit) {
d(tag, "Request: Librus/Messages - $LIBRUS_SANDBOX_URL$action") d(tag, "Request: Librus/Messages - $url")
val callback = object : FileCallbackHandler(targetFile) { val callback = object : FileCallbackHandler(targetFile) {
override fun onSuccess(file: File?, response: Response?) { override fun onSuccess(file: File?, response: Response?) {
@ -292,9 +292,14 @@ open class LibrusMessages(open val data: DataLibrus, open val lastSync: Long?) {
} }
Request.builder() Request.builder()
.url("$LIBRUS_SANDBOX_URL$action") .url(url)
.userAgent(SYNERGIA_USER_AGENT) .userAgent(SYNERGIA_USER_AGENT)
.post() .also {
when (method) {
POST -> it.post()
else -> it.get()
}
}
.callback(callback) .callback(callback)
.build() .build()
.enqueue() .enqueue()

View File

@ -44,6 +44,7 @@ open class LibrusPortal(open val data: DataLibrus) {
"Access token is invalid" -> ERROR_LIBRUS_PORTAL_ACCESS_DENIED "Access token is invalid" -> ERROR_LIBRUS_PORTAL_ACCESS_DENIED
"ApiDisabled" -> ERROR_LIBRUS_PORTAL_API_DISABLED "ApiDisabled" -> ERROR_LIBRUS_PORTAL_API_DISABLED
"Account not found" -> ERROR_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND "Account not found" -> ERROR_LIBRUS_PORTAL_SYNERGIA_NOT_FOUND
"Unable to refresh the account" -> ERROR_LIBRUS_PORTAL_MAINTENANCE
else -> when (json.getString("hint")) { else -> when (json.getString("hint")) {
"Error while decoding to JSON" -> ERROR_LIBRUS_PORTAL_ACCESS_DENIED "Error while decoding to JSON" -> ERROR_LIBRUS_PORTAL_ACCESS_DENIED
else -> ERROR_LIBRUS_PORTAL_OTHER else -> ERROR_LIBRUS_PORTAL_OTHER
@ -97,6 +98,7 @@ open class LibrusPortal(open val data: DataLibrus) {
.allowErrorCode(HttpURLConnection.HTTP_UNAUTHORIZED) .allowErrorCode(HttpURLConnection.HTTP_UNAUTHORIZED)
.allowErrorCode(HttpURLConnection.HTTP_BAD_REQUEST) .allowErrorCode(HttpURLConnection.HTTP_BAD_REQUEST)
.allowErrorCode(HttpURLConnection.HTTP_GONE) .allowErrorCode(HttpURLConnection.HTTP_GONE)
.allowErrorCode(424)
.callback(callback) .callback(callback)
.build() .build()
.enqueue() .enqueue()

View File

@ -35,7 +35,7 @@ open class LibrusSynergia(open val data: DataLibrus, open val lastSync: Long?) {
return return
} }
if (!text.contains("jesteś zalogowany")) { if (!text.contains("jesteś zalogowany") && !text.contains("Podgląd zadania")) {
when { when {
text.contains("stop.png") -> ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED text.contains("stop.png") -> ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED
text.contains("Przerwa techniczna") -> ERROR_LIBRUS_SYNERGIA_MAINTENANCE text.contains("Przerwa techniczna") -> ERROR_LIBRUS_SYNERGIA_MAINTENANCE
@ -48,7 +48,6 @@ open class LibrusSynergia(open val data: DataLibrus, open val lastSync: Long?) {
} }
} }
try { try {
onSuccess(text) onSuccess(text)
} catch (e: Exception) { } catch (e: Exception) {
@ -90,4 +89,44 @@ open class LibrusSynergia(open val data: DataLibrus, open val lastSync: Long?) {
.build() .build()
.enqueue() .enqueue()
} }
fun redirectUrlGet(tag: String, url: String, onSuccess: (url: String) -> Unit) {
d(tag, "Request: Librus/Synergia - $url")
val callback = object : TextCallbackHandler() {
override fun onSuccess(text: String?, response: Response) {
val redirectUrl = response.headers().get("Location")
if (redirectUrl != null) {
try {
onSuccess(redirectUrl)
} catch (e: Exception) {
data.error(ApiError(tag, EXCEPTION_LIBRUS_SYNERGIA_REQUEST)
.withResponse(response)
.withThrowable(e)
.withApiResponse(text))
}
} else {
data.error(ApiError(tag, ERROR_LIBRUS_SYNERGIA_OTHER)
.withResponse(response)
.withApiResponse(text))
}
}
override fun onFailure(response: Response?, throwable: Throwable?) {
data.error(ApiError(tag, ERROR_REQUEST_FAILURE)
.withResponse(response)
.withThrowable(throwable))
}
}
Request.builder()
.url(url)
.userAgent(LIBRUS_USER_AGENT)
.withClient(data.app.httpLazy)
.get()
.callback(callback)
.build()
.enqueue()
}
} }

View File

@ -37,8 +37,7 @@ class LibrusApiAnnouncementMarkAsRead(override val data: DataLibrus,
Metadata.TYPE_ANNOUNCEMENT, Metadata.TYPE_ANNOUNCEMENT,
announcement.id, announcement.id,
announcement.seen, announcement.seen,
announcement.notified, announcement.notified
announcement.addedDate
)) ))
onSuccess() onSuccess()
} }

View File

@ -38,15 +38,17 @@ class LibrusApiAnnouncements(override val data: DataLibrus,
val read = announcement.getBoolean("WasRead") ?: false val read = announcement.getBoolean("WasRead") ?: false
val announcementObject = Announcement( val announcementObject = Announcement(
profileId, profileId = profileId,
id, id = id,
subject, subject = subject,
text, text = text,
startDate, startDate = startDate,
endDate, endDate = endDate,
teacherId, teacherId = teacherId,
longId addedDate = addedDate
) ).also {
it.idString = longId
}
data.announcementList.add(announcementObject) data.announcementList.add(announcementObject)
data.setSeenMetadataList.add(Metadata( data.setSeenMetadataList.add(Metadata(
@ -54,8 +56,7 @@ class LibrusApiAnnouncements(override val data: DataLibrus,
Metadata.TYPE_ANNOUNCEMENT, Metadata.TYPE_ANNOUNCEMENT,
id, id,
read, read,
profile.empty || read, profile.empty || read
addedDate
)) ))
} }

View File

@ -26,25 +26,39 @@ class LibrusApiAttendanceTypes(override val data: DataLibrus,
attendanceTypes?.forEach { attendanceType -> attendanceTypes?.forEach { attendanceType ->
val id = attendanceType.getLong("Id") ?: return@forEach val id = attendanceType.getLong("Id") ?: return@forEach
val name = attendanceType.getString("Name") ?: ""
val color = attendanceType.getString("ColorRGB")?.let { Color.parseColor("#$it") } ?: -1
val standardId = when (attendanceType.getBoolean("Standard") ?: false) { val typeName = attendanceType.getString("Name") ?: ""
true -> id val typeSymbol = attendanceType.getString("Short") ?: ""
false -> attendanceType.getJsonObject("StandardType")?.getLong("Id") ?: id val typeColor = attendanceType.getString("ColorRGB")?.let { Color.parseColor("#$it") }
}
val type = when (standardId) { val isStandard = attendanceType.getBoolean("Standard") ?: false
val baseType = when (attendanceType.getJsonObject("StandardType")?.getLong("Id") ?: id) {
1L -> Attendance.TYPE_ABSENT 1L -> Attendance.TYPE_ABSENT
2L -> Attendance.TYPE_BELATED 2L -> Attendance.TYPE_BELATED
3L -> Attendance.TYPE_ABSENT_EXCUSED 3L -> Attendance.TYPE_ABSENT_EXCUSED
4L -> Attendance.TYPE_RELEASED 4L -> Attendance.TYPE_RELEASED
/*100*/else -> Attendance.TYPE_PRESENT /*100*/else -> when (isStandard) {
true -> Attendance.TYPE_PRESENT
false -> Attendance.TYPE_PRESENT_CUSTOM
}
}
val typeShort = when (isStandard) {
true -> data.app.attendanceManager.getTypeShort(baseType)
false -> typeSymbol
} }
data.attendanceTypes.put(id, AttendanceType(profileId, id, name, type, color)) data.attendanceTypes.put(id, AttendanceType(
profileId,
id,
baseType,
typeName,
typeShort,
typeSymbol,
typeColor
))
} }
data.setSyncNext(ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES, 4*DAY) data.setSyncNext(ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES, 2*DAY)
onSuccess(ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES) onSuccess(ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES)
} }
} }

View File

@ -41,39 +41,44 @@ class LibrusApiAttendances(override val data: DataLibrus,
val lessonDate = Date.fromY_m_d(attendance.getString("Date")) val lessonDate = Date.fromY_m_d(attendance.getString("Date"))
val teacherId = attendance.getJsonObject("AddedBy")?.getLong("Id") val teacherId = attendance.getJsonObject("AddedBy")?.getLong("Id")
val semester = attendance.getInt("Semester") ?: return@forEach 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 startTime = data.lessonRanges.get(lessonNo).startTime val typeId = attendance.getJsonObject("Type")?.getLong("Id") ?: return@forEach
val type = data.attendanceTypes[typeId] ?: null
val startTime = data.lessonRanges.get(lessonNo)?.startTime
val lesson = if (lessonId != -1L) val lesson = if (lessonId != -1L)
data.librusLessons.singleOrNull { it.lessonId == lessonId } data.librusLessons.singleOrNull { it.lessonId == lessonId }
else null else null
val attendanceObject = Attendance(
profileId,
id,
teacherId ?: lesson?.teacherId ?: -1,
lesson?.subjectId ?: -1,
semester,
topic,
lessonDate,
startTime,
typeObject.type
)
val addedDate = Date.fromIso(attendance.getString("AddDate") ?: return@forEach) val addedDate = Date.fromIso(attendance.getString("AddDate") ?: return@forEach)
val attendanceObject = Attendance(
profileId = profileId,
id = id,
baseType = type?.baseType ?: Attendance.TYPE_UNKNOWN,
typeName = type?.typeName ?: "nieznany rodzaj",
typeShort = type?.typeShort ?: "?",
typeSymbol = type?.typeSymbol ?: "?",
typeColor = type?.typeColor,
date = lessonDate,
startTime = startTime,
semester = semester,
teacherId = teacherId ?: lesson?.teacherId ?: -1,
subjectId = lesson?.subjectId ?: -1,
addedDate = addedDate
).also {
it.lessonNumber = lessonNo
}
data.attendanceList.add(attendanceObject) data.attendanceList.add(attendanceObject)
if(typeObject.type != Attendance.TYPE_PRESENT) { if(type?.baseType != Attendance.TYPE_PRESENT) {
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
profileId, profileId,
Metadata.TYPE_ATTENDANCE, Metadata.TYPE_ATTENDANCE,
id, id,
profile?.empty ?: false, profile?.empty ?: false || type?.baseType == Attendance.TYPE_PRESENT_CUSTOM || type?.baseType == Attendance.TYPE_UNKNOWN,
profile?.empty ?: false, profile?.empty ?: false || type?.baseType == Attendance.TYPE_PRESENT_CUSTOM || type?.baseType == Attendance.TYPE_UNKNOWN
addedDate
)) ))
} }
} }

View File

@ -10,6 +10,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Grade import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_POINT_SUM
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
@ -26,23 +27,37 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
private val nameFormat by lazy { DecimalFormat("#.##") } private val nameFormat by lazy { DecimalFormat("#.##") }
private val types by lazy {
mapOf(
1 to ("wz" to "wzorowe"),
2 to ("bdb" to "bardzo dobre"),
3 to ("db" to "dobre"),
4 to ("popr" to "poprawne"),
5 to ("ndp" to "nieodpowiednie"),
6 to ("ng" to "naganne")
)
}
init { data.profile?.also { profile -> init { data.profile?.also { profile ->
apiGet(TAG, "BehaviourGrades/Points") { json -> apiGet(TAG, "BehaviourGrades/Points") { json ->
if (data.startPointsSemester1 > 0) { if (data.startPointsSemester1 > 0) {
val semester1StartGradeObject = Grade( val semester1StartGradeObject = Grade(
profileId, profileId = profileId,
-101, id = -101,
data.app.getString(R.string.grade_start_points), name = nameFormat.format(data.startPointsSemester1),
0xffbdbdbd.toInt(), type = TYPE_POINT_SUM,
data.app.getString(R.string.grade_start_points_format, 1), value = data.startPointsSemester1.toFloat(),
nameFormat.format(data.startPointsSemester1), weight = 0f,
data.startPointsSemester1.toFloat(), color = 0xffbdbdbd.toInt(),
-1f, category = data.app.getString(R.string.grade_start_points),
1, description = data.app.getString(R.string.grade_start_points_format, 1),
-1, comment = null,
1 semester = 1,
).apply { type = Grade.TYPE_POINT_SUM } teacherId = -1,
subjectId = 1,
addedDate = profile.getSemesterStart(1).inMillis
)
data.gradeList.add(semester1StartGradeObject) data.gradeList.add(semester1StartGradeObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
@ -50,25 +65,27 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
Metadata.TYPE_GRADE, Metadata.TYPE_GRADE,
semester1StartGradeObject.id, semester1StartGradeObject.id,
true, true,
true, true
profile.getSemesterStart(1).inMillis
)) ))
} }
if (data.startPointsSemester2 > 0) { if (data.startPointsSemester2 > 0) {
val semester2StartGradeObject = Grade( val semester2StartGradeObject = Grade(
profileId, profileId = profileId,
-102, id = -102,
data.app.getString(R.string.grade_start_points), name = nameFormat.format(data.startPointsSemester2),
0xffbdbdbd.toInt(), type = TYPE_POINT_SUM,
data.app.getString(R.string.grade_start_points_format, 2), value = data.startPointsSemester2.toFloat(),
nameFormat.format(data.startPointsSemester2), weight = -1f,
data.startPointsSemester2.toFloat(), color = 0xffbdbdbd.toInt(),
-1f, category = data.app.getString(R.string.grade_start_points),
2, description = data.app.getString(R.string.grade_start_points_format, 2),
-1, comment = null,
1 semester = 2,
).apply { type = Grade.TYPE_POINT_SUM } teacherId = -1,
subjectId = 1,
addedDate = profile.getSemesterStart(2).inMillis
)
data.gradeList.add(semester2StartGradeObject) data.gradeList.add(semester2StartGradeObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
@ -76,8 +93,7 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
Metadata.TYPE_GRADE, Metadata.TYPE_GRADE,
semester2StartGradeObject.id, semester2StartGradeObject.id,
true, true,
true, true
profile.getSemesterStart(2).inMillis
)) ))
} }
@ -90,8 +106,12 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
val addedDate = grade.getString("AddDate")?.let { Date.fromIso(it) } val addedDate = grade.getString("AddDate")?.let { Date.fromIso(it) }
?: System.currentTimeMillis() ?: System.currentTimeMillis()
val text = grade.getString("Text")
val type = grade.getJsonObject("BehaviourGrade")?.getInt("Id")?.let { types[it] }
val name = when { val name = when {
value != null -> (if (value >= 0) "+" else "") + nameFormat.format(value) type != null -> type.first
value != null -> (if (value > 0) "+" else "") + nameFormat.format(value)
shortName != null -> shortName shortName != null -> shortName
else -> return@forEach else -> return@forEach
} }
@ -110,32 +130,34 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
val categoryName = category?.text ?: "" val categoryName = category?.text ?: ""
val description = grade.getJsonArray("Comments")?.asJsonObjectList()?.let { comments -> val comments = grade.getJsonArray("Comments")
if (comments.isNotEmpty()) { ?.asJsonObjectList()
data.gradeCategories.singleOrNull { ?.mapNotNull { comment ->
it.type == GradeCategory.TYPE_BEHAVIOUR_COMMENT val cId = comment.getLong("Id") ?: return@mapNotNull null
&& it.categoryId == comments[0].asJsonObject.getLong("Id") data.gradeCategories[cId]?.text
}?.text } ?: listOf()
} else null
} ?: "" val description = listOfNotNull(type?.second) + comments
val valueFrom = value ?: category?.valueFrom ?: 0f val valueFrom = value ?: category?.valueFrom ?: 0f
val valueTo = category?.valueTo ?: 0f val valueTo = category?.valueTo ?: 0f
val gradeObject = Grade( val gradeObject = Grade(
profileId, profileId = profileId,
id, id = id,
categoryName, name = name,
color, type = TYPE_POINT_SUM,
description, value = valueFrom,
name, weight = -1f,
valueFrom, color = color,
-1f, category = categoryName,
semester, description = text ?: description.join(" - "),
teacherId, comment = if (text != null) description.join(" - ") else null,
1 semester = semester,
teacherId = teacherId,
subjectId = 1,
addedDate = addedDate
).apply { ).apply {
type = Grade.TYPE_POINT_SUM
valueMax = valueTo valueMax = valueTo
} }
@ -145,8 +167,7 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
Metadata.TYPE_GRADE, Metadata.TYPE_GRADE,
id, id,
profile.empty, profile.empty,
profile.empty, profile.empty
addedDate
)) ))
} }

View File

@ -10,8 +10,8 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Grade import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_DESCRIPTIVE_TEXT import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_DESCRIPTIVE_TEXT
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_TEXT import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_TEXT
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
@ -53,20 +53,21 @@ class LibrusApiDescriptiveGrades(override val data: DataLibrus,
val addedDate = Date.fromIso(grade.getString("AddDate") ?: return@forEach) val addedDate = Date.fromIso(grade.getString("AddDate") ?: return@forEach)
val gradeObject = Grade( val gradeObject = Grade(
profileId, profileId = profileId,
id, id = id,
category?.text ?: "", name = " ",
category?.color ?: -1, type = type,
description, value = 0f,
" ", weight = 0f,
0f, color = category?.color ?: -1,
0f, category = category?.text,
semester, description = description,
teacherId, comment = null,
subjectId semester = semester,
).apply { teacherId = teacherId,
this.type = type subjectId = subjectId,
} addedDate = addedDate
)
data.gradeList.add(gradeObject) data.gradeList.add(gradeObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
@ -74,8 +75,7 @@ class LibrusApiDescriptiveGrades(override val data: DataLibrus,
Metadata.TYPE_GRADE, Metadata.TYPE_GRADE,
id, id,
profile.empty, profile.empty,
profile.empty, profile.empty
addedDate
)) ))
} }

View File

@ -35,7 +35,7 @@ class LibrusApiEvents(override val data: DataLibrus,
events?.forEach { event -> events?.forEach { event ->
val id = event.getLong("Id") ?: return@forEach val id = event.getLong("Id") ?: return@forEach
val eventDate = Date.fromY_m_d(event.getString("Date")) val eventDate = Date.fromY_m_d(event.getString("Date"))
val topic = event.getString("Content") ?: "" var topic = event.getString("Content")?.trim() ?: ""
val type = event.getJsonObject("Category")?.getLong("Id") ?: -1 val type = event.getJsonObject("Category")?.getLong("Id") ?: -1
val teacherId = event.getJsonObject("CreatedBy")?.getLong("Id") ?: -1 val teacherId = event.getJsonObject("CreatedBy")?.getLong("Id") ?: -1
val subjectId = event.getJsonObject("Subject")?.getLong("Id") ?: -1 val subjectId = event.getJsonObject("Subject")?.getLong("Id") ?: -1
@ -46,18 +46,24 @@ class LibrusApiEvents(override val data: DataLibrus,
val startTime = lessonRange?.startTime ?: Time.fromH_m(event.getString("TimeFrom")) val startTime = lessonRange?.startTime ?: Time.fromH_m(event.getString("TimeFrom"))
val addedDate = Date.fromIso(event.getString("AddDate")) val addedDate = Date.fromIso(event.getString("AddDate"))
event.getJsonObject("onlineLessonUrl")?.let { onlineLesson ->
val text = onlineLesson.getString("text")?.let { "$it - " } ?: ""
val url = onlineLesson.getString("url")
topic += "\n\n$text$url"
}
val eventObject = Event( val eventObject = Event(
profileId, profileId = profileId,
id, id = id,
eventDate, date = eventDate,
startTime, time = startTime,
topic, topic = topic,
-1, color = null,
type, type = type,
false, teacherId = teacherId,
teacherId, subjectId = subjectId,
subjectId, teamId = teamId,
teamId addedDate = addedDate
) )
data.eventList.add(eventObject) data.eventList.add(eventObject)
@ -67,8 +73,7 @@ class LibrusApiEvents(override val data: DataLibrus,
Metadata.TYPE_EVENT, Metadata.TYPE_EVENT,
id, id,
profile?.empty ?: false, profile?.empty ?: false,
profile?.empty ?: false, profile?.empty ?: false
addedDate
)) ))
} }

View File

@ -6,7 +6,13 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.Grade import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.* import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_NORMAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_FINAL
import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
@ -54,32 +60,29 @@ class LibrusApiGrades(override val data: DataLibrus,
} ?: "" } ?: ""
val gradeObject = Grade( val gradeObject = Grade(
profileId, profileId = profileId,
id, id = id,
category?.text ?: "", name = name,
category?.color ?: -1, type = when {
description, grade.getBoolean("IsConstituent") ?: false -> TYPE_NORMAL
name, grade.getBoolean("IsSemester") ?: false -> if (semester == 1) TYPE_SEMESTER1_FINAL else TYPE_SEMESTER2_FINAL
value, grade.getBoolean("IsSemesterProposition") ?: false -> if (semester == 1) TYPE_SEMESTER1_PROPOSED else TYPE_SEMESTER2_PROPOSED
weight, grade.getBoolean("IsFinal") ?: false -> TYPE_YEAR_FINAL
semester, grade.getBoolean("IsFinalProposition") ?: false -> TYPE_YEAR_PROPOSED
teacherId, else -> TYPE_NORMAL
subjectId },
value = value,
weight = weight,
color = category?.color ?: -1,
category = category?.text ?: "",
description = description,
comment = null,
semester = semester,
teacherId = teacherId,
subjectId = subjectId,
addedDate = addedDate
) )
when {
grade.getBoolean("IsConstituent") ?: false ->
gradeObject.type = TYPE_NORMAL
grade.getBoolean("IsSemester") ?: false -> // semester final
gradeObject.type = if (gradeObject.semester == 1) TYPE_SEMESTER1_FINAL else TYPE_SEMESTER2_FINAL
grade.getBoolean("IsSemesterProposition") ?: false -> // semester proposed
gradeObject.type = if (gradeObject.semester == 1) TYPE_SEMESTER1_PROPOSED else TYPE_SEMESTER2_PROPOSED
grade.getBoolean("IsFinal") ?: false -> // year final
gradeObject.type = TYPE_YEAR_FINAL
grade.getBoolean("IsFinalProposition") ?: false -> // year final
gradeObject.type = TYPE_YEAR_PROPOSED
}
grade.getJsonObject("Improvement")?.also { grade.getJsonObject("Improvement")?.also {
val historicalId = it.getLong("Id") val historicalId = it.getLong("Id")
data.gradeList.firstOrNull { grade -> grade.id == historicalId }?.also { grade -> data.gradeList.firstOrNull { grade -> grade.id == historicalId }?.also { grade ->
@ -96,8 +99,7 @@ class LibrusApiGrades(override val data: DataLibrus,
Metadata.TYPE_GRADE, Metadata.TYPE_GRADE,
id, id,
profile.empty, profile.empty,
profile.empty, profile.empty
addedDate
)) ))
} }

View File

@ -34,17 +34,17 @@ class LibrusApiHomework(override val data: DataLibrus,
val addedDate = Date.fromY_m_d(homework.getString("Date")) val addedDate = Date.fromY_m_d(homework.getString("Date"))
val eventObject = Event( val eventObject = Event(
profileId, profileId = profileId,
id, id = id,
eventDate, date = eventDate,
null, time = null,
topic, topic = topic,
-1, color = null,
-1, type = -1,
false, teacherId = teacherId,
teacherId, subjectId = -1,
-1, teamId = -1,
-1 addedDate = addedDate.inMillis
) )
data.eventList.add(eventObject) data.eventList.add(eventObject)
@ -53,8 +53,7 @@ class LibrusApiHomework(override val data: DataLibrus,
Metadata.TYPE_HOMEWORK, Metadata.TYPE_HOMEWORK,
id, id,
profile?.empty ?: false, profile?.empty ?: false,
profile?.empty ?: false, profile?.empty ?: false
addedDate.inMillis
)) ))
} }

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