Compare commits

...

68 Commits

Author SHA1 Message Date
1acf1547d5 [3.9.13-dev] 2019-12-02 22:24:55 +01:00
5d3de35c10 [UI/Timetable] Fix non-null cast exception. 2019-12-02 22:20:53 +01:00
8f8d613f6e [UI] Update libraries & NavLib to fix missing badges on most profiles. 2019-12-02 22:10:36 +01:00
6a161b3c97 [UI/Timetable] Add showing event indicators on lessons. 2019-12-02 21:25:18 +01:00
3e97572100 [Dialog/Events] Use new event dialog in homework fragment and event list dialog. 2019-12-02 19:36:19 +01:00
fc3b6fd1e0 [UI/Event] Fix stuff because i'm dumb 2019-12-02 19:07:30 +01:00
9bc7f9ac11 [UI/Event] Make use of default values in event manual dialog. 2019-12-02 19:04:30 +01:00
0a2f252405 [UI/Home] Implement home card swapping and saving. 2019-12-02 18:12:52 +01:00
09bc658f97 [UI] Fix blank screen when loading activity. 2019-12-02 18:11:12 +01:00
7b04202a00 [Config] Add the rest of config. Migrate from AppConfig and remove most values from AppConfig. 2019-12-01 22:35:42 +01:00
acf364166b [Notifications/LessonChange] Fix lesson change notifications text. 2019-12-01 21:54:59 +01:00
4e88efae94 [Home/Grades] Fix padding. 2019-12-01 20:25:50 +01:00
8df24dc1c4 [Dialog/Events] Create adapter outside of the observer. 2019-12-01 20:25:28 +01:00
8482c27689 [Timetable] Add marking lessons as seen in timetable. 2019-12-01 20:23:48 +01:00
d1265dc1f2 [Dialog/Events] Make new event list dialog. 2019-11-30 23:33:20 +01:00
47d395de71 [Home] Add home grades card. 2019-11-29 23:16:50 +01:00
5b443e02a3 [Mobidziennik] Fix getting grades with no category. Add support for comments. 2019-11-29 18:59:47 +01:00
f8a7d52b1d [Mobidziennik] Fix extracting when attachment has no size. 2019-11-29 17:51:16 +01:00
a133a96819 [UI/Messages] Make message list scroll to last opened message. 2019-11-29 17:29:23 +01:00
c71b8f994c [Messages] Implement Mobidziennik attachments. Fix multiplying attachments in UI. 2019-11-28 23:32:10 +01:00
9b02c97926 [UI/Messages] Convert adapter to Kotlin. 2019-11-28 23:12:21 +01:00
ab06efc934 [Librus/Attachment] A huge structure reformat. 2019-11-28 23:00:25 +01:00
928b73f139 [Config] Implement per-profile config. Update timetable card. 2019-11-28 21:45:27 +01:00
a36fb09bc3 [Dialog/Event] Add event adding in the new event manual dialog. 2019-11-28 15:11:23 +01:00
eaed4b76aa [Timetable] Fix showing question marks in lesson changes. 2019-11-26 23:11:14 +01:00
6d8960f089 [Home/Timetable] Add click listener to timetable card. 2019-11-26 22:56:43 +01:00
ca3b6d0705 [APIv2/Idziennik] Fix getting subject and rewrite getting proposed grades. 2019-11-26 22:28:52 +01:00
c2e7931ea6 [Config] Implement (basic) new app config storage. 2019-11-26 21:55:04 +01:00
d1a5d8cba9 [APIv2/Vulcan] Fix start time in events. 2019-11-26 21:44:35 +01:00
c2f91e6867 [APIv2] Move AppError to API directory. 2019-11-26 21:10:31 +01:00
55e32b8d88 [APIv2/Librus] Temporarily fix subject in attendance. 2019-11-26 21:02:33 +01:00
462b1df767 [3.9.12-dev] 2019-11-25 22:55:09 +01:00
d17d2c8417 [APIv2/Librus] Fix looking for the lesson in getting homework. 2019-11-25 22:53:55 +01:00
6892832fff [APIv2/Idziennik] Add getting homework and rewrite getting exams. 2019-11-25 22:53:55 +01:00
66d54c7c45 [APIv2/Librus] Fix messages login. 2019-11-25 22:40:14 +01:00
d432685aa8 [Update] Fix update downloading from notification. 2019-11-25 22:23:55 +01:00
37f3d76fb8 [UI] Implement home timetable card. 2019-11-25 22:17:08 +01:00
7961a74995 [APIv2/Events] Fix fetching events and homework. Add DataRemoveModel for events. 2019-11-25 21:13:55 +01:00
9d590508ad [APIv2/Librus] Fix messages session ID extraction. 2019-11-25 15:00:51 +01:00
f79b7eaf83 [3.9.11-dev] 2019-11-24 21:47:05 +01:00
ae13bf946f [Home] Remove useless dummy cards. 2019-11-24 21:21:37 +01:00
f116c4f1f4 [Home] Implement basic timetable card. 2019-11-24 21:15:01 +01:00
867c8920a8 [APIv2/Messages] Add downloading attachments. 2019-11-24 20:55:04 +01:00
6e6dd34872 [UI] Add new Home fragment. Add Lucky number card and number selection dialog. 2019-11-24 19:41:17 +01:00
0759468fa7 [Sync] Add syncing all to manual sync dialog. 2019-11-24 16:47:10 +01:00
1b1fb09211 [APIv2/Vulcan] Fix problems with week start in timetable. 2019-11-24 16:31:51 +01:00
de414c912c [Sync] Fix error when user selects no features. 2019-11-24 13:20:42 +01:00
d274a2fed1 [Timetable] Change date receiver argument to timetableDate. 2019-11-24 13:20:22 +01:00
285b7e9b9e [Timetable] Make going to the specified date on the notification click. 2019-11-24 12:57:00 +01:00
875efcff7e [APIv2/Timetable] Fix ID in lessons. 2019-11-24 12:11:39 +01:00
07ae37167d [Notifications/Timetable] Make notifications for timetable changes 2019-11-24 11:09:45 +01:00
f689f4d427 [APIv2/Timetable] Fix lesson changes metadata. 2019-11-24 10:36:20 +01:00
19bc2b8b37 [3.9.10-dev] New UI + stability fixes 2019-11-23 23:26:19 +01:00
673116e27e [Settings/About] Add a new developer to about! 2019-11-23 23:09:03 +01:00
59fcb0a050 [APIv2/Timetable] Add lesson change metadata only when the lesson is today or in the future 2019-11-23 22:40:20 +01:00
cd76f99bbf [APIv2/Timetable] Add showing unread lesson changes 2019-11-23 22:26:21 +01:00
6a4994b9c2 [APIv2/Timetable] Make swipe refresh download timetable for the selected week 2019-11-23 21:57:30 +01:00
63960c5e05 [APIv2/Timetable] Add selecting date, marking as read and fix stepForward in Date 2019-11-23 21:27:52 +01:00
540afb6a28 [Home] Start making new home timetable card in Kotlin 2019-11-23 19:41:55 +01:00
ae10b8abbd [APIv2/Idziennik] Add new timetable getting and fix week start 2019-11-23 19:40:32 +01:00
db2ebab879 [APIv2/Vulcan] Add missing Lublin endpoints 2019-11-23 19:39:13 +01:00
6ec3d062df [UI] Update header image. Fix fragment bundle passing. 2019-11-23 19:37:00 +01:00
86b6060a09 [UI] Migrate to outlined icons. 2019-11-23 18:32:18 +01:00
83d123e341 [UI] New notifications view. 2019-11-22 22:41:40 +01:00
34061695f9 [UI] Update colored placeholder icons. 2019-11-22 19:23:49 +01:00
de68476442 [UI/Event] Add Sync text to manual event dialog. 2019-11-22 18:42:45 +01:00
678a81a44b [APIv2/Vulcan] Improve Vulcan login when migrating from APIv1. 2019-11-22 18:42:11 +01:00
cfb3096d53 [Sync] Add better task cancelling and better frozen task detection. 2019-11-22 18:41:15 +01:00
186 changed files with 5724 additions and 1947 deletions

2
.gitignore vendored
View File

@ -81,3 +81,5 @@ lint/generated/
lint/outputs/ lint/outputs/
lint/tmp/ lint/tmp/
# lint/reports/ # lint/reports/
app/schemas/

2
.idea/misc.xml generated
View File

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

View File

@ -24,6 +24,7 @@
-keep class pl.szczodrzynski.edziennik.utils.models.** { *; } -keep class pl.szczodrzynski.edziennik.utils.models.** { *; }
-keep class pl.szczodrzynski.edziennik.data.db.modules.events.Event { *; } -keep class pl.szczodrzynski.edziennik.data.db.modules.events.Event { *; }
-keep class pl.szczodrzynski.edziennik.data.db.modules.events.EventFull { *; } -keep class pl.szczodrzynski.edziennik.data.db.modules.events.EventFull { *; }
-keep class pl.szczodrzynski.edziennik.ui.modules.home.HomeCardModel { *; }
-keepclassmembers class pl.szczodrzynski.edziennik.widgets.WidgetConfig { public *; } -keepclassmembers class pl.szczodrzynski.edziennik.widgets.WidgetConfig { public *; }
-keepnames class pl.szczodrzynski.edziennik.WidgetTimetable -keepnames class pl.szczodrzynski.edziennik.WidgetTimetable
-keepnames class pl.szczodrzynski.edziennik.notifications.WidgetNotifications -keepnames class pl.szczodrzynski.edziennik.notifications.WidgetNotifications

View File

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

View File

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

View File

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

View File

@ -5,6 +5,8 @@ import android.app.Activity
import android.content.Context import android.content.Context
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Resources import android.content.res.Resources
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
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
@ -17,6 +19,7 @@ import android.util.LongSparseArray
import android.util.SparseArray import android.util.SparseArray
import android.util.TypedValue import android.util.TypedValue
import android.view.View import android.view.View
import android.widget.CompoundButton
import android.widget.TextView import android.widget.TextView
import androidx.annotation.* import androidx.annotation.*
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
@ -28,10 +31,14 @@ 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 im.wangchao.mhttp.Response import im.wangchao.mhttp.Response
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
import pl.szczodrzynski.edziennik.data.db.modules.teams.Team import pl.szczodrzynski.edziennik.data.db.modules.teams.Team
import pl.szczodrzynski.navlib.R import pl.szczodrzynski.edziennik.utils.models.Time
import pl.szczodrzynski.navlib.getColorFromAttr
import pl.szczodrzynski.navlib.getColorFromRes import pl.szczodrzynski.navlib.getColorFromRes
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@ -196,6 +203,7 @@ const val DAY = 24L*HOUR
const val WEEK = 7L*DAY const val WEEK = 7L*DAY
const val MONTH = 30L*DAY const val MONTH = 30L*DAY
const val YEAR = 365L*DAY const val YEAR = 365L*DAY
const val MS = 1000L
fun <T> LongSparseArray<T>.values(): List<T> { fun <T> LongSparseArray<T>.values(): List<T> {
val result = mutableListOf<T>() val result = mutableListOf<T>()
@ -374,13 +382,13 @@ fun CharSequence?.asItalicSpannable(): Spannable {
*/ */
fun <T : CharSequence> listOfNotEmpty(vararg elements: T): List<T> = elements.filterNot { it.isEmpty() } fun <T : CharSequence> listOfNotEmpty(vararg elements: T): List<T> = elements.filterNot { it.isEmpty() }
fun List<CharSequence>.concat(delimiter: String? = null): CharSequence { fun List<CharSequence?>.concat(delimiter: String? = null): CharSequence {
if (this.isEmpty()) { if (this.isEmpty()) {
return "" return ""
} }
if (this.size == 1) { if (this.size == 1) {
return this[0] return this[0] ?: ""
} }
var spanned = false var spanned = false
@ -395,6 +403,8 @@ fun List<CharSequence>.concat(delimiter: String? = null): CharSequence {
if (spanned) { if (spanned) {
val ssb = SpannableStringBuilder() val ssb = SpannableStringBuilder()
for (piece in this) { for (piece in this) {
if (piece == null)
continue
if (!first && delimiter != null) if (!first && delimiter != null)
ssb.append(delimiter) ssb.append(delimiter)
first = false first = false
@ -404,6 +414,8 @@ fun List<CharSequence>.concat(delimiter: String? = null): CharSequence {
} else { } else {
val sb = StringBuilder() val sb = StringBuilder()
for (piece in this) { for (piece in this) {
if (piece == null)
continue
if (!first && delimiter != null) if (!first && delimiter != null)
sb.append(delimiter) sb.append(delimiter)
first = false first = false
@ -417,15 +429,15 @@ fun TextView.setText(@StringRes resid: Int, vararg formatArgs: Any) {
text = context.getString(resid, *formatArgs) text = context.getString(resid, *formatArgs)
} }
fun JsonObject(vararg properties: Pair<String, Any>): JsonObject { fun JsonObject(vararg properties: Pair<String, Any?>): JsonObject {
return JsonObject().apply { return JsonObject().apply {
for (property in properties) { for (property in properties) {
when (property.second) { when (property.second) {
is JsonElement -> add(property.first, property.second as JsonElement) is JsonElement -> add(property.first, property.second as JsonElement?)
is String -> addProperty(property.first, property.second as String) is String -> addProperty(property.first, property.second as String?)
is Char -> addProperty(property.first, property.second as Char) is Char -> addProperty(property.first, property.second as Char?)
is Number -> addProperty(property.first, property.second as Number) is Number -> addProperty(property.first, property.second as Number?)
is Boolean -> addProperty(property.first, property.second as Boolean) is Boolean -> addProperty(property.first, property.second as Boolean?)
} }
} }
} }
@ -441,6 +453,13 @@ inline fun <T : View> T.onClick(crossinline onClickListener: (v: T) -> Unit) {
} }
} }
@Suppress("UNCHECKED_CAST")
inline fun <T : CompoundButton> T.onChange(crossinline onChangeListener: (v: T, isChecked: Boolean) -> Unit) {
setOnCheckedChangeListener { buttonView, isChecked ->
onChangeListener(buttonView as T, isChecked)
}
}
fun <T> LiveData<T>.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observer<T>) { fun <T> LiveData<T>.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observer<T>) {
observe(lifecycleOwner, object : Observer<T> { observe(lifecycleOwner, object : Observer<T> {
override fun onChanged(t: T?) { override fun onChanged(t: T?) {
@ -494,4 +513,96 @@ fun View.findParentById(targetId: Int): View? {
return viewParent.findParentById(targetId) return viewParent.findParentById(targetId)
} }
return null return null
} }
fun CoroutineScope.startCoroutineTimer(delayMillis: Long = 0, repeatMillis: Long = 0, action: () -> Unit) = launch {
delay(delayMillis)
if (repeatMillis > 0) {
while (true) {
action()
delay(repeatMillis)
}
} else {
action()
}
}
operator fun Time?.compareTo(other: Time?): Int {
if (this == null && other == null)
return 0
if (this == null)
return -1
if (other == null)
return 1
return this.compareTo(other)
}
operator fun StringBuilder.plusAssign(str: String?) {
this.append(str)
}
fun Context.timeTill(time: Int, delimiter: String = " "): String {
val parts = mutableListOf<Pair<Int, Int>>()
val hours = time / 3600
val minutes = (time - hours*3600) / 60
val seconds = time - minutes*60 - hours*3600
var prefixAdded = false
if (hours > 0) {
if (!prefixAdded) parts += R.plurals.time_till_text to hours; prefixAdded = true
parts += R.plurals.time_till_hours to hours
}
if (minutes > 0) {
if (!prefixAdded) parts += R.plurals.time_till_text to minutes; prefixAdded = true
parts += R.plurals.time_till_minutes to minutes
}
if (hours == 0 && minutes < 10) {
if (!prefixAdded) parts += R.plurals.time_till_text to seconds; prefixAdded = true
parts += R.plurals.time_till_seconds to seconds
}
return parts.joinToString(delimiter) { resources.getQuantityString(it.first, it.second, it.second) }
}
fun Context.timeLeft(time: Int, delimiter: String = " "): String {
val parts = mutableListOf<Pair<Int, Int>>()
val hours = time / 3600
val minutes = (time - hours*3600) / 60
val seconds = time - minutes*60 - hours*3600
var prefixAdded = false
if (hours > 0) {
if (!prefixAdded) parts += R.plurals.time_left_text to hours
prefixAdded = true
parts += R.plurals.time_left_hours to hours
}
if (minutes > 0) {
if (!prefixAdded) parts += R.plurals.time_left_text to minutes
prefixAdded = true
parts += R.plurals.time_left_minutes to minutes
}
if (hours == 0 && minutes < 10) {
if (!prefixAdded) parts += R.plurals.time_left_text to seconds
prefixAdded = true
parts += R.plurals.time_left_seconds to seconds
}
return parts.joinToString(delimiter) { resources.getQuantityString(it.first, it.second, it.second) }
}
inline fun <reified T> Any?.instanceOfOrNull(): T? {
return when (this) {
is T -> this
else -> null
}
}
fun Drawable.setTintColor(color: Int): Drawable {
colorFilter = PorterDuffColorFilter(
color,
PorterDuff.Mode.SRC_ATOP
)
return this
}

View File

@ -56,7 +56,7 @@ import pl.szczodrzynski.edziennik.ui.modules.feedback.FeedbackFragment
import pl.szczodrzynski.edziennik.ui.modules.feedback.HelpFragment import pl.szczodrzynski.edziennik.ui.modules.feedback.HelpFragment
import pl.szczodrzynski.edziennik.ui.modules.grades.GradesFragment import pl.szczodrzynski.edziennik.ui.modules.grades.GradesFragment
import pl.szczodrzynski.edziennik.ui.modules.grades.editor.GradesEditorFragment import pl.szczodrzynski.edziennik.ui.modules.grades.editor.GradesEditorFragment
import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragmentV2
import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment import pl.szczodrzynski.edziennik.ui.modules.homework.HomeworkFragment
import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity import pl.szczodrzynski.edziennik.ui.modules.login.LoginActivity
import pl.szczodrzynski.edziennik.ui.modules.messages.MessageFragment import pl.szczodrzynski.edziennik.ui.modules.messages.MessageFragment
@ -122,9 +122,9 @@ class MainActivity : AppCompatActivity() {
val list: MutableList<NavTarget> = mutableListOf() val list: MutableList<NavTarget> = mutableListOf()
// home item // home item
list += NavTarget(DRAWER_ITEM_HOME, R.string.menu_home_page, HomeFragment::class) list += NavTarget(DRAWER_ITEM_HOME, R.string.menu_home_page, HomeFragmentV2::class)
.withTitle(R.string.app_name) .withTitle(R.string.app_name)
.withIcon(CommunityMaterial.Icon2.cmd_home) .withIcon(CommunityMaterial.Icon2.cmd_home_outline)
.isInDrawer(true) .isInDrawer(true)
.isStatic(true) .isStatic(true)
.withPopToHome(false) .withPopToHome(false)
@ -135,50 +135,50 @@ class MainActivity : AppCompatActivity() {
.isInDrawer(true) .isInDrawer(true)
list += NavTarget(DRAWER_ITEM_AGENDA, R.string.menu_agenda, AgendaFragment::class) list += NavTarget(DRAWER_ITEM_AGENDA, R.string.menu_agenda, AgendaFragment::class)
.withIcon(CommunityMaterial.Icon.cmd_calendar) .withIcon(CommunityMaterial.Icon.cmd_calendar_outline)
.withBadgeTypeId(TYPE_EVENT) .withBadgeTypeId(TYPE_EVENT)
.isInDrawer(true) .isInDrawer(true)
list += NavTarget(DRAWER_ITEM_GRADES, R.string.menu_grades, GradesFragment::class) list += NavTarget(DRAWER_ITEM_GRADES, R.string.menu_grades, GradesFragment::class)
.withIcon(CommunityMaterial.Icon2.cmd_numeric_5_box) .withIcon(CommunityMaterial.Icon2.cmd_numeric_5_box_outline)
.withBadgeTypeId(TYPE_GRADE) .withBadgeTypeId(TYPE_GRADE)
.isInDrawer(true) .isInDrawer(true)
list += NavTarget(DRAWER_ITEM_MESSAGES, R.string.menu_messages, MessagesFragment::class) list += NavTarget(DRAWER_ITEM_MESSAGES, R.string.menu_messages, MessagesFragment::class)
.withIcon(CommunityMaterial.Icon.cmd_email) .withIcon(CommunityMaterial.Icon.cmd_email_outline)
.withBadgeTypeId(TYPE_MESSAGE) .withBadgeTypeId(TYPE_MESSAGE)
.isInDrawer(true) .isInDrawer(true)
list += NavTarget(DRAWER_ITEM_HOMEWORK, R.string.menu_homework, HomeworkFragment::class) list += NavTarget(DRAWER_ITEM_HOMEWORK, R.string.menu_homework, HomeworkFragment::class)
.withIcon(SzkolnyFont.Icon.szf_file_document_edit) .withIcon(SzkolnyFont.Icon.szf_notebook_outline)
.withBadgeTypeId(TYPE_HOMEWORK) .withBadgeTypeId(TYPE_HOMEWORK)
.isInDrawer(true) .isInDrawer(true)
list += NavTarget(DRAWER_ITEM_BEHAVIOUR, R.string.menu_notices, BehaviourFragment::class) list += NavTarget(DRAWER_ITEM_BEHAVIOUR, R.string.menu_notices, BehaviourFragment::class)
.withIcon(CommunityMaterial.Icon2.cmd_message_alert) .withIcon(CommunityMaterial.Icon.cmd_emoticon_outline)
.withBadgeTypeId(TYPE_NOTICE) .withBadgeTypeId(TYPE_NOTICE)
.isInDrawer(true) .isInDrawer(true)
list += NavTarget(DRAWER_ITEM_ATTENDANCE, R.string.menu_attendance, AttendanceFragment::class) list += NavTarget(DRAWER_ITEM_ATTENDANCE, R.string.menu_attendance, AttendanceFragment::class)
.withIcon(CommunityMaterial.Icon.cmd_calendar_remove) .withIcon(CommunityMaterial.Icon.cmd_calendar_remove_outline)
.withBadgeTypeId(TYPE_ATTENDANCE) .withBadgeTypeId(TYPE_ATTENDANCE)
.isInDrawer(true) .isInDrawer(true)
list += NavTarget(DRAWER_ITEM_ANNOUNCEMENTS, R.string.menu_announcements, AnnouncementsFragment::class) list += NavTarget(DRAWER_ITEM_ANNOUNCEMENTS, R.string.menu_announcements, AnnouncementsFragment::class)
.withIcon(CommunityMaterial.Icon.cmd_bulletin_board) .withIcon(CommunityMaterial.Icon.cmd_bullhorn_outline)
.withBadgeTypeId(TYPE_ANNOUNCEMENT) .withBadgeTypeId(TYPE_ANNOUNCEMENT)
.isInDrawer(true) .isInDrawer(true)
// static drawer items // static drawer items
list += NavTarget(DRAWER_ITEM_NOTIFICATIONS, R.string.menu_notifications, NotificationsFragment::class) list += NavTarget(DRAWER_ITEM_NOTIFICATIONS, R.string.menu_notifications, NotificationsFragment::class)
.withIcon(CommunityMaterial.Icon.cmd_bell_ring) .withIcon(CommunityMaterial.Icon.cmd_bell_ring_outline)
.isInDrawer(true) .isInDrawer(true)
.isStatic(true) .isStatic(true)
.isBelowSeparator(true) .isBelowSeparator(true)
list += NavTarget(DRAWER_ITEM_SETTINGS, R.string.menu_settings, SettingsNewFragment::class) list += NavTarget(DRAWER_ITEM_SETTINGS, R.string.menu_settings, SettingsNewFragment::class)
.withIcon(CommunityMaterial.Icon2.cmd_settings) .withIcon(CommunityMaterial.Icon2.cmd_settings_outline)
.isInDrawer(true) .isInDrawer(true)
.isStatic(true) .isStatic(true)
.isBelowSeparator(true) .isBelowSeparator(true)
@ -197,7 +197,7 @@ class MainActivity : AppCompatActivity() {
.isInProfileList(false) .isInProfileList(false)
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.Icon2.cmd_sync) .withIcon(CommunityMaterial.Icon.cmd_download_outline)
.isInProfileList(true) .isInProfileList(true)
@ -243,7 +243,7 @@ class MainActivity : AppCompatActivity() {
setTheme(Themes.appTheme) setTheme(Themes.appTheme)
app.appConfig.language?.let { app.config.ui.language?.let {
setLanguage(it) setLanguage(it)
} }
@ -306,10 +306,10 @@ class MainActivity : AppCompatActivity() {
} }
drawer.apply { drawer.apply {
setAccountHeaderBackground(app.appConfig.headerBackground) setAccountHeaderBackground(app.config.ui.headerBackground)
drawerProfileListEmptyListener = { drawerProfileListEmptyListener = {
app.appConfig.loginFinished = false app.config.loginFinished = false
app.saveConfig("loginFinished") app.saveConfig("loginFinished")
profileListEmptyListener() profileListEmptyListener()
} }
@ -334,7 +334,7 @@ class MainActivity : AppCompatActivity() {
drawerProfileSettingClickListener = this@MainActivity.profileSettingClickListener drawerProfileSettingClickListener = this@MainActivity.profileSettingClickListener
miniDrawerVisibleLandscape = null miniDrawerVisibleLandscape = null
miniDrawerVisiblePortrait = app.appConfig.miniDrawerVisible miniDrawerVisiblePortrait = app.config.ui.miniMenuVisible
} }
} }
@ -387,21 +387,23 @@ class MainActivity : AppCompatActivity() {
SyncWorker.scheduleNext(app) SyncWorker.scheduleNext(app)
// APP BACKGROUND // APP BACKGROUND
if (app.appConfig.appBackground != null) { if (app.config.ui.appBackground != null) {
try { try {
var bg = app.appConfig.appBackground app.config.ui.appBackground?.let {
val bgDir = File(Environment.getExternalStoragePublicDirectory("Szkolny.eu"), "bg") var bg = it
if (bgDir.exists()) { val bgDir = File(Environment.getExternalStoragePublicDirectory("Szkolny.eu"), "bg")
val files = bgDir.listFiles() if (bgDir.exists()) {
val r = Random() val files = bgDir.listFiles()
val i = r.nextInt(files.size) val r = Random()
bg = files[i].toString() val i = r.nextInt(files.size)
} bg = files[i].toString()
val linearLayout = b.root }
if (bg.endsWith(".gif")) { val linearLayout = b.root
linearLayout.background = GifDrawable(bg) if (bg.endsWith(".gif")) {
} else { linearLayout.background = GifDrawable(bg)
linearLayout.background = BitmapDrawable.createFromPath(bg) } else {
linearLayout.background = BitmapDrawable.createFromPath(bg)
}
} }
} catch (e: IOException) { } catch (e: IOException) {
e.printStackTrace() e.printStackTrace()
@ -409,7 +411,7 @@ class MainActivity : AppCompatActivity() {
} }
// WHAT'S NEW DIALOG // WHAT'S NEW DIALOG
if (app.appConfig.lastAppVersion != BuildConfig.VERSION_CODE) { if (app.config.appVersion < BuildConfig.VERSION_CODE) {
ServerRequest(app, app.requestScheme + APP_URL + "main.php?just_updated", "MainActivity/JU") ServerRequest(app, app.requestScheme + APP_URL + "main.php?just_updated", "MainActivity/JU")
.run { e, result -> .run { e, result ->
Handler(Looper.getMainLooper()).post { Handler(Looper.getMainLooper()).post {
@ -420,21 +422,20 @@ class MainActivity : AppCompatActivity() {
} }
} }
} }
if (app.appConfig.lastAppVersion < 170) { if (app.config.appVersion < 170) {
//Intent intent = new Intent(this, ChangelogIntroActivity.class); //Intent intent = new Intent(this, ChangelogIntroActivity.class);
//startActivity(intent); //startActivity(intent);
} else { } else {
app.appConfig.lastAppVersion = BuildConfig.VERSION_CODE app.config.appVersion = BuildConfig.VERSION_CODE
app.saveConfig("lastAppVersion")
} }
} }
// RATE SNACKBAR // RATE SNACKBAR
if (app.appConfig.appRateSnackbarTime != 0L && app.appConfig.appRateSnackbarTime <= System.currentTimeMillis()) { if (app.config.appRateSnackbarTime != 0L && app.config.appRateSnackbarTime <= System.currentTimeMillis()) {
navView.coordinator.postDelayed({ navView.coordinator.postDelayed({
CafeBar.builder(this) CafeBar.builder(this)
.content(R.string.rate_snackbar_text) .content(R.string.rate_snackbar_text)
.icon(IconicsDrawable(this).icon(CommunityMaterial.Icon2.cmd_star).size(IconicsSize.dp(20)).color(IconicsColor.colorInt(Themes.getPrimaryTextColor(this)))) .icon(IconicsDrawable(this).icon(CommunityMaterial.Icon2.cmd_star_outline).size(IconicsSize.dp(20)).color(IconicsColor.colorInt(Themes.getPrimaryTextColor(this))))
.positiveText(R.string.rate_snackbar_positive) .positiveText(R.string.rate_snackbar_positive)
.positiveColor(-0xb350b0) .positiveColor(-0xb350b0)
.negativeText(R.string.rate_snackbar_negative) .negativeText(R.string.rate_snackbar_negative)
@ -444,20 +445,17 @@ class MainActivity : AppCompatActivity() {
.onPositive { cafeBar -> .onPositive { cafeBar ->
Utils.openGooglePlay(this) Utils.openGooglePlay(this)
cafeBar.dismiss() cafeBar.dismiss()
app.appConfig.appRateSnackbarTime = 0 app.config.appRateSnackbarTime = 0
app.saveConfig("appRateSnackbarTime")
} }
.onNegative { cafeBar -> .onNegative { cafeBar ->
Toast.makeText(this, "Szkoda, opinie innych pomagają mi rozwijać aplikację.", Toast.LENGTH_LONG).show() Toast.makeText(this, "Szkoda, opinie innych pomagają mi rozwijać aplikację.", Toast.LENGTH_LONG).show()
cafeBar.dismiss() cafeBar.dismiss()
app.appConfig.appRateSnackbarTime = 0 app.config.appRateSnackbarTime = 0
app.saveConfig("appRateSnackbarTime")
} }
.onNeutral { cafeBar -> .onNeutral { cafeBar ->
Toast.makeText(this, "OK", Toast.LENGTH_LONG).show() Toast.makeText(this, "OK", Toast.LENGTH_LONG).show()
cafeBar.dismiss() cafeBar.dismiss()
app.appConfig.appRateSnackbarTime = System.currentTimeMillis() + 7 * 24 * 60 * 60 * 1000 app.config.appRateSnackbarTime = System.currentTimeMillis() + 7 * 24 * 60 * 60 * 1000
app.saveConfig("appRateSnackbarTime")
} }
.autoDismiss(false) .autoDismiss(false)
.swipeToDismiss(true) .swipeToDismiss(true)
@ -471,7 +469,7 @@ class MainActivity : AppCompatActivity() {
bottomSheet.appendItems( bottomSheet.appendItems(
BottomSheetPrimaryItem(false) BottomSheetPrimaryItem(false)
.withTitle(R.string.menu_sync) .withTitle(R.string.menu_sync)
.withIcon(CommunityMaterial.Icon2.cmd_sync) .withIcon(CommunityMaterial.Icon.cmd_download_outline)
.withOnClickListener(View.OnClickListener { .withOnClickListener(View.OnClickListener {
bottomSheet.close() bottomSheet.close()
SyncViewListDialog(this, navTargetId) SyncViewListDialog(this, navTargetId)
@ -479,17 +477,17 @@ class MainActivity : AppCompatActivity() {
BottomSheetSeparatorItem(false), BottomSheetSeparatorItem(false),
BottomSheetPrimaryItem(false) BottomSheetPrimaryItem(false)
.withTitle(R.string.menu_settings) .withTitle(R.string.menu_settings)
.withIcon(CommunityMaterial.Icon2.cmd_settings) .withIcon(CommunityMaterial.Icon2.cmd_settings_outline)
.withOnClickListener(View.OnClickListener { loadTarget(DRAWER_ITEM_SETTINGS) }), .withOnClickListener(View.OnClickListener { loadTarget(DRAWER_ITEM_SETTINGS) }),
BottomSheetPrimaryItem(false) BottomSheetPrimaryItem(false)
.withTitle(R.string.menu_feedback) .withTitle(R.string.menu_feedback)
.withIcon(CommunityMaterial.Icon2.cmd_help_circle) .withIcon(CommunityMaterial.Icon2.cmd_help_circle_outline)
.withOnClickListener(View.OnClickListener { loadTarget(TARGET_FEEDBACK) }) .withOnClickListener(View.OnClickListener { loadTarget(TARGET_FEEDBACK) })
) )
if (App.devMode) { if (App.devMode) {
bottomSheet += BottomSheetPrimaryItem(false) bottomSheet += BottomSheetPrimaryItem(false)
.withTitle(R.string.menu_debug) .withTitle(R.string.menu_debug)
.withIcon(CommunityMaterial.Icon.cmd_android_debug_bridge) .withIcon(CommunityMaterial.Icon.cmd_android_studio)
.withOnClickListener(View.OnClickListener { loadTarget(DRAWER_ITEM_DEBUG) }) .withOnClickListener(View.OnClickListener { loadTarget(DRAWER_ITEM_DEBUG) })
} }
@ -537,9 +535,14 @@ class MainActivity : AppCompatActivity() {
DRAWER_ITEM_MESSAGES -> MessagesFragment.pageSelection DRAWER_ITEM_MESSAGES -> MessagesFragment.pageSelection
else -> 0 else -> 0
} }
val arguments = when (navTargetId) {
DRAWER_ITEM_TIMETABLE -> JsonObject("weekStart" to TimetableFragment.pageSelection?.weekStart?.stringY_m_d)
else -> null
}
EdziennikTask.syncProfile( EdziennikTask.syncProfile(
App.profileId, App.profileId,
listOf(navTargetId to fragmentParam) listOf(navTargetId to fragmentParam),
arguments
).enqueue(this) ).enqueue(this)
} }
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)
@ -684,30 +687,33 @@ class MainActivity : AppCompatActivity() {
}*/ }*/
if (navLoading) { if (navLoading) {
navLoading = false
b.fragment.removeAllViews() b.fragment.removeAllViews()
if (intentTargetId == -1) if (intentTargetId == -1)
intentTargetId = HOME_ID intentTargetId = HOME_ID
} }
when { when {
app.profile == null -> { app.profile == null || app.profile.id == -1 -> {
if (intentProfileId == -1) if (intentProfileId == -1)
intentProfileId = app.appSharedPrefs.getInt("current_profile_id", 1) intentProfileId = app.appSharedPrefs.getInt("current_profile_id", 1)
loadProfile(intentProfileId, intentTargetId) loadProfile(intentProfileId, intentTargetId, extras)
} }
intentProfileId != -1 -> { intentProfileId != -1 -> {
loadProfile(intentProfileId, intentTargetId) if (app.profile.id != intentProfileId)
loadProfile(intentProfileId, intentTargetId, extras)
else
loadTarget(intentTargetId, extras)
} }
intentTargetId != -1 -> { intentTargetId != -1 -> {
drawer.currentProfile = app.profile.id drawer.currentProfile = app.profile.id
if (navTargetId != intentTargetId) if (navTargetId != intentTargetId || navLoading)
loadTarget(intentTargetId, extras) loadTarget(intentTargetId, extras)
} }
else -> { else -> {
drawer.currentProfile = app.profile.id drawer.currentProfile = app.profile.id
} }
} }
navLoading = false
} }
override fun recreate() { override fun recreate() {
@ -755,7 +761,7 @@ class MainActivity : AppCompatActivity() {
finish() finish()
} }
else { else {
if (!app.appConfig.loginFinished) if (!app.config.loginFinished)
finish() finish()
else { else {
handleIntent(data?.extras) handleIntent(data?.extras)
@ -792,13 +798,16 @@ class MainActivity : AppCompatActivity() {
this.runOnUiThread { this.runOnUiThread {
if (app.profile == null) { if (app.profile == null) {
LoginActivity.firstCompleted = false LoginActivity.firstCompleted = false
if (app.appConfig.loginFinished) { if (app.config.loginFinished) {
// this shouldn't run // this shouldn't run
profileListEmptyListener() profileListEmptyListener()
} }
} else { } else {
setDrawerItems() setDrawerItems()
drawer.currentProfile = app.profile.id // the drawer profile is updated automatically when the drawer item is clicked
// update it manually when switching profiles from other source
//if (drawer.currentProfile != app.profile.id)
drawer.currentProfile = app.profile.id
loadTarget(drawerSelection, arguments) loadTarget(drawerSelection, arguments)
} }
} }
@ -965,7 +974,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.appConfig.miniDrawerButtonIds.contains(target.id)) .withHiddenInMiniDrawer(!app.config.ui.miniMenuButtons.contains(target.id))
.also { if (target.description != null) it.withDescription(target.description!!) } .also { if (target.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!!)) }

View File

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

View File

@ -52,9 +52,9 @@ class WidgetTimetable : AppWidgetProvider() {
val app = context.applicationContext as App val app = context.applicationContext as App
var bellSyncDiffMillis: Long = 0 var bellSyncDiffMillis: Long = 0
if (app.appConfig.bellSyncDiff != null) { app.config.timetable.bellSyncDiff?.let {
bellSyncDiffMillis = (app.appConfig.bellSyncDiff.hour * 60 * 60 * 1000 + app.appConfig.bellSyncDiff.minute * 60 * 1000 + app.appConfig.bellSyncDiff.second * 1000).toLong() bellSyncDiffMillis = (it.hour * 60 * 60 * 1000 + it.minute * 60 * 1000 + it.second * 1000).toLong()
bellSyncDiffMillis *= app.appConfig.bellSyncMultiplier.toLong() bellSyncDiffMillis *= app.config.timetable.bellSyncMultiplier.toLong()
bellSyncDiffMillis *= -1 bellSyncDiffMillis *= -1
} }
@ -87,7 +87,7 @@ class WidgetTimetable : AppWidgetProvider() {
.colorInt(Color.WHITE) .colorInt(Color.WHITE)
.sizeDp(if (widgetConfig.bigStyle) 24 else 16).toBitmap()) .sizeDp(if (widgetConfig.bigStyle) 24 else 16).toBitmap())
views.setImageViewBitmap(R.id.widgetTimetableSync, IconicsDrawable(context, CommunityMaterial.Icon2.cmd_sync) views.setImageViewBitmap(R.id.widgetTimetableSync, IconicsDrawable(context, CommunityMaterial.Icon.cmd_download_outline)
.colorInt(Color.WHITE) .colorInt(Color.WHITE)
.sizeDp(if (widgetConfig.bigStyle) 24 else 16).toBitmap()) .sizeDp(if (widgetConfig.bigStyle) 24 else 16).toBitmap())

View File

@ -60,6 +60,9 @@ class ApiService : Service() {
private val notification by lazy { EdziennikNotification(this) } private val notification by lazy { EdziennikNotification(this) }
private var lastEventTime = System.currentTimeMillis()
private var taskCancelTries = 0
/* ______ _ _ _ _ _____ _ _ _ _ /* ______ _ _ _ _ _____ _ _ _ _
| ____| | | (_) (_) | / ____| | | | | | | | ____| | | (_) (_) | / ____| | | | | | |
| |__ __| |_____ ___ _ __ _ __ _| | __ | | __ _| | | |__ __ _ ___| | __ | |__ __| |_____ ___ _ __ _ __ _| | __ | | __ _| | | |__ __ _ ___| | __
@ -68,22 +71,17 @@ class ApiService : Service() {
|______\__,_/___|_|\___|_| |_|_| |_|_|_|\_\ \_____\__,_|_|_|_.__/ \__,_|\___|_|\*/ |______\__,_/___|_|\___|_| |_|_| |_|_|_|\_\ \_____\__,_|_|_|_.__/ \__,_|\___|_|\*/
private val taskCallback = object : EdziennikCallback { private val taskCallback = object : EdziennikCallback {
override fun onCompleted() { override fun onCompleted() {
lastEventTime = System.currentTimeMillis()
d(TAG, "Task $taskRunningId (profile $taskProfileId) - $taskProgressText - finished") d(TAG, "Task $taskRunningId (profile $taskProfileId) - $taskProgressText - finished")
//if (!taskCancelled) { EventBus.getDefault().post(ApiTaskFinishedEvent(taskProfileId))
EventBus.getDefault().post(ApiTaskFinishedEvent(taskProfileId)) clearTask()
//}
taskIsRunning = false
taskRunningId = -1
taskRunning = null
taskProfileId = -1
taskProgress = -1f
taskProgressText = null
notification.setIdle().post() notification.setIdle().post()
runTask() runTask()
} }
override fun onError(apiError: ApiError) { override fun onError(apiError: ApiError) {
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().post(ApiTaskErrorEvent(apiError)) EventBus.getDefault().post(ApiTaskErrorEvent(apiError))
@ -92,9 +90,7 @@ class ApiService : Service() {
if (apiError.isCritical) { if (apiError.isCritical) {
taskRunning?.cancel() taskRunning?.cancel()
notification.setCriticalError().post() notification.setCriticalError().post()
taskRunning = null clearTask()
taskIsRunning = false
taskRunningId = -1
runTask() runTask()
} }
else { else {
@ -103,6 +99,7 @@ class ApiService : Service() {
} }
override fun onProgress(step: Float) { override fun onProgress(step: Float) {
lastEventTime = System.currentTimeMillis()
if (step <= 0) if (step <= 0)
return return
if (taskProgress < 0) if (taskProgress < 0)
@ -115,6 +112,7 @@ class ApiService : Service() {
} }
override fun onStartProgress(stringRes: Int) { override fun onStartProgress(stringRes: Int) {
lastEventTime = System.currentTimeMillis()
taskProgressText = getString(stringRes) taskProgressText = getString(stringRes)
d(TAG, "Task $taskRunningId progress: $taskProgressText") d(TAG, "Task $taskRunningId progress: $taskProgressText")
EventBus.getDefault().post(ApiTaskProgressEvent(taskProfileId, taskProgress, taskProgressText)) EventBus.getDefault().post(ApiTaskProgressEvent(taskProfileId, taskProgress, taskProgressText))
@ -129,6 +127,7 @@ class ApiService : Service() {
| | (_| \__ \ < | __/> < __/ (__| |_| | |_| | (_) | | | | | | (_| \__ \ < | __/> < __/ (__| |_| | |_| | (_) | | | |
|_|\__,_|___/_|\_\ \___/_/\_\___|\___|\__,_|\__|_|\___/|_| |*/ |_|\__,_|___/_|\_\ \___/_/\_\___|\___|\__,_|\__|_|\___/|_| |*/
private fun runTask() { private fun runTask() {
checkIfTaskFrozen()
if (taskIsRunning) if (taskIsRunning)
return return
if (taskCancelled || serviceClosed || (taskQueue.isEmpty() && finishingTaskQueue.isEmpty())) { if (taskCancelled || serviceClosed || (taskQueue.isEmpty() && finishingTaskQueue.isEmpty())) {
@ -137,6 +136,8 @@ class ApiService : Service() {
return return
} }
lastEventTime = System.currentTimeMillis()
val task = if (taskQueue.isEmpty()) finishingTaskQueue.removeAt(0) else taskQueue.removeAt(0) val task = if (taskQueue.isEmpty()) finishingTaskQueue.removeAt(0) else taskQueue.removeAt(0)
task.taskId = ++taskMaximumId task.taskId = ++taskMaximumId
task.prepare(app) task.prepare(app)
@ -166,6 +167,48 @@ class ApiService : Service() {
} }
} }
/**
* Check if a task is inactive for more than 30 seconds.
* If the user tries to cancel a task with no success at least three times,
* consider it frozen as well.
*
* This usually means it is broken and won't become active again.
* This method cancels the task and removes any pointers to it.
*/
private fun checkIfTaskFrozen(): Boolean {
if (System.currentTimeMillis() - lastEventTime > 30*1000
|| taskCancelTries >= 3) {
val time = System.currentTimeMillis() - lastEventTime
d(TAG, "!!! Task $taskRunningId froze for $time ms. $taskRunning")
clearTask()
return true
}
return false
}
/**
* Stops the service if the current task is frozen/broken.
*/
private fun stopIfTaskFrozen() {
if (checkIfTaskFrozen()) {
stopSelf()
}
}
/**
* Remove any task descriptors or pointers from the service.
*/
private fun clearTask() {
taskIsRunning = false
taskRunningId = -1
taskRunning = null
taskProfileId = -1
taskProgress = -1f
taskProgressText = null
taskCancelled = false
taskCancelTries = 0
}
private fun allCompleted() { private fun allCompleted() {
EventBus.getDefault().post(ApiTaskAllFinishedEvent()) EventBus.getDefault().post(ApiTaskAllFinishedEvent())
stopSelf() stopSelf()
@ -211,8 +254,10 @@ class ApiService : Service() {
EventBus.getDefault().removeStickyEvent(request) EventBus.getDefault().removeStickyEvent(request)
d(TAG, request.toString()) d(TAG, request.toString())
taskCancelTries++
taskCancelled = true taskCancelled = true
taskRunning?.cancel() taskRunning?.cancel()
stopIfTaskFrozen()
} }
@Subscribe(sticky = true, threadMode = ThreadMode.ASYNC) @Subscribe(sticky = true, threadMode = ThreadMode.ASYNC)
fun onServiceCloseRequest(request: ServiceCloseRequest) { fun onServiceCloseRequest(request: ServiceCloseRequest) {

View File

@ -64,6 +64,7 @@ const val IDZIENNIK_WEB_TIMETABLE = "mod_panelRodzica/plan/WS_Plan.asmx/pobierzP
const val IDZIENNIK_WEB_GRADES = "mod_panelRodzica/oceny/WS_ocenyUcznia.asmx/pobierzOcenyUcznia" const val IDZIENNIK_WEB_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"
const val IDZIENNIK_WEB_EXAMS = "mod_panelRodzica/sprawdziany/mod_sprawdzianyPanel.asmx/pobierzListe" const val IDZIENNIK_WEB_EXAMS = "mod_panelRodzica/sprawdziany/mod_sprawdzianyPanel.asmx/pobierzListe"
const val IDZIENNIK_WEB_HOMEWORK = "mod_panelRodzica/pracaDomowa/WS_pracaDomowa.asmx/pobierzPraceDomowe"
const val IDZIENNIK_WEB_NOTICES = "mod_panelRodzica/uwagi/WS_uwagiUcznia.asmx/pobierzUwagiUcznia" const val IDZIENNIK_WEB_NOTICES = "mod_panelRodzica/uwagi/WS_uwagiUcznia.asmx/pobierzUwagiUcznia"
const val IDZIENNIK_WEB_ATTENDANCE = "mod_panelRodzica/obecnosci/WS_obecnosciUcznia.asmx/pobierzObecnosciUcznia" const val IDZIENNIK_WEB_ATTENDANCE = "mod_panelRodzica/obecnosci/WS_obecnosciUcznia.asmx/pobierzObecnosciUcznia"
const val IDZIENNIK_WEB_ANNOUNCEMENTS = "mod_panelRodzica/tabOgl/WS_tablicaOgloszen.asmx/GetOgloszenia" const val IDZIENNIK_WEB_ANNOUNCEMENTS = "mod_panelRodzica/tabOgl/WS_tablicaOgloszen.asmx/GetOgloszenia"

View File

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

View File

@ -48,6 +48,7 @@ const val ERROR_PROFILE_MISSING = 105
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
const val ERROR_FILE_DOWNLOAD = 113
const val ERROR_NO_STUDENTS_IN_ACCOUNT = 115 const val ERROR_NO_STUDENTS_IN_ACCOUNT = 115
@ -167,6 +168,8 @@ const val EXCEPTION_LIBRUS_API_REQUEST = 904
const val EXCEPTION_LIBRUS_SYNERGIA_REQUEST = 905 const val EXCEPTION_LIBRUS_SYNERGIA_REQUEST = 905
const val EXCEPTION_MOBIDZIENNIK_WEB_REQUEST = 906 const val EXCEPTION_MOBIDZIENNIK_WEB_REQUEST = 906
const val EXCEPTION_VULCAN_API_REQUEST = 907 const val EXCEPTION_VULCAN_API_REQUEST = 907
const val EXCEPTION_MOBIDZIENNIK_WEB_FILE_REQUEST = 908
const val EXCEPTION_LIBRUS_MESSAGES_FILE_REQUEST = 909
const val EXCEPTION_NOTIFY_AND_SYNC = 910 const val EXCEPTION_NOTIFY_AND_SYNC = 910
const val EXCEPTION_LIBRUS_MESSAGES_REQUEST = 911 const val EXCEPTION_LIBRUS_MESSAGES_REQUEST = 911
const val EXCEPTION_IDZIENNIK_WEB_REQUEST = 912 const val EXCEPTION_IDZIENNIK_WEB_REQUEST = 912

View File

@ -24,7 +24,7 @@ object Regexes {
"""Liczona do średniej:.*?<strong>nie<br/?></strong>""".toRegex(RegexOption.DOT_MATCHES_ALL) """Liczona do średniej:.*?<strong>nie<br/?></strong>""".toRegex(RegexOption.DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_GRADES_DETAILS by lazy { val MOBIDZIENNIK_GRADES_DETAILS by lazy {
"""<strong.*?>(.+?)</strong>.*?<sup>.+?</sup>.*?<small>\((.+?)\)</small>.*?<span>.*?Wartość oceny:.*?<strong>([0-9.]+)</strong>.*?Wpisał\(a\):.*?<strong>(.+?)</strong>""".toRegex(RegexOption.DOT_MATCHES_ALL) """<strong.*?>(.+?)</strong>.*?<sup>.+?</sup>.*?(?:<small>\((.+?)\)</small>.*?)?<span>.*?Wartość oceny:.*?<strong>([0-9.]+)</strong>.*?Wpisał\(a\):.*?<strong>(.+?)</strong>.*?(?:Komentarz:.*?<strong>(.+?)</strong>)?</span>""".toRegex(RegexOption.DOT_MATCHES_ALL)
} }
val MOBIDZIENNIK_EVENT_TYPE by lazy { val MOBIDZIENNIK_EVENT_TYPE by lazy {
@ -76,4 +76,10 @@ object Regexes {
val VULCAN_SHITFT_ANNOTATION by lazy { val VULCAN_SHITFT_ANNOTATION by lazy {
"""\(przeniesiona (z|na) lekcj[ię] ([0-9]+), (.+)\)""".toRegex() """\(przeniesiona (z|na) lekcj[ię] ([0-9]+), (.+)\)""".toRegex()
} }
val LIBRUS_ATTACHMENT_KEY by lazy {
"""singleUseKey=([0-9A-f_]+)""".toRegex()
}
} }

View File

@ -5,8 +5,8 @@ import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.models.AppError.CODE_APP_SERVER_ERROR
import pl.szczodrzynski.edziennik.api.v2.models.Data import pl.szczodrzynski.edziennik.api.v2.models.Data
import pl.szczodrzynski.edziennik.data.api.AppError.CODE_APP_SERVER_ERROR
import pl.szczodrzynski.edziennik.data.db.modules.events.Event import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_HOMEWORK import pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_HOMEWORK
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
@ -176,4 +176,4 @@ class ServerSync(val data: Data, val onSuccess: () -> Unit) {
onSuccess() onSuccess()
}} }}
} }

View File

@ -0,0 +1,14 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-11-24
*/
package pl.szczodrzynski.edziennik.api.v2.events
data class AttachmentGetEvent(val profileId: Int, val messageId: Long, val attachmentId: Long,
var eventType: Int = TYPE_PROGRESS, val fileName: String? = null,
val bytesWritten: Long = 0) {
companion object {
const val TYPE_PROGRESS = 0
const val TYPE_FINISHED = 1
}
}

View File

@ -12,6 +12,7 @@ import pl.szczodrzynski.edziennik.api.v2.mobidziennik.Mobidziennik
import pl.szczodrzynski.edziennik.api.v2.template.Template import pl.szczodrzynski.edziennik.api.v2.template.Template
import pl.szczodrzynski.edziennik.api.v2.vulcan.Vulcan import pl.szczodrzynski.edziennik.api.v2.vulcan.Vulcan
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull import pl.szczodrzynski.edziennik.data.db.modules.messages.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) {
@ -24,6 +25,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
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 announcementsRead(profileId: Int) = EdziennikTask(profileId, AnnouncementsReadRequest()) fun announcementsRead(profileId: Int) = EdziennikTask(profileId, AnnouncementsReadRequest())
fun attachmentGet(profileId: Int, message: Message, attachmentId: Long, attachmentName: String) = EdziennikTask(profileId, AttachmentGetRequest(message, attachmentId, attachmentName))
} }
private lateinit var loginStore: LoginStore private lateinit var loginStore: LoginStore
@ -35,8 +37,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
loginStore = request.loginStore loginStore = request.loginStore
// save the profile ID and name as the current task's // save the profile ID and name as the current task's
taskName = app.getString(R.string.edziennik_notification_api_first_login_title) taskName = app.getString(R.string.edziennik_notification_api_first_login_title)
} } else {
else {
// get the requested profile and login store // get the requested profile and login store
val profile = app.db.profileDao().getByIdNow(profileId) val profile = app.db.profileDao().getByIdNow(profileId)
this.profile = profile this.profile = profile
@ -67,12 +68,14 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
when (request) { when (request) {
is SyncProfileRequest -> edziennikInterface?.sync( is SyncProfileRequest -> edziennikInterface?.sync(
featureIds = request.viewIds?.flatMap { Features.getIdsByView(it.first, it.second) } ?: Features.getAllIds(), featureIds = request.viewIds?.flatMap { Features.getIdsByView(it.first, it.second) }
?: Features.getAllIds(),
viewId = request.viewIds?.get(0)?.first, viewId = request.viewIds?.get(0)?.first,
arguments = request.arguments) arguments = request.arguments)
is MessageGetRequest -> edziennikInterface?.getMessage(request.message) is MessageGetRequest -> edziennikInterface?.getMessage(request.message)
is FirstLoginRequest -> edziennikInterface?.firstLogin() is FirstLoginRequest -> edziennikInterface?.firstLogin()
is AnnouncementsReadRequest -> edziennikInterface?.markAllAnnouncementsAsRead() is AnnouncementsReadRequest -> edziennikInterface?.markAllAnnouncementsAsRead()
is AttachmentGetRequest -> edziennikInterface?.getAttachment(request.message, request.attachmentId, request.attachmentName)
} }
} }
@ -90,4 +93,5 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
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)
class AnnouncementsReadRequest class AnnouncementsReadRequest
data class AttachmentGetRequest(val message: Message, val attachmentId: Long, val attachmentName: String)
} }

View File

@ -9,6 +9,7 @@ import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.Notifier.ID_NOTIFICATIONS import pl.szczodrzynski.edziennik.Notifier.ID_NOTIFICATIONS
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.db.modules.notification.getNotificationTitle
import pl.szczodrzynski.edziennik.utils.models.Notification import pl.szczodrzynski.edziennik.utils.models.Notification
import kotlin.math.min import kotlin.math.min
@ -33,9 +34,9 @@ class NotifyTask : IApiTask(-1) {
val pendingIntent = PendingIntent.getActivity(app, notification.id, intent, 0) val pendingIntent = PendingIntent.getActivity(app, notification.id, intent, 0)
val notificationBuilder = NotificationCompat.Builder(app, app.notifier.notificationGroup) val notificationBuilder = NotificationCompat.Builder(app, app.notifier.notificationGroup)
// title, text, type, date // title, text, type, date
.setContentTitle(notification.title) .setContentTitle(notification.profileName)
.setContentText(notification.text) .setContentText(notification.text)
.setSubText(Notification.stringType(app, notification.type)) .setSubText(app.getNotificationTitle(notification.type))
.setWhen(notification.addedDate) .setWhen(notification.addedDate)
.setTicker(app.getString(R.string.notification_ticker_format, Notification.stringType(app, notification.type))) .setTicker(app.getString(R.string.notification_ticker_format, Notification.stringType(app, notification.type)))
// icon, color, lights, priority // icon, color, lights, priority

View File

@ -138,10 +138,12 @@ class DataIdziennik(app: App, profile: Profile?, loginStore: LoginStore) : Data(
val teacher = teacherList.singleOrNull { it.fullName == "$firstName $lastName" } val teacher = teacherList.singleOrNull { it.fullName == "$firstName $lastName" }
return validateTeacher(teacher, firstName, lastName) return validateTeacher(teacher, firstName, lastName)
} }
fun getTeacher(firstNameChar: Char, lastName: String): Teacher { fun getTeacher(firstNameChar: Char, lastName: String): Teacher {
val teacher = teacherList.singleOrNull { it.shortName == "$firstNameChar.$lastName" } val teacher = teacherList.singleOrNull { it.shortName == "$firstNameChar.$lastName" }
return validateTeacher(teacher, firstNameChar.toString(), lastName) return validateTeacher(teacher, firstNameChar.toString(), lastName)
} }
fun getTeacherByLastFirst(nameLastFirst: String): Teacher { fun getTeacherByLastFirst(nameLastFirst: String): Teacher {
val nameParts = nameLastFirst.split(" ") val nameParts = nameLastFirst.split(" ")
return if (nameParts.size == 1) getTeacher(nameParts[0], "") else getTeacher(nameParts[1], nameParts[0]) return if (nameParts.size == 1) getTeacher(nameParts[0], "") else getTeacher(nameParts[1], nameParts[0])

View File

@ -16,6 +16,7 @@ import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.prepare import pl.szczodrzynski.edziennik.api.v2.prepare
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
@ -70,6 +71,10 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
} }
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
}
override fun firstLogin() { override fun firstLogin() {
IdziennikFirstLogin(data) { IdziennikFirstLogin(data) {
completed() completed()

View File

@ -11,6 +11,7 @@ const val ENDPOINT_IDZIENNIK_WEB_TIMETABLE = 1030
const val ENDPOINT_IDZIENNIK_WEB_GRADES = 1040 const val ENDPOINT_IDZIENNIK_WEB_GRADES = 1040
const val ENDPOINT_IDZIENNIK_WEB_PROPOSED_GRADES = 1050 const val ENDPOINT_IDZIENNIK_WEB_PROPOSED_GRADES = 1050
const val ENDPOINT_IDZIENNIK_WEB_EXAMS = 1060 const val ENDPOINT_IDZIENNIK_WEB_EXAMS = 1060
const val ENDPOINT_IDZIENNIK_WEB_HOMEWORK = 1061
const val ENDPOINT_IDZIENNIK_WEB_NOTICES = 1070 const val ENDPOINT_IDZIENNIK_WEB_NOTICES = 1070
const val ENDPOINT_IDZIENNIK_WEB_ANNOUNCEMENTS = 1080 const val ENDPOINT_IDZIENNIK_WEB_ANNOUNCEMENTS = 1080
const val ENDPOINT_IDZIENNIK_WEB_ATTENDANCE = 1090 const val ENDPOINT_IDZIENNIK_WEB_ATTENDANCE = 1090
@ -34,6 +35,10 @@ val IdziennikFeatures = listOf(
ENDPOINT_IDZIENNIK_WEB_EXAMS to LOGIN_METHOD_IDZIENNIK_WEB ENDPOINT_IDZIENNIK_WEB_EXAMS to LOGIN_METHOD_IDZIENNIK_WEB
), listOf(LOGIN_METHOD_IDZIENNIK_WEB)), ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)),
Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_HOMEWORK, listOf(
ENDPOINT_IDZIENNIK_WEB_HOMEWORK to LOGIN_METHOD_IDZIENNIK_WEB
), listOf(LOGIN_METHOD_IDZIENNIK_WEB)),
Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_BEHAVIOUR, listOf( Feature(LOGIN_TYPE_IDZIENNIK, FEATURE_BEHAVIOUR, listOf(
ENDPOINT_IDZIENNIK_WEB_NOTICES to LOGIN_METHOD_IDZIENNIK_WEB ENDPOINT_IDZIENNIK_WEB_NOTICES to LOGIN_METHOD_IDZIENNIK_WEB
), listOf(LOGIN_METHOD_IDZIENNIK_WEB)), ), listOf(LOGIN_METHOD_IDZIENNIK_WEB)),

View File

@ -55,6 +55,10 @@ class IdziennikData(val data: DataIdziennik, val onSuccess: () -> Unit) {
data.startProgress(R.string.edziennik_progress_endpoint_exams) data.startProgress(R.string.edziennik_progress_endpoint_exams)
IdziennikWebExams(data, onSuccess) IdziennikWebExams(data, onSuccess)
} }
ENDPOINT_IDZIENNIK_WEB_HOMEWORK -> {
data.startProgress(R.string.edziennik_progress_endpoint_homework)
IdziennikWebHomework(data, 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, onSuccess)

View File

@ -94,6 +94,7 @@ open class IdziennikWeb(open val data: DataIdziennik) {
is Long -> json.addProperty(name, value) is Long -> json.addProperty(name, value)
is Float -> json.addProperty(name, value) is Float -> json.addProperty(name, value)
is Char -> json.addProperty(name, value) is Char -> json.addProperty(name, value)
is Boolean -> json.addProperty(name, value)
} }
} }
setJsonBody(json) setJsonBody(json)

View File

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

View File

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

View File

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

View File

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

View File

@ -5,12 +5,14 @@
package pl.szczodrzynski.edziennik.api.v2.interfaces package pl.szczodrzynski.edziennik.api.v2.interfaces
import com.google.gson.JsonObject import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
interface EdziennikInterface { interface EdziennikInterface {
fun sync(featureIds: List<Int>, viewId: Int? = null, arguments: JsonObject? = null) fun sync(featureIds: List<Int>, viewId: Int? = null, arguments: JsonObject? = null)
fun getMessage(message: MessageFull) fun getMessage(message: MessageFull)
fun markAllAnnouncementsAsRead() fun markAllAnnouncementsAsRead()
fun getAttachment(message: Message, attachmentId: Long, attachmentName: String)
fun firstLogin() fun firstLogin()
fun cancel() fun cancel()
} }

View File

@ -10,15 +10,14 @@ import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusData import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusData
import pl.szczodrzynski.edziennik.api.v2.librus.data.messages.LibrusMessagesGetAttachment
import pl.szczodrzynski.edziennik.api.v2.librus.data.messages.LibrusMessagesGetMessage import pl.szczodrzynski.edziennik.api.v2.librus.data.messages.LibrusMessagesGetMessage
import pl.szczodrzynski.edziennik.api.v2.librus.data.synergia.LibrusSynergiaMarkAllAnnouncementsAsRead import pl.szczodrzynski.edziennik.api.v2.librus.data.synergia.LibrusSynergiaMarkAllAnnouncementsAsRead
import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.LibrusFirstLogin import pl.szczodrzynski.edziennik.api.v2.librus.firstlogin.LibrusFirstLogin
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLogin import pl.szczodrzynski.edziennik.api.v2.librus.login.*
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginApi
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginMessages
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginSynergia
import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
@ -82,11 +81,13 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
} }
override fun getMessage(message: MessageFull) { override fun getMessage(message: MessageFull) {
LibrusLoginApi(data) { LibrusLoginPortal(data) {
LibrusLoginSynergia(data) { LibrusLoginApi(data) {
LibrusLoginMessages(data) { LibrusLoginSynergia(data) {
LibrusMessagesGetMessage(data, message) { LibrusLoginMessages(data) {
completed() LibrusMessagesGetMessage(data, message) {
completed()
}
} }
} }
} }
@ -94,10 +95,26 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
} }
override fun markAllAnnouncementsAsRead() { override fun markAllAnnouncementsAsRead() {
LibrusLoginApi(data) { LibrusLoginPortal(data) {
LibrusLoginSynergia(data) { LibrusLoginApi(data) {
LibrusSynergiaMarkAllAnnouncementsAsRead(data) { LibrusLoginSynergia(data) {
completed() LibrusSynergiaMarkAllAnnouncementsAsRead(data) {
completed()
}
}
}
}
}
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
LibrusLoginPortal(data) {
LibrusLoginApi(data) {
LibrusLoginSynergia(data) {
LibrusLoginMessages(data) {
LibrusMessagesGetAttachment(data, message, attachmentId, attachmentName) {
completed()
}
}
} }
} }
} }

View File

@ -60,7 +60,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.appConfig.fcmTokens[LOGIN_TYPE_LIBRUS]?.second?.contains(data.profileId) == false !data.app.config.sync.tokenLibrusList.contains(data.profileId)
}, },

View File

@ -4,9 +4,12 @@
package pl.szczodrzynski.edziennik.api.v2.librus.data package pl.szczodrzynski.edziennik.api.v2.librus.data
import com.google.gson.JsonObject
import im.wangchao.mhttp.Request import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response import im.wangchao.mhttp.Response
import im.wangchao.mhttp.body.MediaTypeUtils import im.wangchao.mhttp.body.MediaTypeUtils
import im.wangchao.mhttp.callback.FileCallbackHandler
import im.wangchao.mhttp.callback.JsonCallbackHandler
import im.wangchao.mhttp.callback.TextCallbackHandler import im.wangchao.mhttp.callback.TextCallbackHandler
import okhttp3.Cookie import okhttp3.Cookie
import org.jsoup.Jsoup import org.jsoup.Jsoup
@ -16,6 +19,7 @@ import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
import java.io.File
import java.io.StringWriter import java.io.StringWriter
import javax.xml.parsers.DocumentBuilderFactory import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.OutputKeys import javax.xml.transform.OutputKeys
@ -131,4 +135,95 @@ open class LibrusMessages(open val data: DataLibrus) {
.build() .build()
.enqueue() .enqueue()
} }
fun sandboxGet(tag: String, action: String, parameters: Map<String, Any>? = null,
onSuccess: (json: JsonObject) -> Unit) {
d(tag, "Request: Librus/Messages - $LIBRUS_SANDBOX_URL$action")
val callback = object : JsonCallbackHandler() {
override fun onSuccess(json: JsonObject?, response: Response?) {
if (json == null) {
data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY)
.withResponse(response))
return
}
try {
onSuccess(json)
} catch (e: Exception) {
data.error(ApiError(tag, EXCEPTION_LIBRUS_MESSAGES_REQUEST)
.withResponse(response)
.withThrowable(e)
.withApiResponse(json))
}
}
override fun onFailure(response: Response?, throwable: Throwable?) {
data.error(ApiError(tag, ERROR_REQUEST_FAILURE)
.withResponse(response)
.withThrowable(throwable))
}
}
Request.builder()
.url("$LIBRUS_SANDBOX_URL$action")
.userAgent(SYNERGIA_USER_AGENT)
.apply {
parameters?.forEach { (k, v) ->
addParameter(k, v)
}
}
.post()
.callback(callback)
.build()
.enqueue()
}
fun sandboxGetFile(tag: String, action: String, targetFile: File, onSuccess: (file: File) -> Unit,
onProgress: (written: Long, total: Long) -> Unit) {
d(tag, "Request: Librus/Messages - $LIBRUS_SANDBOX_URL$action")
val callback = object : FileCallbackHandler(targetFile) {
override fun onSuccess(file: File?, response: Response?) {
if (file == null) {
data.error(ApiError(TAG, ERROR_FILE_DOWNLOAD)
.withResponse(response))
return
}
try {
onSuccess(file)
} catch (e: Exception) {
data.error(ApiError(tag, EXCEPTION_LIBRUS_MESSAGES_FILE_REQUEST)
.withResponse(response)
.withThrowable(e))
}
}
override fun onProgress(bytesWritten: Long, bytesTotal: Long) {
try {
onProgress(bytesWritten, bytesTotal)
} catch (e: Exception) {
data.error(ApiError(tag, EXCEPTION_LIBRUS_MESSAGES_FILE_REQUEST)
.withThrowable(e))
}
}
override fun onFailure(response: Response?, throwable: Throwable?) {
data.error(ApiError(tag, ERROR_REQUEST_FAILURE)
.withResponse(response)
.withThrowable(throwable))
}
}
Request.builder()
.url("$LIBRUS_SANDBOX_URL$action")
.userAgent(SYNERGIA_USER_AGENT)
.post()
.callback(callback)
.build()
.enqueue()
}
} }

View File

@ -36,14 +36,14 @@ class LibrusApiAttendances(override val data: DataLibrus,
val lessonNo = attendance.getInt("LessonNo") ?: return@forEach val lessonNo = attendance.getInt("LessonNo") ?: return@forEach
val startTime = data.lessonRanges.get(lessonNo).startTime val startTime = data.lessonRanges.get(lessonNo).startTime
val lessonDate = Date.fromY_m_d(attendance.getString("Date")) val lessonDate = Date.fromY_m_d(attendance.getString("Date"))
val subjectId = data.lessonList.singleOrNull {
it.weekDay == lessonDate.weekDay && it.startTime.value == startTime.value
}?.subjectId ?: -1
val semester = attendance.getInt("Semester") ?: return@forEach val 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.get(type)
val topic = typeObject?.name ?: "" val topic = typeObject?.name ?: ""
val lessonList = data.db.timetableDao().getForDateNow(profileId, lessonDate)
val subjectId = lessonList.firstOrNull { it.startTime == startTime }?.subjectId ?: -1
val attendanceObject = Attendance( val attendanceObject = Attendance(
profileId, profileId,
id, id,

View File

@ -9,6 +9,7 @@ import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_EVENTS import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_EVENTS
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.events.Event import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
@ -69,6 +70,8 @@ class LibrusApiEvents(override val data: DataLibrus,
)) ))
} }
data.toRemove.add(DataRemoveModel.Events.futureExceptType(Event.TYPE_HOMEWORK))
data.setSyncNext(ENDPOINT_LIBRUS_API_EVENTS, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_LIBRUS_API_EVENTS, SYNC_ALWAYS)
onSuccess() onSuccess()
} }

View File

@ -8,6 +8,7 @@ import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_HOMEWORK import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_API_HOMEWORK
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS import pl.szczodrzynski.edziennik.data.db.modules.api.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.modules.events.Event import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
@ -55,6 +56,8 @@ class LibrusApiHomework(override val data: DataLibrus,
)) ))
} }
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_HOMEWORK))
data.setSyncNext(ENDPOINT_LIBRUS_API_HOMEWORK, SYNC_ALWAYS) data.setSyncNext(ENDPOINT_LIBRUS_API_HOMEWORK, SYNC_ALWAYS)
onSuccess() onSuccess()
} }

View File

@ -17,6 +17,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
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
class LibrusApiTimetables(override val data: DataLibrus, class LibrusApiTimetables(override val data: DataLibrus,
val onSuccess: () -> Unit) : LibrusApi(data) { val onSuccess: () -> Unit) : LibrusApi(data) {
@ -29,9 +30,18 @@ class LibrusApiTimetables(override val data: DataLibrus,
data.db.classroomDao().getAllNow(profileId).toSparseArray(data.classrooms) { it.id } data.db.classroomDao().getAllNow(profileId).toSparseArray(data.classrooms) { it.id }
} }
val currentWeekStart = Date.getToday().let { it.stepForward(0, 0, -it.weekDay) } val currentWeekStart = Week.getWeekStart()
if (Date.getToday().weekDay > 4) {
currentWeekStart.stepForward(0, 0, 7)
}
val getDate = data.arguments?.getString("weekStart") ?: currentWeekStart.stringY_m_d val getDate = data.arguments?.getString("weekStart") ?: currentWeekStart.stringY_m_d
apiGet(TAG, "Timetables?weekStart=$getDate") { json ->
val weekStart = Date.fromY_m_d(getDate)
val weekEnd = weekStart.clone().stepForward(0, 0, 6)
apiGet(TAG, "Timetables?weekStart=${weekStart.stringY_m_d}") { json ->
val days = json.getJsonObject("Timetable") val days = json.getJsonObject("Timetable")
days?.entrySet()?.forEach { (dateString, dayEl) -> days?.entrySet()?.forEach { (dateString, dayEl) ->
@ -57,8 +67,6 @@ class LibrusApiTimetables(override val data: DataLibrus,
} }
} }
val weekStart = Date.fromY_m_d(getDate)
val weekEnd = weekStart.clone().stepForward(0, 0, 6)
d(TAG, "Clearing lessons between ${weekStart.stringY_m_d} and ${weekEnd.stringY_m_d} - timetable downloaded for $getDate") d(TAG, "Clearing lessons between ${weekStart.stringY_m_d} and ${weekEnd.stringY_m_d} - timetable downloaded for $getDate")
data.toRemove.add(DataRemoveModel.Timetable.between(weekStart, weekEnd)) data.toRemove.add(DataRemoveModel.Timetable.between(weekStart, weekEnd))
@ -67,7 +75,7 @@ class LibrusApiTimetables(override val data: DataLibrus,
} }
} }
private fun parseLesson(lessonDate: Date, lesson: JsonObject) { private fun parseLesson(lessonDate: Date, lesson: JsonObject) { data.profile?.also { profile ->
val isSubstitution = lesson.getBoolean("IsSubstitutionClass") ?: false val isSubstitution = lesson.getBoolean("IsSubstitutionClass") ?: false
val isCancelled = lesson.getBoolean("IsCanceled") ?: false val isCancelled = lesson.getBoolean("IsCanceled") ?: false
@ -80,8 +88,7 @@ class LibrusApiTimetables(override val data: DataLibrus,
val virtualClassId = lesson.getJsonObject("VirtualClass")?.getLong("Id") val virtualClassId = lesson.getJsonObject("VirtualClass")?.getLong("Id")
val teamId = lesson.getJsonObject("Class")?.getLong("Id") ?: virtualClassId val teamId = lesson.getJsonObject("Class")?.getLong("Id") ?: virtualClassId
val id = lessonDate.combineWith(startTime) / 6L * 10L + (lesson.hashCode() and 0xFFFF) val lessonObject = Lesson(profileId, -1)
val lessonObject = Lesson(profileId, id)
if (isSubstitution && isCancelled) { if (isSubstitution && isCancelled) {
// shifted lesson - source // shifted lesson - source
@ -176,17 +183,21 @@ class LibrusApiTimetables(override val data: DataLibrus,
} }
} }
lessonObject.id = lessonObject.buildId()
val seen = profile.empty || lessonDate < Date.getToday()
if (lessonObject.type != Lesson.TYPE_NORMAL) { if (lessonObject.type != Lesson.TYPE_NORMAL) {
data.metadataList.add( data.metadataList.add(
Metadata( Metadata(
data.profileId, profileId,
Metadata.TYPE_LESSON_CHANGE, Metadata.TYPE_LESSON_CHANGE,
lessonObject.id, lessonObject.id,
data.profile?.empty ?: false, seen,
data.profile?.empty ?: false, seen,
System.currentTimeMillis() System.currentTimeMillis()
)) ))
} }
data.lessonNewList.add(lessonObject) data.lessonNewList.add(lessonObject)
} }}
} }

View File

@ -0,0 +1,123 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-11-24
*/
package pl.szczodrzynski.edziennik.api.v2.librus.data.messages
import kotlinx.coroutines.*
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.api.v2.ERROR_FILE_DOWNLOAD
import pl.szczodrzynski.edziennik.api.v2.EXCEPTION_LIBRUS_MESSAGES_REQUEST
import pl.szczodrzynski.edziennik.api.v2.Regexes
import pl.szczodrzynski.edziennik.api.v2.events.AttachmentGetEvent
import pl.szczodrzynski.edziennik.api.v2.events.AttachmentGetEvent.Companion.TYPE_FINISHED
import pl.szczodrzynski.edziennik.api.v2.events.AttachmentGetEvent.Companion.TYPE_PROGRESS
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusMessages
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.utils.Utils
import java.io.File
import kotlin.coroutines.CoroutineContext
class LibrusMessagesGetAttachment(
override val data: DataLibrus, val message: Message, val attachmentId: Long,
val attachmentName: String, val onSuccess: () -> Unit) : LibrusMessages(data), CoroutineScope {
companion object {
const val TAG = "LibrusMessagesGetAttachment"
}
private var job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Default
private var getAttachmentCheckKeyTries = 0
init {
messagesGet(TAG, "GetFileDownloadLink", parameters = mapOf(
"fileId" to attachmentId,
"msgId" to message.id,
"archive" to 0
)) { doc ->
val downloadLink = doc.select("response GetFileDownloadLink downloadLink").text()
val keyMatcher = Regexes.LIBRUS_ATTACHMENT_KEY.find(downloadLink)
if (keyMatcher != null) {
getAttachmentCheckKeyTries = 0
val attachmentKey = keyMatcher[1]
getAttachmentCheckKey(attachmentKey) {
downloadAttachment(attachmentKey)
}
} else {
data.error(ApiError(TAG, ERROR_FILE_DOWNLOAD)
.withApiResponse(doc.toString()))
}
}
}
private fun getAttachmentCheckKey(attachmentKey: String, callback: () -> Unit) {
sandboxGet(TAG, "CSCheckKey",
parameters = mapOf("singleUseKey" to attachmentKey)) { json ->
when (json.getString("status")) {
"not_downloaded_yet" -> {
if (getAttachmentCheckKeyTries++ > 5) {
data.error(ApiError(TAG, ERROR_FILE_DOWNLOAD)
.withApiResponse(json))
return@sandboxGet
}
launch {
delay(2000)
getAttachmentCheckKey(attachmentKey, callback)
}
}
"ready" -> {
launch { callback() }
}
else -> {
data.error(ApiError(TAG, EXCEPTION_LIBRUS_MESSAGES_REQUEST)
.withApiResponse(json))
}
}
}
}
private fun downloadAttachment(attachmentKey: String) {
val targetFile = File(Utils.getStorageDir(), attachmentName)
sandboxGetFile(TAG, "CSDownload&singleUseKey=$attachmentKey", targetFile, { file ->
val event = AttachmentGetEvent(
profileId,
message.id,
attachmentId,
TYPE_FINISHED,
file.absolutePath
)
val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.messageId}_${event.attachmentId}")
Utils.writeStringToFile(attachmentDataFile, event.fileName)
EventBus.getDefault().post(event)
onSuccess()
}) { written, _ ->
val event = AttachmentGetEvent(
profileId,
message.id,
attachmentId,
TYPE_PROGRESS,
bytesWritten = written
)
EventBus.getDefault().post(event)
}
}
}

View File

@ -11,6 +11,7 @@ import pl.szczodrzynski.edziennik.api.v2.POST
import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus import pl.szczodrzynski.edziennik.api.v2.librus.DataLibrus
import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK import pl.szczodrzynski.edziennik.api.v2.librus.ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK
import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusSynergia import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusSynergia
import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.modules.events.Event import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.get import pl.szczodrzynski.edziennik.get
@ -55,19 +56,18 @@ class LibrusSynergiaHomework(override val data: DataLibrus, val onSuccess: () ->
val id = "/podglad/([0-9]+)'".toRegex().find( val id = "/podglad/([0-9]+)'".toRegex().find(
elements[9].select("input").attr("onclick") elements[9].select("input").attr("onclick")
)?.get(1)?.toLong() ?: return@forEachIndexed )?.get(1)?.toLong() ?: return@forEachIndexed
val startTime = data.lessonList.singleOrNull {
it.weekDay == eventDate.weekDay && it.subjectId == subjectId val lessons = data.db.timetableDao().getForDateNow(profileId, eventDate)
}?.startTime val startTime = lessons.firstOrNull { it.subjectId == subjectId }?.startTime
val moreInfo = graphElements[2 * i + 1].select("td[title]") val moreInfo = graphElements[2 * i + 1].select("td[title]")
.attr("title").trim() .attr("title").trim()
val description = "Treść: (.*)".toRegex(RegexOption.DOT_MATCHES_ALL).find(moreInfo) val description = "Treść: (.*)".toRegex(RegexOption.DOT_MATCHES_ALL).find(moreInfo)
?.get(1)?.replace("<br.*/>".toRegex(), "\n")?.trim() ?.get(1)?.replace("<br.*/>".toRegex(), "\n")?.trim()
val notified = when (profile?.empty) { val seen = when (profile?.empty) {
true -> true true -> true
false -> Date.getToday() < eventDate else -> eventDate < Date.getToday()
else -> false
} }
val eventObject = Event( val eventObject = Event(
@ -89,13 +89,15 @@ class LibrusSynergiaHomework(override val data: DataLibrus, val onSuccess: () ->
profileId, profileId,
Metadata.TYPE_HOMEWORK, Metadata.TYPE_HOMEWORK,
id, id,
notified, seen,
notified, seen,
addedDate addedDate
)) ))
} }
} }
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_HOMEWORK))
// because this requires a synergia login (2 more requests) sync this every two hours or if explicit :D // because this requires a synergia login (2 more requests) sync this every two hours or if explicit :D
data.setSyncNext(ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK, 2 * HOUR, DRAWER_ITEM_HOMEWORK) data.setSyncNext(ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK, 2 * HOUR, DRAWER_ITEM_HOMEWORK)
onSuccess() onSuccess()

View File

@ -13,8 +13,8 @@ import pl.szczodrzynski.edziennik.api.v2.librus.data.LibrusPortal
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginApi import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginApi
import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginPortal import pl.szczodrzynski.edziennik.api.v2.librus.login.LibrusLoginPortal
import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.data.api.AppError.CODE_LIBRUS_DISCONNECTED import pl.szczodrzynski.edziennik.api.v2.models.AppError.CODE_LIBRUS_DISCONNECTED
import pl.szczodrzynski.edziennik.data.api.AppError.CODE_SYNERGIA_NOT_ACTIVATED import pl.szczodrzynski.edziennik.api.v2.models.AppError.CODE_SYNERGIA_NOT_ACTIVATED
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) { class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) {
@ -89,4 +89,4 @@ class LibrusFirstLogin(val data: DataLibrus, val onSuccess: () -> Unit) {
} }
} }
} }
} }

View File

@ -101,7 +101,7 @@ class LibrusLoginMessages(val data: DataLibrus, val onSuccess: () -> Unit) {
val loginElement = doc.createElement("login") val loginElement = doc.createElement("login")
loginElement.appendChild(doc.createTextNode(data.apiLogin)) loginElement.appendChild(doc.createTextNode(data.apiLogin))
dataElement.appendChild(loginElement) dataElement.appendChild(loginElement)
val passwordElement = doc.createElement("login") val passwordElement = doc.createElement("password")
passwordElement.appendChild(doc.createTextNode(data.apiPassword)) passwordElement.appendChild(doc.createTextNode(data.apiPassword))
dataElement.appendChild(passwordElement) dataElement.appendChild(passwordElement)
val keyStrokeElement = doc.createElement("KeyStroke") val keyStrokeElement = doc.createElement("KeyStroke")
@ -150,6 +150,7 @@ class LibrusLoginMessages(val data: DataLibrus, val onSuccess: () -> Unit) {
private fun saveSessionId(response: Response?, text: String?) { private fun saveSessionId(response: Response?, text: String?) {
var sessionId = data.app.cookieJar.getCookie("wiadomosci.librus.pl", "DZIENNIKSID") var sessionId = data.app.cookieJar.getCookie("wiadomosci.librus.pl", "DZIENNIKSID")
sessionId = sessionId?.replace("-MAINT", "") // dunno what's this sessionId = sessionId?.replace("-MAINT", "") // dunno what's this
sessionId = sessionId?.replace("MAINT", "") // dunno what's this
if (sessionId == null) { if (sessionId == null) {
data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_NO_SESSION_ID) data.error(ApiError(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_NO_SESSION_ID)
.withResponse(response) .withResponse(response)

View File

@ -10,6 +10,7 @@ import pl.szczodrzynski.edziennik.api.v2.CODE_INTERNAL_LIBRUS_ACCOUNT_410
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.api.v2.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikData import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikData
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.MobidziennikWebGetAttachment
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.MobidziennikWebGetMessage import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web.MobidziennikWebGetMessage
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.firstlogin.MobidziennikFirstLogin import pl.szczodrzynski.edziennik.api.v2.mobidziennik.firstlogin.MobidziennikFirstLogin
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.login.MobidziennikLogin import pl.szczodrzynski.edziennik.api.v2.mobidziennik.login.MobidziennikLogin
@ -18,6 +19,7 @@ import pl.szczodrzynski.edziennik.api.v2.mobidziennikLoginMethods
import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.api.v2.prepare import pl.szczodrzynski.edziennik.api.v2.prepare
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
@ -76,6 +78,14 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto
} }
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
MobidziennikLoginWeb(data) {
MobidziennikWebGetAttachment(data, message, attachmentId, attachmentName) {
completed()
}
}
}
override fun firstLogin() { override fun firstLogin() {
MobidziennikFirstLogin(data) { MobidziennikFirstLogin(data) {
completed() completed()

View File

@ -6,12 +6,14 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik.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.FileCallbackHandler
import im.wangchao.mhttp.callback.TextCallbackHandler import im.wangchao.mhttp.callback.TextCallbackHandler
import okhttp3.Cookie import okhttp3.Cookie
import pl.szczodrzynski.edziennik.api.v2.* import pl.szczodrzynski.edziennik.api.v2.*
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik
import pl.szczodrzynski.edziennik.api.v2.models.ApiError import pl.szczodrzynski.edziennik.api.v2.models.ApiError
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
import java.io.File
open class MobidziennikWeb(open val data: DataMobidziennik) { open class MobidziennikWeb(open val data: DataMobidziennik) {
companion object { companion object {
@ -93,4 +95,77 @@ open class MobidziennikWeb(open val data: DataMobidziennik) {
.build() .build()
.enqueue() .enqueue()
} }
fun webGetFile(tag: String, action: String, targetFile: File, onSuccess: (file: File) -> Unit,
onProgress: (written: Long, total: Long) -> Unit) {
val url = "https://${data.loginServerName}.mobidziennik.pl$action"
d(tag, "Request: Mobidziennik/Web - $url")
if (data.webSessionKey == null) {
data.error(TAG, ERROR_MOBIDZIENNIK_WEB_NO_SESSION_KEY)
return
}
if (data.webSessionValue == null) {
data.error(TAG, ERROR_MOBIDZIENNIK_WEB_NO_SESSION_VALUE)
return
}
if (data.webServerId == null) {
data.error(TAG, ERROR_MOBIDZIENNIK_WEB_NO_SERVER_ID)
return
}
val callback = object : FileCallbackHandler(targetFile) {
override fun onSuccess(file: File?, response: Response?) {
if (file == null) {
data.error(ApiError(TAG, ERROR_FILE_DOWNLOAD)
.withResponse(response))
return
}
try {
onSuccess(file)
} catch (e: Exception) {
data.error(ApiError(tag, EXCEPTION_MOBIDZIENNIK_WEB_FILE_REQUEST)
.withResponse(response)
.withThrowable(e))
}
}
override fun onProgress(bytesWritten: Long, bytesTotal: Long) {
try {
onProgress(bytesWritten, bytesTotal)
} catch (e: Exception) {
data.error(ApiError(tag, EXCEPTION_MOBIDZIENNIK_WEB_FILE_REQUEST)
.withThrowable(e))
}
}
override fun onFailure(response: Response?, throwable: Throwable?) {
data.error(ApiError(TAG, ERROR_REQUEST_FAILURE)
.withResponse(response)
.withThrowable(throwable))
}
}
data.app.cookieJar.saveFromResponse(null, listOf(
Cookie.Builder()
.name(data.webSessionKey!!)
.value(data.webSessionValue!!)
.domain("${data.loginServerName}.mobidziennik.pl")
.secure().httpOnly().build(),
Cookie.Builder()
.name("SERVERID")
.value(data.webServerId!!)
.domain("${data.loginServerName}.mobidziennik.pl")
.secure().httpOnly().build()
))
Request.builder()
.url(url)
.userAgent(MOBIDZIENNIK_USER_AGENT)
.callback(callback)
.build()
.enqueue()
}
} }

View File

@ -7,6 +7,7 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api
import androidx.core.util.contains import androidx.core.util.contains
import pl.szczodrzynski.edziennik.api.v2.Regexes import pl.szczodrzynski.edziennik.api.v2.Regexes
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik
import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.modules.events.Event import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
@ -74,5 +75,7 @@ class MobidziennikApiEvents(val data: DataMobidziennik, rows: List<String>) {
)) ))
} }
} }
data.toRemove.add(DataRemoveModel.Events.futureExceptType(Event.TYPE_HOMEWORK))
} }
} }

View File

@ -6,6 +6,7 @@ package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.api
import androidx.core.util.contains import androidx.core.util.contains
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik
import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.modules.events.Event import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
@ -53,5 +54,7 @@ class MobidziennikApiHomework(val data: DataMobidziennik, rows: List<String>) {
)) ))
} }
} }
data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_HOMEWORK))
} }
} }

View File

@ -14,7 +14,7 @@ import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) { class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
init { init { data.profile?.also { profile ->
val lessons = rows.filterNot { it.isEmpty() }.map { it.split("|") } val lessons = rows.filterNot { it.isEmpty() }.map { it.split("|") }
val dataStart = Date.getToday() val dataStart = Date.getToday()
@ -32,7 +32,6 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
val date = Date.fromYmd(lesson[2]) val date = Date.fromYmd(lesson[2])
val startTime = Time.fromYmdHm(lesson[3]) val startTime = Time.fromYmdHm(lesson[3])
val endTime = Time.fromYmdHm(lesson[4]) val endTime = Time.fromYmdHm(lesson[4])
val id = date.combineWith(startTime) / 6L * 10L + (lesson.joinToString("|").hashCode() and 0xFFFF)
dataDays.remove(date.value) dataDays.remove(date.value)
@ -41,7 +40,7 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
val teamId = data.teamList.singleOrNull { it.name == lesson[8]+lesson[9] }?.id ?: -1 val teamId = data.teamList.singleOrNull { it.name == lesson[8]+lesson[9] }?.id ?: -1
val classroom = lesson[11] val classroom = lesson[11]
Lesson(data.profileId, id).also { Lesson(data.profileId, -1).also {
when (lesson[1]) { when (lesson[1]) {
"plan_lekcji", "lekcja" -> { "plan_lekcji", "lekcja" -> {
it.type = Lesson.TYPE_NORMAL it.type = Lesson.TYPE_NORMAL
@ -75,14 +74,18 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
} }
} }
it.id = it.buildId()
val seen = profile.empty || date < Date.getToday()
if (it.type != Lesson.TYPE_NORMAL) { if (it.type != Lesson.TYPE_NORMAL) {
data.metadataList.add( data.metadataList.add(
Metadata( Metadata(
data.profileId, data.profileId,
Metadata.TYPE_LESSON_CHANGE, Metadata.TYPE_LESSON_CHANGE,
it.id, it.id,
data.profile?.empty ?: false, seen,
data.profile?.empty ?: false, seen,
System.currentTimeMillis() System.currentTimeMillis()
)) ))
} }
@ -194,5 +197,5 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List<String>) {
} }
} }
}*/ }*/
} }}
} }

View File

@ -0,0 +1,60 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-28.
*/
package pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.web
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.api.v2.events.AttachmentGetEvent
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.DataMobidziennik
import pl.szczodrzynski.edziennik.api.v2.mobidziennik.data.MobidziennikWeb
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.utils.Utils
import java.io.File
class MobidziennikWebGetAttachment(
override val data: DataMobidziennik, val message: Message, val attachmentId: Long,
val attachmentName: String, val onSuccess: () -> Unit) : MobidziennikWeb(data) {
companion object {
private const val TAG = "MobidziennikWebGetAttachment"
}
init {
val targetFile = File(Utils.getStorageDir(), attachmentName)
val typeUrl = if (message.type == Message.TYPE_SENT)
"wiadwyslana"
else
"wiadodebrana"
webGetFile(TAG, "/dziennik/$typeUrl/?id=${message.id}&zalacznik=$attachmentId", targetFile, { file ->
val event = AttachmentGetEvent(
profileId,
message.id,
attachmentId,
AttachmentGetEvent.TYPE_FINISHED,
file.absolutePath
)
val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.messageId}_${event.attachmentId}")
Utils.writeStringToFile(attachmentDataFile, event.fileName)
EventBus.getDefault().post(event)
onSuccess()
}) { written, _ ->
// TODO make use of bytesTotal
val event = AttachmentGetEvent(
profileId,
message.id,
attachmentId,
AttachmentGetEvent.TYPE_PROGRESS,
bytesWritten = written
)
EventBus.getDefault().post(event)
}
}
}

View File

@ -124,7 +124,7 @@ class MobidziennikWebGetMessage(
val attachmentName = it.ownText() val attachmentName = it.ownText()
Regexes.MOBIDZIENNIK_MESSAGE_ATTACHMENT.find(it.outerHtml())?.let { match -> Regexes.MOBIDZIENNIK_MESSAGE_ATTACHMENT.find(it.outerHtml())?.let { match ->
val attachmentId = match[1].toLong() val attachmentId = match[1].toLong()
var size = match[2].toFloatOrNull() ?: 0f var size = match[2].toFloatOrNull() ?: -1f
when (match[3]) { when (match[3]) {
"K" -> size *= 1024f "K" -> size *= 1024f
"M" -> size *= 1024f * 1024f "M" -> size *= 1024f * 1024f

View File

@ -96,13 +96,17 @@ class MobidziennikWebGrades(override val data: DataMobidziennik,
if (Regexes.MOBIDZIENNIK_GRADES_COUNT_TO_AVG.containsMatchIn(html)) { if (Regexes.MOBIDZIENNIK_GRADES_COUNT_TO_AVG.containsMatchIn(html)) {
Regexes.MOBIDZIENNIK_GRADES_DETAILS.find(html)?.let { match -> Regexes.MOBIDZIENNIK_GRADES_DETAILS.find(html)?.let { match ->
val gradeName = match[1] val gradeName = match[1]
val gradeDescription = match[2] var gradeDescription = match[2]
val gradeValue = match[3].toFloatOrNull() ?: 0.0f val gradeValue = match[3].toFloatOrNull() ?: 0.0f
val teacherName = match[4].fixWhiteSpaces() val teacherName = match[4].fixWhiteSpaces()
val teacherId = data.teacherList.singleOrNull { it.fullNameLastFirst == teacherName }?.id ?: -1 val teacherId = data.teacherList.singleOrNull { it.fullNameLastFirst == teacherName }?.id ?: -1
val subjectId = data.subjectList.singleOrNull { it.longName == subjectName }?.id ?: -1 val subjectId = data.subjectList.singleOrNull { it.longName == subjectName }?.id ?: -1
if (match[5].isNotEmpty()) {
gradeDescription += "\n"+match[5].replace("<br>", "\n")
}
val gradeObject = Grade( val gradeObject = Grade(
profileId, profileId,
gradeId, gradeId,

View File

@ -91,7 +91,7 @@ class MobidziennikLoginWeb(val data: DataMobidziennik, val onSuccess: () -> Unit
.addParameter("ip", data.app.deviceId) .addParameter("ip", data.app.deviceId)
.addParameter("login", data.loginUsername) .addParameter("login", data.loginUsername)
.addParameter("haslo", data.loginPassword) .addParameter("haslo", data.loginPassword)
.addParameter("token", data.app.appConfig.fcmTokens[LOGIN_TYPE_MOBIDZIENNIK]?.first) .addParameter("token", data.app.config.sync.tokenMobidziennik)
.post() .post()
.callback(callback) .callback(callback)
.build() .build()

View File

@ -8,7 +8,6 @@ import android.content.Context
import com.google.gson.JsonObject import com.google.gson.JsonObject
import im.wangchao.mhttp.Request import im.wangchao.mhttp.Request
import im.wangchao.mhttp.Response import im.wangchao.mhttp.Response
import pl.szczodrzynski.edziennik.data.api.AppError
class ApiError(val tag: String, val errorCode: Int) { class ApiError(val tag: String, val errorCode: Int) {
var profileId: Int? = null var profileId: Int? = null
@ -67,4 +66,4 @@ class ApiError(val tag: String, val errorCode: Int) {
} }
} }

View File

@ -1,4 +1,8 @@
package pl.szczodrzynski.edziennik.data.api; /*
* Copyright (c) Kacper Ziubryniewicz 2019-11-26
*/
package pl.szczodrzynski.edziennik.api.v2.models;
import android.content.Context; import android.content.Context;
import android.os.AsyncTask; import android.os.AsyncTask;

View File

@ -10,7 +10,7 @@ import pl.szczodrzynski.edziennik.api.v2.DataNotifications
import pl.szczodrzynski.edziennik.api.v2.EXCEPTION_NOTIFY_AND_SYNC import pl.szczodrzynski.edziennik.api.v2.EXCEPTION_NOTIFY_AND_SYNC
import pl.szczodrzynski.edziennik.api.v2.ServerSync import pl.szczodrzynski.edziennik.api.v2.ServerSync
import pl.szczodrzynski.edziennik.api.v2.interfaces.EndpointCallback import pl.szczodrzynski.edziennik.api.v2.interfaces.EndpointCallback
import pl.szczodrzynski.edziennik.data.api.AppError.* import pl.szczodrzynski.edziennik.api.v2.models.AppError.*
import pl.szczodrzynski.edziennik.data.db.AppDb import pl.szczodrzynski.edziennik.data.db.AppDb
import pl.szczodrzynski.edziennik.data.db.modules.announcements.Announcement import pl.szczodrzynski.edziennik.data.db.modules.announcements.Announcement
import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimer import pl.szczodrzynski.edziennik.data.db.modules.api.EndpointTimer
@ -283,6 +283,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
when (model) { when (model) {
is DataRemoveModel.Timetable -> model.commit(profileId, db.timetableDao()) is DataRemoveModel.Timetable -> model.commit(profileId, db.timetableDao())
is DataRemoveModel.Grades -> model.commit(profileId, db.gradeDao()) is DataRemoveModel.Grades -> model.commit(profileId, db.gradeDao())
is DataRemoveModel.Events -> model.commit(profileId, db.eventDao())
} }
} }
@ -305,7 +306,6 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
db.gradeDao().addAll(gradeList) db.gradeDao().addAll(gradeList)
} }
if (eventList.isNotEmpty()) { if (eventList.isNotEmpty()) {
db.eventDao().removeFuture(profile.id, Date.getToday())
db.eventDao().addAll(eventList) db.eventDao().addAll(eventList)
} }
if (noticeList.isNotEmpty()) { if (noticeList.isNotEmpty()) {
@ -375,7 +375,7 @@ open class Data(val app: App, val profile: Profile?, val loginStore: LoginStore)
} }
fun shouldSyncLuckyNumber(): Boolean { fun shouldSyncLuckyNumber(): Boolean {
return (db.luckyNumberDao().getNearestFutureNow(profileId, Date.getToday()) ?: -1) == -1 return (db.luckyNumberDao().getNearestFutureNow(profileId, Date.getToday().value) ?: -1) == -1
} }
fun error(tag: String, errorCode: Int, response: Response? = null, throwable: Throwable? = null, apiResponse: JsonObject? = null) { fun error(tag: String, errorCode: Int, response: Response? = null, throwable: Throwable? = null, apiResponse: JsonObject? = null) {

View File

@ -4,6 +4,7 @@
package pl.szczodrzynski.edziennik.api.v2.models package pl.szczodrzynski.edziennik.api.v2.models
import pl.szczodrzynski.edziennik.data.db.modules.events.EventDao
import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeDao import pl.szczodrzynski.edziennik.data.db.modules.grades.GradeDao
import pl.szczodrzynski.edziennik.data.db.modules.timetable.TimetableDao import pl.szczodrzynski.edziennik.data.db.modules.timetable.TimetableDao
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
@ -15,21 +16,23 @@ open class DataRemoveModel {
fun to(dateTo: Date) = Timetable(null, dateTo) fun to(dateTo: Date) = Timetable(null, dateTo)
fun between(dateFrom: Date, dateTo: Date) = Timetable(dateFrom, dateTo) fun between(dateFrom: Date, dateTo: Date) = Timetable(dateFrom, dateTo)
} }
fun commit(profileId: Int, dao: TimetableDao) { fun commit(profileId: Int, dao: TimetableDao) {
if (dateFrom != null && dateTo != null) { if (dateFrom != null && dateTo != null) {
dao.clearBetweenDates(profileId, dateFrom, dateTo) dao.clearBetweenDates(profileId, dateFrom, dateTo)
} } else {
else {
dateFrom?.let { dateFrom -> dao.clearFromDate(profileId, dateFrom) } dateFrom?.let { dateFrom -> dao.clearFromDate(profileId, dateFrom) }
dateTo?.let { dateTo -> dao.clearToDate(profileId, dateTo) } dateTo?.let { dateTo -> dao.clearToDate(profileId, dateTo) }
} }
} }
} }
class Grades(val all: Boolean, val semester: Int?) : DataRemoveModel() {
class Grades(private val all: Boolean, private val semester: Int?) : DataRemoveModel() {
companion object { companion object {
fun all() = Grades(true, null) fun all() = Grades(true, null)
fun semester(semester: Int) = Grades(false, semester) fun semester(semester: Int) = Grades(false, semester)
} }
fun commit(profileId: Int, dao: GradeDao) { fun commit(profileId: Int, dao: GradeDao) {
if (all) { if (all) {
dao.clear(profileId) dao.clear(profileId)
@ -37,4 +40,16 @@ open class DataRemoveModel {
semester?.let { dao.clearForSemester(profileId, it) } semester?.let { dao.clearForSemester(profileId, it) }
} }
} }
}
class Events(private val type: Int?, private val exceptType: Int?) : DataRemoveModel() {
companion object {
fun futureExceptType(exceptType: Int) = Events(null, exceptType)
fun futureWithType(type: Int) = Events(type, null)
}
fun commit(profileId: Int, dao: EventDao) {
type?.let { dao.removeFutureWithType(profileId, Date.getToday(), it) }
exceptType?.let { dao.removeFutureExceptType(profileId, Date.getToday(), it) }
}
}
}

View File

@ -16,6 +16,7 @@ import pl.szczodrzynski.edziennik.api.v2.template.firstlogin.TemplateFirstLogin
import pl.szczodrzynski.edziennik.api.v2.template.login.TemplateLogin import pl.szczodrzynski.edziennik.api.v2.template.login.TemplateLogin
import pl.szczodrzynski.edziennik.api.v2.templateLoginMethods import pl.szczodrzynski.edziennik.api.v2.templateLoginMethods
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
@ -70,6 +71,10 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore,
} }
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
}
override fun firstLogin() { override fun firstLogin() {
TemplateFirstLogin(data) { TemplateFirstLogin(data) {
completed() completed()

View File

@ -164,6 +164,8 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
"GD1" -> "https://uonetplus-komunikacja.edu.gdansk.pl" "GD1" -> "https://uonetplus-komunikacja.edu.gdansk.pl"
"KA1" -> "https://uonetplus-komunikacja.mcuw.katowice.eu" "KA1" -> "https://uonetplus-komunikacja.mcuw.katowice.eu"
"KA2" -> "https://uonetplus-komunikacja-test.mcuw.katowice.eu" "KA2" -> "https://uonetplus-komunikacja-test.mcuw.katowice.eu"
"LU1" -> "https://uonetplus-komunikacja.edu.lublin.eu"
"LU2" -> "https://test-uonetplus-komunikacja.edu.lublin.eu"
"P03" -> "https://efeb-komunikacja-pro-efebmobile.pro.vulcan.pl" "P03" -> "https://efeb-komunikacja-pro-efebmobile.pro.vulcan.pl"
"P01" -> "http://efeb-komunikacja.pro-hudson.win.vulcan.pl" "P01" -> "http://efeb-komunikacja.pro-hudson.win.vulcan.pl"
"P02" -> "http://efeb-komunikacja.pro-hudsonrc.win.vulcan.pl" "P02" -> "http://efeb-komunikacja.pro-hudsonrc.win.vulcan.pl"
@ -172,7 +174,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
"SZ9" -> "http://vulcan.szkolny.eu" "SZ9" -> "http://vulcan.szkolny.eu"
else -> null else -> null
} }
return if (url != null) "$url/$symbol" else null return if (url != null) "$url/$symbol" else loginStore.getLoginData("apiUrl", null)
} }
val fullApiUrl: String? val fullApiUrl: String?

View File

@ -18,6 +18,7 @@ import pl.szczodrzynski.edziennik.api.v2.vulcan.login.VulcanLogin
import pl.szczodrzynski.edziennik.api.v2.vulcan.login.VulcanLoginApi import pl.szczodrzynski.edziennik.api.v2.vulcan.login.VulcanLoginApi
import pl.szczodrzynski.edziennik.api.v2.vulcanLoginMethods import pl.szczodrzynski.edziennik.api.v2.vulcanLoginMethods
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message
import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull import pl.szczodrzynski.edziennik.data.db.modules.messages.MessageFull
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
@ -76,6 +77,10 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va
} }
override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {
}
override fun firstLogin() { override fun firstLogin() {
VulcanFirstLogin(data) { VulcanFirstLogin(data) {
completed() completed()

View File

@ -6,6 +6,7 @@ package pl.szczodrzynski.edziennik.api.v2.vulcan.data.api
import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_EVENTS import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_EVENTS
import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_HOMEWORK import pl.szczodrzynski.edziennik.api.v2.VULCAN_API_ENDPOINT_HOMEWORK
import pl.szczodrzynski.edziennik.api.v2.models.DataRemoveModel
import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan
import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_EVENTS import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_EVENTS
import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_HOMEWORK import pl.szczodrzynski.edziennik.api.v2.vulcan.ENDPOINT_VULCAN_API_HOMEWORK
@ -52,10 +53,11 @@ class VulcanApiEvents(override val data: DataVulcan, private val isHomework: Boo
val eventDate = Date.fromY_m_d(event.getString("DataTekst") ?: return@forEach) val eventDate = Date.fromY_m_d(event.getString("DataTekst") ?: return@forEach)
val subjectId = event.getLong("IdPrzedmiot") ?: -1 val subjectId = event.getLong("IdPrzedmiot") ?: -1
val teacherId = event.getLong("IdPracownik") ?: -1 val teacherId = event.getLong("IdPracownik") ?: -1
val startTime = data.lessonList.singleOrNull {
it.weekDay == eventDate.weekDay && it.subjectId == subjectId
}?.startTime
val topic = event.getString("Opis") ?: "" val topic = event.getString("Opis") ?: ""
val lessonList = data.db.timetableDao().getForDateNow(profileId, eventDate)
val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.startTime
val type = when (isHomework) { val type = when (isHomework) {
true -> Event.TYPE_HOMEWORK true -> Event.TYPE_HOMEWORK
else -> when (event.getBoolean("Rodzaj")) { else -> when (event.getBoolean("Rodzaj")) {
@ -91,8 +93,14 @@ class VulcanApiEvents(override val data: DataVulcan, private val isHomework: Boo
} }
when (isHomework) { when (isHomework) {
true -> data.setSyncNext(ENDPOINT_VULCAN_API_HOMEWORK, SYNC_ALWAYS) true -> {
false -> data.setSyncNext(ENDPOINT_VULCAN_API_EVENTS, SYNC_ALWAYS) data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_HOMEWORK))
data.setSyncNext(ENDPOINT_VULCAN_API_HOMEWORK, SYNC_ALWAYS)
}
false -> {
data.toRemove.add(DataRemoveModel.Events.futureExceptType(Event.TYPE_HOMEWORK))
data.setSyncNext(ENDPOINT_VULCAN_API_EVENTS, SYNC_ALWAYS)
}
} }
onSuccess() onSuccess()
} }

View File

@ -20,6 +20,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
import pl.szczodrzynski.edziennik.utils.Utils.crc16 import pl.szczodrzynski.edziennik.utils.Utils.crc16
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.Week
class VulcanApiTimetable(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) { class VulcanApiTimetable(override val data: DataVulcan, val onSuccess: () -> Unit) : VulcanApi(data) {
companion object { companion object {
@ -27,7 +28,12 @@ class VulcanApiTimetable(override val data: DataVulcan, val onSuccess: () -> Uni
} }
init { data.profile?.also { profile -> init { data.profile?.also { profile ->
val currentWeekStart = Date.getToday().let { it.stepForward(0, 0, -it.weekDay) } val currentWeekStart = Week.getWeekStart()
if (Date.getToday().weekDay > 4) {
currentWeekStart.stepForward(0, 0, 7)
}
val getDate = data.arguments?.getString("weekStart") ?: currentWeekStart.stringY_m_d val getDate = data.arguments?.getString("weekStart") ?: currentWeekStart.stringY_m_d
val weekStart = Date.fromY_m_d(getDate) val weekStart = Date.fromY_m_d(getDate)
@ -40,8 +46,8 @@ class VulcanApiTimetable(override val data: DataVulcan, val onSuccess: () -> Uni
"IdOddzial" to data.studentClassId, "IdOddzial" to data.studentClassId,
"IdOkresKlasyfikacyjny" to data.studentSemesterId "IdOkresKlasyfikacyjny" to data.studentSemesterId
)) { json, _ -> )) { json, _ ->
val dates: MutableSet<Int> = mutableSetOf() val dates = mutableSetOf<Int>()
val lessons: MutableList<Lesson> = mutableListOf() val lessons = mutableListOf<Lesson>()
json.getJsonArray("Data")?.asJsonObjectList()?.forEach { lesson -> json.getJsonArray("Data")?.asJsonObjectList()?.forEach { lesson ->
if (lesson.getBoolean("PlanUcznia") != true) if (lesson.getBoolean("PlanUcznia") != true)
@ -108,9 +114,7 @@ class VulcanApiTimetable(override val data: DataVulcan, val onSuccess: () -> Uni
} }
} }
val id = lessonDate.combineWith(startTime) / 6L * 10L + (lesson.hashCode() and 0xFFFF) val lessonObject = Lesson(profileId, -1).apply {
val lessonObject = Lesson(profileId, id).apply {
this.type = type this.type = type
when (type) { when (type) {
@ -164,15 +168,19 @@ class VulcanApiTimetable(override val data: DataVulcan, val onSuccess: () -> Uni
} }
} }
} }
this.id = buildId()
} }
val seen = profile.empty || lessonDate < Date.getToday()
if (type != Lesson.TYPE_NORMAL) { if (type != Lesson.TYPE_NORMAL) {
data.metadataList.add(Metadata( data.metadataList.add(Metadata(
profileId, profileId,
Metadata.TYPE_LESSON_CHANGE, Metadata.TYPE_LESSON_CHANGE,
id, lessonObject.id,
profile.empty, seen,
profile.empty, seen,
System.currentTimeMillis() System.currentTimeMillis()
)) ))
} }

View File

@ -38,10 +38,12 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) {
if (data.apiToken?.get(0) == 'F') VULCAN_API_PASSWORD_FAKELOG else VULCAN_API_PASSWORD, if (data.apiToken?.get(0) == 'F') VULCAN_API_PASSWORD_FAKELOG else VULCAN_API_PASSWORD,
data.apiCertificatePfx ?: "" data.apiCertificatePfx ?: ""
) )
onSuccess() data.loginStore.removeLoginData("certificatePfx")
return@run
} catch (e: Throwable) { } catch (e: Throwable) {
e.printStackTrace() e.printStackTrace()
} finally {
onSuccess()
return@run
} }
} }
if (data.symbol.isNotNullNorEmpty() && data.apiToken.isNotNullNorEmpty() && data.apiPin.isNotNullNorEmpty()) { if (data.symbol.isNotNullNorEmpty() && data.apiToken.isNotNullNorEmpty() && data.apiPin.isNotNullNorEmpty()) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-27.
*/
package pl.szczodrzynski.edziennik.config
import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.Companion.COLOR_MODE_WEIGHTED
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile.Companion.YEAR_ALL_GRADES
class ProfileConfigGrades(private val config: ProfileConfig) {
private var mColorMode: Int? = null
var colorMode: Int
get() { mColorMode = mColorMode ?: config.values.get("gradesColorMode", COLOR_MODE_WEIGHTED); return mColorMode ?: COLOR_MODE_WEIGHTED }
set(value) { config.set("gradesColorMode", value); mColorMode = value }
private var mYearAverageMode: Int? = null
var yearAverageMode: Int
get() { mYearAverageMode = mYearAverageMode ?: config.values.get("yearAverageMode", YEAR_ALL_GRADES); return mYearAverageMode ?: YEAR_ALL_GRADES }
set(value) { config.set("yearAverageMode", value); mYearAverageMode = value }
private var mCountZeroToAvg: Boolean? = null
var countZeroToAvg: Boolean
get() { mCountZeroToAvg = mCountZeroToAvg ?: config.values.get("countZeroToAvg", true); return mCountZeroToAvg ?: true }
set(value) { config.set("countZeroToAvg", value); mCountZeroToAvg = value }
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,6 +10,8 @@ import androidx.room.TypeConverters;
import androidx.room.migration.Migration; import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase; import androidx.sqlite.db.SupportSQLiteDatabase;
import pl.szczodrzynski.edziennik.config.db.ConfigDao;
import pl.szczodrzynski.edziennik.config.db.ConfigEntry;
import pl.szczodrzynski.edziennik.data.db.converters.ConverterDate; import pl.szczodrzynski.edziennik.data.db.converters.ConverterDate;
import pl.szczodrzynski.edziennik.data.db.converters.ConverterDateInt; import pl.szczodrzynski.edziennik.data.db.converters.ConverterDateInt;
import pl.szczodrzynski.edziennik.data.db.converters.ConverterJsonObject; import pl.szczodrzynski.edziennik.data.db.converters.ConverterJsonObject;
@ -105,7 +107,8 @@ import pl.szczodrzynski.edziennik.utils.models.Date;
NoticeType.class, NoticeType.class,
AttendanceType.class, AttendanceType.class,
pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson.class, pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson.class,
Metadata.class}, version = 64) ConfigEntry.class,
Metadata.class}, version = 66)
@TypeConverters({ @TypeConverters({
ConverterTime.class, ConverterTime.class,
ConverterDate.class, ConverterDate.class,
@ -144,6 +147,7 @@ public abstract class AppDb extends RoomDatabase {
public abstract NoticeTypeDao noticeTypeDao(); public abstract NoticeTypeDao noticeTypeDao();
public abstract AttendanceTypeDao attendanceTypeDao(); public abstract AttendanceTypeDao attendanceTypeDao();
public abstract TimetableDao timetableDao(); public abstract TimetableDao timetableDao();
public abstract ConfigDao configDao();
public abstract MetadataDao metadataDao(); public abstract MetadataDao metadataDao();
private static volatile AppDb INSTANCE; private static volatile AppDb INSTANCE;
@ -763,6 +767,27 @@ public abstract class AppDb extends RoomDatabase {
database.execSQL("CREATE INDEX index_lessons_profileId_type_oldDate ON timetable (profileId, type, oldDate);"); database.execSQL("CREATE INDEX index_lessons_profileId_type_oldDate ON timetable (profileId, type, oldDate);");
} }
}; };
private static final Migration MIGRATION_64_65 = new Migration(64, 65) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("CREATE TABLE config (" +
"profileId INTEGER NOT NULL DEFAULT -1," +
"`key` TEXT NOT NULL," +
"value TEXT NOT NULL," +
"PRIMARY KEY(profileId, `key`));");
}
};
private static final Migration MIGRATION_65_66 = new Migration(65, 66) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("DROP TABLE config;");
database.execSQL("CREATE TABLE config (" +
"profileId INTEGER NOT NULL DEFAULT -1," +
"`key` TEXT NOT NULL," +
"value TEXT," +
"PRIMARY KEY(profileId, `key`));");
}
};
public static AppDb getDatabase(final Context context) { public static AppDb getDatabase(final Context context) {
@ -824,7 +849,9 @@ public abstract class AppDb extends RoomDatabase {
MIGRATION_60_61, MIGRATION_60_61,
MIGRATION_61_62, MIGRATION_61_62,
MIGRATION_62_63, MIGRATION_62_63,
MIGRATION_63_64 MIGRATION_63_64,
MIGRATION_64_65,
MIGRATION_65_66
) )
.allowMainThreadQueries() .allowMainThreadQueries()
//.fallbackToDestructiveMigration() //.fallbackToDestructiveMigration()

View File

@ -1,16 +1,17 @@
package pl.szczodrzynski.edziennik.data.db.modules.events; package pl.szczodrzynski.edziennik.data.db.modules.events;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.sqlite.db.SimpleSQLiteQuery;
import androidx.sqlite.db.SupportSQLiteQuery;
import androidx.room.Dao; import androidx.room.Dao;
import androidx.room.Insert; import androidx.room.Insert;
import androidx.room.OnConflictStrategy; import androidx.room.OnConflictStrategy;
import androidx.room.Query; import androidx.room.Query;
import androidx.room.RawQuery; import androidx.room.RawQuery;
import androidx.room.Transaction; import androidx.room.Transaction;
import androidx.annotation.NonNull; import androidx.sqlite.db.SimpleSQLiteQuery;
import android.util.Log; import androidx.sqlite.db.SupportSQLiteQuery;
import java.util.List; import java.util.List;
@ -130,6 +131,12 @@ public abstract class EventDao {
@Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0 AND eventDate >= :todayDate") @Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0 AND eventDate >= :todayDate")
public abstract void removeFuture(int profileId, Date todayDate); public abstract void removeFuture(int profileId, Date todayDate);
@Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0 AND eventDate >= :todayDate AND eventType = :type")
public abstract void removeFutureWithType(int profileId, Date todayDate, int type);
@Query("DELETE FROM events WHERE profileId = :profileId AND eventAddedManually = 0 AND eventDate >= :todayDate AND eventType != :exceptType")
public abstract void removeFutureExceptType(int profileId, Date todayDate, int exceptType);
@Query("UPDATE metadata SET seen = :seen WHERE profileId = :profileId AND (thingType = "+TYPE_EVENT+" OR thingType = "+TYPE_LESSON_CHANGE+" OR thingType = "+TYPE_HOMEWORK+") AND thingId IN (SELECT eventId FROM events WHERE profileId = :profileId AND eventDate = :date)") @Query("UPDATE metadata SET seen = :seen WHERE profileId = :profileId AND (thingType = "+TYPE_EVENT+" OR thingType = "+TYPE_LESSON_CHANGE+" OR thingType = "+TYPE_HOMEWORK+") AND thingId IN (SELECT eventId FROM events WHERE profileId = :profileId AND eventDate = :date)")
public abstract void setSeenByDate(int profileId, Date date, boolean seen); public abstract void setSeenByDate(int profileId, Date date, boolean seen);

View File

@ -1,18 +1,16 @@
package pl.szczodrzynski.edziennik.data.db.modules.grades; package pl.szczodrzynski.edziennik.data.db.modules.grades;
import android.util.LongSparseArray;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.sqlite.db.SimpleSQLiteQuery;
import androidx.sqlite.db.SupportSQLiteQuery;
import androidx.room.Dao; import androidx.room.Dao;
import androidx.room.Insert; import androidx.room.Insert;
import androidx.room.OnConflictStrategy; import androidx.room.OnConflictStrategy;
import androidx.room.Query; import androidx.room.Query;
import androidx.room.RawQuery; import androidx.room.RawQuery;
import androidx.room.Transaction; import androidx.room.Transaction;
import androidx.sqlite.db.SimpleSQLiteQuery;
import android.util.LongSparseArray; import androidx.sqlite.db.SupportSQLiteQuery;
import android.util.SparseArray;
import android.util.SparseIntArray;
import java.util.List; import java.util.List;
@ -133,4 +131,8 @@ public abstract class GradeDao {
} }
} }
} }
public LiveData<List<GradeFull>> getAllFromDate(int profileId, int semester, long date) {
return getAllWhere(profileId, "gradeSemester = " + semester + " AND addedDate > " + date);
}
} }

View File

@ -37,7 +37,10 @@ public abstract class LuckyNumberDao {
@Nullable @Nullable
@Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId AND luckyNumberDate >= :date ORDER BY luckyNumberDate DESC LIMIT 1") @Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId AND luckyNumberDate >= :date ORDER BY luckyNumberDate DESC LIMIT 1")
public abstract LuckyNumber getNearestFutureNow(int profileId, Date date); public abstract LuckyNumber getNearestFutureNow(int profileId, int date);
@Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId AND luckyNumberDate >= :date ORDER BY luckyNumberDate DESC LIMIT 1")
public abstract LiveData<LuckyNumber> getNearestFuture(int profileId, int date);
@RawQuery(observedEntities = {LuckyNumber.class}) @RawQuery(observedEntities = {LuckyNumber.class})
abstract LiveData<List<LuckyNumberFull>> getAll(SupportSQLiteQuery query); abstract LiveData<List<LuckyNumberFull>> getAll(SupportSQLiteQuery query);

View File

@ -9,7 +9,6 @@ import androidx.room.Transaction;
import java.util.List; import java.util.List;
import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice;
import pl.szczodrzynski.edziennik.data.db.modules.announcements.Announcement; import pl.szczodrzynski.edziennik.data.db.modules.announcements.Announcement;
import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance; import pl.szczodrzynski.edziennik.data.db.modules.attendance.Attendance;
import pl.szczodrzynski.edziennik.data.db.modules.events.Event; import pl.szczodrzynski.edziennik.data.db.modules.events.Event;
@ -17,6 +16,7 @@ import pl.szczodrzynski.edziennik.data.db.modules.grades.Grade;
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange; import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange;
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonFull; import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonFull;
import pl.szczodrzynski.edziennik.data.db.modules.messages.Message; import pl.szczodrzynski.edziennik.data.db.modules.messages.Message;
import pl.szczodrzynski.edziennik.data.db.modules.notices.Notice;
import pl.szczodrzynski.edziennik.utils.models.UnreadCounter; import pl.szczodrzynski.edziennik.utils.models.UnreadCounter;
import static pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata.TYPE_ANNOUNCEMENT; import static pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata.TYPE_ANNOUNCEMENT;
@ -85,6 +85,11 @@ public abstract class MetadataDao {
updateSeen(profileId, TYPE_LESSON_CHANGE, ((LessonFull) o).changeId, seen); updateSeen(profileId, TYPE_LESSON_CHANGE, ((LessonFull) o).changeId, seen);
} }
} }
if (o instanceof pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull) {
if (add(new Metadata(profileId, TYPE_LESSON_CHANGE, ((pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull) o).getId(), seen, false, 0)) == -1) {
updateSeen(profileId, TYPE_LESSON_CHANGE, ((pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull) o).getId(), seen);
}
}
if (o instanceof Announcement) { if (o instanceof Announcement) {
if (add(new Metadata(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).id, seen, false, 0)) == -1) { if (add(new Metadata(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).id, seen, false, 0)) == -1) {
updateSeen(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).id, seen); updateSeen(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).id, seen);
@ -129,6 +134,11 @@ public abstract class MetadataDao {
updateNotified(profileId, TYPE_LESSON_CHANGE, ((LessonFull) o).changeId, notified); updateNotified(profileId, TYPE_LESSON_CHANGE, ((LessonFull) o).changeId, notified);
} }
} }
if (o instanceof pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull) {
if (add(new Metadata(profileId, TYPE_LESSON_CHANGE, ((pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull) o).getId(), false, notified, 0)) == -1) {
updateNotified(profileId, TYPE_LESSON_CHANGE, ((pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull) o).getId(), notified);
}
}
if (o instanceof Announcement) { if (o instanceof Announcement) {
if (add(new Metadata(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).id, false, notified, 0)) == -1) { if (add(new Metadata(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).id, false, notified, 0)) == -1) {
updateNotified(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).id, notified); updateNotified(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).id, notified);

View File

@ -21,7 +21,10 @@ interface NotificationDao {
@Query("DELETE FROM notifications WHERE profileId = :profileId") @Query("DELETE FROM notifications WHERE profileId = :profileId")
fun clear(profileId: Int) fun clear(profileId: Int)
@Query("SELECT * FROM notifications") @Query("DELETE FROM notifications")
fun clearAll()
@Query("SELECT * FROM notifications ORDER BY addedDate DESC")
fun getAll(): LiveData<List<Notification>> fun getAll(): LiveData<List<Notification>>
@Query("SELECT * FROM notifications") @Query("SELECT * FROM notifications")

View File

@ -1,5 +1,6 @@
package pl.szczodrzynski.edziennik.data.db.modules.profiles; package pl.szczodrzynski.edziennik.data.db.modules.profiles;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.room.Dao; import androidx.room.Dao;
import androidx.room.Insert; import androidx.room.Insert;
@ -24,6 +25,7 @@ public interface ProfileDao {
@Query("SELECT profiles.*, loginStores.loginStoreType, loginStores.loginStoreData FROM profiles LEFT JOIN loginStores ON profiles.loginStoreId = loginStores.loginStoreId WHERE profileId = :profileId") @Query("SELECT profiles.*, loginStores.loginStoreType, loginStores.loginStoreData FROM profiles LEFT JOIN loginStores ON profiles.loginStoreId = loginStores.loginStoreId WHERE profileId = :profileId")
LiveData<ProfileFull> getById(int profileId); LiveData<ProfileFull> getById(int profileId);
@Nullable
@Query("SELECT profiles.*, loginStores.loginStoreType, loginStores.loginStoreData FROM profiles LEFT JOIN loginStores ON profiles.loginStoreId = loginStores.loginStoreId WHERE profileId = :profileId") @Query("SELECT profiles.*, loginStores.loginStoreType, loginStores.loginStoreData FROM profiles LEFT JOIN loginStores ON profiles.loginStoreId = loginStores.loginStoreId WHERE profileId = :profileId")
ProfileFull getFullByIdNow(int profileId); ProfileFull getFullByIdNow(int profileId);

View File

@ -2,14 +2,15 @@ package pl.szczodrzynski.edziennik.data.db.modules.profiles
import android.content.Context import android.content.Context
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Ignore
import com.google.gson.JsonObject import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA 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_ANNOUNCEMENTS
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ATTENDANCE 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_GRADES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_BEHAVIOUR
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore
import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.* import pl.szczodrzynski.edziennik.data.db.modules.login.LoginStore.*
@ -74,6 +75,7 @@ class ProfileFull : Profile {
fragmentIds.add(DRAWER_ITEM_AGENDA) fragmentIds.add(DRAWER_ITEM_AGENDA)
fragmentIds.add(DRAWER_ITEM_GRADES) fragmentIds.add(DRAWER_ITEM_GRADES)
fragmentIds.add(DRAWER_ITEM_MESSAGES) fragmentIds.add(DRAWER_ITEM_MESSAGES)
fragmentIds.add(DRAWER_ITEM_HOMEWORK)
fragmentIds.add(DRAWER_ITEM_BEHAVIOUR) fragmentIds.add(DRAWER_ITEM_BEHAVIOUR)
fragmentIds.add(DRAWER_ITEM_ATTENDANCE) fragmentIds.add(DRAWER_ITEM_ATTENDANCE)
fragmentIds.add(DRAWER_ITEM_ANNOUNCEMENTS) fragmentIds.add(DRAWER_ITEM_ANNOUNCEMENTS)
@ -87,9 +89,7 @@ class ProfileFull : Profile {
return fragmentIds return fragmentIds
} }
constructor() : super() { constructor() : super()
}
constructor(profile: Profile, loginStore: LoginStore) { constructor(profile: Profile, loginStore: LoginStore) {
@ -122,7 +122,10 @@ class ProfileFull : Profile {
}*/ }*/
} }
constructor(context: Context) : super(context) {} constructor(context: Context) : super(context)
@Ignore
constructor(id: Int, name: String, subname: String, loginStoreId: Int) : super(id, name, subname, loginStoreId)
fun canChangeLoginPassword(): Boolean { fun canChangeLoginPassword(): Boolean {
return loginStoreType == LOGIN_TYPE_MOBIDZIENNIK || loginStoreType == LOGIN_TYPE_LIBRUS || loginStoreType == LOGIN_TYPE_IUCZNIOWIE return loginStoreType == LOGIN_TYPE_MOBIDZIENNIK || loginStoreType == LOGIN_TYPE_LIBRUS || loginStoreType == LOGIN_TYPE_IUCZNIOWIE

View File

@ -15,7 +15,7 @@ import pl.szczodrzynski.edziennik.utils.models.Time
Index(value = ["profileId", "type", "date"]), Index(value = ["profileId", "type", "date"]),
Index(value = ["profileId", "type", "oldDate"]) Index(value = ["profileId", "type", "oldDate"])
]) ])
open class Lesson(val profileId: Int, @PrimaryKey val id: Long) { open class Lesson(val profileId: Int, @PrimaryKey var id: Long) {
companion object { companion object {
const val TYPE_NO_LESSONS = -1 const val TYPE_NO_LESSONS = -1
const val TYPE_NORMAL = 0 const val TYPE_NORMAL = 0
@ -45,6 +45,27 @@ open class Lesson(val profileId: Int, @PrimaryKey val id: Long) {
var oldTeamId: Long? = null var oldTeamId: Long? = null
var oldClassroom: String? = null var oldClassroom: String? = null
val displayDate: Date?
get() {
if (type == TYPE_SHIFTED_SOURCE)
return oldDate
return date ?: oldDate
}
val displayStartTime: Time?
get() {
if (type == TYPE_SHIFTED_SOURCE)
return oldStartTime
return startTime ?: oldStartTime
}
val isCancelled
get() = type == TYPE_CANCELLED || type == TYPE_SHIFTED_SOURCE
val isChange
get() = type == TYPE_CHANGE || type == TYPE_SHIFTED_TARGET
fun buildId(): Long = (displayDate?.combineWith(displayStartTime) ?: 0L) / 6L * 10L + (hashCode() and 0xFFFF)
override fun toString(): String { override fun toString(): String {
return "Lesson(profileId=$profileId, " + return "Lesson(profileId=$profileId, " +
"id=$id, " + "id=$id, " +
@ -66,32 +87,53 @@ open class Lesson(val profileId: Int, @PrimaryKey val id: Long) {
"oldTeamId=$oldTeamId, " + "oldTeamId=$oldTeamId, " +
"oldClassroom=$oldClassroom)" "oldClassroom=$oldClassroom)"
} }
}
/*
DROP TABLE lessons;
DROP TABLE lessonChanges;
CREATE TABLE lessons (
profileId INTEGER NOT NULL,
type INTEGER NOT NULL,
date TEXT DEFAULT NULL, override fun equals(other: Any?): Boolean {
lessonNumber INTEGER DEFAULT NULL, if (this === other) return true
startTime TEXT DEFAULT NULL, if (other !is Lesson) return false
endTime TEXT DEFAULT NULL,
teacherId INTEGER DEFAULT NULL,
subjectId INTEGER DEFAULT NULL,
teamId INTEGER DEFAULT NULL,
classroom TEXT DEFAULT NULL,
oldDate TEXT DEFAULT NULL, if (profileId != other.profileId) return false
oldLessonNumber INTEGER DEFAULT NULL, if (id != other.id) return false
oldStartTime TEXT DEFAULT NULL, if (type != other.type) return false
oldEndTime TEXT DEFAULT NULL, if (date != other.date) return false
oldTeacherId INTEGER DEFAULT NULL, if (lessonNumber != other.lessonNumber) return false
oldSubjectId INTEGER DEFAULT NULL, if (startTime != other.startTime) return false
oldTeamId INTEGER DEFAULT NULL, if (endTime != other.endTime) return false
oldClassroom TEXT DEFAULT NULL, if (subjectId != other.subjectId) return false
if (teacherId != other.teacherId) return false
if (teamId != other.teamId) return false
if (classroom != other.classroom) return false
if (oldDate != other.oldDate) return false
if (oldLessonNumber != other.oldLessonNumber) return false
if (oldStartTime != other.oldStartTime) return false
if (oldEndTime != other.oldEndTime) return false
if (oldSubjectId != other.oldSubjectId) return false
if (oldTeacherId != other.oldTeacherId) return false
if (oldTeamId != other.oldTeamId) return false
if (oldClassroom != other.oldClassroom) return false
PRIMARY KEY(profileId) return true
); }
*/
override fun hashCode(): Int { // intentionally ignoring ID and display* here
var result = profileId
result = 31 * result + type
result = 31 * result + (date?.hashCode() ?: 0)
result = 31 * result + (lessonNumber ?: 0)
result = 31 * result + (startTime?.hashCode() ?: 0)
result = 31 * result + (endTime?.hashCode() ?: 0)
result = 31 * result + (subjectId?.hashCode() ?: 0)
result = 31 * result + (teacherId?.hashCode() ?: 0)
result = 31 * result + (teamId?.hashCode() ?: 0)
result = 31 * result + (classroom?.hashCode() ?: 0)
result = 31 * result + (oldDate?.hashCode() ?: 0)
result = 31 * result + (oldLessonNumber ?: 0)
result = 31 * result + (oldStartTime?.hashCode() ?: 0)
result = 31 * result + (oldEndTime?.hashCode() ?: 0)
result = 31 * result + (oldSubjectId?.hashCode() ?: 0)
result = 31 * result + (oldTeacherId?.hashCode() ?: 0)
result = 31 * result + (oldTeamId?.hashCode() ?: 0)
result = 31 * result + (oldClassroom?.hashCode() ?: 0)
return result
}
}

View File

@ -1,6 +1,7 @@
package pl.szczodrzynski.edziennik.data.db.modules.timetable package pl.szczodrzynski.edziennik.data.db.modules.timetable
import pl.szczodrzynski.edziennik.utils.models.Date import android.content.Context
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
class LessonFull(profileId: Int, id: Long) : Lesson(profileId, id) { class LessonFull(profileId: Int, id: Long) : Lesson(profileId, id) {
@ -11,24 +12,13 @@ class LessonFull(profileId: Int, id: Long) : Lesson(profileId, id) {
var oldTeacherName: String? = null var oldTeacherName: String? = null
var oldTeamName: String? = null var oldTeamName: String? = null
val displayDate: Date?
get() {
if (type == TYPE_SHIFTED_SOURCE)
return oldDate
return date ?: oldDate
}
val displayLessonNumber: Int? val displayLessonNumber: Int?
get() { get() {
if (type == TYPE_SHIFTED_SOURCE) if (type == TYPE_SHIFTED_SOURCE)
return oldLessonNumber return oldLessonNumber
return lessonNumber ?: oldLessonNumber return lessonNumber ?: oldLessonNumber
} }
val displayStartTime: Time?
get() {
if (type == TYPE_SHIFTED_SOURCE)
return oldStartTime
return startTime ?: oldStartTime
}
val displayEndTime: Time? val displayEndTime: Time?
get() { get() {
if (type == TYPE_SHIFTED_SOURCE) if (type == TYPE_SHIFTED_SOURCE)
@ -42,12 +32,14 @@ class LessonFull(profileId: Int, id: Long) : Lesson(profileId, id) {
return oldSubjectName return oldSubjectName
return subjectName ?: oldSubjectName return subjectName ?: oldSubjectName
} }
val displayTeacherName: String? val displayTeacherName: String?
get() { get() {
if (type == TYPE_SHIFTED_SOURCE) if (type == TYPE_SHIFTED_SOURCE)
return oldTeacherName return oldTeacherName
return teacherName ?: oldTeacherName return teacherName ?: oldTeacherName
} }
val displayTeamName: String? val displayTeamName: String?
get() { get() {
if (type == TYPE_SHIFTED_SOURCE) if (type == TYPE_SHIFTED_SOURCE)
@ -68,12 +60,14 @@ class LessonFull(profileId: Int, id: Long) : Lesson(profileId, id) {
return oldTeamId return oldTeamId
return teamId ?: oldTeamId return teamId ?: oldTeamId
} }
val displaySubjectId: Long? val displaySubjectId: Long?
get() { get() {
if (type == TYPE_SHIFTED_SOURCE) if (type == TYPE_SHIFTED_SOURCE)
return oldSubjectId return oldSubjectId
return subjectId ?: oldSubjectId return subjectId ?: oldSubjectId
} }
val displayTeacherId: Long? val displayTeacherId: Long?
get() { get() {
if (type == TYPE_SHIFTED_SOURCE) if (type == TYPE_SHIFTED_SOURCE)
@ -81,8 +75,55 @@ class LessonFull(profileId: Int, id: Long) : Lesson(profileId, id) {
return teacherId ?: oldTeacherId return teacherId ?: oldTeacherId
} }
fun getDisplayChangeType(context: Context): String {
return context.getString(when (type) {
TYPE_CHANGE -> R.string.lesson_change
TYPE_CANCELLED -> R.string.lesson_cancelled
TYPE_SHIFTED_TARGET, TYPE_SHIFTED_SOURCE -> R.string.lesson_shifted
else -> R.string.lesson_timetable_change
})
}
private fun changeText(actual: String?, old: String?): String {
val first = when (type) {
TYPE_CHANGE, TYPE_CANCELLED, TYPE_SHIFTED_SOURCE -> old
else -> actual
}
val second = when (type) {
TYPE_CHANGE -> actual
else -> null
}
return when (second) {
null -> first.orEmpty()
first -> second
else -> if (first != null) "$first -> $second" else second.orEmpty()
}
}
val changeSubjectName: String
get() = changeText(subjectName, oldSubjectName)
val isSubjectNameChanged: Boolean
get() = type == TYPE_CHANGE && subjectName != oldSubjectName
val changeTeacherName: String
get() = changeText(teacherName, oldTeacherName)
val isTeacherNameChanged: Boolean
get() = type == TYPE_CHANGE && teacherName != oldTeacherName
val changeClassroom: String
get() = changeText(classroom, oldClassroom)
val isClassroomChanged: Boolean
get() = type == TYPE_CHANGE && classroom != oldClassroom
// metadata // metadata
var seen: Boolean = false var seen: Boolean = false
var notified: Boolean = false var notified: Boolean = false
var addedDate: Long = 0 var addedDate: Long = 0
} }

View File

@ -52,6 +52,13 @@ interface TimetableDao {
""") """)
fun getForDate(profileId: Int, date: Date) : LiveData<List<LessonFull>> fun getForDate(profileId: Int, date: Date) : LiveData<List<LessonFull>>
@Query("""
$QUERY
WHERE timetable.profileId = :profileId AND ((type != 3 AND date = :date) OR ((type = 3 OR type = 1) AND oldDate = :date))
ORDER BY id, type
""")
fun getForDateNow(profileId: Int, date: Date) : List<LessonFull>
@Query(""" @Query("""
$QUERY $QUERY
WHERE timetable.profileId = :profileId AND ((type != 3 AND date > :today) OR ((type = 3 OR type = 1) AND oldDate > :today)) AND timetable.subjectId = :subjectId WHERE timetable.profileId = :profileId AND ((type != 3 AND date > :today) OR ((type = 3 OR type = 1) AND oldDate > :today)) AND timetable.subjectId = :subjectId
@ -75,10 +82,23 @@ interface TimetableDao {
""") """)
fun getBetweenDatesNow(dateFrom: Date, dateTo: Date) : List<LessonFull> fun getBetweenDatesNow(dateFrom: Date, dateTo: Date) : List<LessonFull>
@Query("""
$QUERY
WHERE (type != 3 AND date >= :dateFrom AND date <= :dateTo) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom AND oldDate <= :dateTo)
ORDER BY profileId, id, type
""")
fun getBetweenDates(dateFrom: Date, dateTo: Date) : LiveData<List<LessonFull>>
@Query(""" @Query("""
$QUERY $QUERY
WHERE timetable.profileId = :profileId AND timetable.id = :lessonId WHERE timetable.profileId = :profileId AND timetable.id = :lessonId
ORDER BY id, type ORDER BY id, type
""") """)
fun getByIdNow(profileId: Int, lessonId: Long) : LessonFull? fun getByIdNow(profileId: Int, lessonId: Long) : LessonFull?
@Query("""
$QUERY
WHERE timetable.profileId = :profileId AND timetable.type NOT IN (${Lesson.TYPE_NORMAL}, ${Lesson.TYPE_NO_LESSONS}, ${Lesson.TYPE_SHIFTED_SOURCE}) AND metadata.notified = 0
""")
fun getNotNotifiedNow(profileId: Int): List<LessonFull>
} }

View File

@ -90,12 +90,12 @@ public class ServerRequest {
.addParameter("device_id", Settings.Secure.getString(app.getContext().getContentResolver(), Settings.Secure.ANDROID_ID)) .addParameter("device_id", Settings.Secure.getString(app.getContext().getContentResolver(), Settings.Secure.ANDROID_ID))
.addParameter("device_model", Build.MANUFACTURER+" "+Build.MODEL) .addParameter("device_model", Build.MANUFACTURER+" "+Build.MODEL)
.addParameter("device_os_version", Build.VERSION.RELEASE) .addParameter("device_os_version", Build.VERSION.RELEASE)
.addParameter("fcm_token", app.appConfig.fcmToken) .addParameter("fcm_token", app.config.getSync().getTokenApp())
.addParameter("signature", sign(app.signature, timestamp)) .addParameter("signature", sign(app.signature, timestamp))
.addParameter("signature_timestamp", timestamp) .addParameter("signature_timestamp", timestamp)
.addParameter("package_name", "pl.szczodrzynski.edziennik") .addParameter("package_name", "pl.szczodrzynski.edziennik")
.addParameter("source", source) .addParameter("source", source)
.addParameter("update_frequency", app.appConfig.registerSyncEnabled ? app.appConfig.registerSyncInterval : -1) .addParameter("update_frequency", app.config.getSync().getEnabled() ? app.config.getSync().getInterval() : -1)
.post() .post()
.callback(new JsonCallbackHandler() { .callback(new JsonCallbackHandler() {
@Override @Override
@ -127,12 +127,12 @@ public class ServerRequest {
.addParameter("device_id", Settings.Secure.getString(app.getContext().getContentResolver(), Settings.Secure.ANDROID_ID)) .addParameter("device_id", Settings.Secure.getString(app.getContext().getContentResolver(), Settings.Secure.ANDROID_ID))
.addParameter("device_model", Build.MANUFACTURER+" "+Build.MODEL) .addParameter("device_model", Build.MANUFACTURER+" "+Build.MODEL)
.addParameter("device_os_version", Build.VERSION.RELEASE) .addParameter("device_os_version", Build.VERSION.RELEASE)
.addParameter("fcm_token", app.appConfig.fcmToken) .addParameter("fcm_token", app.config.getSync().getTokenApp())
.addParameter("signature", sign(app.signature, timestamp)) .addParameter("signature", sign(app.signature, timestamp))
.addParameter("signature_timestamp", timestamp) .addParameter("signature_timestamp", timestamp)
.addParameter("package_name", "pl.szczodrzynski.edziennik") .addParameter("package_name", "pl.szczodrzynski.edziennik")
.addParameter("source", source) .addParameter("source", source)
.addParameter("update_frequency", app.appConfig.registerSyncEnabled ? app.appConfig.registerSyncInterval : -1) .addParameter("update_frequency", app.config.getSync().getEnabled() ? app.config.getSync().getInterval() : -1)
.post() .post()
.build() .build()
.execute(); .execute();

View File

@ -47,9 +47,8 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
Log.d(TAG, "New token: "+s); Log.d(TAG, "New token: "+s);
App app = (App)getApplicationContext(); App app = (App)getApplicationContext();
if (app.appConfig.fcmToken == null || !app.appConfig.fcmToken.equals(s)) { if (app.config.getSync().getTokenApp() == null || !app.config.getSync().getTokenApp().equals(s)) {
app.appConfig.fcmToken = s; app.config.getSync().setTokenApp(s);
app.saveConfig();
} }
} }
@ -198,8 +197,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
else { else {
feedbackMessage.sentTime = Long.parseLong(remoteMessage.getData().get("sent_time")); feedbackMessage.sentTime = Long.parseLong(remoteMessage.getData().get("sent_time"));
if (feedbackMessage.text.startsWith("devmode")) { if (feedbackMessage.text.startsWith("devmode")) {
app.appConfig.devModePassword = feedbackMessage.text.replace("devmode", ""); app.config.setDevModePassword(feedbackMessage.text.replace("devmode", ""));
app.saveConfig("devModePassword");
app.checkDevModePassword(); app.checkDevModePassword();
feedbackMessage.text = "devmode "+(App.devMode ? "allowed" : "disallowed"); feedbackMessage.text = "devmode "+(App.devMode ? "allowed" : "disallowed");
} }

View File

@ -57,16 +57,16 @@ class SyncWorker(val context: Context, val params: WorkerParameters) : Worker(co
/** /**
* Cancel any existing sync jobs and schedule a new one. * Cancel any existing sync jobs and schedule a new one.
* *
* If [registerSyncEnabled] is not true, just cancel every job. * If [ConfigSync.enabled] is not true, just cancel every job.
*/ */
fun rescheduleNext(app: App) { fun rescheduleNext(app: App) {
cancelNext(app) cancelNext(app)
val enableSync = app.appConfig.registerSyncEnabled val enableSync = app.config.sync.enabled
if (!enableSync) { if (!enableSync) {
return return
} }
val onlyWifi = app.appConfig.registerSyncOnlyWifi val onlyWifi = app.config.sync.onlyWifi
val syncInterval = app.appConfig.registerSyncInterval.toLong() val syncInterval = app.config.sync.interval.toLong()
val syncAt = System.currentTimeMillis() + syncInterval*1000 val syncAt = System.currentTimeMillis() + syncInterval*1000
d(TAG, "Scheduling work at ${syncAt.formatDate()}") d(TAG, "Scheduling work at ${syncAt.formatDate()}")

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-11-30
*/
package pl.szczodrzynski.edziennik.ui.dialogs.event
import android.content.Context
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.RecyclerView
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull
import pl.szczodrzynski.edziennik.databinding.RowDialogEventListItemBinding
import pl.szczodrzynski.edziennik.utils.Utils.bs
import pl.szczodrzynski.edziennik.utils.models.Date
class EventListAdapter(
val context: Context,
val parentDialog: EventListDialog
) : RecyclerView.Adapter<EventListAdapter.ViewHolder>() {
private val app by lazy { context.applicationContext as App }
val eventList = mutableListOf<EventFull>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val view: RowDialogEventListItemBinding = DataBindingUtil.inflate(inflater, R.layout.row_dialog_event_list_item, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val event = eventList[position]
holder.apply {
b.eventListItemRoot.background.colorFilter = when (event.type) {
Event.TYPE_HOMEWORK -> PorterDuffColorFilter((0xffffffff).toInt(), PorterDuff.Mode.CLEAR)
else -> PorterDuffColorFilter(event.color, PorterDuff.Mode.MULTIPLY)
}
b.eventListItemStartTime.text = if (event.startTime == null) app.getString(R.string.event_all_day) else event.startTime?.stringHM
b.eventListItemTeamName.text = bs(event.teamName)
b.eventListItemTeacherName.text = app.getString(R.string.concat_2_strings, bs(null, event.teacherFullName, "\n"), bs(event.subjectLongName))
b.eventListItemAddedDate.text = Date.fromMillis(event.addedDate).formattedStringShort
b.eventListItemType.text = event.typeName
b.eventListItemTopic.text = event.topic
b.eventListItemHomework.visibility = if (event.type == Event.TYPE_HOMEWORK) View.VISIBLE else View.GONE
b.eventListItemSharedBy.text = app.getString(R.string.event_shared_by_format, if (event.sharedBy == "self") app.getString(R.string.event_shared_by_self) else event.sharedByName)
b.eventListItemSharedBy.visibility = if (event.sharedByName.isNullOrBlank()) View.GONE else View.VISIBLE
b.eventListItemEdit.visibility = if (event.addedManually) View.VISIBLE else View.GONE
b.eventListItemEdit.setOnClickListener {
parentDialog.dismiss()
EventManualV2Dialog(
context as MainActivity,
event.profileId,
editingEvent = event,
onShowListener = parentDialog.onShowListener,
onDismissListener = parentDialog.onDismissListener
)
}
}
}
override fun getItemCount(): Int = eventList.size
class ViewHolder(val b: RowDialogEventListItemBinding) : RecyclerView.ViewHolder(b.root)
}

View File

@ -3,14 +3,15 @@ package pl.szczodrzynski.edziennik.ui.dialogs.event;
import android.content.Context; import android.content.Context;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter; import android.graphics.PorterDuffColorFilter;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.RecyclerView;
import com.mikepenz.iconics.view.IconicsImageView; import com.mikepenz.iconics.view.IconicsImageView;
import com.mikepenz.iconics.view.IconicsTextView; import com.mikepenz.iconics.view.IconicsTextView;
@ -20,21 +21,21 @@ import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.db.modules.events.Event; import pl.szczodrzynski.edziennik.data.db.modules.events.Event;
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull; import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.Utils; import pl.szczodrzynski.edziennik.utils.Utils;
import pl.szczodrzynski.edziennik.utils.models.Date;
import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_HOMEWORK; import static pl.szczodrzynski.edziennik.data.db.modules.events.Event.TYPE_HOMEWORK;
import static pl.szczodrzynski.edziennik.utils.Utils.bs; import static pl.szczodrzynski.edziennik.utils.Utils.bs;
import static pl.szczodrzynski.edziennik.utils.Utils.d; import static pl.szczodrzynski.edziennik.utils.Utils.d;
public class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.ViewHolder> { public class EventListAdapterOld extends RecyclerView.Adapter<EventListAdapterOld.ViewHolder> {
private static final String TAG = "EventListAdapter"; private static final String TAG = "EventListAdapterOld";
private Context context; private Context context;
private List<EventFull> examList; private List<EventFull> examList;
private EventListDialog parentDialog; private EventListDialogOld parentDialog;
//getting the context and product list with constructor //getting the context and product list with constructor
public EventListAdapter(Context mCtx, List<EventFull> examList, EventListDialog parentDialog) { public EventListAdapterOld(Context mCtx, List<EventFull> examList, EventListDialogOld parentDialog) {
this.context = mCtx; this.context = mCtx;
this.examList = examList; this.examList = examList;
this.parentDialog = parentDialog; this.parentDialog = parentDialog;
@ -42,15 +43,15 @@ public class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.View
@NonNull @NonNull
@Override @Override
public EventListAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public EventListAdapterOld.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//inflating and returning our view holder //inflating and returning our view holder
LayoutInflater inflater = LayoutInflater.from(context); LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.row_dialog_event_list_item, parent, false); View view = inflater.inflate(R.layout.row_dialog_event_list_item, parent, false);
return new EventListAdapter.ViewHolder(view); return new EventListAdapterOld.ViewHolder(view);
} }
@Override @Override
public void onBindViewHolder(@NonNull EventListAdapter.ViewHolder holder, int position) { public void onBindViewHolder(@NonNull EventListAdapterOld.ViewHolder holder, int position) {
App app = (App) context.getApplicationContext(); App app = (App) context.getApplicationContext();
EventFull event = examList.get(position); EventFull event = examList.get(position);

View File

@ -0,0 +1,138 @@
/*
* Copyright (c) Kacper Ziubryniewicz 2019-11-30
*/
package pl.szczodrzynski.edziennik.ui.dialogs.event
import android.graphics.Typeface
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
import pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull
import pl.szczodrzynski.edziennik.databinding.DialogEventListBinding
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
import kotlin.coroutines.CoroutineContext
class EventListDialog(
val activity: AppCompatActivity,
val profileId: Int,
val date: Date,
val time: Time? = null,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) : CoroutineScope {
companion object {
const val TAG = "EventListDialog"
}
private lateinit var job: Job
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
private val app by lazy { activity.application as App }
private lateinit var b: DialogEventListBinding
private lateinit var dialog: AlertDialog
private lateinit var adapter: EventListAdapter
private var lesson: LessonFull? = null
init {
run {
if (activity.isFinishing)
return@run
job = Job()
onShowListener?.invoke(TAG)
b = DialogEventListBinding.inflate(activity.layoutInflater)
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(date.formattedString + (time?.let { ", " + it.stringHM } ?: ""))
.setView(b.root)
.setPositiveButton(R.string.close) { dialog, _ -> dialog.dismiss() }
.setNeutralButton(R.string.add) { _, _ ->
EventManualV2Dialog(
activity,
lesson?.profileId ?: profileId,
lesson,
date,
time,
onShowListener = onShowListener,
onDismissListener = onDismissListener
)
}
.setOnDismissListener {
onDismissListener?.invoke(TAG)
}
.show()
app.db.timetableDao().getForDate(profileId, date).observe(activity, Observer { lessons ->
lesson = lessons.firstOrNull { it.displayStartTime == time }
update()
})
}
}
fun dismiss() = dialog.dismiss()
private fun update() {
b.eventListLessonDetails.visibility = if (lesson == null) View.GONE else View.VISIBLE
if (lesson != null) {
dialog.setTitle(if (time == null) date.formattedString else (lesson?.displaySubjectName
?: date.formattedString) + ", " + time.stringHM)
b.eventListLessonDate.text = app.getString(R.string.date_time_format, date.formattedString, "")
if (lesson?.type == Lesson.TYPE_CANCELLED) {
b.eventListLessonChange.text = app.getString(R.string.lesson_cancelled)
b.eventListLessonChange.setTypeface(null, Typeface.BOLD_ITALIC)
b.eventListTeacher.visibility = View.GONE
b.eventListClassroom.visibility = View.GONE
} else {
b.eventListLessonChange.text = lesson?.changeSubjectName
b.eventListLessonChange.setTypeface(null, Typeface.ITALIC)
b.eventListLessonChange.visibility = if (lesson?.isSubjectNameChanged == true) View.VISIBLE else View.GONE
b.eventListTeacher.text = lesson?.changeTeacherName
b.eventListTeacher.setTypeface(null, if (lesson?.isTeacherNameChanged == true) Typeface.ITALIC else Typeface.NORMAL)
b.eventListClassroom.text = lesson?.changeClassroom
b.eventListClassroom.setTypeface(null, if (lesson?.isClassroomChanged == true) Typeface.ITALIC else Typeface.NORMAL)
}
}
b.eventListView.apply {
setHasFixedSize(false)
isNestedScrollingEnabled = true
layoutManager = LinearLayoutManager(activity)
}
adapter = EventListAdapter(activity, this@EventListDialog)
b.eventListView.adapter = adapter
app.db.eventDao().getAllByDateTime(profileId, date, time).observe(activity, Observer { events ->
if (events.isNullOrEmpty()) {
b.eventListView.visibility = View.GONE
b.textNoEvents.visibility = View.VISIBLE
} else {
adapter.run {
eventList.apply {
clear()
addAll(events)
}
notifyDataSetChanged()
}
}
})
}
}

View File

@ -8,14 +8,14 @@ import android.view.View;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import com.afollestad.materialdialogs.MaterialDialog;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.cardview.widget.CardView; import androidx.cardview.widget.CardView;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.afollestad.materialdialogs.MaterialDialog;
import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange; import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonChange;
@ -26,16 +26,16 @@ import pl.szczodrzynski.edziennik.ui.dialogs.teacherabsence.TeacherAbsenceDialog
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;
public class EventListDialog { public class EventListDialogOld {
private App app; private App app;
private Context context; private Context context;
private int profileId; private int profileId;
public EventListDialog(Context context) { public EventListDialogOld(Context context) {
this.context = context; this.context = context;
this.profileId = App.profileId; this.profileId = App.profileId;
} }
public EventListDialog(Context context, int profileId) { public EventListDialogOld(Context context, int profileId) {
this.context = context; this.context = context;
this.profileId = profileId; this.profileId = profileId;
} }
@ -47,7 +47,7 @@ public class EventListDialog {
public boolean callDismissListener = true; public boolean callDismissListener = true;
private LessonFull lesson; private LessonFull lesson;
public EventListDialog withDismissListener(DialogInterface.OnDismissListener dismissListener) { public EventListDialogOld withDismissListener(DialogInterface.OnDismissListener dismissListener) {
this.dismissListener = dismissListener; this.dismissListener = dismissListener;
return this; return this;
} }
@ -222,7 +222,7 @@ public class EventListDialog {
dialogView.findViewById(R.id.textNoEvents).setVisibility(View.VISIBLE); dialogView.findViewById(R.id.textNoEvents).setVisibility(View.VISIBLE);
} }
else { else {
EventListAdapter adapter = new EventListAdapter(context, events, this); EventListAdapterOld adapter = new EventListAdapterOld(context, events, this);
examsView.setAdapter(adapter); examsView.setAdapter(adapter);
} }
}); });

View File

@ -517,7 +517,7 @@ public class EventManualDialog {
registerEventManualDateLayout = dialogView.findViewById(R.id.registerEventManualDateLayout); registerEventManualDateLayout = dialogView.findViewById(R.id.registerEventManualDateLayout);
registerEventManualDate = dialogView.findViewById(R.id.registerEventManualDate); registerEventManualDate = dialogView.findViewById(R.id.registerEventManualDate);
registerEventManualDate.setCompoundDrawablesWithIntrinsicBounds(null, null, new IconicsDrawable(context, CommunityMaterial.Icon.cmd_calendar).size(IconicsSize.dp(16)).color(IconicsColor.colorInt(primaryTextColor)), null); registerEventManualDate.setCompoundDrawablesWithIntrinsicBounds(null, null, new IconicsDrawable(context, CommunityMaterial.Icon.cmd_calendar_outline).size(IconicsSize.dp(16)).color(IconicsColor.colorInt(primaryTextColor)), null);
//registerEventManualDate.setCompoundDrawablePadding(Utils.dpToPx(6)); //registerEventManualDate.setCompoundDrawablePadding(Utils.dpToPx(6));
registerEventManualLessonLayout = dialogView.findViewById(R.id.registerEventManualLessonLayout); registerEventManualLessonLayout = dialogView.findViewById(R.id.registerEventManualLessonLayout);
registerEventManualLesson = dialogView.findViewById(R.id.registerEventManualLesson); registerEventManualLesson = dialogView.findViewById(R.id.registerEventManualLesson);

View File

@ -6,7 +6,10 @@ package pl.szczodrzynski.edziennik.ui.dialogs.event
import android.graphics.PorterDuff import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter import android.graphics.PorterDuffColorFilter
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import com.google.android.material.datepicker.MaterialDatePicker import com.google.android.material.datepicker.MaterialDatePicker
@ -17,7 +20,9 @@ import kotlinx.coroutines.*
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.db.modules.events.Event import pl.szczodrzynski.edziennik.data.db.modules.events.Event
import pl.szczodrzynski.edziennik.data.db.modules.events.EventType import pl.szczodrzynski.edziennik.data.db.modules.events.EventType
import pl.szczodrzynski.edziennik.data.db.modules.metadata.Metadata
import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject import pl.szczodrzynski.edziennik.data.db.modules.subjects.Subject
import pl.szczodrzynski.edziennik.data.db.modules.teachers.Teacher
import pl.szczodrzynski.edziennik.data.db.modules.teams.Team import pl.szczodrzynski.edziennik.data.db.modules.teams.Team
import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson import pl.szczodrzynski.edziennik.data.db.modules.timetable.Lesson
import pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull import pl.szczodrzynski.edziennik.data.db.modules.timetable.LessonFull
@ -67,25 +72,28 @@ class EventManualV2Dialog(
.setTitle(R.string.dialog_event_manual_title) .setTitle(R.string.dialog_event_manual_title)
.setView(b.root) .setView(b.root)
.setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() } .setNegativeButton(R.string.cancel) { dialog, _ -> dialog.dismiss() }
.setPositiveButton(R.string.save) { _, _ -> saveEvent() } .setPositiveButton(R.string.save, null)
.setOnDismissListener { .setOnDismissListener {
onDismissListener?.invoke(TAG) onDismissListener?.invoke(TAG)
} }
.show() .create()
.apply {
setOnShowListener { dialog ->
val positiveButton = (dialog as AlertDialog).getButton(BUTTON_POSITIVE)
positiveButton.setOnClickListener {
saveEvent()
}
}
show()
}
event = editingEvent?.clone() ?: Event().also { event -> event = editingEvent?.clone() ?: Event().also { event ->
event.profileId = profileId event.profileId = profileId
/*defaultDate?.let {
event.eventDate = it
b.date = it
}
defaultTime?.let {
event.startTime = it
b.time = it
}
defaultType?.let { defaultType?.let {
event.type = it event.type = it
}*/ }
b.shareSwitch.isChecked = event.sharedBy != null
} }
b.showMore.onClick { // TODO iconics is broken b.showMore.onClick { // TODO iconics is broken
@ -99,9 +107,32 @@ class EventManualV2Dialog(
} }
} }
updateShareText()
b.shareSwitch.onChange { _, isChecked ->
updateShareText(isChecked)
}
loadLists() loadLists()
}} }}
private fun updateShareText(checked: Boolean = b.shareSwitch.isChecked) {
val editingShared = editingEvent?.sharedBy != null
val editingOwn = editingEvent?.sharedBy == "self"
b.shareDetails.visibility = if (checked || editingShared)
View.VISIBLE
else View.GONE
val text = when {
checked && editingShared && editingOwn -> R.string.dialog_event_manual_share_will_change
checked && editingShared -> R.string.dialog_event_manual_share_will_request
!checked && editingShared -> R.string.dialog_event_manual_share_will_remove
else -> R.string.dialog_event_manual_share_first_notice
}
b.shareDetails.setText(text)
}
private fun loadLists() { launch { private fun loadLists() { launch {
val deferred = async(Dispatchers.Default) { val deferred = async(Dispatchers.Default) {
// get the team list // get the team list
@ -155,6 +186,8 @@ class EventManualV2Dialog(
b.teamDropdown.select(it.teamId) b.teamDropdown.select(it.teamId)
b.subjectDropdown.select(it.subjectId) b.subjectDropdown.select(it.subjectId)
b.teacherDropdown.select(it.teacherId) b.teacherDropdown.select(it.teacherId)
b.topic.setText(it.topic)
b.shareSwitch.isChecked = true
b.typeDropdown.select(it.type)?.let { item -> b.typeDropdown.select(it.type)?.let { item ->
customColor = (item.tag as EventType).color customColor = (item.tag as EventType).color
} }
@ -263,6 +296,16 @@ class EventManualV2Dialog(
val dates = deferred.await() val dates = deferred.await()
b.dateDropdown.clear().append(dates) b.dateDropdown.clear().append(dates)
defaultDate?.let {
event.eventDate = it
if (b.dateDropdown.select(it) == null)
b.dateDropdown.select(TextInputDropDown.Item(
it.value.toLong(),
it.formattedString,
tag = it
))
}
editingEvent?.eventDate?.let { editingEvent?.eventDate?.let {
b.dateDropdown.select(TextInputDropDown.Item( b.dateDropdown.select(TextInputDropDown.Item(
it.value.toLong(), it.value.toLong(),
@ -400,6 +443,16 @@ class EventManualV2Dialog(
b.teacherDropdown.deselect() b.teacherDropdown.deselect()
} }
else { else {
defaultTime?.let {
event.startTime = it
if (b.timeDropdown.select(it) == null)
b.timeDropdown.select(TextInputDropDown.Item(
it.value.toLong(),
it.stringHM,
tag = it
))
}
editingEvent?.let { editingEvent?.let {
b.timeDropdown.select(it.startTime?.value?.toLong()) b.timeDropdown.select(it.startTime?.value?.toLong())
} }
@ -417,17 +470,16 @@ class EventManualV2Dialog(
// attach a listener to time dropdown // attach a listener to time dropdown
b.timeDropdown.setOnChangeListener { item -> b.timeDropdown.setOnChangeListener { item ->
when { when (item.id) {
// no lessons this day // no lessons this day
item.id == -2L -> { -2L -> {
b.timeDropdown.deselect() b.timeDropdown.deselect()
return@setOnChangeListener false return@setOnChangeListener false
} }
// custom start hour
item.id == -1L -> {
return@setOnChangeListener false // custom start hour
} -1L -> return@setOnChangeListener false
// selected a specific lesson // selected a specific lesson
else -> { else -> {
if (item.tag is LessonFull) { if (item.tag is LessonFull) {
@ -436,7 +488,7 @@ class EventManualV2Dialog(
b.teamDropdown.deselect() b.teamDropdown.deselect()
b.subjectDropdown.deselect() b.subjectDropdown.deselect()
b.teacherDropdown.deselect() b.teacherDropdown.deselect()
item.tag.displayTeamId?.let { item.tag.displayTeamId?.let {
b.teamDropdown.select(it) b.teamDropdown.select(it)
} }
item.tag.displaySubjectId?.let { item.tag.displaySubjectId?.let {
@ -454,6 +506,80 @@ class EventManualV2Dialog(
} }
private fun saveEvent() { private fun saveEvent() {
val date = b.dateDropdown.selected?.tag.instanceOfOrNull<Date>()
val lesson = b.timeDropdown.selected?.tag.instanceOfOrNull<LessonFull>()
val team = b.teamDropdown.selected?.tag.instanceOfOrNull<Team>()
val share = b.shareSwitch.isChecked
val type = b.typeDropdown.selected?.tag.instanceOfOrNull<EventType>()
val topic = b.topic.text?.toString()
val subject = b.subjectDropdown.selected?.tag.instanceOfOrNull<Subject>()
val teacher = b.teacherDropdown.selected?.tag.instanceOfOrNull<Teacher>()
b.teamDropdown.error = null
b.typeDropdown.error = null
b.topic.error = null
var isError = false
if (share && team == null) {
b.teamDropdown.error = app.getString(R.string.dialog_event_manual_team_choose)
isError = true
}
if (type == null) {
b.typeDropdown.error = app.getString(R.string.dialog_event_manual_type_choose)
isError = true
}
if (topic.isNullOrBlank()) {
b.topic.error = app.getString(R.string.dialog_event_manual_topic_choose)
isError = true
}
if (isError) return
val id = System.currentTimeMillis()
val eventObject = Event(
profileId,
id,
date,
lesson?.displayStartTime,
topic,
customColor ?: -1,
type?.id?.toInt() ?: Event.TYPE_DEFAULT,
true,
teacher?.id ?: -1,
subject?.id ?: -1,
team?.id ?: -1
)
val metadataObject = Metadata(
profileId,
when (type?.id?.toInt()) {
Event.TYPE_HOMEWORK -> Metadata.TYPE_HOMEWORK
else -> Metadata.TYPE_EVENT
},
eventObject.id,
true,
true,
System.currentTimeMillis()
)
finishAdding(eventObject, metadataObject)
}
private fun finishAdding(eventObject: Event, metadataObject: Metadata) {
launch {
val deferred = async(Dispatchers.Default) {
app.db.eventDao().add(eventObject)
app.db.metadataDao().add(metadataObject)
}
deferred.await()
}
dialog.dismiss()
Toast.makeText(app, R.string.saved, Toast.LENGTH_SHORT).show()
} }
} }

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) Kuba Szczodrzyński 2019-11-24.
*/
package pl.szczodrzynski.edziennik.ui.dialogs.home
import android.text.InputType
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.afollestad.materialdialogs.MaterialDialog
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.modules.profiles.Profile
class StudentNumberDialog(
val activity: AppCompatActivity,
val profile: Profile,
val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null
) {
companion object {
private const val TAG = "StudentNumberDialog"
}
private lateinit var dialog: AlertDialog
init { run {
if (activity.isFinishing)
return@run
onShowListener?.invoke(TAG)
MaterialDialog.Builder(activity)
.title(R.string.card_lucky_number_set_title)
.content(R.string.card_lucky_number_set_text)
.inputType(InputType.TYPE_CLASS_NUMBER)
.input(null, if (profile.studentNumber == -1) "" else profile.studentNumber.toString()) { _: MaterialDialog?, input: CharSequence ->
try {
profile.studentNumber = input.toString().toInt()
} catch (e: Exception) {
Toast.makeText(activity, R.string.incorrect_format, Toast.LENGTH_SHORT).show()
}
}
.dismissListener {
onDismissListener?.invoke(TAG)
}.show()
}}
}

View File

@ -94,15 +94,23 @@ class SyncViewListDialog(
listOfNotNull(*it.toTypedArray()) listOfNotNull(*it.toTypedArray())
} }
if (selectedViewIds.isNotEmpty()) {
activity.swipeRefreshLayout.isRefreshing = true
EdziennikTask.syncProfile(
App.profileId,
selectedViewIds
).enqueue(activity)
}
}
.setNeutralButton(R.string.sync_feature_all) { _, _ ->
dialog.dismiss()
activity.swipeRefreshLayout.isRefreshing = true activity.swipeRefreshLayout.isRefreshing = true
EdziennikTask.syncProfile( EdziennikTask.syncProfile(App.profileId).enqueue(activity)
App.profileId,
selectedViewIds
).enqueue(activity)
} }
.setNegativeButton(R.string.cancel) { _, _ -> .setNegativeButton(R.string.cancel) { _, _ ->
dialog.dismiss() dialog.dismiss()
} }
.show() .show()
}} }}
} }

View File

@ -42,7 +42,7 @@ class LessonDetailsDialog(
.setPositiveButton(R.string.close) { dialog, _ -> .setPositiveButton(R.string.close) { dialog, _ ->
dialog.dismiss() dialog.dismiss()
} }
.setNeutralButton(R.string.add) { dialog, _ -> .setNeutralButton(R.string.add) { _, _ ->
EventManualV2Dialog( EventManualV2Dialog(
activity, activity,
lesson.profileId, lesson.profileId,
@ -120,7 +120,7 @@ class LessonDetailsDialog(
dialog.dismiss() dialog.dismiss()
val dateStr = otherLessonDate?.stringY_m_d ?: return@setOnClickListener val dateStr = otherLessonDate?.stringY_m_d ?: return@setOnClickListener
val intent = Intent(TimetableFragment.ACTION_SCROLL_TO_DATE).apply { val intent = Intent(TimetableFragment.ACTION_SCROLL_TO_DATE).apply {
putExtra("date", dateStr) putExtra("timetableDate", dateStr)
} }
activity.sendBroadcast(intent) activity.sendBroadcast(intent)
} }
@ -157,4 +157,4 @@ class LessonDetailsDialog(
b.teamName = lesson.teamName b.teamName = lesson.teamName
} }
} }
} }

View File

@ -6,15 +6,16 @@ import android.graphics.drawable.Drawable;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.fragment.app.Fragment;
import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.materialdialogs.MaterialDialog;
import com.applandeo.materialcalendarview.CalendarView; import com.applandeo.materialcalendarview.CalendarView;
import com.applandeo.materialcalendarview.EventDay; import com.applandeo.materialcalendarview.EventDay;
@ -27,6 +28,7 @@ import com.mikepenz.iconics.IconicsColor;
import com.mikepenz.iconics.IconicsDrawable; import com.mikepenz.iconics.IconicsDrawable;
import com.mikepenz.iconics.IconicsSize; import com.mikepenz.iconics.IconicsSize;
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial; import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
@ -34,13 +36,13 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.MainActivity; import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull;
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonFull;
import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsenceFull; import pl.szczodrzynski.edziennik.data.db.modules.teachers.TeacherAbsenceFull;
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaCalendarBinding; import pl.szczodrzynski.edziennik.databinding.FragmentAgendaCalendarBinding;
import pl.szczodrzynski.edziennik.databinding.FragmentAgendaDefaultBinding; import pl.szczodrzynski.edziennik.databinding.FragmentAgendaDefaultBinding;
import pl.szczodrzynski.edziennik.data.db.modules.events.EventFull;
import pl.szczodrzynski.edziennik.data.db.modules.lessons.LessonFull;
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListDialog; import pl.szczodrzynski.edziennik.ui.dialogs.event.EventListDialog;
import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog; import pl.szczodrzynski.edziennik.ui.dialogs.event.EventManualDialog;
import pl.szczodrzynski.edziennik.ui.dialogs.lessonchange.LessonChangeDialog; import pl.szczodrzynski.edziennik.ui.dialogs.lessonchange.LessonChangeDialog;
@ -51,11 +53,11 @@ import pl.szczodrzynski.edziennik.ui.modules.agenda.lessonchange.LessonChangeEve
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceCounter; import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceCounter;
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEvent; import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEvent;
import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEventRenderer; import pl.szczodrzynski.edziennik.ui.modules.agenda.teacherabsence.TeacherAbsenceEventRenderer;
import pl.szczodrzynski.edziennik.utils.models.Date;
import pl.szczodrzynski.edziennik.utils.models.Time;
import pl.szczodrzynski.edziennik.utils.Colors; import pl.szczodrzynski.edziennik.utils.Colors;
import pl.szczodrzynski.edziennik.utils.Themes; import pl.szczodrzynski.edziennik.utils.Themes;
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.Time;
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem; import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem;
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem; import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem;
@ -82,7 +84,7 @@ public class AgendaFragment extends Fragment {
if (app.profile == null) if (app.profile == null)
return inflater.inflate(R.layout.fragment_loading, container, false); return inflater.inflate(R.layout.fragment_loading, container, false);
// activity, context and profile is valid // activity, context and profile is valid
viewType = app.profile.getAgendaViewType(); viewType = app.config.getUi().getAgendaViewType();
if (viewType == AGENDA_DEFAULT) { if (viewType == AGENDA_DEFAULT) {
b_default = DataBindingUtil.inflate(inflater, R.layout.fragment_agenda_default, container, false); b_default = DataBindingUtil.inflate(inflater, R.layout.fragment_agenda_default, container, false);
return b_default.getRoot(); return b_default.getRoot();
@ -102,7 +104,7 @@ public class AgendaFragment extends Fragment {
new BottomSheetPrimaryItem(true) new BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_add_event) .withTitle(R.string.menu_add_event)
.withDescription(R.string.menu_add_event_desc) .withDescription(R.string.menu_add_event_desc)
.withIcon(CommunityMaterial.Icon.cmd_calendar_plus) .withIcon(SzkolnyFont.Icon.szf_calendar_plus_outline)
.withOnClickListener(v3 -> { .withOnClickListener(v3 -> {
activity.getBottomSheet().close(); activity.getBottomSheet().close();
new MaterialDialog.Builder(activity) new MaterialDialog.Builder(activity)
@ -122,18 +124,17 @@ public class AgendaFragment extends Fragment {
}), }),
new BottomSheetPrimaryItem(true) new BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_agenda_change_view) .withTitle(R.string.menu_agenda_change_view)
.withIcon(viewType == AGENDA_DEFAULT ? CommunityMaterial.Icon.cmd_calendar : CommunityMaterial.Icon2.cmd_view_list) .withIcon(viewType == AGENDA_DEFAULT ? CommunityMaterial.Icon.cmd_calendar_outline : CommunityMaterial.Icon.cmd_format_list_bulleted_square)
.withOnClickListener(v3 -> { .withOnClickListener(v3 -> {
activity.getBottomSheet().close(); activity.getBottomSheet().close();
viewType = viewType == AGENDA_DEFAULT ? AGENDA_CALENDAR : AGENDA_DEFAULT; viewType = viewType == AGENDA_DEFAULT ? AGENDA_CALENDAR : AGENDA_DEFAULT;
app.profile.setAgendaViewType(viewType); app.config.getUi().setAgendaViewType(viewType);
app.profileSaveAsync();
activity.reloadTarget(); activity.reloadTarget();
}), }),
new BottomSheetSeparatorItem(true), new BottomSheetSeparatorItem(true),
new BottomSheetPrimaryItem(true) new BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_mark_as_read) .withTitle(R.string.menu_mark_as_read)
.withIcon(CommunityMaterial.Icon.cmd_eye_check) .withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
.withOnClickListener(v3 -> { .withOnClickListener(v3 -> {
activity.getBottomSheet().close(); activity.getBottomSheet().close();
AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_EVENT, true)); AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_EVENT, true));
@ -309,7 +310,7 @@ public class AgendaFragment extends Fragment {
int scrolledDate = Date.fromCalendar(calendar).getValue(); int scrolledDate = Date.fromCalendar(calendar).getValue();
if (unreadEventDates.contains(scrolledDate)) { if (unreadEventDates.contains(scrolledDate)) {
AsyncTask.execute(() -> app.db.eventDao().setSeenByDate(App.profileId, Date.fromYmd(intToStr(scrolledDate)), true)); AsyncTask.execute(() -> app.db.eventDao().setSeenByDate(App.profileId, Date.fromYmd(intToStr(scrolledDate)), true));
unreadEventDates.remove(unreadEventDates.indexOf(scrolledDate)); unreadEventDates.remove((Integer) scrolledDate);
} }
} }
@ -317,9 +318,23 @@ public class AgendaFragment extends Fragment {
public void onEventSelected(CalendarEvent calendarEvent) { public void onEventSelected(CalendarEvent calendarEvent) {
if (calendarEvent instanceof BaseCalendarEvent) { if (calendarEvent instanceof BaseCalendarEvent) {
if (!calendarEvent.isPlaceholder() && !calendarEvent.isAllDay()) { if (!calendarEvent.isPlaceholder() && !calendarEvent.isAllDay()) {
new EventListDialog(activity).show(app, Date.fromCalendar(calendarEvent.getInstanceDay()), Time.fromMillis(calendarEvent.getStartTime().getTimeInMillis()), true); // new EventListDialogOld(activity).show(app, Date.fromCalendar(calendarEvent.getInstanceDay()), Time.fromMillis(calendarEvent.getStartTime().getTimeInMillis()), true);
new EventListDialog(
activity,
App.profileId,
Date.fromCalendar(calendarEvent.getInstanceDay()),
Time.fromMillis(calendarEvent.getStartTime().getTimeInMillis()),
null,
null);
} else { } else {
new EventListDialog(activity).show(app, Date.fromCalendar(calendarEvent.getInstanceDay())); // new EventListDialogOld(activity).show(app, Date.fromCalendar(calendarEvent.getInstanceDay()));
new EventListDialog(
activity,
App.profileId,
Date.fromCalendar(calendarEvent.getInstanceDay()),
null,
null,
null);
} }
} else if (calendarEvent instanceof LessonChangeEvent) { } else if (calendarEvent instanceof LessonChangeEvent) {
new LessonChangeDialog(activity).show(app, Date.fromCalendar(calendarEvent.getInstanceDay())); new LessonChangeDialog(activity).show(app, Date.fromCalendar(calendarEvent.getInstanceDay()));
@ -401,10 +416,18 @@ public class AgendaFragment extends Fragment {
int scrolledDate = dayDate.getValue(); int scrolledDate = dayDate.getValue();
if (unreadEventDates.contains(scrolledDate)) { if (unreadEventDates.contains(scrolledDate)) {
AsyncTask.execute(() -> app.db.eventDao().setSeenByDate(App.profileId, Date.fromYmd(intToStr(scrolledDate)), true)); AsyncTask.execute(() -> app.db.eventDao().setSeenByDate(App.profileId, Date.fromYmd(intToStr(scrolledDate)), true));
unreadEventDates.remove(unreadEventDates.indexOf(scrolledDate)); unreadEventDates.remove((Integer) scrolledDate);
} }
new EventListDialog(getContext()).show(app, dayDate); // new EventListDialogOld(getContext()).show(app, dayDate);
new EventListDialog(
activity,
App.profileId,
dayDate,
null,
null,
null
);
}); });
b_calendar.progressBar.setVisibility(View.GONE); b_calendar.progressBar.setVisibility(View.GONE);
}); });

View File

@ -58,7 +58,7 @@ public class AnnouncementsFragment extends Fragment {
activity.getBottomSheet().prependItems( activity.getBottomSheet().prependItems(
new BottomSheetPrimaryItem(true) new BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_mark_as_read) .withTitle(R.string.menu_mark_as_read)
.withIcon(CommunityMaterial.Icon.cmd_eye_check) .withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
.withOnClickListener(v3 -> { .withOnClickListener(v3 -> {
activity.getBottomSheet().close(); activity.getBottomSheet().close();
AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_ANNOUNCEMENT, true)); AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_ANNOUNCEMENT, true));

View File

@ -95,7 +95,7 @@ public class AttendanceFragment extends Fragment {
activity.getBottomSheet().prependItems( activity.getBottomSheet().prependItems(
new BottomSheetPrimaryItem(true) new BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_mark_as_read) .withTitle(R.string.menu_mark_as_read)
.withIcon(CommunityMaterial.Icon.cmd_eye_check) .withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
.withOnClickListener(v3 -> { .withOnClickListener(v3 -> {
activity.getBottomSheet().close(); activity.getBottomSheet().close();
AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_ATTENDANCE, true)); AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_ATTENDANCE, true));
@ -133,7 +133,7 @@ public class AttendanceFragment extends Fragment {
CafeBar.builder(activity) CafeBar.builder(activity)
.to(activity.getNavView().getCoordinator()) .to(activity.getNavView().getCoordinator())
.content(R.string.sync_old_data_info) .content(R.string.sync_old_data_info)
.icon(new IconicsDrawable(activity).icon(CommunityMaterial.Icon2.cmd_sync).size(IconicsSize.dp(20)).color(IconicsColor.colorInt(Themes.INSTANCE.getPrimaryTextColor(activity)))) .icon(new IconicsDrawable(activity).icon(CommunityMaterial.Icon.cmd_download_outline).size(IconicsSize.dp(20)).color(IconicsColor.colorInt(Themes.INSTANCE.getPrimaryTextColor(activity))))
.positiveText(R.string.refresh) .positiveText(R.string.refresh)
.positiveColor(0xff4caf50) .positiveColor(0xff4caf50)
.negativeText(R.string.ok) .negativeText(R.string.ok)

View File

@ -69,7 +69,7 @@ public class BehaviourFragment extends Fragment {
activity.getBottomSheet().prependItems( activity.getBottomSheet().prependItems(
new BottomSheetPrimaryItem(true) new BottomSheetPrimaryItem(true)
.withTitle(R.string.menu_mark_as_read) .withTitle(R.string.menu_mark_as_read)
.withIcon(CommunityMaterial.Icon.cmd_eye_check) .withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
.withOnClickListener(v3 -> { .withOnClickListener(v3 -> {
activity.getBottomSheet().close(); activity.getBottomSheet().close();
AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_NOTICE, true)); AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_NOTICE, true));

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