Compare commits

...

216 Commits

Author SHA1 Message Date
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
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
c2b8f71467 [4.0-beta.9] Update build.gradle, signing and changelog. 2020-02-19 23:33:30 +01:00
a6b91c3a14 [Models] Add basic error protection in Date, Time. 2020-02-19 23:04:28 +01:00
164cfbfd0d [API/Mobidziennik] Fix getting grade added dates, colors and averages. 2020-02-19 22:59:32 +01:00
0bb340e96e [API/Mobidziennik] Implement web attendance scrapper. 2020-02-19 18:58:57 +01:00
f0447dc455 [API/Mobidziennik] Fix too much clearing grades from DB. Add performance debugging to Data and ApiService. 2020-02-19 16:38:11 +01:00
626bbfa7a4 [API/Mobidziennik] Add sent messages endpoint. 2020-02-18 20:10:05 +01:00
169a900f01 [API] Implement passing last sync time to endpoints. 2020-02-18 18:58:51 +01:00
d0992eaf54 [API] Implement error handling and exception catching in Szkolny API. 2020-02-16 22:50:06 +01:00
fc21d757c3 [API/Szkolny] Restrict AppSync to run only every 24 hours (if no WebPush needed). 2020-02-16 14:30:13 +01:00
54363ee919 [UI/Timetable] Add lesson type annotation in LessonDetailsDialog. 2020-02-16 14:12:55 +01:00
fdad3b9997 [Push/Mobidziennik] Add support for behaviour grades push. 2020-02-16 13:44:45 +01:00
4ad826ebe8 [API] Implement Librus Captcha. Refactor notification constants. Update empty account error as a dialog. 2020-02-16 13:42:14 +01:00
f5e1e9fdd9 [Deprecated] Remove ServerRequest, GenericDialog. 2020-02-15 18:53:38 +01:00
82b232d0e5 [Messages/Compose] Add dropdown icon to show all recipient categories. Add before-send confirmation dialog. 2020-02-15 14:36:36 +01:00
c8c1fe5367 [4.0-beta.8] Update build.gradle and signing. 2020-02-14 22:39:13 +01:00
71128e0244 [Messages/Compose] Fix text layout jumping and scrolling off-screen when typing a long message. 2020-02-14 22:28:58 +01:00
453bcaa1f6 [Dialog/Day] Show lesson changes and teacher absences in the day dialog. 2020-02-13 23:04:29 +01:00
48898ab1d4 [Widgets] Fix profile separator text color. 2020-02-13 13:44:20 +01:00
a095520d0d [UI/Agenda] Fix subject, teacher and time display in all day events. 2020-02-13 13:33:04 +01:00
2e0c6fa6a5 [Errors] Add request body in error reporting. 2020-02-13 09:57:59 +01:00
bfbc0861df [Notifications] Disable notifications about past events & timetable changes. 2020-02-12 23:05:03 +01:00
3a500f3f28 [API/Librus] Fix student name not normalized, short name not having a trailing dot (remove legacy code). 2020-02-12 19:20:39 +01:00
df8094c39c [Dialog/LessonChanges] Add a new lesson changes dialog. 2020-02-11 16:34:40 +01:00
448fd0e884 [API/Librus] Fix marking removed announcements as read. 2020-02-10 23:53:04 +01:00
4717b4549e [Feedback] Fix crashing when null message is received. 2020-02-09 23:03:37 +01:00
57a8d72f1c [Feedback] Fix received messages not displaying for user. 2020-02-09 23:00:01 +01:00
7e57617e04 [Feedback] Update proguard rules for feedback message entity. 2020-02-09 22:03:41 +01:00
37ddd643ac [Feedback] Hide notification when feedback is open. Fix mixing messages when a thread is open. 2020-02-09 15:10:03 +01:00
bcf3fef303 [Widget/Timetable] Fix no lessons text not legible on dark background. 2020-02-09 14:34:38 +01:00
7ac4d24106 [4.0-beta.7] Update build.gradle, signing and changelog. 2020-02-08 23:16:54 +01:00
93e5bce778 [Feedback] Fix showing wrong names, improve messages filtering by device id. 2020-02-08 23:12:32 +01:00
d48beba307 [Notifications] Fix timetable notification not having subject name. 2020-02-08 23:11:59 +01:00
760338496c [Dialog/GenerateBlockTimetable] Add option for showing teacher names. 2020-02-08 01:33:34 +01:00
b52e7a3078 [Database] Remove unnecessary migration. 2020-02-04 20:59:13 +01:00
78c5b6b2a5 [Database] Fix migrations from 3.2.1 to 4.0-beta. 2020-02-04 00:31:56 +01:00
60a3c38951 [API/Vulcan] Add automatic semester date and ID updating. 2020-02-01 21:41:09 +01:00
4763033f24 [4.0-beta.6] Update build.gradle, signing and changelog. 2020-01-28 22:53:52 +01:00
3b0570d21c Revert "[Sync] Lower the priority of sync notification."
This reverts commit 1677be9e6fa30478564df94201ea0f8193d6e5b8.
2020-01-28 22:47:59 +01:00
16bf478d1a [UI/Agenda] Rewrite agenda in Kotlin and add lesson change counters. 2020-01-26 22:20:46 +01:00
5bf181b6d1 [Feedback] Implement notifications. 2020-01-26 22:03:20 +01:00
21b2e5d194 [Feedback] Add new feedback fragment and API. 2020-01-26 20:05:32 +01:00
759afcf3ca [Database/Migrations] Move migrations to files. 2020-01-25 17:23:47 +01:00
d48c7844a4 [UI/Settings] Add grades config dialog to settings fragment. 2020-01-25 13:31:40 +01:00
7d8caa8df7 [API/Librus] Use added by teacher id instead of lesson teacher in attendance. 2020-01-25 12:42:03 +01:00
62f53930da [Dialog/GradesConfig] Add grades view config dialog. 2020-01-22 23:09:07 +01:00
9a45cbb679 [UI] Add keepScreenOn in counter activity and bell sync dialog. 2020-01-22 23:08:23 +01:00
8e5a10f6d8 [API/Mobidziennik] Implement getting email for push registration. 2020-01-21 20:44:49 +01:00
10c57d2272 [Sync] Lower the priority of sync notification. 2020-01-20 23:10:35 +01:00
67d4d0f898 [Sync] Fix doubled and dead notifications during sync. 2020-01-20 21:36:53 +01:00
97e0d04842 [API] Partially revert "Include device object in each request." 2020-01-20 21:34:22 +01:00
3ba30ede92 [Sync] Fix sync notification crashing on Oreo+. 2020-01-20 20:26:52 +01:00
1035e411ab [API] Include device object in each request. 2020-01-20 19:43:14 +01:00
d5ae4b7ec9 [Home/Grades] Remove filtering grades by semester. 2020-01-20 19:30:20 +01:00
111d040cf9 [Updates] Fix no update toast not visible. 2020-01-20 19:27:59 +01:00
8cc594d170 [Widget/Timetable] Fix widget crashing with NO_LESSONS item. 2020-01-20 19:27:06 +01:00
d8a8bed68d [4.0-beta.5] Update build.gradle and signing. 2020-01-19 22:42:29 +01:00
eedbd954bd [Updates] Add toast for error while checking and for no updates. 2020-01-19 22:35:12 +01:00
0eb8366027 [Changelog] Fix changelog dialog appearance on Android N+. 2020-01-19 22:29:34 +01:00
894135104b [4.0-beta.4] Update build.gradle, signing and changelog. 2020-01-19 22:18:20 +01:00
7b2e408efc [Sync] Make sync not possible for archived profiles. Translate some error codes. 2020-01-19 22:17:57 +01:00
e4115c122e [Errors] Add reporting app version name along with the error. 2020-01-19 21:57:17 +01:00
537b16949e [Updates] Fix running update worker in Java. 2020-01-19 21:52:08 +01:00
ca60ceb2a7 [Firebase] Implement handling app updates. 2020-01-19 21:49:58 +01:00
0fad12fea5 [Updates] Change update channel to beta. 2020-01-19 21:39:28 +01:00
6cd2c23aac [Firebase] Implement handling server messages. 2020-01-19 21:31:37 +01:00
512baaa43f [Errors] Include parser error body when reporting HTTP errors. 2020-01-19 20:34:10 +01:00
d097fcc973 [API/Librus] Fix classrooms name short extraction when name contains two spaces. 2020-01-19 20:22:41 +01:00
621dbd459c [API/Vulcan] Fix marking messages as seen. 2020-01-19 20:03:25 +01:00
840ab4b0c4 [Models] Remove unused models and classes. 2020-01-19 19:30:38 +01:00
904be34a87 [Notifications] Fix showing an empty notification where the list is empty. 2020-01-19 19:19:30 +01:00
b7fc6fcc38 [Structure] Refactor App class to Kotlin. Rewrite SzkolnyTask and posting notifications. Remove dependency on AppConfig. Update libraries and gradle. 2020-01-19 19:07:27 +01:00
55c6e40d6d [DB] Convert AppDb to Kotlin. 2020-01-19 18:44:57 +01:00
4dfb015057 [Firebase/Librus] Implement basic push integration. 2020-01-19 01:17:33 +01:00
e40a0ba2bb [Strings] Add missing translations. 2020-01-18 00:44:13 +01:00
fd48f10df9 [Dialog/EventDetails] Show toast when calendar app not found instead of crashing. 2020-01-18 00:26:26 +01:00
6a54e7fef7 [Firebase] Implement Mobidziennik push service. 2020-01-16 09:27:30 +01:00
5c4d6ed140 [API/Edudziennik] Fix getting attendances. 2020-01-15 23:19:33 +01:00
9ed1be3594 [UI/DrawerProfiles] Add button for marking everything as read in every profile. 2020-01-15 22:58:22 +01:00
c5ce582678 [API/Edudziennik] Fix fix for semesters and getting grades on first login. 2020-01-15 22:26:38 +01:00
2050083bce [API/Edudziennik] Fix semesters and getting grades on first login. 2020-01-15 21:19:48 +01:00
92e6bdb562 [API/iDziennik] Fix regex getting school year in first login. 2020-01-13 22:42:30 +01:00
93e70c38b7 [Firebase] Implement base for per-register FCM tasks. 2020-01-12 21:39:06 +01:00
45b96179a5 [DB] Fix timetable migration crashing app. 2020-01-12 21:33:46 +01:00
a29a534a40 [API/Edudziennik] Fix showing notifications for presence attendances. 2020-01-12 19:45:20 +01:00
8e2297359c [Firebase] Implement new custom FCM service. 2020-01-11 19:07:25 +01:00
92ba7248ef [UI] Fix vector drawables crashing on API < 21. 2020-01-11 13:56:09 +01:00
f657d37cbd [DB/Timetable] Fix migration fixing wrong primary key columns. 2020-01-10 22:23:14 +01:00
9e312f60bf [APIService] Fix showing notification with no service running. 2020-01-10 21:34:55 +01:00
85f72b78f7 [DB/Timetable] Fix wrong primary key columns. 2020-01-10 21:33:59 +01:00
40acb67ceb [Errors] Fix Timeout error detection (SocketTimeoutException inherits from InterruptedIOException). 2020-01-10 16:44:54 +01:00
3ae8100bda [4.0-beta.3] Update build.gradle, signing and changelog. 2020-01-10 11:17:42 +01:00
1a3dc41edf [API/Idziennik] Fix error when historical grade has no color. 2020-01-10 11:14:43 +01:00
b111d33b04 [Widget/Config] Disable Unified lucky number widget in config activity. 2020-01-09 21:11:43 +01:00
ea5720d1c8 [API/Edudziennik] Fix grades colors. 2020-01-08 21:12:10 +01:00
53675122c6 [Dialog/EventDetails] Add feature for saving events in calendar app. 2020-01-08 20:51:06 +01:00
4ba7997bc1 [Database] Fix bug with teacher and subject id when sharing event. 2020-01-07 21:37:58 +01:00
19c446d267 [Widgets/LuckyNumber] Implement a widget. Not knowing if it works or not, sorry. 2020-01-07 11:42:51 +01:00
1abb9ac378 [API] Fix config not reading from DB. Do not sync device if not changed. 2020-01-07 10:45:21 +01:00
f9c7492726 [Widget/Timetable] Fix past lessons (today) not displayed. 2020-01-07 10:14:43 +01:00
6ece6ca52a [UI/Counter] Add Bell Sync option in counter activity. 2020-01-07 09:48:24 +01:00
563 changed files with 18376 additions and 11975 deletions

View File

@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
//apply plugin: 'me.tatarka.retrolambda' //apply plugin: 'me.tatarka.retrolambda'
android { android {
compileSdkVersion rootProject.ext.compileSdkVersion compileSdkVersion setup.compileSdk
android { android {
lintOptions { lintOptions {
@ -12,7 +12,7 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 14 minSdkVersion 14
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion setup.targetSdk
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
} }
@ -43,9 +43,9 @@ android {
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
// Google libraries // Google libraries
implementation "androidx.appcompat:appcompat:${androidXAppCompat}" implementation "androidx.appcompat:appcompat:${versions.appcompat}"
implementation "androidx.recyclerview:recyclerview:${androidXRecyclerView}" implementation "androidx.recyclerview:recyclerview:${versions.recyclerView}"
implementation "com.google.android.material:material:${googleMaterial}" implementation "com.google.android.material:material:${versions.material}"
// other libraries // other libraries
//implementation 'se.emilsjolander:stickylistheaders:2.7.0' //implementation 'se.emilsjolander:stickylistheaders:2.7.0'

View File

@ -1,13 +1,14 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'
apply plugin: 'io.fabric' apply plugin: 'io.fabric'
android { android {
signingConfigs { signingConfigs {
} }
compileSdkVersion rootProject.ext.compileSdkVersion compileSdkVersion setup.compileSdk
defaultConfig { defaultConfig {
applicationId 'pl.szczodrzynski.edziennik' applicationId 'pl.szczodrzynski.edziennik'
minSdkVersion setup.minSdk minSdkVersion setup.minSdk
@ -103,7 +104,7 @@ tasks.whenTaskAdded { task ->
dependencies { dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
annotationProcessor "androidx.room:room-compiler:${versions.room}" kapt "androidx.room:room-compiler:${versions.room}"
debugImplementation "com.amitshekhar.android:debug-db:1.0.5" debugImplementation "com.amitshekhar.android:debug-db:1.0.5"
implementation "android.arch.navigation:navigation-fragment-ktx:${versions.navigationFragment}" implementation "android.arch.navigation:navigation-fragment-ktx:${versions.navigationFragment}"
@ -127,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}"
@ -165,8 +167,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'
@ -187,6 +189,10 @@ dependencies {
implementation "com.squareup.retrofit2:converter-gson:${versions.retrofit}" implementation "com.squareup.retrofit2:converter-gson:${versions.retrofit}"
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 'com.github.kuba2k2:NumberSlidingPicker:2921225f76'
} }
repositories { repositories {
mavenCentral() mavenCentral()

View File

@ -24,11 +24,18 @@
-keep class pl.szczodrzynski.edziennik.utils.models.** { *; } -keep class pl.szczodrzynski.edziennik.utils.models.** { *; }
-keep class pl.szczodrzynski.edziennik.data.db.entity.Event { *; } -keep class pl.szczodrzynski.edziennik.data.db.entity.Event { *; }
-keep class pl.szczodrzynski.edziennik.data.db.full.EventFull { *; } -keep class pl.szczodrzynski.edziennik.data.db.full.EventFull { *; }
-keep class pl.szczodrzynski.edziennik.data.db.entity.FeedbackMessage { *; }
-keep class pl.szczodrzynski.edziennik.ui.modules.home.HomeCardModel { *; } -keep class pl.szczodrzynski.edziennik.ui.modules.home.HomeCardModel { *; }
-keepclassmembers class pl.szczodrzynski.edziennik.ui.widgets.WidgetConfig { public *; } -keepclassmembers class pl.szczodrzynski.edziennik.ui.widgets.WidgetConfig { public *; }
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.timetable.WidgetTimetableProvider -keepnames class pl.szczodrzynski.edziennik.ui.widgets.timetable.WidgetTimetableProvider
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.notifications.WidgetNotificationsProvider -keepnames class pl.szczodrzynski.edziennik.ui.widgets.notifications.WidgetNotificationsProvider
-keepnames class pl.szczodrzynski.edziennik.widgets.luckynumber.WidgetLuckyNumber -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$* {

View File

@ -62,7 +62,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>
@ -80,10 +80,11 @@
<service android:name=".ui.widgets.timetable.WidgetTimetableService" <service android:name=".ui.widgets.timetable.WidgetTimetableService"
android:permission="android.permission.BIND_REMOTEVIEWS" /> android:permission="android.permission.BIND_REMOTEVIEWS" />
<activity android:name=".ui.widgets.LessonDialogActivity" <activity android:name=".ui.widgets.LessonDialogActivity"
android:label=""
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">
@ -98,7 +99,7 @@
<service android:name=".ui.widgets.notifications.WidgetNotificationsService" <service android:name=".ui.widgets.notifications.WidgetNotificationsService"
android:permission="android.permission.BIND_REMOTEVIEWS" /> android:permission="android.permission.BIND_REMOTEVIEWS" />
<!-- LUCKY NUMBER --> <!-- LUCKY NUMBER -->
<receiver android:name=".widgets.luckynumber.WidgetLuckyNumber" <receiver android:name=".ui.widgets.luckynumber.WidgetLuckyNumberProvider"
android:label="@string/widget_lucky_number_title"> android:label="@string/widget_lucky_number_title">
<intent-filter> <intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
@ -141,12 +142,8 @@
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/AppTheme" /> android:theme="@style/AppTheme" />
<activity android:name=".ui.modules.settings.SettingsLicenseActivity" <activity android:name=".ui.modules.settings.SettingsLicenseActivity"
android:configChanges="orientation|keyboardHidden" android:configChanges="orientation|keyboardHidden"
android:theme="@style/AppTheme" /> android:theme="@style/AppTheme" />
<activity android:name=".ui.modules.webpush.WebPushConfigActivity"
android:configChanges="orientation|keyboardHidden"
android:theme="@style/AppTheme.Dark" />
<activity android:name=".ui.modules.webpush.QrScannerActivity" /> <activity android:name=".ui.modules.webpush.QrScannerActivity" />
<activity android:name="com.theartofdev.edmodo.cropper.CropImageActivity" <activity android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
android:configChanges="orientation|keyboardHidden" android:configChanges="orientation|keyboardHidden"
@ -165,18 +162,9 @@
<action android:name="android.intent.action.USER_PRESENT" /> <action android:name="android.intent.action.USER_PRESENT" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver android:name=".receivers.BootReceiver"> <receiver android:name=".sync.UpdateDownloaderService$DownloadProgressReceiver">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" /> <action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
<receiver android:name=".sync.FirebaseBroadcastReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver android:name=".receivers.SzkolnyReceiver" <receiver android:name=".receivers.SzkolnyReceiver"
@ -193,16 +181,22 @@
____) | __/ | \ V /| | (_| __/\__ \ ____) | __/ | \ V /| | (_| __/\__ \
|_____/ \___|_| \_/ |_|\___\___||___/ |_____/ \___|_| \_/ |_|\___\___||___/
--> -->
<service android:name=".sync.MyFirebaseMessagingService" <!--<service android:name=".sync.MyFirebaseMessagingService"
android:exported="false"> android:exported="false">
<intent-filter> <intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" /> <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
<action android:name="com.google.firebase.MESSAGING_EVENT" /> <action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter> </intent-filter>
</service> </service>-->
<service android:name=".receivers.BootReceiver$NotificationActionService" />
<service android:name=".Notifier$GetDataRetryService" />
<service android:name=".data.api.ApiService" /> <service android:name=".data.api.ApiService" />
<service android:name=".data.firebase.MyFirebaseService"
android:exported="false">
<intent-filter android:priority="10000000">
<action android:name="com.google.firebase.MESSAGING_EVENT" />
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
<service android:name=".sync.UpdateDownloaderService" />
<!-- <!--
_____ _ _ _____ _ _

View File

@ -1,38 +1,29 @@
<h3>Wersja 4.0-beta.2, 2020-01-06</h3> <h3>Wersja 4.0-rc.2, 2020-03-26</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><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><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><b>Przebudowaliśmy cały moduł synchronizacji</b>, co oznacza większą stabilność aplikacji, szybkość oraz poprawność pobieranych danych</li>
<li>Udoskonalony wygląd Szkolnego - sprawi, że korzystanie z aplikacji będzie jeszcze przyjemniejsze</li> <li>Udoskonalony wygląd Szkolnego - sprawi, że korzystanie z aplikacji będzie jeszcze przyjemniejsze</li>
<li>Nowa <b>Strona główna</b> - ładniejszy wygląd oraz możliwość przestawiania kart na każdym profilu</li> <li>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>Nowy <b>Plan lekcji</b> - z doskonałą obsługą lekcji przesuniętych oraz dwóch lekcji o tej samej godzinie</li>
<li>Nowe <b>Oceny</b> - z możliwością zmiany wartości plusów oraz minusów oraz wyłączenia niektórych ocen ze średniej</li>
<li>Opcja wyłączenia wybranych powiadomień z aplikacji</li>
<li>Znaczki nieprzeczytanych informacji na obrazkach profili.</li>
<br>
<br>
<li>Nowe okienka informacji o wydarzeniach oraz lekcjach</li> <li>Nowe okienka informacji o wydarzeniach oraz lekcjach</li>
<li>Łatwiejsze dodawanie własnych wydarzeń</li> <li>Nowe, przyjemniejsze powiadomienia</li>
<li>Dużo poprawek w widoku <b>Wiadomości</b> oraz <b>Ogłoszeń</b></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>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: 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: <b>poprawione obliczanie frekwencji</b></li>
<li>Librus: obsługa Zadań domowych bez posiadania Mobilnych dodatków (przez system Synergia)</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>Lepsze <b>przekazywanie powiadomień na komputer</b> oraz łatwiejsze parowanie</li>
<li>Łatwiejsze dodawanie własnych wydarzeń</li>
<li>Poprawiliśmy synchronizację w tle na niektórych telefonach</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>Znaczna ilość błędów z poprzednich wersji już nie występuje</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 czasami nie działać - proszę o zgłaszanie wszystkich błędów na naszym serwerze Discord</li>
<li>Widget szczęśliwego numerka</li>
<li>Terminarz - brak informacji o odwołanych lekcjach w dialogu</li>
</ul>
<br>
<br>
<br>
<br>
<i>Okazja ograniczona czasowo:</i> Poczuj prawdziwą zimę, włączając w Ustawieniach widok padającego śniegu!
<br>
<br>
<br>
Dzięki za korzystanie ze Szkolnego!<br> 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] = {
0xf5, 0xbe, 0x91, 0x89, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 0x30, 0xfe, 0xe2, 0x8d, 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

@ -1,714 +0,0 @@
package pl.szczodrzynski.edziennik;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.content.pm.Signature;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Handler;
import android.provider.Settings;
import android.util.Base64;
import android.util.Log;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.work.Configuration;
import com.chuckerteam.chucker.api.ChuckerCollector;
import com.chuckerteam.chucker.api.ChuckerInterceptor;
import com.chuckerteam.chucker.api.RetentionManager;
import com.google.android.gms.security.ProviderInstaller;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import com.hypertrack.hyperlog.HyperLog;
import com.mikepenz.iconics.Iconics;
import com.mikepenz.iconics.IconicsColor;
import com.mikepenz.iconics.IconicsDrawable;
import com.mikepenz.iconics.IconicsSize;
import com.mikepenz.iconics.typeface.IIcon;
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont;
import java.lang.reflect.Field;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import cat.ereza.customactivityoncrash.config.CaocConfig;
import im.wangchao.mhttp.MHttp;
import im.wangchao.mhttp.internal.cookie.PersistentCookieJar;
import im.wangchao.mhttp.internal.cookie.cache.SetCookieCache;
import im.wangchao.mhttp.internal.cookie.persistence.SharedPrefsCookiePersistor;
import me.leolin.shortcutbadger.ShortcutBadger;
import okhttp3.ConnectionSpec;
import okhttp3.OkHttpClient;
import okhttp3.TlsVersion;
import pl.szczodrzynski.edziennik.config.Config;
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing;
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask;
import pl.szczodrzynski.edziennik.data.db.AppDb;
import pl.szczodrzynski.edziennik.data.db.entity.DebugLog;
import pl.szczodrzynski.edziennik.data.db.entity.Profile;
import pl.szczodrzynski.edziennik.network.NetworkUtils;
import pl.szczodrzynski.edziennik.network.TLSSocketFactory;
import pl.szczodrzynski.edziennik.sync.SyncWorker;
import pl.szczodrzynski.edziennik.ui.modules.base.CrashActivity;
import pl.szczodrzynski.edziennik.utils.DebugLogFormat;
import pl.szczodrzynski.edziennik.utils.PermissionChecker;
import pl.szczodrzynski.edziennik.utils.Themes;
import pl.szczodrzynski.edziennik.utils.Utils;
import pl.szczodrzynski.edziennik.utils.models.AppConfig;
import static pl.szczodrzynski.edziennik.data.db.entity.LoginStore.LOGIN_TYPE_MOBIDZIENNIK;
public class App extends androidx.multidex.MultiDexApplication implements Configuration.Provider {
private static final String TAG = "App";
public static int profileId = -1;
private Context mContext;
@Override
public Configuration getWorkManagerConfiguration() {
return new Configuration.Builder()
.setMinimumLoggingLevel(Log.VERBOSE)
.build();
}
public static final int REQUEST_TIMEOUT = 10 * 1000;
// notifications
//public NotificationManager mNotificationManager;
//public final String NOTIFICATION_CHANNEL_ID_UPDATES = "4566";
//public String NOTIFICATION_CHANNEL_NAME_UPDATES;
public Notifier notifier;
public static final String APP_URL = "://edziennik.szczodrzynski.pl/app/";
public ShortcutManager shortcutManager;
public PermissionChecker permissionChecker;
public String signature = "";
public String deviceId = "";
public AppDb db;
public void debugLog(String text) {
if (!devMode)
return;
db.debugLogDao().add(new DebugLog(Utils.getCurrentTimeUsingCalendar()+": "+text));
}
public void debugLogAsync(String text) {
if (!devMode)
return;
AsyncTask.execute(() -> {
db.debugLogDao().add(new DebugLog(Utils.getCurrentTimeUsingCalendar()+": "+text));
});
}
// network & APIs
public NetworkUtils networkUtils;
public PersistentCookieJar cookieJar;
public OkHttpClient http;
public OkHttpClient httpLazy;
public SharedPreferences appSharedPrefs; // sharedPreferences for APPCONFIG + JOBS STORE
public AppConfig appConfig; // APPCONFIG: common for all profiles
//public AppProfile profile; // current profile
public SharedPreferences registerStore; // sharedPreferences for REGISTER
//public Register register; // REGISTER for current profile, read from registerStore
public Profile profile;
public Config config;
private static Config mConfig;
public static Config getConfig() {
return mConfig;
}
// other stuff
public Gson gson;
public String requestScheme = "https";
public boolean unreadBadgesAvailable = true;
public static boolean devMode = false;
public static final boolean UPDATES_ON_PLAY_STORE = true;
@RequiresApi(api = Build.VERSION_CODES.M)
public Icon getDesktopIconFromIconics(IIcon icon) {
final IconicsDrawable drawable = new IconicsDrawable(mContext, icon)
.color(IconicsColor.colorInt(Color.WHITE))
.size(IconicsSize.dp(48))
.padding(IconicsSize.dp(8))
.backgroundColor(IconicsColor.colorRes(R.color.colorPrimaryDark))
.roundedCorners(IconicsSize.dp(10));
//drawable.setStyle(Paint.Style.FILL);
final Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return Icon.createWithBitmap(bitmap);
}
@Override
public void onCreate() {
super.onCreate();
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
CaocConfig.Builder.create()
.backgroundMode(CaocConfig.BACKGROUND_MODE_SHOW_CUSTOM) //default: CaocConfig.BACKGROUND_MODE_SHOW_CUSTOM
.enabled(true) //default: true
.showErrorDetails(true) //default: true
.showRestartButton(true) //default: true
.logErrorOnRestart(true) //default: true
.trackActivities(true) //default: false
.minTimeBetweenCrashesMs(2000) //default: 3000
.errorDrawable(R.drawable.ic_rip) //default: bug image
.restartActivity(MainActivity.class) //default: null (your app's launch activity)
.errorActivity(CrashActivity.class) //default: null (default error activity)
//.eventListener(new YourCustomEventListener()) //default: null
.apply();
mContext = this;
db = AppDb.getDatabase(this);
gson = new Gson();
networkUtils = new NetworkUtils(this);
config = new Config(db);
config.migrate(this);
mConfig = config;
Iconics.init(getApplicationContext());
Iconics.registerFont(SzkolnyFont.INSTANCE);
notifier = new Notifier(this);
permissionChecker = new PermissionChecker(mContext);
deviceId = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
cookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(this));
appSharedPrefs = getSharedPreferences(getString(R.string.preference_file_global), Context.MODE_PRIVATE);
loadConfig();
Signing.INSTANCE.getCert(this);
Themes.INSTANCE.setThemeInt(config.getUi().getTheme());
try {
PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);
for (Signature signature: packageInfo.signatures) {
byte[] signatureBytes = signature.toByteArray();
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(signatureBytes);
this.signature = Base64.encodeToString(md.digest(), Base64.NO_WRAP);
//Log.d(TAG, "Signature is "+this.signature);
}
}
catch (Exception e) {
e.printStackTrace();
}
if ("f054761fbdb6a238".equals(deviceId) || BuildConfig.DEBUG) {
devMode = true;
}
else if (config.getDevModePassword() != null) {
checkDevModePassword();
}
OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder()
.cache(null)
.followRedirects(true)
.followSslRedirects(true)
.retryOnConnectionFailure(true)
.cookieJar(cookieJar)
.connectTimeout(30, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.readTimeout(40, TimeUnit.SECONDS);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
try {
try {
ProviderInstaller.installIfNeeded(this);
} catch (Exception e) {
Log.e("OkHttpTLSCompat", "Play Services not found or outdated");
X509TrustManager x509TrustManager = null;
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
for (TrustManager trustManager: trustManagerFactory.getTrustManagers()) {
if (trustManager instanceof X509TrustManager)
x509TrustManager = (X509TrustManager) trustManager;
}
SSLContext sc = SSLContext.getInstance("TLSv1.2");
sc.init(null, null, null);
httpBuilder.sslSocketFactory(new TLSSocketFactory(sc.getSocketFactory()), x509TrustManager);
ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_0)
.tlsVersions(TlsVersion.TLS_1_1)
.tlsVersions(TlsVersion.TLS_1_2)
.build();
List<ConnectionSpec> specs = new ArrayList<>();
specs.add(cs);
specs.add(ConnectionSpec.COMPATIBLE_TLS);
specs.add(ConnectionSpec.CLEARTEXT);
httpBuilder.connectionSpecs(specs);
}
} catch (Exception exc) {
Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc);
}
}
if (App.devMode || BuildConfig.DEBUG) {
HyperLog.initialize(this);
HyperLog.setLogLevel(Log.VERBOSE);
HyperLog.setLogFormat(new DebugLogFormat(this));
ChuckerCollector chuckerCollector = new ChuckerCollector(this, true, RetentionManager.Period.ONE_HOUR);
ChuckerInterceptor chuckerInterceptor = new ChuckerInterceptor(this, chuckerCollector);
httpBuilder.addInterceptor(chuckerInterceptor);
}
http = httpBuilder.build();
httpLazy = http.newBuilder().followRedirects(false).followSslRedirects(false).build();
MHttp.instance()
.customOkHttpClient(http);
//register = new Register(mContext);
//profileLoadById(appSharedPrefs.getInt("current_profile_id", 1));
if (config.getSync().getEnabled()) {
SyncWorker.Companion.scheduleNext(this, false);
}
else {
SyncWorker.Companion.cancelNext(this);
}
db.metadataDao().countUnseen().observeForever(count -> {
Log.d("MainActivity", "Overall unseen count changed");
assert count != null;
if (unreadBadgesAvailable) {
unreadBadgesAvailable = ShortcutBadger.applyCount(this, count);
}
});
//new IonCookieManager(mContext);
new Handler().post(() -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
shortcutManager = getSystemService(ShortcutManager.class);
ShortcutInfo shortcutTimetable = new ShortcutInfo.Builder(mContext, "item_timetable")
.setShortLabel(getString(R.string.shortcut_timetable)).setLongLabel(getString(R.string.shortcut_timetable))
.setIcon(Icon.createWithResource(this, R.mipmap.ic_shortcut_timetable))
//.setIcon(getDesktopIconFromIconics(CommunityMaterial.Icon2.cmd_timetable))
.setIntent(new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_TIMETABLE))
.build();
ShortcutInfo shortcutAgenda = new ShortcutInfo.Builder(mContext, "item_agenda")
.setShortLabel(getString(R.string.shortcut_agenda)).setLongLabel(getString(R.string.shortcut_agenda))
.setIcon(Icon.createWithResource(this, R.mipmap.ic_shortcut_agenda))
//.setIcon(getDesktopIconFromIconics(CommunityMaterial.Icon.cmd_calendar))
.setIntent(new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_AGENDA))
.build();
ShortcutInfo shortcutGrades = new ShortcutInfo.Builder(mContext, "item_grades")
.setShortLabel(getString(R.string.shortcut_grades)).setLongLabel(getString(R.string.shortcut_grades))
.setIcon(Icon.createWithResource(this, R.mipmap.ic_shortcut_grades))
//.setIcon(getDesktopIconFromIconics(CommunityMaterial.Icon2.cmd_numeric_5_box))
.setIntent(new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_GRADES))
.build();
ShortcutInfo shortcutHomework = new ShortcutInfo.Builder(mContext, "item_homeworks")
.setShortLabel(getString(R.string.shortcut_homework)).setLongLabel(getString(R.string.shortcut_homework))
.setIcon(Icon.createWithResource(this, R.mipmap.ic_shortcut_homework))
//.setIcon(getDesktopIconFromIconics(SzkolnyFont.Icon.szf_file_document_edit))
.setIntent(new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_HOMEWORK))
.build();
ShortcutInfo shortcutMessages = new ShortcutInfo.Builder(mContext, "item_messages")
.setShortLabel(getString(R.string.shortcut_messages)).setLongLabel(getString(R.string.shortcut_messages))
.setIcon(Icon.createWithResource(this, R.mipmap.ic_shortcut_messages))
//.setIcon(getDesktopIconFromIconics(CommunityMaterial.Icon.cmd_email))
.setIntent(new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_MESSAGES ))
.build();
shortcutManager.setDynamicShortcuts(Arrays.asList(shortcutTimetable, shortcutAgenda, shortcutGrades, shortcutHomework, shortcutMessages));
}
if (config.getAppInstalledTime() == 0) {
try {
config.setAppInstalledTime(getPackageManager().getPackageInfo(getPackageName(), 0).firstInstallTime);
config.setAppRateSnackbarTime(config.getAppInstalledTime() + 7 * 24 * 60 * 60 * 1000);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
/*Task<CapabilityInfo> capabilityInfoTask =
Wearable.getCapabilityClient(this)
.getCapability("edziennik_wear_app", CapabilityClient.FILTER_REACHABLE);
capabilityInfoTask.addOnCompleteListener((task) -> {
if (task.isSuccessful()) {
CapabilityInfo capabilityInfo = task.getResult();
assert capabilityInfo != null;
Set<Node> nodes;
nodes = capabilityInfo.getNodes();
Log.d(TAG, "Nodes "+nodes);
if (nodes.size() > 0) {
Wearable.getMessageClient(this).sendMessage(
nodes.toArray(new Node[]{})[0].getId(), "/ping", "Hello world".getBytes());
}
} else {
Log.d(TAG, "Capability request failed to return any results.");
}
});
Wearable.getDataClient(this).addListener(dataEventBuffer -> {
Log.d(TAG, "onDataChanged(): " + dataEventBuffer);
for (DataEvent event : dataEventBuffer) {
if (event.getType() == DataEvent.TYPE_CHANGED) {
String path = event.getDataItem().getUri().getPath();
Log.d(TAG, "Data "+path+ " :: "+Arrays.toString(event.getDataItem().getData()));
}
}
});*/
FirebaseApp pushMobidziennikApp = FirebaseApp.initializeApp(
this,
new FirebaseOptions.Builder()
.setApiKey("AIzaSyCi5LmsZ5BBCQnGtrdvWnp1bWLCNP8OWQE")
.setApplicationId("1:747285019373:android:f6341bf7b158621d")
.build(),
"Mobidziennik2"
);
FirebaseApp pushLibrusApp = FirebaseApp.initializeApp(
this,
new FirebaseOptions.Builder()
.setApiKey("AIzaSyDfTuEoYPKdv4aceEws1CO3n0-HvTndz-o")
.setApplicationId("1:513056078587:android:1e29083b760af544")
.build(),
"Librus"
);
FirebaseApp pushVulcanApp = FirebaseApp.initializeApp(
this,
new FirebaseOptions.Builder()
.setApiKey("AIzaSyDW8MUtanHy64_I0oCpY6cOxB3jrvJd_iA")
.setApplicationId("1:987828170337:android:ac97431a0a4578c3")
.build(),
"Vulcan"
);
if (config.getRunSync()) {
config.setRunSync(false);
EdziennikTask.Companion.sync().enqueue(this);
}
try {
final long startTime = System.currentTimeMillis();
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(instanceIdResult -> {
Log.d(TAG, "Token for App is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId()+". Time is "+(System.currentTimeMillis() - startTime));
config.getSync().setTokenApp(instanceIdResult.getToken());
});
/*FirebaseInstanceId.getInstance(pushMobidziennikApp).getInstanceId().addOnSuccessListener(instanceIdResult -> {
Log.d(TAG, "Token for Mobidziennik is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId());
appConfig.fcmTokens.put(LOGIN_TYPE_MOBIDZIENNIK, new Pair<>(instanceIdResult.getToken(), new ArrayList<>()));
});
FirebaseInstanceId.getInstance(pushLibrusApp).getInstanceId().addOnSuccessListener(instanceIdResult -> {
Log.d(TAG, "Token for Librus is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId());
appConfig.fcmTokens.put(LOGIN_TYPE_LIBRUS, new Pair<>(instanceIdResult.getToken(), new ArrayList<>()));
});
FirebaseInstanceId.getInstance(pushVulcanApp).getInstanceId().addOnSuccessListener(instanceIdResult -> {
Log.d(TAG, "Token for Vulcan is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId());
Pair<String, List<Integer>> pair = appConfig.fcmTokens.get(LOGIN_TYPE_VULCAN);
if (pair == null || pair.first == null || !pair.first.equals(instanceIdResult.getToken())) {
appConfig.fcmTokens.put(LOGIN_TYPE_VULCAN, new Pair<>(instanceIdResult.getToken(), new ArrayList<>()));
}
});*/
FirebaseMessaging.getInstance().subscribeToTopic(getPackageName());
}
catch (IllegalStateException e) {
e.printStackTrace();
}
});
}
public void loadConfig()
{
appConfig = new AppConfig(this);
if (appSharedPrefs.contains("config")) {
// remove old-format config, save the new one and empty the incorrectly-nulled config
appConfig = gson.fromJson(appSharedPrefs.getString("config", ""), AppConfig.class);
appSharedPrefs.edit().remove("config").apply();
saveConfig();
appConfig = new AppConfig(this);
}
if (appSharedPrefs.contains("profiles")) {
SharedPreferences.Editor appSharedPrefsEditor = appSharedPrefs.edit();
/*List<Integer> appProfileIds = gson.fromJson(appSharedPrefs.getString("profiles", ""), new TypeToken<List<Integer>>(){}.getType());
for (int id: appProfileIds) {
AppProfile appProfile = gson.fromJson(appSharedPrefs.getString("profile"+id, ""), AppProfile.class);
if (appProfile != null) {
appConfig.profiles.add(appProfile);
}
appSharedPrefsEditor.remove("profile"+id);
}*/
appSharedPrefsEditor.remove("profiles");
appSharedPrefsEditor.apply();
//profilesSave();
}
Map<String,?> keys = appSharedPrefs.getAll();
for (Map.Entry<String,?> entry : keys.entrySet()) {
if (entry.getKey().startsWith("app.appConfig.")) {
String fieldName = entry.getKey().replace("app.appConfig.", "");
try {
Field field = AppConfig.class.getField(fieldName);
Object object;
try {
object = gson.fromJson(entry.getValue().toString(), field.getGenericType());
} catch (JsonSyntaxException e) {
Log.d(TAG, "For field "+fieldName);
e.printStackTrace();
object = entry.getValue().toString();
}
if (object != null)
field.set(appConfig, object);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
Log.w(TAG, "Should remove app.appConfig."+fieldName);
//appSharedPrefs.edit().remove("app.appConfig."+fieldName).apply(); TODO migration
}
}
}
/*if (appConfig.lastAppVersion > BuildConfig.VERSION_CODE) {
BootReceiver br = new BootReceiver();
Intent i = new Intent();
//i.putExtra("UserChecked", true);
br.onReceive(getContext(), i);
Toast.makeText(mContext, R.string.warning_older_version_running, Toast.LENGTH_LONG).show();
//Toast.makeText(mContext, "Zaktualizuj aplikację.", Toast.LENGTH_LONG).show();
//System.exit(0);
}*/
if (appConfig == null) {
appConfig = new AppConfig(this);
}
}
public void saveConfig()
{
try {
appConfig.savePending = false;
SharedPreferences.Editor appSharedPrefsEditor = appSharedPrefs.edit();
JsonObject appConfigJson = gson.toJsonTree(appConfig).getAsJsonObject();
for (Map.Entry<String, JsonElement> entry : appConfigJson.entrySet()) {
String jsonObj;
jsonObj = entry.getValue().toString();
/*if (entry.getValue().isJsonObject()) {
jsonObj = entry.getValue().getAsJsonObject().toString();
}
else if (entry.getValue().isJsonArray()) {
jsonObj = entry.getValue().getAsJsonArray().toString();
}
else {
jsonObj = entry.getValue().toString();
}*/
appSharedPrefsEditor.putString("app.appConfig." + entry.getKey(), jsonObj);
}
appSharedPrefsEditor.apply();
}
catch (ConcurrentModificationException e) {
e.printStackTrace();
}
//appSharedPrefs.edit().putString("config", gson.toJson(appConfig)).apply();
}
public void saveConfig(String ... fieldNames)
{
appConfig.savePending = false;
SharedPreferences.Editor appSharedPrefsEditor = appSharedPrefs.edit();
for (String fieldName: fieldNames) {
try {
Object object = AppConfig.class.getField(fieldName).get(appConfig);
appSharedPrefsEditor.putString("app.appConfig."+fieldName, gson.toJson(object));
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (ConcurrentModificationException e) {
e.printStackTrace();
}
}
appSharedPrefsEditor.apply();
//appSharedPrefs.edit().putString("config", gson.toJson(appConfig)).apply();
}
public void profileSave() {
AsyncTask.execute(() -> {
db.profileDao().add(profile);
});
}
public void profileSaveAsync() {
AsyncTask.execute(() -> {
db.profileDao().add(profile);
});
}
public void profileSaveAsync(Profile profile) {
AsyncTask.execute(() -> {
db.profileDao().add(profile);
});
}
public void profileLoadById(int id) {
profileLoadById(id, false);
}
public void profileLoadById(int id, boolean loadedLast) {
//Log.d(TAG, "Loading ID "+id);
/*if (profile == null) {
profile = profileNew();
AppDb.profileId = profile.id;
appSharedPrefs.edit().putInt("current_profile_id", profile.id).apply();
return;
}*/
if (profile == null || profile.getId() != id) {
profile = db.profileDao().getByIdNow(id);
/*if (profile == null) {
profileLoadById(id);
return;
}*/
if (profile != null) {
MainActivity.Companion.setUseOldMessages(profile.getLoginStoreType() == LOGIN_TYPE_MOBIDZIENNIK && appConfig.mobidziennikOldMessages == 1);
profileId = profile.getId();
appSharedPrefs.edit().putInt("current_profile_id", profile.getId()).apply();
config.setProfile(profileId);
}
else if (!loadedLast) {
profileLoadById(profileLastId(), true);
}
else {
profileId = -1;
}
}
}
/*public void profileRemove(int id)
{
Profile profile = db.profileDao().getFullByIdNow(id);
if (profile.id == profile.loginStoreId) {
// this profile is the owner of the login store
// we need to check if any other profile is using it
List<Integer> transferProfileIds = db.profileDao().getIdsByLoginStoreIdNow(profile.loginStoreId);
if (transferProfileIds.size() == 1) {
// this login store is free of users, remove it along with the profile
db.loginStoreDao().remove(profile.loginStoreId);
// the current store is removed, we are ready to remove the profile
}
else if (transferProfileIds.size() > 1) {
transferProfileIds.remove(transferProfileIds.indexOf(profile.id));
// someone is using the store
// we need to transfer it to the firstProfileId
db.loginStoreDao().changeId(profile.loginStoreId, transferProfileIds.get(0));
db.profileDao().changeStoreId(profile.loginStoreId, transferProfileIds.get(0));
// the current store is removed, we are ready to remove the profile
}
}
// else, the profile uses a store that it doesn't own
// leave the store and go on with removing
Log.d(TAG, "Before removal: "+db.profileDao().getAllNow().toString());
db.profileDao().remove(profile.id);
Log.d(TAG, "After removal: "+db.profileDao().getAllNow().toString());
*//*int newId = 1;
if (appConfig.profiles.size() > 0) {
newId = appConfig.profiles.get(appConfig.profiles.size() - 1).id;
}
Log.d(TAG, "New ID: "+newId);
//Toast.makeText(mContext, "selected new id "+newId, Toast.LENGTH_SHORT).show();
profileLoadById(newId);*//*
}*/
public int profileFirstId() {
Integer id = db.profileDao().getFirstId();
return id == null ? 1 : id;
}
public int profileLastId() {
Integer id = db.profileDao().getLastId();
return id == null ? 1 : id;
}
public Context getContext()
{
return mContext;
}
public void checkDevModePassword() {
try {
devMode = Utils.AESCrypt.decrypt("nWFVxY65Pa8/aRrT7EylNAencmOD+IxUY2Gg/beiIWY=", config.getDevModePassword()).equals("ok here you go it's enabled now")
|| BuildConfig.DEBUG;
} catch (Exception e) {
e.printStackTrace();
devMode = false;
}
}
}

View File

@ -4,20 +4,78 @@
package pl.szczodrzynski.edziennik package pl.szczodrzynski.edziennik
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ShortcutInfo
import android.content.pm.ShortcutManager
import android.graphics.drawable.Icon
import android.os.Build
import android.provider.Settings
import android.util.Log import android.util.Log
import androidx.appcompat.app.AppCompatDelegate
import androidx.multidex.MultiDexApplication
import androidx.work.Configuration import androidx.work.Configuration
import kotlinx.coroutines.CoroutineScope import cat.ereza.customactivityoncrash.config.CaocConfig
import kotlinx.coroutines.Dispatchers import com.chuckerteam.chucker.api.ChuckerCollector
import kotlinx.coroutines.Job import com.chuckerteam.chucker.api.ChuckerInterceptor
import com.chuckerteam.chucker.api.RetentionManager
import com.google.firebase.FirebaseApp
import com.google.firebase.FirebaseOptions
import com.google.firebase.iid.FirebaseInstanceId
import com.google.firebase.messaging.FirebaseMessaging
import com.google.gson.Gson
import com.hypertrack.hyperlog.HyperLog
import com.mikepenz.iconics.Iconics
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
import im.wangchao.mhttp.MHttp
import kotlinx.coroutines.*
import me.leolin.shortcutbadger.ShortcutBadger
import okhttp3.OkHttpClient
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.config.Config
import pl.szczodrzynski.edziennik.data.api.events.ProfileListEmptyEvent
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing
import pl.szczodrzynski.edziennik.data.db.AppDb
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.network.NetworkUtils
import pl.szczodrzynski.edziennik.network.cookie.DumbCookieJar
import pl.szczodrzynski.edziennik.sync.SyncWorker
import pl.szczodrzynski.edziennik.sync.UpdateWorker
import pl.szczodrzynski.edziennik.ui.modules.base.CrashActivity
import pl.szczodrzynski.edziennik.utils.*
import pl.szczodrzynski.edziennik.utils.managers.GradesManager
import pl.szczodrzynski.edziennik.utils.managers.NotificationChannelsManager
import pl.szczodrzynski.edziennik.utils.managers.TimetableManager
import pl.szczodrzynski.edziennik.utils.managers.UserActionManager
import java.util.concurrent.TimeUnit
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScope { class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
companion object { companion object {
@Volatile
lateinit var db: AppDb
val config: Config by lazy { Config(db) }
var profile: Profile by mutableLazy { Profile(0, 0, 0, "") }
val profileId
get() = profile.id
var devMode = false var devMode = false
var debugMode = false
} }
//lateinit var db: AppDb val notificationChannelsManager by lazy { NotificationChannelsManager(this) }
//val config by lazy { Config(db); // TODO migrate } val userActionManager by lazy { UserActionManager(this) }
val gradesManager by lazy { GradesManager(this) }
val timetableManager by lazy { TimetableManager(this) }
val db
get() = App.db
val config
get() = App.config
val profile
get() = App.profile
val profileId
get() = App.profileId
private val job = Job() private val job = Job()
override val coroutineContext: CoroutineContext override val coroutineContext: CoroutineContext
@ -26,11 +84,9 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
.setMinimumLoggingLevel(Log.VERBOSE) .setMinimumLoggingLevel(Log.VERBOSE)
.build() .build()
/*val preferences by lazy { getSharedPreferences(getString(R.string.preference_file), Context.MODE_PRIVATE) }
val notifier by lazy { Notifier(this) }
val permissionChecker by lazy { PermissionChecker(this) } val permissionChecker by lazy { PermissionChecker(this) }
val networkUtils by lazy { NetworkUtils(this) }
lateinit var profile: ProfileFull val gson by lazy { Gson() }
/* _ _ _______ _______ _____ /* _ _ _______ _______ _____
| | | |__ __|__ __| __ \ | | | |__ __|__ __| __ \
@ -48,13 +104,13 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
.connectTimeout(20, TimeUnit.SECONDS) .connectTimeout(20, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS) .writeTimeout(5, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS)
builder.installHttpsSupport() builder.installHttpsSupport(this)
if (devMode || BuildConfig.DEBUG) { if (debugMode || BuildConfig.DEBUG) {
HyperLog.initialize(this) HyperLog.initialize(this)
HyperLog.setLogLevel(Log.VERBOSE) HyperLog.setLogLevel(Log.VERBOSE)
HyperLog.setLogFormat(DebugLogFormat(this)) HyperLog.setLogFormat(DebugLogFormat(this))
val chuckerCollector = ChuckerCollector(this, true, Period.ONE_HOUR) val chuckerCollector = ChuckerCollector(this, true, RetentionManager.Period.ONE_HOUR)
val chuckerInterceptor = ChuckerInterceptor(this, chuckerCollector) val chuckerInterceptor = ChuckerInterceptor(this, chuckerCollector)
builder.addInterceptor(chuckerInterceptor) builder.addInterceptor(chuckerInterceptor)
} }
@ -67,7 +123,7 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
.followSslRedirects(false) .followSslRedirects(false)
.build() .build()
} }
val cookieJar by lazy { PersistentCookieJar(SetCookieCache(), SharedPrefsCookiePersistor(this)) } val cookieJar by lazy { DumbCookieJar(this) }
/* _____ _ _ /* _____ _ _
/ ____(_) | | / ____(_) | |
@ -77,22 +133,7 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
|_____/|_|\__, |_| |_|\__,_|\__|\__,_|_| \___| |_____/|_|\__, |_| |_|\__,_|\__|\__,_|_| \___|
__/ | __/ |
|__*/ |__*/
private val deviceId: String by lazy { Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID) ?: "" } val deviceId: String by lazy { Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID) ?: "" }
private val signature: String by lazy {
var str = ""
try {
val packageInfo: PackageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
for (signature in packageInfo.signatures) {
val signatureBytes = signature.toByteArray()
val md = MessageDigest.getInstance("SHA")
md.update(signatureBytes)
str = Base64.encodeToString(md.digest(), Base64.DEFAULT)
}
} catch (e: Exception) {
e.printStackTrace()
}
str
}
private var unreadBadgesAvailable = true private var unreadBadgesAvailable = true
/* _____ _ /* _____ _
@ -118,193 +159,194 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
.apply() .apply()
Iconics.init(applicationContext) Iconics.init(applicationContext)
Iconics.registerFont(SzkolnyFont) Iconics.registerFont(SzkolnyFont)
db = AppDb.getDatabase(this) App.db = AppDb(this)
Themes.themeInt = config.ui.theme Themes.themeInt = config.ui.theme
debugMode = config.debugMode
MHttp.instance().customOkHttpClient(http) MHttp.instance().customOkHttpClient(http)
if (!profileLoadById(config.lastProfileId)) {
db.profileDao().firstId?.let { profileLoadById(it) }
}
devMode = "f054761fbdb6a238" == deviceId || BuildConfig.DEBUG devMode = "f054761fbdb6a238" == deviceId || BuildConfig.DEBUG
if (config.devModePassword != null)
checkDevModePassword()
Signing.getCert(this) Signing.getCert(this)
launch { async(Dispatchers.Default) { launch {
if (config.sync.enabled) { withContext(Dispatchers.Default) {
scheduleNext(this@App, false) config.migrate(this@App)
} else {
cancelNext(this@App) if (config.devModePassword != null)
checkDevModePassword()
debugMode = devMode || config.debugMode
if (config.sync.enabled)
SyncWorker.scheduleNext(this@App, false)
else
SyncWorker.cancelNext(this@App)
if (config.sync.notifyAboutUpdates)
UpdateWorker.scheduleNext(this@App, false)
else
UpdateWorker.cancelNext(this@App)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
val shortcutManager = getSystemService(ShortcutManager::class.java)
val shortcutTimetable = ShortcutInfo.Builder(this@App, "item_timetable")
.setShortLabel(getString(R.string.shortcut_timetable)).setLongLabel(getString(R.string.shortcut_timetable))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_timetable))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_TIMETABLE))
.build()
val shortcutAgenda = ShortcutInfo.Builder(this@App, "item_agenda")
.setShortLabel(getString(R.string.shortcut_agenda)).setLongLabel(getString(R.string.shortcut_agenda))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_agenda))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_AGENDA))
.build()
val shortcutGrades = ShortcutInfo.Builder(this@App, "item_grades")
.setShortLabel(getString(R.string.shortcut_grades)).setLongLabel(getString(R.string.shortcut_grades))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_grades))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_GRADES))
.build()
val shortcutHomework = ShortcutInfo.Builder(this@App, "item_homeworks")
.setShortLabel(getString(R.string.shortcut_homework)).setLongLabel(getString(R.string.shortcut_homework))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_homework))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_HOMEWORK))
.build()
val shortcutMessages = ShortcutInfo.Builder(this@App, "item_messages")
.setShortLabel(getString(R.string.shortcut_messages)).setLongLabel(getString(R.string.shortcut_messages))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_messages))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_MESSAGES))
.build()
shortcutManager.dynamicShortcuts = listOf(
shortcutTimetable,
shortcutAgenda,
shortcutGrades,
shortcutHomework,
shortcutMessages
)
} // shortcuts - end
notificationChannelsManager.registerAllChannels()
if (config.appInstalledTime == 0L)
try {
config.appInstalledTime = packageManager.getPackageInfo(packageName, 0).firstInstallTime
config.appRateSnackbarTime = config.appInstalledTime + 7 * DAY * MS
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
}
val pushMobidziennikApp = FirebaseApp.initializeApp(
this@App,
FirebaseOptions.Builder()
.setApiKey("AIzaSyCi5LmsZ5BBCQnGtrdvWnp1bWLCNP8OWQE")
.setApplicationId("1:747285019373:android:f6341bf7b158621d")
.build(),
"Mobidziennik2"
)
val pushLibrusApp = FirebaseApp.initializeApp(
this@App,
FirebaseOptions.Builder()
.setApiKey("AIzaSyDfTuEoYPKdv4aceEws1CO3n0-HvTndz-o")
.setApplicationId("1:513056078587:android:1e29083b760af544")
.build(),
"Librus"
)
val pushVulcanApp = FirebaseApp.initializeApp(
this@App,
FirebaseOptions.Builder()
.setApiKey("AIzaSyDW8MUtanHy64_I0oCpY6cOxB3jrvJd_iA")
.setApplicationId("1:987828170337:android:ac97431a0a4578c3")
.build(),
"Vulcan"
)
try {
FirebaseInstanceId.getInstance().instanceId.addOnSuccessListener { instanceIdResult ->
val token = instanceIdResult.token
config.sync.tokenApp = token
}
FirebaseInstanceId.getInstance(pushMobidziennikApp).instanceId.addOnSuccessListener { instanceIdResult ->
val token = instanceIdResult.token
if (token != config.sync.tokenMobidziennik) {
config.sync.tokenMobidziennik = token
config.sync.tokenMobidziennikList = listOf()
}
}
FirebaseInstanceId.getInstance(pushLibrusApp).instanceId.addOnSuccessListener { instanceIdResult ->
val token = instanceIdResult.token
if (token != config.sync.tokenLibrus) {
config.sync.tokenLibrus = token
config.sync.tokenLibrusList = listOf()
}
}
FirebaseInstanceId.getInstance(pushVulcanApp).instanceId.addOnSuccessListener { instanceIdResult ->
val token = instanceIdResult.token
if (token != config.sync.tokenVulcan) {
config.sync.tokenVulcan = token
config.sync.tokenVulcanList = listOf()
}
}
FirebaseMessaging.getInstance().subscribeToTopic(packageName)
} catch (e: IllegalStateException) {
e.printStackTrace()
}
} }
db.metadataDao().countUnseen().observeForever { count: Int -> db.metadataDao().countUnseen().observeForever { count: Int ->
if (unreadBadgesAvailable) if (unreadBadgesAvailable)
unreadBadgesAvailable = ShortcutBadger.applyCount(this@App, count) unreadBadgesAvailable = ShortcutBadger.applyCount(this@App, count)
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
val shortcutManager = getSystemService(ShortcutManager::class.java)
val shortcutTimetable = ShortcutInfo.Builder(this@App, "item_timetable")
.setShortLabel(getString(R.string.shortcut_timetable)).setLongLabel(getString(R.string.shortcut_timetable))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_timetable))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_TIMETABLE))
.build()
val shortcutAgenda = ShortcutInfo.Builder(this@App, "item_agenda")
.setShortLabel(getString(R.string.shortcut_agenda)).setLongLabel(getString(R.string.shortcut_agenda))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_agenda))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_AGENDA))
.build()
val shortcutGrades = ShortcutInfo.Builder(this@App, "item_grades")
.setShortLabel(getString(R.string.shortcut_grades)).setLongLabel(getString(R.string.shortcut_grades))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_grades))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_GRADES))
.build()
val shortcutHomework = ShortcutInfo.Builder(this@App, "item_homeworks")
.setShortLabel(getString(R.string.shortcut_homework)).setLongLabel(getString(R.string.shortcut_homework))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_homework))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_HOMEWORK))
.build()
val shortcutMessages = ShortcutInfo.Builder(this@App, "item_messages")
.setShortLabel(getString(R.string.shortcut_messages)).setLongLabel(getString(R.string.shortcut_messages))
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_messages))
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_MESSAGES))
.build()
shortcutManager.dynamicShortcuts = listOf(
shortcutTimetable,
shortcutAgenda,
shortcutGrades,
shortcutHomework,
shortcutMessages
)
} // shortcuts - end
if (config.appInstalledTime == 0L)
try {
config.appInstalledTime = packageManager.getPackageInfo(packageName, 0).firstInstallTime
config.appRateSnackbarTime = config.appInstalledTime + 7*DAY*MS
} catch (e: NameNotFoundException) {
e.printStackTrace()
}
val pushMobidziennikApp = FirebaseApp.initializeApp(
this@App,
FirebaseOptions.Builder()
.setApiKey("AIzaSyCi5LmsZ5BBCQnGtrdvWnp1bWLCNP8OWQE")
.setApplicationId("1:747285019373:android:f6341bf7b158621d")
.build(),
"Mobidziennik2"
)
val pushLibrusApp = FirebaseApp.initializeApp(
this@App,
FirebaseOptions.Builder()
.setApiKey("AIzaSyDfTuEoYPKdv4aceEws1CO3n0-HvTndz-o")
.setApplicationId("1:513056078587:android:1e29083b760af544")
.build(),
"Librus"
)
val pushVulcanApp = FirebaseApp.initializeApp(
this@App,
FirebaseOptions.Builder()
.setApiKey("AIzaSyDW8MUtanHy64_I0oCpY6cOxB3jrvJd_iA")
.setApplicationId("1:987828170337:android:ac97431a0a4578c3")
.build(),
"Vulcan"
)
try {
FirebaseInstanceId.getInstance().instanceId.addOnSuccessListener { instanceIdResult ->
val token = instanceIdResult.token
config.sync.tokenApp = token
}
FirebaseInstanceId.getInstance(pushMobidziennikApp).instanceId.addOnSuccessListener { instanceIdResult ->
val token = instanceIdResult.token
if (token != config.sync.tokenMobidziennik) {
config.sync.tokenMobidziennik = token
config.sync.tokenMobidziennikList = listOf()
}
}
FirebaseInstanceId.getInstance(pushLibrusApp).instanceId.addOnSuccessListener { instanceIdResult ->
val token = instanceIdResult.token
if (token != config.sync.tokenLibrus) {
config.sync.tokenLibrus = token
config.sync.tokenLibrusList = listOf()
}
}
FirebaseInstanceId.getInstance(pushVulcanApp).instanceId.addOnSuccessListener { instanceIdResult ->
val token = instanceIdResult.token
if (token != config.sync.tokenVulcan) {
config.sync.tokenVulcan = token
config.sync.tokenVulcanList = listOf()
}
}
FirebaseMessaging.getInstance().subscribeToTopic(packageName)
} catch (e: IllegalStateException) {
e.printStackTrace()
}
}}
}
private fun profileLoad(profileId: Int) {
db.profileDao().getFullByIdNow(profileId)?.also {
profile = it
} ?: run {
if (!::profile.isInitialized) {
profile = ProfileFull(-1, "", "", -1)
}
} }
} }
fun profileLoad(profileId: Int, onSuccess: (profile: ProfileFull) -> Unit) {
private fun profileLoadById(profileId: Int): Boolean {
db.profileDao().getByIdNow(profileId)?.also {
App.profile = it
App.config.lastProfileId = it.id
return true
}
return false
}
fun profileLoad(profileId: Int, onSuccess: (profile: Profile) -> Unit) {
launch { launch {
val deferred = async(Dispatchers.Default) { val success = withContext(Dispatchers.Default) {
profileLoad(profileId) profileLoadById(profileId)
} }
deferred.await() if (success)
onSuccess(profile) onSuccess(profile)
else
profileLoadLast(onSuccess)
} }
} }
fun profileLoadLast(onSuccess: (profile: Profile) -> Unit) {
private fun OkHttpClient.Builder.installHttpsSupport() { launch {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) { val success = withContext(Dispatchers.Default) {
try { profileLoadById(db.profileDao().lastId ?: return@withContext false)
try {
ProviderInstaller.installIfNeeded(this@App)
} catch (e: Exception) {
Log.e("OkHttpTLSCompat", "Play Services not found or outdated")
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustManagerFactory.init(null as KeyStore?)
val x509TrustManager = trustManagerFactory.trustManagers.singleOrNull { it is X509TrustManager } as X509TrustManager?
?: return
val sc = SSLContext.getInstance("TLSv1.2")
sc.init(null, null, null)
sslSocketFactory(TLSSocketFactory(sc.socketFactory), x509TrustManager)
val cs: ConnectionSpec = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_0)
.tlsVersions(TlsVersion.TLS_1_1)
.tlsVersions(TlsVersion.TLS_1_2)
.build()
val specs: MutableList<ConnectionSpec> = ArrayList()
specs.add(cs)
specs.add(ConnectionSpec.COMPATIBLE_TLS)
specs.add(ConnectionSpec.CLEARTEXT)
connectionSpecs(specs)
}
} catch (exc: Exception) {
Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc)
} }
if (!success) {
EventBus.getDefault().post(ProfileListEmptyEvent())
}
}
}
fun profileSave() = profileSave(profile)
fun profileSave(profile: Profile) {
launch(Dispatchers.Default) {
App.db.profileDao().add(profile)
} }
} }
@ -315,5 +357,5 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
e.printStackTrace() e.printStackTrace()
false false
} }
}*/ }
} }

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,13 +2,17 @@ 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.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.content.res.Resources 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
@ -22,8 +26,10 @@ 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.view.WindowManager
import android.widget.CheckBox import android.widget.CheckBox
import android.widget.CompoundButton import android.widget.CompoundButton
import android.widget.RadioButton
import android.widget.TextView import android.widget.TextView
import androidx.annotation.* import androidx.annotation.*
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
@ -34,30 +40,49 @@ 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 com.google.android.gms.security.ProviderInstaller
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
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import okhttp3.ConnectionSpec
import okhttp3.OkHttpClient
import okhttp3.RequestBody import okhttp3.RequestBody
import okhttp3.TlsVersion
import okio.Buffer import okio.Buffer
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.response.ApiResponse
import pl.szczodrzynski.edziennik.data.db.entity.Notification import pl.szczodrzynski.edziennik.data.db.entity.Notification
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.entity.Team import pl.szczodrzynski.edziennik.data.db.entity.Team
import pl.szczodrzynski.edziennik.network.TLSSocketFactory
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
import java.io.InterruptedIOException
import java.io.PrintWriter import java.io.PrintWriter
import java.io.StringWriter import java.io.StringWriter
import java.math.BigInteger import java.math.BigInteger
import java.net.ConnectException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import java.nio.charset.Charset import java.nio.charset.Charset
import java.security.KeyStore
import java.security.MessageDigest import java.security.MessageDigest
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import java.util.zip.CRC32 import java.util.zip.CRC32
import javax.crypto.Mac import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec import javax.crypto.spec.SecretKeySpec
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLException
import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509TrustManager
import kotlin.Pair import kotlin.Pair
@ -75,8 +100,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
@ -84,8 +109,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)
@ -105,6 +141,10 @@ fun CharSequence?.isNotNullNorEmpty(): Boolean {
return this != null && this.isNotEmpty() return this != null && this.isNotEmpty()
} }
fun CharSequence?.isNotNullNorBlank(): Boolean {
return this != null && this.isNotBlank()
}
fun currentTimeUnix() = System.currentTimeMillis() / 1000 fun currentTimeUnix() = System.currentTimeMillis() / 1000
fun Bundle?.getInt(key: String, defaultValue: Int): Int { fun Bundle?.getInt(key: String, defaultValue: Int): Int {
@ -638,6 +678,34 @@ fun Bundle(vararg properties: Pair<String, Any?>): Bundle {
} }
} }
} }
fun Intent(action: String? = null, vararg properties: Pair<String, Any?>): Intent {
return Intent(action).putExtras(Bundle(*properties))
}
fun Intent(packageContext: Context, cls: Class<*>, vararg properties: Pair<String, Any?>): Intent {
return Intent(packageContext, cls).putExtras(Bundle(*properties))
}
fun Bundle.toJsonObject(): JsonObject {
val json = JsonObject()
keySet()?.forEach { key ->
get(key)?.let {
when (it) {
is String -> json.addProperty(key, it)
is Char -> json.addProperty(key, it)
is Int -> json.addProperty(key, it)
is Long -> json.addProperty(key, it)
is Float -> json.addProperty(key, it)
is Short -> json.addProperty(key, it)
is Double -> json.addProperty(key, it)
is Boolean -> json.addProperty(key, it)
is Bundle -> json.add(key, it.toJsonObject())
else -> json.addProperty(key, it.toString())
}
}
}
return json
}
fun Intent.toJsonObject() = extras?.toJsonObject()
fun JsonArray?.isNullOrEmpty(): Boolean = (this?.size() ?: 0) == 0 fun JsonArray?.isNullOrEmpty(): Boolean = (this?.size() ?: 0) == 0
fun JsonArray.isEmpty(): Boolean = this.size() == 0 fun JsonArray.isEmpty(): Boolean = this.size() == 0
@ -930,6 +998,8 @@ fun Context.getNotificationTitle(type: Int): String {
Notification.TYPE_NEW_EVENT -> R.string.notification_type_new_event Notification.TYPE_NEW_EVENT -> R.string.notification_type_new_event
Notification.TYPE_NEW_HOMEWORK -> R.string.notification_type_new_homework Notification.TYPE_NEW_HOMEWORK -> R.string.notification_type_new_homework
Notification.TYPE_NEW_SHARED_EVENT -> R.string.notification_type_new_shared_event Notification.TYPE_NEW_SHARED_EVENT -> R.string.notification_type_new_shared_event
Notification.TYPE_NEW_SHARED_HOMEWORK -> R.string.notification_type_new_shared_homework
Notification.TYPE_REMOVED_SHARED_EVENT -> R.string.notification_type_removed_shared_event
Notification.TYPE_NEW_MESSAGE -> R.string.notification_type_new_message Notification.TYPE_NEW_MESSAGE -> R.string.notification_type_new_message
Notification.TYPE_NEW_NOTICE -> R.string.notification_type_notice Notification.TYPE_NEW_NOTICE -> R.string.notification_type_notice
Notification.TYPE_NEW_ATTENDANCE -> R.string.notification_type_attendance Notification.TYPE_NEW_ATTENDANCE -> R.string.notification_type_attendance
@ -946,3 +1016,153 @@ fun Context.getNotificationTitle(type: Int): String {
fun Cursor?.getString(columnName: String) = this?.getStringOrNull(getColumnIndex(columnName)) fun Cursor?.getString(columnName: String) = this?.getStringOrNull(getColumnIndex(columnName))
fun Cursor?.getInt(columnName: String) = this?.getIntOrNull(getColumnIndex(columnName)) fun Cursor?.getInt(columnName: String) = this?.getIntOrNull(getColumnIndex(columnName))
fun Cursor?.getLong(columnName: String) = this?.getLongOrNull(getColumnIndex(columnName)) fun Cursor?.getLong(columnName: String) = this?.getLongOrNull(getColumnIndex(columnName))
fun OkHttpClient.Builder.installHttpsSupport(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
try {
try {
ProviderInstaller.installIfNeeded(context)
} catch (e: Exception) {
Log.e("OkHttpTLSCompat", "Play Services not found or outdated")
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustManagerFactory.init(null as KeyStore?)
val x509TrustManager = trustManagerFactory.trustManagers.singleOrNull { it is X509TrustManager } as X509TrustManager?
?: return
val sc = SSLContext.getInstance("TLSv1.2")
sc.init(null, null, null)
sslSocketFactory(TLSSocketFactory(sc.socketFactory), x509TrustManager)
val cs: ConnectionSpec = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_0)
.tlsVersions(TlsVersion.TLS_1_1)
.tlsVersions(TlsVersion.TLS_1_2)
.build()
val specs: MutableList<ConnectionSpec> = ArrayList()
specs.add(cs)
specs.add(ConnectionSpec.COMPATIBLE_TLS)
specs.add(ConnectionSpec.CLEARTEXT)
connectionSpecs(specs)
}
} catch (exc: Exception) {
Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc)
}
}
}
fun CharSequence.containsAll(list: List<CharSequence>, ignoreCase: Boolean = false): Boolean {
for (i in list) {
if (!contains(i, ignoreCase))
return false
}
return true
}
inline fun RadioButton.setOnSelectedListener(crossinline listener: (buttonView: CompoundButton) -> Unit)
= setOnCheckedChangeListener { buttonView, isChecked -> if (isChecked) listener(buttonView) }
fun Response.toErrorCode() = when (this.code()) {
400 -> ERROR_REQUEST_HTTP_400
401 -> ERROR_REQUEST_HTTP_401
403 -> ERROR_REQUEST_HTTP_403
404 -> ERROR_REQUEST_HTTP_404
405 -> ERROR_REQUEST_HTTP_405
410 -> ERROR_REQUEST_HTTP_410
424 -> ERROR_REQUEST_HTTP_424
500 -> ERROR_REQUEST_HTTP_500
503 -> ERROR_REQUEST_HTTP_503
else -> null
}
fun Throwable.toErrorCode() = when (this) {
is UnknownHostException -> ERROR_REQUEST_FAILURE_HOSTNAME_NOT_FOUND
is SSLException -> ERROR_REQUEST_FAILURE_SSL_ERROR
is SocketTimeoutException -> ERROR_REQUEST_FAILURE_TIMEOUT
is InterruptedIOException, is ConnectException -> ERROR_REQUEST_FAILURE_NO_INTERNET
is SzkolnyApiException -> this.error?.toErrorCode()
else -> null
}
private fun ApiResponse.Error.toErrorCode() = when (this.code) {
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
}

View File

@ -1,6 +1,5 @@
package pl.szczodrzynski.edziennik package pl.szczodrzynski.edziennik
import android.app.Activity
import android.app.ActivityManager import android.app.ActivityManager
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
@ -9,12 +8,10 @@ import android.content.IntentFilter
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.BitmapDrawable
import android.os.AsyncTask
import android.os.Build 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
@ -34,21 +31,25 @@ 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 org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
import pl.droidsonroids.gif.GifDrawable import pl.droidsonroids.gif.GifDrawable
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.events.* import pl.szczodrzynski.edziennik.data.api.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.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask
import pl.szczodrzynski.edziennik.data.db.entity.Metadata.* import pl.szczodrzynski.edziennik.data.db.entity.Metadata.*
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.ui.dialogs.ServerMessageDialog
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
@ -89,13 +90,13 @@ 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.*
import kotlin.coroutines.CoroutineContext
import kotlin.math.roundToInt import kotlin.math.roundToInt
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity(), CoroutineScope {
companion object { companion object {
var useOldMessages = false var useOldMessages = false
@ -108,6 +109,7 @@ class MainActivity : AppCompatActivity() {
const val DRAWER_PROFILE_SYNC_ALL = 201 const val DRAWER_PROFILE_SYNC_ALL = 201
const val DRAWER_PROFILE_EXPORT_DATA = 202 const val DRAWER_PROFILE_EXPORT_DATA = 202
const val DRAWER_PROFILE_MANAGE = 203 const val DRAWER_PROFILE_MANAGE = 203
const val DRAWER_PROFILE_MARK_ALL_AS_READ = 204
const val DRAWER_ITEM_HOME = 1 const val DRAWER_ITEM_HOME = 1
const val DRAWER_ITEM_TIMETABLE = 11 const val DRAWER_ITEM_TIMETABLE = 11
const val DRAWER_ITEM_AGENDA = 12 const val DRAWER_ITEM_AGENDA = 12
@ -208,6 +210,10 @@ class MainActivity : AppCompatActivity() {
.withDescription(R.string.drawer_manage_profiles_desc) .withDescription(R.string.drawer_manage_profiles_desc)
.isInProfileList(false) .isInProfileList(false)
list += NavTarget(DRAWER_PROFILE_MARK_ALL_AS_READ, R.string.menu_mark_everything_as_read, null)
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
.isInProfileList(true)
list += NavTarget(DRAWER_PROFILE_SYNC_ALL, R.string.menu_sync_all, null) list += NavTarget(DRAWER_PROFILE_SYNC_ALL, R.string.menu_sync_all, null)
.withIcon(CommunityMaterial.Icon.cmd_download_outline) .withIcon(CommunityMaterial.Icon.cmd_download_outline)
.isInProfileList(true) .isInProfileList(true)
@ -226,6 +232,10 @@ class MainActivity : AppCompatActivity() {
} }
} }
private var job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
val b: ActivitySzkolnyBinding by lazy { ActivitySzkolnyBinding.inflate(layoutInflater) } val b: ActivitySzkolnyBinding by lazy { ActivitySzkolnyBinding.inflate(layoutInflater) }
val navView: NavView by lazy { b.navView } val navView: NavView by lazy { b.navView }
val drawer: NavDrawer by lazy { navView.drawer } val drawer: NavDrawer by lazy { navView.drawer }
@ -257,15 +267,22 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
d(TAG, "Activity created")
setTheme(Themes.appTheme) setTheme(Themes.appTheme)
app.config.ui.language?.let { app.config.ui.language?.let {
setLanguage(it) setLanguage(it)
} }
setContentView(b.root) if (App.profileId == 0) {
onProfileListEmptyEvent(ProfileListEmptyEvent())
return
}
Log.d(TAG, Signing.appPassword) d(TAG, "Profile is valid, inflating views")
setContentView(b.root)
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)
@ -332,8 +349,7 @@ class MainActivity : AppCompatActivity() {
setAccountHeaderBackground(app.config.ui.headerBackground) setAccountHeaderBackground(app.config.ui.headerBackground)
drawerProfileListEmptyListener = { drawerProfileListEmptyListener = {
app.config.loginFinished = false onProfileListEmptyEvent(ProfileListEmptyEvent())
profileListEmptyListener()
} }
drawerItemSelectedListener = { id, position, drawerItem -> drawerItemSelectedListener = { id, position, drawerItem ->
loadTarget(id) loadTarget(id)
@ -344,7 +360,7 @@ class MainActivity : AppCompatActivity() {
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
} }
@ -362,30 +378,19 @@ class MainActivity : AppCompatActivity() {
navTarget = navTargetList[0] navTarget = navTargetList[0]
var profileListEmpty = drawer.profileListEmpty
if (savedInstanceState != null) { if (savedInstanceState != null) {
intent?.putExtras(savedInstanceState) intent?.putExtras(savedInstanceState)
savedInstanceState.clear() savedInstanceState.clear()
} }
if (!profileListEmpty) {
handleIntent(intent?.extras)
}
app.db.profileDao().all.observe(this, Observer { profiles -> app.db.profileDao().all.observe(this, Observer { profiles ->
drawer.setProfileList(profiles.filter { it.id >= 0 }.toMutableList()) drawer.setProfileList(profiles.filter { it.id >= 0 }.toMutableList())
if (profileListEmpty) { drawer.currentProfile = App.profileId
profileListEmpty = false
handleIntent(intent?.extras)
}
else if (app.profile != null) {
drawer.currentProfile = app.profile.id
}
}) })
// if null, getAllFull will load a profile and update drawerItems setDrawerItems()
if (app.profile != null)
setDrawerItems() handleIntent(intent?.extras)
app.db.metadataDao().unreadCounts.observe(this, Observer { unreadCounters -> app.db.metadataDao().unreadCounts.observe(this, Observer { unreadCounters ->
unreadCounters.map { unreadCounters.map {
@ -405,6 +410,7 @@ class MainActivity : AppCompatActivity() {
isStoragePermissionGranted() isStoragePermissionGranted()
SyncWorker.scheduleNext(app) SyncWorker.scheduleNext(app)
UpdateWorker.scheduleNext(app)
// APP BACKGROUND // APP BACKGROUND
if (app.config.ui.appBackground != null) { if (app.config.ui.appBackground != null) {
@ -438,6 +444,8 @@ class MainActivity : AppCompatActivity() {
// 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);
@ -509,17 +517,25 @@ class MainActivity : AppCompatActivity() {
} }
} }
var profileListEmptyListener = {
startActivityForResult(Intent(this, LoginActivity::class.java), REQUEST_LOGIN_ACTIVITY)
}
private var profileSettingClickListener = { id: Int, view: View? -> private var profileSettingClickListener = { id: Int, view: View? ->
when (id) { when (id) {
DRAWER_PROFILE_ADD_NEW -> { DRAWER_PROFILE_ADD_NEW -> {
profileListEmptyListener() startActivityForResult(Intent(this, LoginActivity::class.java), REQUEST_LOGIN_ACTIVITY)
} }
DRAWER_PROFILE_SYNC_ALL -> { DRAWER_PROFILE_SYNC_ALL -> {
EdziennikTask.sync().enqueue(this) EdziennikTask.sync().enqueue(this)
} }
DRAWER_PROFILE_MARK_ALL_AS_READ -> { launch {
withContext(Dispatchers.Default) {
app.db.profileDao().allNow.forEach { profile ->
if (profile.loginStoreType != LoginStore.LOGIN_TYPE_LIBRUS)
app.db.metadataDao().setAllSeenExceptMessagesAndAnnouncements(profile.id, true)
else
app.db.metadataDao().setAllSeenExceptMessages(profile.id, true)
}
}
Toast.makeText(this@MainActivity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show()
}}
else -> { else -> {
loadTarget(id) loadTarget(id)
} }
@ -549,7 +565,7 @@ class MainActivity : AppCompatActivity() {
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) @Subscribe(threadMode = ThreadMode.MAIN)
@ -564,6 +580,13 @@ class MainActivity : AppCompatActivity() {
} }
} }
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)
fun onProfileListEmptyEvent(event: ProfileListEmptyEvent) {
d(TAG, "Profile list is empty. Launch LoginActivity.")
app.config.loginFinished = false
startActivity(Intent(this, LoginActivity::class.java))
finish()
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onApiTaskProgressEvent(event: ApiTaskProgressEvent) { fun onApiTaskProgressEvent(event: ApiTaskProgressEvent) {
if (event.profileId == App.profileId) { if (event.profileId == App.profileId) {
navView.toolbar.apply { navView.toolbar.apply {
@ -579,6 +602,7 @@ class MainActivity : AppCompatActivity() {
} }
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true) @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onApiTaskFinishedEvent(event: ApiTaskFinishedEvent) { fun onApiTaskFinishedEvent(event: ApiTaskFinishedEvent) {
EventBus.getDefault().removeStickyEvent(event)
if (event.profileId == App.profileId) { if (event.profileId == App.profileId) {
navView.toolbar.apply { navView.toolbar.apply {
subtitleFormat = R.string.toolbar_subtitle subtitleFormat = R.string.toolbar_subtitle
@ -589,6 +613,7 @@ class MainActivity : AppCompatActivity() {
} }
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true) @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onApiTaskAllFinishedEvent(event: ApiTaskAllFinishedEvent) { fun onApiTaskAllFinishedEvent(event: ApiTaskAllFinishedEvent) {
EventBus.getDefault().removeStickyEvent(event)
swipeRefreshLayout.isRefreshing = false swipeRefreshLayout.isRefreshing = false
} }
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true) @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
@ -604,7 +629,8 @@ class MainActivity : AppCompatActivity() {
} }
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true) @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onAppManagerDetectedEvent(event: AppManagerDetectedEvent) { fun onAppManagerDetectedEvent(event: AppManagerDetectedEvent) {
if (app.appConfig.dontShowAppManagerDialog) EventBus.getDefault().removeStickyEvent(event)
if (app.config.sync.dontShowAppManagerDialog)
return return
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setTitle(R.string.app_manager_dialog_title) .setTitle(R.string.app_manager_dialog_title)
@ -626,12 +652,15 @@ class MainActivity : AppCompatActivity() {
} }
} }
.setNeutralButton(R.string.dont_ask_again) { dialog, which -> .setNeutralButton(R.string.dont_ask_again) { dialog, which ->
app.appConfig.dontShowAppManagerDialog = true app.config.sync.dontShowAppManagerDialog = true
app.saveConfig("dontShowAppManagerDialog")
} }
.setCancelable(false) .setCancelable(false)
.show() .show()
} }
@Subscribe(threadMode = ThreadMode.MAIN)
fun onUserActionRequiredEvent(event: UserActionRequiredEvent) {
app.userActionManager.execute(this, event.profileId, event.type)
}
private fun fragmentToSyncName(currentFragment: Int): Int { private fun fragmentToSyncName(currentFragment: Int): Int {
return when (currentFragment) { return when (currentFragment) {
@ -669,26 +698,64 @@ class MainActivity : AppCompatActivity() {
} }
d(TAG, "}") d(TAG, "}")
var intentProfileId = -1
var intentTargetId = -1
if (extras?.containsKey("action") == true) {
val handled = when (extras.getString("action")) {
"serverMessage" -> {
ServerMessageDialog(
this,
extras.getString("serverMessageTitle") ?: getString(R.string.app_name),
extras.getString("serverMessageText") ?: ""
)
true
}
"feedbackMessage" -> {
intentTargetId = TARGET_FEEDBACK
false
}
"userActionRequired" -> {
app.userActionManager.execute(
this,
extras.getInt("profileId"),
extras.getInt("type")
)
true
}
"createManualEvent" -> {
val date = extras.getString("eventDate")?.let { Date.fromY_m_d(it) } ?: Date.getToday()
EventManualDialog(
this,
App.profileId,
defaultDate = date
)
true
}
else -> false
}
if (handled && !navLoading) {
return
}
}
if (extras?.containsKey("reloadProfileId") == true) { if (extras?.containsKey("reloadProfileId") == true) {
val reloadProfileId = extras.getInt("reloadProfileId", -1) val reloadProfileId = extras.getInt("reloadProfileId", -1)
extras.remove("reloadProfileId") extras.remove("reloadProfileId")
if (reloadProfileId == -1 || (app.profile != null && app.profile.id == reloadProfileId)) { if (reloadProfileId == -1 || app.profile.id == reloadProfileId) {
reloadTarget() reloadTarget()
return return
} }
} }
var intentProfileId = -1 if (extras?.getInt("profileId", -1) != -1) {
var intentTargetId = -1
if (extras?.containsKey("profileId") == true) {
intentProfileId = extras.getInt("profileId", -1) intentProfileId = extras.getInt("profileId", -1)
extras.remove("profileId") extras?.remove("profileId")
} }
if (extras?.containsKey("fragmentId") == true) { if (extras?.getInt("fragmentId", -1) != -1) {
intentTargetId = extras.getInt("fragmentId", -1) intentTargetId = extras.getInt("fragmentId", -1)
extras.remove("fragmentId") extras?.remove("fragmentId")
} }
/*if (intentTargetId == -1 && navController.currentDestination?.id == R.id.loadingFragment) { /*if (intentTargetId == -1 && navController.currentDestination?.id == R.id.loadingFragment) {
@ -702,9 +769,9 @@ class MainActivity : AppCompatActivity() {
} }
when { when {
app.profile == null || app.profile.id == -1 -> { app.profile.id == 0 -> {
if (intentProfileId == -1) if (intentProfileId == -1)
intentProfileId = app.appSharedPrefs.getInt("current_profile_id", 1) intentProfileId = app.config.lastProfileId
loadProfile(intentProfileId, intentTargetId, extras) loadProfile(intentProfileId, intentTargetId, extras)
} }
intentProfileId != -1 -> { intentProfileId != -1 -> {
@ -743,7 +810,16 @@ class MainActivity : AppCompatActivity() {
startActivity(intent) startActivity(intent)
} }
override fun onStart() {
d(TAG, "Activity started")
super.onStart()
}
override fun onStop() {
d(TAG, "Activity stopped")
super.onStop()
}
override fun onResume() { override fun onResume() {
d(TAG, "Activity resumed")
val filter = IntentFilter() val filter = IntentFilter()
filter.addAction(Intent.ACTION_MAIN) filter.addAction(Intent.ACTION_MAIN)
registerReceiver(intentReceiver, filter) registerReceiver(intentReceiver, filter)
@ -751,10 +827,15 @@ class MainActivity : AppCompatActivity() {
super.onResume() super.onResume()
} }
override fun onPause() { override fun onPause() {
d(TAG, "Activity paused")
unregisterReceiver(intentReceiver) unregisterReceiver(intentReceiver)
EventBus.getDefault().unregister(this) EventBus.getDefault().unregister(this)
super.onPause() super.onPause()
} }
override fun onDestroy() {
d(TAG, "Activity destroyed")
super.onDestroy()
}
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
@ -768,15 +849,10 @@ class MainActivity : AppCompatActivity() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_LOGIN_ACTIVITY) { if (requestCode == REQUEST_LOGIN_ACTIVITY) {
if (resultCode == Activity.RESULT_CANCELED && false) { if (!app.config.loginFinished)
finish() finish()
}
else { else {
if (!app.config.loginFinished) handleIntent(data?.extras)
finish()
else {
handleIntent(data?.extras)
}
} }
} }
} }
@ -797,34 +873,23 @@ class MainActivity : AppCompatActivity() {
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(id: Int, drawerSelection: Int, arguments: Bundle? = null) {
//d("NavDebug", "loadProfile(id = $id, drawerSelection = $drawerSelection)") if (App.profileId == id) {
if (app.profile != null && App.profileId == id) {
drawer.currentProfile = app.profile.id drawer.currentProfile = app.profile.id
loadTarget(drawerSelection, arguments) loadTarget(drawerSelection, arguments)
return return
} }
AsyncTask.execute { app.profileLoad(id) {
app.profileLoadById(id)
MessagesFragment.pageSelection = -1 MessagesFragment.pageSelection = -1
MessagesListFragment.tapPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION) MessagesListFragment.tapPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
MessagesListFragment.topPositions = 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) MessagesListFragment.bottomPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
this.runOnUiThread { setDrawerItems()
if (app.profile == null) { // the drawer profile is updated automatically when the drawer item is clicked
if (app.config.loginFinished) { // update it manually when switching profiles from other source
// this shouldn't run //if (drawer.currentProfile != app.profile.id)
profileListEmptyListener() drawer.currentProfile = app.profileId
} loadTarget(drawerSelection, arguments)
} else {
setDrawerItems()
// the drawer profile is updated automatically when the drawer item is clicked
// update it manually when switching profiles from other source
//if (drawer.currentProfile != app.profile.id)
drawer.currentProfile = app.profile.id
loadTarget(drawerSelection, arguments)
}
}
} }
} }
fun loadTarget(id: Int, arguments: Bundle? = null) { fun loadTarget(id: Int, arguments: Bundle? = null) {
@ -848,9 +913,9 @@ class MainActivity : AppCompatActivity() {
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
@ -969,7 +1034,7 @@ class MainActivity : AppCompatActivity() {
* that something has changed in the bottom sheet. * that something has changed in the bottom sheet.
*/ */
fun gainAttention() { fun gainAttention() {
if (app.config.ui.bottomSheetOpened || true) if (app.config.ui.bottomSheetOpened)
return return
b.navView.postDelayed({ b.navView.postDelayed({
navView.gainAttentionOnBottomBar() navView.gainAttentionOnBottomBar()
@ -998,7 +1063,7 @@ class MainActivity : AppCompatActivity() {
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!!)) }
@ -1018,12 +1083,11 @@ class MainActivity : AppCompatActivity() {
} }
fun setDrawerItems() { fun setDrawerItems() {
d("NavDebug", "setDrawerItems() app.profile = ${app.profile ?: "null"}") d("NavDebug", "setDrawerItems() app.profile = ${app.profile}")
val drawerItems = arrayListOf<IDrawerItem<*>>() val drawerItems = arrayListOf<IDrawerItem<*>>()
val drawerProfiles = arrayListOf<ProfileSettingDrawerItem>() val drawerProfiles = arrayListOf<ProfileSettingDrawerItem>()
val supportedFragments = if (app.profile == null) arrayListOf<Int>() val supportedFragments = app.profile.supportedFragments
else app.profile.supportedFragments
targetPopToHomeList.clear() targetPopToHomeList.clear()
@ -1063,7 +1127,7 @@ class MainActivity : AppCompatActivity() {
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)
@ -1076,7 +1140,7 @@ class MainActivity : AppCompatActivity() {
} }
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
} }
@ -1087,7 +1151,8 @@ class MainActivity : AppCompatActivity() {
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.getConfig().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

@ -1,350 +0,0 @@
package pl.szczodrzynski.edziennik;
import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import pl.szczodrzynski.edziennik.receivers.BootReceiver;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.models.Time;
import static androidx.core.app.NotificationCompat.PRIORITY_DEFAULT;
import static androidx.core.app.NotificationCompat.PRIORITY_MAX;
public class Notifier {
private static final String TAG = "Notifier";
public static final int ID_GET_DATA = 1337000;
public static final int ID_GET_DATA_ERROR = 1337001;
private static String CHANNEL_GET_DATA_NAME;
private static String CHANNEL_GET_DATA_DESC;
private static final String GROUP_KEY_GET_DATA = "pl.szczodrzynski.edziennik.GET_DATA";
public static final int ID_NOTIFICATIONS = 1337002;
public static String CHANNEL_NOTIFICATIONS_NAME;
public static String CHANNEL_NOTIFICATIONS_DESC;
public static final String GROUP_KEY_NOTIFICATIONS = "pl.szczodrzynski.edziennik.NOTIFICATIONS";
public static final int ID_NOTIFICATIONS_QUIET = 1337002;
public static String CHANNEL_NOTIFICATIONS_QUIET_NAME;
public static String CHANNEL_NOTIFICATIONS_QUIET_DESC;
public static final String GROUP_KEY_NOTIFICATIONS_QUIET = "pl.szczodrzynski.edziennik.NOTIFICATIONS_QUIET";
private static final int ID_UPDATES = 1337003;
private static String CHANNEL_UPDATES_NAME;
private static String CHANNEL_UPDATES_DESC;
private static final String GROUP_KEY_UPDATES = "pl.szczodrzynski.edziennik.UPDATES";
private App app;
public NotificationManager notificationManager;
private NotificationCompat.Builder getDataNotificationBuilder;
public int notificationColor;
Notifier(App _app) {
this.app = _app;
CHANNEL_GET_DATA_NAME = app.getString(R.string.notification_channel_get_data_name);
CHANNEL_GET_DATA_DESC = app.getString(R.string.notification_channel_get_data_desc);
CHANNEL_NOTIFICATIONS_NAME = app.getString(R.string.notification_channel_notifications_name);
CHANNEL_NOTIFICATIONS_DESC = app.getString(R.string.notification_channel_notifications_desc);
CHANNEL_NOTIFICATIONS_QUIET_NAME = app.getString(R.string.notification_channel_notifications_quiet_name);
CHANNEL_NOTIFICATIONS_QUIET_DESC = app.getString(R.string.notification_channel_notifications_quiet_desc);
CHANNEL_UPDATES_NAME = app.getString(R.string.notification_channel_updates_name);
CHANNEL_UPDATES_DESC = app.getString(R.string.notification_channel_updates_desc);
notificationColor = ContextCompat.getColor(app.getContext(), R.color.colorPrimary);
notificationManager = (NotificationManager) app.getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channelGetData = new NotificationChannel(GROUP_KEY_GET_DATA, CHANNEL_GET_DATA_NAME, NotificationManager.IMPORTANCE_MIN);
channelGetData.setDescription(CHANNEL_GET_DATA_DESC);
notificationManager.createNotificationChannel(channelGetData);
NotificationChannel channelNotifications = new NotificationChannel(GROUP_KEY_NOTIFICATIONS, CHANNEL_NOTIFICATIONS_NAME, NotificationManager.IMPORTANCE_HIGH);
channelNotifications.setDescription(CHANNEL_NOTIFICATIONS_DESC);
channelNotifications.enableLights(true);
channelNotifications.setLightColor(notificationColor);
notificationManager.createNotificationChannel(channelNotifications);
NotificationChannel channelNotificationsQuiet = new NotificationChannel(GROUP_KEY_NOTIFICATIONS_QUIET, CHANNEL_NOTIFICATIONS_QUIET_NAME, NotificationManager.IMPORTANCE_DEFAULT);
channelNotificationsQuiet.setDescription(CHANNEL_NOTIFICATIONS_QUIET_DESC);
channelNotificationsQuiet.setSound(null, null);
channelNotificationsQuiet.enableVibration(false);
notificationManager.createNotificationChannel(channelNotificationsQuiet);
NotificationChannel channelUpdates = new NotificationChannel(GROUP_KEY_UPDATES, CHANNEL_UPDATES_NAME, NotificationManager.IMPORTANCE_HIGH);
channelUpdates.setDescription(CHANNEL_UPDATES_DESC);
notificationManager.createNotificationChannel(channelUpdates);
}
}
public boolean shouldBeQuiet() {
long now = Time.getNow().getInMillis();
long start = app.config.getSync().getQuietHoursStart();
long end = app.config.getSync().getQuietHoursEnd();
if (start > end) {
end += 1000 * 60 * 60 * 24;
//Log.d(TAG, "Night passing");
}
if (start > now) {
now += 1000 * 60 * 60 * 24;
//Log.d(TAG, "Now is smaller");
}
//Log.d(TAG, "Start is "+start+", now is "+now+", end is "+end);
return start > 0 && now >= start && now <= end;
}
public int getNotificationDefaults() {
return (shouldBeQuiet() ? 0 : Notification.DEFAULT_ALL);
}
public String getNotificationGroup() {
return shouldBeQuiet() ? GROUP_KEY_NOTIFICATIONS_QUIET : GROUP_KEY_NOTIFICATIONS;
}
public int getNotificationPriority() {
return shouldBeQuiet() ? PRIORITY_DEFAULT : PRIORITY_MAX;
}
/* _____ _ _____ _
| __ \ | | / ____| | |
| | | | __ _| |_ __ _ | | __ ___| |_
| | | |/ _` | __/ _` | | | |_ |/ _ \ __|
| |__| | (_| | || (_| | | |__| | __/ |_
|_____/ \__,_|\__\__,_| \_____|\___|\_*/
public Notification notificationGetDataShow(int maxProgress) {
/*Intent notificationIntent = new Intent(app.getContext(), SyncService.class);
notificationIntent.setAction(ACTION_CANCEL);
PendingIntent pendingIntent = PendingIntent.getService(app.getContext(), 0,
notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);*/
getDataNotificationBuilder = new NotificationCompat.Builder(app, GROUP_KEY_GET_DATA)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setColor(notificationColor)
.setContentTitle(app.getString(R.string.notification_get_data_title))
.setContentText(app.getString(R.string.notification_get_data_text))
//.addAction(R.drawable.ic_notification, app.getString(R.string.notification_get_data_cancel), pendingIntent)
//.setGroup(GROUP_KEY_GET_DATA)
.setOngoing(true)
.setProgress(maxProgress, 0, false)
.setTicker(app.getString(R.string.notification_get_data_summary))
.setPriority(NotificationCompat.PRIORITY_LOW);
return getDataNotificationBuilder.build();
}
public Notification notificationGetDataProgress(int progress, int maxProgress) {
getDataNotificationBuilder.setProgress(maxProgress, progress, false);
return getDataNotificationBuilder.build();
}
public Notification notificationGetDataAction(int stringResId) {
getDataNotificationBuilder.setContentTitle(app.getString(R.string.sync_action_format, app.getString(stringResId)));
return getDataNotificationBuilder.build();
}
public Notification notificationGetDataProfile(String profileName) {
getDataNotificationBuilder.setContentText(profileName);
return getDataNotificationBuilder.build();
}
public Notification notificationGetDataError(String profileName, String error, int failedProfileId) {
Intent notificationIntent = new Intent(app.getContext(), Notifier.GetDataRetryService.class);
notificationIntent.putExtra("failedProfileId", failedProfileId);
PendingIntent pendingIntent = PendingIntent.getService(app.getContext(), 0,
notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
getDataNotificationBuilder.mActions.clear();
/*try {
//Use reflection clean up old actions
Field f = getDataNotificationBuilder.getClass().getDeclaredField("mActions");
f.setAccessible(true);
f.set(getDataNotificationBuilder, new ArrayList<NotificationCompat.Action>());
} catch (Exception e) {
e.printStackTrace();
}*/
getDataNotificationBuilder.setProgress(0, 0, false)
.setTicker(app.getString(R.string.notification_get_data_error_summary))
.setSmallIcon(android.R.drawable.stat_sys_warning)
.addAction(R.drawable.ic_notification, app.getString(R.string.notification_get_data_once_again), pendingIntent)
.setContentTitle(app.getString(R.string.notification_get_data_error_title, profileName))
.setContentText(error)
.setStyle(new NotificationCompat.BigTextStyle().bigText(error))
.setOngoing(false);
return getDataNotificationBuilder.build();
}
public void notificationPost(int id, Notification notification) {
notificationManager.notify(id, notification);
}
public void notificationCancel(int id) {
notificationManager.cancel(id);
}
//public void notificationGetDataHide() {
// notificationManager.cancel(ID_GET_DATA);
// }
public static class GetDataRetryService extends IntentService {
private static final String TAG = "Notifier/GetDataRetry";
public GetDataRetryService() {
super(Notifier.GetDataRetryService.class.getSimpleName());
}
@Override
protected void onHandleIntent(Intent intent) {
}
}
/* _ _ _ _ __ _ _ _
| \ | | | | (_)/ _(_) | | (_)
| \| | ___ | |_ _| |_ _ ___ __ _| |_ _ ___ _ __
| . ` |/ _ \| __| | _| |/ __/ _` | __| |/ _ \| '_ \
| |\ | (_) | |_| | | | | (_| (_| | |_| | (_) | | | |
|_| \_|\___/ \__|_|_| |_|\___\__,_|\__|_|\___/|_| |*/
public void add(pl.szczodrzynski.edziennik.utils.models.Notification notification) {
app.appConfig.notifications.add(notification);
}
public void postAll() {
Collections.sort(app.appConfig.notifications, (o1, o2) -> (o2.addedDate - o1.addedDate > 0) ? 1 : (o2.addedDate - o1.addedDate < 0) ? -1 : 0);
if (app.appConfig.notifications.size() > 40) {
app.appConfig.notifications.subList(40, app.appConfig.notifications.size() - 1).clear();
}
int unreadCount = 0;
List<pl.szczodrzynski.edziennik.utils.models.Notification> notificationList = new ArrayList<>();
for (pl.szczodrzynski.edziennik.utils.models.Notification notification: app.appConfig.notifications) {
if (!notification.notified) {
notification.seen = false;
notification.notified = true;
unreadCount++;
if (notificationList.size() < 10) {
notificationList.add(notification);
}
}
else {
notification.seen = true;
}
}
for (pl.szczodrzynski.edziennik.utils.models.Notification notification: notificationList) {
Intent intent = new Intent(app, MainActivity.class);
notification.fillIntent(intent);
PendingIntent pendingIntent = PendingIntent.getActivity(app, notification.id, intent, 0);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(app, getNotificationGroup())
// title, text, type, date
.setContentTitle(notification.title)
.setContentText(notification.text)
.setSubText(pl.szczodrzynski.edziennik.utils.models.Notification.stringType(app, notification.type))
.setWhen(notification.addedDate)
.setTicker(app.getString(R.string.notification_ticker_format, pl.szczodrzynski.edziennik.utils.models.Notification.stringType(app, notification.type)))
// icon, color, lights, priority
.setSmallIcon(R.drawable.ic_notification)
.setColor(notificationColor)
.setLights(0xFF00FFFF, 2000, 2000)
.setPriority(getNotificationPriority())
// channel, group, style
.setChannelId(getNotificationGroup())
.setGroup(getNotificationGroup())
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
.setStyle(new NotificationCompat.BigTextStyle().bigText(notification.text))
// intent, auto cancel
.setContentIntent(pendingIntent)
.setAutoCancel(true);
if (!shouldBeQuiet()) {
notificationBuilder.setDefaults(getNotificationDefaults());
}
notificationManager.notify(notification.id, notificationBuilder.build());
}
if (notificationList.size() > 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Intent intent = new Intent(app, MainActivity.class);
intent.setAction("android.intent.action.MAIN");
intent.putExtra("fragmentId", MainActivity.DRAWER_ITEM_NOTIFICATIONS);
PendingIntent pendingIntent = PendingIntent.getActivity(app, ID_NOTIFICATIONS,
intent, 0);
NotificationCompat.Builder groupBuilder =
new NotificationCompat.Builder(app, getNotificationGroup())
.setSmallIcon(R.drawable.ic_notification)
.setColor(notificationColor)
.setContentTitle(app.getString(R.string.notification_new_notification_title_format, unreadCount))
.setGroupSummary(true)
.setAutoCancel(true)
.setChannelId(getNotificationGroup())
.setGroup(getNotificationGroup())
.setLights(0xFF00FFFF, 2000, 2000)
.setPriority(getNotificationPriority())
.setContentIntent(pendingIntent)
.setStyle(new NotificationCompat.BigTextStyle());
if (!shouldBeQuiet()) {
groupBuilder.setDefaults(getNotificationDefaults());
}
notificationManager.notify(ID_NOTIFICATIONS, groupBuilder.build());
}
}
/* _ _ _ _
| | | | | | | |
| | | |_ __ __| | __ _| |_ ___ ___
| | | | '_ \ / _` |/ _` | __/ _ \/ __|
| |__| | |_) | (_| | (_| | || __/\__ \
\____/| .__/ \__,_|\__,_|\__\___||___/
| |
|*/
public void notificationUpdatesShow(String updateVersion, String updateUrl, String updateFilename, boolean updateDirect) {
if (!app.config.getSync().getNotifyAboutUpdates())
return;
Intent notificationIntent = new Intent(app.getContext(), BootReceiver.NotificationActionService.class)
.putExtra("update_version", updateVersion)
.putExtra("update_url", updateUrl)
.putExtra("update_filename", updateFilename)
.putExtra("update_direct", updateDirect);
PendingIntent pendingIntent = PendingIntent.getService(app.getContext(), 0,
notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(app, GROUP_KEY_UPDATES)
.setSmallIcon(android.R.drawable.stat_sys_download_done)
.setColor(notificationColor)
.setContentTitle(app.getString(R.string.notification_updates_title))
.setContentText(app.getString(R.string.notification_updates_text, updateVersion))
.setLights(0xFF00FFFF, 2000, 2000)
.setContentIntent(pendingIntent)
.setTicker(app.getString(R.string.notification_updates_summary))
.setPriority(PRIORITY_MAX)
.setAutoCancel(true);
if (!shouldBeQuiet()) {
notificationBuilder.setDefaults(getNotificationDefaults());
}
notificationManager.notify(ID_UPDATES, notificationBuilder.build());
}
public void notificationUpdatesHide() {
if (!app.config.getSync().getNotifyAboutUpdates())
return;
notificationManager.cancel(ID_UPDATES);
}
public void dump() {
for (pl.szczodrzynski.edziennik.utils.models.Notification notification: app.appConfig.notifications) {
Log.d(TAG, "Profile"+notification.profileId+" Notification from "+ Date.fromMillis(notification.addedDate).getFormattedString()+" "+ Time.fromMillis(notification.addedDate).getStringHMS()+" - "+notification.text);
}
}
}

View File

@ -16,12 +16,13 @@ import pl.szczodrzynski.edziennik.config.utils.ConfigMigration
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.config.utils.toHashMap import pl.szczodrzynski.edziennik.config.utils.toHashMap
import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update
import pl.szczodrzynski.edziennik.data.db.AppDb import pl.szczodrzynski.edziennik.data.db.AppDb
import kotlin.coroutines.CoroutineContext 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 = 2 const val DATA_VERSION = 11
} }
private val job = Job() private val job = Job()
@ -40,6 +41,25 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
get() { mDataVersion = mDataVersion ?: values.get("dataVersion", 0); return mDataVersion ?: 0 } get() { mDataVersion = mDataVersion ?: values.get("dataVersion", 0); return mDataVersion ?: 0 }
set(value) { set("dataVersion", value); mDataVersion = value } set(value) { set("dataVersion", value); mDataVersion = value }
private var mHash: String? = null
var hash: String
get() { mHash = mHash ?: values.get("hash", ""); return mHash ?: "" }
set(value) { set("hash", value); mHash = value }
private var mLastProfileId: Int? = null
var lastProfileId: Int
get() { mLastProfileId = mLastProfileId ?: values.get("lastProfileId", 0); return mLastProfileId ?: 0 }
set(value) { set("lastProfileId", value); mLastProfileId = value }
private var mUpdatesChannel: String? = null
var updatesChannel: String
get() { mUpdatesChannel = mUpdatesChannel ?: values.get("updatesChannel", "release"); return mUpdatesChannel ?: "release" }
set(value) { set("updatesChannel", value); mUpdatesChannel = value }
private var mUpdate: Update? = null
var update: Update?
get() { mUpdate = mUpdate ?: values.get("update", null as Update?); return mUpdate ?: null as Update? }
set(value) { set("update", value); mUpdate = value }
private var mAppVersion: Int? = null private var mAppVersion: Int? = null
var appVersion: Int var appVersion: Int
get() { mAppVersion = mAppVersion ?: values.get("appVersion", BuildConfig.VERSION_CODE); return mAppVersion ?: BuildConfig.VERSION_CODE } get() { mAppVersion = mAppVersion ?: values.get("appVersion", BuildConfig.VERSION_CODE); return mAppVersion ?: BuildConfig.VERSION_CODE }
@ -55,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 }
@ -90,11 +115,11 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
ConfigMigration(app, this) ConfigMigration(app, this)
} }
fun getFor(profileId: Int): ProfileConfig { fun getFor(profileId: Int): ProfileConfig {
return profileConfigs[profileId] ?: ProfileConfig(db, profileId, rawEntries) return profileConfigs[profileId] ?: ProfileConfig(db, profileId, db.configDao().getAllNow(profileId)).also {
} profileConfigs[profileId] = it
fun forProfile(): ProfileConfig { }
return profileConfigs[App.profileId] ?: ProfileConfig(db, App.profileId, rawEntries)
} }
fun forProfile() = getFor(App.profileId)
fun setProfile(profileId: Int) { fun setProfile(profileId: Int) {
} }

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 ?: 0 } 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

@ -7,13 +7,24 @@ 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.utils.models.Time
class ConfigSync(private val config: Config) { class ConfigSync(private val config: Config) {
private var mDontShowAppManagerDialog: Boolean? = null
var dontShowAppManagerDialog: Boolean
get() { mDontShowAppManagerDialog = mDontShowAppManagerDialog ?: config.values.get("dontShowAppManagerDialog", false); return mDontShowAppManagerDialog ?: false }
set(value) { config.set("dontShowAppManagerDialog", value); mDontShowAppManagerDialog = value }
private var mSyncEnabled: Boolean? = null private var mSyncEnabled: Boolean? = null
var enabled: Boolean var enabled: Boolean
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 }
@ -29,20 +40,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

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,16 +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 mAgendaViewType: Int? = null
var agendaViewType: Int
get() { mAgendaViewType = mAgendaViewType ?: config.values.get("agendaViewType", 0); return mAgendaViewType ?: 0 }
set(value) { config.set("agendaViewType", value); mAgendaViewType = value }
private var mHomeCards: List<HomeCardModel>? = null
var homeCards: List<HomeCardModel>
get() { mHomeCards = mHomeCards ?: config.values.get("homeCards", listOf(), HomeCardModel::class.java); return mHomeCards ?: listOf() }
set(value) { config.set("homeCards", value); mHomeCards = value }
private var mSnowfall: Boolean? = null 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

@ -9,6 +9,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.config.db.ConfigEntry import pl.szczodrzynski.edziennik.config.db.ConfigEntry
import pl.szczodrzynski.edziennik.config.utils.ProfileConfigMigration
import pl.szczodrzynski.edziennik.config.utils.get import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.config.utils.toHashMap import pl.szczodrzynski.edziennik.config.utils.toHashMap
@ -27,8 +28,9 @@ class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEn
val values: HashMap<String, String?> = hashMapOf() val values: HashMap<String, String?> = hashMapOf()
val grades by lazy { ProfileConfigGrades(this) } val grades by lazy { ProfileConfigGrades(this) }
val ui by lazy { ProfileConfigUI(this) }
val sync by lazy { ProfileConfigSync(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) }*/
@ -37,10 +39,15 @@ class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEn
get() { mDataVersion = mDataVersion ?: values.get("dataVersion", 0); return mDataVersion ?: 0 } get() { mDataVersion = mDataVersion ?: values.get("dataVersion", 0); return mDataVersion ?: 0 }
set(value) { set("dataVersion", value); mDataVersion = value } set(value) { set("dataVersion", value); mDataVersion = value }
private var mHash: String? = null
var hash: String
get() { mHash = mHash ?: values.get("hash", ""); return mHash ?: "" }
set(value) { set("hash", value); mHash = value }
init { init {
rawEntries.toHashMap(profileId, values) rawEntries.toHashMap(profileId, values)
/*if (dataVersion < DATA_VERSION) if (dataVersion < DATA_VERSION)
ProfileConfigMigration(this)*/ ProfileConfigMigration(this)
} }
override fun set(key: String, value: String?) { override fun set(key: String, value: String?) {

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

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

@ -0,0 +1,22 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-1-19.
*/
package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.AGENDA_DEFAULT
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardModel
class ProfileConfigUI(private val config: ProfileConfig) {
private var mAgendaViewType: Int? = null
var agendaViewType: Int
get() { mAgendaViewType = mAgendaViewType ?: config.values.get("agendaViewType", 0); return mAgendaViewType ?: AGENDA_DEFAULT }
set(value) { config.set("agendaViewType", value); mAgendaViewType = value }
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

@ -22,4 +22,7 @@ interface ConfigDao {
@Query("SELECT * FROM config WHERE profileId = :profileId") @Query("SELECT * FROM config WHERE profileId = :profileId")
fun getAllNow(profileId: Int): List<ConfigEntry> fun getAllNow(profileId: Int): List<ConfigEntry>
@Query("DELETE FROM config WHERE profileId = :profileId")
fun clear(profileId: Int)
} }

View File

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

View File

@ -37,6 +37,9 @@ fun AbstractConfig.set(key: String, value: JsonElement?) {
fun AbstractConfig.set(key: String, value: List<Any>?) { fun AbstractConfig.set(key: String, value: List<Any>?) {
set(key, value?.let { gson.toJson(it) }) set(key, value?.let { gson.toJson(it) })
} }
fun AbstractConfig.set(key: String, value: Any?) {
set(key, value?.let { gson.toJson(it) })
}
fun AbstractConfig.setStringList(key: String, value: List<String>?) { fun AbstractConfig.setStringList(key: String, value: List<String>?) {
set(key, value?.let { gson.toJson(it) }) set(key, value?.let { gson.toJson(it) })
} }
@ -74,6 +77,9 @@ fun HashMap<String, String?>.get(key: String, default: JsonObject?): JsonObject?
fun HashMap<String, String?>.get(key: String, default: JsonArray?): JsonArray? { fun HashMap<String, String?>.get(key: String, default: JsonArray?): JsonArray? {
return this[key]?.let { JsonParser().parse(it)?.asJsonArray } ?: default return this[key]?.let { JsonParser().parse(it)?.asJsonArray } ?: default
} }
inline fun <reified T> HashMap<String, String?>.get(key: String, default: T?): T? {
return this[key]?.let { Gson().fromJson(it, T::class.java) } ?: default
}
/* !!! cannot use mutable list here - modifying it will not update the DB */ /* !!! cannot use mutable list here - modifying it will not update the DB */
fun <T> HashMap<String, String?>.get(key: String, default: List<T>?, classOfT: Class<T>): List<T>? { fun <T> HashMap<String, String?>.get(key: String, default: List<T>?, classOfT: Class<T>): List<T>? {
return this[key]?.let { ConfigGsonUtils().deserializeList<T>(gson, it, classOfT) } ?: default return this[key]?.let { ConfigGsonUtils().deserializeList<T>(gson, it, classOfT) } ?: default
@ -88,6 +94,10 @@ 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 {

View File

@ -5,32 +5,34 @@
package pl.szczodrzynski.edziennik.config.utils package pl.szczodrzynski.edziennik.config.utils
import android.content.Context import android.content.Context
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.BuildConfig import pl.szczodrzynski.edziennik.BuildConfig
import pl.szczodrzynski.edziennik.HOUR
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_LIBRUS
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_MOBIDZIENNIK
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_VULCAN
import pl.szczodrzynski.edziennik.config.Config import pl.szczodrzynski.edziennik.config.Config
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.ORDER_BY_DATE_DESC
import pl.szczodrzynski.edziennik.utils.models.Time 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 {
val p = app.getSharedPreferences("pl.szczodrzynski.edziennik_profiles", Context.MODE_PRIVATE)
val s = "app.appConfig"
if (dataVersion < 1) { val p = app.getSharedPreferences("pl.szczodrzynski.edziennik_profiles", Context.MODE_PRIVATE)
ui.theme = p.getString("$s.appTheme", null)?.toIntOrNull() ?: 1 if (p.contains("app.appConfig.appTheme")) {
sync.enabled = p.getString("$s.registerSyncEnabled", null)?.toBoolean() ?: true // migrate appConfig from app version 3.x and lower.
sync.interval = p.getString("$s.registerSyncEnabled", null)?.toIntOrNull() ?: 3600 // Updates dataVersion to level 2.
val oldButtons = p.getString("$s.miniDrawerButtonIds", null)?.let { str -> AppConfigMigrationV3(p, config)
str.replace("[\\[\\]]*".toRegex(), "") }
.split(",\\s?".toRegex())
.mapNotNull { it.toIntOrNull() } if (dataVersion < 2) {
} appVersion = BuildConfig.VERSION_CODE
ui.miniMenuButtons = oldButtons ?: listOf( loginFinished = false
ui.language = null
ui.theme = 1
ui.appBackground = null
ui.headerBackground = null
ui.miniMenuVisible = false
ui.miniMenuButtons = listOf(
MainActivity.DRAWER_ITEM_HOME, MainActivity.DRAWER_ITEM_HOME,
MainActivity.DRAWER_ITEM_TIMETABLE, MainActivity.DRAWER_ITEM_TIMETABLE,
MainActivity.DRAWER_ITEM_AGENDA, MainActivity.DRAWER_ITEM_AGENDA,
@ -39,46 +41,56 @@ class ConfigMigration(app: App, config: Config) {
MainActivity.DRAWER_ITEM_HOMEWORK, MainActivity.DRAWER_ITEM_HOMEWORK,
MainActivity.DRAWER_ITEM_SETTINGS MainActivity.DRAWER_ITEM_SETTINGS
) )
dataVersion = 1 sync.enabled = true
} sync.interval = 1*HOUR.toInt()
if (dataVersion < 2) { sync.notifyAboutUpdates = true
devModePassword = p.getString("$s.devModePassword", null).fix() sync.onlyWifi = false
sync.tokenApp = p.getString("$s.fcmToken", null).fix() sync.quietHoursEnabled = false
timetable.bellSyncMultiplier = p.getString("$s.bellSyncMultiplier", null)?.toIntOrNull() ?: 0 sync.quietHoursStart = null
sync.quietHoursStart = p.getString("$s.quietHoursStart", null)?.toLongOrNull() ?: 0 sync.quietHoursEnd = null
appRateSnackbarTime = p.getString("$s.appRateSnackbarTime", null)?.toLongOrNull() ?: 0 sync.quietDuringLessons = false
sync.quietHoursEnd = p.getString("$s.quietHoursEnd", null)?.toLongOrNull() ?: 0 sync.tokenApp = null
timetable.countInSeconds = p.getString("$s.countInSeconds", null)?.toBoolean() ?: false sync.tokenMobidziennik = null
ui.headerBackground = p.getString("$s.headerBackground", null).fix()
ui.appBackground = p.getString("$s.appBackground", null).fix()
ui.language = p.getString("$s.language", null).fix()
appVersion = p.getString("$s.lastAppVersion", null)?.toIntOrNull() ?: BuildConfig.VERSION_CODE
appInstalledTime = p.getString("$s.appInstalledTime", null)?.toLongOrNull() ?: 0
grades.orderBy = p.getString("$s.gradesOrderBy", null)?.toIntOrNull() ?: 0
sync.quietDuringLessons = p.getString("$s.quietDuringLessons", null)?.toBoolean() ?: false
ui.miniMenuVisible = p.getString("$s.miniDrawerVisible", null)?.toBoolean() ?: false
loginFinished = p.getString("$s.loginFinished", null)?.toBoolean() ?: false
sync.onlyWifi = p.getString("$s.registerSyncOnlyWifi", null)?.toBoolean() ?: false
sync.notifyAboutUpdates = p.getString("$s.notifyAboutUpdates", null)?.toBoolean() ?: true
timetable.bellSyncDiff = p.getString("$s.bellSyncDiff", null)?.let { Gson().fromJson(it, Time::class.java) }
sync.tokenMobidziennikList = listOf() sync.tokenMobidziennikList = listOf()
sync.tokenVulcanList = listOf() sync.tokenLibrus = null
sync.tokenLibrusList = listOf() sync.tokenLibrusList = listOf()
val tokens = p.getString("$s.fcmTokens", null)?.let { Gson().fromJson<Map<Int, Pair<String, List<Int>>>>(it, object: TypeToken<Map<Int, Pair<String, List<Int>>>>(){}.type) } sync.tokenVulcan = null
tokens?.forEach { sync.tokenVulcanList = listOf()
val token = it.value.first timetable.bellSyncMultiplier = 0
when (it.key) { timetable.bellSyncDiff = null
LOGIN_TYPE_MOBIDZIENNIK -> sync.tokenMobidziennik = token timetable.countInSeconds = false
LOGIN_TYPE_VULCAN -> sync.tokenVulcan = token grades.orderBy = ORDER_BY_DATE_DESC
LOGIN_TYPE_LIBRUS -> sync.tokenLibrus = token
}
}
dataVersion = 2 dataVersion = 2
} }
}}
private fun String?.fix(): String? { if (dataVersion < 10) {
return this?.replace("\"", "")?.let { if (it == "null") null else it } ui.openDrawerOnBackPressed = false
} ui.snowfall = false
ui.bottomSheetOpened = false
sync.dontShowAppManagerDialog = false
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

@ -4,23 +4,21 @@
package pl.szczodrzynski.edziennik.config.utils package pl.szczodrzynski.edziennik.config.utils
import android.content.Context import pl.szczodrzynski.edziennik.config.ProfileConfig
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.AGENDA_DEFAULT
import pl.szczodrzynski.edziennik.config.Config import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.COLOR_MODE_WEIGHTED
import pl.szczodrzynski.edziennik.utils.managers.GradesManager.Companion.YEAR_ALL_GRADES
class ProfileConfigMigration(app: App, config: Config) { class ProfileConfigMigration(config: ProfileConfig) {
init { config.apply { init { config.apply {
val p = app.getSharedPreferences("pl.szczodrzynski.edziennik_profiles", Context.MODE_PRIVATE)
val s = "app.appConfig"
if (dataVersion < 1) { if (dataVersion < 1) {
grades.colorMode = COLOR_MODE_WEIGHTED
grades.dontCountEnabled = false
grades.yearAverageMode = YEAR_ALL_GRADES
ui.agendaViewType = AGENDA_DEFAULT
//dataVersion = 1 dataVersion = 1
}
if (dataVersion < 2) {
//gradesColorMode do profilu !
//agendaViewType do profilu !
// app.appConfig.dontCountZeroToAverage do profilu !
} }
}} }}
} }

View File

@ -12,13 +12,17 @@ import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.events.* import pl.szczodrzynski.edziennik.data.api.events.*
import pl.szczodrzynski.edziennik.data.api.events.requests.ServiceCloseRequest import pl.szczodrzynski.edziennik.data.api.events.requests.ServiceCloseRequest
import pl.szczodrzynski.edziennik.data.api.events.requests.TaskCancelRequest import pl.szczodrzynski.edziennik.data.api.events.requests.TaskCancelRequest
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.task.* import pl.szczodrzynski.edziennik.data.api.task.ErrorReportTask
import pl.szczodrzynski.edziennik.data.api.task.IApiTask
import pl.szczodrzynski.edziennik.data.api.task.SzkolnyTask
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.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
@ -26,7 +30,7 @@ import kotlin.math.roundToInt
class ApiService : Service() { class ApiService : Service() {
companion object { companion object {
const val TAG = "ApiService" const val TAG = "ApiService"
const val NOTIFICATION_API_CHANNEL_ID = "pl.szczodrzynski.edziennik.GET_DATA" const val NOTIFICATION_API_CHANNEL_ID = "pl.szczodrzynski.edziennik.SYNC"
fun start(context: Context) { fun start(context: Context) {
context.startService(Intent(context, ApiService::class.java)) context.startService(Intent(context, ApiService::class.java))
} }
@ -40,26 +44,25 @@ class ApiService : Service() {
private val syncingProfiles = mutableListOf<Profile>() private val syncingProfiles = mutableListOf<Profile>()
private val finishingTaskQueue = mutableListOf( private var szkolnyTaskFinished = false
SzkolnyTask.sync(syncingProfiles), private val allTaskRequestList = mutableListOf<Any>()
NotifyTask()
)
private val allTaskList = mutableListOf<IApiTask>()
private val taskQueue = mutableListOf<IApiTask>() private val taskQueue = mutableListOf<IApiTask>()
private val errorList = mutableListOf<ApiError>() private val errorList = mutableListOf<ApiError>()
private var serviceClosed = false private var serviceClosed = false
set(value) { field = value; notification.serviceClosed = value }
private var taskCancelled = false private var taskCancelled = false
private var taskIsRunning = false private var taskIsRunning = false
private var taskRunning: IApiTask? = null // for debug purposes private var taskRunning: IApiTask? = null // for debug purposes
private var taskRunningId = -1 private var taskRunningId = -1
private var taskStartTime = 0L
private var taskMaximumId = 0 private var taskMaximumId = 0
private var taskProfileId = -1 private var taskProfileId = -1
private var taskProgress = -1f private var taskProgress = -1f
private var taskProgressText: String? = null private var taskProgressText: String? = null
private val notification by lazy { EdziennikNotification(this) } private val notification by lazy { EdziennikNotification(app) }
private var lastEventTime = System.currentTimeMillis() private var lastEventTime = System.currentTimeMillis()
private var taskCancelTries = 0 private var taskCancelTries = 0
@ -73,7 +76,7 @@ class ApiService : Service() {
private val taskCallback = object : EdziennikCallback { private val taskCallback = object : EdziennikCallback {
override fun onCompleted() { override fun onCompleted() {
lastEventTime = System.currentTimeMillis() lastEventTime = System.currentTimeMillis()
d(TAG, "Task $taskRunningId (profile $taskProfileId) - $taskProgressText - finished") d(TAG, "Task $taskRunningId (profile $taskProfileId) finished in ${System.currentTimeMillis()-taskStartTime}")
EventBus.getDefault().postSticky(ApiTaskFinishedEvent(taskProfileId)) EventBus.getDefault().postSticky(ApiTaskFinishedEvent(taskProfileId))
clearTask() clearTask()
@ -85,9 +88,16 @@ class ApiService : Service() {
lastEventTime = System.currentTimeMillis() lastEventTime = System.currentTimeMillis()
d(TAG, "Task $taskRunningId threw an error - $apiError") d(TAG, "Task $taskRunningId threw an error - $apiError")
apiError.profileId = taskProfileId apiError.profileId = taskProfileId
EventBus.getDefault().postSticky(ApiTaskErrorEvent(apiError))
errorList.add(apiError) if (app.userActionManager.requiresUserAction(apiError)) {
apiError.throwable?.printStackTrace() app.userActionManager.sendToUser(apiError)
}
else {
EventBus.getDefault().postSticky(ApiTaskErrorEvent(apiError))
errorList.add(apiError)
apiError.throwable?.printStackTrace()
}
if (apiError.isCritical) { if (apiError.isCritical) {
taskRunning?.cancel() taskRunning?.cancel()
notification.setCriticalError().post() notification.setCriticalError().post()
@ -131,15 +141,20 @@ class ApiService : Service() {
checkIfTaskFrozen() checkIfTaskFrozen()
if (taskIsRunning) if (taskIsRunning)
return return
if (taskCancelled || serviceClosed || (taskQueue.isEmpty() && finishingTaskQueue.isEmpty())) { if (taskCancelled || serviceClosed || (taskQueue.isEmpty() && szkolnyTaskFinished)) {
serviceClosed = false
allCompleted() allCompleted()
return return
} }
lastEventTime = System.currentTimeMillis() lastEventTime = System.currentTimeMillis()
val task = if (taskQueue.isEmpty()) finishingTaskQueue.removeAt(0) else taskQueue.removeAt(0) val task = if (taskQueue.isNotEmpty()) {
taskQueue.removeAt(0)
} else {
szkolnyTaskFinished = true
SzkolnyTask(app, syncingProfiles)
}
task.taskId = ++taskMaximumId task.taskId = ++taskMaximumId
task.prepare(app) task.prepare(app)
taskIsRunning = true taskIsRunning = true
@ -149,7 +164,7 @@ class ApiService : Service() {
taskProgress = -1f taskProgress = -1f
taskProgressText = task.taskName taskProgressText = task.taskName
d(TAG, "Executing task $taskRunningId ($taskProgressText) - $task") d(TAG, "Executing task $taskRunningId - ${task::class.java.name}")
// update the notification // update the notification
notification.setCurrentTask(taskRunningId, taskProgressText).post() notification.setCurrentTask(taskRunningId, taskProgressText).post()
@ -159,15 +174,15 @@ class ApiService : Service() {
task.profile?.let { syncingProfiles.add(it) } task.profile?.let { syncingProfiles.add(it) }
taskStartTime = System.currentTimeMillis()
try { try {
when (task) { when (task) {
is EdziennikTask -> task.run(app, taskCallback) is EdziennikTask -> task.run(app, taskCallback)
is NotifyTask -> task.run(app, taskCallback)
is ErrorReportTask -> task.run(app, taskCallback, notification, errorList) is ErrorReportTask -> task.run(app, taskCallback, notification, errorList)
is SzkolnyTask -> task.run(app, 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))
} }
} }
@ -214,6 +229,7 @@ class ApiService : Service() {
} }
private fun allCompleted() { private fun allCompleted() {
serviceClosed = true
EventBus.getDefault().postSticky(ApiTaskAllFinishedEvent()) EventBus.getDefault().postSticky(ApiTaskAllFinishedEvent())
stopSelf() stopSelf()
} }
@ -229,10 +245,12 @@ class ApiService : Service() {
EventBus.getDefault().removeStickyEvent(task) EventBus.getDefault().removeStickyEvent(task)
d(TAG, task.toString()) d(TAG, task.toString())
// fix for duplicated tasks, thank you EventBus if (task is EdziennikTask) {
if (task in allTaskList) // fix for duplicated tasks, thank you EventBus
return if (task.request in allTaskRequestList)
allTaskList += task return
allTaskRequestList += task.request
}
if (task is EdziennikTask) { if (task is EdziennikTask) {
when (task.request) { when (task.request) {
@ -293,11 +311,13 @@ class ApiService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
d(TAG, "Foreground service onStartCommand") d(TAG, "Foreground service onStartCommand")
startForeground(EdziennikNotification.NOTIFICATION_ID, notification.notification) startForeground(app.notificationChannelsManager.sync.id, notification.notification)
return START_NOT_STICKY return START_NOT_STICKY
} }
override fun onDestroy() { override fun onDestroy() {
d(TAG, "Service destroyed")
serviceClosed = true
EventBus.getDefault().unregister(this) EventBus.getDefault().unregister(this)
} }

View File

@ -22,9 +22,9 @@ 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 = "6XPsKf10LPz1nxgHQLcvZ1KM48DYzlBAhxipaXY8"
const val LIBRUS_REDIRECT_URL = "http://localhost/bar" const val LIBRUS_REDIRECT_URL = "http://localhost/bar"
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"
@ -60,6 +60,7 @@ 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"

View File

@ -1,209 +0,0 @@
package pl.szczodrzynski.edziennik.data.api
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ANNOUNCEMENTS
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ATTENDANCE
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_BEHAVIOUR
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_GRADES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOME
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.models.Data
import pl.szczodrzynski.edziennik.data.db.entity.Attendance
import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.Grade.*
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Notice
import pl.szczodrzynski.edziennik.data.db.entity.Notification
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_LUCKY_NUMBER
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_ANNOUNCEMENT
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_ATTENDANCE
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_EVENT
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_GRADE
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_HOMEWORK
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_MESSAGE
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_NOTICE
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_TIMETABLE_LESSON_CHANGE
import pl.szczodrzynski.edziennik.getNotificationTitle
import pl.szczodrzynski.edziennik.utils.models.Date
class DataNotifications(val data: Data) {
companion object {
private const val TAG = "DataNotifications"
}
val app = data.app
val profileId = data.profile?.id ?: -1
val profileName = data.profile?.name ?: ""
val profile = data.profile
val loginStore = data.loginStore
init { run {
if (profile == null) {
return@run
}
val today = Date.getToday()
val todayValue = today.value
for (lesson in app.db.timetableDao().getNotNotifiedNow(profileId)) {
val text = app.getString(R.string.notification_lesson_change_format, lesson.getDisplayChangeType(app), if (lesson.displayDate == null) "" else lesson.displayDate!!.formattedString, lesson.changeSubjectName)
data.notifications += Notification(
title = app.getNotificationTitle(TYPE_TIMETABLE_LESSON_CHANGE),
text = text,
type = TYPE_TIMETABLE_LESSON_CHANGE,
profileId = profileId,
profileName = profileName,
viewId = DRAWER_ITEM_TIMETABLE,
addedDate = lesson.addedDate
).addExtra("timetableDate", lesson.displayDate?.stringY_m_d ?: "")
}
for (event in app.db.eventDao().getNotNotifiedNow(profileId)) {
val text = if (event.type == Event.TYPE_HOMEWORK)
app.getString(
if (event.subjectLongName.isNullOrEmpty())
R.string.notification_homework_no_subject_format
else
R.string.notification_homework_format,
event.subjectLongName,
event.eventDate.formattedString
)
else
app.getString(
if (event.subjectLongName.isNullOrEmpty())
R.string.notification_event_no_subject_format
else
R.string.notification_event_format,
event.typeName,
event.eventDate.formattedString,
event.subjectLongName
)
val type = if (event.type == Event.TYPE_HOMEWORK) TYPE_NEW_HOMEWORK else TYPE_NEW_EVENT
data.notifications += Notification(
title = app.getNotificationTitle(type),
text = text,
type = type,
profileId = profileId,
profileName = profileName,
viewId = if (event.type == Event.TYPE_HOMEWORK) DRAWER_ITEM_HOMEWORK else DRAWER_ITEM_AGENDA,
addedDate = event.addedDate
).addExtra("eventId", event.id).addExtra("eventDate", event.eventDate.value.toLong())
}
for (grade in app.db.gradeDao().getNotNotifiedNow(profileId)) {
val gradeName = when (grade.type) {
TYPE_SEMESTER1_PROPOSED, TYPE_SEMESTER2_PROPOSED -> app.getString(R.string.grade_semester_proposed_format_2, grade.name)
TYPE_SEMESTER1_FINAL, TYPE_SEMESTER2_FINAL -> app.getString(R.string.grade_semester_final_format_2, grade.name)
TYPE_YEAR_PROPOSED -> app.getString(R.string.grade_year_proposed_format_2, grade.name)
TYPE_YEAR_FINAL -> app.getString(R.string.grade_year_final_format_2, grade.name)
else -> grade.name
}
val text = app.getString(R.string.notification_grade_format, gradeName, grade.subjectLongName)
data.notifications += Notification(
title = app.getNotificationTitle(TYPE_NEW_GRADE),
text = text,
type = TYPE_NEW_GRADE,
profileId = profileId,
profileName = profileName,
viewId = DRAWER_ITEM_GRADES,
addedDate = grade.addedDate
).addExtra("gradeId", grade.id).addExtra("gradesSubjectId", grade.subjectId)
}
for (notice in app.db.noticeDao().getNotNotifiedNow(profileId)) {
val noticeTypeStr = if (notice.type == Notice.TYPE_POSITIVE) app.getString(R.string.notification_notice_praise) else if (notice.type == Notice.TYPE_NEGATIVE) app.getString(R.string.notification_notice_warning) else app.getString(R.string.notification_notice_new)
val text = app.getString(R.string.notification_notice_format, noticeTypeStr, notice.teacherFullName, Date.fromMillis(notice.addedDate).formattedString)
data.notifications += Notification(
title = app.getNotificationTitle(TYPE_NEW_NOTICE),
text = text,
type = TYPE_NEW_NOTICE,
profileId = profileId,
profileName = profileName,
viewId = DRAWER_ITEM_BEHAVIOUR,
addedDate = notice.addedDate
).addExtra("noticeId", notice.id)
}
for (attendance in app.db.attendanceDao().getNotNotifiedNow(profileId)) {
var attendanceTypeStr = app.getString(R.string.notification_type_attendance)
when (attendance.type) {
Attendance.TYPE_ABSENT -> attendanceTypeStr = app.getString(R.string.notification_absence)
Attendance.TYPE_ABSENT_EXCUSED -> attendanceTypeStr = app.getString(R.string.notification_absence_excused)
Attendance.TYPE_BELATED -> attendanceTypeStr = app.getString(R.string.notification_belated)
Attendance.TYPE_BELATED_EXCUSED -> attendanceTypeStr = app.getString(R.string.notification_belated_excused)
Attendance.TYPE_RELEASED -> attendanceTypeStr = app.getString(R.string.notification_release)
}
val text = app.getString(
if (attendance.subjectLongName.isNullOrEmpty())
R.string.notification_attendance_no_lesson_format
else
R.string.notification_attendance_format,
attendanceTypeStr,
attendance.subjectLongName,
attendance.lessonDate.formattedString
)
data.notifications += Notification(
title = app.getNotificationTitle(TYPE_NEW_ATTENDANCE),
text = text,
type = TYPE_NEW_ATTENDANCE,
profileId = profileId,
profileName = profileName,
viewId = DRAWER_ITEM_ATTENDANCE,
addedDate = attendance.addedDate
).addExtra("attendanceId", attendance.id).addExtra("attendanceSubjectId", attendance.subjectId)
}
for (announcement in app.db.announcementDao().getNotNotifiedNow(profileId)) {
val text = app.context.getString(R.string.notification_announcement_format, announcement.subject)
data.notifications += Notification(
title = app.getNotificationTitle(TYPE_NEW_ANNOUNCEMENT),
text = text,
type = TYPE_NEW_ANNOUNCEMENT,
profileId = profileId,
profileName = profileName,
viewId = DRAWER_ITEM_ANNOUNCEMENTS,
addedDate = announcement.addedDate
).addExtra("announcementId", announcement.id)
}
for (message in app.db.messageDao().getReceivedNotNotifiedNow(profileId)) {
val text = app.context.getString(R.string.notification_message_format, message.senderFullName, message.subject)
data.notifications += Notification(
title = app.getNotificationTitle(TYPE_NEW_MESSAGE),
text = text,
type = TYPE_NEW_MESSAGE,
profileId = profileId,
profileName = profileName,
viewId = DRAWER_ITEM_MESSAGES,
addedDate = message.addedDate
).addExtra("messageType", Message.TYPE_RECEIVED.toLong()).addExtra("messageId", message.id)
}
val luckyNumbers = app.db.luckyNumberDao().getNotNotifiedNow(profileId)
luckyNumbers?.removeAll { it.date < today }
luckyNumbers?.forEach { luckyNumber ->
val text = when (luckyNumber.date.value) {
todayValue -> // LN for today
app.getString(if (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) R.string.notification_lucky_number_yours_format else R.string.notification_lucky_number_format, luckyNumber.number)
todayValue + 1 -> // LN for tomorrow
app.getString(if (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) R.string.notification_lucky_number_yours_tomorrow_format else R.string.notification_lucky_number_tomorrow_format, luckyNumber.number)
else -> // LN for later
app.getString(if (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) R.string.notification_lucky_number_yours_later_format else R.string.notification_lucky_number_later_format, luckyNumber.date.formattedString, luckyNumber.number)
}
data.notifications += Notification(
title = app.getNotificationTitle(TYPE_LUCKY_NUMBER),
text = text,
type = TYPE_LUCKY_NUMBER,
profileId = profileId,
profileName = profileName,
viewId = DRAWER_ITEM_HOME,
addedDate = luckyNumber.addedDate
)
}
data.db.metadataDao().setAllNotified(profileId, true)
}}
}

View File

@ -11,19 +11,16 @@ import android.content.Context
import android.content.Intent import 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.R import pl.szczodrzynski.edziennik.R
import kotlin.math.roundToInt import kotlin.math.roundToInt
class EdziennikNotification(val context: Context) { class EdziennikNotification(val app: App) {
companion object { private val notificationManager by lazy { app.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager }
const val NOTIFICATION_ID = 20191001
}
private val notificationManager by lazy { context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager }
private val notificationBuilder: NotificationCompat.Builder by lazy { private val notificationBuilder: NotificationCompat.Builder by lazy {
NotificationCompat.Builder(context, ApiService.NOTIFICATION_API_CHANNEL_ID) NotificationCompat.Builder(app, ApiService.NOTIFICATION_API_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification) .setSmallIcon(R.drawable.ic_notification)
.setPriority(PRIORITY_MIN) .setPriority(PRIORITY_MIN)
.setOngoing(true) .setOngoing(true)
@ -35,39 +32,40 @@ class EdziennikNotification(val context: Context) {
private var errorCount = 0 private var errorCount = 0
private var criticalErrorCount = 0 private var criticalErrorCount = 0
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 = Intent("pl.szczodrzynski.edziennik.SZKOLNY_MAIN")
intent.putExtra("task", "TaskCancelRequest") intent.putExtra("task", "TaskCancelRequest")
intent.putExtra("taskId", taskId) intent.putExtra("taskId", taskId)
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT) as PendingIntent return PendingIntent.getBroadcast(app, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT) as PendingIntent
} }
private val closePendingIntent: PendingIntent private val closePendingIntent: PendingIntent
get() { get() {
val intent = Intent("pl.szczodrzynski.edziennik.SZKOLNY_MAIN") val intent = Intent("pl.szczodrzynski.edziennik.SZKOLNY_MAIN")
intent.putExtra("task", "ServiceCloseRequest") intent.putExtra("task", "ServiceCloseRequest")
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT) as PendingIntent return PendingIntent.getBroadcast(app, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT) as PendingIntent
} }
private fun errorCountText(): String? { private fun errorCountText(): String? {
var result = "" var result = ""
if (criticalErrorCount > 0) { if (criticalErrorCount > 0) {
result += context.resources.getQuantityString(R.plurals.critical_errors_format, criticalErrorCount, criticalErrorCount) result += app.resources.getQuantityString(R.plurals.critical_errors_format, criticalErrorCount, criticalErrorCount)
} }
if (criticalErrorCount > 0 && errorCount > 0) { if (criticalErrorCount > 0 && errorCount > 0) {
result += ", " result += ", "
} }
if (errorCount > 0) { if (errorCount > 0) {
result += context.resources.getQuantityString(R.plurals.normal_errors_format, errorCount, errorCount) result += app.resources.getQuantityString(R.plurals.normal_errors_format, errorCount, errorCount)
} }
return if (result.isEmpty()) null else result return if (result.isEmpty()) null else result
} }
fun setIdle(): EdziennikNotification { fun setIdle(): EdziennikNotification {
notificationBuilder.setContentTitle(context.getString(R.string.edziennik_notification_api_title)) notificationBuilder.setContentTitle(app.getString(R.string.edziennik_notification_api_title))
notificationBuilder.setProgress(0, 0, false) notificationBuilder.setProgress(0, 0, false)
notificationBuilder.apply { notificationBuilder.apply {
val str = context.getString(R.string.edziennik_notification_api_text) val str = app.getString(R.string.edziennik_notification_api_text)
setStyle(NotificationCompat.BigTextStyle().bigText(str)) setStyle(NotificationCompat.BigTextStyle().bigText(str))
setContentText(str) setContentText(str)
} }
@ -81,7 +79,7 @@ class EdziennikNotification(val context: Context) {
} }
fun setCriticalError(): EdziennikNotification { fun setCriticalError(): EdziennikNotification {
criticalErrorCount++ criticalErrorCount++
notificationBuilder.setContentTitle(context.getString(R.string.edziennik_notification_api_error_title)) notificationBuilder.setContentTitle(app.getString(R.string.edziennik_notification_api_error_title))
notificationBuilder.setProgress(0, 0, false) notificationBuilder.setProgress(0, 0, false)
notificationBuilder.apply { notificationBuilder.apply {
val str = errorCountText() val str = errorCountText()
@ -118,7 +116,7 @@ class EdziennikNotification(val context: Context) {
notificationBuilder.addAction( notificationBuilder.addAction(
NotificationCompat.Action( NotificationCompat.Action(
R.drawable.ic_notification, R.drawable.ic_notification,
context.getString(R.string.edziennik_notification_api_close), app.getString(R.string.edziennik_notification_api_close),
closePendingIntent closePendingIntent
)) ))
return this return this
@ -128,13 +126,15 @@ class EdziennikNotification(val context: Context) {
notificationBuilder.addAction( notificationBuilder.addAction(
NotificationCompat.Action( NotificationCompat.Action(
R.drawable.ic_notification, R.drawable.ic_notification,
context.getString(R.string.edziennik_notification_api_cancel), app.getString(R.string.edziennik_notification_api_cancel),
cancelPendingIntent(taskId) cancelPendingIntent(taskId)
)) ))
} }
fun post() { fun post() {
notificationManager.notify(NOTIFICATION_ID, notification) if (serviceClosed)
return
notificationManager.notify(app.notificationChannelsManager.sync.id, notification)
} }
} }

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,14 +46,19 @@ 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 ||
data.targetEndpointIds.add(endpoint.first) viewId != null && timer.viewId == viewId ||
timer.nextSync != SYNC_NEVER && timer.nextSync < timestamp
) {
data.targetEndpointIds[endpoint.first] = timer.lastSync
requiredLoginMethods.add(endpoint.second) requiredLoginMethods.add(endpoint.second)
} }
} }
@ -76,8 +81,8 @@ fun Data.prepare(loginMethods: List<LoginMethod>, features: List<Feature>, featu
data.targetLoginMethodIds = data.targetLoginMethodIds.toHashSet().toMutableList() data.targetLoginMethodIds = data.targetLoginMethodIds.toHashSet().toMutableList()
data.targetLoginMethodIds.sort() data.targetLoginMethodIds.sort()
data.targetEndpointIds = data.targetEndpointIds.toHashSet().toMutableList() //data.targetEndpointIds = data.targetEndpointIds.toHashSet().toMutableList()
data.targetEndpointIds.sort() //data.targetEndpointIds.sort()
progressCount = targetLoginMethodIds.size + targetEndpointIds.size progressCount = targetLoginMethodIds.size + targetEndpointIds.size
progressStep = if (progressCount <= 0) 0f else 100f / progressCount.toFloat() progressStep = if (progressCount <= 0) 0f else 100f / progressCount.toFloat()
@ -91,7 +96,6 @@ fun Data.prepareFor(loginMethods: List<LoginMethod>, loginMethodId: Int) {
possibleLoginMethods += it.loginMethodId possibleLoginMethods += it.loginMethodId
} }
targetEndpointIds.clear()
targetLoginMethodIds.clear() targetLoginMethodIds.clear()
// check the login method for any dependencies // check the login method for any dependencies

View File

@ -32,6 +32,8 @@ const val CODE_LIBRUS_DISCONNECTED = 31
const val CODE_PROFILE_ARCHIVED = 30*/ const val CODE_PROFILE_ARCHIVED = 30*/
const val ERROR_APP_CRASH = 1 const val ERROR_APP_CRASH = 1
const val ERROR_EXCEPTION = 2
const val ERROR_API_EXCEPTION = 3
const val ERROR_MESSAGE_NOT_SENT = 10 const val ERROR_MESSAGE_NOT_SENT = 10
const val ERROR_REQUEST_FAILURE = 50 const val ERROR_REQUEST_FAILURE = 50
@ -51,6 +53,7 @@ const val ERROR_REQUEST_FAILURE_SSL_ERROR = 63
const val ERROR_RESPONSE_EMPTY = 100 const val ERROR_RESPONSE_EMPTY = 100
const val ERROR_LOGIN_DATA_MISSING = 101 const val ERROR_LOGIN_DATA_MISSING = 101
const val ERROR_PROFILE_MISSING = 105 const val ERROR_PROFILE_MISSING = 105
const val ERROR_PROFILE_ARCHIVED = 106
const val ERROR_INVALID_LOGIN_MODE = 110 const val ERROR_INVALID_LOGIN_MODE = 110
const val ERROR_LOGIN_METHOD_NOT_SATISFIED = 111 const val ERROR_LOGIN_METHOD_NOT_SATISFIED = 111
const val ERROR_NOT_IMPLEMENTED = 112 const val ERROR_NOT_IMPLEMENTED = 112
@ -58,6 +61,9 @@ const val ERROR_FILE_DOWNLOAD = 113
const val ERROR_NO_STUDENTS_IN_ACCOUNT = 115 const val ERROR_NO_STUDENTS_IN_ACCOUNT = 115
const val ERROR_CAPTCHA_NEEDED = 3000
const val ERROR_CAPTCHA_LIBRUS_PORTAL = 3001
const val CODE_INTERNAL_LIBRUS_ACCOUNT_410 = 120 const val CODE_INTERNAL_LIBRUS_ACCOUNT_410 = 120
const val CODE_INTERNAL_LIBRUS_SYNERGIA_EXPIRED = 121 const val CODE_INTERNAL_LIBRUS_SYNERGIA_EXPIRED = 121
const val ERROR_LOGIN_LIBRUS_API_CAPTCHA_NEEDED = 124 const val ERROR_LOGIN_LIBRUS_API_CAPTCHA_NEEDED = 124
@ -115,6 +121,11 @@ const val ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN = 179
const val ERROR_LOGIN_LIBRUS_PORTAL_INVALID_LOGIN = 180 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_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_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
@ -129,6 +140,9 @@ const val ERROR_MOBIDZIENNIK_WEB_NO_SESSION_VALUE = 216
const val ERROR_MOBIDZIENNIK_WEB_NO_SERVER_ID = 213 const val ERROR_MOBIDZIENNIK_WEB_NO_SERVER_ID = 213
const val ERROR_MOBIDZIENNIK_WEB_INVALID_RESPONSE = 214 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_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

View File

@ -41,6 +41,7 @@ internal const val FEATURE_PUSH_CONFIG = 120
object Features { object Features {
private fun getAllNecessary(): List<Int> = listOf( private fun getAllNecessary(): List<Int> = listOf(
FEATURE_ALWAYS_NEEDED, FEATURE_ALWAYS_NEEDED,
FEATURE_PUSH_CONFIG,
FEATURE_STUDENT_INFO, FEATURE_STUDENT_INFO,
FEATURE_STUDENT_NUMBER, FEATURE_STUDENT_NUMBER,
FEATURE_SCHOOL_INFO, FEATURE_SCHOOL_INFO,

View File

@ -11,6 +11,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginApi
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginMessages import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginMessages
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginPortal import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginPortal
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginSynergia import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginSynergia
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.login.MobidziennikLoginApi2
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.login.MobidziennikLoginWeb import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.login.MobidziennikLoginWeb
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
@ -69,13 +70,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
} }
) )
@ -86,11 +87,11 @@ const val LOGIN_METHOD_MOBIDZIENNIK_API2 = 300
val mobidziennikLoginMethods = listOf( val mobidziennikLoginMethods = listOf(
LoginMethod(LOGIN_TYPE_MOBIDZIENNIK, LOGIN_METHOD_MOBIDZIENNIK_WEB, MobidziennikLoginWeb::class.java) LoginMethod(LOGIN_TYPE_MOBIDZIENNIK, LOGIN_METHOD_MOBIDZIENNIK_WEB, MobidziennikLoginWeb::class.java)
.withIsPossible { _, _ -> true } .withIsPossible { _, _ -> true }
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }/*, .withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED },
LoginMethod(LOGIN_TYPE_MOBIDZIENNIK, LOGIN_METHOD_MOBIDZIENNIK_API2, MobidziennikLoginApi2::class.java) LoginMethod(LOGIN_TYPE_MOBIDZIENNIK, LOGIN_METHOD_MOBIDZIENNIK_API2, MobidziennikLoginApi2::class.java)
.withIsPossible { _, loginStore -> loginStore.hasLoginData("email") } .withIsPossible { profile, _ -> profile?.getStudentData("email", null) != null }
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }*/ .withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }
) )
const val LOGIN_TYPE_VULCAN = 4 const val LOGIN_TYPE_VULCAN = 4

View File

@ -5,10 +5,11 @@
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()
} }
@ -54,9 +55,33 @@ object Regexes {
val MOBIDZIENNIK_MESSAGE_ATTACHMENT by lazy { val MOBIDZIENNIK_MESSAGE_ATTACHMENT by lazy {
"""href="https://.+?\.mobidziennik.pl/.+?&(?:amp;)?zalacznik=([0-9]+)"(?:.+?<small.+?\(([0-9.]+)\s(M|K|G|)B\))*""".toRegex(DOT_MATCHES_ALL) """href="https://.+?\.mobidziennik.pl/.+?&(?:amp;)?zalacznik=([0-9]+)"(?:.+?<small.+?\(([0-9.]+)\s(M|K|G|)B\))*""".toRegex(DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_MESSAGE_SENT_READ_BY by lazy {
"""([0-9]+)/([0-9]+)""".toRegex()
}
val MOBIDZIENNIK_MESSAGE_RECIPIENTS_JSON by lazy { val MOBIDZIENNIK_MESSAGE_RECIPIENTS_JSON by lazy {
"""odbiorcy: (\[.+?\]),${'$'}""".toRegex(RegexOption.MULTILINE) """odbiorcy: (\[.+?]),${'$'}""".toRegex(RegexOption.MULTILINE)
}
val MOBIDZIENNIK_ACCOUNT_EMAIL by lazy {
"""name="email" value="(.+?@.+?\..+?)"""".toRegex(DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_ATTENDANCE_TABLE by lazy {
"""<table .+?id="obecnosci_tabela">(.+?)</table>""".toRegex(DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_ATTENDANCE_LESSON_COUNT by lazy {
"""rel="([0-9-]{10})" colspan="([0-9]+)"""".toRegex()
}
val MOBIDZIENNIK_ATTENDANCE_ENTRIES by lazy {
"""font-size:.+?class=".*?">(.*?)</td>""".toRegex(DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_ATTENDANCE_RANGE by lazy {
"""<span>([0-9:]+) - .+? (.+?)</span></a>""".toRegex(DOT_MATCHES_ALL)
}
val MOBIDZIENNIK_ATTENDANCE_LESSON by lazy {
"""<strong>(.+?) - (.*?)</strong>.+?<small>.+?\((.+?), .+?(.+?)\)""".toRegex(DOT_MATCHES_ALL)
} }
@ -74,7 +99,7 @@ object Regexes {
"""id="ctl00_CzyRodzic" value="([01])" />""".toRegex() """id="ctl00_CzyRodzic" value="([01])" />""".toRegex()
} }
val IDZIENNIK_LOGIN_FIRST_SCHOOL_YEAR by lazy { val IDZIENNIK_LOGIN_FIRST_SCHOOL_YEAR by lazy {
"""name="ctl00\\${'$'}dxComboRokSzkolny".+?selected="selected".*?value="([0-9]+)">([0-9]+)/([0-9]+)<""".toRegex(DOT_MATCHES_ALL) """name="ctl00\${"$"}dxComboRokSzkolny".+?selected="selected".*?value="([0-9]+)">([0-9]+)/([0-9]+)<""".toRegex(DOT_MATCHES_ALL)
} }
val IDZIENNIK_LOGIN_FIRST_STUDENT_SELECT by lazy { val IDZIENNIK_LOGIN_FIRST_STUDENT_SELECT by lazy {
"""<select.*?name="ctl00\${"$"}dxComboUczniowie".*?</select>""".toRegex(DOT_MATCHES_ALL) """<select.*?name="ctl00\${"$"}dxComboUczniowie".*?</select>""".toRegex(DOT_MATCHES_ALL)
@ -85,6 +110,13 @@ 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()
}
@ -95,7 +127,7 @@ object Regexes {
val LIBRUS_ATTACHMENT_KEY by lazy { val LIBRUS_ATTACHMENT_KEY by lazy {
"""singleUseKey=([0-9A-f_]+)""".toRegex() """singleUseKey=([0-9A-z_]+)""".toRegex()
} }
@ -168,4 +200,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

@ -1,4 +1,8 @@
package pl.szczodrzynski.edziennik.data.api.task /*
* Copyright (c) Kuba Szczodrzyński 2020-1-16.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik
import com.google.gson.JsonObject import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
@ -12,11 +16,13 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.template.Template
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.Vulcan import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.Vulcan
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.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.task.IApiTask
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
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.MessageFull
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 {
@ -24,7 +30,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
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))
@ -57,6 +63,10 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
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) {
taskCallback.onError(ApiError(TAG, ERROR_PROFILE_ARCHIVED))
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)
@ -75,6 +85,7 @@ 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)
@ -96,7 +107,7 @@ 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)

View File

@ -7,11 +7,7 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_EDUDZIENNIK_WEB import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_EDUDZIENNIK_WEB
import pl.szczodrzynski.edziennik.data.api.models.Data import pl.szczodrzynski.edziennik.data.api.models.Data
import pl.szczodrzynski.edziennik.data.db.entity.EventType import pl.szczodrzynski.edziennik.data.db.entity.*
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Subject
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
/** /**
* Use http://patorjk.com/software/taag/#p=display&f=Big for the ascii art * Use http://patorjk.com/software/taag/#p=display&f=Big for the ascii art

View File

@ -15,12 +15,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.login.Edudzienn
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.data.api.interfaces.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.full.AnnouncementFull
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.Message
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
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.MessageFull
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
@ -41,9 +41,7 @@ class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStor
private fun completed() { private fun completed() {
data.saveData() data.saveData()
data.notify { callback.onCompleted()
callback.onCompleted()
}
} }
/* _______ _ _ _ _ _ /* _______ _ _ _ _ _
@ -54,9 +52,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()
} }

View File

@ -27,60 +27,62 @@ class EdudziennikData(val data: DataEdudziennik, val onSuccess: () -> Unit) {
onSuccess() onSuccess()
return return
} }
useEndpoint(data.targetEndpointIds.removeAt(0)) { val id = data.targetEndpointIds.firstKey()
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: () -> Unit) { private fun useEndpoint(endpointId: Int, lastSync: Long?, onSuccess: (endpointId: Int) -> Unit) {
Utils.d(TAG, "Using endpoint $endpointId") Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync")
when (endpointId) { when (endpointId) {
ENDPOINT_EDUDZIENNIK_WEB_START -> { ENDPOINT_EDUDZIENNIK_WEB_START -> {
data.startProgress(R.string.edziennik_progress_endpoint_data) data.startProgress(R.string.edziennik_progress_endpoint_data)
EdudziennikWebStart(data, onSuccess) EdudziennikWebStart(data, lastSync, onSuccess)
} }
ENDPOINT_EDUDZIENNIK_WEB_TEACHERS -> { ENDPOINT_EDUDZIENNIK_WEB_TEACHERS -> {
data.startProgress(R.string.edziennik_progress_endpoint_teachers) data.startProgress(R.string.edziennik_progress_endpoint_teachers)
EdudziennikWebTeachers(data, onSuccess) EdudziennikWebTeachers(data, lastSync, onSuccess)
} }
ENDPOINT_EDUDZIENNIK_WEB_GRADES -> { ENDPOINT_EDUDZIENNIK_WEB_GRADES -> {
data.startProgress(R.string.edziennik_progress_endpoint_grades) data.startProgress(R.string.edziennik_progress_endpoint_grades)
EdudziennikWebGrades(data, onSuccess) EdudziennikWebGrades(data, lastSync, onSuccess)
} }
ENDPOINT_EDUDZIENNIK_WEB_TIMETABLE -> { ENDPOINT_EDUDZIENNIK_WEB_TIMETABLE -> {
data.startProgress(R.string.edziennik_progress_endpoint_timetable) data.startProgress(R.string.edziennik_progress_endpoint_timetable)
EdudziennikWebTimetable(data, onSuccess) EdudziennikWebTimetable(data, lastSync, onSuccess)
} }
ENDPOINT_EDUDZIENNIK_WEB_EXAMS -> { ENDPOINT_EDUDZIENNIK_WEB_EXAMS -> {
data.startProgress(R.string.edziennik_progress_endpoint_exams) data.startProgress(R.string.edziennik_progress_endpoint_exams)
EdudziennikWebExams(data, onSuccess) EdudziennikWebExams(data, lastSync, onSuccess)
} }
ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE -> { ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE -> {
data.startProgress(R.string.edziennik_progress_endpoint_attendance) data.startProgress(R.string.edziennik_progress_endpoint_attendance)
EdudziennikWebAttendance(data, onSuccess) EdudziennikWebAttendance(data, lastSync, onSuccess)
} }
ENDPOINT_EDUDZIENNIK_WEB_ANNOUNCEMENTS -> { ENDPOINT_EDUDZIENNIK_WEB_ANNOUNCEMENTS -> {
data.startProgress(R.string.edziennik_progress_endpoint_announcements) data.startProgress(R.string.edziennik_progress_endpoint_announcements)
EdudziennikWebAnnouncements(data, onSuccess) EdudziennikWebAnnouncements(data, lastSync, onSuccess)
} }
ENDPOINT_EDUDZIENNIK_WEB_HOMEWORK -> { ENDPOINT_EDUDZIENNIK_WEB_HOMEWORK -> {
data.startProgress(R.string.edziennik_progress_endpoint_homework) data.startProgress(R.string.edziennik_progress_endpoint_homework)
EdudziennikWebHomework(data, onSuccess) EdudziennikWebHomework(data, lastSync, onSuccess)
} }
ENDPOINT_EDUDZIENNIK_WEB_EVENTS -> { ENDPOINT_EDUDZIENNIK_WEB_EVENTS -> {
data.startProgress(R.string.edziennik_progress_endpoint_events) data.startProgress(R.string.edziennik_progress_endpoint_events)
EdudziennikWebEvents(data, onSuccess) EdudziennikWebEvents(data, lastSync, onSuccess)
} }
ENDPOINT_EDUDZIENNIK_WEB_NOTES -> { ENDPOINT_EDUDZIENNIK_WEB_NOTES -> {
data.startProgress(R.string.edziennik_progress_endpoint_notices) data.startProgress(R.string.edziennik_progress_endpoint_notices)
EdudziennikWebNotes(data, onSuccess) EdudziennikWebNotes(data, lastSync, onSuccess)
} }
ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER -> { ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER -> {
data.startProgress(R.string.edziennik_progress_endpoint_lucky_number) data.startProgress(R.string.edziennik_progress_endpoint_lucky_number)
EdudziennikWebLuckyNumber(data, onSuccess) EdudziennikWebLuckyNumber(data, lastSync, onSuccess)
} }
else -> onSuccess() else -> onSuccess(endpointId)
} }
} }
} }

View File

@ -7,13 +7,13 @@ 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
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.models.Date
open class EdudziennikWeb(open val data: DataEdudziennik) { open class EdudziennikWeb(open val data: DataEdudziennik, open val lastSync: Long?) {
companion object { companion object {
private const val TAG = "EdudziennikWeb" private const val TAG = "EdudziennikWeb"
} }
@ -24,11 +24,11 @@ open class EdudziennikWeb(open val data: DataEdudziennik) {
val profile val profile
get() = data.profile get() = data.profile
fun webGet(tag: String, endpoint: String, xhr: Boolean = false, onSuccess: (text: String) -> Unit) { fun webGet(tag: String, endpoint: String, xhr: Boolean = false, semester: Int? = null, onSuccess: (text: String) -> Unit) {
val url = "https://dziennikel.appspot.com/" + when (endpoint.endsWith('/') || endpoint.contains('?') || endpoint.isEmpty()) { val url = "https://dziennikel.appspot.com/" + when (endpoint.endsWith('/') || endpoint.contains('?') || endpoint.isEmpty()) {
true -> endpoint true -> endpoint
else -> "$endpoint/" else -> "$endpoint/"
} } + (semester?.let { "?semester=" + if(it == -1) "all" else it } ?: "")
d(tag, "Request: Edudziennik/Web - $url") d(tag, "Request: Edudziennik/Web - $url")
@ -40,6 +40,18 @@ open class EdudziennikWeb(open val data: DataEdudziennik) {
return return
} }
if (semester == null && url.contains("start")) {
profile?.also { profile ->
val cookies = data.app.cookieJar.getAll("dziennikel.appspot.com")
val semesterCookie = cookies["semester"]?.toIntOrNull()
semesterCookie?.let { data.currentSemester = it }
if (semesterCookie == 2 && profile.dateSemester2Start > Date.getToday())
profile.dateSemester2Start = Date.getToday().stepForward(0, 0, -1)
}
}
try { try {
onSuccess(text) onSuccess(text)
} catch (e: Exception) { } catch (e: Exception) {
@ -62,18 +74,7 @@ open class EdudziennikWeb(open val data: DataEdudziennik) {
} }
} }
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(),
Cookie.Builder()
.name("semester")
.value((data.currentSemester).toString())
.domain("dziennikel.appspot.com")
.secure().httpOnly().build()
))
Request.builder() Request.builder()
.url(url) .url(url)

View File

@ -11,13 +11,15 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_ANNOUNCEMENTS import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_ANNOUNCEMENTS
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.db.entity.Announcement import pl.szczodrzynski.edziennik.data.db.entity.Announcement
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.get import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class EdudziennikWebAnnouncements(override val data: DataEdudziennik, class EdudziennikWebAnnouncements(override val data: DataEdudziennik,
val onSuccess: () -> Unit) : EdudziennikWeb(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : EdudziennikWeb(data, lastSync) {
companion object { companion object {
const val TAG = "EdudziennikWebAnnouncements" const val TAG = "EdudziennikWebAnnouncements"
} }
@ -66,7 +68,7 @@ class EdudziennikWebAnnouncements(override val data: DataEdudziennik,
} }
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_ANNOUNCEMENTS, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_ANNOUNCEMENTS, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_EDUDZIENNIK_WEB_ANNOUNCEMENTS)
} }
} ?: onSuccess() } } ?: onSuccess(ENDPOINT_EDUDZIENNIK_WEB_ANNOUNCEMENTS) }
} }

View File

@ -11,22 +11,31 @@ import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_ATTENDANCE_TYPES
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Attendance import pl.szczodrzynski.edziennik.data.db.entity.Attendance
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.get import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.singleOrNull import pl.szczodrzynski.edziennik.singleOrNull
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import java.util.* import java.util.*
class EdudziennikWebAttendance(override val data: DataEdudziennik, class EdudziennikWebAttendance(override val data: DataEdudziennik,
val onSuccess: () -> Unit) : EdudziennikWeb(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : EdudziennikWeb(data, lastSync) {
companion object { companion object {
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") { 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())
@ -69,7 +78,7 @@ class EdudziennikWebAttendance(override val data: DataEdudziennik,
id, id,
lesson?.displayTeacherId ?: -1, lesson?.displayTeacherId ?: -1,
lesson?.displaySubjectId ?: -1, lesson?.displaySubjectId ?: -1,
data.currentSemester, profile.currentSemester,
name, name,
date, date,
lesson?.displayStartTime ?: startTime, lesson?.displayStartTime ?: startTime,
@ -77,18 +86,25 @@ class EdudziennikWebAttendance(override val data: DataEdudziennik,
) )
data.attendanceList.add(attendanceObject) data.attendanceList.add(attendanceObject)
data.metadataList.add(Metadata( if(type != Attendance.TYPE_PRESENT) {
profileId, data.metadataList.add(Metadata(
Metadata.TYPE_ATTENDANCE, profileId,
id, Metadata.TYPE_ATTENDANCE,
profile.empty, id,
profile.empty, profile.empty,
System.currentTimeMillis() profile.empty,
)) System.currentTimeMillis()
))
}
} }
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE, SYNC_ALWAYS) if (profile.empty && requestSemester == 1 && data.currentSemester == 2) {
onSuccess() requestSemester = null
getAttendances()
} else {
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE, SYNC_ALWAYS)
onSuccess(ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE)
}
} }
} ?: onSuccess() } } ?: onSuccess(ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE) }
} }

View File

@ -11,14 +11,16 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_EVENTS import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_EVENTS
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.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Event 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.get import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class EdudziennikWebEvents(override val data: DataEdudziennik, class EdudziennikWebEvents(override val data: DataEdudziennik,
val onSuccess: () -> Unit) : EdudziennikWeb(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : EdudziennikWeb(data, lastSync) {
companion object { companion object {
const val TAG = "EdudziennikWebEvents" const val TAG = "EdudziennikWebEvents"
} }
@ -64,7 +66,7 @@ class EdudziennikWebEvents(override val data: DataEdudziennik,
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_CLASS_EVENT)) data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_CLASS_EVENT))
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_EVENTS, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_EVENTS, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_EDUDZIENNIK_WEB_EVENTS)
} }
} ?: onSuccess() } } ?: onSuccess(ENDPOINT_EDUDZIENNIK_WEB_EVENTS) }
} }

View File

@ -13,14 +13,16 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_EXAMS import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_EXAMS
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.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Event 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.get import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class EdudziennikWebExams(override val data: DataEdudziennik, class EdudziennikWebExams(override val data: DataEdudziennik,
val onSuccess: () -> Unit) : EdudziennikWeb(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : EdudziennikWeb(data, lastSync) {
companion object { companion object {
const val TAG = "EdudziennikWebExams" const val TAG = "EdudziennikWebExams"
} }
@ -60,7 +62,7 @@ class EdudziennikWebExams(override val data: DataEdudziennik,
startTime, startTime,
topic, topic,
-1, -1,
eventType.id.toInt(), eventType.id,
false, false,
-1, -1,
subject.id, subject.id,
@ -84,7 +86,7 @@ class EdudziennikWebExams(override val data: DataEdudziennik,
))) )))
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_EXAMS, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_EXAMS, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_EDUDZIENNIK_WEB_EXAMS)
} }
}} }}
} }

View File

@ -12,11 +12,10 @@ import pl.szczodrzynski.edziennik.data.api.events.AnnouncementGetEvent
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.get import pl.szczodrzynski.edziennik.get
class EdudziennikWebGetAnnouncement( class EdudziennikWebGetAnnouncement(override val data: DataEdudziennik,
override val data: DataEdudziennik, private val announcement: AnnouncementFull,
private val announcement: AnnouncementFull, val onSuccess: () -> Unit
val onSuccess: () -> Unit ) : EdudziennikWeb(data, null) {
) : EdudziennikWeb(data) {
companion object { companion object {
const val TAG = "EdudziennikWebGetAnnouncement" const val TAG = "EdudziennikWebGetAnnouncement"
} }

View File

@ -13,24 +13,39 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_GRADES import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_GRADES
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.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Grade import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.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.get import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class EdudziennikWebGrades(override val data: DataEdudziennik, class EdudziennikWebGrades(override val data: DataEdudziennik,
val onSuccess: () -> Unit) : EdudziennikWeb(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : EdudziennikWeb(data, lastSync) {
companion object { companion object {
private const val TAG = "EdudziennikWebGrades" private const val TAG = "EdudziennikWebGrades"
} }
init { data.profile?.also { profile -> private var requestSemester: Int? = null
webGet(TAG, data.studentEndpoint + "start") { text ->
val doc = Jsoup.parse(text)
init {
if (profile?.empty == true && data.currentSemester == 2) requestSemester = 1
getGrades()
}
private fun getGrades() { data.profile?.also { profile ->
webGet(TAG, data.studentEndpoint + "start", semester = requestSemester) { text ->
val semester = requestSemester ?: data.currentSemester
val doc = Jsoup.parse(text)
val subjects = doc.select("#student_grades tbody").firstOrNull()?.children() val subjects = doc.select("#student_grades tbody").firstOrNull()?.children()
subjects?.forEach { subjectElement -> subjects?.forEach { subjectElement ->
@ -80,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)
@ -99,20 +114,20 @@ 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,
data.currentSemester, description = description,
teacher.id, comment = null,
subject.id semester = semester,
).apply { teacherId = teacher.id,
type = gradeType subjectId = subject.id
} )
data.gradeList.add(gradeObject) data.gradeList.add(gradeObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
@ -129,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,
data.currentSemester, 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(
@ -162,23 +177,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,
data.currentSemester, 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(
@ -201,12 +216,17 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
TYPE_SEMESTER1_FINAL, TYPE_SEMESTER1_FINAL,
TYPE_SEMESTER2_FINAL TYPE_SEMESTER2_FINAL
).map { ).map {
DataRemoveModel.Grades.semesterWithType(data.currentSemester, it) DataRemoveModel.Grades.semesterWithType(semester, it)
}) })
} }
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_GRADES, SYNC_ALWAYS) if (profile.empty && requestSemester == 1 && data.currentSemester == 2) {
onSuccess() requestSemester = null
getGrades()
} else {
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_GRADES, SYNC_ALWAYS)
onSuccess(ENDPOINT_EDUDZIENNIK_WEB_GRADES)
}
} }
}} } ?: onSuccess(ENDPOINT_EDUDZIENNIK_WEB_GRADES) }
} }

View File

@ -12,14 +12,16 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_HOMEWORK import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_HOMEWORK
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.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Event 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.get import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class EdudziennikWebHomework(override val data: DataEdudziennik, class EdudziennikWebHomework(override val data: DataEdudziennik,
val onSuccess: () -> Unit) : EdudziennikWeb(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : EdudziennikWeb(data, lastSync) {
companion object { companion object {
const val TAG = "EdudziennikWebHomework" const val TAG = "EdudziennikWebHomework"
} }
@ -78,7 +80,7 @@ class EdudziennikWebHomework(override val data: DataEdudziennik,
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_HOMEWORK)) data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_HOMEWORK))
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_HOMEWORK, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_HOMEWORK, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_EDUDZIENNIK_WEB_HOMEWORK)
} }
} ?: onSuccess() } } ?: onSuccess(ENDPOINT_EDUDZIENNIK_WEB_HOMEWORK) }
} }

View File

@ -7,13 +7,15 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.web
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.LuckyNumber import pl.szczodrzynski.edziennik.data.db.entity.LuckyNumber
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.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class EdudziennikWebLuckyNumber(override val data: DataEdudziennik, class EdudziennikWebLuckyNumber(override val data: DataEdudziennik,
val onSuccess: () -> Unit) : EdudziennikWeb(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : EdudziennikWeb(data, lastSync) {
companion object { companion object {
private const val TAG = "EdudziennikWebLuckyNumber" private const val TAG = "EdudziennikWebLuckyNumber"
} }
@ -39,7 +41,7 @@ class EdudziennikWebLuckyNumber(override val data: DataEdudziennik,
} }
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER)
} }
} ?: onSuccess() } } ?: onSuccess(ENDPOINT_EDUDZIENNIK_WEB_LUCKY_NUMBER) }
} }

View File

@ -10,14 +10,16 @@ import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_NOTE_ID
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_NOTES import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_NOTES
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Notice import pl.szczodrzynski.edziennik.data.db.entity.Notice
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.get import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class EdudziennikWebNotes(override val data: DataEdudziennik, class EdudziennikWebNotes(override val data: DataEdudziennik,
val onSuccess: () -> Unit) : EdudziennikWeb(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : EdudziennikWeb(data, lastSync) {
companion object { companion object {
const val TAG = "EdudziennikWebNotes" const val TAG = "EdudziennikWebNotes"
} }
@ -42,7 +44,7 @@ class EdudziennikWebNotes(override val data: DataEdudziennik,
profileId, profileId,
id, id,
description, description,
data.currentSemester, profile.currentSemester,
Notice.TYPE_NEUTRAL, Notice.TYPE_NEUTRAL,
teacher.id teacher.id
) )
@ -59,7 +61,7 @@ class EdudziennikWebNotes(override val data: DataEdudziennik,
} }
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_NOTES, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_NOTES, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_EDUDZIENNIK_WEB_NOTES)
} }
} ?: onSuccess() } } ?: onSuccess(ENDPOINT_EDUDZIENNIK_WEB_NOTES) }
} }

View File

@ -18,7 +18,9 @@ import pl.szczodrzynski.edziennik.firstLettersName
import pl.szczodrzynski.edziennik.get import pl.szczodrzynski.edziennik.get
class EdudziennikWebStart(override val data: DataEdudziennik, class EdudziennikWebStart(override val data: DataEdudziennik,
val onSuccess: () -> Unit) : EdudziennikWeb(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : EdudziennikWeb(data, lastSync) {
companion object { companion object {
private const val TAG = "EdudziennikWebStart" private const val TAG = "EdudziennikWebStart"
} }
@ -29,7 +31,7 @@ class EdudziennikWebStart(override val data: DataEdudziennik,
getSubjects(text) getSubjects(text)
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_START, MONTH) data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_START, MONTH)
onSuccess() onSuccess(ENDPOINT_EDUDZIENNIK_WEB_START)
} }
} }

View File

@ -12,7 +12,9 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.Edudzienni
import pl.szczodrzynski.edziennik.get import pl.szczodrzynski.edziennik.get
class EdudziennikWebTeachers(override val data: DataEdudziennik, class EdudziennikWebTeachers(override val data: DataEdudziennik,
val onSuccess: () -> Unit) : EdudziennikWeb(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : EdudziennikWeb(data, lastSync) {
companion object { companion object {
private const val TAG = "EdudziennikWebTeachers" private const val TAG = "EdudziennikWebTeachers"
} }
@ -26,7 +28,7 @@ class EdudziennikWebTeachers(override val data: DataEdudziennik,
} }
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_TEACHERS, MONTH) data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_TEACHERS, MONTH)
onSuccess() onSuccess(ENDPOINT_EDUDZIENNIK_WEB_TEACHERS)
} }
} }
} }

View File

@ -11,10 +11,10 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_TIMETABLE import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_TIMETABLE
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.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.Lesson
import pl.szczodrzynski.edziennik.data.db.entity.LessonRange import pl.szczodrzynski.edziennik.data.db.entity.LessonRange
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Lesson import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.get import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.getString import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.singleOrNull import pl.szczodrzynski.edziennik.singleOrNull
@ -24,7 +24,9 @@ import pl.szczodrzynski.edziennik.utils.models.Time
import pl.szczodrzynski.edziennik.utils.models.Week import pl.szczodrzynski.edziennik.utils.models.Week
class EdudziennikWebTimetable(override val data: DataEdudziennik, class EdudziennikWebTimetable(override val data: DataEdudziennik,
val onSuccess: () -> Unit) : EdudziennikWeb(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : EdudziennikWeb(data, lastSync) {
companion object { companion object {
private const val TAG = "EdudziennikWebTimetable" private const val TAG = "EdudziennikWebTimetable"
} }
@ -142,7 +144,7 @@ class EdudziennikWebTimetable(override val data: DataEdudziennik,
data.toRemove.add(DataRemoveModel.Timetable.between(weekStart, weekEnd)) data.toRemove.add(DataRemoveModel.Timetable.between(weekStart, weekEnd))
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_TIMETABLE, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_TIMETABLE, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_EDUDZIENNIK_WEB_TIMETABLE)
} }
} ?: onSuccess() } } ?: onSuccess(ENDPOINT_EDUDZIENNIK_WEB_TIMETABLE) }
} }

View File

@ -23,7 +23,7 @@ class EdudziennikFirstLogin(val data: DataEdudziennik, val onSuccess: () -> Unit
private const val TAG = "EdudziennikFirstLogin" private const val TAG = "EdudziennikFirstLogin"
} }
private val web = EdudziennikWeb(data) private val web = EdudziennikWeb(data, null)
private val profileList = mutableListOf<Profile>() private val profileList = mutableListOf<Profile>()
init { init {

View File

@ -13,7 +13,6 @@ import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.getUnixDate import pl.szczodrzynski.edziennik.getUnixDate
import pl.szczodrzynski.edziennik.isNotNullNorEmpty import pl.szczodrzynski.edziennik.isNotNullNorEmpty
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
import pl.szczodrzynski.edziennik.utils.models.Date
class EdudziennikLoginWeb(val data: DataEdudziennik, val onSuccess: () -> Unit) { class EdudziennikLoginWeb(val data: DataEdudziennik, val onSuccess: () -> Unit) {
companion object { companion object {
@ -25,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()
} }
@ -60,9 +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"]
val semester = cookies.firstOrNull { it.name() == "semester" }?.value()?.toIntOrNull()
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)
@ -72,14 +70,6 @@ class EdudziennikLoginWeb(val data: DataEdudziennik, val onSuccess: () -> Unit)
} }
data.webSessionId = sessionId data.webSessionId = sessionId
if (data.profile != null && semester != null) {
data.currentSemester = semester
if (semester == 2 && data.profile.dateSemester2Start > Date.getToday())
data.profile.dateSemester2Start = Date.getToday().stepForward(0, 0, -1)
}
data.webSessionIdExpiryTime = response.getUnixDate() + 45 * 60 /* 45 min */ data.webSessionIdExpiryTime = response.getUnixDate() + 45 * 60 /* 45 min */
onSuccess() onSuccess()
} }

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

@ -17,12 +17,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login.IdziennikLo
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.data.api.interfaces.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.full.AnnouncementFull
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.Message
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
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.MessageFull
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
@ -43,9 +43,7 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
private fun completed() { private fun completed() {
data.saveData() data.saveData()
data.notify { callback.onCompleted()
callback.onCompleted()
}
} }
/* _______ _ _ _ _ _ /* _______ _ _ _ _ _
@ -56,9 +54,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()
} }

View File

@ -18,7 +18,7 @@ import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils
import java.net.HttpURLConnection import java.net.HttpURLConnection
open class IdziennikApi(open val data: DataIdziennik) { open class IdziennikApi(open val data: DataIdziennik, open val lastSync: Long?) {
companion object { companion object {
const val TAG = "IdziennikApi" const val TAG = "IdziennikApi"
} }

View File

@ -30,60 +30,62 @@ class IdziennikData(val data: DataIdziennik, val onSuccess: () -> Unit) {
onSuccess() onSuccess()
return return
} }
useEndpoint(data.targetEndpointIds.removeAt(0)) { val id = data.targetEndpointIds.firstKey()
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: () -> Unit) { private fun useEndpoint(endpointId: Int, lastSync: Long?, onSuccess: (endpointId: Int) -> Unit) {
Utils.d(TAG, "Using endpoint $endpointId") Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync")
when (endpointId) { when (endpointId) {
ENDPOINT_IDZIENNIK_WEB_TIMETABLE -> { ENDPOINT_IDZIENNIK_WEB_TIMETABLE -> {
data.startProgress(R.string.edziennik_progress_endpoint_timetable) data.startProgress(R.string.edziennik_progress_endpoint_timetable)
IdziennikWebTimetable(data, onSuccess) IdziennikWebTimetable(data, lastSync, onSuccess)
} }
ENDPOINT_IDZIENNIK_WEB_GRADES -> { ENDPOINT_IDZIENNIK_WEB_GRADES -> {
data.startProgress(R.string.edziennik_progress_endpoint_grades) data.startProgress(R.string.edziennik_progress_endpoint_grades)
IdziennikWebGrades(data, onSuccess) IdziennikWebGrades(data, lastSync, onSuccess)
} }
ENDPOINT_IDZIENNIK_WEB_PROPOSED_GRADES -> { ENDPOINT_IDZIENNIK_WEB_PROPOSED_GRADES -> {
data.startProgress(R.string.edziennik_progress_endpoint_proposed_grades) data.startProgress(R.string.edziennik_progress_endpoint_proposed_grades)
IdziennikWebProposedGrades(data, onSuccess) IdziennikWebProposedGrades(data, lastSync, onSuccess)
} }
ENDPOINT_IDZIENNIK_WEB_EXAMS -> { ENDPOINT_IDZIENNIK_WEB_EXAMS -> {
data.startProgress(R.string.edziennik_progress_endpoint_exams) data.startProgress(R.string.edziennik_progress_endpoint_exams)
IdziennikWebExams(data, onSuccess) IdziennikWebExams(data, lastSync, onSuccess)
} }
ENDPOINT_IDZIENNIK_WEB_HOMEWORK -> { ENDPOINT_IDZIENNIK_WEB_HOMEWORK -> {
data.startProgress(R.string.edziennik_progress_endpoint_homework) data.startProgress(R.string.edziennik_progress_endpoint_homework)
IdziennikWebHomework(data, onSuccess) IdziennikWebHomework(data, lastSync, onSuccess)
} }
ENDPOINT_IDZIENNIK_WEB_NOTICES -> { ENDPOINT_IDZIENNIK_WEB_NOTICES -> {
data.startProgress(R.string.edziennik_progress_endpoint_notices) data.startProgress(R.string.edziennik_progress_endpoint_notices)
IdziennikWebNotices(data, onSuccess) IdziennikWebNotices(data, lastSync, onSuccess)
} }
ENDPOINT_IDZIENNIK_WEB_ANNOUNCEMENTS -> { ENDPOINT_IDZIENNIK_WEB_ANNOUNCEMENTS -> {
data.startProgress(R.string.edziennik_progress_endpoint_announcements) data.startProgress(R.string.edziennik_progress_endpoint_announcements)
IdziennikWebAnnouncements(data, onSuccess) IdziennikWebAnnouncements(data, lastSync, onSuccess)
} }
ENDPOINT_IDZIENNIK_WEB_ATTENDANCE -> { ENDPOINT_IDZIENNIK_WEB_ATTENDANCE -> {
data.startProgress(R.string.edziennik_progress_endpoint_attendance) data.startProgress(R.string.edziennik_progress_endpoint_attendance)
IdziennikWebAttendance(data, onSuccess) IdziennikWebAttendance(data, lastSync, onSuccess)
} }
ENDPOINT_IDZIENNIK_API_CURRENT_REGISTER -> { ENDPOINT_IDZIENNIK_API_CURRENT_REGISTER -> {
data.startProgress(R.string.edziennik_progress_endpoint_lucky_number) data.startProgress(R.string.edziennik_progress_endpoint_lucky_number)
IdziennikApiCurrentRegister(data, onSuccess) IdziennikApiCurrentRegister(data, lastSync, onSuccess)
} }
ENDPOINT_IDZIENNIK_API_MESSAGES_INBOX -> { ENDPOINT_IDZIENNIK_API_MESSAGES_INBOX -> {
data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox) data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox)
IdziennikApiMessagesInbox(data, onSuccess) IdziennikApiMessagesInbox(data, lastSync, onSuccess)
} }
ENDPOINT_IDZIENNIK_API_MESSAGES_SENT -> { ENDPOINT_IDZIENNIK_API_MESSAGES_SENT -> {
data.startProgress(R.string.edziennik_progress_endpoint_messages_outbox) data.startProgress(R.string.edziennik_progress_endpoint_messages_outbox)
IdziennikApiMessagesSent(data, onSuccess) IdziennikApiMessagesSent(data, lastSync, onSuccess)
} }
else -> onSuccess() else -> onSuccess(endpointId)
} }
} }
} }

View File

@ -13,13 +13,14 @@ 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
import java.net.HttpURLConnection.HTTP_INTERNAL_ERROR import java.net.HttpURLConnection.HTTP_INTERNAL_ERROR
import java.net.HttpURLConnection.HTTP_UNAUTHORIZED import java.net.HttpURLConnection.HTTP_UNAUTHORIZED
open class IdziennikWeb(open val data: DataIdziennik) { open class IdziennikWeb(open val data: DataIdziennik, open val lastSync: Long?) {
companion object { companion object {
const val TAG = "IdziennikWeb" const val TAG = "IdziennikWeb"
} }
@ -48,6 +49,17 @@ open class IdziennikWeb(open val data: DataIdziennik) {
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) {
.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) {
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()

View File

@ -19,7 +19,9 @@ import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
class IdziennikApiCurrentRegister(override val data: DataIdziennik, class IdziennikApiCurrentRegister(override val data: DataIdziennik,
val onSuccess: () -> Unit) : IdziennikApi(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : IdziennikApi(data, lastSync) {
companion object { companion object {
private const val TAG = "IdziennikApiCurrentRegister" private const val TAG = "IdziennikApiCurrentRegister"
} }
@ -27,7 +29,7 @@ class IdziennikApiCurrentRegister(override val data: DataIdziennik,
init { init {
apiGet(TAG, IDZIENNIK_API_CURRENT_REGISTER) { json -> apiGet(TAG, IDZIENNIK_API_CURRENT_REGISTER) { json ->
if (json !is JsonObject) { if (json !is JsonObject) {
onSuccess() onSuccess(ENDPOINT_IDZIENNIK_API_CURRENT_REGISTER)
return@apiGet return@apiGet
} }
@ -67,7 +69,7 @@ class IdziennikApiCurrentRegister(override val data: DataIdziennik,
val luckyNumberObject = LuckyNumber( val luckyNumberObject = LuckyNumber(
data.profileId, data.profileId,
Date.getToday(), luckyNumberDate,
luckyNumber luckyNumber
) )
@ -85,7 +87,7 @@ class IdziennikApiCurrentRegister(override val data: DataIdziennik,
data.setSyncNext(ENDPOINT_IDZIENNIK_API_CURRENT_REGISTER, syncAt = nextSync) data.setSyncNext(ENDPOINT_IDZIENNIK_API_CURRENT_REGISTER, syncAt = nextSync)
onSuccess() onSuccess(ENDPOINT_IDZIENNIK_API_CURRENT_REGISTER)
} }
} }
} }

View File

@ -10,20 +10,18 @@ import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_API_MESSAGES_INBOX
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.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.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.*
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_DELETED import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_DELETED
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.getBoolean import pl.szczodrzynski.edziennik.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
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class IdziennikApiMessagesInbox(override val data: DataIdziennik, class IdziennikApiMessagesInbox(override val data: DataIdziennik,
val onSuccess: () -> Unit) : IdziennikApi(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : IdziennikApi(data, lastSync) {
companion object { companion object {
private const val TAG = "IdziennikApiMessagesInbox" private const val TAG = "IdziennikApiMessagesInbox"
} }
@ -31,7 +29,7 @@ class IdziennikApiMessagesInbox(override val data: DataIdziennik,
init { init {
apiGet(TAG, IDZIENNIK_API_MESSAGES_INBOX) { json -> apiGet(TAG, IDZIENNIK_API_MESSAGES_INBOX) { json ->
if (json !is JsonArray) { if (json !is JsonArray) {
onSuccess() onSuccess(ENDPOINT_IDZIENNIK_API_MESSAGES_INBOX)
return@apiGet return@apiGet
} }
@ -96,7 +94,7 @@ class IdziennikApiMessagesInbox(override val data: DataIdziennik,
} }
data.setSyncNext(ENDPOINT_IDZIENNIK_API_MESSAGES_INBOX, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_IDZIENNIK_API_MESSAGES_INBOX, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_IDZIENNIK_API_MESSAGES_INBOX)
} }
} }
} }

View File

@ -20,7 +20,9 @@ import pl.szczodrzynski.edziennik.utils.Utils.crc32
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class IdziennikApiMessagesSent(override val data: DataIdziennik, class IdziennikApiMessagesSent(override val data: DataIdziennik,
val onSuccess: () -> Unit) : IdziennikApi(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : IdziennikApi(data, lastSync) {
companion object { companion object {
private const val TAG = "IdziennikApiMessagesSent" private const val TAG = "IdziennikApiMessagesSent"
} }
@ -28,7 +30,7 @@ class IdziennikApiMessagesSent(override val data: DataIdziennik,
init { init {
apiGet(TAG, IDZIENNIK_API_MESSAGES_SENT) { json -> apiGet(TAG, IDZIENNIK_API_MESSAGES_SENT) { json ->
if (json !is JsonArray) { if (json !is JsonArray) {
onSuccess() onSuccess(ENDPOINT_IDZIENNIK_API_MESSAGES_SENT)
return@apiGet return@apiGet
} }
@ -79,7 +81,7 @@ class IdziennikApiMessagesSent(override val data: DataIdziennik,
} }
data.setSyncNext(ENDPOINT_IDZIENNIK_API_MESSAGES_SENT, DAY, DRAWER_ITEM_MESSAGES) data.setSyncNext(ENDPOINT_IDZIENNIK_API_MESSAGES_SENT, DAY, DRAWER_ITEM_MESSAGES)
onSuccess() onSuccess(ENDPOINT_IDZIENNIK_API_MESSAGES_SENT)
} }
} }
} }

View File

@ -13,13 +13,17 @@ 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.Announcement import pl.szczodrzynski.edziennik.data.db.entity.Announcement
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
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,
val onSuccess: () -> Unit) : IdziennikWeb(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : IdziennikWeb(data, lastSync) {
companion object { companion object {
private const val TAG = "IdziennikWebAnnouncements" private const val TAG = "IdziennikWebAnnouncements"
} }
@ -41,11 +45,11 @@ 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,
@ -69,7 +73,7 @@ class IdziennikWebAnnouncements(override val data: DataIdziennik,
} }
data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_ANNOUNCEMENTS, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_ANNOUNCEMENTS, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_IDZIENNIK_WEB_ANNOUNCEMENTS)
} }
} }
} }

View File

@ -4,23 +4,25 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
import pl.szczodrzynski.edziennik.crc16
import pl.szczodrzynski.edziennik.data.api.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA import pl.szczodrzynski.edziennik.data.api.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_ATTENDANCE import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_ATTENDANCE
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.ENDPOINT_IDZIENNIK_WEB_ATTENDANCE import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNIK_WEB_ATTENDANCE
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.crc16
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Attendance import pl.szczodrzynski.edziennik.data.db.entity.Attendance
import pl.szczodrzynski.edziennik.data.db.entity.Attendance.* import pl.szczodrzynski.edziennik.data.db.entity.Attendance.*
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.getJsonObject import pl.szczodrzynski.edziennik.getJsonObject
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
class IdziennikWebAttendance(override val data: DataIdziennik, class IdziennikWebAttendance(override val data: DataIdziennik,
val onSuccess: () -> Unit) : IdziennikWeb(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : IdziennikWeb(data, lastSync) {
companion object { companion object {
private const val TAG = "IdziennikWebAttendance" private const val TAG = "IdziennikWebAttendance"
} }
@ -137,7 +139,7 @@ class IdziennikWebAttendance(override val data: DataIdziennik,
getAttendance() getAttendance()
} else { } else {
data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_ATTENDANCE, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_ATTENDANCE, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_IDZIENNIK_WEB_ATTENDANCE)
} }
} }
} }

View File

@ -13,13 +13,16 @@ 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.api.models.DataRemoveModel import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Event import pl.szczodrzynski.edziennik.data.db.entity.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.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,
val onSuccess: () -> Unit) : IdziennikWeb(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : IdziennikWeb(data, lastSync) {
companion object { companion object {
private const val TAG = "IdziennikWebExams" private const val TAG = "IdziennikWebExams"
} }
@ -68,9 +71,12 @@ class IdziennikWebExams(override val data: DataIdziennik,
val lessonList = data.db.timetableDao().getForDateNow(profileId, examDate) val lessonList = data.db.timetableDao().getForDateNow(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(
@ -116,7 +122,7 @@ class IdziennikWebExams(override val data: DataIdziennik,
data.toRemove.add(DataRemoveModel.Events.futureExceptType(Event.TYPE_HOMEWORK)) data.toRemove.add(DataRemoveModel.Events.futureExceptType(Event.TYPE_HOMEWORK))
data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_EXAMS, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_EXAMS, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_IDZIENNIK_WEB_EXAMS)
} }
} }
} }

View File

@ -14,10 +14,12 @@ import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils
import java.io.File import java.io.File
class IdziennikWebGetAttachment( class IdziennikWebGetAttachment(override val data: DataIdziennik,
override val data: DataIdziennik, val message: Message, val attachmentId: Long, val message: Message,
val attachmentName: String, val onSuccess: () -> Unit val attachmentId: Long,
) : IdziennikWeb(data) { val attachmentName: String,
val onSuccess: () -> Unit
) : IdziennikWeb(data, null) {
companion object { companion object {
const val TAG = "IdziennikWebGetAttachment" const val TAG = "IdziennikWebGetAttachment"
} }

View File

@ -12,16 +12,15 @@ 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.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT
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
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class IdziennikWebGetMessage( class IdziennikWebGetMessage(override val data: DataIdziennik,
override val data: DataIdziennik, private val message: MessageFull,
private val message: MessageFull, val onSuccess: () -> Unit
val onSuccess: () -> Unit ) : IdziennikWeb(data, null) {
) : IdziennikWeb(data) {
companion object { companion object {
const val TAG = "IdziennikWebGetMessage" const val TAG = "IdziennikWebGetMessage"
} }

View File

@ -16,8 +16,9 @@ import pl.szczodrzynski.edziennik.data.api.events.RecipientListGetEvent
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
class IdziennikWebGetRecipientList( class IdziennikWebGetRecipientList(override val data: DataIdziennik,
override val data: DataIdziennik, val onSuccess: () -> Unit) : IdziennikWeb(data) { val onSuccess: () -> Unit
) : IdziennikWeb(data, null) {
companion object { companion object {
private const val TAG = "IdziennikWebGetRecipientList" private const val TAG = "IdziennikWebGetRecipientList"
} }

View File

@ -13,13 +13,16 @@ 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.api.models.DataRemoveModel import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Grade import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.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.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class IdziennikWebGrades(override val data: DataIdziennik, class IdziennikWebGrades(override val data: DataIdziennik,
val onSuccess: () -> Unit) : IdziennikWeb(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : IdziennikWeb(data, lastSync) {
companion object { companion object {
private const val TAG = "IdziennikWebGrades" private const val TAG = "IdziennikWebGrades"
} }
@ -61,17 +64,19 @@ class IdziennikWebGrades(override val data: DataIdziennik,
} }
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)
when (grade.getInt("Typ")) { when (grade.getInt("Typ")) {
0 -> { 0 -> {
@ -89,18 +94,26 @@ class IdziennikWebGrades(override val data: DataIdziennik,
count += weight count += weight
} }
val historyColor = historyItem.getString("Kolor") ?: ""
colorInt = 0xff2196f3.toInt()
if (historyColor.isNotEmpty()) {
colorInt = Color.parseColor("#$historyColor")
}
val historyObject = Grade( val historyObject = Grade(
profileId, profileId = profileId,
gradeObject.id * -1, id = gradeObject.id * -1,
historyItem.get("Kategoria").asString, name = historyItem.getString("Ocena") ?: "",
Color.parseColor("#" + historyItem.get("Kolor").asString), type = TYPE_NORMAL,
historyItem.get("Uzasadnienie").asString, value = value,
historyItem.get("Ocena").asString, weight = if (value > 0f && countToTheAverage) weight * -1f else 0f,
value, color = colorInt,
if (value > 0f && countToTheAverage) weight * -1f else 0f, category = historyItem.getString("Kategoria"),
historyItem.get("Semestr").asInt, description = historyItem.getString("Uzasadnienie"),
teacher.id, comment = null,
subject.id) semester = historyItem.getInt("Semestr") ?: 1,
teacherId = teacher.id,
subjectId = subject.id)
historyObject.parentId = gradeObject.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()
@ -157,7 +170,7 @@ class IdziennikWebGrades(override val data: DataIdziennik,
DataRemoveModel.Grades.semesterWithType(profile.currentSemester, it) DataRemoveModel.Grades.semesterWithType(profile.currentSemester, it)
}) })
data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_GRADES, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_GRADES, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_IDZIENNIK_WEB_GRADES)
} }
} ?: onSuccess() } } ?: onSuccess(ENDPOINT_IDZIENNIK_WEB_GRADES) }
} }

View File

@ -13,13 +13,15 @@ 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.api.models.DataRemoveModel import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Event import pl.szczodrzynski.edziennik.data.db.entity.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.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class IdziennikWebHomework(override val data: DataIdziennik, class IdziennikWebHomework(override val data: DataIdziennik,
val onSuccess: () -> Unit) : IdziennikWeb(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : IdziennikWeb(data, lastSync) {
companion object { companion object {
private const val TAG = "IdziennikWebHomework" private const val TAG = "IdziennikWebHomework"
} }
@ -92,7 +94,7 @@ class IdziennikWebHomework(override val data: DataIdziennik,
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_HOMEWORK)) data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_HOMEWORK))
data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_HOMEWORK, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_HOMEWORK, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_IDZIENNIK_WEB_HOMEWORK)
} }
} }
} }

View File

@ -4,22 +4,24 @@
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
import pl.szczodrzynski.edziennik.crc16
import pl.szczodrzynski.edziennik.data.api.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA import pl.szczodrzynski.edziennik.data.api.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_NOTICES import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_NOTICES
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.ENDPOINT_IDZIENNIK_WEB_NOTICES import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNIK_WEB_NOTICES
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.crc16
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
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.*
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.getJsonObject import pl.szczodrzynski.edziennik.getJsonObject
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,
val onSuccess: () -> Unit) : IdziennikWeb(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : IdziennikWeb(data, lastSync) {
companion object { companion object {
private const val TAG = "IdziennikWebNotices" private const val TAG = "IdziennikWebNotices"
} }
@ -69,7 +71,7 @@ class IdziennikWebNotices(override val data: DataIdziennik,
} }
data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_NOTICES, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_NOTICES, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_IDZIENNIK_WEB_NOTICES)
} }
} }
} }

View File

@ -12,18 +12,20 @@ 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.api.models.DataRemoveModel import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Grade import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_SEMESTER1_PROPOSED import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_PROPOSED
import pl.szczodrzynski.edziennik.data.db.entity.Grade.TYPE_YEAR_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.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 import pl.szczodrzynski.edziennik.utils.Utils.getWordGradeValue
class IdziennikWebProposedGrades(override val data: DataIdziennik, class IdziennikWebProposedGrades(override val data: DataIdziennik,
val onSuccess: () -> Unit) : IdziennikWeb(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : IdziennikWeb(data, lastSync) {
companion object { companion object {
private const val TAG = "IdziennikWebProposedGrades" private const val TAG = "IdziennikWebProposedGrades"
} }
@ -52,20 +54,20 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
if (semester1Proposed != "") { if (semester1Proposed != "") {
val gradeObject = Grade( val gradeObject = Grade(
profileId, profileId = profileId,
semester1Id, id = semester1Id,
"", name = semester1Value.toString(),
-1, type = TYPE_SEMESTER1_PROPOSED,
"", value = semester1Value.toFloat(),
semester1Value.toString(), weight = 0f,
semester1Value.toFloat(), color = -1,
0f, category = null,
1, description = null,
-1, comment = null,
subjectObject.id semester = 1,
).apply { teacherId = -1,
type = TYPE_SEMESTER1_PROPOSED subjectId = subjectObject.id
} )
data.gradeList.add(gradeObject) data.gradeList.add(gradeObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
@ -80,20 +82,25 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
if (semester2Proposed != "") { if (semester2Proposed != "") {
val gradeObject = Grade( val gradeObject = Grade(
profileId, profileId = profileId,
semester2Id, id = semester2Id,
"", name = semester2Value.toString(),
-1, type = TYPE_YEAR_PROPOSED,
"", value = semester2Value.toFloat(),
semester2Value.toString(), weight = 0f,
semester2Value.toFloat(), color = -1,
0f, category = null,
2, description = null,
-1, comment = null,
subjectObject.id semester = 2,
).apply { teacherId = -1,
type = TYPE_YEAR_PROPOSED subjectId = subjectObject.id
} )
val addedDate = if (data.profile.empty)
data.profile.dateSemester1Start.inMillis
else
System.currentTimeMillis()
data.gradeList.add(gradeObject) data.gradeList.add(gradeObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
@ -102,7 +109,7 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
gradeObject.id, gradeObject.id,
profile.empty, profile.empty,
profile.empty, profile.empty,
System.currentTimeMillis() addedDate
)) ))
} }
} }
@ -111,7 +118,7 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik,
DataRemoveModel.Grades.semesterWithType(profile.currentSemester, it) DataRemoveModel.Grades.semesterWithType(profile.currentSemester, it)
}) })
data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_PROPOSED_GRADES, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_PROPOSED_GRADES, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_IDZIENNIK_WEB_PROPOSED_GRADES)
} }
} ?: onSuccess() } } ?: onSuccess(ENDPOINT_IDZIENNIK_WEB_PROPOSED_GRADES) }
} }

View File

@ -18,13 +18,12 @@ import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import java.util.* import java.util.*
class IdziennikWebSendMessage( class IdziennikWebSendMessage(override val data: DataIdziennik,
override val data: DataIdziennik, val recipients: List<Teacher>,
val recipients: List<Teacher>, val subject: String,
val subject: String, val text: String,
val text: String, val onSuccess: () -> Unit
val onSuccess: () -> Unit ) : IdziennikWeb(data, null) {
) : IdziennikWeb(data) {
companion object { companion object {
private const val TAG = "IdziennikWebSendMessage" private const val TAG = "IdziennikWebSendMessage"
} }
@ -57,7 +56,7 @@ class IdziennikWebSendMessage(
return@webApiGet return@webApiGet
} }
IdziennikApiMessagesSent(data) { IdziennikApiMessagesSent(data, null) {
val message = data.messageIgnoreList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject } val message = data.messageIgnoreList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject }
val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id } val 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, metadata?.addedDate)

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

@ -13,17 +13,19 @@ 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.api.models.DataRemoveModel import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.entity.Lesson
import pl.szczodrzynski.edziennik.data.db.entity.LessonRange import pl.szczodrzynski.edziennik.data.db.entity.LessonRange
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Lesson import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
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
import pl.szczodrzynski.edziennik.utils.models.Week import pl.szczodrzynski.edziennik.utils.models.Week
class IdziennikWebTimetable(override val data: DataIdziennik, class IdziennikWebTimetable(override val data: DataIdziennik,
val onSuccess: () -> Unit) : IdziennikWeb(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : IdziennikWeb(data, lastSync) {
companion object { companion object {
private const val TAG = "IdziennikWebTimetable" private const val TAG = "IdziennikWebTimetable"
} }
@ -187,7 +189,7 @@ class IdziennikWebTimetable(override val data: DataIdziennik,
data.toRemove.add(DataRemoveModel.Timetable.between(weekStart, weekEnd)) data.toRemove.add(DataRemoveModel.Timetable.between(weekStart, weekEnd))
data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_TIMETABLE, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_IDZIENNIK_WEB_TIMETABLE, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_IDZIENNIK_WEB_TIMETABLE)
} }
}} }}
} }

View File

@ -25,7 +25,7 @@ class IdziennikFirstLogin(val data: DataIdziennik, val onSuccess: () -> Unit) {
private const val TAG = "IdziennikFirstLogin" private const val TAG = "IdziennikFirstLogin"
} }
private val web = IdziennikWeb(data) private val web = IdziennikWeb(data, null)
private val profileList = mutableListOf<Profile>() private val profileList = mutableListOf<Profile>()
init { init {

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,47 @@ 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
}
data.profile?.let { profile ->
Regexes.IDZIENNIK_WEB_LUCKY_NUMBER.find(text)?.also {
val number = it[1].toIntOrNull() ?: return@also
val luckyNumberObject = LuckyNumber(
data.profileId,
Date.getToday(),
number
)
data.luckyNumberList.add(luckyNumberObject)
data.metadataList.add(
Metadata(
profile.id,
Metadata.TYPE_LUCKY_NUMBER,
luckyNumberObject.date.value.toLong(),
true,
profile.empty,
System.currentTimeMillis()
))
}
}
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()
))
} }
} }
@ -191,6 +178,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; }
/* _____ _ /* _____ _
/ ____| (_) / ____| (_)
| (___ _ _ _ __ ___ _ __ __ _ _ __ _ | (___ _ _ _ __ ___ _ __ __ _ _ __ _

View File

@ -19,12 +19,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLogin
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.data.api.interfaces.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.full.AnnouncementFull
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.Message
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
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.MessageFull
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
@ -45,9 +45,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
private fun completed() { private fun completed() {
data.saveData() data.saveData()
data.notify { callback.onCompleted()
callback.onCompleted()
}
} }
/* _______ _ _ _ _ _ /* _______ _ _ _ _ _
@ -58,9 +56,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()
} }
@ -182,6 +180,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()
@ -200,7 +199,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()
@ -209,6 +207,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

@ -53,8 +53,6 @@ const val ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK = 2030
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 +64,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 +116,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 +227,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

@ -15,7 +15,7 @@ import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
import java.net.HttpURLConnection.* import java.net.HttpURLConnection.*
open class LibrusApi(open val data: DataLibrus) { open class LibrusApi(open val data: DataLibrus, open val lastSync: Long?) {
companion object { companion object {
private const val TAG = "LibrusApi" private const val TAG = "LibrusApi"
} }
@ -44,9 +44,12 @@ open class LibrusApi(open val data: DataLibrus) {
.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) {
@ -63,6 +66,9 @@ open class LibrusApi(open val data: DataLibrus) {
"NotesIsNotActive" -> ERROR_LIBRUS_API_NOTES_NOT_ACTIVE "NotesIsNotActive" -> ERROR_LIBRUS_API_NOTES_NOT_ACTIVE
"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
"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) {
@ -114,6 +120,8 @@ open class LibrusApi(open val data: DataLibrus) {
.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

@ -31,158 +31,163 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
onSuccess() onSuccess()
return return
} }
useEndpoint(data.targetEndpointIds.removeAt(0)) { val id = data.targetEndpointIds.firstKey()
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: () -> Unit) { private fun useEndpoint(endpointId: Int, lastSync: Long?, onSuccess: (endpointId: Int) -> Unit) {
Utils.d(TAG, "Using endpoint $endpointId") Utils.d(TAG, "Using endpoint $endpointId. Last sync time = $lastSync")
when (endpointId) { when (endpointId) {
/** /**
* API * API
*/ */
ENDPOINT_LIBRUS_API_ME -> { ENDPOINT_LIBRUS_API_ME -> {
data.startProgress(R.string.edziennik_progress_endpoint_student_info) data.startProgress(R.string.edziennik_progress_endpoint_student_info)
LibrusApiMe(data, onSuccess) LibrusApiMe(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_SCHOOLS -> { ENDPOINT_LIBRUS_API_SCHOOLS -> {
data.startProgress(R.string.edziennik_progress_endpoint_school_info) data.startProgress(R.string.edziennik_progress_endpoint_school_info)
LibrusApiSchools(data, onSuccess) LibrusApiSchools(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_CLASSES -> { ENDPOINT_LIBRUS_API_CLASSES -> {
data.startProgress(R.string.edziennik_progress_endpoint_classes) data.startProgress(R.string.edziennik_progress_endpoint_classes)
LibrusApiClasses(data, onSuccess) LibrusApiClasses(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_VIRTUAL_CLASSES -> { ENDPOINT_LIBRUS_API_VIRTUAL_CLASSES -> {
data.startProgress(R.string.edziennik_progress_endpoint_teams) data.startProgress(R.string.edziennik_progress_endpoint_teams)
LibrusApiVirtualClasses(data, onSuccess) LibrusApiVirtualClasses(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_UNITS -> { ENDPOINT_LIBRUS_API_UNITS -> {
data.startProgress(R.string.edziennik_progress_endpoint_units) data.startProgress(R.string.edziennik_progress_endpoint_units)
LibrusApiUnits(data, onSuccess) LibrusApiUnits(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_USERS -> { ENDPOINT_LIBRUS_API_USERS -> {
data.startProgress(R.string.edziennik_progress_endpoint_teachers) data.startProgress(R.string.edziennik_progress_endpoint_teachers)
LibrusApiUsers(data, onSuccess) LibrusApiUsers(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_SUBJECTS -> { ENDPOINT_LIBRUS_API_SUBJECTS -> {
data.startProgress(R.string.edziennik_progress_endpoint_subjects) data.startProgress(R.string.edziennik_progress_endpoint_subjects)
LibrusApiSubjects(data, onSuccess) LibrusApiSubjects(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_CLASSROOMS -> { ENDPOINT_LIBRUS_API_CLASSROOMS -> {
data.startProgress(R.string.edziennik_progress_endpoint_classrooms) data.startProgress(R.string.edziennik_progress_endpoint_classrooms)
LibrusApiClassrooms(data, onSuccess) LibrusApiClassrooms(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_LESSONS -> { ENDPOINT_LIBRUS_API_LESSONS -> {
data.startProgress(R.string.edziennik_progress_endpoint_lessons) data.startProgress(R.string.edziennik_progress_endpoint_lessons)
LibrusApiLessons(data, onSuccess) LibrusApiLessons(data, lastSync, onSuccess)
}
ENDPOINT_LIBRUS_API_PUSH_CONFIG -> {
data.startProgress(R.string.edziennik_progress_endpoint_push_config)
LibrusApiPushConfig(data, lastSync, onSuccess)
} }
// TODO push config
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, onSuccess) LibrusApiTimetables(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_NORMAL_GRADE_CATEGORIES -> { ENDPOINT_LIBRUS_API_NORMAL_GRADE_CATEGORIES -> {
data.startProgress(R.string.edziennik_progress_endpoint_grade_categories) data.startProgress(R.string.edziennik_progress_endpoint_grade_categories)
LibrusApiGradeCategories(data, onSuccess) LibrusApiGradeCategories(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_CATEGORIES -> { ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_CATEGORIES -> {
data.startProgress(R.string.edziennik_progress_endpoint_grade_categories) data.startProgress(R.string.edziennik_progress_endpoint_grade_categories)
LibrusApiBehaviourGradeCategories(data, onSuccess) LibrusApiBehaviourGradeCategories(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADE_CATEGORIES -> { ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADE_CATEGORIES -> {
data.startProgress(R.string.edziennik_progress_endpoint_grade_categories) data.startProgress(R.string.edziennik_progress_endpoint_grade_categories)
LibrusApiDescriptiveGradeCategories(data, onSuccess) LibrusApiDescriptiveGradeCategories(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_TEXT_GRADE_CATEGORIES -> { ENDPOINT_LIBRUS_API_TEXT_GRADE_CATEGORIES -> {
data.startProgress(R.string.edziennik_progress_endpoint_grade_categories) data.startProgress(R.string.edziennik_progress_endpoint_grade_categories)
LibrusApiTextGradeCategories(data, onSuccess) LibrusApiTextGradeCategories(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_POINT_GRADE_CATEGORIES -> { ENDPOINT_LIBRUS_API_POINT_GRADE_CATEGORIES -> {
data.startProgress(R.string.edziennik_progress_endpoint_grade_categories) data.startProgress(R.string.edziennik_progress_endpoint_grade_categories)
LibrusApiPointGradeCategories(data, onSuccess) LibrusApiPointGradeCategories(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS -> { ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS -> {
data.startProgress(R.string.edziennik_progress_endpoint_grade_comments) data.startProgress(R.string.edziennik_progress_endpoint_grade_comments)
LibrusApiGradeComments(data, onSuccess) LibrusApiGradeComments(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_COMMENTS -> { ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_COMMENTS -> {
data.startProgress(R.string.edziennik_progress_endpoint_grade_comments) data.startProgress(R.string.edziennik_progress_endpoint_grade_comments)
LibrusApiBehaviourGradeComments(data, onSuccess) LibrusApiBehaviourGradeComments(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_NORMAL_GRADES -> { ENDPOINT_LIBRUS_API_NORMAL_GRADES -> {
data.startProgress(R.string.edziennik_progress_endpoint_grades) data.startProgress(R.string.edziennik_progress_endpoint_grades)
LibrusApiGrades(data, onSuccess) LibrusApiGrades(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES -> { ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES -> {
data.startProgress(R.string.edziennik_progress_endpoint_behaviour_grades) data.startProgress(R.string.edziennik_progress_endpoint_behaviour_grades)
LibrusApiBehaviourGrades(data, onSuccess) LibrusApiBehaviourGrades(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES -> { ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES -> {
data.startProgress(R.string.edziennik_progress_endpoint_descriptive_grades) data.startProgress(R.string.edziennik_progress_endpoint_descriptive_grades)
LibrusApiDescriptiveGrades(data, onSuccess) LibrusApiDescriptiveGrades(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_TEXT_GRADES -> { ENDPOINT_LIBRUS_API_TEXT_GRADES -> {
data.startProgress(R.string.edziennik_progress_endpoint_descriptive_grades) data.startProgress(R.string.edziennik_progress_endpoint_descriptive_grades)
LibrusApiTextGrades(data, onSuccess) LibrusApiTextGrades(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_POINT_GRADES -> { ENDPOINT_LIBRUS_API_POINT_GRADES -> {
data.startProgress(R.string.edziennik_progress_endpoint_point_grades) data.startProgress(R.string.edziennik_progress_endpoint_point_grades)
LibrusApiPointGrades(data, onSuccess) LibrusApiPointGrades(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_EVENT_TYPES -> { ENDPOINT_LIBRUS_API_EVENT_TYPES -> {
data.startProgress(R.string.edziennik_progress_endpoint_event_types) data.startProgress(R.string.edziennik_progress_endpoint_event_types)
LibrusApiEventTypes(data, onSuccess) LibrusApiEventTypes(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_EVENTS -> { ENDPOINT_LIBRUS_API_EVENTS -> {
data.startProgress(R.string.edziennik_progress_endpoint_events) data.startProgress(R.string.edziennik_progress_endpoint_events)
LibrusApiEvents(data, onSuccess) LibrusApiEvents(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_HOMEWORK -> { ENDPOINT_LIBRUS_API_HOMEWORK -> {
data.startProgress(R.string.edziennik_progress_endpoint_homework) data.startProgress(R.string.edziennik_progress_endpoint_homework)
LibrusApiHomework(data, onSuccess) LibrusApiHomework(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_LUCKY_NUMBER -> { ENDPOINT_LIBRUS_API_LUCKY_NUMBER -> {
data.startProgress(R.string.edziennik_progress_endpoint_lucky_number) data.startProgress(R.string.edziennik_progress_endpoint_lucky_number)
LibrusApiLuckyNumber(data, onSuccess) LibrusApiLuckyNumber(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_NOTICE_TYPES -> { ENDPOINT_LIBRUS_API_NOTICE_TYPES -> {
data.startProgress(R.string.edziennik_progress_endpoint_notice_types) data.startProgress(R.string.edziennik_progress_endpoint_notice_types)
LibrusApiNoticeTypes(data, onSuccess) LibrusApiNoticeTypes(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_NOTICES -> { ENDPOINT_LIBRUS_API_NOTICES -> {
data.startProgress(R.string.edziennik_progress_endpoint_notices) data.startProgress(R.string.edziennik_progress_endpoint_notices)
LibrusApiNotices(data, onSuccess) LibrusApiNotices(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES -> { ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES -> {
data.startProgress(R.string.edziennik_progress_endpoint_attendance_types) data.startProgress(R.string.edziennik_progress_endpoint_attendance_types)
LibrusApiAttendanceTypes(data, onSuccess) LibrusApiAttendanceTypes(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_ATTENDANCES -> { ENDPOINT_LIBRUS_API_ATTENDANCES -> {
data.startProgress(R.string.edziennik_progress_endpoint_attendance) data.startProgress(R.string.edziennik_progress_endpoint_attendance)
LibrusApiAttendances(data, onSuccess) LibrusApiAttendances(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_ANNOUNCEMENTS -> { ENDPOINT_LIBRUS_API_ANNOUNCEMENTS -> {
data.startProgress(R.string.edziennik_progress_endpoint_announcements) data.startProgress(R.string.edziennik_progress_endpoint_announcements)
LibrusApiAnnouncements(data, onSuccess) LibrusApiAnnouncements(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_PT_MEETINGS -> { ENDPOINT_LIBRUS_API_PT_MEETINGS -> {
data.startProgress(R.string.edziennik_progress_endpoint_pt_meetings) data.startProgress(R.string.edziennik_progress_endpoint_pt_meetings)
LibrusApiPtMeetings(data, onSuccess) LibrusApiPtMeetings(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_TEACHER_FREE_DAY_TYPES -> { ENDPOINT_LIBRUS_API_TEACHER_FREE_DAY_TYPES -> {
data.startProgress(R.string.edziennik_progress_endpoint_teacher_free_day_types) data.startProgress(R.string.edziennik_progress_endpoint_teacher_free_day_types)
LibrusApiTeacherFreeDayTypes(data, onSuccess) LibrusApiTeacherFreeDayTypes(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS -> { ENDPOINT_LIBRUS_API_TEACHER_FREE_DAYS -> {
data.startProgress(R.string.edziennik_progress_endpoint_teacher_free_days) data.startProgress(R.string.edziennik_progress_endpoint_teacher_free_days)
LibrusApiTeacherFreeDays(data, onSuccess) LibrusApiTeacherFreeDays(data, lastSync, onSuccess)
} }
/** /**
@ -190,11 +195,11 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
*/ */
ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK -> { ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK -> {
data.startProgress(R.string.edziennik_progress_endpoint_homework) data.startProgress(R.string.edziennik_progress_endpoint_homework)
LibrusSynergiaHomework(data, onSuccess) LibrusSynergiaHomework(data, lastSync, onSuccess)
} }
ENDPOINT_LIBRUS_SYNERGIA_INFO -> { ENDPOINT_LIBRUS_SYNERGIA_INFO -> {
data.startProgress(R.string.edziennik_progress_endpoint_student_info) data.startProgress(R.string.edziennik_progress_endpoint_student_info)
LibrusSynergiaInfo(data, onSuccess) LibrusSynergiaInfo(data, lastSync, onSuccess)
} }
/** /**
@ -202,14 +207,14 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) {
*/ */
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, onSuccess = onSuccess) LibrusMessagesGetList(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, onSuccess = onSuccess) LibrusMessagesGetList(data, type = Message.TYPE_SENT, lastSync = lastSync, onSuccess = onSuccess)
} }
else -> onSuccess() 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
@ -30,7 +29,7 @@ import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult import javax.xml.transform.stream.StreamResult
open class LibrusMessages(open val data: DataLibrus) { open class LibrusMessages(open val data: DataLibrus, open val lastSync: Long?) {
companion object { companion object {
private const val TAG = "LibrusMessages" private const val TAG = "LibrusMessages"
} }
@ -55,13 +54,20 @@ open class LibrusMessages(open val data: DataLibrus) {
} }
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) {
} }
} }
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) {
} }
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) {
} }
} }
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()

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

@ -12,7 +12,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
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
open class LibrusSynergia(open val data: DataLibrus) { open class LibrusSynergia(open val data: DataLibrus, open val lastSync: Long?) {
companion object { companion object {
private const val TAG = "LibrusSynergia" private const val TAG = "LibrusSynergia"
} }

View File

@ -6,25 +6,28 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.ERROR_LIBRUS_API_INVALID_REQUEST_PARAMS import pl.szczodrzynski.edziennik.data.api.ERROR_LIBRUS_API_INVALID_REQUEST_PARAMS
import pl.szczodrzynski.edziennik.data.api.ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM
import pl.szczodrzynski.edziennik.data.api.POST import pl.szczodrzynski.edziennik.data.api.POST
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.api.events.AnnouncementGetEvent import pl.szczodrzynski.edziennik.data.api.events.AnnouncementGetEvent
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
class LibrusApiAnnouncementMarkAsRead( class LibrusApiAnnouncementMarkAsRead(override val data: DataLibrus,
override val data: DataLibrus, private val announcement: AnnouncementFull,
private val announcement: AnnouncementFull, val onSuccess: () -> Unit
val onSuccess: () -> Unit ) : LibrusApi(data, null) {
) : LibrusApi(data) {
companion object { companion object {
const val TAG = "LibrusApiAnnouncementMarkAsRead" const val TAG = "LibrusApiAnnouncementMarkAsRead"
} }
init { init {
apiGet(TAG, "SchoolNotices/MarkAsRead/${announcement.idString}", method = POST, apiGet(TAG, "SchoolNotices/MarkAsRead/${announcement.idString}", method = POST,
ignoreErrors = listOf(ERROR_LIBRUS_API_INVALID_REQUEST_PARAMS)) { ignoreErrors = listOf(
ERROR_LIBRUS_API_INVALID_REQUEST_PARAMS,
ERROR_LIBRUS_API_NOTICEBOARD_PROBLEM
)) {
announcement.seen = true announcement.seen = true
EventBus.getDefault().postSticky(AnnouncementGetEvent(announcement)) EventBus.getDefault().postSticky(AnnouncementGetEvent(announcement))

View File

@ -9,12 +9,14 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_ANNOUNCEMENTS import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_ANNOUNCEMENTS
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.db.entity.Announcement import pl.szczodrzynski.edziennik.data.db.entity.Announcement
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class LibrusApiAnnouncements(override val data: DataLibrus, class LibrusApiAnnouncements(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : LibrusApi(data, lastSync) {
companion object { companion object {
const val TAG = "LibrusApiAnnouncements" const val TAG = "LibrusApiAnnouncements"
} }
@ -58,7 +60,7 @@ class LibrusApiAnnouncements(override val data: DataLibrus,
} }
data.setSyncNext(ENDPOINT_LIBRUS_API_ANNOUNCEMENTS, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_LIBRUS_API_ANNOUNCEMENTS, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_LIBRUS_API_ANNOUNCEMENTS)
} }
}} }}
} }

View File

@ -13,7 +13,9 @@ import pl.szczodrzynski.edziennik.data.db.entity.Attendance
import pl.szczodrzynski.edziennik.data.db.entity.AttendanceType import pl.szczodrzynski.edziennik.data.db.entity.AttendanceType
class LibrusApiAttendanceTypes(override val data: DataLibrus, class LibrusApiAttendanceTypes(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : LibrusApi(data, lastSync) {
companion object { companion object {
const val TAG = "LibrusApiAttendanceTypes" const val TAG = "LibrusApiAttendanceTypes"
} }
@ -43,7 +45,7 @@ class LibrusApiAttendanceTypes(override val data: DataLibrus,
} }
data.setSyncNext(ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES, 4*DAY) data.setSyncNext(ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES, 4*DAY)
onSuccess() onSuccess(ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES)
} }
} }
} }

View File

@ -9,14 +9,16 @@ import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_ATTENDANCES import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_ATTENDANCES
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.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Attendance import pl.szczodrzynski.edziennik.data.db.entity.Attendance
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
class LibrusApiAttendances(override val data: DataLibrus, class LibrusApiAttendances(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : LibrusApi(data, lastSync) {
companion object { companion object {
const val TAG = "LibrusApiAttendances" const val TAG = "LibrusApiAttendances"
} }
@ -33,17 +35,18 @@ class LibrusApiAttendances(override val data: DataLibrus,
val attendances = json.getJsonArray("Attendances")?.asJsonObjectList() val attendances = json.getJsonArray("Attendances")?.asJsonObjectList()
attendances?.forEach { attendance -> attendances?.forEach { attendance ->
val id = Utils.strToInt((attendance.getString("Id") ?: return@forEach) val id = ((attendance.getString("Id") ?: return@forEach)
.replace("[^\\d.]".toRegex(), "")).toLong() .replace("[^\\d.]".toRegex(), "")).toLong()
val lessonId = attendance.getJsonObject("Lesson")?.getLong("Id") ?: -1 val lessonId = attendance.getJsonObject("Lesson")?.getLong("Id") ?: -1
val lessonNo = attendance.getInt("LessonNo") ?: return@forEach val lessonNo = attendance.getInt("LessonNo") ?: return@forEach
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 semester = attendance.getInt("Semester") ?: return@forEach val semester = attendance.getInt("Semester") ?: return@forEach
val type = attendance.getJsonObject("Type")?.getLong("Id") ?: return@forEach val type = attendance.getJsonObject("Type")?.getLong("Id") ?: return@forEach
val typeObject = data.attendanceTypes.get(type) val typeObject = data.attendanceTypes[type] ?: null
val topic = typeObject?.name ?: "" val topic = typeObject?.name ?: ""
val startTime = data.lessonRanges.get(lessonNo).startTime 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 }
@ -52,19 +55,19 @@ class LibrusApiAttendances(override val data: DataLibrus,
val attendanceObject = Attendance( val attendanceObject = Attendance(
profileId, profileId,
id, id,
lesson?.teacherId ?: -1, teacherId ?: lesson?.teacherId ?: -1,
lesson?.subjectId ?: -1, lesson?.subjectId ?: -1,
semester, semester,
topic, topic,
lessonDate, lessonDate,
startTime, startTime ?: Time(0, 0, 0),
typeObject.type typeObject?.type ?: Attendance.TYPE_CUSTOM
) )
val addedDate = Date.fromIso(attendance.getString("AddDate") ?: return@forEach) val addedDate = Date.fromIso(attendance.getString("AddDate") ?: return@forEach)
data.attendanceList.add(attendanceObject) data.attendanceList.add(attendanceObject)
if(typeObject.type != Attendance.TYPE_PRESENT) { if(typeObject?.type != Attendance.TYPE_PRESENT) {
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
profileId, profileId,
Metadata.TYPE_ATTENDANCE, Metadata.TYPE_ATTENDANCE,
@ -77,7 +80,7 @@ class LibrusApiAttendances(override val data: DataLibrus,
} }
data.setSyncNext(ENDPOINT_LIBRUS_API_ATTENDANCES, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_LIBRUS_API_ATTENDANCES, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_LIBRUS_API_ATTENDANCES)
} }
} }
} }

View File

@ -12,7 +12,9 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
class LibrusApiBehaviourGradeCategories(override val data: DataLibrus, class LibrusApiBehaviourGradeCategories(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : LibrusApi(data, lastSync) {
companion object { companion object {
const val TAG = "LibrusApiBehaviourGradeCategories" const val TAG = "LibrusApiBehaviourGradeCategories"
} }
@ -40,7 +42,7 @@ class LibrusApiBehaviourGradeCategories(override val data: DataLibrus,
} }
data.setSyncNext(ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_CATEGORIES, 1 * WEEK) data.setSyncNext(ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_CATEGORIES, 1 * WEEK)
onSuccess() onSuccess(ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_CATEGORIES)
} }
} }
} }

View File

@ -8,11 +8,13 @@ import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_COMMENTS import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_COMMENTS
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.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
class LibrusApiBehaviourGradeComments(override val data: DataLibrus, class LibrusApiBehaviourGradeComments(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : LibrusApi(data, lastSync) {
companion object { companion object {
const val TAG = "LibrusApiBehaviourGradeComments" const val TAG = "LibrusApiBehaviourGradeComments"
} }
@ -38,7 +40,7 @@ class LibrusApiBehaviourGradeComments(override val data: DataLibrus,
} }
data.setSyncNext(ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_COMMENTS, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_COMMENTS, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADE_COMMENTS)
} }
} }
} }

View File

@ -9,38 +9,54 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES
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.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Grade import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.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.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import java.text.DecimalFormat import java.text.DecimalFormat
class LibrusApiBehaviourGrades(override val data: DataLibrus, class LibrusApiBehaviourGrades(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : LibrusApi(data, lastSync) {
companion object { companion object {
const val TAG = "LibrusApiBehaviourGrades" const val TAG = "LibrusApiBehaviourGrades"
} }
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
)
data.gradeList.add(semester1StartGradeObject) data.gradeList.add(semester1StartGradeObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
@ -55,18 +71,20 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
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
)
data.gradeList.add(semester2StartGradeObject) data.gradeList.add(semester2StartGradeObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
@ -88,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
} }
@ -108,32 +130,33 @@ 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
).apply { ).apply {
type = Grade.TYPE_POINT_SUM
valueMax = valueTo valueMax = valueTo
} }
@ -150,7 +173,7 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus,
data.toRemove.add(DataRemoveModel.Grades.semesterWithType(profile.currentSemester, Grade.TYPE_POINT_SUM)) data.toRemove.add(DataRemoveModel.Grades.semesterWithType(profile.currentSemester, Grade.TYPE_POINT_SUM))
data.setSyncNext(ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES)
} }
} ?: onSuccess() } } ?: onSuccess(ENDPOINT_LIBRUS_API_BEHAVIOUR_GRADES) }
} }

View File

@ -15,7 +15,9 @@ import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class LibrusApiClasses(override val data: DataLibrus, class LibrusApiClasses(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : LibrusApi(data, lastSync) {
companion object { companion object {
const val TAG = "LibrusApiClasses" const val TAG = "LibrusApiClasses"
} }
@ -55,7 +57,7 @@ class LibrusApiClasses(override val data: DataLibrus,
} }
data.setSyncNext(ENDPOINT_LIBRUS_API_CLASSES, 4 * DAY) data.setSyncNext(ENDPOINT_LIBRUS_API_CLASSES, 4 * DAY)
onSuccess() onSuccess(ENDPOINT_LIBRUS_API_CLASSES)
} }
} }
} }

View File

@ -12,7 +12,9 @@ import pl.szczodrzynski.edziennik.data.db.entity.Classroom
import java.util.* import java.util.*
class LibrusApiClassrooms(override val data: DataLibrus, class LibrusApiClassrooms(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : LibrusApi(data, lastSync) {
companion object { companion object {
const val TAG = "LibrusApiClassrooms" const val TAG = "LibrusApiClassrooms"
} }
@ -25,9 +27,10 @@ class LibrusApiClassrooms(override val data: DataLibrus,
val id = classroom.getLong("Id") ?: return@forEach val id = classroom.getLong("Id") ?: return@forEach
val name = classroom.getString("Name")?.toLowerCase(Locale.getDefault()) ?: "" val name = classroom.getString("Name")?.toLowerCase(Locale.getDefault()) ?: ""
val symbol = classroom.getString("Symbol")?.toLowerCase(Locale.getDefault()) ?: "" val symbol = classroom.getString("Symbol")?.toLowerCase(Locale.getDefault()) ?: ""
val nameShort = name.split(" ").onEach { it[0] }.joinToString() val nameShort = name.fixWhiteSpaces().split(" ").onEach { it[0] }.joinToString()
val symbolParts = symbol.fixWhiteSpaces().split(" ")
val friendlyName = if (name != symbol && !name.contains(symbol) && !nameShort.contains(symbol)) { val friendlyName = if (name != symbol && !name.contains(symbol) && !name.containsAll(symbolParts) && !nameShort.contains(symbol)) {
classroom.getString("Symbol") + " " + classroom.getString("Name") classroom.getString("Symbol") + " " + classroom.getString("Name")
} }
else { else {
@ -38,7 +41,7 @@ class LibrusApiClassrooms(override val data: DataLibrus,
} }
data.setSyncNext(ENDPOINT_LIBRUS_API_CLASSROOMS, 4*DAY) data.setSyncNext(ENDPOINT_LIBRUS_API_CLASSROOMS, 4*DAY)
onSuccess() onSuccess(ENDPOINT_LIBRUS_API_CLASSROOMS)
} }
} }
} }

View File

@ -12,7 +12,9 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
class LibrusApiDescriptiveGradeCategories(override val data: DataLibrus, class LibrusApiDescriptiveGradeCategories(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : LibrusApi(data, lastSync) {
companion object { companion object {
const val TAG = "LibrusApiDescriptiveGradeCategories" const val TAG = "LibrusApiDescriptiveGradeCategories"
} }
@ -39,7 +41,7 @@ class LibrusApiDescriptiveGradeCategories(override val data: DataLibrus,
} }
data.setSyncNext(ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADE_CATEGORIES, 1 * DAY) data.setSyncNext(ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADE_CATEGORIES, 1 * DAY)
onSuccess() onSuccess(ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADE_CATEGORIES)
} }
} }
} }

View File

@ -9,16 +9,18 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES
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.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Grade import pl.szczodrzynski.edziennik.data.db.entity.Grade
import pl.szczodrzynski.edziennik.data.db.entity.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.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
class LibrusApiDescriptiveGrades(override val data: DataLibrus, class LibrusApiDescriptiveGrades(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : LibrusApi(data, lastSync) {
companion object { companion object {
const val TAG = "LibrusApiDescriptiveGrades" const val TAG = "LibrusApiDescriptiveGrades"
} }
@ -51,20 +53,20 @@ 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
} )
data.gradeList.add(gradeObject) data.gradeList.add(gradeObject)
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
@ -85,7 +87,7 @@ class LibrusApiDescriptiveGrades(override val data: DataLibrus,
}) })
data.setSyncNext(ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES)
} }
} ?: onSuccess() } } ?: onSuccess(ENDPOINT_LIBRUS_API_DESCRIPTIVE_GRADES) }
} }

View File

@ -11,7 +11,9 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.db.entity.EventType import pl.szczodrzynski.edziennik.data.db.entity.EventType
class LibrusApiEventTypes(override val data: DataLibrus, class LibrusApiEventTypes(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : LibrusApi(data, lastSync) {
companion object { companion object {
const val TAG = "LibrusApiEventTypes" const val TAG = "LibrusApiEventTypes"
} }
@ -29,7 +31,7 @@ class LibrusApiEventTypes(override val data: DataLibrus,
} }
data.setSyncNext(ENDPOINT_LIBRUS_API_EVENT_TYPES, 4*DAY) data.setSyncNext(ENDPOINT_LIBRUS_API_EVENT_TYPES, 4*DAY)
onSuccess() onSuccess(ENDPOINT_LIBRUS_API_EVENT_TYPES)
} }
} }
} }

View File

@ -10,14 +10,16 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_EVENTS import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_EVENTS
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.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Event 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.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
class LibrusApiEvents(override val data: DataLibrus, class LibrusApiEvents(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : LibrusApi(data, lastSync) {
companion object { companion object {
const val TAG = "LibrusApiEvents" const val TAG = "LibrusApiEvents"
} }
@ -34,7 +36,7 @@ class LibrusApiEvents(override val data: DataLibrus,
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") ?: "" val topic = event.getString("Content") ?: ""
val type = event.getJsonObject("Category")?.getInt("Id") ?: -1 val type = event.getJsonObject("Category")?.getLong("Id") ?: -1
val teacherId = event.getJsonObject("CreatedBy")?.getLong("Id") ?: -1 val teacherId = event.getJsonObject("CreatedBy")?.getLong("Id") ?: -1
val subjectId = event.getJsonObject("Subject")?.getLong("Id") ?: -1 val subjectId = event.getJsonObject("Subject")?.getLong("Id") ?: -1
val teamId = event.getJsonObject("Class")?.getLong("Id") ?: -1 val teamId = event.getJsonObject("Class")?.getLong("Id") ?: -1
@ -76,7 +78,7 @@ class LibrusApiEvents(override val data: DataLibrus,
))) )))
data.setSyncNext(ENDPOINT_LIBRUS_API_EVENTS, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_LIBRUS_API_EVENTS, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_LIBRUS_API_EVENTS)
} }
} }
} }

View File

@ -9,11 +9,13 @@ import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADE_CATEGORIES import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADE_CATEGORIES
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.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
class LibrusApiGradeCategories(override val data: DataLibrus, class LibrusApiGradeCategories(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : LibrusApi(data, lastSync) {
companion object { companion object {
const val TAG = "LibrusApiGradeCategories" const val TAG = "LibrusApiGradeCategories"
} }
@ -42,7 +44,7 @@ class LibrusApiGradeCategories(override val data: DataLibrus,
} }
data.setSyncNext(ENDPOINT_LIBRUS_API_NORMAL_GRADE_CATEGORIES, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_LIBRUS_API_NORMAL_GRADE_CATEGORIES, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_LIBRUS_API_NORMAL_GRADE_CATEGORIES)
} }
} }
} }

View File

@ -8,11 +8,13 @@ import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS
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.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory import pl.szczodrzynski.edziennik.data.db.entity.GradeCategory
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
class LibrusApiGradeComments(override val data: DataLibrus, class LibrusApiGradeComments(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) { override val lastSync: Long?,
val onSuccess: (endpointId: Int) -> Unit
) : LibrusApi(data, lastSync) {
companion object { companion object {
const val TAG = "LibrusApiGradeComments" const val TAG = "LibrusApiGradeComments"
} }
@ -38,7 +40,7 @@ class LibrusApiGradeComments(override val data: DataLibrus,
} }
data.setSyncNext(ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS, SYNC_ALWAYS)
onSuccess() onSuccess(ENDPOINT_LIBRUS_API_NORMAL_GRADE_COMMENTS)
} }
} }
} }

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