mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-06-25 19:45:32 +02:00
Compare commits
35 Commits
v4.0-beta.
...
v4.0-beta.
Author | SHA1 | Date | |
---|---|---|---|
d8a8bed68d | |||
eedbd954bd | |||
0eb8366027 | |||
894135104b | |||
7b2e408efc | |||
e4115c122e | |||
537b16949e | |||
ca60ceb2a7 | |||
0fad12fea5 | |||
6cd2c23aac | |||
512baaa43f | |||
d097fcc973 | |||
621dbd459c | |||
840ab4b0c4 | |||
904be34a87 | |||
b7fc6fcc38 | |||
55c6e40d6d | |||
4dfb015057 | |||
e40a0ba2bb | |||
fd48f10df9 | |||
6a54e7fef7 | |||
5c4d6ed140 | |||
9ed1be3594 | |||
c5ce582678 | |||
2050083bce | |||
92e6bdb562 | |||
93e70c38b7 | |||
45b96179a5 | |||
a29a534a40 | |||
8e2297359c | |||
92ba7248ef | |||
f657d37cbd | |||
9e312f60bf | |||
85f72b78f7 | |||
40acb67ceb |
@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
|
||||
//apply plugin: 'me.tatarka.retrolambda'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
compileSdkVersion setup.compileSdk
|
||||
|
||||
android {
|
||||
lintOptions {
|
||||
@ -12,7 +12,7 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
targetSdkVersion setup.targetSdk
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
@ -43,9 +43,9 @@ android {
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
// Google libraries
|
||||
implementation "androidx.appcompat:appcompat:${androidXAppCompat}"
|
||||
implementation "androidx.recyclerview:recyclerview:${androidXRecyclerView}"
|
||||
implementation "com.google.android.material:material:${googleMaterial}"
|
||||
implementation "androidx.appcompat:appcompat:${versions.appcompat}"
|
||||
implementation "androidx.recyclerview:recyclerview:${versions.recyclerView}"
|
||||
implementation "com.google.android.material:material:${versions.material}"
|
||||
|
||||
// other libraries
|
||||
//implementation 'se.emilsjolander:stickylistheaders:2.7.0'
|
||||
|
@ -1,13 +1,14 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
apply plugin: 'io.fabric'
|
||||
|
||||
android {
|
||||
signingConfigs {
|
||||
}
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
compileSdkVersion setup.compileSdk
|
||||
defaultConfig {
|
||||
applicationId 'pl.szczodrzynski.edziennik'
|
||||
minSdkVersion setup.minSdk
|
||||
@ -103,7 +104,7 @@ tasks.whenTaskAdded { task ->
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
|
||||
annotationProcessor "androidx.room:room-compiler:${versions.room}"
|
||||
kapt "androidx.room:room-compiler:${versions.room}"
|
||||
debugImplementation "com.amitshekhar.android:debug-db:1.0.5"
|
||||
|
||||
implementation "android.arch.navigation:navigation-fragment-ktx:${versions.navigationFragment}"
|
||||
|
@ -28,7 +28,7 @@
|
||||
-keepclassmembers class pl.szczodrzynski.edziennik.ui.widgets.WidgetConfig { public *; }
|
||||
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.timetable.WidgetTimetableProvider
|
||||
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.notifications.WidgetNotificationsProvider
|
||||
-keepnames class pl.szczodrzynski.edziennik.widgets.luckynumber.WidgetLuckyNumber
|
||||
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.luckynumber.WidgetLuckyNumberProvider
|
||||
|
||||
-keep class .R
|
||||
-keep class **.R$* {
|
||||
|
@ -80,6 +80,7 @@
|
||||
<service android:name=".ui.widgets.timetable.WidgetTimetableService"
|
||||
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||
<activity android:name=".ui.widgets.LessonDialogActivity"
|
||||
android:label=""
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:excludeFromRecents="true"
|
||||
android:noHistory="true"
|
||||
@ -141,12 +142,8 @@
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme" />
|
||||
<activity android:name=".ui.modules.settings.SettingsLicenseActivity"
|
||||
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:theme="@style/AppTheme" />
|
||||
<activity android:name=".ui.modules.webpush.WebPushConfigActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:theme="@style/AppTheme.Dark" />
|
||||
<activity android:name=".ui.modules.webpush.QrScannerActivity" />
|
||||
<activity android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
@ -165,18 +162,9 @@
|
||||
<action android:name="android.intent.action.USER_PRESENT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver android:name=".receivers.BootReceiver">
|
||||
<receiver android:name=".sync.UpdateDownloaderService$DownloadProgressReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
|
||||
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver android:name=".sync.FirebaseBroadcastReceiver"
|
||||
android:exported="true"
|
||||
android:permission="com.google.android.c2dm.permission.SEND">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver android:name=".receivers.SzkolnyReceiver"
|
||||
@ -193,16 +181,22 @@
|
||||
____) | __/ | \ V /| | (_| __/\__ \
|
||||
|_____/ \___|_| \_/ |_|\___\___||___/
|
||||
-->
|
||||
<service android:name=".sync.MyFirebaseMessagingService"
|
||||
<!--<service android:name=".sync.MyFirebaseMessagingService"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<service android:name=".receivers.BootReceiver$NotificationActionService" />
|
||||
<service android:name=".Notifier$GetDataRetryService" />
|
||||
</service>-->
|
||||
<service android:name=".data.api.ApiService" />
|
||||
<service android:name=".data.firebase.MyFirebaseService"
|
||||
android:exported="false">
|
||||
<intent-filter android:priority="10000000">
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<service android:name=".sync.UpdateDownloaderService" />
|
||||
|
||||
<!--
|
||||
_____ _ _
|
||||
|
@ -1,4 +1,4 @@
|
||||
<h3>Wersja 4.0-beta.3, 2020-01-10</h3>
|
||||
<h3>Wersja 4.0-beta.4, 2020-01-19</h3>
|
||||
<ul>
|
||||
<li><b>Przebudowaliśmy cały moduł synchronizacji</b>, co oznacza większą stabilność aplikacji, szybkosć oraz poprawność pobieranych danych.</li>
|
||||
<li><b><u>Wysyłanie wiadomości</u></b> - funkcja, na którą czekał każdy. Od teraz w Szkolnym można wysyłać oraz odpowiadać na wiadomości do nauczycieli 👏</li>
|
||||
@ -6,6 +6,7 @@
|
||||
<li>Nowa <b>Strona główna</b> - ładniejszy wygląd oraz możliwość przestawiania kart na każdym profilu</li>
|
||||
<li>Nowy <b>Plan lekcji</b> - z doskonałą obsługą lekcji przesuniętych oraz dwóch lekcji o tej samej godzinie</li>
|
||||
<li>Nowe okienka informacji o wydarzeniach oraz lekcjach</li>
|
||||
<li>Nowe, przyjemniejsze powiadomienia</li>
|
||||
<li>Łatwiejsze dodawanie własnych wydarzeń</li>
|
||||
<li>Dużo poprawek w widoku <b>Wiadomości</b> oraz <b>Ogłoszeń</b></li>
|
||||
<li>Częściowa <b>Obsługa dziennika EduDziennik</b></li>
|
||||
@ -14,7 +15,9 @@
|
||||
<li>Librus: obsługa Zadań domowych bez posiadania Mobilnych dodatków (przez system Synergia)</li>
|
||||
<li>Lepsze <b>przekazywanie powiadomień na komputer</b> oraz łatwiejsze parowanie</li>
|
||||
<li>Poprawiliśmy synchronizację w tle na niektórych telefonach</li>
|
||||
<li>Usunąłem denerwujący brak zaznaczenia w lewym menu</li>
|
||||
<li>Znaczna ilość błędów z poprzednich wersji już nie występuje</li>
|
||||
<li><strike>Występują natomiast nowe błędy, dlatego proszę o ich zgłaszanie :)</strike></li>
|
||||
</ul>
|
||||
<br>
|
||||
<br>
|
||||
@ -24,6 +27,7 @@ Staramy się usuwać takie przypadki, jednak na chwilę obecną mogą występowa
|
||||
<ul>
|
||||
<li>Wysyłanie wiadomości może czasami nie działać - proszę o zgłaszanie wszystkich błędów na naszym serwerze Discord</li>
|
||||
<li>Terminarz - brak informacji o odwołanych lekcjach w dialogu</li>
|
||||
<li>Cisza nocna w powiadomieniach jeszcze nie działa.</li>
|
||||
</ul>
|
||||
<br>
|
||||
<br>
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
/*secret password - removed for source code publication*/
|
||||
static toys AES_IV[16] = {
|
||||
0x1e, 0xef, 0x4e, 0xc6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
0x90, 0xad, 0xe8, 0xea, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
|
||||
unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat);
|
||||
|
||||
|
@ -1,714 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.pm.ShortcutManager;
|
||||
import android.content.pm.Signature;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.provider.Settings;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.work.Configuration;
|
||||
|
||||
import com.chuckerteam.chucker.api.ChuckerCollector;
|
||||
import com.chuckerteam.chucker.api.ChuckerInterceptor;
|
||||
import com.chuckerteam.chucker.api.RetentionManager;
|
||||
import com.google.android.gms.security.ProviderInstaller;
|
||||
import com.google.firebase.FirebaseApp;
|
||||
import com.google.firebase.FirebaseOptions;
|
||||
import com.google.firebase.iid.FirebaseInstanceId;
|
||||
import com.google.firebase.messaging.FirebaseMessaging;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.hypertrack.hyperlog.HyperLog;
|
||||
import com.mikepenz.iconics.Iconics;
|
||||
import com.mikepenz.iconics.IconicsColor;
|
||||
import com.mikepenz.iconics.IconicsDrawable;
|
||||
import com.mikepenz.iconics.IconicsSize;
|
||||
import com.mikepenz.iconics.typeface.IIcon;
|
||||
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.security.KeyStore;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import cat.ereza.customactivityoncrash.config.CaocConfig;
|
||||
import im.wangchao.mhttp.MHttp;
|
||||
import im.wangchao.mhttp.internal.cookie.PersistentCookieJar;
|
||||
import im.wangchao.mhttp.internal.cookie.cache.SetCookieCache;
|
||||
import im.wangchao.mhttp.internal.cookie.persistence.SharedPrefsCookiePersistor;
|
||||
import me.leolin.shortcutbadger.ShortcutBadger;
|
||||
import okhttp3.ConnectionSpec;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.TlsVersion;
|
||||
import pl.szczodrzynski.edziennik.config.Config;
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing;
|
||||
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask;
|
||||
import pl.szczodrzynski.edziennik.data.db.AppDb;
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.DebugLog;
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile;
|
||||
import pl.szczodrzynski.edziennik.network.NetworkUtils;
|
||||
import pl.szczodrzynski.edziennik.network.TLSSocketFactory;
|
||||
import pl.szczodrzynski.edziennik.sync.SyncWorker;
|
||||
import pl.szczodrzynski.edziennik.ui.modules.base.CrashActivity;
|
||||
import pl.szczodrzynski.edziennik.utils.DebugLogFormat;
|
||||
import pl.szczodrzynski.edziennik.utils.PermissionChecker;
|
||||
import pl.szczodrzynski.edziennik.utils.Themes;
|
||||
import pl.szczodrzynski.edziennik.utils.Utils;
|
||||
import pl.szczodrzynski.edziennik.utils.models.AppConfig;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.data.db.entity.LoginStore.LOGIN_TYPE_MOBIDZIENNIK;
|
||||
|
||||
public class App extends androidx.multidex.MultiDexApplication implements Configuration.Provider {
|
||||
private static final String TAG = "App";
|
||||
public static int profileId = -1;
|
||||
private Context mContext;
|
||||
|
||||
@Override
|
||||
public Configuration getWorkManagerConfiguration() {
|
||||
return new Configuration.Builder()
|
||||
.setMinimumLoggingLevel(Log.VERBOSE)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
public static final int REQUEST_TIMEOUT = 10 * 1000;
|
||||
|
||||
// notifications
|
||||
//public NotificationManager mNotificationManager;
|
||||
//public final String NOTIFICATION_CHANNEL_ID_UPDATES = "4566";
|
||||
//public String NOTIFICATION_CHANNEL_NAME_UPDATES;
|
||||
public Notifier notifier;
|
||||
|
||||
public static final String APP_URL = "://edziennik.szczodrzynski.pl/app/";
|
||||
|
||||
public ShortcutManager shortcutManager;
|
||||
|
||||
public PermissionChecker permissionChecker;
|
||||
|
||||
public String signature = "";
|
||||
public String deviceId = "";
|
||||
|
||||
public AppDb db;
|
||||
public void debugLog(String text) {
|
||||
if (!devMode)
|
||||
return;
|
||||
db.debugLogDao().add(new DebugLog(Utils.getCurrentTimeUsingCalendar()+": "+text));
|
||||
}
|
||||
public void debugLogAsync(String text) {
|
||||
if (!devMode)
|
||||
return;
|
||||
AsyncTask.execute(() -> {
|
||||
db.debugLogDao().add(new DebugLog(Utils.getCurrentTimeUsingCalendar()+": "+text));
|
||||
});
|
||||
}
|
||||
|
||||
// network & APIs
|
||||
public NetworkUtils networkUtils;
|
||||
public PersistentCookieJar cookieJar;
|
||||
public OkHttpClient http;
|
||||
public OkHttpClient httpLazy;
|
||||
|
||||
public SharedPreferences appSharedPrefs; // sharedPreferences for APPCONFIG + JOBS STORE
|
||||
public AppConfig appConfig; // APPCONFIG: common for all profiles
|
||||
//public AppProfile profile; // current profile
|
||||
public SharedPreferences registerStore; // sharedPreferences for REGISTER
|
||||
//public Register register; // REGISTER for current profile, read from registerStore
|
||||
|
||||
public Profile profile;
|
||||
public Config config;
|
||||
private static Config mConfig;
|
||||
public static Config getConfig() {
|
||||
return mConfig;
|
||||
}
|
||||
|
||||
// other stuff
|
||||
public Gson gson;
|
||||
public String requestScheme = "https";
|
||||
public boolean unreadBadgesAvailable = true;
|
||||
|
||||
public static boolean devMode = false;
|
||||
|
||||
public static final boolean UPDATES_ON_PLAY_STORE = true;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public Icon getDesktopIconFromIconics(IIcon icon) {
|
||||
final IconicsDrawable drawable = new IconicsDrawable(mContext, icon)
|
||||
.color(IconicsColor.colorInt(Color.WHITE))
|
||||
.size(IconicsSize.dp(48))
|
||||
.padding(IconicsSize.dp(8))
|
||||
.backgroundColor(IconicsColor.colorRes(R.color.colorPrimaryDark))
|
||||
.roundedCorners(IconicsSize.dp(10));
|
||||
//drawable.setStyle(Paint.Style.FILL);
|
||||
final Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
||||
final Canvas canvas = new Canvas(bitmap);
|
||||
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
drawable.draw(canvas);
|
||||
|
||||
return Icon.createWithBitmap(bitmap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
|
||||
CaocConfig.Builder.create()
|
||||
.backgroundMode(CaocConfig.BACKGROUND_MODE_SHOW_CUSTOM) //default: CaocConfig.BACKGROUND_MODE_SHOW_CUSTOM
|
||||
.enabled(true) //default: true
|
||||
.showErrorDetails(true) //default: true
|
||||
.showRestartButton(true) //default: true
|
||||
.logErrorOnRestart(true) //default: true
|
||||
.trackActivities(true) //default: false
|
||||
.minTimeBetweenCrashesMs(2000) //default: 3000
|
||||
.errorDrawable(R.drawable.ic_rip) //default: bug image
|
||||
.restartActivity(MainActivity.class) //default: null (your app's launch activity)
|
||||
.errorActivity(CrashActivity.class) //default: null (default error activity)
|
||||
//.eventListener(new YourCustomEventListener()) //default: null
|
||||
.apply();
|
||||
mContext = this;
|
||||
db = AppDb.getDatabase(this);
|
||||
gson = new Gson();
|
||||
networkUtils = new NetworkUtils(this);
|
||||
|
||||
config = new Config(db);
|
||||
config.migrate(this);
|
||||
mConfig = config;
|
||||
|
||||
Iconics.init(getApplicationContext());
|
||||
Iconics.registerFont(SzkolnyFont.INSTANCE);
|
||||
|
||||
notifier = new Notifier(this);
|
||||
permissionChecker = new PermissionChecker(mContext);
|
||||
|
||||
deviceId = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
|
||||
|
||||
cookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(this));
|
||||
|
||||
appSharedPrefs = getSharedPreferences(getString(R.string.preference_file_global), Context.MODE_PRIVATE);
|
||||
|
||||
loadConfig();
|
||||
|
||||
Signing.INSTANCE.getCert(this);
|
||||
|
||||
Themes.INSTANCE.setThemeInt(config.getUi().getTheme());
|
||||
|
||||
try {
|
||||
PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);
|
||||
for (Signature signature: packageInfo.signatures) {
|
||||
byte[] signatureBytes = signature.toByteArray();
|
||||
MessageDigest md = MessageDigest.getInstance("SHA");
|
||||
md.update(signatureBytes);
|
||||
this.signature = Base64.encodeToString(md.digest(), Base64.NO_WRAP);
|
||||
//Log.d(TAG, "Signature is "+this.signature);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if ("f054761fbdb6a238".equals(deviceId) || BuildConfig.DEBUG) {
|
||||
devMode = true;
|
||||
}
|
||||
else if (config.getDevModePassword() != null) {
|
||||
checkDevModePassword();
|
||||
}
|
||||
|
||||
OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder()
|
||||
.cache(null)
|
||||
.followRedirects(true)
|
||||
.followSslRedirects(true)
|
||||
.retryOnConnectionFailure(true)
|
||||
.cookieJar(cookieJar)
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
.writeTimeout(20, TimeUnit.SECONDS)
|
||||
.readTimeout(40, TimeUnit.SECONDS);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
try {
|
||||
try {
|
||||
ProviderInstaller.installIfNeeded(this);
|
||||
} catch (Exception e) {
|
||||
Log.e("OkHttpTLSCompat", "Play Services not found or outdated");
|
||||
X509TrustManager x509TrustManager = null;
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init((KeyStore) null);
|
||||
for (TrustManager trustManager: trustManagerFactory.getTrustManagers()) {
|
||||
if (trustManager instanceof X509TrustManager)
|
||||
x509TrustManager = (X509TrustManager) trustManager;
|
||||
}
|
||||
|
||||
SSLContext sc = SSLContext.getInstance("TLSv1.2");
|
||||
sc.init(null, null, null);
|
||||
httpBuilder.sslSocketFactory(new TLSSocketFactory(sc.getSocketFactory()), x509TrustManager);
|
||||
|
||||
ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
|
||||
.tlsVersions(TlsVersion.TLS_1_0)
|
||||
.tlsVersions(TlsVersion.TLS_1_1)
|
||||
.tlsVersions(TlsVersion.TLS_1_2)
|
||||
.build();
|
||||
|
||||
List<ConnectionSpec> specs = new ArrayList<>();
|
||||
specs.add(cs);
|
||||
specs.add(ConnectionSpec.COMPATIBLE_TLS);
|
||||
specs.add(ConnectionSpec.CLEARTEXT);
|
||||
|
||||
httpBuilder.connectionSpecs(specs);
|
||||
}
|
||||
|
||||
|
||||
} catch (Exception exc) {
|
||||
Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc);
|
||||
}
|
||||
}
|
||||
|
||||
if (App.devMode || BuildConfig.DEBUG) {
|
||||
HyperLog.initialize(this);
|
||||
HyperLog.setLogLevel(Log.VERBOSE);
|
||||
HyperLog.setLogFormat(new DebugLogFormat(this));
|
||||
|
||||
ChuckerCollector chuckerCollector = new ChuckerCollector(this, true, RetentionManager.Period.ONE_HOUR);
|
||||
ChuckerInterceptor chuckerInterceptor = new ChuckerInterceptor(this, chuckerCollector);
|
||||
httpBuilder.addInterceptor(chuckerInterceptor);
|
||||
}
|
||||
|
||||
http = httpBuilder.build();
|
||||
httpLazy = http.newBuilder().followRedirects(false).followSslRedirects(false).build();
|
||||
|
||||
MHttp.instance()
|
||||
.customOkHttpClient(http);
|
||||
|
||||
//register = new Register(mContext);
|
||||
|
||||
//profileLoadById(appSharedPrefs.getInt("current_profile_id", 1));
|
||||
|
||||
if (config.getSync().getEnabled()) {
|
||||
SyncWorker.Companion.scheduleNext(this, false);
|
||||
}
|
||||
else {
|
||||
SyncWorker.Companion.cancelNext(this);
|
||||
}
|
||||
|
||||
db.metadataDao().countUnseen().observeForever(count -> {
|
||||
Log.d("MainActivity", "Overall unseen count changed");
|
||||
assert count != null;
|
||||
if (unreadBadgesAvailable) {
|
||||
unreadBadgesAvailable = ShortcutBadger.applyCount(this, count);
|
||||
}
|
||||
});
|
||||
|
||||
//new IonCookieManager(mContext);
|
||||
|
||||
new Handler().post(() -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
||||
shortcutManager = getSystemService(ShortcutManager.class);
|
||||
|
||||
ShortcutInfo shortcutTimetable = new ShortcutInfo.Builder(mContext, "item_timetable")
|
||||
.setShortLabel(getString(R.string.shortcut_timetable)).setLongLabel(getString(R.string.shortcut_timetable))
|
||||
.setIcon(Icon.createWithResource(this, R.mipmap.ic_shortcut_timetable))
|
||||
//.setIcon(getDesktopIconFromIconics(CommunityMaterial.Icon2.cmd_timetable))
|
||||
.setIntent(new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_TIMETABLE))
|
||||
.build();
|
||||
|
||||
ShortcutInfo shortcutAgenda = new ShortcutInfo.Builder(mContext, "item_agenda")
|
||||
.setShortLabel(getString(R.string.shortcut_agenda)).setLongLabel(getString(R.string.shortcut_agenda))
|
||||
.setIcon(Icon.createWithResource(this, R.mipmap.ic_shortcut_agenda))
|
||||
//.setIcon(getDesktopIconFromIconics(CommunityMaterial.Icon.cmd_calendar))
|
||||
.setIntent(new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_AGENDA))
|
||||
.build();
|
||||
|
||||
ShortcutInfo shortcutGrades = new ShortcutInfo.Builder(mContext, "item_grades")
|
||||
.setShortLabel(getString(R.string.shortcut_grades)).setLongLabel(getString(R.string.shortcut_grades))
|
||||
.setIcon(Icon.createWithResource(this, R.mipmap.ic_shortcut_grades))
|
||||
//.setIcon(getDesktopIconFromIconics(CommunityMaterial.Icon2.cmd_numeric_5_box))
|
||||
.setIntent(new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_GRADES))
|
||||
.build();
|
||||
|
||||
ShortcutInfo shortcutHomework = new ShortcutInfo.Builder(mContext, "item_homeworks")
|
||||
.setShortLabel(getString(R.string.shortcut_homework)).setLongLabel(getString(R.string.shortcut_homework))
|
||||
.setIcon(Icon.createWithResource(this, R.mipmap.ic_shortcut_homework))
|
||||
//.setIcon(getDesktopIconFromIconics(SzkolnyFont.Icon.szf_file_document_edit))
|
||||
.setIntent(new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_HOMEWORK))
|
||||
.build();
|
||||
|
||||
ShortcutInfo shortcutMessages = new ShortcutInfo.Builder(mContext, "item_messages")
|
||||
.setShortLabel(getString(R.string.shortcut_messages)).setLongLabel(getString(R.string.shortcut_messages))
|
||||
.setIcon(Icon.createWithResource(this, R.mipmap.ic_shortcut_messages))
|
||||
//.setIcon(getDesktopIconFromIconics(CommunityMaterial.Icon.cmd_email))
|
||||
.setIntent(new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_MESSAGES ))
|
||||
.build();
|
||||
|
||||
shortcutManager.setDynamicShortcuts(Arrays.asList(shortcutTimetable, shortcutAgenda, shortcutGrades, shortcutHomework, shortcutMessages));
|
||||
}
|
||||
|
||||
if (config.getAppInstalledTime() == 0) {
|
||||
try {
|
||||
config.setAppInstalledTime(getPackageManager().getPackageInfo(getPackageName(), 0).firstInstallTime);
|
||||
config.setAppRateSnackbarTime(config.getAppInstalledTime() + 7 * 24 * 60 * 60 * 1000);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/*Task<CapabilityInfo> capabilityInfoTask =
|
||||
Wearable.getCapabilityClient(this)
|
||||
.getCapability("edziennik_wear_app", CapabilityClient.FILTER_REACHABLE);
|
||||
capabilityInfoTask.addOnCompleteListener((task) -> {
|
||||
if (task.isSuccessful()) {
|
||||
CapabilityInfo capabilityInfo = task.getResult();
|
||||
assert capabilityInfo != null;
|
||||
Set<Node> nodes;
|
||||
nodes = capabilityInfo.getNodes();
|
||||
Log.d(TAG, "Nodes "+nodes);
|
||||
|
||||
if (nodes.size() > 0) {
|
||||
Wearable.getMessageClient(this).sendMessage(
|
||||
nodes.toArray(new Node[]{})[0].getId(), "/ping", "Hello world".getBytes());
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "Capability request failed to return any results.");
|
||||
}
|
||||
});
|
||||
|
||||
Wearable.getDataClient(this).addListener(dataEventBuffer -> {
|
||||
Log.d(TAG, "onDataChanged(): " + dataEventBuffer);
|
||||
|
||||
for (DataEvent event : dataEventBuffer) {
|
||||
if (event.getType() == DataEvent.TYPE_CHANGED) {
|
||||
String path = event.getDataItem().getUri().getPath();
|
||||
Log.d(TAG, "Data "+path+ " :: "+Arrays.toString(event.getDataItem().getData()));
|
||||
}
|
||||
}
|
||||
});*/
|
||||
|
||||
FirebaseApp pushMobidziennikApp = FirebaseApp.initializeApp(
|
||||
this,
|
||||
new FirebaseOptions.Builder()
|
||||
.setApiKey("AIzaSyCi5LmsZ5BBCQnGtrdvWnp1bWLCNP8OWQE")
|
||||
.setApplicationId("1:747285019373:android:f6341bf7b158621d")
|
||||
.build(),
|
||||
"Mobidziennik2"
|
||||
);
|
||||
|
||||
FirebaseApp pushLibrusApp = FirebaseApp.initializeApp(
|
||||
this,
|
||||
new FirebaseOptions.Builder()
|
||||
.setApiKey("AIzaSyDfTuEoYPKdv4aceEws1CO3n0-HvTndz-o")
|
||||
.setApplicationId("1:513056078587:android:1e29083b760af544")
|
||||
.build(),
|
||||
"Librus"
|
||||
);
|
||||
|
||||
FirebaseApp pushVulcanApp = FirebaseApp.initializeApp(
|
||||
this,
|
||||
new FirebaseOptions.Builder()
|
||||
.setApiKey("AIzaSyDW8MUtanHy64_I0oCpY6cOxB3jrvJd_iA")
|
||||
.setApplicationId("1:987828170337:android:ac97431a0a4578c3")
|
||||
.build(),
|
||||
"Vulcan"
|
||||
);
|
||||
|
||||
if (config.getRunSync()) {
|
||||
config.setRunSync(false);
|
||||
EdziennikTask.Companion.sync().enqueue(this);
|
||||
}
|
||||
|
||||
try {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(instanceIdResult -> {
|
||||
Log.d(TAG, "Token for App is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId()+". Time is "+(System.currentTimeMillis() - startTime));
|
||||
config.getSync().setTokenApp(instanceIdResult.getToken());
|
||||
});
|
||||
/*FirebaseInstanceId.getInstance(pushMobidziennikApp).getInstanceId().addOnSuccessListener(instanceIdResult -> {
|
||||
Log.d(TAG, "Token for Mobidziennik is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId());
|
||||
appConfig.fcmTokens.put(LOGIN_TYPE_MOBIDZIENNIK, new Pair<>(instanceIdResult.getToken(), new ArrayList<>()));
|
||||
});
|
||||
FirebaseInstanceId.getInstance(pushLibrusApp).getInstanceId().addOnSuccessListener(instanceIdResult -> {
|
||||
Log.d(TAG, "Token for Librus is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId());
|
||||
appConfig.fcmTokens.put(LOGIN_TYPE_LIBRUS, new Pair<>(instanceIdResult.getToken(), new ArrayList<>()));
|
||||
});
|
||||
FirebaseInstanceId.getInstance(pushVulcanApp).getInstanceId().addOnSuccessListener(instanceIdResult -> {
|
||||
Log.d(TAG, "Token for Vulcan is " + instanceIdResult.getToken() + ", ID is " + instanceIdResult.getId());
|
||||
Pair<String, List<Integer>> pair = appConfig.fcmTokens.get(LOGIN_TYPE_VULCAN);
|
||||
if (pair == null || pair.first == null || !pair.first.equals(instanceIdResult.getToken())) {
|
||||
appConfig.fcmTokens.put(LOGIN_TYPE_VULCAN, new Pair<>(instanceIdResult.getToken(), new ArrayList<>()));
|
||||
}
|
||||
});*/
|
||||
|
||||
|
||||
FirebaseMessaging.getInstance().subscribeToTopic(getPackageName());
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void loadConfig()
|
||||
{
|
||||
appConfig = new AppConfig(this);
|
||||
|
||||
|
||||
if (appSharedPrefs.contains("config")) {
|
||||
// remove old-format config, save the new one and empty the incorrectly-nulled config
|
||||
appConfig = gson.fromJson(appSharedPrefs.getString("config", ""), AppConfig.class);
|
||||
appSharedPrefs.edit().remove("config").apply();
|
||||
saveConfig();
|
||||
appConfig = new AppConfig(this);
|
||||
}
|
||||
|
||||
if (appSharedPrefs.contains("profiles")) {
|
||||
SharedPreferences.Editor appSharedPrefsEditor = appSharedPrefs.edit();
|
||||
/*List<Integer> appProfileIds = gson.fromJson(appSharedPrefs.getString("profiles", ""), new TypeToken<List<Integer>>(){}.getType());
|
||||
for (int id: appProfileIds) {
|
||||
AppProfile appProfile = gson.fromJson(appSharedPrefs.getString("profile"+id, ""), AppProfile.class);
|
||||
if (appProfile != null) {
|
||||
appConfig.profiles.add(appProfile);
|
||||
}
|
||||
appSharedPrefsEditor.remove("profile"+id);
|
||||
}*/
|
||||
appSharedPrefsEditor.remove("profiles");
|
||||
appSharedPrefsEditor.apply();
|
||||
//profilesSave();
|
||||
}
|
||||
|
||||
|
||||
Map<String,?> keys = appSharedPrefs.getAll();
|
||||
for (Map.Entry<String,?> entry : keys.entrySet()) {
|
||||
if (entry.getKey().startsWith("app.appConfig.")) {
|
||||
String fieldName = entry.getKey().replace("app.appConfig.", "");
|
||||
|
||||
try {
|
||||
Field field = AppConfig.class.getField(fieldName);
|
||||
Object object;
|
||||
try {
|
||||
object = gson.fromJson(entry.getValue().toString(), field.getGenericType());
|
||||
} catch (JsonSyntaxException e) {
|
||||
Log.d(TAG, "For field "+fieldName);
|
||||
e.printStackTrace();
|
||||
object = entry.getValue().toString();
|
||||
}
|
||||
if (object != null)
|
||||
field.set(appConfig, object);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
Log.w(TAG, "Should remove app.appConfig."+fieldName);
|
||||
//appSharedPrefs.edit().remove("app.appConfig."+fieldName).apply(); TODO migration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*if (appConfig.lastAppVersion > BuildConfig.VERSION_CODE) {
|
||||
BootReceiver br = new BootReceiver();
|
||||
Intent i = new Intent();
|
||||
//i.putExtra("UserChecked", true);
|
||||
br.onReceive(getContext(), i);
|
||||
Toast.makeText(mContext, R.string.warning_older_version_running, Toast.LENGTH_LONG).show();
|
||||
//Toast.makeText(mContext, "Zaktualizuj aplikację.", Toast.LENGTH_LONG).show();
|
||||
//System.exit(0);
|
||||
}*/
|
||||
|
||||
if (appConfig == null) {
|
||||
appConfig = new AppConfig(this);
|
||||
}
|
||||
}
|
||||
public void saveConfig()
|
||||
{
|
||||
try {
|
||||
appConfig.savePending = false;
|
||||
|
||||
SharedPreferences.Editor appSharedPrefsEditor = appSharedPrefs.edit();
|
||||
|
||||
JsonObject appConfigJson = gson.toJsonTree(appConfig).getAsJsonObject();
|
||||
for (Map.Entry<String, JsonElement> entry : appConfigJson.entrySet()) {
|
||||
String jsonObj;
|
||||
jsonObj = entry.getValue().toString();
|
||||
/*if (entry.getValue().isJsonObject()) {
|
||||
jsonObj = entry.getValue().getAsJsonObject().toString();
|
||||
}
|
||||
else if (entry.getValue().isJsonArray()) {
|
||||
jsonObj = entry.getValue().getAsJsonArray().toString();
|
||||
}
|
||||
else {
|
||||
jsonObj = entry.getValue().toString();
|
||||
}*/
|
||||
appSharedPrefsEditor.putString("app.appConfig." + entry.getKey(), jsonObj);
|
||||
}
|
||||
|
||||
appSharedPrefsEditor.apply();
|
||||
}
|
||||
catch (ConcurrentModificationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//appSharedPrefs.edit().putString("config", gson.toJson(appConfig)).apply();
|
||||
}
|
||||
public void saveConfig(String ... fieldNames)
|
||||
{
|
||||
appConfig.savePending = false;
|
||||
|
||||
SharedPreferences.Editor appSharedPrefsEditor = appSharedPrefs.edit();
|
||||
|
||||
for (String fieldName: fieldNames) {
|
||||
try {
|
||||
Object object = AppConfig.class.getField(fieldName).get(appConfig);
|
||||
appSharedPrefsEditor.putString("app.appConfig."+fieldName, gson.toJson(object));
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ConcurrentModificationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
appSharedPrefsEditor.apply();
|
||||
//appSharedPrefs.edit().putString("config", gson.toJson(appConfig)).apply();
|
||||
}
|
||||
|
||||
public void profileSave() {
|
||||
AsyncTask.execute(() -> {
|
||||
db.profileDao().add(profile);
|
||||
});
|
||||
}
|
||||
|
||||
public void profileSaveAsync() {
|
||||
AsyncTask.execute(() -> {
|
||||
db.profileDao().add(profile);
|
||||
});
|
||||
}
|
||||
public void profileSaveAsync(Profile profile) {
|
||||
AsyncTask.execute(() -> {
|
||||
db.profileDao().add(profile);
|
||||
});
|
||||
}
|
||||
|
||||
public void profileLoadById(int id) {
|
||||
profileLoadById(id, false);
|
||||
}
|
||||
public void profileLoadById(int id, boolean loadedLast) {
|
||||
//Log.d(TAG, "Loading ID "+id);
|
||||
/*if (profile == null) {
|
||||
profile = profileNew();
|
||||
AppDb.profileId = profile.id;
|
||||
appSharedPrefs.edit().putInt("current_profile_id", profile.id).apply();
|
||||
return;
|
||||
}*/
|
||||
if (profile == null || profile.getId() != id) {
|
||||
profile = db.profileDao().getByIdNow(id);
|
||||
/*if (profile == null) {
|
||||
profileLoadById(id);
|
||||
return;
|
||||
}*/
|
||||
if (profile != null) {
|
||||
MainActivity.Companion.setUseOldMessages(profile.getLoginStoreType() == LOGIN_TYPE_MOBIDZIENNIK && appConfig.mobidziennikOldMessages == 1);
|
||||
profileId = profile.getId();
|
||||
appSharedPrefs.edit().putInt("current_profile_id", profile.getId()).apply();
|
||||
config.setProfile(profileId);
|
||||
}
|
||||
else if (!loadedLast) {
|
||||
profileLoadById(profileLastId(), true);
|
||||
}
|
||||
else {
|
||||
profileId = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*public void profileRemove(int id)
|
||||
{
|
||||
Profile profile = db.profileDao().getFullByIdNow(id);
|
||||
|
||||
if (profile.id == profile.loginStoreId) {
|
||||
// this profile is the owner of the login store
|
||||
// we need to check if any other profile is using it
|
||||
List<Integer> transferProfileIds = db.profileDao().getIdsByLoginStoreIdNow(profile.loginStoreId);
|
||||
if (transferProfileIds.size() == 1) {
|
||||
// this login store is free of users, remove it along with the profile
|
||||
db.loginStoreDao().remove(profile.loginStoreId);
|
||||
// the current store is removed, we are ready to remove the profile
|
||||
}
|
||||
else if (transferProfileIds.size() > 1) {
|
||||
transferProfileIds.remove(transferProfileIds.indexOf(profile.id));
|
||||
// someone is using the store
|
||||
// we need to transfer it to the firstProfileId
|
||||
db.loginStoreDao().changeId(profile.loginStoreId, transferProfileIds.get(0));
|
||||
db.profileDao().changeStoreId(profile.loginStoreId, transferProfileIds.get(0));
|
||||
// the current store is removed, we are ready to remove the profile
|
||||
}
|
||||
}
|
||||
// else, the profile uses a store that it doesn't own
|
||||
// leave the store and go on with removing
|
||||
|
||||
Log.d(TAG, "Before removal: "+db.profileDao().getAllNow().toString());
|
||||
db.profileDao().remove(profile.id);
|
||||
Log.d(TAG, "After removal: "+db.profileDao().getAllNow().toString());
|
||||
|
||||
*//*int newId = 1;
|
||||
if (appConfig.profiles.size() > 0) {
|
||||
newId = appConfig.profiles.get(appConfig.profiles.size() - 1).id;
|
||||
}
|
||||
Log.d(TAG, "New ID: "+newId);
|
||||
//Toast.makeText(mContext, "selected new id "+newId, Toast.LENGTH_SHORT).show();
|
||||
profileLoadById(newId);*//*
|
||||
}*/
|
||||
|
||||
public int profileFirstId() {
|
||||
Integer id = db.profileDao().getFirstId();
|
||||
return id == null ? 1 : id;
|
||||
}
|
||||
|
||||
public int profileLastId() {
|
||||
Integer id = db.profileDao().getLastId();
|
||||
return id == null ? 1 : id;
|
||||
}
|
||||
|
||||
|
||||
public Context getContext()
|
||||
{
|
||||
return mContext;
|
||||
}
|
||||
|
||||
public void checkDevModePassword() {
|
||||
try {
|
||||
devMode = Utils.AESCrypt.decrypt("nWFVxY65Pa8/aRrT7EylNAencmOD+IxUY2Gg/beiIWY=", config.getDevModePassword()).equals("ok here you go it's enabled now")
|
||||
|| BuildConfig.DEBUG;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
devMode = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -4,20 +4,93 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik
|
||||
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.ShortcutInfo
|
||||
import android.content.pm.ShortcutManager
|
||||
import android.graphics.drawable.Icon
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.multidex.MultiDexApplication
|
||||
import androidx.work.Configuration
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import cat.ereza.customactivityoncrash.config.CaocConfig
|
||||
import com.chuckerteam.chucker.api.ChuckerCollector
|
||||
import com.chuckerteam.chucker.api.ChuckerInterceptor
|
||||
import com.chuckerteam.chucker.api.RetentionManager
|
||||
import com.google.firebase.FirebaseApp
|
||||
import com.google.firebase.FirebaseOptions
|
||||
import com.google.firebase.iid.FirebaseInstanceId
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
import com.google.gson.Gson
|
||||
import com.hypertrack.hyperlog.HyperLog
|
||||
import com.mikepenz.iconics.Iconics
|
||||
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
|
||||
import im.wangchao.mhttp.MHttp
|
||||
import im.wangchao.mhttp.internal.cookie.PersistentCookieJar
|
||||
import im.wangchao.mhttp.internal.cookie.cache.SetCookieCache
|
||||
import im.wangchao.mhttp.internal.cookie.persistence.SharedPrefsCookiePersistor
|
||||
import kotlinx.coroutines.*
|
||||
import me.leolin.shortcutbadger.ShortcutBadger
|
||||
import okhttp3.OkHttpClient
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import pl.szczodrzynski.edziennik.config.Config
|
||||
import pl.szczodrzynski.edziennik.data.api.events.ProfileListEmptyEvent
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing
|
||||
import pl.szczodrzynski.edziennik.data.db.AppDb
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.network.NetworkUtils
|
||||
import pl.szczodrzynski.edziennik.sync.SyncWorker
|
||||
import pl.szczodrzynski.edziennik.sync.UpdateWorker
|
||||
import pl.szczodrzynski.edziennik.ui.modules.base.CrashActivity
|
||||
import pl.szczodrzynski.edziennik.utils.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScope {
|
||||
class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
|
||||
companion object {
|
||||
@Volatile
|
||||
lateinit var db: AppDb
|
||||
val config: Config by lazy { Config(db) }
|
||||
var profile: Profile by mutableLazy { Profile(0, 0, 0, "") }
|
||||
val profileId
|
||||
get() = profile.id
|
||||
|
||||
var devMode = false
|
||||
}
|
||||
|
||||
//lateinit var db: AppDb
|
||||
//val config by lazy { Config(db); // TODO migrate }
|
||||
val notifications by lazy { Notifications() }
|
||||
inner class Notifications {
|
||||
val syncId = 1
|
||||
val syncKey = "pl.szczodrzynski.edziennik.SYNC"
|
||||
val syncChannelName: String by lazy { getString(R.string.notification_channel_get_data_name) }
|
||||
val syncChannelDesc: String by lazy { getString(R.string.notification_channel_get_data_desc) }
|
||||
val dataId = 50
|
||||
val dataKey = "pl.szczodrzynski.edziennik.DATA"
|
||||
val dataChannelName: String by lazy { getString(R.string.notification_channel_notifications_name) }
|
||||
val dataChannelDesc: String by lazy { getString(R.string.notification_channel_notifications_desc) }
|
||||
val dataQuietId = 60
|
||||
val dataQuietKey = "pl.szczodrzynski.edziennik.DATA_QUIET"
|
||||
val dataQuietChannelName: String by lazy { getString(R.string.notification_channel_notifications_quiet_name) }
|
||||
val dataQuietChannelDesc: String by lazy { getString(R.string.notification_channel_notifications_quiet_desc) }
|
||||
val updatesId = 100
|
||||
val updatesKey = "pl.szczodrzynski.edziennik.UPDATES"
|
||||
val updatesChannelName: String by lazy { getString(R.string.notification_channel_updates_name) }
|
||||
val updatesChannelDesc: String by lazy { getString(R.string.notification_channel_updates_desc) }
|
||||
}
|
||||
|
||||
val db
|
||||
get() = App.db
|
||||
val config
|
||||
get() = App.config
|
||||
val profile
|
||||
get() = App.profile
|
||||
val profileId
|
||||
get() = App.profileId
|
||||
|
||||
private val job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
@ -26,11 +99,10 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
|
||||
.setMinimumLoggingLevel(Log.VERBOSE)
|
||||
.build()
|
||||
|
||||
/*val preferences by lazy { getSharedPreferences(getString(R.string.preference_file), Context.MODE_PRIVATE) }
|
||||
val notifier by lazy { Notifier(this) }
|
||||
val preferences by lazy { getSharedPreferences(getString(R.string.preference_file), Context.MODE_PRIVATE) }
|
||||
val permissionChecker by lazy { PermissionChecker(this) }
|
||||
|
||||
lateinit var profile: ProfileFull
|
||||
val networkUtils by lazy { NetworkUtils(this) }
|
||||
val gson by lazy { Gson() }
|
||||
|
||||
/* _ _ _______ _______ _____
|
||||
| | | |__ __|__ __| __ \
|
||||
@ -48,13 +120,13 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
|
||||
.connectTimeout(20, TimeUnit.SECONDS)
|
||||
.writeTimeout(5, TimeUnit.SECONDS)
|
||||
.readTimeout(10, TimeUnit.SECONDS)
|
||||
builder.installHttpsSupport()
|
||||
builder.installHttpsSupport(this)
|
||||
|
||||
if (devMode || BuildConfig.DEBUG) {
|
||||
HyperLog.initialize(this)
|
||||
HyperLog.setLogLevel(Log.VERBOSE)
|
||||
HyperLog.setLogFormat(DebugLogFormat(this))
|
||||
val chuckerCollector = ChuckerCollector(this, true, Period.ONE_HOUR)
|
||||
val chuckerCollector = ChuckerCollector(this, true, RetentionManager.Period.ONE_HOUR)
|
||||
val chuckerInterceptor = ChuckerInterceptor(this, chuckerCollector)
|
||||
builder.addInterceptor(chuckerInterceptor)
|
||||
}
|
||||
@ -77,22 +149,7 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
|
||||
|_____/|_|\__, |_| |_|\__,_|\__|\__,_|_| \___|
|
||||
__/ |
|
||||
|__*/
|
||||
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
|
||||
}
|
||||
val deviceId: String by lazy { Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID) ?: "" }
|
||||
private var unreadBadgesAvailable = true
|
||||
|
||||
/* _____ _
|
||||
@ -118,193 +175,214 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
|
||||
.apply()
|
||||
Iconics.init(applicationContext)
|
||||
Iconics.registerFont(SzkolnyFont)
|
||||
db = AppDb.getDatabase(this)
|
||||
App.db = AppDb(this)
|
||||
Themes.themeInt = config.ui.theme
|
||||
MHttp.instance().customOkHttpClient(http)
|
||||
|
||||
if (!profileLoadById(config.lastProfileId)) {
|
||||
db.profileDao().firstId?.let { profileLoadById(it) }
|
||||
}
|
||||
|
||||
devMode = "f054761fbdb6a238" == deviceId || BuildConfig.DEBUG
|
||||
if (config.devModePassword != null)
|
||||
checkDevModePassword()
|
||||
|
||||
Signing.getCert(this)
|
||||
|
||||
launch { async(Dispatchers.Default) {
|
||||
if (config.sync.enabled) {
|
||||
scheduleNext(this@App, false)
|
||||
} else {
|
||||
cancelNext(this@App)
|
||||
launch {
|
||||
withContext(Dispatchers.Default) {
|
||||
config.migrate(this@App)
|
||||
|
||||
if (config.devModePassword != null)
|
||||
checkDevModePassword()
|
||||
|
||||
if (config.sync.enabled)
|
||||
SyncWorker.scheduleNext(this@App, false)
|
||||
else
|
||||
SyncWorker.cancelNext(this@App)
|
||||
|
||||
if (config.sync.notifyAboutUpdates)
|
||||
UpdateWorker.scheduleNext(this@App, false)
|
||||
else
|
||||
UpdateWorker.cancelNext(this@App)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
||||
val shortcutManager = getSystemService(ShortcutManager::class.java)
|
||||
|
||||
val shortcutTimetable = ShortcutInfo.Builder(this@App, "item_timetable")
|
||||
.setShortLabel(getString(R.string.shortcut_timetable)).setLongLabel(getString(R.string.shortcut_timetable))
|
||||
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_timetable))
|
||||
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_TIMETABLE))
|
||||
.build()
|
||||
|
||||
val shortcutAgenda = ShortcutInfo.Builder(this@App, "item_agenda")
|
||||
.setShortLabel(getString(R.string.shortcut_agenda)).setLongLabel(getString(R.string.shortcut_agenda))
|
||||
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_agenda))
|
||||
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_AGENDA))
|
||||
.build()
|
||||
|
||||
val shortcutGrades = ShortcutInfo.Builder(this@App, "item_grades")
|
||||
.setShortLabel(getString(R.string.shortcut_grades)).setLongLabel(getString(R.string.shortcut_grades))
|
||||
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_grades))
|
||||
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_GRADES))
|
||||
.build()
|
||||
|
||||
val shortcutHomework = ShortcutInfo.Builder(this@App, "item_homeworks")
|
||||
.setShortLabel(getString(R.string.shortcut_homework)).setLongLabel(getString(R.string.shortcut_homework))
|
||||
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_homework))
|
||||
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_HOMEWORK))
|
||||
.build()
|
||||
|
||||
val shortcutMessages = ShortcutInfo.Builder(this@App, "item_messages")
|
||||
.setShortLabel(getString(R.string.shortcut_messages)).setLongLabel(getString(R.string.shortcut_messages))
|
||||
.setIcon(Icon.createWithResource(this@App, R.mipmap.ic_shortcut_messages))
|
||||
.setIntent(Intent(Intent.ACTION_MAIN, null, this@App, MainActivity::class.java)
|
||||
.putExtra("fragmentId", MainActivity.DRAWER_ITEM_MESSAGES))
|
||||
.build()
|
||||
|
||||
shortcutManager.dynamicShortcuts = listOf(
|
||||
shortcutTimetable,
|
||||
shortcutAgenda,
|
||||
shortcutGrades,
|
||||
shortcutHomework,
|
||||
shortcutMessages
|
||||
)
|
||||
} // shortcuts - end
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(notifications.syncKey, notifications.syncChannelName, NotificationManager.IMPORTANCE_MIN).apply {
|
||||
description = notifications.syncChannelDesc
|
||||
})
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(notifications.dataKey, notifications.dataChannelName, NotificationManager.IMPORTANCE_HIGH).apply {
|
||||
description = notifications.dataChannelDesc
|
||||
enableLights(true)
|
||||
lightColor = 0xff2196f3.toInt()
|
||||
})
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(notifications.dataQuietKey, notifications.dataQuietChannelName, NotificationManager.IMPORTANCE_LOW).apply {
|
||||
description = notifications.dataQuietChannelDesc
|
||||
setSound(null, null)
|
||||
enableVibration(false)
|
||||
})
|
||||
notificationManager.createNotificationChannel(
|
||||
NotificationChannel(notifications.updatesKey, notifications.updatesChannelName, NotificationManager.IMPORTANCE_DEFAULT).apply {
|
||||
description = notifications.updatesChannelDesc
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
if (config.appInstalledTime == 0L)
|
||||
try {
|
||||
config.appInstalledTime = packageManager.getPackageInfo(packageName, 0).firstInstallTime
|
||||
config.appRateSnackbarTime = config.appInstalledTime + 7 * DAY * MS
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
val pushMobidziennikApp = FirebaseApp.initializeApp(
|
||||
this@App,
|
||||
FirebaseOptions.Builder()
|
||||
.setApiKey("AIzaSyCi5LmsZ5BBCQnGtrdvWnp1bWLCNP8OWQE")
|
||||
.setApplicationId("1:747285019373:android:f6341bf7b158621d")
|
||||
.build(),
|
||||
"Mobidziennik2"
|
||||
)
|
||||
|
||||
val pushLibrusApp = FirebaseApp.initializeApp(
|
||||
this@App,
|
||||
FirebaseOptions.Builder()
|
||||
.setApiKey("AIzaSyDfTuEoYPKdv4aceEws1CO3n0-HvTndz-o")
|
||||
.setApplicationId("1:513056078587:android:1e29083b760af544")
|
||||
.build(),
|
||||
"Librus"
|
||||
)
|
||||
|
||||
val pushVulcanApp = FirebaseApp.initializeApp(
|
||||
this@App,
|
||||
FirebaseOptions.Builder()
|
||||
.setApiKey("AIzaSyDW8MUtanHy64_I0oCpY6cOxB3jrvJd_iA")
|
||||
.setApplicationId("1:987828170337:android:ac97431a0a4578c3")
|
||||
.build(),
|
||||
"Vulcan"
|
||||
)
|
||||
|
||||
try {
|
||||
FirebaseInstanceId.getInstance().instanceId.addOnSuccessListener { instanceIdResult ->
|
||||
val token = instanceIdResult.token
|
||||
config.sync.tokenApp = token
|
||||
}
|
||||
FirebaseInstanceId.getInstance(pushMobidziennikApp).instanceId.addOnSuccessListener { instanceIdResult ->
|
||||
val token = instanceIdResult.token
|
||||
if (token != config.sync.tokenMobidziennik) {
|
||||
config.sync.tokenMobidziennik = token
|
||||
config.sync.tokenMobidziennikList = listOf()
|
||||
}
|
||||
}
|
||||
FirebaseInstanceId.getInstance(pushLibrusApp).instanceId.addOnSuccessListener { instanceIdResult ->
|
||||
val token = instanceIdResult.token
|
||||
if (token != config.sync.tokenLibrus) {
|
||||
config.sync.tokenLibrus = token
|
||||
config.sync.tokenLibrusList = listOf()
|
||||
}
|
||||
}
|
||||
FirebaseInstanceId.getInstance(pushVulcanApp).instanceId.addOnSuccessListener { instanceIdResult ->
|
||||
val token = instanceIdResult.token
|
||||
if (token != config.sync.tokenVulcan) {
|
||||
config.sync.tokenVulcan = token
|
||||
config.sync.tokenVulcanList = listOf()
|
||||
}
|
||||
}
|
||||
FirebaseMessaging.getInstance().subscribeToTopic(packageName)
|
||||
} catch (e: IllegalStateException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
db.metadataDao().countUnseen().observeForever { count: Int ->
|
||||
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) {
|
||||
|
||||
private fun profileLoadById(profileId: Int): Boolean {
|
||||
db.profileDao().getByIdNow(profileId)?.also {
|
||||
App.profile = it
|
||||
App.config.lastProfileId = it.id
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
fun profileLoad(profileId: Int, onSuccess: (profile: Profile) -> Unit) {
|
||||
launch {
|
||||
val deferred = async(Dispatchers.Default) {
|
||||
profileLoad(profileId)
|
||||
val success = withContext(Dispatchers.Default) {
|
||||
profileLoadById(profileId)
|
||||
}
|
||||
deferred.await()
|
||||
onSuccess(profile)
|
||||
if (success)
|
||||
onSuccess(profile)
|
||||
else
|
||||
profileLoadLast(onSuccess)
|
||||
}
|
||||
}
|
||||
|
||||
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 profileLoadLast(onSuccess: (profile: Profile) -> Unit) {
|
||||
launch {
|
||||
val success = withContext(Dispatchers.Default) {
|
||||
profileLoadById(db.profileDao().lastId ?: return@withContext false)
|
||||
}
|
||||
if (!success) {
|
||||
EventBus.getDefault().post(ProfileListEmptyEvent())
|
||||
}
|
||||
}
|
||||
}
|
||||
fun profileSave() = profileSave(profile)
|
||||
fun profileSave(profile: Profile) {
|
||||
launch(Dispatchers.Default) {
|
||||
App.db.profileDao().add(profile)
|
||||
}
|
||||
}
|
||||
|
||||
@ -315,5 +393,5 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package pl.szczodrzynski.edziennik
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.ColorStateList
|
||||
import android.content.res.Resources
|
||||
@ -34,6 +35,7 @@ import androidx.core.util.forEach
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import com.google.android.gms.security.ProviderInstaller
|
||||
import com.google.gson.JsonArray
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonObject
|
||||
@ -41,23 +43,31 @@ import im.wangchao.mhttp.Response
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.ConnectionSpec
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.TlsVersion
|
||||
import okio.Buffer
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Team
|
||||
import pl.szczodrzynski.edziennik.network.TLSSocketFactory
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import java.math.BigInteger
|
||||
import java.nio.charset.Charset
|
||||
import java.security.KeyStore
|
||||
import java.security.MessageDigest
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.zip.CRC32
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
import javax.net.ssl.SSLContext
|
||||
import javax.net.ssl.TrustManagerFactory
|
||||
import javax.net.ssl.X509TrustManager
|
||||
import kotlin.Pair
|
||||
|
||||
|
||||
@ -642,6 +652,34 @@ fun Bundle(vararg properties: Pair<String, Any?>): Bundle {
|
||||
}
|
||||
}
|
||||
}
|
||||
fun Intent(action: String? = null, vararg properties: Pair<String, Any?>): Intent {
|
||||
return Intent(action).putExtras(Bundle(*properties))
|
||||
}
|
||||
fun Intent(packageContext: Context, cls: Class<*>, vararg properties: Pair<String, Any?>): Intent {
|
||||
return Intent(packageContext, cls).putExtras(Bundle(*properties))
|
||||
}
|
||||
|
||||
fun Bundle.toJsonObject(): JsonObject {
|
||||
val json = JsonObject()
|
||||
keySet()?.forEach { key ->
|
||||
get(key)?.let {
|
||||
when (it) {
|
||||
is String -> json.addProperty(key, it)
|
||||
is Char -> json.addProperty(key, it)
|
||||
is Int -> json.addProperty(key, it)
|
||||
is Long -> json.addProperty(key, it)
|
||||
is Float -> json.addProperty(key, it)
|
||||
is Short -> json.addProperty(key, it)
|
||||
is Double -> json.addProperty(key, it)
|
||||
is Boolean -> json.addProperty(key, it)
|
||||
is Bundle -> json.add(key, it.toJsonObject())
|
||||
else -> json.addProperty(key, it.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
return json
|
||||
}
|
||||
fun Intent.toJsonObject() = extras?.toJsonObject()
|
||||
|
||||
fun JsonArray?.isNullOrEmpty(): Boolean = (this?.size() ?: 0) == 0
|
||||
fun JsonArray.isEmpty(): Boolean = this.size() == 0
|
||||
@ -934,6 +972,8 @@ fun Context.getNotificationTitle(type: Int): String {
|
||||
Notification.TYPE_NEW_EVENT -> R.string.notification_type_new_event
|
||||
Notification.TYPE_NEW_HOMEWORK -> R.string.notification_type_new_homework
|
||||
Notification.TYPE_NEW_SHARED_EVENT -> R.string.notification_type_new_shared_event
|
||||
Notification.TYPE_NEW_SHARED_HOMEWORK -> R.string.notification_type_new_shared_homework
|
||||
Notification.TYPE_REMOVED_SHARED_EVENT -> R.string.notification_type_removed_shared_event
|
||||
Notification.TYPE_NEW_MESSAGE -> R.string.notification_type_new_message
|
||||
Notification.TYPE_NEW_NOTICE -> R.string.notification_type_notice
|
||||
Notification.TYPE_NEW_ATTENDANCE -> R.string.notification_type_attendance
|
||||
@ -950,3 +990,45 @@ fun Context.getNotificationTitle(type: Int): String {
|
||||
fun Cursor?.getString(columnName: String) = this?.getStringOrNull(getColumnIndex(columnName))
|
||||
fun Cursor?.getInt(columnName: String) = this?.getIntOrNull(getColumnIndex(columnName))
|
||||
fun Cursor?.getLong(columnName: String) = this?.getLongOrNull(getColumnIndex(columnName))
|
||||
|
||||
fun OkHttpClient.Builder.installHttpsSupport(context: Context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
try {
|
||||
try {
|
||||
ProviderInstaller.installIfNeeded(context)
|
||||
} catch (e: Exception) {
|
||||
Log.e("OkHttpTLSCompat", "Play Services not found or outdated")
|
||||
|
||||
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
|
||||
trustManagerFactory.init(null as KeyStore?)
|
||||
|
||||
val x509TrustManager = trustManagerFactory.trustManagers.singleOrNull { it is X509TrustManager } as X509TrustManager?
|
||||
?: return
|
||||
|
||||
val sc = SSLContext.getInstance("TLSv1.2")
|
||||
sc.init(null, null, null)
|
||||
sslSocketFactory(TLSSocketFactory(sc.socketFactory), x509TrustManager)
|
||||
val cs: ConnectionSpec = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
|
||||
.tlsVersions(TlsVersion.TLS_1_0)
|
||||
.tlsVersions(TlsVersion.TLS_1_1)
|
||||
.tlsVersions(TlsVersion.TLS_1_2)
|
||||
.build()
|
||||
val specs: MutableList<ConnectionSpec> = ArrayList()
|
||||
specs.add(cs)
|
||||
specs.add(ConnectionSpec.COMPATIBLE_TLS)
|
||||
specs.add(ConnectionSpec.CLEARTEXT)
|
||||
connectionSpecs(specs)
|
||||
}
|
||||
} catch (exc: Exception) {
|
||||
Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun CharSequence.containsAll(list: List<CharSequence>, ignoreCase: Boolean = false): Boolean {
|
||||
for (i in list) {
|
||||
if (!contains(i, ignoreCase))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package pl.szczodrzynski.edziennik
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.ActivityManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
@ -9,7 +8,6 @@ import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.os.AsyncTask
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
@ -36,18 +34,22 @@ import com.mikepenz.materialdrawer.model.ProfileDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.ProfileSettingDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.interfaces.IProfile
|
||||
import kotlinx.coroutines.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import pl.droidsonroids.gif.GifDrawable
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.data.api.events.*
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing
|
||||
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata.*
|
||||
import pl.szczodrzynski.edziennik.databinding.ActivitySzkolnyBinding
|
||||
import pl.szczodrzynski.edziennik.sync.AppManagerDetectedEvent
|
||||
import pl.szczodrzynski.edziennik.sync.SyncWorker
|
||||
import pl.szczodrzynski.edziennik.sync.UpdateWorker
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.ServerMessageDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.settings.ProfileRemoveDialog
|
||||
import pl.szczodrzynski.edziennik.ui.dialogs.sync.SyncViewListDialog
|
||||
@ -93,9 +95,10 @@ import pl.szczodrzynski.navlib.drawer.items.withAppTitle
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
class MainActivity : AppCompatActivity(), CoroutineScope {
|
||||
companion object {
|
||||
|
||||
var useOldMessages = false
|
||||
@ -108,6 +111,7 @@ class MainActivity : AppCompatActivity() {
|
||||
const val DRAWER_PROFILE_SYNC_ALL = 201
|
||||
const val DRAWER_PROFILE_EXPORT_DATA = 202
|
||||
const val DRAWER_PROFILE_MANAGE = 203
|
||||
const val DRAWER_PROFILE_MARK_ALL_AS_READ = 204
|
||||
const val DRAWER_ITEM_HOME = 1
|
||||
const val DRAWER_ITEM_TIMETABLE = 11
|
||||
const val DRAWER_ITEM_AGENDA = 12
|
||||
@ -208,6 +212,10 @@ class MainActivity : AppCompatActivity() {
|
||||
.withDescription(R.string.drawer_manage_profiles_desc)
|
||||
.isInProfileList(false)
|
||||
|
||||
list += NavTarget(DRAWER_PROFILE_MARK_ALL_AS_READ, R.string.menu_mark_everything_as_read, null)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
|
||||
.isInProfileList(true)
|
||||
|
||||
list += NavTarget(DRAWER_PROFILE_SYNC_ALL, R.string.menu_sync_all, null)
|
||||
.withIcon(CommunityMaterial.Icon.cmd_download_outline)
|
||||
.isInProfileList(true)
|
||||
@ -226,6 +234,10 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private var job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
val b: ActivitySzkolnyBinding by lazy { ActivitySzkolnyBinding.inflate(layoutInflater) }
|
||||
val navView: NavView by lazy { b.navView }
|
||||
val drawer: NavDrawer by lazy { navView.drawer }
|
||||
@ -257,12 +269,21 @@ class MainActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
d(TAG, "Activity created")
|
||||
|
||||
setTheme(Themes.appTheme)
|
||||
|
||||
app.config.ui.language?.let {
|
||||
setLanguage(it)
|
||||
}
|
||||
|
||||
if (App.profileId == 0) {
|
||||
onProfileListEmptyEvent(ProfileListEmptyEvent())
|
||||
return
|
||||
}
|
||||
|
||||
d(TAG, "Profile is valid, inflating views")
|
||||
|
||||
setContentView(b.root)
|
||||
|
||||
Log.d(TAG, Signing.appPassword)
|
||||
@ -332,8 +353,7 @@ class MainActivity : AppCompatActivity() {
|
||||
setAccountHeaderBackground(app.config.ui.headerBackground)
|
||||
|
||||
drawerProfileListEmptyListener = {
|
||||
app.config.loginFinished = false
|
||||
profileListEmptyListener()
|
||||
onProfileListEmptyEvent(ProfileListEmptyEvent())
|
||||
}
|
||||
drawerItemSelectedListener = { id, position, drawerItem ->
|
||||
loadTarget(id)
|
||||
@ -362,30 +382,19 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
navTarget = navTargetList[0]
|
||||
|
||||
var profileListEmpty = drawer.profileListEmpty
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
intent?.putExtras(savedInstanceState)
|
||||
savedInstanceState.clear()
|
||||
}
|
||||
|
||||
if (!profileListEmpty) {
|
||||
handleIntent(intent?.extras)
|
||||
}
|
||||
app.db.profileDao().all.observe(this, Observer { profiles ->
|
||||
drawer.setProfileList(profiles.filter { it.id >= 0 }.toMutableList())
|
||||
if (profileListEmpty) {
|
||||
profileListEmpty = false
|
||||
handleIntent(intent?.extras)
|
||||
}
|
||||
else if (app.profile != null) {
|
||||
drawer.currentProfile = app.profile.id
|
||||
}
|
||||
drawer.currentProfile = App.profileId
|
||||
})
|
||||
|
||||
// if null, getAllFull will load a profile and update drawerItems
|
||||
if (app.profile != null)
|
||||
setDrawerItems()
|
||||
setDrawerItems()
|
||||
|
||||
handleIntent(intent?.extras)
|
||||
|
||||
app.db.metadataDao().unreadCounts.observe(this, Observer { unreadCounters ->
|
||||
unreadCounters.map {
|
||||
@ -405,6 +414,7 @@ class MainActivity : AppCompatActivity() {
|
||||
isStoragePermissionGranted()
|
||||
|
||||
SyncWorker.scheduleNext(app)
|
||||
UpdateWorker.scheduleNext(app)
|
||||
|
||||
// APP BACKGROUND
|
||||
if (app.config.ui.appBackground != null) {
|
||||
@ -509,17 +519,25 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
var profileListEmptyListener = {
|
||||
startActivityForResult(Intent(this, LoginActivity::class.java), REQUEST_LOGIN_ACTIVITY)
|
||||
}
|
||||
private var profileSettingClickListener = { id: Int, view: View? ->
|
||||
when (id) {
|
||||
DRAWER_PROFILE_ADD_NEW -> {
|
||||
profileListEmptyListener()
|
||||
startActivityForResult(Intent(this, LoginActivity::class.java), REQUEST_LOGIN_ACTIVITY)
|
||||
}
|
||||
DRAWER_PROFILE_SYNC_ALL -> {
|
||||
EdziennikTask.sync().enqueue(this)
|
||||
}
|
||||
DRAWER_PROFILE_MARK_ALL_AS_READ -> { launch {
|
||||
withContext(Dispatchers.Default) {
|
||||
app.db.profileDao().allNow.forEach { profile ->
|
||||
if (profile.loginStoreType != LoginStore.LOGIN_TYPE_LIBRUS)
|
||||
app.db.metadataDao().setAllSeenExceptMessagesAndAnnouncements(profile.id, true)
|
||||
else
|
||||
app.db.metadataDao().setAllSeenExceptMessages(profile.id, true)
|
||||
}
|
||||
}
|
||||
Toast.makeText(this@MainActivity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show()
|
||||
}}
|
||||
else -> {
|
||||
loadTarget(id)
|
||||
}
|
||||
@ -564,6 +582,13 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onProfileListEmptyEvent(event: ProfileListEmptyEvent) {
|
||||
d(TAG, "Profile list is empty. Launch LoginActivity.")
|
||||
app.config.loginFinished = false
|
||||
startActivity(Intent(this, LoginActivity::class.java))
|
||||
finish()
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onApiTaskProgressEvent(event: ApiTaskProgressEvent) {
|
||||
if (event.profileId == App.profileId) {
|
||||
navView.toolbar.apply {
|
||||
@ -579,6 +604,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||
fun onApiTaskFinishedEvent(event: ApiTaskFinishedEvent) {
|
||||
EventBus.getDefault().removeStickyEvent(event)
|
||||
if (event.profileId == App.profileId) {
|
||||
navView.toolbar.apply {
|
||||
subtitleFormat = R.string.toolbar_subtitle
|
||||
@ -589,6 +615,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||
fun onApiTaskAllFinishedEvent(event: ApiTaskAllFinishedEvent) {
|
||||
EventBus.getDefault().removeStickyEvent(event)
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||
@ -604,7 +631,8 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
|
||||
fun onAppManagerDetectedEvent(event: AppManagerDetectedEvent) {
|
||||
if (app.appConfig.dontShowAppManagerDialog)
|
||||
EventBus.getDefault().removeStickyEvent(event)
|
||||
if (app.config.sync.dontShowAppManagerDialog)
|
||||
return
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.app_manager_dialog_title)
|
||||
@ -626,8 +654,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
.setNeutralButton(R.string.dont_ask_again) { dialog, which ->
|
||||
app.appConfig.dontShowAppManagerDialog = true
|
||||
app.saveConfig("dontShowAppManagerDialog")
|
||||
app.config.sync.dontShowAppManagerDialog = true
|
||||
}
|
||||
.setCancelable(false)
|
||||
.show()
|
||||
@ -669,10 +696,26 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
d(TAG, "}")
|
||||
|
||||
if (extras?.containsKey("action") == true) {
|
||||
val handled = when (extras.getString("action")) {
|
||||
"serverMessage" -> {
|
||||
ServerMessageDialog(
|
||||
this,
|
||||
extras.getString("serverMessageTitle") ?: getString(R.string.app_name),
|
||||
extras.getString("serverMessageText") ?: ""
|
||||
)
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
if (handled)
|
||||
return
|
||||
}
|
||||
|
||||
if (extras?.containsKey("reloadProfileId") == true) {
|
||||
val reloadProfileId = extras.getInt("reloadProfileId", -1)
|
||||
extras.remove("reloadProfileId")
|
||||
if (reloadProfileId == -1 || (app.profile != null && app.profile.id == reloadProfileId)) {
|
||||
if (reloadProfileId == -1 || app.profile.id == reloadProfileId) {
|
||||
reloadTarget()
|
||||
return
|
||||
}
|
||||
@ -702,9 +745,9 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
when {
|
||||
app.profile == null || app.profile.id == -1 -> {
|
||||
app.profile.id == 0 -> {
|
||||
if (intentProfileId == -1)
|
||||
intentProfileId = app.appSharedPrefs.getInt("current_profile_id", 1)
|
||||
intentProfileId = app.config.lastProfileId
|
||||
loadProfile(intentProfileId, intentTargetId, extras)
|
||||
}
|
||||
intentProfileId != -1 -> {
|
||||
@ -743,7 +786,16 @@ class MainActivity : AppCompatActivity() {
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
d(TAG, "Activity started")
|
||||
super.onStart()
|
||||
}
|
||||
override fun onStop() {
|
||||
d(TAG, "Activity stopped")
|
||||
super.onStop()
|
||||
}
|
||||
override fun onResume() {
|
||||
d(TAG, "Activity resumed")
|
||||
val filter = IntentFilter()
|
||||
filter.addAction(Intent.ACTION_MAIN)
|
||||
registerReceiver(intentReceiver, filter)
|
||||
@ -751,10 +803,15 @@ class MainActivity : AppCompatActivity() {
|
||||
super.onResume()
|
||||
}
|
||||
override fun onPause() {
|
||||
d(TAG, "Activity paused")
|
||||
unregisterReceiver(intentReceiver)
|
||||
EventBus.getDefault().unregister(this)
|
||||
super.onPause()
|
||||
}
|
||||
override fun onDestroy() {
|
||||
d(TAG, "Activity destroyed")
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
@ -768,15 +825,10 @@ class MainActivity : AppCompatActivity() {
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode == REQUEST_LOGIN_ACTIVITY) {
|
||||
if (resultCode == Activity.RESULT_CANCELED && false) {
|
||||
if (!app.config.loginFinished)
|
||||
finish()
|
||||
}
|
||||
else {
|
||||
if (!app.config.loginFinished)
|
||||
finish()
|
||||
else {
|
||||
handleIntent(data?.extras)
|
||||
}
|
||||
handleIntent(data?.extras)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -797,34 +849,23 @@ class MainActivity : AppCompatActivity() {
|
||||
fun loadProfile(id: Int) = loadProfile(id, navTargetId)
|
||||
fun loadProfile(id: Int, arguments: Bundle?) = loadProfile(id, navTargetId, arguments)
|
||||
fun loadProfile(id: Int, drawerSelection: Int, arguments: Bundle? = null) {
|
||||
//d("NavDebug", "loadProfile(id = $id, drawerSelection = $drawerSelection)")
|
||||
if (app.profile != null && App.profileId == id) {
|
||||
if (App.profileId == id) {
|
||||
drawer.currentProfile = app.profile.id
|
||||
loadTarget(drawerSelection, arguments)
|
||||
return
|
||||
}
|
||||
AsyncTask.execute {
|
||||
app.profileLoadById(id)
|
||||
app.profileLoad(id) {
|
||||
MessagesFragment.pageSelection = -1
|
||||
MessagesListFragment.tapPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
|
||||
MessagesListFragment.topPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
|
||||
MessagesListFragment.bottomPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
|
||||
|
||||
this.runOnUiThread {
|
||||
if (app.profile == null) {
|
||||
if (app.config.loginFinished) {
|
||||
// this shouldn't run
|
||||
profileListEmptyListener()
|
||||
}
|
||||
} else {
|
||||
setDrawerItems()
|
||||
// the drawer profile is updated automatically when the drawer item is clicked
|
||||
// update it manually when switching profiles from other source
|
||||
//if (drawer.currentProfile != app.profile.id)
|
||||
drawer.currentProfile = app.profile.id
|
||||
loadTarget(drawerSelection, arguments)
|
||||
}
|
||||
}
|
||||
setDrawerItems()
|
||||
// the drawer profile is updated automatically when the drawer item is clicked
|
||||
// update it manually when switching profiles from other source
|
||||
//if (drawer.currentProfile != app.profile.id)
|
||||
drawer.currentProfile = app.profileId
|
||||
loadTarget(drawerSelection, arguments)
|
||||
}
|
||||
}
|
||||
fun loadTarget(id: Int, arguments: Bundle? = null) {
|
||||
@ -969,7 +1010,7 @@ class MainActivity : AppCompatActivity() {
|
||||
* that something has changed in the bottom sheet.
|
||||
*/
|
||||
fun gainAttention() {
|
||||
if (app.config.ui.bottomSheetOpened || true)
|
||||
if (app.config.ui.bottomSheetOpened)
|
||||
return
|
||||
b.navView.postDelayed({
|
||||
navView.gainAttentionOnBottomBar()
|
||||
@ -1018,12 +1059,11 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
fun setDrawerItems() {
|
||||
d("NavDebug", "setDrawerItems() app.profile = ${app.profile ?: "null"}")
|
||||
d("NavDebug", "setDrawerItems() app.profile = ${app.profile}")
|
||||
val drawerItems = arrayListOf<IDrawerItem<*>>()
|
||||
val drawerProfiles = arrayListOf<ProfileSettingDrawerItem>()
|
||||
|
||||
val supportedFragments = if (app.profile == null) arrayListOf<Int>()
|
||||
else app.profile.supportedFragments
|
||||
val supportedFragments = app.profile.supportedFragments
|
||||
|
||||
targetPopToHomeList.clear()
|
||||
|
||||
@ -1087,7 +1127,7 @@ class MainActivity : AppCompatActivity() {
|
||||
private var targetHomeId: Int = -1
|
||||
override fun onBackPressed() {
|
||||
if (!b.navView.onBackPressed()) {
|
||||
if (App.getConfig().ui.openDrawerOnBackPressed) {
|
||||
if (App.config.ui.openDrawerOnBackPressed) {
|
||||
b.navView.drawer.toggle()
|
||||
} else {
|
||||
navigateUp()
|
||||
|
@ -1,350 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.receivers.BootReceiver;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time;
|
||||
|
||||
import static androidx.core.app.NotificationCompat.PRIORITY_DEFAULT;
|
||||
import static androidx.core.app.NotificationCompat.PRIORITY_MAX;
|
||||
|
||||
public class Notifier {
|
||||
|
||||
private static final String TAG = "Notifier";
|
||||
public static final int ID_GET_DATA = 1337000;
|
||||
public static final int ID_GET_DATA_ERROR = 1337001;
|
||||
private static String CHANNEL_GET_DATA_NAME;
|
||||
private static String CHANNEL_GET_DATA_DESC;
|
||||
private static final String GROUP_KEY_GET_DATA = "pl.szczodrzynski.edziennik.GET_DATA";
|
||||
|
||||
public static final int ID_NOTIFICATIONS = 1337002;
|
||||
public static String CHANNEL_NOTIFICATIONS_NAME;
|
||||
public static String CHANNEL_NOTIFICATIONS_DESC;
|
||||
public static final String GROUP_KEY_NOTIFICATIONS = "pl.szczodrzynski.edziennik.NOTIFICATIONS";
|
||||
|
||||
public static final int ID_NOTIFICATIONS_QUIET = 1337002;
|
||||
public static String CHANNEL_NOTIFICATIONS_QUIET_NAME;
|
||||
public static String CHANNEL_NOTIFICATIONS_QUIET_DESC;
|
||||
public static final String GROUP_KEY_NOTIFICATIONS_QUIET = "pl.szczodrzynski.edziennik.NOTIFICATIONS_QUIET";
|
||||
|
||||
private static final int ID_UPDATES = 1337003;
|
||||
private static String CHANNEL_UPDATES_NAME;
|
||||
private static String CHANNEL_UPDATES_DESC;
|
||||
private static final String GROUP_KEY_UPDATES = "pl.szczodrzynski.edziennik.UPDATES";
|
||||
|
||||
private App app;
|
||||
public NotificationManager notificationManager;
|
||||
private NotificationCompat.Builder getDataNotificationBuilder;
|
||||
public int notificationColor;
|
||||
|
||||
Notifier(App _app) {
|
||||
this.app = _app;
|
||||
|
||||
CHANNEL_GET_DATA_NAME = app.getString(R.string.notification_channel_get_data_name);
|
||||
CHANNEL_GET_DATA_DESC = app.getString(R.string.notification_channel_get_data_desc);
|
||||
CHANNEL_NOTIFICATIONS_NAME = app.getString(R.string.notification_channel_notifications_name);
|
||||
CHANNEL_NOTIFICATIONS_DESC = app.getString(R.string.notification_channel_notifications_desc);
|
||||
CHANNEL_NOTIFICATIONS_QUIET_NAME = app.getString(R.string.notification_channel_notifications_quiet_name);
|
||||
CHANNEL_NOTIFICATIONS_QUIET_DESC = app.getString(R.string.notification_channel_notifications_quiet_desc);
|
||||
CHANNEL_UPDATES_NAME = app.getString(R.string.notification_channel_updates_name);
|
||||
CHANNEL_UPDATES_DESC = app.getString(R.string.notification_channel_updates_desc);
|
||||
|
||||
notificationColor = ContextCompat.getColor(app.getContext(), R.color.colorPrimary);
|
||||
notificationManager = (NotificationManager) app.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channelGetData = new NotificationChannel(GROUP_KEY_GET_DATA, CHANNEL_GET_DATA_NAME, NotificationManager.IMPORTANCE_MIN);
|
||||
channelGetData.setDescription(CHANNEL_GET_DATA_DESC);
|
||||
notificationManager.createNotificationChannel(channelGetData);
|
||||
|
||||
NotificationChannel channelNotifications = new NotificationChannel(GROUP_KEY_NOTIFICATIONS, CHANNEL_NOTIFICATIONS_NAME, NotificationManager.IMPORTANCE_HIGH);
|
||||
channelNotifications.setDescription(CHANNEL_NOTIFICATIONS_DESC);
|
||||
channelNotifications.enableLights(true);
|
||||
channelNotifications.setLightColor(notificationColor);
|
||||
notificationManager.createNotificationChannel(channelNotifications);
|
||||
|
||||
NotificationChannel channelNotificationsQuiet = new NotificationChannel(GROUP_KEY_NOTIFICATIONS_QUIET, CHANNEL_NOTIFICATIONS_QUIET_NAME, NotificationManager.IMPORTANCE_DEFAULT);
|
||||
channelNotificationsQuiet.setDescription(CHANNEL_NOTIFICATIONS_QUIET_DESC);
|
||||
channelNotificationsQuiet.setSound(null, null);
|
||||
channelNotificationsQuiet.enableVibration(false);
|
||||
notificationManager.createNotificationChannel(channelNotificationsQuiet);
|
||||
|
||||
NotificationChannel channelUpdates = new NotificationChannel(GROUP_KEY_UPDATES, CHANNEL_UPDATES_NAME, NotificationManager.IMPORTANCE_HIGH);
|
||||
channelUpdates.setDescription(CHANNEL_UPDATES_DESC);
|
||||
notificationManager.createNotificationChannel(channelUpdates);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean shouldBeQuiet() {
|
||||
long now = Time.getNow().getInMillis();
|
||||
long start = app.config.getSync().getQuietHoursStart();
|
||||
long end = app.config.getSync().getQuietHoursEnd();
|
||||
if (start > end) {
|
||||
end += 1000 * 60 * 60 * 24;
|
||||
//Log.d(TAG, "Night passing");
|
||||
}
|
||||
if (start > now) {
|
||||
now += 1000 * 60 * 60 * 24;
|
||||
//Log.d(TAG, "Now is smaller");
|
||||
}
|
||||
//Log.d(TAG, "Start is "+start+", now is "+now+", end is "+end);
|
||||
return start > 0 && now >= start && now <= end;
|
||||
}
|
||||
|
||||
public int getNotificationDefaults() {
|
||||
return (shouldBeQuiet() ? 0 : Notification.DEFAULT_ALL);
|
||||
}
|
||||
public String getNotificationGroup() {
|
||||
return shouldBeQuiet() ? GROUP_KEY_NOTIFICATIONS_QUIET : GROUP_KEY_NOTIFICATIONS;
|
||||
}
|
||||
public int getNotificationPriority() {
|
||||
return shouldBeQuiet() ? PRIORITY_DEFAULT : PRIORITY_MAX;
|
||||
}
|
||||
|
||||
/* _____ _ _____ _
|
||||
| __ \ | | / ____| | |
|
||||
| | | | __ _| |_ __ _ | | __ ___| |_
|
||||
| | | |/ _` | __/ _` | | | |_ |/ _ \ __|
|
||||
| |__| | (_| | || (_| | | |__| | __/ |_
|
||||
|_____/ \__,_|\__\__,_| \_____|\___|\_*/
|
||||
public Notification notificationGetDataShow(int maxProgress) {
|
||||
/*Intent notificationIntent = new Intent(app.getContext(), SyncService.class);
|
||||
notificationIntent.setAction(ACTION_CANCEL);
|
||||
PendingIntent pendingIntent = PendingIntent.getService(app.getContext(), 0,
|
||||
notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);*/
|
||||
|
||||
getDataNotificationBuilder = new NotificationCompat.Builder(app, GROUP_KEY_GET_DATA)
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||
.setColor(notificationColor)
|
||||
.setContentTitle(app.getString(R.string.notification_get_data_title))
|
||||
.setContentText(app.getString(R.string.notification_get_data_text))
|
||||
//.addAction(R.drawable.ic_notification, app.getString(R.string.notification_get_data_cancel), pendingIntent)
|
||||
//.setGroup(GROUP_KEY_GET_DATA)
|
||||
.setOngoing(true)
|
||||
.setProgress(maxProgress, 0, false)
|
||||
.setTicker(app.getString(R.string.notification_get_data_summary))
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW);
|
||||
return getDataNotificationBuilder.build();
|
||||
}
|
||||
|
||||
public Notification notificationGetDataProgress(int progress, int maxProgress) {
|
||||
getDataNotificationBuilder.setProgress(maxProgress, progress, false);
|
||||
return getDataNotificationBuilder.build();
|
||||
}
|
||||
|
||||
public Notification notificationGetDataAction(int stringResId) {
|
||||
getDataNotificationBuilder.setContentTitle(app.getString(R.string.sync_action_format, app.getString(stringResId)));
|
||||
return getDataNotificationBuilder.build();
|
||||
}
|
||||
|
||||
public Notification notificationGetDataProfile(String profileName) {
|
||||
getDataNotificationBuilder.setContentText(profileName);
|
||||
return getDataNotificationBuilder.build();
|
||||
}
|
||||
|
||||
public Notification notificationGetDataError(String profileName, String error, int failedProfileId) {
|
||||
Intent notificationIntent = new Intent(app.getContext(), Notifier.GetDataRetryService.class);
|
||||
notificationIntent.putExtra("failedProfileId", failedProfileId);
|
||||
PendingIntent pendingIntent = PendingIntent.getService(app.getContext(), 0,
|
||||
notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
getDataNotificationBuilder.mActions.clear();
|
||||
/*try {
|
||||
//Use reflection clean up old actions
|
||||
Field f = getDataNotificationBuilder.getClass().getDeclaredField("mActions");
|
||||
f.setAccessible(true);
|
||||
f.set(getDataNotificationBuilder, new ArrayList<NotificationCompat.Action>());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}*/
|
||||
|
||||
getDataNotificationBuilder.setProgress(0, 0, false)
|
||||
.setTicker(app.getString(R.string.notification_get_data_error_summary))
|
||||
.setSmallIcon(android.R.drawable.stat_sys_warning)
|
||||
.addAction(R.drawable.ic_notification, app.getString(R.string.notification_get_data_once_again), pendingIntent)
|
||||
.setContentTitle(app.getString(R.string.notification_get_data_error_title, profileName))
|
||||
.setContentText(error)
|
||||
.setStyle(new NotificationCompat.BigTextStyle().bigText(error))
|
||||
.setOngoing(false);
|
||||
return getDataNotificationBuilder.build();
|
||||
}
|
||||
|
||||
public void notificationPost(int id, Notification notification) {
|
||||
notificationManager.notify(id, notification);
|
||||
}
|
||||
|
||||
public void notificationCancel(int id) {
|
||||
notificationManager.cancel(id);
|
||||
}
|
||||
|
||||
//public void notificationGetDataHide() {
|
||||
// notificationManager.cancel(ID_GET_DATA);
|
||||
// }
|
||||
|
||||
public static class GetDataRetryService extends IntentService {
|
||||
private static final String TAG = "Notifier/GetDataRetry";
|
||||
|
||||
public GetDataRetryService() {
|
||||
super(Notifier.GetDataRetryService.class.getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* _ _ _ _ __ _ _ _
|
||||
| \ | | | | (_)/ _(_) | | (_)
|
||||
| \| | ___ | |_ _| |_ _ ___ __ _| |_ _ ___ _ __
|
||||
| . ` |/ _ \| __| | _| |/ __/ _` | __| |/ _ \| '_ \
|
||||
| |\ | (_) | |_| | | | | (_| (_| | |_| | (_) | | | |
|
||||
|_| \_|\___/ \__|_|_| |_|\___\__,_|\__|_|\___/|_| |*/
|
||||
public void add(pl.szczodrzynski.edziennik.utils.models.Notification notification) {
|
||||
app.appConfig.notifications.add(notification);
|
||||
}
|
||||
|
||||
public void postAll() {
|
||||
Collections.sort(app.appConfig.notifications, (o1, o2) -> (o2.addedDate - o1.addedDate > 0) ? 1 : (o2.addedDate - o1.addedDate < 0) ? -1 : 0);
|
||||
|
||||
if (app.appConfig.notifications.size() > 40) {
|
||||
app.appConfig.notifications.subList(40, app.appConfig.notifications.size() - 1).clear();
|
||||
}
|
||||
|
||||
int unreadCount = 0;
|
||||
List<pl.szczodrzynski.edziennik.utils.models.Notification> notificationList = new ArrayList<>();
|
||||
for (pl.szczodrzynski.edziennik.utils.models.Notification notification: app.appConfig.notifications) {
|
||||
if (!notification.notified) {
|
||||
notification.seen = false;
|
||||
notification.notified = true;
|
||||
unreadCount++;
|
||||
if (notificationList.size() < 10) {
|
||||
notificationList.add(notification);
|
||||
}
|
||||
}
|
||||
else {
|
||||
notification.seen = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (pl.szczodrzynski.edziennik.utils.models.Notification notification: notificationList) {
|
||||
Intent intent = new Intent(app, MainActivity.class);
|
||||
notification.fillIntent(intent);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(app, notification.id, intent, 0);
|
||||
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(app, getNotificationGroup())
|
||||
// title, text, type, date
|
||||
.setContentTitle(notification.title)
|
||||
.setContentText(notification.text)
|
||||
.setSubText(pl.szczodrzynski.edziennik.utils.models.Notification.stringType(app, notification.type))
|
||||
.setWhen(notification.addedDate)
|
||||
.setTicker(app.getString(R.string.notification_ticker_format, pl.szczodrzynski.edziennik.utils.models.Notification.stringType(app, notification.type)))
|
||||
// icon, color, lights, priority
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setColor(notificationColor)
|
||||
.setLights(0xFF00FFFF, 2000, 2000)
|
||||
.setPriority(getNotificationPriority())
|
||||
// channel, group, style
|
||||
.setChannelId(getNotificationGroup())
|
||||
.setGroup(getNotificationGroup())
|
||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
||||
.setStyle(new NotificationCompat.BigTextStyle().bigText(notification.text))
|
||||
// intent, auto cancel
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true);
|
||||
if (!shouldBeQuiet()) {
|
||||
notificationBuilder.setDefaults(getNotificationDefaults());
|
||||
}
|
||||
notificationManager.notify(notification.id, notificationBuilder.build());
|
||||
}
|
||||
|
||||
if (notificationList.size() > 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
Intent intent = new Intent(app, MainActivity.class);
|
||||
intent.setAction("android.intent.action.MAIN");
|
||||
intent.putExtra("fragmentId", MainActivity.DRAWER_ITEM_NOTIFICATIONS);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(app, ID_NOTIFICATIONS,
|
||||
intent, 0);
|
||||
|
||||
NotificationCompat.Builder groupBuilder =
|
||||
new NotificationCompat.Builder(app, getNotificationGroup())
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setColor(notificationColor)
|
||||
.setContentTitle(app.getString(R.string.notification_new_notification_title_format, unreadCount))
|
||||
.setGroupSummary(true)
|
||||
.setAutoCancel(true)
|
||||
.setChannelId(getNotificationGroup())
|
||||
.setGroup(getNotificationGroup())
|
||||
.setLights(0xFF00FFFF, 2000, 2000)
|
||||
.setPriority(getNotificationPriority())
|
||||
.setContentIntent(pendingIntent)
|
||||
.setStyle(new NotificationCompat.BigTextStyle());
|
||||
if (!shouldBeQuiet()) {
|
||||
groupBuilder.setDefaults(getNotificationDefaults());
|
||||
}
|
||||
notificationManager.notify(ID_NOTIFICATIONS, groupBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
/* _ _ _ _
|
||||
| | | | | | | |
|
||||
| | | |_ __ __| | __ _| |_ ___ ___
|
||||
| | | | '_ \ / _` |/ _` | __/ _ \/ __|
|
||||
| |__| | |_) | (_| | (_| | || __/\__ \
|
||||
\____/| .__/ \__,_|\__,_|\__\___||___/
|
||||
| |
|
||||
|*/
|
||||
public void notificationUpdatesShow(String updateVersion, String updateUrl, String updateFilename, boolean updateDirect) {
|
||||
if (!app.config.getSync().getNotifyAboutUpdates())
|
||||
return;
|
||||
Intent notificationIntent = new Intent(app.getContext(), BootReceiver.NotificationActionService.class)
|
||||
.putExtra("update_version", updateVersion)
|
||||
.putExtra("update_url", updateUrl)
|
||||
.putExtra("update_filename", updateFilename)
|
||||
.putExtra("update_direct", updateDirect);
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getService(app.getContext(), 0,
|
||||
notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(app, GROUP_KEY_UPDATES)
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download_done)
|
||||
.setColor(notificationColor)
|
||||
.setContentTitle(app.getString(R.string.notification_updates_title))
|
||||
.setContentText(app.getString(R.string.notification_updates_text, updateVersion))
|
||||
.setLights(0xFF00FFFF, 2000, 2000)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setTicker(app.getString(R.string.notification_updates_summary))
|
||||
.setPriority(PRIORITY_MAX)
|
||||
.setAutoCancel(true);
|
||||
if (!shouldBeQuiet()) {
|
||||
notificationBuilder.setDefaults(getNotificationDefaults());
|
||||
}
|
||||
notificationManager.notify(ID_UPDATES, notificationBuilder.build());
|
||||
}
|
||||
|
||||
public void notificationUpdatesHide() {
|
||||
if (!app.config.getSync().getNotifyAboutUpdates())
|
||||
return;
|
||||
notificationManager.cancel(ID_UPDATES);
|
||||
}
|
||||
|
||||
public void dump() {
|
||||
for (pl.szczodrzynski.edziennik.utils.models.Notification notification: app.appConfig.notifications) {
|
||||
Log.d(TAG, "Profile"+notification.profileId+" Notification from "+ Date.fromMillis(notification.addedDate).getFormattedString()+" "+ Time.fromMillis(notification.addedDate).getStringHMS()+" - "+notification.text);
|
||||
}
|
||||
}
|
||||
}
|
@ -16,12 +16,13 @@ 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.api.szkolny.response.Update
|
||||
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
|
||||
const val DATA_VERSION = 10
|
||||
}
|
||||
|
||||
private val job = Job()
|
||||
@ -45,6 +46,20 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
|
||||
get() { mHash = mHash ?: values.get("hash", ""); return mHash ?: "" }
|
||||
set(value) { set("hash", value); mHash = value }
|
||||
|
||||
private var mLastProfileId: Int? = null
|
||||
var lastProfileId: Int
|
||||
get() { mLastProfileId = mLastProfileId ?: values.get("lastProfileId", 0); return mLastProfileId ?: 0 }
|
||||
set(value) { set("lastProfileId", value); mLastProfileId = value }
|
||||
|
||||
private var mUpdatesChannel: String? = null
|
||||
var updatesChannel: String
|
||||
get() { mUpdatesChannel = mUpdatesChannel ?: values.get("updatesChannel", "release"); return mUpdatesChannel ?: "release" }
|
||||
set(value) { set("updatesChannel", value); mUpdatesChannel = value }
|
||||
private var mUpdate: Update? = null
|
||||
var update: Update?
|
||||
get() { mUpdate = mUpdate ?: values.get("update", null as Update?); return mUpdate ?: null as Update? }
|
||||
set(value) { set("update", value); mUpdate = value }
|
||||
|
||||
private var mAppVersion: Int? = null
|
||||
var appVersion: Int
|
||||
get() { mAppVersion = mAppVersion ?: values.get("appVersion", BuildConfig.VERSION_CODE); return mAppVersion ?: BuildConfig.VERSION_CODE }
|
||||
|
@ -17,6 +17,6 @@ class ConfigGrades(private val config: Config) {
|
||||
|
||||
private var mOrderBy: Int? = null
|
||||
var orderBy: Int
|
||||
get() { mOrderBy = mOrderBy ?: config.values.get("gradesOrderBy", 0); return mOrderBy ?: 0 }
|
||||
get() { mOrderBy = mOrderBy ?: config.values.get("gradesOrderBy", 0); return mOrderBy ?: ORDER_BY_DATE_DESC }
|
||||
set(value) { config.set("gradesOrderBy", value); mOrderBy = value }
|
||||
}
|
@ -9,6 +9,11 @@ import pl.szczodrzynski.edziennik.config.utils.getIntList
|
||||
import pl.szczodrzynski.edziennik.config.utils.set
|
||||
|
||||
class ConfigSync(private val config: Config) {
|
||||
private var mDontShowAppManagerDialog: Boolean? = null
|
||||
var dontShowAppManagerDialog: Boolean
|
||||
get() { mDontShowAppManagerDialog = mDontShowAppManagerDialog ?: config.values.get("dontShowAppManagerDialog", false); return mDontShowAppManagerDialog ?: false }
|
||||
set(value) { config.set("dontShowAppManagerDialog", value); mDontShowAppManagerDialog = value }
|
||||
|
||||
private var mSyncEnabled: Boolean? = null
|
||||
var enabled: Boolean
|
||||
get() { mSyncEnabled = mSyncEnabled ?: config.values.get("syncEnabled", true); return mSyncEnabled ?: true }
|
||||
|
@ -45,11 +45,6 @@ class ConfigUI(private val config: Config) {
|
||||
get() { mOpenDrawerOnBackPressed = mOpenDrawerOnBackPressed ?: config.values.get("openDrawerOnBackPressed", false); return mOpenDrawerOnBackPressed ?: false }
|
||||
set(value) { config.set("openDrawerOnBackPressed", value); mOpenDrawerOnBackPressed = value }
|
||||
|
||||
private var mAgendaViewType: Int? = null
|
||||
var agendaViewType: Int
|
||||
get() { mAgendaViewType = mAgendaViewType ?: config.values.get("agendaViewType", 0); return mAgendaViewType ?: 0 }
|
||||
set(value) { config.set("agendaViewType", value); mAgendaViewType = value }
|
||||
|
||||
private var mHomeCards: List<HomeCardModel>? = null
|
||||
var homeCards: List<HomeCardModel>
|
||||
get() { mHomeCards = mHomeCards ?: config.values.get("homeCards", listOf(), HomeCardModel::class.java); return mHomeCards ?: listOf() }
|
||||
|
@ -9,6 +9,7 @@ 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.ProfileConfigMigration
|
||||
import pl.szczodrzynski.edziennik.config.utils.get
|
||||
import pl.szczodrzynski.edziennik.config.utils.set
|
||||
import pl.szczodrzynski.edziennik.config.utils.toHashMap
|
||||
@ -27,6 +28,7 @@ class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEn
|
||||
val values: HashMap<String, String?> = hashMapOf()
|
||||
|
||||
val grades by lazy { ProfileConfigGrades(this) }
|
||||
val ui by lazy { ProfileConfigUI(this) }
|
||||
/*
|
||||
val sync by lazy { ConfigSync(this) }
|
||||
val timetable by lazy { ConfigTimetable(this) }
|
||||
@ -44,8 +46,8 @@ class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEn
|
||||
|
||||
init {
|
||||
rawEntries.toHashMap(profileId, values)
|
||||
/*if (dataVersion < DATA_VERSION)
|
||||
ProfileConfigMigration(this)*/
|
||||
if (dataVersion < DATA_VERSION)
|
||||
ProfileConfigMigration(this)
|
||||
}
|
||||
|
||||
override fun set(key: String, value: String?) {
|
||||
|
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-1-19.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.config
|
||||
|
||||
import pl.szczodrzynski.edziennik.config.utils.get
|
||||
import pl.szczodrzynski.edziennik.config.utils.set
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.AGENDA_DEFAULT
|
||||
|
||||
class ProfileConfigUI(private val config: ProfileConfig) {
|
||||
private var mAgendaViewType: Int? = null
|
||||
var agendaViewType: Int
|
||||
get() { mAgendaViewType = mAgendaViewType ?: config.values.get("agendaViewType", 0); return mAgendaViewType ?: AGENDA_DEFAULT }
|
||||
set(value) { config.set("agendaViewType", value); mAgendaViewType = value }
|
||||
}
|
@ -22,4 +22,7 @@ interface ConfigDao {
|
||||
|
||||
@Query("SELECT * FROM config WHERE profileId = :profileId")
|
||||
fun getAllNow(profileId: Int): List<ConfigEntry>
|
||||
|
||||
@Query("DELETE FROM config WHERE profileId = :profileId")
|
||||
fun clear(profileId: Int)
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-1-19.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.config.utils
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import pl.szczodrzynski.edziennik.BuildConfig
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.config.Config
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_LIBRUS
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_MOBIDZIENNIK
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_VULCAN
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
class AppConfigMigrationV3(p: SharedPreferences, config: Config) {
|
||||
init { config.apply {
|
||||
val s = "app.appConfig"
|
||||
if (dataVersion < 1) {
|
||||
ui.theme = p.getString("$s.appTheme", null)?.toIntOrNull() ?: 1
|
||||
sync.enabled = p.getString("$s.registerSyncEnabled", null)?.toBoolean() ?: true
|
||||
sync.interval = p.getString("$s.registerSyncInterval", null)?.toIntOrNull() ?: 3600
|
||||
val oldButtons = p.getString("$s.miniDrawerButtonIds", null)?.let { str ->
|
||||
str.replace("[\\[\\]]*".toRegex(), "")
|
||||
.split(",\\s?".toRegex())
|
||||
.mapNotNull { it.toIntOrNull() }
|
||||
}
|
||||
ui.miniMenuButtons = oldButtons ?: listOf(
|
||||
MainActivity.DRAWER_ITEM_HOME,
|
||||
MainActivity.DRAWER_ITEM_TIMETABLE,
|
||||
MainActivity.DRAWER_ITEM_AGENDA,
|
||||
MainActivity.DRAWER_ITEM_GRADES,
|
||||
MainActivity.DRAWER_ITEM_MESSAGES,
|
||||
MainActivity.DRAWER_ITEM_HOMEWORK,
|
||||
MainActivity.DRAWER_ITEM_SETTINGS
|
||||
)
|
||||
dataVersion = 1
|
||||
}
|
||||
if (dataVersion < 2) {
|
||||
devModePassword = p.getString("$s.devModePassword", null).fix()
|
||||
sync.tokenApp = p.getString("$s.fcmToken", null).fix()
|
||||
timetable.bellSyncMultiplier = p.getString("$s.bellSyncMultiplier", null)?.toIntOrNull() ?: 0
|
||||
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 }
|
||||
}
|
||||
}
|
@ -37,6 +37,9 @@ fun AbstractConfig.set(key: String, value: JsonElement?) {
|
||||
fun AbstractConfig.set(key: String, value: List<Any>?) {
|
||||
set(key, value?.let { gson.toJson(it) })
|
||||
}
|
||||
fun AbstractConfig.set(key: String, value: Any?) {
|
||||
set(key, value?.let { gson.toJson(it) })
|
||||
}
|
||||
fun AbstractConfig.setStringList(key: String, value: List<String>?) {
|
||||
set(key, value?.let { gson.toJson(it) })
|
||||
}
|
||||
@ -74,6 +77,9 @@ fun HashMap<String, String?>.get(key: String, default: JsonObject?): JsonObject?
|
||||
fun HashMap<String, String?>.get(key: String, default: JsonArray?): JsonArray? {
|
||||
return this[key]?.let { JsonParser().parse(it)?.asJsonArray } ?: default
|
||||
}
|
||||
inline fun <reified T> HashMap<String, String?>.get(key: String, default: T?): T? {
|
||||
return this[key]?.let { Gson().fromJson(it, T::class.java) } ?: default
|
||||
}
|
||||
/* !!! cannot use mutable list here - modifying it will not update the DB */
|
||||
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
|
||||
|
@ -5,32 +5,32 @@
|
||||
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.HOUR
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_LIBRUS
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_MOBIDZIENNIK
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_TYPE_VULCAN
|
||||
import pl.szczodrzynski.edziennik.config.Config
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
import pl.szczodrzynski.edziennik.config.ConfigGrades.Companion.ORDER_BY_DATE_DESC
|
||||
|
||||
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(
|
||||
val p = app.getSharedPreferences("pl.szczodrzynski.edziennik_profiles", Context.MODE_PRIVATE)
|
||||
if (p.contains("app.appConfig.appTheme")) {
|
||||
// migrate appConfig from app version 3.x and lower.
|
||||
// Updates dataVersion to level 2.
|
||||
AppConfigMigrationV3(p, config)
|
||||
}
|
||||
|
||||
if (dataVersion < 2) {
|
||||
appVersion = BuildConfig.VERSION_CODE
|
||||
loginFinished = false
|
||||
ui.language = null
|
||||
ui.theme = 1
|
||||
ui.appBackground = null
|
||||
ui.headerBackground = null
|
||||
ui.miniMenuVisible = false
|
||||
ui.miniMenuButtons = listOf(
|
||||
MainActivity.DRAWER_ITEM_HOME,
|
||||
MainActivity.DRAWER_ITEM_TIMETABLE,
|
||||
MainActivity.DRAWER_ITEM_AGENDA,
|
||||
@ -39,46 +39,36 @@ class ConfigMigration(app: App, config: Config) {
|
||||
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.enabled = true
|
||||
sync.interval = 1*HOUR.toInt()
|
||||
sync.notifyAboutUpdates = true
|
||||
sync.onlyWifi = false
|
||||
sync.quietHoursStart = 0
|
||||
sync.quietHoursEnd = 0
|
||||
sync.quietDuringLessons = false
|
||||
sync.tokenApp = null
|
||||
sync.tokenMobidziennik = null
|
||||
sync.tokenMobidziennikList = listOf()
|
||||
sync.tokenVulcanList = listOf()
|
||||
sync.tokenLibrus = null
|
||||
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
|
||||
}
|
||||
}
|
||||
sync.tokenVulcan = null
|
||||
sync.tokenVulcanList = listOf()
|
||||
timetable.bellSyncMultiplier = 0
|
||||
timetable.bellSyncDiff = null
|
||||
timetable.countInSeconds = false
|
||||
grades.orderBy = ORDER_BY_DATE_DESC
|
||||
|
||||
dataVersion = 2
|
||||
}
|
||||
}}
|
||||
|
||||
private fun String?.fix(): String? {
|
||||
return this?.replace("\"", "")?.let { if (it == "null") null else it }
|
||||
}
|
||||
if (dataVersion < 10) {
|
||||
ui.openDrawerOnBackPressed = false
|
||||
ui.homeCards = listOf()
|
||||
ui.snowfall = false
|
||||
ui.bottomSheetOpened = false
|
||||
sync.dontShowAppManagerDialog = false
|
||||
|
||||
dataVersion = 10
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
@ -4,23 +4,21 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.config.utils
|
||||
|
||||
import android.content.Context
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.config.Config
|
||||
import pl.szczodrzynski.edziennik.config.ProfileConfig
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.AGENDA_DEFAULT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.COLOR_MODE_WEIGHTED
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile.Companion.YEAR_ALL_GRADES
|
||||
|
||||
class ProfileConfigMigration(app: App, config: Config) {
|
||||
class ProfileConfigMigration(config: ProfileConfig) {
|
||||
init { config.apply {
|
||||
val p = app.getSharedPreferences("pl.szczodrzynski.edziennik_profiles", Context.MODE_PRIVATE)
|
||||
val s = "app.appConfig"
|
||||
|
||||
if (dataVersion < 1) {
|
||||
grades.colorMode = COLOR_MODE_WEIGHTED
|
||||
grades.countZeroToAvg = true
|
||||
grades.yearAverageMode = YEAR_ALL_GRADES
|
||||
ui.agendaViewType = AGENDA_DEFAULT
|
||||
|
||||
//dataVersion = 1
|
||||
}
|
||||
if (dataVersion < 2) {
|
||||
//gradesColorMode do profilu !
|
||||
//agendaViewType do profilu !
|
||||
// app.appConfig.dontCountZeroToAverage do profilu !
|
||||
dataVersion = 1
|
||||
}
|
||||
}}
|
||||
}
|
@ -12,12 +12,15 @@ import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
|
||||
import pl.szczodrzynski.edziennik.data.api.events.*
|
||||
import pl.szczodrzynski.edziennik.data.api.events.requests.ServiceCloseRequest
|
||||
import pl.szczodrzynski.edziennik.data.api.events.requests.TaskCancelRequest
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.api.task.*
|
||||
import pl.szczodrzynski.edziennik.data.api.task.ErrorReportTask
|
||||
import pl.szczodrzynski.edziennik.data.api.task.IApiTask
|
||||
import pl.szczodrzynski.edziennik.data.api.task.SzkolnyTask
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import kotlin.math.min
|
||||
@ -40,15 +43,13 @@ class ApiService : Service() {
|
||||
|
||||
private val syncingProfiles = mutableListOf<Profile>()
|
||||
|
||||
private val finishingTaskQueue = mutableListOf(
|
||||
SzkolnyTask.sync(syncingProfiles),
|
||||
NotifyTask()
|
||||
)
|
||||
private val allTaskList = mutableListOf<IApiTask>()
|
||||
private var szkolnyTaskFinished = false
|
||||
private val allTaskRequestList = mutableListOf<Any>()
|
||||
private val taskQueue = mutableListOf<IApiTask>()
|
||||
private val errorList = mutableListOf<ApiError>()
|
||||
|
||||
private var serviceClosed = false
|
||||
set(value) { field = value; notification.serviceClosed = value }
|
||||
private var taskCancelled = false
|
||||
private var taskIsRunning = false
|
||||
private var taskRunning: IApiTask? = null // for debug purposes
|
||||
@ -131,15 +132,20 @@ class ApiService : Service() {
|
||||
checkIfTaskFrozen()
|
||||
if (taskIsRunning)
|
||||
return
|
||||
if (taskCancelled || serviceClosed || (taskQueue.isEmpty() && finishingTaskQueue.isEmpty())) {
|
||||
serviceClosed = false
|
||||
if (taskCancelled || serviceClosed || (taskQueue.isEmpty() && szkolnyTaskFinished)) {
|
||||
allCompleted()
|
||||
return
|
||||
}
|
||||
|
||||
lastEventTime = System.currentTimeMillis()
|
||||
|
||||
val task = if (taskQueue.isEmpty()) finishingTaskQueue.removeAt(0) else taskQueue.removeAt(0)
|
||||
val task = if (taskQueue.isNotEmpty()) {
|
||||
taskQueue.removeAt(0)
|
||||
} else {
|
||||
szkolnyTaskFinished = true
|
||||
SzkolnyTask(app, syncingProfiles)
|
||||
}
|
||||
|
||||
task.taskId = ++taskMaximumId
|
||||
task.prepare(app)
|
||||
taskIsRunning = true
|
||||
@ -162,9 +168,8 @@ class ApiService : Service() {
|
||||
try {
|
||||
when (task) {
|
||||
is EdziennikTask -> task.run(app, taskCallback)
|
||||
is NotifyTask -> task.run(app, taskCallback)
|
||||
is ErrorReportTask -> task.run(app, taskCallback, notification, errorList)
|
||||
is SzkolnyTask -> task.run(app, taskCallback)
|
||||
is SzkolnyTask -> task.run(taskCallback)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
taskCallback.onError(ApiError(TAG, EXCEPTION_API_TASK).withThrowable(e))
|
||||
@ -214,6 +219,7 @@ class ApiService : Service() {
|
||||
}
|
||||
|
||||
private fun allCompleted() {
|
||||
serviceClosed = true
|
||||
EventBus.getDefault().postSticky(ApiTaskAllFinishedEvent())
|
||||
stopSelf()
|
||||
}
|
||||
@ -229,10 +235,12 @@ class ApiService : Service() {
|
||||
EventBus.getDefault().removeStickyEvent(task)
|
||||
d(TAG, task.toString())
|
||||
|
||||
// fix for duplicated tasks, thank you EventBus
|
||||
if (task in allTaskList)
|
||||
return
|
||||
allTaskList += task
|
||||
if (task is EdziennikTask) {
|
||||
// fix for duplicated tasks, thank you EventBus
|
||||
if (task.request in allTaskRequestList)
|
||||
return
|
||||
allTaskRequestList += task.request
|
||||
}
|
||||
|
||||
if (task is EdziennikTask) {
|
||||
when (task.request) {
|
||||
@ -298,6 +306,8 @@ class ApiService : Service() {
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
d(TAG, "Service destroyed")
|
||||
serviceClosed = true
|
||||
EventBus.getDefault().unregister(this)
|
||||
}
|
||||
|
||||
|
@ -1,209 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.data.api
|
||||
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ANNOUNCEMENTS
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_ATTENDANCE
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_BEHAVIOUR
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_GRADES
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOME
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_HOMEWORK
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES
|
||||
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_TIMETABLE
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.api.models.Data
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Attendance
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.*
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notice
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_LUCKY_NUMBER
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_ANNOUNCEMENT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_ATTENDANCE
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_EVENT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_GRADE
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_HOMEWORK
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_MESSAGE
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_NOTICE
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_TIMETABLE_LESSON_CHANGE
|
||||
import pl.szczodrzynski.edziennik.getNotificationTitle
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class DataNotifications(val data: Data) {
|
||||
companion object {
|
||||
private const val TAG = "DataNotifications"
|
||||
}
|
||||
|
||||
val app = data.app
|
||||
val profileId = data.profile?.id ?: -1
|
||||
val profileName = data.profile?.name ?: ""
|
||||
val profile = data.profile
|
||||
val loginStore = data.loginStore
|
||||
|
||||
init { run {
|
||||
if (profile == null) {
|
||||
return@run
|
||||
}
|
||||
|
||||
val today = Date.getToday()
|
||||
val todayValue = today.value
|
||||
|
||||
for (lesson in app.db.timetableDao().getNotNotifiedNow(profileId)) {
|
||||
val text = app.getString(R.string.notification_lesson_change_format, lesson.getDisplayChangeType(app), if (lesson.displayDate == null) "" else lesson.displayDate!!.formattedString, lesson.changeSubjectName)
|
||||
data.notifications += Notification(
|
||||
title = app.getNotificationTitle(TYPE_TIMETABLE_LESSON_CHANGE),
|
||||
text = text,
|
||||
type = TYPE_TIMETABLE_LESSON_CHANGE,
|
||||
profileId = profileId,
|
||||
profileName = profileName,
|
||||
viewId = DRAWER_ITEM_TIMETABLE,
|
||||
addedDate = lesson.addedDate
|
||||
).addExtra("timetableDate", lesson.displayDate?.stringY_m_d ?: "")
|
||||
}
|
||||
|
||||
for (event in app.db.eventDao().getNotNotifiedNow(profileId)) {
|
||||
val text = if (event.type == Event.TYPE_HOMEWORK)
|
||||
app.getString(
|
||||
if (event.subjectLongName.isNullOrEmpty())
|
||||
R.string.notification_homework_no_subject_format
|
||||
else
|
||||
R.string.notification_homework_format,
|
||||
event.subjectLongName,
|
||||
event.eventDate.formattedString
|
||||
)
|
||||
else
|
||||
app.getString(
|
||||
if (event.subjectLongName.isNullOrEmpty())
|
||||
R.string.notification_event_no_subject_format
|
||||
else
|
||||
R.string.notification_event_format,
|
||||
event.typeName,
|
||||
event.eventDate.formattedString,
|
||||
event.subjectLongName
|
||||
)
|
||||
val type = if (event.type == Event.TYPE_HOMEWORK) TYPE_NEW_HOMEWORK else TYPE_NEW_EVENT
|
||||
data.notifications += Notification(
|
||||
title = app.getNotificationTitle(type),
|
||||
text = text,
|
||||
type = type,
|
||||
profileId = profileId,
|
||||
profileName = profileName,
|
||||
viewId = if (event.type == Event.TYPE_HOMEWORK) DRAWER_ITEM_HOMEWORK else DRAWER_ITEM_AGENDA,
|
||||
addedDate = event.addedDate
|
||||
).addExtra("eventId", event.id).addExtra("eventDate", event.eventDate.value.toLong())
|
||||
}
|
||||
|
||||
for (grade in app.db.gradeDao().getNotNotifiedNow(profileId)) {
|
||||
val gradeName = when (grade.type) {
|
||||
TYPE_SEMESTER1_PROPOSED, TYPE_SEMESTER2_PROPOSED -> app.getString(R.string.grade_semester_proposed_format_2, grade.name)
|
||||
TYPE_SEMESTER1_FINAL, TYPE_SEMESTER2_FINAL -> app.getString(R.string.grade_semester_final_format_2, grade.name)
|
||||
TYPE_YEAR_PROPOSED -> app.getString(R.string.grade_year_proposed_format_2, grade.name)
|
||||
TYPE_YEAR_FINAL -> app.getString(R.string.grade_year_final_format_2, grade.name)
|
||||
else -> grade.name
|
||||
}
|
||||
val text = app.getString(R.string.notification_grade_format, gradeName, grade.subjectLongName)
|
||||
data.notifications += Notification(
|
||||
title = app.getNotificationTitle(TYPE_NEW_GRADE),
|
||||
text = text,
|
||||
type = TYPE_NEW_GRADE,
|
||||
profileId = profileId,
|
||||
profileName = profileName,
|
||||
viewId = DRAWER_ITEM_GRADES,
|
||||
addedDate = grade.addedDate
|
||||
).addExtra("gradeId", grade.id).addExtra("gradesSubjectId", grade.subjectId)
|
||||
}
|
||||
|
||||
for (notice in app.db.noticeDao().getNotNotifiedNow(profileId)) {
|
||||
val noticeTypeStr = if (notice.type == Notice.TYPE_POSITIVE) app.getString(R.string.notification_notice_praise) else if (notice.type == Notice.TYPE_NEGATIVE) app.getString(R.string.notification_notice_warning) else app.getString(R.string.notification_notice_new)
|
||||
val text = app.getString(R.string.notification_notice_format, noticeTypeStr, notice.teacherFullName, Date.fromMillis(notice.addedDate).formattedString)
|
||||
data.notifications += Notification(
|
||||
title = app.getNotificationTitle(TYPE_NEW_NOTICE),
|
||||
text = text,
|
||||
type = TYPE_NEW_NOTICE,
|
||||
profileId = profileId,
|
||||
profileName = profileName,
|
||||
viewId = DRAWER_ITEM_BEHAVIOUR,
|
||||
addedDate = notice.addedDate
|
||||
).addExtra("noticeId", notice.id)
|
||||
}
|
||||
|
||||
for (attendance in app.db.attendanceDao().getNotNotifiedNow(profileId)) {
|
||||
var attendanceTypeStr = app.getString(R.string.notification_type_attendance)
|
||||
when (attendance.type) {
|
||||
Attendance.TYPE_ABSENT -> attendanceTypeStr = app.getString(R.string.notification_absence)
|
||||
Attendance.TYPE_ABSENT_EXCUSED -> attendanceTypeStr = app.getString(R.string.notification_absence_excused)
|
||||
Attendance.TYPE_BELATED -> attendanceTypeStr = app.getString(R.string.notification_belated)
|
||||
Attendance.TYPE_BELATED_EXCUSED -> attendanceTypeStr = app.getString(R.string.notification_belated_excused)
|
||||
Attendance.TYPE_RELEASED -> attendanceTypeStr = app.getString(R.string.notification_release)
|
||||
}
|
||||
val text = app.getString(
|
||||
if (attendance.subjectLongName.isNullOrEmpty())
|
||||
R.string.notification_attendance_no_lesson_format
|
||||
else
|
||||
R.string.notification_attendance_format,
|
||||
attendanceTypeStr,
|
||||
attendance.subjectLongName,
|
||||
attendance.lessonDate.formattedString
|
||||
)
|
||||
data.notifications += Notification(
|
||||
title = app.getNotificationTitle(TYPE_NEW_ATTENDANCE),
|
||||
text = text,
|
||||
type = TYPE_NEW_ATTENDANCE,
|
||||
profileId = profileId,
|
||||
profileName = profileName,
|
||||
viewId = DRAWER_ITEM_ATTENDANCE,
|
||||
addedDate = attendance.addedDate
|
||||
).addExtra("attendanceId", attendance.id).addExtra("attendanceSubjectId", attendance.subjectId)
|
||||
}
|
||||
|
||||
for (announcement in app.db.announcementDao().getNotNotifiedNow(profileId)) {
|
||||
val text = app.context.getString(R.string.notification_announcement_format, announcement.subject)
|
||||
data.notifications += Notification(
|
||||
title = app.getNotificationTitle(TYPE_NEW_ANNOUNCEMENT),
|
||||
text = text,
|
||||
type = TYPE_NEW_ANNOUNCEMENT,
|
||||
profileId = profileId,
|
||||
profileName = profileName,
|
||||
viewId = DRAWER_ITEM_ANNOUNCEMENTS,
|
||||
addedDate = announcement.addedDate
|
||||
).addExtra("announcementId", announcement.id)
|
||||
}
|
||||
|
||||
for (message in app.db.messageDao().getReceivedNotNotifiedNow(profileId)) {
|
||||
val text = app.context.getString(R.string.notification_message_format, message.senderFullName, message.subject)
|
||||
data.notifications += Notification(
|
||||
title = app.getNotificationTitle(TYPE_NEW_MESSAGE),
|
||||
text = text,
|
||||
type = TYPE_NEW_MESSAGE,
|
||||
profileId = profileId,
|
||||
profileName = profileName,
|
||||
viewId = DRAWER_ITEM_MESSAGES,
|
||||
addedDate = message.addedDate
|
||||
).addExtra("messageType", Message.TYPE_RECEIVED.toLong()).addExtra("messageId", message.id)
|
||||
}
|
||||
|
||||
val luckyNumbers = app.db.luckyNumberDao().getNotNotifiedNow(profileId)
|
||||
luckyNumbers?.removeAll { it.date < today }
|
||||
luckyNumbers?.forEach { luckyNumber ->
|
||||
val text = when (luckyNumber.date.value) {
|
||||
todayValue -> // LN for today
|
||||
app.getString(if (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) R.string.notification_lucky_number_yours_format else R.string.notification_lucky_number_format, luckyNumber.number)
|
||||
todayValue + 1 -> // LN for tomorrow
|
||||
app.getString(if (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) R.string.notification_lucky_number_yours_tomorrow_format else R.string.notification_lucky_number_tomorrow_format, luckyNumber.number)
|
||||
else -> // LN for later
|
||||
app.getString(if (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) R.string.notification_lucky_number_yours_later_format else R.string.notification_lucky_number_later_format, luckyNumber.date.formattedString, luckyNumber.number)
|
||||
}
|
||||
data.notifications += Notification(
|
||||
title = app.getNotificationTitle(TYPE_LUCKY_NUMBER),
|
||||
text = text,
|
||||
type = TYPE_LUCKY_NUMBER,
|
||||
profileId = profileId,
|
||||
profileName = profileName,
|
||||
viewId = DRAWER_ITEM_HOME,
|
||||
addedDate = luckyNumber.addedDate
|
||||
)
|
||||
}
|
||||
|
||||
data.db.metadataDao().setAllNotified(profileId, true)
|
||||
}}
|
||||
}
|
@ -35,6 +35,7 @@ class EdziennikNotification(val context: Context) {
|
||||
|
||||
private var errorCount = 0
|
||||
private var criticalErrorCount = 0
|
||||
var serviceClosed = false
|
||||
|
||||
private fun cancelPendingIntent(taskId: Int): PendingIntent {
|
||||
val intent = Intent("pl.szczodrzynski.edziennik.SZKOLNY_MAIN")
|
||||
@ -134,6 +135,8 @@ class EdziennikNotification(val context: Context) {
|
||||
}
|
||||
|
||||
fun post() {
|
||||
if (serviceClosed)
|
||||
return
|
||||
notificationManager.notify(NOTIFICATION_ID, notification)
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,6 @@ fun Data.prepareFor(loginMethods: List<LoginMethod>, loginMethodId: Int) {
|
||||
possibleLoginMethods += it.loginMethodId
|
||||
}
|
||||
|
||||
targetEndpointIds.clear()
|
||||
targetLoginMethodIds.clear()
|
||||
|
||||
// check the login method for any dependencies
|
||||
|
@ -51,6 +51,7 @@ const val ERROR_REQUEST_FAILURE_SSL_ERROR = 63
|
||||
const val ERROR_RESPONSE_EMPTY = 100
|
||||
const val ERROR_LOGIN_DATA_MISSING = 101
|
||||
const val ERROR_PROFILE_MISSING = 105
|
||||
const val ERROR_PROFILE_ARCHIVED = 106
|
||||
const val ERROR_INVALID_LOGIN_MODE = 110
|
||||
const val ERROR_LOGIN_METHOD_NOT_SATISFIED = 111
|
||||
const val ERROR_NOT_IMPLEMENTED = 112
|
||||
@ -129,6 +130,8 @@ const val ERROR_MOBIDZIENNIK_WEB_NO_SESSION_VALUE = 216
|
||||
const val ERROR_MOBIDZIENNIK_WEB_NO_SERVER_ID = 213
|
||||
const val ERROR_MOBIDZIENNIK_WEB_INVALID_RESPONSE = 214
|
||||
const val ERROR_LOGIN_MOBIDZIENNIK_WEB_NO_SESSION_ID = 215
|
||||
const val ERROR_LOGIN_MOBIDZIENNIK_API2_INVALID_LOGIN = 216
|
||||
const val ERROR_LOGIN_MOBIDZIENNIK_API2_OTHER = 217
|
||||
|
||||
const val ERROR_LOGIN_VULCAN_INVALID_SYMBOL = 301
|
||||
const val ERROR_LOGIN_VULCAN_INVALID_TOKEN = 302
|
||||
|
@ -41,6 +41,7 @@ internal const val FEATURE_PUSH_CONFIG = 120
|
||||
object Features {
|
||||
private fun getAllNecessary(): List<Int> = listOf(
|
||||
FEATURE_ALWAYS_NEEDED,
|
||||
FEATURE_PUSH_CONFIG,
|
||||
FEATURE_STUDENT_INFO,
|
||||
FEATURE_STUDENT_NUMBER,
|
||||
FEATURE_SCHOOL_INFO,
|
||||
|
@ -11,6 +11,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginApi
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginMessages
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginPortal
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLoginSynergia
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.login.MobidziennikLoginApi2
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.login.MobidziennikLoginWeb
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.template.login.TemplateLoginApi
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.template.login.TemplateLoginWeb
|
||||
@ -86,11 +87,11 @@ const val LOGIN_METHOD_MOBIDZIENNIK_API2 = 300
|
||||
val mobidziennikLoginMethods = listOf(
|
||||
LoginMethod(LOGIN_TYPE_MOBIDZIENNIK, LOGIN_METHOD_MOBIDZIENNIK_WEB, MobidziennikLoginWeb::class.java)
|
||||
.withIsPossible { _, _ -> true }
|
||||
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }/*,
|
||||
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED },
|
||||
|
||||
LoginMethod(LOGIN_TYPE_MOBIDZIENNIK, LOGIN_METHOD_MOBIDZIENNIK_API2, MobidziennikLoginApi2::class.java)
|
||||
.withIsPossible { _, loginStore -> loginStore.hasLoginData("email") }
|
||||
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }*/
|
||||
.withIsPossible { profile, _ -> profile?.hasStudentData("email") == true }
|
||||
.withRequiredLoginMethod { _, _ -> LOGIN_METHOD_NOT_NEEDED }
|
||||
)
|
||||
|
||||
const val LOGIN_TYPE_VULCAN = 4
|
||||
|
@ -74,7 +74,7 @@ object Regexes {
|
||||
"""id="ctl00_CzyRodzic" value="([01])" />""".toRegex()
|
||||
}
|
||||
val IDZIENNIK_LOGIN_FIRST_SCHOOL_YEAR by lazy {
|
||||
"""name="ctl00\\${'$'}dxComboRokSzkolny".+?selected="selected".*?value="([0-9]+)">([0-9]+)/([0-9]+)<""".toRegex(DOT_MATCHES_ALL)
|
||||
"""name="ctl00\${"$"}dxComboRokSzkolny".+?selected="selected".*?value="([0-9]+)">([0-9]+)/([0-9]+)<""".toRegex(DOT_MATCHES_ALL)
|
||||
}
|
||||
val IDZIENNIK_LOGIN_FIRST_STUDENT_SELECT by lazy {
|
||||
"""<select.*?name="ctl00\${"$"}dxComboUczniowie".*?</select>""".toRegex(DOT_MATCHES_ALL)
|
||||
|
@ -1,4 +1,8 @@
|
||||
package pl.szczodrzynski.edziennik.data.api.task
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-1-16.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
@ -12,11 +16,13 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.template.Template
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.Vulcan
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.api.task.IApiTask
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
|
||||
open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTask(profileId) {
|
||||
companion object {
|
||||
@ -57,6 +63,10 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa
|
||||
private var edziennikInterface: EdziennikInterface? = null
|
||||
|
||||
internal fun run(app: App, taskCallback: EdziennikCallback) {
|
||||
if (profile?.archived == true) {
|
||||
taskCallback.onError(ApiError(TAG, ERROR_PROFILE_ARCHIVED))
|
||||
return
|
||||
}
|
||||
edziennikInterface = when (loginStore.type) {
|
||||
LOGIN_TYPE_LIBRUS -> Librus(app, profile, loginStore, taskCallback)
|
||||
LOGIN_TYPE_MOBIDZIENNIK -> Mobidziennik(app, profile, loginStore, taskCallback)
|
@ -7,11 +7,7 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.api.LOGIN_METHOD_EDUDZIENNIK_WEB
|
||||
import pl.szczodrzynski.edziennik.data.api.models.Data
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.EventType
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Subject
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.*
|
||||
|
||||
/**
|
||||
* Use http://patorjk.com/software/taag/#p=display&f=Big for the ascii art
|
||||
|
@ -15,12 +15,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.login.Edudzienn
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
|
||||
@ -41,9 +41,7 @@ class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStor
|
||||
|
||||
private fun completed() {
|
||||
data.saveData()
|
||||
data.notify {
|
||||
callback.onCompleted()
|
||||
}
|
||||
callback.onCompleted()
|
||||
}
|
||||
|
||||
/* _______ _ _ _ _ _
|
||||
|
@ -12,6 +12,7 @@ import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
open class EdudziennikWeb(open val data: DataEdudziennik) {
|
||||
companion object {
|
||||
@ -24,11 +25,11 @@ open class EdudziennikWeb(open val data: DataEdudziennik) {
|
||||
val profile
|
||||
get() = data.profile
|
||||
|
||||
fun webGet(tag: String, endpoint: String, xhr: Boolean = false, onSuccess: (text: String) -> Unit) {
|
||||
fun webGet(tag: String, endpoint: String, xhr: Boolean = false, semester: Int? = null, onSuccess: (text: String) -> Unit) {
|
||||
val url = "https://dziennikel.appspot.com/" + when (endpoint.endsWith('/') || endpoint.contains('?') || endpoint.isEmpty()) {
|
||||
true -> endpoint
|
||||
else -> "$endpoint/"
|
||||
}
|
||||
} + (semester?.let { "?semester=" + if(it == -1) "all" else it } ?: "")
|
||||
|
||||
d(tag, "Request: Edudziennik/Web - $url")
|
||||
|
||||
@ -40,6 +41,18 @@ open class EdudziennikWeb(open val data: DataEdudziennik) {
|
||||
return
|
||||
}
|
||||
|
||||
if (semester == null && url.contains("start")) {
|
||||
profile?.also { profile ->
|
||||
val cookies = data.app.cookieJar.getForDomain("dziennikel.appspot.com")
|
||||
val semesterCookie = cookies.firstOrNull { it.name() == "semester" }?.value()?.toIntOrNull()
|
||||
|
||||
semesterCookie?.let { data.currentSemester = it }
|
||||
|
||||
if (semesterCookie == 2 && profile.dateSemester2Start > Date.getToday())
|
||||
profile.dateSemester2Start = Date.getToday().stepForward(0, 0, -1)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
onSuccess(text)
|
||||
} catch (e: Exception) {
|
||||
@ -67,11 +80,6 @@ open class EdudziennikWeb(open val data: DataEdudziennik) {
|
||||
.name("sessionid")
|
||||
.value(data.webSessionId!!)
|
||||
.domain("dziennikel.appspot.com")
|
||||
.secure().httpOnly().build(),
|
||||
Cookie.Builder()
|
||||
.name("semester")
|
||||
.value((data.currentSemester).toString())
|
||||
.domain("dziennikel.appspot.com")
|
||||
.secure().httpOnly().build()
|
||||
))
|
||||
|
||||
|
@ -11,9 +11,9 @@ import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_ATTENDANCE_TYPES
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Attendance
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.get
|
||||
import pl.szczodrzynski.edziennik.singleOrNull
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
@ -26,7 +26,7 @@ class EdudziennikWebAttendance(override val data: DataEdudziennik,
|
||||
}
|
||||
|
||||
init { data.profile?.also { profile ->
|
||||
webGet(TAG, data.studentEndpoint + "Presence") { text ->
|
||||
webGet(TAG, data.studentEndpoint + "Presence", semester = -1) { text ->
|
||||
|
||||
val attendanceTypes = EDUDZIENNIK_ATTENDANCE_TYPES.find(text)?.get(1)?.split(',')?.map {
|
||||
val type = EDUDZIENNIK_ATTENDANCE_TYPE.find(it.trim())
|
||||
@ -69,7 +69,7 @@ class EdudziennikWebAttendance(override val data: DataEdudziennik,
|
||||
id,
|
||||
lesson?.displayTeacherId ?: -1,
|
||||
lesson?.displaySubjectId ?: -1,
|
||||
data.currentSemester,
|
||||
profile.currentSemester,
|
||||
name,
|
||||
date,
|
||||
lesson?.displayStartTime ?: startTime,
|
||||
@ -77,14 +77,16 @@ class EdudziennikWebAttendance(override val data: DataEdudziennik,
|
||||
)
|
||||
|
||||
data.attendanceList.add(attendanceObject)
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_ATTENDANCE,
|
||||
id,
|
||||
profile.empty,
|
||||
profile.empty,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
if(type != Attendance.TYPE_PRESENT) {
|
||||
data.metadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_ATTENDANCE,
|
||||
id,
|
||||
profile.empty,
|
||||
profile.empty,
|
||||
System.currentTimeMillis()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_ATTENDANCE, SYNC_ALWAYS)
|
||||
|
@ -13,9 +13,9 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_EXAMS
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
|
||||
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.get
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
@ -60,7 +60,7 @@ class EdudziennikWebExams(override val data: DataEdudziennik,
|
||||
startTime,
|
||||
topic,
|
||||
-1,
|
||||
eventType.id.toInt(),
|
||||
eventType.id,
|
||||
false,
|
||||
-1,
|
||||
subject.id,
|
||||
|
@ -13,10 +13,10 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_GRADES
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
|
||||
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade.*
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.get
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
@ -27,10 +27,18 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
|
||||
private const val TAG = "EdudziennikWebGrades"
|
||||
}
|
||||
|
||||
init { data.profile?.also { profile ->
|
||||
webGet(TAG, data.studentEndpoint + "start") { text ->
|
||||
val doc = Jsoup.parse(text)
|
||||
private var requestSemester: Int? = null
|
||||
|
||||
init {
|
||||
if (profile?.empty == true && data.currentSemester == 2) requestSemester = 1
|
||||
getGrades()
|
||||
}
|
||||
|
||||
private fun getGrades() { data.profile?.also { profile ->
|
||||
webGet(TAG, data.studentEndpoint + "start", semester = requestSemester) { text ->
|
||||
val semester = requestSemester ?: data.currentSemester
|
||||
|
||||
val doc = Jsoup.parse(text)
|
||||
val subjects = doc.select("#student_grades tbody").firstOrNull()?.children()
|
||||
|
||||
subjects?.forEach { subjectElement ->
|
||||
@ -107,7 +115,7 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
|
||||
name,
|
||||
value,
|
||||
if (gradeCountToAverage) weight else 0f,
|
||||
data.currentSemester,
|
||||
semester,
|
||||
teacher.id,
|
||||
subject.id
|
||||
).apply {
|
||||
@ -137,7 +145,7 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
|
||||
proposed,
|
||||
proposed.toFloatOrNull() ?: 0f,
|
||||
0f,
|
||||
data.currentSemester,
|
||||
semester,
|
||||
-1,
|
||||
subject.id
|
||||
).apply {
|
||||
@ -170,7 +178,7 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
|
||||
final,
|
||||
final.toFloatOrNull() ?: 0f,
|
||||
0f,
|
||||
data.currentSemester,
|
||||
semester,
|
||||
-1,
|
||||
subject.id
|
||||
).apply {
|
||||
@ -201,12 +209,17 @@ class EdudziennikWebGrades(override val data: DataEdudziennik,
|
||||
TYPE_SEMESTER1_FINAL,
|
||||
TYPE_SEMESTER2_FINAL
|
||||
).map {
|
||||
DataRemoveModel.Grades.semesterWithType(data.currentSemester, it)
|
||||
DataRemoveModel.Grades.semesterWithType(semester, it)
|
||||
})
|
||||
}
|
||||
|
||||
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_GRADES, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
if (profile.empty && requestSemester == 1 && data.currentSemester == 2) {
|
||||
requestSemester = null
|
||||
getGrades()
|
||||
} else {
|
||||
data.setSyncNext(ENDPOINT_EDUDZIENNIK_WEB_GRADES, SYNC_ALWAYS)
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}}
|
||||
} ?: onSuccess() }
|
||||
}
|
||||
|
@ -10,9 +10,9 @@ import pl.szczodrzynski.edziennik.data.api.Regexes.EDUDZIENNIK_NOTE_ID
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.DataEdudziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.ENDPOINT_EDUDZIENNIK_WEB_NOTES
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notice
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.get
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
@ -42,7 +42,7 @@ class EdudziennikWebNotes(override val data: DataEdudziennik,
|
||||
profileId,
|
||||
id,
|
||||
description,
|
||||
data.currentSemester,
|
||||
profile.currentSemester,
|
||||
Notice.TYPE_NEUTRAL,
|
||||
teacher.id
|
||||
)
|
||||
|
@ -13,7 +13,6 @@ import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.getUnixDate
|
||||
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class EdudziennikLoginWeb(val data: DataEdudziennik, val onSuccess: () -> Unit) {
|
||||
companion object {
|
||||
@ -62,7 +61,6 @@ class EdudziennikLoginWeb(val data: DataEdudziennik, val onSuccess: () -> Unit)
|
||||
|
||||
val cookies = data.app.cookieJar.getForDomain("dziennikel.appspot.com")
|
||||
val sessionId = cookies.firstOrNull { it.name() == "sessionid" }?.value()
|
||||
val semester = cookies.firstOrNull { it.name() == "semester" }?.value()?.toIntOrNull()
|
||||
|
||||
if (sessionId == null) {
|
||||
data.error(ApiError(TAG, ERROR_LOGIN_EDUDZIENNIK_WEB_NO_SESSION_ID)
|
||||
@ -72,14 +70,6 @@ class EdudziennikLoginWeb(val data: DataEdudziennik, val onSuccess: () -> Unit)
|
||||
}
|
||||
|
||||
data.webSessionId = sessionId
|
||||
|
||||
if (data.profile != null && semester != null) {
|
||||
data.currentSemester = semester
|
||||
|
||||
if (semester == 2 && data.profile.dateSemester2Start > Date.getToday())
|
||||
data.profile.dateSemester2Start = Date.getToday().stepForward(0, 0, -1)
|
||||
}
|
||||
|
||||
data.webSessionIdExpiryTime = response.getUnixDate() + 45 * 60 /* 45 min */
|
||||
onSuccess()
|
||||
}
|
||||
|
@ -17,12 +17,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login.IdziennikLo
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
|
||||
@ -43,9 +43,7 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
|
||||
|
||||
private fun completed() {
|
||||
data.saveData()
|
||||
data.notify {
|
||||
callback.onCompleted()
|
||||
}
|
||||
callback.onCompleted()
|
||||
}
|
||||
|
||||
/* _______ _ _ _ _ _
|
||||
|
@ -19,12 +19,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLogin
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
|
||||
@ -45,9 +45,7 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
|
||||
private fun completed() {
|
||||
data.saveData()
|
||||
data.notify {
|
||||
callback.onCompleted()
|
||||
}
|
||||
callback.onCompleted()
|
||||
}
|
||||
|
||||
/* _______ _ _ _ _ _
|
||||
|
@ -25,9 +25,10 @@ class LibrusApiClassrooms(override val data: DataLibrus,
|
||||
val id = classroom.getLong("Id") ?: return@forEach
|
||||
val name = classroom.getString("Name")?.toLowerCase(Locale.getDefault()) ?: ""
|
||||
val symbol = classroom.getString("Symbol")?.toLowerCase(Locale.getDefault()) ?: ""
|
||||
val nameShort = name.split(" ").onEach { it[0] }.joinToString()
|
||||
val nameShort = name.fixWhiteSpaces().split(" ").onEach { it[0] }.joinToString()
|
||||
val symbolParts = symbol.fixWhiteSpaces().split(" ")
|
||||
|
||||
val friendlyName = if (name != symbol && !name.contains(symbol) && !nameShort.contains(symbol)) {
|
||||
val friendlyName = if (name != symbol && !name.contains(symbol) && !name.containsAll(symbolParts) && !nameShort.contains(symbol)) {
|
||||
classroom.getString("Symbol") + " " + classroom.getString("Name")
|
||||
}
|
||||
else {
|
||||
|
@ -10,9 +10,9 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.ENDPOINT_LIBRUS_API_EVENTS
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
|
||||
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
@ -34,7 +34,7 @@ class LibrusApiEvents(override val data: DataLibrus,
|
||||
val id = event.getLong("Id") ?: return@forEach
|
||||
val eventDate = Date.fromY_m_d(event.getString("Date"))
|
||||
val topic = event.getString("Content") ?: ""
|
||||
val type = event.getJsonObject("Category")?.getInt("Id") ?: -1
|
||||
val type = event.getJsonObject("Category")?.getLong("Id") ?: -1
|
||||
val teacherId = event.getJsonObject("CreatedBy")?.getLong("Id") ?: -1
|
||||
val subjectId = event.getJsonObject("Subject")?.getLong("Id") ?: -1
|
||||
val teamId = event.getJsonObject("Class")?.getLong("Id") ?: -1
|
||||
|
@ -23,6 +23,10 @@ class DataMobidziennik(app: App, profile: Profile?, loginStore: LoginStore) : Da
|
||||
&& webSessionKey.isNotNullNorEmpty()
|
||||
&& webServerId.isNotNullNorEmpty()
|
||||
|
||||
fun isApi2LoginValid() = loginEmail.isNotNullNorEmpty()
|
||||
&& loginId.isNotNullNorEmpty()
|
||||
&& globalId.isNotNullNorEmpty()
|
||||
|
||||
override fun satisfyLoginMethods() {
|
||||
loginMethods.clear()
|
||||
if (isWebLoginValid()) {
|
||||
@ -44,11 +48,6 @@ class DataMobidziennik(app: App, profile: Profile?, loginStore: LoginStore) : Da
|
||||
get() { mLoginServerName = mLoginServerName ?: loginStore.getLoginData("serverName", null); return mLoginServerName }
|
||||
set(value) { loginStore.putLoginData("serverName", value); mLoginServerName = value }
|
||||
|
||||
private var mLoginEmail: String? = null
|
||||
var loginEmail: String?
|
||||
get() { mLoginEmail = mLoginEmail ?: loginStore.getLoginData("email", null); return mLoginEmail }
|
||||
set(value) { loginStore.putLoginData("email", value); mLoginEmail = value }
|
||||
|
||||
private var mLoginUsername: String? = null
|
||||
var loginUsername: String?
|
||||
get() { mLoginUsername = mLoginUsername ?: loginStore.getLoginData("username", null); return mLoginUsername }
|
||||
@ -90,6 +89,48 @@ class DataMobidziennik(app: App, profile: Profile?, loginStore: LoginStore) : Da
|
||||
get() { mWebSessionIdExpiryTime = mWebSessionIdExpiryTime ?: loginStore.getLoginData("sessionIDTime", 0L); return mWebSessionIdExpiryTime ?: 0L }
|
||||
set(value) { loginStore.putLoginData("sessionIDTime", value); mWebSessionIdExpiryTime = value }
|
||||
|
||||
/* _____ _____ ___
|
||||
/\ | __ \_ _| |__ \
|
||||
/ \ | |__) || | ) |
|
||||
/ /\ \ | ___/ | | / /
|
||||
/ ____ \| | _| |_ / /_
|
||||
/_/ \_\_| |_____| |___*/
|
||||
/**
|
||||
* A global ID (whatever it is) used in API 2
|
||||
* and Firebase push from Mobidziennik.
|
||||
*/
|
||||
var globalId: String?
|
||||
get() { mGlobalId = mGlobalId ?: profile?.getStudentData("globalId", null); return mGlobalId }
|
||||
set(value) { profile?.putStudentData("globalId", value) ?: return; mGlobalId = value }
|
||||
private var mGlobalId: String? = null
|
||||
|
||||
/**
|
||||
* User's email that may or may not
|
||||
* be retrieved from Web by [MobidziennikWebAccountEmail].
|
||||
* Used to log in to API 2.
|
||||
*/
|
||||
var loginEmail: String?
|
||||
get() { mLoginEmail = mLoginEmail ?: profile?.getStudentData("email", null); return mLoginEmail }
|
||||
set(value) { profile?.putStudentData("email", value); mLoginEmail = value }
|
||||
private var mLoginEmail: String? = null
|
||||
|
||||
/**
|
||||
* A login ID used in the API 2.
|
||||
* Looks more or less like "7063@2019@zslpoznan".
|
||||
*/
|
||||
var loginId: String?
|
||||
get() { mLoginId = mLoginId ?: profile?.getStudentData("loginId", null); return mLoginId }
|
||||
set(value) { profile?.putStudentData("loginId", value) ?: return; mLoginId = value }
|
||||
private var mLoginId: String? = null
|
||||
|
||||
/**
|
||||
* No need to explain.
|
||||
*/
|
||||
var ciasteczkoAutoryzacji: String?
|
||||
get() { mCiasteczkoAutoryzacji = mCiasteczkoAutoryzacji ?: profile?.getStudentData("ciasteczkoAutoryzacji", null); return mCiasteczkoAutoryzacji }
|
||||
set(value) { profile?.putStudentData("ciasteczkoAutoryzacji", value) ?: return; mCiasteczkoAutoryzacji = value }
|
||||
private var mCiasteczkoAutoryzacji: String? = null
|
||||
|
||||
|
||||
override fun saveData() {
|
||||
super.saveData()
|
||||
|
@ -17,12 +17,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.login.Mobidzie
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
|
||||
@ -45,9 +45,7 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto
|
||||
|
||||
private fun completed() {
|
||||
data.saveData()
|
||||
data.notify {
|
||||
callback.onCompleted()
|
||||
}
|
||||
callback.onCompleted()
|
||||
}
|
||||
|
||||
/* _______ _ _ _ _ _
|
||||
|
@ -22,16 +22,16 @@ const val ENDPOINT_MOBIDZIENNIK_API2_MAIN = 3000
|
||||
val MobidziennikFeatures = listOf(
|
||||
// always synced
|
||||
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_ALWAYS_NEEDED, listOf(
|
||||
ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB,
|
||||
ENDPOINT_MOBIDZIENNIK_WEB_ACCOUNT_EMAIL to LOGIN_METHOD_MOBIDZIENNIK_WEB
|
||||
ENDPOINT_MOBIDZIENNIK_API_MAIN to LOGIN_METHOD_MOBIDZIENNIK_WEB
|
||||
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB)), // TODO divide features into separate view IDs (all with API_MAIN)
|
||||
|
||||
// push config
|
||||
/*Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_PUSH_CONFIG, listOf(
|
||||
Feature(LOGIN_TYPE_MOBIDZIENNIK, FEATURE_PUSH_CONFIG, listOf(
|
||||
ENDPOINT_MOBIDZIENNIK_WEB_ACCOUNT_EMAIL to LOGIN_METHOD_MOBIDZIENNIK_WEB,
|
||||
ENDPOINT_MOBIDZIENNIK_API2_MAIN to LOGIN_METHOD_MOBIDZIENNIK_API2
|
||||
), listOf(LOGIN_METHOD_MOBIDZIENNIK_API2)).withShouldSync { data ->
|
||||
data.app.appConfig.fcmTokens[LOGIN_TYPE_MOBIDZIENNIK]?.second?.contains(data.profileId) == false
|
||||
},*/
|
||||
), listOf(LOGIN_METHOD_MOBIDZIENNIK_WEB, LOGIN_METHOD_MOBIDZIENNIK_API2)).withShouldSync { data ->
|
||||
!data.app.config.sync.tokenMobidziennikList.contains(data.profileId)
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
@ -7,6 +7,7 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.api.MobidziennikApi
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.api2.MobidziennikApi2Main
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.web.MobidziennikWebCalendar
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.web.MobidziennikWebGrades
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.web.MobidziennikWebMessagesAll
|
||||
@ -44,6 +45,10 @@ class MobidziennikData(val data: DataMobidziennik, val onSuccess: () -> Unit) {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_data)
|
||||
MobidziennikApi(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_MOBIDZIENNIK_API2_MAIN -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_push_config)
|
||||
MobidziennikApi2Main(data, onSuccess)
|
||||
}
|
||||
ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_INBOX -> {
|
||||
data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox)
|
||||
MobidziennikWebMessagesInbox(data) { onSuccess() }
|
||||
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-1-12.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.api2
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
||||
import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.ENDPOINT_MOBIDZIENNIK_API2_MAIN
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.login.MobidziennikLoginApi2
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.getJsonObject
|
||||
import pl.szczodrzynski.edziennik.getString
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
|
||||
class MobidziennikApi2Main(val data: DataMobidziennik,
|
||||
val onSuccess: () -> Unit) {
|
||||
companion object {
|
||||
private const val TAG = "MobidziennikApi2Main"
|
||||
}
|
||||
|
||||
val profileId
|
||||
get() = data.profile?.id ?: -1
|
||||
|
||||
val profile
|
||||
get() = data.profile
|
||||
|
||||
init {
|
||||
Utils.d(TAG, "Request: Mobidziennik/Api2/Main - https://${data.loginServerName}.mobidziennik.pl/api2/logowanie")
|
||||
|
||||
val callback = object : JsonCallbackHandler() {
|
||||
override fun onSuccess(json: JsonObject?, response: Response?) {
|
||||
if (json == null) {
|
||||
data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
|
||||
json.getJsonObject("error")?.let {
|
||||
val text = it.getString("type") ?: it.getString("message")
|
||||
when (text) {
|
||||
"LOGIN_ERROR" -> ERROR_LOGIN_MOBIDZIENNIK_API2_INVALID_LOGIN
|
||||
// TODO other error types
|
||||
else -> ERROR_LOGIN_MOBIDZIENNIK_API2_OTHER
|
||||
}.let { errorCode ->
|
||||
data.error(ApiError(TAG, errorCode)
|
||||
.withApiResponse(text)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
val user = json.getJsonObject("user")
|
||||
data.ciasteczkoAutoryzacji = user.getString("auth_key")
|
||||
|
||||
// sync always: this endpoint has .shouldSync set
|
||||
data.setSyncNext(ENDPOINT_MOBIDZIENNIK_API2_MAIN, SYNC_ALWAYS)
|
||||
data.app.config.sync.tokenMobidziennikList =
|
||||
data.app.config.sync.tokenMobidziennikList + profileId
|
||||
onSuccess()
|
||||
}
|
||||
|
||||
override fun onFailure(response: Response?, throwable: Throwable?) {
|
||||
data.error(ApiError(TAG, ERROR_REQUEST_FAILURE)
|
||||
.withResponse(response)
|
||||
.withThrowable(throwable))
|
||||
}
|
||||
}
|
||||
|
||||
Request.builder()
|
||||
.url("https://${data.loginServerName}.mobidziennik.pl/api2/logowanie")
|
||||
.userAgent(MOBIDZIENNIK_USER_AGENT)
|
||||
.contentType("application/x-www-form-urlencoded; charset=UTF-8")
|
||||
.addParameter("login", data.loginId)
|
||||
.addParameter("email", data.loginEmail)
|
||||
.addParameter("haslo", data.loginPassword)
|
||||
.addParameter("device", MobidziennikLoginApi2.getDevice(data.app).toString())
|
||||
.apply {
|
||||
data.ciasteczkoAutoryzacji?.let { addParameter("ciasteczko_autoryzacji", it) }
|
||||
}
|
||||
.post()
|
||||
.callback(callback)
|
||||
.build()
|
||||
.enqueue()
|
||||
}
|
||||
}
|
@ -9,16 +9,16 @@ import pl.szczodrzynski.edziennik.data.api.Regexes
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_CALENDAR
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.getString
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.crc16
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import java.util.*
|
||||
|
||||
class MobidziennikWebCalendar(override val data: DataMobidziennik,
|
||||
val onSuccess: () -> Unit) : MobidziennikWeb(data) {
|
||||
val onSuccess: () -> Unit) : MobidziennikWeb(data) {
|
||||
companion object {
|
||||
private const val TAG = "MobidziennikWebCalendar"
|
||||
}
|
||||
|
@ -12,9 +12,9 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.Mobidzien
|
||||
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageRecipientFull
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.fixName
|
||||
import pl.szczodrzynski.edziennik.get
|
||||
import pl.szczodrzynski.edziennik.singleOrNull
|
||||
@ -25,7 +25,7 @@ import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
class MobidziennikWebGetMessage(
|
||||
override val data: DataMobidziennik,
|
||||
private val message: MessageFull,
|
||||
val onSuccess: () -> Unit) : MobidziennikWeb(data) {
|
||||
val onSuccess: () -> Unit) : MobidziennikWeb(data) {
|
||||
companion object {
|
||||
private const val TAG = "MobidziennikWebGetMessage"
|
||||
}
|
||||
|
@ -11,9 +11,9 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidzienn
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_GRADES
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
|
||||
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Grade
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.fixWhiteSpaces
|
||||
import pl.szczodrzynski.edziennik.get
|
||||
import pl.szczodrzynski.edziennik.singleOrNull
|
||||
@ -21,7 +21,7 @@ import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
class MobidziennikWebGrades(override val data: DataMobidziennik,
|
||||
val onSuccess: () -> Unit) : MobidziennikWeb(data) {
|
||||
val onSuccess: () -> Unit) : MobidziennikWeb(data) {
|
||||
companion object {
|
||||
private const val TAG = "MobidziennikWebGrades"
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import pl.szczodrzynski.edziennik.singleOrNull
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class MobidziennikWebMessagesAll(override val data: DataMobidziennik,
|
||||
val onSuccess: () -> Unit) : MobidziennikWeb(data) {
|
||||
val onSuccess: () -> Unit) : MobidziennikWeb(data) {
|
||||
companion object {
|
||||
private const val TAG = "MobidziennikWebMessagesAll"
|
||||
}
|
||||
|
@ -8,16 +8,16 @@ import org.jsoup.Jsoup
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.ENDPOINT_MOBIDZIENNIK_WEB_MESSAGES_INBOX
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.fixName
|
||||
import pl.szczodrzynski.edziennik.singleOrNull
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class MobidziennikWebMessagesInbox(override val data: DataMobidziennik,
|
||||
val onSuccess: () -> Unit) : MobidziennikWeb(data) {
|
||||
val onSuccess: () -> Unit) : MobidziennikWeb(data) {
|
||||
companion object {
|
||||
private const val TAG = "MobidziennikWebMessagesInbox"
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.Mobidzien
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
|
||||
class MobidziennikWebNotices(override val data: DataMobidziennik,
|
||||
val onSuccess: () -> Unit) : MobidziennikWeb(data) {
|
||||
val onSuccess: () -> Unit) : MobidziennikWeb(data) {
|
||||
companion object {
|
||||
private const val TAG = "MobidziennikWebNotices"
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ class MobidziennikLogin(val data: DataMobidziennik, val onSuccess: () -> Unit) {
|
||||
}
|
||||
LOGIN_METHOD_MOBIDZIENNIK_API2 -> {
|
||||
data.startProgress(R.string.edziennik_progress_login_mobidziennik_api2)
|
||||
//MobidziennikLoginApi2(data) { onSuccess(loginMethodId) }
|
||||
MobidziennikLoginApi2(data) { onSuccess(loginMethodId) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-1-12.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.login
|
||||
|
||||
import android.os.Build
|
||||
import com.google.gson.JsonObject
|
||||
import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.api.*
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
|
||||
class MobidziennikLoginApi2(val data: DataMobidziennik, val onSuccess: () -> Unit) {
|
||||
companion object {
|
||||
private const val TAG = "MobidziennikLoginApi2"
|
||||
|
||||
fun getDevice(app: App) = JsonObject(
|
||||
"available" to true,
|
||||
"platform" to "Android",
|
||||
"version" to Build.VERSION.RELEASE,
|
||||
"uuid" to app.deviceId,
|
||||
"cordova" to "7.1.2",
|
||||
"model" to "${Build.MANUFACTURER} ${Build.MODEL}",
|
||||
"manufacturer" to "Aplikacja Szkolny.eu",
|
||||
"isVirtual" to false,
|
||||
"serial" to try { System.getProperty("ro.serialno") ?: System.getProperty("ro.boot.serialno") } catch (_: Exception) { Build.UNKNOWN },
|
||||
"appVersion" to "10.6, 2020.01.09-12.15.53",
|
||||
"pushRegistrationId" to app.config.sync.tokenMobidziennik
|
||||
)
|
||||
}
|
||||
|
||||
init { run {
|
||||
if (data.isApi2LoginValid()) {
|
||||
onSuccess()
|
||||
}
|
||||
else {
|
||||
if (data.loginServerName.isNotNullNorEmpty() && data.loginEmail.isNotNullNorEmpty() && data.loginPassword.isNotNullNorEmpty()) {
|
||||
loginWithCredentials()
|
||||
}
|
||||
else {
|
||||
data.error(ApiError(TAG, ERROR_LOGIN_DATA_MISSING))
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
private fun loginWithCredentials() {
|
||||
Utils.d(TAG, "Request: Mobidziennik/Login/Api2 - https://mobidziennik.pl/logowanie")
|
||||
|
||||
val callback = object : JsonCallbackHandler() {
|
||||
override fun onSuccess(json: JsonObject?, response: Response?) {
|
||||
if (json == null) {
|
||||
data.error(ApiError(TAG, ERROR_RESPONSE_EMPTY)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
|
||||
json.getJsonObject("error")?.let {
|
||||
val text = it.getString("type") ?: it.getString("message")
|
||||
when (text) {
|
||||
"LOGIN_ERROR" -> ERROR_LOGIN_MOBIDZIENNIK_API2_INVALID_LOGIN
|
||||
// TODO other error types
|
||||
else -> ERROR_LOGIN_MOBIDZIENNIK_API2_OTHER
|
||||
}.let { errorCode ->
|
||||
data.error(ApiError(TAG, errorCode)
|
||||
.withApiResponse(text)
|
||||
.withResponse(response))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
data.loginEmail = json.getString("email")
|
||||
data.globalId = json.getString("id_global")
|
||||
data.loginId = json.getString("login")
|
||||
onSuccess()
|
||||
}
|
||||
|
||||
override fun onFailure(response: Response?, throwable: Throwable?) {
|
||||
data.error(ApiError(TAG, ERROR_REQUEST_FAILURE)
|
||||
.withResponse(response)
|
||||
.withThrowable(throwable))
|
||||
}
|
||||
}
|
||||
|
||||
Request.builder()
|
||||
.url("https://mobidziennik.pl/logowanie")
|
||||
.userAgent(MOBIDZIENNIK_USER_AGENT)
|
||||
.contentType("application/x-www-form-urlencoded; charset=UTF-8")
|
||||
.addParameter("api2", true)
|
||||
.addParameter("email", data.loginEmail)
|
||||
.addParameter("haslo", data.loginPassword)
|
||||
.addParameter("device", getDevice(data.app).toString())
|
||||
.post()
|
||||
.callback(callback)
|
||||
.build()
|
||||
.enqueue()
|
||||
}
|
||||
}
|
@ -15,12 +15,12 @@ import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
|
||||
import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.api.prepare
|
||||
import pl.szczodrzynski.edziennik.data.api.templateLoginMethods
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
class Template(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
|
||||
@ -40,9 +40,7 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore,
|
||||
|
||||
private fun completed() {
|
||||
data.saveData()
|
||||
data.notify {
|
||||
callback.onCompleted()
|
||||
}
|
||||
callback.onCompleted()
|
||||
}
|
||||
|
||||
/* _______ _ _ _ _ _
|
||||
|
@ -18,12 +18,12 @@ import pl.szczodrzynski.edziennik.data.api.models.ApiError
|
||||
import pl.szczodrzynski.edziennik.data.api.prepare
|
||||
import pl.szczodrzynski.edziennik.data.api.prepareFor
|
||||
import pl.szczodrzynski.edziennik.data.api.vulcanLoginMethods
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
|
||||
@ -44,9 +44,7 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va
|
||||
|
||||
private fun completed() {
|
||||
data.saveData()
|
||||
data.notify {
|
||||
callback.onCompleted()
|
||||
}
|
||||
callback.onCompleted()
|
||||
}
|
||||
|
||||
/* _______ _ _ _ _ _
|
||||
|
@ -4,13 +4,15 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.api
|
||||
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import pl.szczodrzynski.edziennik.data.api.VULCAN_API_ENDPOINT_MESSAGES_CHANGE_STATUS
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi
|
||||
import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_SENT
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
|
||||
|
||||
class VulcanApiMessagesChangeStatus(
|
||||
override val data: DataVulcan,
|
||||
@ -22,40 +24,42 @@ class VulcanApiMessagesChangeStatus(
|
||||
}
|
||||
|
||||
init {
|
||||
data.profile?.also { profile ->
|
||||
apiGet(TAG, VULCAN_API_ENDPOINT_MESSAGES_CHANGE_STATUS, parameters = mapOf(
|
||||
"WiadomoscId" to messageObject.id,
|
||||
"FolderWiadomosci" to "Odebrane",
|
||||
"Status" to "Widoczna",
|
||||
"LoginId" to data.studentLoginId,
|
||||
"IdUczen" to data.studentId
|
||||
)) { _, _ ->
|
||||
apiGet(TAG, VULCAN_API_ENDPOINT_MESSAGES_CHANGE_STATUS, parameters = mapOf(
|
||||
"WiadomoscId" to messageObject.id,
|
||||
"FolderWiadomosci" to "Odebrane",
|
||||
"Status" to "Widoczna",
|
||||
"LoginId" to data.studentLoginId,
|
||||
"IdUczen" to data.studentId
|
||||
)) { _, _ ->
|
||||
|
||||
if (!messageObject.seen) {
|
||||
data.setSeenMetadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_MESSAGE,
|
||||
messageObject.id,
|
||||
true,
|
||||
true,
|
||||
messageObject.addedDate
|
||||
))
|
||||
}
|
||||
if (!messageObject.seen) {
|
||||
data.setSeenMetadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_MESSAGE,
|
||||
messageObject.id,
|
||||
true,
|
||||
true,
|
||||
messageObject.addedDate
|
||||
))
|
||||
|
||||
if (messageObject.type != TYPE_SENT) {
|
||||
val messageRecipientObject = MessageRecipient(
|
||||
profileId,
|
||||
-1,
|
||||
-1,
|
||||
System.currentTimeMillis(),
|
||||
messageObject.id
|
||||
)
|
||||
|
||||
data.messageRecipientList.add(messageRecipientObject)
|
||||
}
|
||||
|
||||
onSuccess()
|
||||
messageObject.seen = true
|
||||
}
|
||||
|
||||
if (messageObject.type != TYPE_SENT) {
|
||||
val messageRecipientObject = MessageRecipient(
|
||||
profileId,
|
||||
-1,
|
||||
-1,
|
||||
System.currentTimeMillis(),
|
||||
messageObject.id
|
||||
)
|
||||
|
||||
data.messageRecipientList.add(messageRecipientObject)
|
||||
}
|
||||
|
||||
EventBus.getDefault().postSticky(MessageGetEvent(messageObject))
|
||||
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,12 +9,8 @@ import pl.szczodrzynski.edziennik.data.api.VULCAN_API_ENDPOINT_MESSAGES_RECEIVED
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.ENDPOINT_VULCAN_API_MESSAGES_INBOX
|
||||
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.*
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
|
||||
import pl.szczodrzynski.edziennik.utils.Utils
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import kotlin.text.replace
|
||||
@ -88,7 +84,7 @@ class VulcanApiMessagesInbox(override val data: DataVulcan, val onSuccess: () ->
|
||||
|
||||
data.messageIgnoreList.add(messageObject)
|
||||
data.messageRecipientList.add(messageRecipientObject)
|
||||
data.metadataList.add(Metadata(
|
||||
data.setSeenMetadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_MESSAGE,
|
||||
id,
|
||||
|
@ -99,7 +99,7 @@ class VulcanApiMessagesSent(override val data: DataVulcan, val onSuccess: () ->
|
||||
)
|
||||
|
||||
data.messageIgnoreList.add(messageObject)
|
||||
data.metadataList.add(Metadata(
|
||||
data.setSeenMetadataList.add(Metadata(
|
||||
profileId,
|
||||
Metadata.TYPE_MESSAGE,
|
||||
id,
|
||||
|
@ -0,0 +1,7 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-1-18.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.events
|
||||
|
||||
class ProfileListEmptyEvent
|
@ -93,7 +93,7 @@ class ApiError(val tag: String, var errorCode: Int) {
|
||||
stackTrace = throwable?.stackTraceString,
|
||||
request = requestString,
|
||||
response = responseString,
|
||||
apiResponse = apiResponse,
|
||||
apiResponse = apiResponse ?: response?.parserErrorBody,
|
||||
isCritical = isCritical
|
||||
)
|
||||
}
|
||||
|
@ -194,7 +194,6 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
|
||||
if (profile == null)
|
||||
return // return on first login
|
||||
|
||||
profile.empty = false
|
||||
profile.userCode = generateUserCode()
|
||||
|
||||
db.profileDao().add(profile)
|
||||
@ -294,21 +293,6 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
|
||||
db.messageRecipientDao().addAllIgnore(messageRecipientIgnoreList)
|
||||
}
|
||||
|
||||
fun notify(onSuccess: () -> Unit) {
|
||||
if (profile == null) {
|
||||
onSuccess()
|
||||
return
|
||||
}
|
||||
try {
|
||||
DataNotifications(this)
|
||||
db.notificationDao().addAll(notifications)
|
||||
onSuccess()
|
||||
} catch (e: Exception) {
|
||||
error(ApiError(TAG, EXCEPTION_NOTIFY)
|
||||
.withThrowable(e))
|
||||
}
|
||||
}
|
||||
|
||||
fun setSyncNext(endpointId: Int, syncIn: Long? = null, viewId: Int? = null, syncAt: Long? = null) {
|
||||
EndpointTimer(profile?.id
|
||||
?: -1, endpointId).apply {
|
||||
@ -366,8 +350,8 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
|
||||
apiError.errorCode = when (apiError.throwable) {
|
||||
is UnknownHostException -> ERROR_REQUEST_FAILURE_HOSTNAME_NOT_FOUND
|
||||
is SSLException -> ERROR_REQUEST_FAILURE_SSL_ERROR
|
||||
is InterruptedIOException, is ConnectException -> ERROR_REQUEST_FAILURE_NO_INTERNET
|
||||
is SocketTimeoutException -> ERROR_REQUEST_FAILURE_TIMEOUT
|
||||
is InterruptedIOException, is ConnectException -> ERROR_REQUEST_FAILURE_NO_INTERNET
|
||||
else ->
|
||||
if (apiError.errorCode == ERROR_REQUEST_FAILURE)
|
||||
when (apiError.response?.code()) {
|
||||
|
@ -47,11 +47,11 @@ open class DataRemoveModel {
|
||||
}
|
||||
}
|
||||
|
||||
class Events(private val type: Int?, private val exceptType: Int?, private val exceptTypes: List<Int>?) : DataRemoveModel() {
|
||||
class Events(private val type: Long?, private val exceptType: Long?, private val exceptTypes: List<Long>?) : DataRemoveModel() {
|
||||
companion object {
|
||||
fun futureExceptType(exceptType: Int) = Events(null, exceptType, null)
|
||||
fun futureExceptTypes(exceptTypes: List<Int>) = Events(null, null, exceptTypes)
|
||||
fun futureWithType(type: Int) = Events(type, null, null)
|
||||
fun futureExceptType(exceptType: Long) = Events(null, exceptType, null)
|
||||
fun futureExceptTypes(exceptTypes: List<Long>) = Events(null, null, exceptTypes)
|
||||
fun futureWithType(type: Long) = Events(type, null, null)
|
||||
}
|
||||
|
||||
fun commit(profileId: Int, dao: EventDao) {
|
||||
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Kacper Ziubryniewicz 2019-12-13
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.szkolny
|
||||
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
|
||||
class Szkolny(val app: App, val callback: EdziennikCallback) {
|
||||
|
||||
private val api = SzkolnyApi(app)
|
||||
|
||||
fun sync(profileList: List<Profile>) {
|
||||
val profiles = profileList.filter { it.registration == Profile.REGISTRATION_ENABLED }
|
||||
if (profiles.isNotEmpty()) {
|
||||
val events = api.getEvents(profiles)
|
||||
|
||||
if (events.isNotEmpty()) {
|
||||
app.db.eventDao().addAll(events)
|
||||
app.db.metadataDao().addAllIgnore(events.map { event ->
|
||||
Metadata(
|
||||
event.profileId,
|
||||
Metadata.TYPE_EVENT,
|
||||
event.id,
|
||||
event.seen,
|
||||
event.notified,
|
||||
event.addedDate
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
completed()
|
||||
}
|
||||
|
||||
/*fun shareEvent(event: EventFull) {
|
||||
api.shareEvent(event)
|
||||
completed()
|
||||
}
|
||||
|
||||
fun unshareEvent(event: EventFull) {
|
||||
api.unshareEvent(event)
|
||||
completed()
|
||||
}*/
|
||||
|
||||
private fun completed() {
|
||||
callback.onCompleted()
|
||||
}
|
||||
}
|
@ -12,15 +12,14 @@ import pl.szczodrzynski.edziennik.BuildConfig
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.adapter.DateAdapter
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.adapter.TimeAdapter
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.SignatureInterceptor
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.request.ErrorReportRequest
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.request.EventShareRequest
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.request.ServerSyncRequest
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.request.WebPushRequest
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.request.*
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.response.ApiResponse
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.response.WebPushResponse
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Event
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.data.db.full.EventFull
|
||||
import pl.szczodrzynski.edziennik.md5
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
@ -56,9 +55,8 @@ class SzkolnyApi(val app: App) {
|
||||
api = retrofit.create()
|
||||
}
|
||||
|
||||
fun getEvents(profiles: List<Profile>): List<EventFull> {
|
||||
fun getEvents(profiles: List<Profile>, notifications: List<Notification>, blacklistedIds: List<Long>): List<EventFull> {
|
||||
val teams = app.db.teamDao().allNow
|
||||
val notifications = app.db.notificationDao().getNotPostedNow()
|
||||
|
||||
val response = api.serverSync(ServerSyncRequest(
|
||||
deviceId = app.deviceId,
|
||||
@ -88,8 +86,8 @@ class SzkolnyApi(val app: App) {
|
||||
val config = app.config.getFor(profile.id)
|
||||
val user = ServerSyncRequest.User(
|
||||
profile.userCode,
|
||||
profile.studentNameLong ?: "",
|
||||
profile.studentNameShort ?: "",
|
||||
profile.studentNameLong,
|
||||
profile.studentNameShort,
|
||||
profile.loginStoreType,
|
||||
teams.filter { it.profileId == profile.id }.map { it.code }
|
||||
)
|
||||
@ -108,17 +106,19 @@ class SzkolnyApi(val app: App) {
|
||||
val events = mutableListOf<EventFull>()
|
||||
|
||||
response?.data?.events?.forEach { event ->
|
||||
teams.filter { it.code == event.teamCode }.forEach { team ->
|
||||
val profile = profiles.firstOrNull { it.id == team.profileId }
|
||||
if (event.id in blacklistedIds)
|
||||
return@forEach
|
||||
teams.filter { it.code == event.teamCode }.onEach { team ->
|
||||
val profile = profiles.firstOrNull { it.id == team.profileId } ?: return@onEach
|
||||
|
||||
events.add(event.apply {
|
||||
events.add(EventFull(event).apply {
|
||||
profileId = team.profileId
|
||||
teamId = team.id
|
||||
addedManually = true
|
||||
seen = profile?.empty ?: false
|
||||
notified = profile?.empty ?: false
|
||||
seen = profile.empty
|
||||
notified = profile.empty
|
||||
|
||||
if (profile?.userCode == event.sharedBy) sharedBy = "self"
|
||||
if (profile.userCode == event.sharedBy) sharedBy = "self"
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -190,7 +190,19 @@ class SzkolnyApi(val app: App) {
|
||||
fun errorReport(errors: List<ErrorReportRequest.Error>): ApiResponse<Nothing>? {
|
||||
return api.errorReport(ErrorReportRequest(
|
||||
deviceId = app.deviceId,
|
||||
appVersion = BuildConfig.VERSION_NAME,
|
||||
errors = errors
|
||||
)).execute().body()
|
||||
}
|
||||
|
||||
fun unregisterAppUser(userCode: String): ApiResponse<Nothing>? {
|
||||
return api.appUser(AppUserRequest(
|
||||
deviceId = app.deviceId,
|
||||
userCode = userCode
|
||||
)).execute().body()
|
||||
}
|
||||
|
||||
fun getUpdate(channel: String): ApiResponse<List<Update>>? {
|
||||
return api.updates(channel).execute().body()
|
||||
}
|
||||
}
|
||||
|
@ -4,16 +4,16 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.szkolny
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.request.ErrorReportRequest
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.request.EventShareRequest
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.request.ServerSyncRequest
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.request.WebPushRequest
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.request.*
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.response.ApiResponse
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.response.ServerSyncResponse
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.response.WebPushResponse
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Query
|
||||
|
||||
interface SzkolnyService {
|
||||
|
||||
@ -28,4 +28,10 @@ interface SzkolnyService {
|
||||
|
||||
@POST("errorReport")
|
||||
fun errorReport(@Body request: ErrorReportRequest): Call<ApiResponse<Nothing>>
|
||||
|
||||
@POST("appUser")
|
||||
fun appUser(@Body request: AppUserRequest): Call<ApiResponse<Nothing>>
|
||||
|
||||
@GET("updates/app")
|
||||
fun updates(@Query("channel") channel: String = "release"): Call<ApiResponse<List<Update>>>
|
||||
}
|
||||
|
@ -46,6 +46,6 @@ object Signing {
|
||||
|
||||
/*fun provideKey(param1: String, param2: Long): ByteArray {*/
|
||||
fun pleaseStopRightNow(param1: String, param2: Long): ByteArray {
|
||||
return "$param1.MTIzNDU2Nzg5MDJ9J602xT===.$param2".sha256()
|
||||
return "$param1.MTIzNDU2Nzg5MDzsOS3K08===.$param2".sha256()
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-1-18.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.szkolny.request
|
||||
|
||||
data class AppUserRequest(
|
||||
val action: String = "unregister",
|
||||
|
||||
val deviceId: String,
|
||||
val userCode: String
|
||||
)
|
@ -6,6 +6,7 @@ package pl.szczodrzynski.edziennik.data.api.szkolny.request
|
||||
|
||||
data class ErrorReportRequest(
|
||||
val deviceId: String,
|
||||
val appVersion: String,
|
||||
val errors: List<Error>
|
||||
) {
|
||||
data class Error(
|
||||
|
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-1-18.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.szkolny.response
|
||||
|
||||
data class Update(
|
||||
val versionCode: Int,
|
||||
val versionName: String,
|
||||
val releaseDate: String,
|
||||
val releaseNotes: String?,
|
||||
val releaseType: String,
|
||||
val isOnGooglePlay: Boolean,
|
||||
val downloadUrl: String?
|
||||
)
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-1-17.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.task
|
||||
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
|
||||
class AppSync(val app: App, val notifications: MutableList<Notification>, val profiles: List<Profile>, val api: SzkolnyApi) {
|
||||
companion object {
|
||||
private const val TAG = "AppSync"
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the app sync, sending all pending notifications
|
||||
* and retrieving a list of shared events.
|
||||
*
|
||||
* Events are automatically saved to app database,
|
||||
* along with corresponding metadata objects.
|
||||
*
|
||||
* @return a number of events inserted to DB, possibly needing a notification
|
||||
*/
|
||||
fun run(): Int {
|
||||
val profiles = profiles.filter { it.registration == Profile.REGISTRATION_ENABLED && !it.archived }
|
||||
if (profiles.isNotEmpty()) {
|
||||
val blacklistedIds = app.db.eventDao().blacklistedIds;
|
||||
val events = api.getEvents(profiles, notifications, blacklistedIds)
|
||||
|
||||
if (events.isNotEmpty()) {
|
||||
app.db.metadataDao().addAllIgnore(events.map { event ->
|
||||
Metadata(
|
||||
event.profileId,
|
||||
Metadata.TYPE_EVENT,
|
||||
event.id,
|
||||
event.seen,
|
||||
event.notified,
|
||||
event.addedDate
|
||||
)
|
||||
})
|
||||
return app.db.eventDao().addAll(events).size
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -39,4 +39,18 @@ abstract class IApiTask(open val profileId: Int) {
|
||||
override fun toString(): String {
|
||||
return "IApiTask(profileId=$profileId, taskId=$taskId, profile=$profile, taskName=$taskName)"
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun enqueueAll(context: Context, tasks: List<IApiTask>) {
|
||||
Intent(context, ApiService::class.java).let {
|
||||
if (SDK_INT >= O)
|
||||
context.startForegroundService(it)
|
||||
else
|
||||
context.startService(it)
|
||||
}
|
||||
tasks.forEach {
|
||||
EventBus.getDefault().postSticky(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-1-16.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.api.task
|
||||
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.*
|
||||
import pl.szczodrzynski.edziennik.getNotificationTitle
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class Notifications(val app: App, val notifications: MutableList<Notification>, val profiles: List<Profile>) {
|
||||
companion object {
|
||||
private const val TAG = "Notifications"
|
||||
}
|
||||
|
||||
private val today by lazy { Date.getToday() }
|
||||
private val todayValue by lazy { today.value }
|
||||
|
||||
/**
|
||||
* Create a [Notification] from every possible
|
||||
* data type. Notifications are posted whenever
|
||||
* the object's metadata `notified` property is
|
||||
* set to false.
|
||||
*/
|
||||
fun run() {
|
||||
timetableNotifications()
|
||||
eventNotifications()
|
||||
gradeNotifications()
|
||||
behaviourNotifications()
|
||||
attendanceNotifications()
|
||||
announcementNotifications()
|
||||
messageNotifications()
|
||||
luckyNumberNotifications()
|
||||
}
|
||||
|
||||
private fun timetableNotifications() {
|
||||
for (lesson in app.db.timetableDao().getNotNotifiedNow()) {
|
||||
val text = app.getString(
|
||||
R.string.notification_lesson_change_format,
|
||||
lesson.getDisplayChangeType(app),
|
||||
if (lesson.displayDate == null) "" else lesson.displayDate!!.formattedString,
|
||||
lesson.changeSubjectName
|
||||
)
|
||||
notifications += Notification(
|
||||
id = Notification.buildId(lesson.profileId, Notification.TYPE_TIMETABLE_LESSON_CHANGE, lesson.id),
|
||||
title = app.getNotificationTitle(Notification.TYPE_TIMETABLE_LESSON_CHANGE),
|
||||
text = text,
|
||||
type = Notification.TYPE_TIMETABLE_LESSON_CHANGE,
|
||||
profileId = lesson.profileId,
|
||||
profileName = profiles.singleOrNull { it.id == lesson.profileId }?.name,
|
||||
viewId = MainActivity.DRAWER_ITEM_TIMETABLE,
|
||||
addedDate = lesson.addedDate
|
||||
).addExtra("timetableDate", lesson.displayDate?.stringY_m_d ?: "")
|
||||
}
|
||||
}
|
||||
|
||||
private fun eventNotifications() {
|
||||
for (event in app.db.eventDao().notNotifiedNow) {
|
||||
val text = if (event.type == Event.TYPE_HOMEWORK)
|
||||
app.getString(
|
||||
if (event.subjectLongName.isNullOrEmpty())
|
||||
R.string.notification_homework_no_subject_format
|
||||
else
|
||||
R.string.notification_homework_format,
|
||||
event.subjectLongName,
|
||||
event.eventDate.formattedString
|
||||
)
|
||||
else
|
||||
app.getString(
|
||||
if (event.subjectLongName.isNullOrEmpty())
|
||||
R.string.notification_event_no_subject_format
|
||||
else
|
||||
R.string.notification_event_format,
|
||||
event.typeName,
|
||||
event.eventDate.formattedString,
|
||||
event.subjectLongName
|
||||
)
|
||||
val type = if (event.type == Event.TYPE_HOMEWORK) Notification.TYPE_NEW_HOMEWORK else Notification.TYPE_NEW_EVENT
|
||||
notifications += Notification(
|
||||
id = Notification.buildId(event.profileId, type, event.id),
|
||||
title = app.getNotificationTitle(type),
|
||||
text = text,
|
||||
type = type,
|
||||
profileId = event.profileId,
|
||||
profileName = profiles.singleOrNull { it.id == event.profileId }?.name,
|
||||
viewId = if (event.type == Event.TYPE_HOMEWORK) MainActivity.DRAWER_ITEM_HOMEWORK else MainActivity.DRAWER_ITEM_AGENDA,
|
||||
addedDate = event.addedDate
|
||||
).addExtra("eventId", event.id).addExtra("eventDate", event.eventDate.value.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
fun sharedEventNotifications() {
|
||||
for (event in app.db.eventDao().notNotifiedNow.filter { it.sharedBy != null }) {
|
||||
val text = app.getString(
|
||||
R.string.notification_shared_event_format,
|
||||
event.sharedByName,
|
||||
event.typeName ?: "wydarzenie",
|
||||
event.eventDate.formattedString,
|
||||
event.topic
|
||||
)
|
||||
val type = if (event.type == Event.TYPE_HOMEWORK) Notification.TYPE_NEW_HOMEWORK else Notification.TYPE_NEW_EVENT
|
||||
notifications += Notification(
|
||||
id = Notification.buildId(event.profileId, type, event.id),
|
||||
title = app.getNotificationTitle(type),
|
||||
text = text,
|
||||
type = type,
|
||||
profileId = event.profileId,
|
||||
profileName = profiles.singleOrNull { it.id == event.profileId }?.name,
|
||||
viewId = if (event.type == Event.TYPE_HOMEWORK) MainActivity.DRAWER_ITEM_HOMEWORK else MainActivity.DRAWER_ITEM_AGENDA,
|
||||
addedDate = event.addedDate
|
||||
).addExtra("eventId", event.id).addExtra("eventDate", event.eventDate.value.toLong())
|
||||
}
|
||||
}
|
||||
|
||||
private fun gradeNotifications() {
|
||||
for (grade in app.db.gradeDao().notNotifiedNow) {
|
||||
val gradeName = when (grade.type) {
|
||||
Grade.TYPE_SEMESTER1_PROPOSED, Grade.TYPE_SEMESTER2_PROPOSED -> app.getString(R.string.grade_semester_proposed_format_2, grade.name)
|
||||
Grade.TYPE_SEMESTER1_FINAL, Grade.TYPE_SEMESTER2_FINAL -> app.getString(R.string.grade_semester_final_format_2, grade.name)
|
||||
Grade.TYPE_YEAR_PROPOSED -> app.getString(R.string.grade_year_proposed_format_2, grade.name)
|
||||
Grade.TYPE_YEAR_FINAL -> app.getString(R.string.grade_year_final_format_2, grade.name)
|
||||
else -> grade.name
|
||||
}
|
||||
val text = app.getString(
|
||||
R.string.notification_grade_format,
|
||||
gradeName,
|
||||
grade.subjectLongName
|
||||
)
|
||||
notifications += Notification(
|
||||
id = Notification.buildId(grade.profileId, Notification.TYPE_NEW_GRADE, grade.id),
|
||||
title = app.getNotificationTitle(Notification.TYPE_NEW_GRADE),
|
||||
text = text,
|
||||
type = Notification.TYPE_NEW_GRADE,
|
||||
profileId = grade.profileId,
|
||||
profileName = profiles.singleOrNull { it.id == grade.profileId }?.name,
|
||||
viewId = MainActivity.DRAWER_ITEM_GRADES,
|
||||
addedDate = grade.addedDate
|
||||
).addExtra("gradeId", grade.id).addExtra("gradesSubjectId", grade.subjectId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun behaviourNotifications() {
|
||||
for (notice in app.db.noticeDao().notNotifiedNow) {
|
||||
|
||||
val noticeTypeStr = when (notice.type) {
|
||||
Notice.TYPE_POSITIVE -> app.getString(R.string.notification_notice_praise)
|
||||
Notice.TYPE_NEGATIVE -> app.getString(R.string.notification_notice_warning)
|
||||
else -> app.getString(R.string.notification_notice_new)
|
||||
}
|
||||
|
||||
val text = app.getString(
|
||||
R.string.notification_notice_format,
|
||||
noticeTypeStr,
|
||||
notice.teacherFullName,
|
||||
Date.fromMillis(notice.addedDate).formattedString
|
||||
)
|
||||
notifications += Notification(
|
||||
id = Notification.buildId(notice.profileId, Notification.TYPE_NEW_NOTICE, notice.id),
|
||||
title = app.getNotificationTitle(Notification.TYPE_NEW_NOTICE),
|
||||
text = text,
|
||||
type = Notification.TYPE_NEW_NOTICE,
|
||||
profileId = notice.profileId,
|
||||
profileName = profiles.singleOrNull { it.id == notice.profileId }?.name,
|
||||
viewId = MainActivity.DRAWER_ITEM_BEHAVIOUR,
|
||||
addedDate = notice.addedDate
|
||||
).addExtra("noticeId", notice.id)
|
||||
}
|
||||
}
|
||||
|
||||
private fun attendanceNotifications() {
|
||||
for (attendance in app.db.attendanceDao().notNotifiedNow) {
|
||||
|
||||
val attendanceTypeStr = when (attendance.type) {
|
||||
Attendance.TYPE_ABSENT -> app.getString(R.string.notification_absence)
|
||||
Attendance.TYPE_ABSENT_EXCUSED -> app.getString(R.string.notification_absence_excused)
|
||||
Attendance.TYPE_BELATED -> app.getString(R.string.notification_belated)
|
||||
Attendance.TYPE_BELATED_EXCUSED -> app.getString(R.string.notification_belated_excused)
|
||||
Attendance.TYPE_RELEASED -> app.getString(R.string.notification_release)
|
||||
Attendance.TYPE_DAY_FREE -> app.getString(R.string.notification_day_free)
|
||||
else -> app.getString(R.string.notification_type_attendance)
|
||||
}
|
||||
|
||||
val text = app.getString(
|
||||
if (attendance.subjectLongName.isNullOrEmpty())
|
||||
R.string.notification_attendance_no_lesson_format
|
||||
else
|
||||
R.string.notification_attendance_format,
|
||||
attendanceTypeStr,
|
||||
attendance.subjectLongName,
|
||||
attendance.lessonDate.formattedString
|
||||
)
|
||||
notifications += Notification(
|
||||
id = Notification.buildId(attendance.profileId, Notification.TYPE_NEW_ATTENDANCE, attendance.id),
|
||||
title = app.getNotificationTitle(Notification.TYPE_NEW_ATTENDANCE),
|
||||
text = text,
|
||||
type = Notification.TYPE_NEW_ATTENDANCE,
|
||||
profileId = attendance.profileId,
|
||||
profileName = profiles.singleOrNull { it.id == attendance.profileId }?.name,
|
||||
viewId = MainActivity.DRAWER_ITEM_ATTENDANCE,
|
||||
addedDate = attendance.addedDate
|
||||
).addExtra("attendanceId", attendance.id).addExtra("attendanceSubjectId", attendance.subjectId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun announcementNotifications() {
|
||||
for (announcement in app.db.announcementDao().notNotifiedNow) {
|
||||
val text = app.getString(
|
||||
R.string.notification_announcement_format,
|
||||
announcement.teacherFullName,
|
||||
announcement.subject
|
||||
)
|
||||
notifications += Notification(
|
||||
id = Notification.buildId(announcement.profileId, Notification.TYPE_NEW_ANNOUNCEMENT, announcement.id),
|
||||
title = app.getNotificationTitle(Notification.TYPE_NEW_ANNOUNCEMENT),
|
||||
text = text,
|
||||
type = Notification.TYPE_NEW_ANNOUNCEMENT,
|
||||
profileId = announcement.profileId,
|
||||
profileName = profiles.singleOrNull { it.id == announcement.profileId }?.name,
|
||||
viewId = MainActivity.DRAWER_ITEM_ANNOUNCEMENTS,
|
||||
addedDate = announcement.addedDate
|
||||
).addExtra("announcementId", announcement.id)
|
||||
}
|
||||
}
|
||||
|
||||
private fun messageNotifications() {
|
||||
for (message in app.db.messageDao().receivedNotNotifiedNow) {
|
||||
val text = app.getString(
|
||||
R.string.notification_message_format,
|
||||
message.senderFullName,
|
||||
message.subject
|
||||
)
|
||||
notifications += Notification(
|
||||
id = Notification.buildId(message.profileId, Notification.TYPE_NEW_MESSAGE, message.id),
|
||||
title = app.getNotificationTitle(Notification.TYPE_NEW_MESSAGE),
|
||||
text = text,
|
||||
type = Notification.TYPE_NEW_MESSAGE,
|
||||
profileId = message.profileId,
|
||||
profileName = profiles.singleOrNull { it.id == message.profileId }?.name,
|
||||
viewId = MainActivity.DRAWER_ITEM_MESSAGES,
|
||||
addedDate = message.addedDate
|
||||
).addExtra("messageType", Message.TYPE_RECEIVED.toLong()).addExtra("messageId", message.id)
|
||||
}
|
||||
}
|
||||
|
||||
private fun luckyNumberNotifications() {
|
||||
val luckyNumbers = app.db.luckyNumberDao().notNotifiedNow
|
||||
luckyNumbers?.removeAll { it.date < today }
|
||||
luckyNumbers?.forEach { luckyNumber ->
|
||||
val profile = profiles.singleOrNull { it.id == luckyNumber.profileId } ?: return@forEach
|
||||
val text = when (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) {
|
||||
true -> when (luckyNumber.date.value) {
|
||||
todayValue -> R.string.notification_lucky_number_yours_format
|
||||
todayValue + 1 -> R.string.notification_lucky_number_yours_tomorrow_format
|
||||
else -> R.string.notification_lucky_number_yours_later_format
|
||||
}
|
||||
else -> when (luckyNumber.date.value) {
|
||||
todayValue -> R.string.notification_lucky_number_format
|
||||
todayValue + 1 -> R.string.notification_lucky_number_tomorrow_format
|
||||
else -> R.string.notification_lucky_number_later_format
|
||||
}
|
||||
}
|
||||
notifications += Notification(
|
||||
id = Notification.buildId(luckyNumber.profileId, Notification.TYPE_LUCKY_NUMBER, luckyNumber.date.value.toLong()),
|
||||
title = app.getNotificationTitle(Notification.TYPE_LUCKY_NUMBER),
|
||||
text = app.getString(text, luckyNumber.date.formattedString, luckyNumber.number),
|
||||
type = Notification.TYPE_LUCKY_NUMBER,
|
||||
profileId = luckyNumber.profileId,
|
||||
profileName = profile.name,
|
||||
viewId = MainActivity.DRAWER_ITEM_HOME,
|
||||
addedDate = luckyNumber.addedDate
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
package pl.szczodrzynski.edziennik.data.api.task
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import androidx.core.app.NotificationCompat
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.Notifier.ID_NOTIFICATIONS
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.getNotificationTitle
|
||||
import pl.szczodrzynski.edziennik.utils.models.Notification
|
||||
import kotlin.math.min
|
||||
|
||||
class NotifyTask : IApiTask(-1) {
|
||||
override fun prepare(app: App) {
|
||||
taskName = app.getString(R.string.edziennik_notification_api_notify_title)
|
||||
}
|
||||
|
||||
override fun cancel() {
|
||||
|
||||
}
|
||||
|
||||
fun run(app: App, taskCallback: EdziennikCallback) {
|
||||
val list = app.db.notificationDao().getNotPostedNow()
|
||||
val notificationList = list.subList(0, min(10, list.size))
|
||||
|
||||
val unreadCount = list.size
|
||||
|
||||
for (notification in notificationList) {
|
||||
val intent = Intent(app, MainActivity::class.java)
|
||||
notification.fillIntent(intent)
|
||||
val pendingIntent = PendingIntent.getActivity(app, notification.id, intent, 0)
|
||||
val notificationBuilder = NotificationCompat.Builder(app, app.notifier.notificationGroup)
|
||||
// title, text, type, date
|
||||
.setContentTitle(notification.profileName)
|
||||
.setContentText(notification.text)
|
||||
.setSubText(app.getNotificationTitle(notification.type))
|
||||
.setWhen(notification.addedDate)
|
||||
.setTicker(app.getString(R.string.notification_ticker_format, Notification.stringType(app, notification.type)))
|
||||
// icon, color, lights, priority
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setColor(app.notifier.notificationColor)
|
||||
.setLights(-0xff0001, 2000, 2000)
|
||||
.setPriority(app.notifier.notificationPriority)
|
||||
// channel, group, style
|
||||
.setChannelId(app.notifier.notificationGroup)
|
||||
.setGroup(app.notifier.notificationGroup)
|
||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
||||
.setStyle(NotificationCompat.BigTextStyle().bigText(notification.text))
|
||||
// intent, auto cancel
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true)
|
||||
if (!app.notifier.shouldBeQuiet()) {
|
||||
notificationBuilder.setDefaults(app.notifier.notificationDefaults)
|
||||
}
|
||||
app.notifier.notificationManager.notify(notification.id, notificationBuilder.build())
|
||||
}
|
||||
|
||||
if (notificationList.isNotEmpty() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
val intent = Intent(app, MainActivity::class.java)
|
||||
intent.action = "android.intent.action.MAIN"
|
||||
intent.putExtra("fragmentId", MainActivity.DRAWER_ITEM_NOTIFICATIONS)
|
||||
val pendingIntent = PendingIntent.getActivity(app, ID_NOTIFICATIONS,
|
||||
intent, 0)
|
||||
|
||||
val groupBuilder = NotificationCompat.Builder(app, app.notifier.notificationGroup)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setColor(app.notifier.notificationColor)
|
||||
.setContentTitle(app.getString(R.string.notification_new_notification_title_format, unreadCount))
|
||||
.setGroupSummary(true)
|
||||
.setAutoCancel(true)
|
||||
.setChannelId(app.notifier.notificationGroup)
|
||||
.setGroup(app.notifier.notificationGroup)
|
||||
.setLights(-0xff0001, 2000, 2000)
|
||||
.setPriority(app.notifier.notificationPriority)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setStyle(NotificationCompat.BigTextStyle())
|
||||
if (!app.notifier.shouldBeQuiet()) {
|
||||
groupBuilder.setDefaults(app.notifier.notificationDefaults)
|
||||
}
|
||||
app.notifier.notificationManager.notify(ID_NOTIFICATIONS, groupBuilder.build())
|
||||
}
|
||||
|
||||
app.db.notificationDao().setAllPosted()
|
||||
|
||||
taskCallback.onCompleted()
|
||||
}
|
||||
}
|
@ -0,0 +1,172 @@
|
||||
package pl.szczodrzynski.edziennik.data.api.task
|
||||
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.SparseIntArray
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.util.forEach
|
||||
import androidx.core.util.set
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_SERVER_MESSAGE
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification as AppNotification
|
||||
|
||||
class PostNotifications(val app: App, nList: List<AppNotification>) {
|
||||
companion object {
|
||||
private const val TAG = "PostNotifications"
|
||||
}
|
||||
|
||||
/*public boolean shouldBeQuiet() {
|
||||
long now = Time.getNow().getInMillis();
|
||||
long start = app.config.getSync().getQuietHoursStart();
|
||||
long end = app.config.getSync().getQuietHoursEnd();
|
||||
if (start > end) {
|
||||
end += 1000 * 60 * 60 * 24;
|
||||
//Log.d(TAG, "Night passing");
|
||||
}
|
||||
if (start > now) {
|
||||
now += 1000 * 60 * 60 * 24;
|
||||
//Log.d(TAG, "Now is smaller");
|
||||
}
|
||||
//Log.d(TAG, "Start is "+start+", now is "+now+", end is "+end);
|
||||
return start > 0 && now >= start && now <= end;
|
||||
}*/
|
||||
fun shouldBeQuiet() = false
|
||||
|
||||
private fun buildSummaryText(summaryCounts: SparseIntArray): CharSequence {
|
||||
val summaryTexts = mutableListOf<String>()
|
||||
summaryCounts.forEach { key, value ->
|
||||
if (value <= 0)
|
||||
return@forEach
|
||||
val pluralRes = when (key) {
|
||||
AppNotification.TYPE_TIMETABLE_LESSON_CHANGE -> R.plurals.notification_new_timetable_change_format
|
||||
AppNotification.TYPE_NEW_GRADE -> R.plurals.notification_new_grades_format
|
||||
AppNotification.TYPE_NEW_EVENT -> R.plurals.notification_new_events_format
|
||||
AppNotification.TYPE_NEW_HOMEWORK -> R.plurals.notification_new_homework_format
|
||||
AppNotification.TYPE_NEW_SHARED_EVENT -> R.plurals.notification_new_shared_events_format
|
||||
AppNotification.TYPE_NEW_SHARED_HOMEWORK -> R.plurals.notification_new_shared_homework_format
|
||||
AppNotification.TYPE_NEW_MESSAGE -> R.plurals.notification_new_messages_format
|
||||
AppNotification.TYPE_NEW_NOTICE -> R.plurals.notification_new_notices_format
|
||||
AppNotification.TYPE_NEW_ATTENDANCE -> R.plurals.notification_new_attendance_format
|
||||
AppNotification.TYPE_LUCKY_NUMBER -> R.plurals.notification_new_lucky_number_format
|
||||
AppNotification.TYPE_NEW_ANNOUNCEMENT -> R.plurals.notification_new_announcements_format
|
||||
else -> R.plurals.notification_other_format
|
||||
}
|
||||
summaryTexts += app.resources.getQuantityString(pluralRes, value, value)
|
||||
}
|
||||
return summaryTexts.concat(", ")
|
||||
}
|
||||
|
||||
init { run {
|
||||
val count = nList.size
|
||||
if (count == 0)
|
||||
return@run
|
||||
val notificationManager = app.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
val summaryCounts = SparseIntArray()
|
||||
|
||||
val newNotificationsText = app.resources.getQuantityString(R.plurals.notification_count_format, count, count)
|
||||
val newNotificationsShortText = app.resources.getQuantityString(R.plurals.notification_count_short_format, count, count)
|
||||
|
||||
val intent = Intent(
|
||||
app,
|
||||
MainActivity::class.java,
|
||||
"fragmentId" to MainActivity.DRAWER_ITEM_NOTIFICATIONS
|
||||
)
|
||||
val summaryIntent = PendingIntent.getActivity(app, app.notifications.dataId, intent, PendingIntent.FLAG_ONE_SHOT)
|
||||
|
||||
// On Nougat or newer - show maximum 8 notifications
|
||||
// On Marshmallow or older - show maximum 4 notifications
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N && count > 4 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && count > 8) {
|
||||
val summaryList = mutableListOf<CharSequence>()
|
||||
nList.forEach {
|
||||
summaryCounts[it.type]++
|
||||
summaryList += listOf(
|
||||
it.profileName.asBoldSpannable(),
|
||||
it.text
|
||||
).concat(": ")
|
||||
}
|
||||
|
||||
// Create a summary to show *instead* of notifications
|
||||
val combined = NotificationCompat.Builder(app, app.notifications.dataKey)
|
||||
.setContentTitle(app.getString(R.string.app_name))
|
||||
.setContentText(buildSummaryText(summaryCounts))
|
||||
.setTicker(newNotificationsText)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setStyle(NotificationCompat.InboxStyle()
|
||||
.also {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
it.setBigContentTitle(app.getString(R.string.app_name))
|
||||
it.setSummaryText(newNotificationsShortText)
|
||||
}
|
||||
else {
|
||||
it.setBigContentTitle(newNotificationsText)
|
||||
it.setSummaryText(app.getString(R.string.notification_click_to_see_all))
|
||||
}
|
||||
summaryList.forEach { line ->
|
||||
it.addLine(line)
|
||||
}
|
||||
})
|
||||
.setColor(0xff2196f3.toInt())
|
||||
.setLights(0xff2196f3.toInt(), 2000, 2000)
|
||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
||||
.setGroup(app.notifications.dataKey)
|
||||
.setContentIntent(summaryIntent)
|
||||
.setAutoCancel(true)
|
||||
.build()
|
||||
notificationManager.notify(System.currentTimeMillis().toInt(), combined)
|
||||
}
|
||||
else {
|
||||
// Less than 8 notifications
|
||||
val notifications = nList.map {
|
||||
summaryCounts[it.type]++
|
||||
NotificationCompat.Builder(app, app.notifications.dataKey)
|
||||
.setContentTitle(it.profileName ?: app.getString(R.string.app_name))
|
||||
.setContentText(it.text)
|
||||
.setSubText(if (it.type == TYPE_SERVER_MESSAGE) null else it.title)
|
||||
.setTicker("${it.profileName}: ${it.title}")
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setStyle(NotificationCompat.BigTextStyle()
|
||||
.bigText(it.text))
|
||||
.setWhen(it.addedDate)
|
||||
.setColor(0xff2196f3.toInt())
|
||||
.setLights(0xff2196f3.toInt(), 2000, 2000)
|
||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
||||
.setGroup(app.notifications.dataKey)
|
||||
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
||||
.setContentIntent(it.getPendingIntent(app))
|
||||
.setAutoCancel(true)
|
||||
.build()
|
||||
}
|
||||
|
||||
val time = System.currentTimeMillis()
|
||||
notificationManager.apply {
|
||||
notifications.forEachIndexed { index, it ->
|
||||
notificationManager.notify((time + index).toInt(), it)
|
||||
}
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
val summary = NotificationCompat.Builder(app, app.notifications.dataKey)
|
||||
.setContentTitle(newNotificationsText)
|
||||
.setContentText(buildSummaryText(summaryCounts))
|
||||
.setTicker(newNotificationsText)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setColor(0xff2196f3.toInt())
|
||||
.setLights(0xff2196f3.toInt(), 2000, 2000)
|
||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||
.setDefaults(NotificationCompat.DEFAULT_ALL)
|
||||
.setGroup(app.notifications.dataKey)
|
||||
.setGroupSummary(true)
|
||||
.setContentIntent(summaryIntent)
|
||||
.setAutoCancel(true)
|
||||
.build()
|
||||
|
||||
notificationManager.notify(app.notifications.dataId, summary)
|
||||
}
|
||||
}
|
||||
}}
|
||||
}
|
@ -7,39 +7,39 @@ package pl.szczodrzynski.edziennik.data.api.task
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.Szkolny
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Profile
|
||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||
|
||||
class SzkolnyTask(val request: Any) : IApiTask(-1) {
|
||||
class SzkolnyTask(val app: App, val syncingProfiles: List<Profile>) : IApiTask(-1) {
|
||||
companion object {
|
||||
private const val TAG = "SzkolnyTask"
|
||||
|
||||
fun sync(profiles: List<Profile>) = SzkolnyTask(SyncRequest(profiles))
|
||||
/*fun shareEvent(event: EventFull) = SzkolnyTask(ShareEventRequest(event))
|
||||
fun unshareEvent(event: EventFull) = SzkolnyTask(UnshareEventRequest(event))*/
|
||||
}
|
||||
private val api by lazy { SzkolnyApi(app) }
|
||||
private val profiles by lazy { app.db.profileDao().allNow }
|
||||
override fun prepare(app: App) { taskName = app.getString(R.string.edziennik_szkolny_creating_notifications) }
|
||||
override fun cancel() {}
|
||||
|
||||
private lateinit var szkolny: Szkolny
|
||||
private val notificationList = mutableListOf<Notification>()
|
||||
|
||||
override fun prepare(app: App) {
|
||||
taskName = app.getString(R.string.edziennik_szkolny_api_sync_title)
|
||||
internal fun run(taskCallback: EdziennikCallback) {
|
||||
val startTime = System.currentTimeMillis()
|
||||
val notifications = Notifications(app, notificationList, profiles)
|
||||
// create all e-register data notifications
|
||||
notifications.run()
|
||||
// send notifications to web push, get shared events
|
||||
AppSync(app, notificationList, profiles, api).run()
|
||||
// create notifications for shared events (not present before app sync)
|
||||
notifications.sharedEventNotifications()
|
||||
d(TAG, "Created ${notificationList.count()} notifications.")
|
||||
// update the database
|
||||
app.db.metadataDao().setAllNotified(true)
|
||||
app.db.notificationDao().addAll(notificationList)
|
||||
app.db.profileDao().setAllNotEmpty()
|
||||
// post all notifications
|
||||
PostNotifications(app, notificationList)
|
||||
d(TAG, "SzkolnyTask: finished in ${System.currentTimeMillis()-startTime} ms.")
|
||||
taskCallback.onCompleted()
|
||||
}
|
||||
|
||||
override fun cancel() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
internal fun run(app: App, taskCallback: EdziennikCallback) {
|
||||
szkolny = Szkolny(app, taskCallback)
|
||||
|
||||
when (request) {
|
||||
is SyncRequest -> szkolny.sync(request.profiles)
|
||||
/*is ShareEventRequest -> szkolny.shareEvent(request.event)
|
||||
is UnshareEventRequest -> szkolny.unshareEvent(request.event)*/
|
||||
}
|
||||
}
|
||||
|
||||
data class SyncRequest(val profiles: List<Profile>)
|
||||
/*data class ShareEventRequest(val event: EventFull)
|
||||
data class UnshareEventRequest(val event: EventFull)*/
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
930
app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.kt
Normal file
930
app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.kt
Normal file
@ -0,0 +1,930 @@
|
||||
package pl.szczodrzynski.edziennik.data.db
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Database
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import pl.szczodrzynski.edziennik.config.db.ConfigDao
|
||||
import pl.szczodrzynski.edziennik.config.db.ConfigEntry
|
||||
import pl.szczodrzynski.edziennik.crc32
|
||||
import pl.szczodrzynski.edziennik.data.db.converter.*
|
||||
import pl.szczodrzynski.edziennik.data.db.dao.*
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.*
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
@Database(entities = [
|
||||
Grade::class,
|
||||
Teacher::class,
|
||||
TeacherAbsence::class,
|
||||
TeacherAbsenceType::class,
|
||||
Subject::class,
|
||||
Notice::class,
|
||||
Team::class,
|
||||
Attendance::class,
|
||||
Event::class,
|
||||
EventType::class,
|
||||
LoginStore::class,
|
||||
Profile::class,
|
||||
LuckyNumber::class,
|
||||
Announcement::class,
|
||||
GradeCategory::class,
|
||||
FeedbackMessage::class,
|
||||
Message::class,
|
||||
MessageRecipient::class,
|
||||
DebugLog::class,
|
||||
EndpointTimer::class,
|
||||
LessonRange::class,
|
||||
Notification::class,
|
||||
Classroom::class,
|
||||
NoticeType::class,
|
||||
AttendanceType::class,
|
||||
Lesson::class,
|
||||
ConfigEntry::class,
|
||||
LibrusLesson::class,
|
||||
Metadata::class
|
||||
], version = 75)
|
||||
@TypeConverters(
|
||||
ConverterTime::class,
|
||||
ConverterDate::class,
|
||||
ConverterJsonObject::class,
|
||||
ConverterListLong::class,
|
||||
ConverterListString::class,
|
||||
ConverterDateInt::class
|
||||
)
|
||||
abstract class AppDb : RoomDatabase() {
|
||||
abstract fun gradeDao(): GradeDao
|
||||
abstract fun teacherDao(): TeacherDao
|
||||
abstract fun teacherAbsenceDao(): TeacherAbsenceDao
|
||||
abstract fun teacherAbsenceTypeDao(): TeacherAbsenceTypeDao
|
||||
abstract fun subjectDao(): SubjectDao
|
||||
abstract fun noticeDao(): NoticeDao
|
||||
abstract fun teamDao(): TeamDao
|
||||
abstract fun attendanceDao(): AttendanceDao
|
||||
abstract fun eventDao(): EventDao
|
||||
abstract fun eventTypeDao(): EventTypeDao
|
||||
abstract fun loginStoreDao(): LoginStoreDao
|
||||
abstract fun profileDao(): ProfileDao
|
||||
abstract fun luckyNumberDao(): LuckyNumberDao
|
||||
abstract fun announcementDao(): AnnouncementDao
|
||||
abstract fun gradeCategoryDao(): GradeCategoryDao
|
||||
abstract fun feedbackMessageDao(): FeedbackMessageDao
|
||||
abstract fun messageDao(): MessageDao
|
||||
abstract fun messageRecipientDao(): MessageRecipientDao
|
||||
abstract fun debugLogDao(): DebugLogDao
|
||||
abstract fun endpointTimerDao(): EndpointTimerDao
|
||||
abstract fun lessonRangeDao(): LessonRangeDao
|
||||
abstract fun notificationDao(): NotificationDao
|
||||
abstract fun classroomDao(): ClassroomDao
|
||||
abstract fun noticeTypeDao(): NoticeTypeDao
|
||||
abstract fun attendanceTypeDao(): AttendanceTypeDao
|
||||
abstract fun timetableDao(): TimetableDao
|
||||
abstract fun configDao(): ConfigDao
|
||||
abstract fun librusLessonDao(): LibrusLessonDao
|
||||
abstract fun metadataDao(): MetadataDao
|
||||
|
||||
companion object {
|
||||
@Volatile private var instance: AppDb? = null
|
||||
private val LOCK = Any()
|
||||
|
||||
private val MIGRATION_11_12: Migration = object : Migration(11, 12) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("PRAGMA foreign_keys=off;")
|
||||
database.execSQL("ALTER TABLE teams RENAME TO _teams_old;")
|
||||
database.execSQL("CREATE TABLE teams (profileId INTEGER NOT NULL, teamId INTEGER NOT NULL, teamType INTEGER NOT NULL, teamName TEXT, teamTeacherId INTEGER NOT NULL, PRIMARY KEY(profileId, teamId));")
|
||||
database.execSQL("INSERT INTO teams (profileId, teamId, teamType, teamName, teamTeacherId) SELECT profileId, teamId, teamType, teamName, teacherId FROM _teams_old;")
|
||||
database.execSQL("DROP TABLE _teams_old;")
|
||||
database.execSQL("PRAGMA foreign_keys=on;")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_12_13: Migration = object : Migration(12, 13) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE lessonChanges ADD lessonChangeWeekDay INTEGER NOT NULL DEFAULT -1;")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_13_14: Migration = object : Migration(13, 14) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE loginStores (loginStoreId INTEGER NOT NULL, loginStoreType INTEGER NOT NULL, loginStoreData TEXT, PRIMARY KEY(loginStoreId));")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_14_15: Migration = object : Migration(14, 15) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE grades RENAME TO _grades_old;")
|
||||
database.execSQL("CREATE TABLE `grades` (\n" +
|
||||
"\t`profileId`\tINTEGER NOT NULL,\n" +
|
||||
"\t`gradeId`\tINTEGER NOT NULL,\n" +
|
||||
"\t`gradeDescription`\tTEXT,\n" +
|
||||
"\t`gradeName`\tTEXT,\n" +
|
||||
"\t`gradeValue`\tREAL NOT NULL,\n" +
|
||||
"\t`gradeWeight`\tREAL NOT NULL,\n" +
|
||||
"\t`gradeSemester`\tINTEGER NOT NULL,\n" +
|
||||
"\t`gradeType`\tINTEGER NOT NULL,\n" +
|
||||
"\t`teacherId`\tINTEGER NOT NULL,\n" +
|
||||
"\t`categoryId`\tINTEGER NOT NULL,\n" +
|
||||
"\t`subjectId`\tINTEGER NOT NULL,\n" +
|
||||
"\tPRIMARY KEY(`profileId`,`gradeId`)\n" +
|
||||
");")
|
||||
database.execSQL("INSERT INTO grades\n" +
|
||||
" SELECT *\n" +
|
||||
" FROM _grades_old;")
|
||||
database.execSQL("DROP TABLE _grades_old;")
|
||||
database.execSQL("CREATE INDEX index_grades_profileId ON grades (profileId);")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_15_16: Migration = object : Migration(15, 16) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE profiles (" +
|
||||
"profileId INTEGER NOT NULL, " +
|
||||
"name TEXT, " +
|
||||
"subname TEXT, " +
|
||||
"image TEXT, " +
|
||||
"syncEnabled INTEGER NOT NULL, " +
|
||||
"syncNotifications INTEGER NOT NULL, " +
|
||||
"enableSharedEvents INTEGER NOT NULL, " +
|
||||
"countInSeconds INTEGER NOT NULL, " +
|
||||
"loggedIn INTEGER NOT NULL, " +
|
||||
"empty INTEGER NOT NULL, " +
|
||||
"studentNameLong TEXT, " +
|
||||
"studentNameShort TEXT, " +
|
||||
"studentNumber INTEGER NOT NULL, " +
|
||||
"studentData TEXT, " +
|
||||
"registration INTEGER NOT NULL, " +
|
||||
"gradeColorMode INTEGER NOT NULL, " +
|
||||
"agendaViewType INTEGER NOT NULL, " +
|
||||
"currentSemester INTEGER NOT NULL, " +
|
||||
"attendancePercentage REAL NOT NULL, " +
|
||||
"dateSemester1Start TEXT, " +
|
||||
"dateSemester2Start TEXT, " +
|
||||
"dateYearEnd TEXT, " +
|
||||
"luckyNumberEnabled INTEGER NOT NULL, " +
|
||||
"luckyNumber INTEGER NOT NULL, " +
|
||||
"luckyNumberDate TEXT, " +
|
||||
"loginStoreId INTEGER NOT NULL, " +
|
||||
"PRIMARY KEY(profileId));")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_16_17: Migration = object : Migration(16, 17) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE profiles ADD archived INTEGER NOT NULL DEFAULT 0;")
|
||||
database.execSQL("ALTER TABLE teams ADD teamCode TEXT;")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_17_18: Migration = object : Migration(17, 18) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE events ADD eventBlacklisted INTEGER NOT NULL DEFAULT 0;")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_18_19: Migration = object : Migration(18, 19) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE grades ADD gradeClassAverage REAL NOT NULL DEFAULT -1;")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_19_20: Migration = object : Migration(19, 20) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE luckyNumbers (" +
|
||||
"profileId INTEGER NOT NULL, " +
|
||||
"luckyNumberDate TEXT NOT NULL, " +
|
||||
"luckyNumber INTEGER NOT NULL, " +
|
||||
"PRIMARY KEY(profileId, luckyNumberDate));")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_20_21: Migration = object : Migration(20, 21) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE grades ADD gradeCategory TEXT")
|
||||
database.execSQL("ALTER TABLE grades ADD gradeColor INTEGER NOT NULL DEFAULT -1")
|
||||
database.execSQL("DROP TABLE gradeCategories")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_21_22: Migration = object : Migration(21, 22) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE eventTypes (" +
|
||||
"profileId INTEGER NOT NULL, " +
|
||||
"eventType INTEGER NOT NULL, " +
|
||||
"eventTypeName TEXT, " +
|
||||
"eventTypeColor INTEGER NOT NULL, " +
|
||||
"PRIMARY KEY(profileId, eventType));")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_22_23: Migration = object : Migration(22, 23) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE grades RENAME TO _grades_old;")
|
||||
database.execSQL("CREATE TABLE `grades` (\n" +
|
||||
"\t`profileId`\tINTEGER NOT NULL,\n" +
|
||||
"\t`gradeId`\tINTEGER NOT NULL,\n" +
|
||||
"\t`gradeCategory`\tTEXT,\n" +
|
||||
"\t`gradeColor`\tINTEGER NOT NULL DEFAULT -1,\n" +
|
||||
"\t`gradeDescription`\tTEXT,\n" +
|
||||
"\t`gradeName`\tTEXT,\n" +
|
||||
"\t`gradeValue`\tREAL NOT NULL,\n" +
|
||||
"\t`gradeWeight`\tREAL NOT NULL,\n" +
|
||||
"\t`gradeSemester`\tINTEGER NOT NULL,\n" +
|
||||
"\t`gradeClassAverage`\tREAL NOT NULL DEFAULT -1,\n" +
|
||||
"\t`gradeType`\tINTEGER NOT NULL,\n" +
|
||||
"\t`teacherId`\tINTEGER NOT NULL,\n" +
|
||||
"\t`subjectId`\tINTEGER NOT NULL,\n" +
|
||||
"\tPRIMARY KEY(`profileId`,`gradeId`)\n" +
|
||||
");")
|
||||
database.execSQL("DROP INDEX index_grades_profileId")
|
||||
database.execSQL("CREATE INDEX `index_grades_profileId` ON `grades` (\n" +
|
||||
"\t`profileId`\n" +
|
||||
");")
|
||||
database.execSQL("INSERT INTO grades (profileId, gradeId, gradeDescription, gradeName, gradeValue, gradeWeight, gradeSemester, gradeType, teacherId, subjectId, gradeClassAverage, gradeCategory, gradeColor) SELECT profileId, gradeId, gradeDescription, gradeName, gradeValue, gradeWeight, gradeSemester, gradeType, teacherId, subjectId, gradeClassAverage, gradeCategory, gradeColor FROM _grades_old;")
|
||||
database.execSQL("DROP TABLE _grades_old;")
|
||||
database.execSQL("ALTER TABLE attendances RENAME TO _attendances_old;")
|
||||
database.execSQL("CREATE TABLE `attendances` (\n" +
|
||||
"\t`profileId`\tINTEGER NOT NULL,\n" +
|
||||
"\t`attendanceId`\tINTEGER NOT NULL,\n" +
|
||||
"\t`attendanceLessonDate`\tTEXT NOT NULL,\n" +
|
||||
"\t`attendanceStartTime`\tTEXT NOT NULL,\n" +
|
||||
"\t`attendanceLessonTopic`\tTEXT,\n" +
|
||||
"\t`attendanceSemester`\tINTEGER NOT NULL,\n" +
|
||||
"\t`attendanceType`\tINTEGER NOT NULL,\n" +
|
||||
"\t`teacherId`\tINTEGER NOT NULL,\n" +
|
||||
"\t`subjectId`\tINTEGER NOT NULL,\n" +
|
||||
"\tPRIMARY KEY(`profileId`,`attendanceId`,`attendanceLessonDate`,`attendanceStartTime`)\n" +
|
||||
");")
|
||||
database.execSQL("DROP INDEX index_attendances_profileId")
|
||||
database.execSQL("CREATE INDEX `index_attendances_profileId` ON `attendances` (\n" +
|
||||
"\t`profileId`\n" +
|
||||
");")
|
||||
database.execSQL("INSERT INTO attendances SELECT * FROM _attendances_old;")
|
||||
database.execSQL("DROP TABLE _attendances_old;")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_23_24: Migration = object : Migration(23, 24) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE profiles ADD yearAverageMode INTEGER NOT NULL DEFAULT 0")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_24_25: Migration = object : Migration(24, 25) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE announcements (" +
|
||||
"profileId INTEGER NOT NULL, " +
|
||||
"announcementId INTEGER NOT NULL, " +
|
||||
"announcementSubject TEXT, " +
|
||||
"announcementText TEXT, " +
|
||||
"announcementStartDate TEXT, " +
|
||||
"announcementEndDate TEXT, " +
|
||||
"teacherId INTEGER NOT NULL, " +
|
||||
"PRIMARY KEY(profileId, announcementId));")
|
||||
//database.execSQL("DROP INDEX index_announcements_profileId");
|
||||
database.execSQL("CREATE INDEX index_announcements_profileId ON announcements (" +
|
||||
"profileId" +
|
||||
");")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_25_26: Migration = object : Migration(25, 26) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE grades ADD gradePointGrade INTEGER NOT NULL DEFAULT 0;")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_26_27: Migration = object : Migration(26, 27) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE grades ADD gradeValueMax REAL NOT NULL DEFAULT 0;")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_27_28: Migration = object : Migration(27, 28) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE gradeCategories (profileId INTEGER NOT NULL, categoryId INTEGER NOT NULL, weight REAL NOT NULL, color INTEGER NOT NULL, `text` TEXT, columns TEXT, valueFrom REAL NOT NULL, valueTo REAL NOT NULL, PRIMARY KEY(profileId, categoryId));")
|
||||
database.execSQL("ALTER TABLE grades ADD gradeComment TEXT;")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_28_29: Migration = object : Migration(28, 29) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE feedbackMessages (messageId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, received INTEGER NOT NULL DEFAULT 0, sentTime INTEGER NOT NULL, `text` TEXT)")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_29_30: Migration = object : Migration(29, 30) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE feedbackMessages ADD fromUser TEXT DEFAULT NULL")
|
||||
database.execSQL("ALTER TABLE feedbackMessages ADD fromUserName TEXT DEFAULT NULL")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_30_31: Migration = object : Migration(30, 31) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE messages (" +
|
||||
"profileId INTEGER NOT NULL, " +
|
||||
"messageId INTEGER NOT NULL, " +
|
||||
"messageSubject TEXT, " +
|
||||
"messageBody TEXT DEFAULT NULL, " +
|
||||
"messageType INTEGER NOT NULL DEFAULT 0, " +
|
||||
"senderId INTEGER NOT NULL DEFAULT -1, " +
|
||||
"senderReplyId INTEGER NOT NULL DEFAULT -1, " +
|
||||
"recipientIds TEXT DEFAULT NULL, " +
|
||||
"recipientReplyIds TEXT DEFAULT NULL, " +
|
||||
"readByRecipientDates TEXT DEFAULT NULL, " +
|
||||
"overrideHasAttachments INTEGER NOT NULL DEFAULT 0, " +
|
||||
"attachmentIds TEXT DEFAULT NULL, " +
|
||||
"attachmentNames TEXT DEFAULT NULL, " +
|
||||
"PRIMARY KEY(profileId, messageId));")
|
||||
//database.execSQL("DROP INDEX index_announcements_profileId");
|
||||
database.execSQL("CREATE INDEX index_messages_profileId ON messages (" +
|
||||
"profileId" +
|
||||
");")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_31_32: Migration = object : Migration(31, 32) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE messages ADD attachmentSizes TEXT DEFAULT NULL")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_32_33: Migration = object : Migration(32, 33) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP TABLE messages")
|
||||
database.execSQL("CREATE TABLE messages (" +
|
||||
"profileId INTEGER NOT NULL, " +
|
||||
"messageId INTEGER NOT NULL, " +
|
||||
"messageSubject TEXT, " +
|
||||
"messageBody TEXT DEFAULT NULL, " +
|
||||
"messageType INTEGER NOT NULL DEFAULT 0, " +
|
||||
"senderId INTEGER NOT NULL DEFAULT -1, " +
|
||||
"senderReplyId INTEGER NOT NULL DEFAULT -1, " +
|
||||
"overrideHasAttachments INTEGER NOT NULL DEFAULT 0, " +
|
||||
"attachmentIds TEXT DEFAULT NULL, " +
|
||||
"attachmentNames TEXT DEFAULT NULL, " +
|
||||
"attachmentSizes TEXT DEFAULT NULL, " +
|
||||
"PRIMARY KEY(profileId, messageId));")
|
||||
//database.execSQL("DROP INDEX index_announcements_profileId");
|
||||
database.execSQL("CREATE INDEX index_messages_profileId ON messages (" +
|
||||
"profileId" +
|
||||
");")
|
||||
database.execSQL("CREATE TABLE messageRecipients (" +
|
||||
"profileId INTEGER NOT NULL, " +
|
||||
"messageRecipientId INTEGER NOT NULL DEFAULT -1, " +
|
||||
"messageRecipientReplyId INTEGER NOT NULL DEFAULT -1, " +
|
||||
"messageRecipientReadDate INTEGER NOT NULL DEFAULT -1, " +
|
||||
"messageId INTEGER NOT NULL, " +
|
||||
"PRIMARY KEY(profileId, messageRecipientId));")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_33_34: Migration = object : Migration(33, 34) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP TABLE messageRecipients")
|
||||
database.execSQL("CREATE TABLE messageRecipients (" +
|
||||
"profileId INTEGER NOT NULL, " +
|
||||
"messageRecipientId INTEGER NOT NULL DEFAULT -1, " +
|
||||
"messageRecipientReplyId INTEGER NOT NULL DEFAULT -1, " +
|
||||
"messageRecipientReadDate INTEGER NOT NULL DEFAULT -1, " +
|
||||
"messageId INTEGER NOT NULL, " +
|
||||
"PRIMARY KEY(profileId, messageRecipientId, messageId));")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_34_35: Migration = object : Migration(34, 35) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE teachers ADD teacherLoginId TEXT DEFAULT NULL;")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_35_36: Migration = object : Migration(35, 36) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("UPDATE profiles SET yearAverageMode = 4 WHERE yearAverageMode = 0")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_36_37: Migration = object : Migration(36, 37) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {}
|
||||
}
|
||||
private val MIGRATION_37_38: Migration = object : Migration(37, 38) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
val today = Date.getToday()
|
||||
val schoolYearStart = if (today.month < 9) today.year - 1 else today.year
|
||||
database.execSQL("UPDATE profiles SET dateSemester1Start = '$schoolYearStart-09-01' WHERE dateSemester1Start IS NULL;")
|
||||
database.execSQL("UPDATE profiles SET dateSemester2Start = '" + (schoolYearStart + 1) + "-02-01' WHERE dateSemester2Start IS NULL;")
|
||||
database.execSQL("UPDATE profiles SET dateYearEnd = '" + (schoolYearStart + 1) + "-06-30' WHERE dateYearEnd IS NULL;")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_38_39: Migration = object : Migration(38, 39) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE debugLogs (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, `text` TEXT);")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_39_40: Migration = object : Migration(39, 40) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE profiles ADD changedEndpoints TEXT DEFAULT NULL;")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_40_41: Migration = object : Migration(40, 41) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("UPDATE profiles SET empty = 1;")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_41_42: Migration = object : Migration(41, 42) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("UPDATE profiles SET empty = 1;")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_42_43: Migration = object : Migration(42, 43) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE profiles ADD lastFullSync INTEGER NOT NULL DEFAULT 0;")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_43_44: Migration = object : Migration(43, 44) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("UPDATE profiles SET empty = 1;")
|
||||
database.execSQL("UPDATE profiles SET currentSemester = 1;")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_44_45: Migration = object : Migration(44, 45) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("UPDATE profiles SET empty = 1;")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_45_46: Migration = object : Migration(45, 46) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("UPDATE profiles SET lastFullSync = 0")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_46_47: Migration = object : Migration(46, 47) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("UPDATE profiles SET lastFullSync = 0")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_47_48: Migration = object : Migration(47, 48) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE notices ADD points REAL NOT NULL DEFAULT 0")
|
||||
database.execSQL("ALTER TABLE notices ADD category TEXT DEFAULT NULL")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_48_49: Migration = object : Migration(48, 49) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE grades ADD gradeParentId INTEGER NOT NULL DEFAULT -1")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_49_50: Migration = object : Migration(49, 50) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE grades ADD gradeIsImprovement INTEGER NOT NULL DEFAULT 0")
|
||||
database.execSQL("DELETE FROM attendances WHERE attendances.profileId IN (SELECT profiles.profileId FROM profiles JOIN loginStores USING(loginStoreId) WHERE loginStores.loginStoreType = 1)")
|
||||
database.execSQL("UPDATE profiles SET empty = 1 WHERE profileId IN (SELECT profiles.profileId FROM profiles JOIN loginStores USING(loginStoreId) WHERE loginStores.loginStoreType = 4)")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_50_51: Migration = object : Migration(50, 51) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE profiles ADD lastReceiversSync INTEGER NOT NULL DEFAULT 0")
|
||||
database.execSQL("ALTER TABLE teachers ADD teacherType INTEGER NOT NULL DEFAULT 0")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_51_52: Migration = object : Migration(51, 52) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE teachers ADD teacherTypeDescription TEXT DEFAULT NULL")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_52_53: Migration = object : Migration(52, 53) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE IF NOT EXISTS teacherAbsence (" +
|
||||
"profileId INTEGER NOT NULL," +
|
||||
"teacherAbsenceId INTEGER NOT NULL," +
|
||||
"teacherId INTEGER NOT NULL," +
|
||||
"teacherAbsenceType INTEGER NOT NULL," +
|
||||
"teacherAbsenceDateFrom TEXT NOT NULL," +
|
||||
"teacherAbsenceDateTo TEXT NOT NULL," +
|
||||
"PRIMARY KEY(profileId, teacherAbsenceId)" +
|
||||
")")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_53_54: Migration = object : Migration(53, 54) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE teacherAbsence ADD teacherAbsenceTimeFrom TEXT DEFAULT NULL")
|
||||
database.execSQL("ALTER TABLE teacherAbsence ADD teacherAbsenceTimeTo TEXT DEFAULT NULL")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_54_55: Migration = object : Migration(54, 55) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// 2019-10-21 for merge compatibility between 3.1.1 and api-v2
|
||||
// moved to Migration 55->56
|
||||
}
|
||||
}
|
||||
private val MIGRATION_55_56: Migration = object : Migration(55, 56) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE IF NOT EXISTS endpointTimers (" +
|
||||
"profileId INTEGER NOT NULL," +
|
||||
"endpointId INTEGER NOT NULL," +
|
||||
"endpointLastSync INTEGER DEFAULT NULL," +
|
||||
"endpointNextSync INTEGER NOT NULL DEFAULT 1," +
|
||||
"endpointViewId INTEGER DEFAULT NULL," +
|
||||
"PRIMARY KEY(profileId, endpointId)" +
|
||||
")")
|
||||
database.execSQL("CREATE TABLE IF NOT EXISTS lessonRanges (" +
|
||||
"profileId INTEGER NOT NULL," +
|
||||
"lessonRangeNumber INTEGER NOT NULL," +
|
||||
"lessonRangeStart TEXT NOT NULL," +
|
||||
"lessonRangeEnd TEXT NOT NULL," +
|
||||
"PRIMARY KEY(profileId, lessonRangeNumber)" +
|
||||
")")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_56_57: Migration = object : Migration(56, 57) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE gradeCategories ADD type INTEGER NOT NULL DEFAULT 0")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_57_58: Migration = object : Migration(57, 58) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE metadata RENAME TO _metadata_old;")
|
||||
database.execSQL("DROP INDEX index_metadata_profileId_thingType_thingId;")
|
||||
database.execSQL("UPDATE _metadata_old SET thingType = " + Metadata.TYPE_HOMEWORK + " WHERE thingType = " + Metadata.TYPE_EVENT + " AND thingId IN (SELECT eventId FROM events WHERE eventType = -1);")
|
||||
database.execSQL("CREATE TABLE metadata (\n" +
|
||||
"profileId INTEGER NOT NULL,\n" +
|
||||
"metadataId INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" +
|
||||
"thingType INTEGER NOT NULL,\n" +
|
||||
"thingId INTEGER NOT NULL,\n" +
|
||||
"seen INTEGER NOT NULL,\n" +
|
||||
"notified INTEGER NOT NULL,\n" +
|
||||
"addedDate INTEGER NOT NULL);")
|
||||
database.execSQL("INSERT INTO metadata SELECT * FROM (SELECT * FROM _metadata_old ORDER BY addedDate DESC) GROUP BY thingId;")
|
||||
database.execSQL("DROP TABLE _metadata_old;")
|
||||
database.execSQL("CREATE UNIQUE INDEX index_metadata_profileId_thingType_thingId ON metadata (profileId, thingType, thingId);")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_58_59: Migration = object : Migration(58, 59) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("UPDATE metadata SET addedDate = addedDate*1000 WHERE addedDate < 10000000000;")
|
||||
database.execSQL("INSERT INTO metadata (profileId, thingType, thingId, seen, notified, addedDate)\n" +
|
||||
"SELECT profileId,\n" +
|
||||
"10 AS thingType,\n" +
|
||||
"luckyNumberDate*10000+substr(luckyNumberDate, 6)*100+substr(luckyNumberDate, 9) AS thingId,\n" +
|
||||
"1 AS seen,\n" +
|
||||
"1 AS notified,\n" +
|
||||
"CAST(strftime('%s', luckyNumberDate) AS INT)*1000 AS addedDate\n" +
|
||||
"FROM luckyNumbers;")
|
||||
database.execSQL("ALTER TABLE luckyNumbers RENAME TO _old_luckyNumbers;")
|
||||
database.execSQL("CREATE TABLE luckyNumbers (\n" +
|
||||
"profileId INTEGER NOT NULL,\n" +
|
||||
"luckyNumberDate INTEGER NOT NULL, \n" +
|
||||
"luckyNumber INTEGER NOT NULL, \n" +
|
||||
"PRIMARY KEY(profileId, luckyNumberDate))")
|
||||
database.execSQL("INSERT INTO luckyNumbers (profileId, luckyNumberDate, luckyNumber)\n" +
|
||||
"SELECT profileId,\n" +
|
||||
"luckyNumberDate*10000+substr(luckyNumberDate, 6)*100+substr(luckyNumberDate, 9) AS luckyNumberDate,\n" +
|
||||
"luckyNumber\n" +
|
||||
"FROM _old_luckyNumbers;")
|
||||
database.execSQL("DROP TABLE _old_luckyNumbers;")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_59_60: Migration = object : Migration(59, 60) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE notifications (\n" +
|
||||
" id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" +
|
||||
" title TEXT NOT NULL,\n" +
|
||||
" `text` TEXT NOT NULL,\n" +
|
||||
" `type` INTEGER NOT NULL,\n" +
|
||||
" profileId INTEGER DEFAULT NULL,\n" +
|
||||
" profileName TEXT DEFAULT NULL,\n" +
|
||||
" posted INTEGER NOT NULL DEFAULT 0,\n" +
|
||||
" viewId INTEGER DEFAULT NULL,\n" +
|
||||
" extras TEXT DEFAULT NULL,\n" +
|
||||
" addedDate INTEGER NOT NULL\n" +
|
||||
");")
|
||||
database.execSQL("ALTER TABLE profiles ADD COLUMN disabledNotifications TEXT DEFAULT NULL")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_60_61: Migration = object : Migration(60, 61) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE IF NOT EXISTS teacherAbsenceTypes (" +
|
||||
"profileId INTEGER NOT NULL," +
|
||||
"teacherAbsenceTypeId INTEGER NOT NULL," +
|
||||
"teacherAbsenceTypeName TEXT NOT NULL," +
|
||||
"PRIMARY KEY(profileId, teacherAbsenceTypeId))")
|
||||
database.execSQL("ALTER TABLE teacherAbsence ADD COLUMN teacherAbsenceName TEXT DEFAULT NULL")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_61_62: Migration = object : Migration(61, 62) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE IF NOT EXISTS classrooms (\n" +
|
||||
" profileId INTEGER NOT NULL,\n" +
|
||||
" id INTEGER NOT NULL,\n" +
|
||||
" name TEXT NOT NULL,\n" +
|
||||
" PRIMARY KEY(profileId, id)\n" +
|
||||
")")
|
||||
database.execSQL("CREATE TABLE IF NOT EXISTS noticeTypes (\n" +
|
||||
" profileId INTEGER NOT NULL,\n" +
|
||||
" id INTEGER NOT NULL,\n" +
|
||||
" name TEXT NOT NULL,\n" +
|
||||
" PRIMARY KEY(profileId, id)\n" +
|
||||
")")
|
||||
database.execSQL("CREATE TABLE IF NOT EXISTS attendanceTypes (\n" +
|
||||
" profileId INTEGER NOT NULL,\n" +
|
||||
" id INTEGER NOT NULL,\n" +
|
||||
" name TEXT NOT NULL,\n" +
|
||||
" type INTEGER NOT NULL,\n" +
|
||||
" color INTEGER NOT NULL,\n" +
|
||||
" PRIMARY KEY(profileId, id)\n" +
|
||||
")")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_62_63: Migration = object : Migration(62, 63) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE profiles ADD COLUMN accountNameLong TEXT DEFAULT NULL")
|
||||
database.execSQL("ALTER TABLE profiles ADD COLUMN studentClassName TEXT DEFAULT NULL")
|
||||
database.execSQL("ALTER TABLE profiles ADD COLUMN studentSchoolYear TEXT DEFAULT NULL")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_63_64: Migration = object : Migration(63, 64) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
//database.execSQL("ALTER TABLE lessons RENAME TO lessonsOld;");
|
||||
database.execSQL("CREATE TABLE timetable (" +
|
||||
"profileId INTEGER NOT NULL," +
|
||||
"id INTEGER NOT NULL," +
|
||||
"type INTEGER NOT NULL," +
|
||||
"date TEXT DEFAULT NULL," +
|
||||
"lessonNumber INTEGER DEFAULT NULL," +
|
||||
"startTime TEXT DEFAULT NULL," +
|
||||
"endTime TEXT DEFAULT NULL," +
|
||||
"subjectId INTEGER DEFAULT NULL," +
|
||||
"teacherId INTEGER DEFAULT NULL," +
|
||||
"teamId INTEGER DEFAULT NULL," +
|
||||
"classroom TEXT DEFAULT NULL," +
|
||||
"oldDate TEXT DEFAULT NULL," +
|
||||
"oldLessonNumber INTEGER DEFAULT NULL," +
|
||||
"oldStartTime TEXT DEFAULT NULL," +
|
||||
"oldEndTime TEXT DEFAULT NULL," +
|
||||
"oldSubjectId INTEGER DEFAULT NULL," +
|
||||
"oldTeacherId INTEGER DEFAULT NULL," +
|
||||
"oldTeamId INTEGER DEFAULT NULL," +
|
||||
"oldClassroom TEXT DEFAULT NULL," +
|
||||
"PRIMARY KEY(id));")
|
||||
database.execSQL("CREATE INDEX index_lessons_profileId_type_date ON timetable (profileId, type, date);")
|
||||
database.execSQL("CREATE INDEX index_lessons_profileId_type_oldDate ON timetable (profileId, type, oldDate);")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_64_65: Migration = object : Migration(64, 65) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE config (" +
|
||||
"profileId INTEGER NOT NULL DEFAULT -1," +
|
||||
"`key` TEXT NOT NULL," +
|
||||
"value TEXT NOT NULL," +
|
||||
"PRIMARY KEY(profileId, `key`));")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_65_66: Migration = object : Migration(65, 66) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
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`));")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_66_67: Migration = object : Migration(66, 67) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DELETE FROM grades WHERE (gradeId=-1 OR gradeId=-2) AND gradeType=20")
|
||||
database.execSQL("DELETE FROM metadata WHERE (thingId=-1 OR thingId=-2) AND thingType=1")
|
||||
database.execSQL("ALTER TABLE gradeCategories RENAME TO _gradeCategories")
|
||||
database.execSQL("CREATE TABLE gradeCategories (" +
|
||||
"profileId INTEGER NOT NULL," +
|
||||
"categoryId INTEGER NOT NULL," +
|
||||
"weight REAL NOT NULL," +
|
||||
"color INTEGER NOT NULL," +
|
||||
"`text` TEXT," +
|
||||
"columns TEXT," +
|
||||
"valueFrom REAL NOT NULL," +
|
||||
"valueTo REAL NOT NULL," +
|
||||
"type INTEGER NOT NULL," +
|
||||
"PRIMARY KEY(profileId, categoryId, type))")
|
||||
database.execSQL("INSERT INTO gradeCategories (profileId, categoryId, weight, color," +
|
||||
"`text`, columns, valueFrom, valueTo, type) " +
|
||||
"SELECT profileId, categoryId, weight, color, `text`, columns, valueFrom," +
|
||||
"valueTo, type FROM _gradeCategories")
|
||||
database.execSQL("DROP TABLE _gradeCategories")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_67_68: Migration = object : Migration(67, 68) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
/* Migration from crc16 to crc32 id */
|
||||
database.execSQL("DELETE FROM announcements")
|
||||
database.execSQL("DELETE FROM metadata WHERE thingType=7")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_68_69: Migration = object : Migration(68, 69) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE loginStores ADD COLUMN loginStoreMode INTEGER NOT NULL DEFAULT 0")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_69_70: Migration = object : Migration(69, 70) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE announcements ADD COLUMN announcementIdString TEXT DEFAULT NULL")
|
||||
database.execSQL("DELETE FROM announcements")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_70_71: Migration = object : Migration(70, 71) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DELETE FROM messages WHERE profileId IN (SELECT profileId FROM profiles WHERE archived = 0);")
|
||||
database.execSQL("DELETE FROM messageRecipients WHERE profileId IN (SELECT profileId FROM profiles WHERE archived = 0);")
|
||||
database.execSQL("DELETE FROM teachers WHERE profileId IN (SELECT profileId FROM profiles WHERE archived = 0);")
|
||||
database.execSQL("DELETE FROM endpointTimers WHERE profileId IN (SELECT profileId FROM profiles WHERE archived = 0);")
|
||||
database.execSQL("DELETE FROM metadata WHERE profileId IN (SELECT profileId FROM profiles WHERE archived = 0) AND thingType = 8;")
|
||||
database.execSQL("UPDATE profiles SET empty = 1 WHERE archived = 0;")
|
||||
database.execSQL("UPDATE profiles SET lastReceiversSync = 0 WHERE archived = 0;")
|
||||
database.execSQL("INSERT INTO config (profileId, `key`, value) VALUES (-1, \"runSync\", \"true\");")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_71_72: Migration = object : Migration(71, 72) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE loginStores RENAME to _loginStores;")
|
||||
database.execSQL("CREATE TABLE loginStores(" +
|
||||
"loginStoreId INTEGER NOT NULL," +
|
||||
"loginStoreType INTEGER NOT NULL," +
|
||||
"loginStoreMode INTEGER NOT NULL," +
|
||||
"loginStoreData TEXT NOT NULL," +
|
||||
"PRIMARY KEY(loginStoreId));")
|
||||
database.execSQL("INSERT INTO loginStores " +
|
||||
"(loginStoreId, loginStoreType, loginStoreMode, loginStoreData) " +
|
||||
"SELECT loginStoreId, loginStoreType, loginStoreMode, loginStoreData " +
|
||||
"FROM _loginStores;")
|
||||
database.execSQL("DROP TABLE _loginStores;")
|
||||
database.execSQL("ALTER TABLE profiles RENAME TO _profiles_old;")
|
||||
database.execSQL("CREATE TABLE profiles (\n" +
|
||||
"profileId INTEGER NOT NULL, name TEXT NOT NULL, subname TEXT, image TEXT DEFAULT NULL, \n" +
|
||||
"studentNameLong TEXT NOT NULL, studentNameShort TEXT NOT NULL, accountName TEXT, \n" +
|
||||
"studentData TEXT NOT NULL, empty INTEGER NOT NULL DEFAULT 1, archived INTEGER NOT NULL DEFAULT 0, \n" +
|
||||
"syncEnabled INTEGER NOT NULL DEFAULT 1, enableSharedEvents INTEGER NOT NULL DEFAULT 1, registration INTEGER NOT NULL DEFAULT 0, \n" +
|
||||
"userCode TEXT NOT NULL DEFAULT \"\", studentNumber INTEGER NOT NULL DEFAULT -1, studentClassName TEXT DEFAULT NULL, \n" +
|
||||
"studentSchoolYearStart INTEGER NOT NULL, dateSemester1Start TEXT NOT NULL, dateSemester2Start TEXT NOT NULL, \n" +
|
||||
"dateYearEnd TEXT NOT NULL, disabledNotifications TEXT DEFAULT NULL, lastReceiversSync INTEGER NOT NULL DEFAULT 0, \n" +
|
||||
"loginStoreId INTEGER NOT NULL, loginStoreType INTEGER NOT NULL, PRIMARY KEY(profileId));")
|
||||
database.execSQL("INSERT INTO profiles (profileId, name, subname, image, studentNameLong, studentNameShort, accountName, \n" +
|
||||
"userCode, studentData, empty, archived, syncEnabled, enableSharedEvents, registration, studentNumber, studentSchoolYearStart, \n" +
|
||||
"dateSemester1Start, dateSemester2Start, dateYearEnd, lastReceiversSync, loginStoreId, loginStoreType \n" +
|
||||
") SELECT profileId, name, subname, image, studentNameLong, studentNameShort, accountNameLong, \"\" AS userCode, studentData, \n" +
|
||||
"empty, archived, syncEnabled, enableSharedEvents, registration, studentNumber, SUBSTR(dateSemester1Start, 0, 5) AS studentSchoolYearStart, \n" +
|
||||
"dateSemester1Start, dateSemester2Start, dateYearEnd, lastReceiversSync, _profiles_old.loginStoreId, loginStoreType FROM _profiles_old \n" +
|
||||
"JOIN loginStores ON loginStores.loginStoreId = _profiles_old.loginStoreId \n" +
|
||||
"WHERE profileId >= 0;")
|
||||
database.execSQL("DROP TABLE _profiles_old;")
|
||||
// MIGRACJA userCode - mobidziennik
|
||||
database.execSQL("DROP TABLE IF EXISTS _userCodes;")
|
||||
database.execSQL("CREATE TABLE _userCodes (profileId INTEGER, loginData TEXT, studentData TEXT, userCode TEXT, serverName TEXT, username TEXT, studentId TEXT);")
|
||||
database.execSQL("DELETE FROM _userCodes;")
|
||||
database.execSQL("INSERT INTO _userCodes SELECT profileId, loginStores.loginStoreData, studentData, \"\", \"\", \"\", \"\" FROM profiles JOIN loginStores ON loginStores.loginStoreId = profiles.loginStoreId WHERE profiles.loginStoreType = 1;")
|
||||
database.execSQL("UPDATE _userCodes SET serverName = SUBSTR(loginData, instr(loginData, '\"serverName\":\"')+14);")
|
||||
database.execSQL("UPDATE _userCodes SET serverName = SUBSTR(serverName, 0, instr(serverName, '\",')+instr(serverName, '\"}')-(instr(serverName, '\"}')*min(instr(serverName, '\",'), 1)));")
|
||||
database.execSQL("UPDATE _userCodes SET username = SUBSTR(loginData, instr(loginData, '\"username\":\"')+12);")
|
||||
database.execSQL("UPDATE _userCodes SET username = SUBSTR(username, 0, instr(username, '\",')+instr(username, '\"}')-(instr(username, '\"}')*min(instr(username, '\",'), 1)));")
|
||||
database.execSQL("UPDATE _userCodes SET studentId = SUBSTR(studentData, instr(studentData, '\"studentId\":')+12);")
|
||||
database.execSQL("UPDATE _userCodes SET studentId = SUBSTR(studentId, 0, instr(studentId, ',')+instr(studentId, '}')-(instr(studentId, '}')*min(instr(studentId, ','), 1)));")
|
||||
database.execSQL("UPDATE _userCodes SET userCode = serverName||\":\"||username||\":\"||studentId;")
|
||||
database.execSQL("UPDATE profiles SET userCode = (SELECT userCode FROM _userCodes WHERE profileId = profiles.profileId) WHERE profileId IN (SELECT profileId FROM _userCodes);")
|
||||
// MIGRACJA userCode - librus
|
||||
database.execSQL("DROP TABLE IF EXISTS _userCodes;")
|
||||
database.execSQL("CREATE TABLE _userCodes (profileId INTEGER, loginData TEXT, studentData TEXT, userCode TEXT, schoolName TEXT, accountLogin TEXT);")
|
||||
database.execSQL("DELETE FROM _userCodes;")
|
||||
database.execSQL("INSERT INTO _userCodes SELECT profileId, loginStores.loginStoreData, studentData, \"\", \"\", \"\" FROM profiles JOIN loginStores ON loginStores.loginStoreId = profiles.loginStoreId WHERE profiles.loginStoreType = 2;")
|
||||
database.execSQL("UPDATE _userCodes SET schoolName = SUBSTR(studentData, instr(studentData, '\"schoolName\":\"')+14);")
|
||||
database.execSQL("UPDATE _userCodes SET schoolName = SUBSTR(schoolName, 0, instr(schoolName, '\",')+instr(schoolName, '\"}')-(instr(schoolName, '\"}')*min(instr(schoolName, '\",'), 1)));")
|
||||
database.execSQL("UPDATE _userCodes SET accountLogin = SUBSTR(studentData, instr(studentData, '\"accountLogin\":\"')+16);")
|
||||
database.execSQL("UPDATE _userCodes SET accountLogin = SUBSTR(accountLogin, 0, instr(accountLogin, '\",')+instr(accountLogin, '\"}')-(instr(accountLogin, '\"}')*min(instr(accountLogin, '\",'), 1)));")
|
||||
database.execSQL("UPDATE _userCodes SET userCode = schoolName||\":\"||accountLogin;")
|
||||
database.execSQL("UPDATE profiles SET userCode = (SELECT userCode FROM _userCodes WHERE profileId = profiles.profileId) WHERE profileId IN (SELECT profileId FROM _userCodes);")
|
||||
// MIGRACJA userCode - iuczniowie
|
||||
database.execSQL("DROP TABLE IF EXISTS _userCodes;")
|
||||
database.execSQL("CREATE TABLE _userCodes (profileId INTEGER, loginData TEXT, studentData TEXT, userCode TEXT, schoolName TEXT, username TEXT, registerId TEXT);")
|
||||
database.execSQL("DELETE FROM _userCodes;")
|
||||
database.execSQL("INSERT INTO _userCodes SELECT profileId, loginStores.loginStoreData, studentData, \"\", \"\", \"\", \"\" FROM profiles JOIN loginStores ON loginStores.loginStoreId = profiles.loginStoreId WHERE profiles.loginStoreType = 3;")
|
||||
database.execSQL("UPDATE _userCodes SET schoolName = SUBSTR(loginData, instr(loginData, '\"schoolName\":\"')+14);")
|
||||
database.execSQL("UPDATE _userCodes SET schoolName = SUBSTR(schoolName, 0, instr(schoolName, '\",')+instr(schoolName, '\"}')-(instr(schoolName, '\"}')*min(instr(schoolName, '\",'), 1)));")
|
||||
database.execSQL("UPDATE _userCodes SET username = SUBSTR(loginData, instr(loginData, '\"username\":\"')+12);")
|
||||
database.execSQL("UPDATE _userCodes SET username = SUBSTR(username, 0, instr(username, '\",')+instr(username, '\"}')-(instr(username, '\"}')*min(instr(username, '\",'), 1)));")
|
||||
database.execSQL("UPDATE _userCodes SET registerId = SUBSTR(studentData, instr(studentData, '\"registerId\":')+13);")
|
||||
database.execSQL("UPDATE _userCodes SET registerId = SUBSTR(registerId, 0, instr(registerId, ',')+instr(registerId, '}')-(instr(registerId, '}')*min(instr(registerId, ','), 1)));")
|
||||
database.execSQL("UPDATE _userCodes SET userCode = schoolName||\":\"||username||\":\"||registerId;")
|
||||
database.execSQL("UPDATE profiles SET userCode = (SELECT userCode FROM _userCodes WHERE profileId = profiles.profileId) WHERE profileId IN (SELECT profileId FROM _userCodes);")
|
||||
// MIGRACJA userCode - vulcan
|
||||
database.execSQL("DROP TABLE IF EXISTS _userCodes;")
|
||||
database.execSQL("CREATE TABLE _userCodes (profileId INTEGER, loginData TEXT, studentData TEXT, userCode TEXT, schoolName TEXT, studentId TEXT);")
|
||||
database.execSQL("DELETE FROM _userCodes;")
|
||||
database.execSQL("INSERT INTO _userCodes SELECT profileId, loginStores.loginStoreData, studentData, \"\", \"\", \"\" FROM profiles JOIN loginStores ON loginStores.loginStoreId = profiles.loginStoreId WHERE profiles.loginStoreType = 4;")
|
||||
database.execSQL("UPDATE _userCodes SET schoolName = SUBSTR(studentData, instr(studentData, '\"schoolName\":\"')+14);")
|
||||
database.execSQL("UPDATE _userCodes SET schoolName = SUBSTR(schoolName, 0, instr(schoolName, '\",')+instr(schoolName, '\"}')-(instr(schoolName, '\"}')*min(instr(schoolName, '\",'), 1)));")
|
||||
database.execSQL("UPDATE _userCodes SET studentId = SUBSTR(studentData, instr(studentData, '\"studentId\":')+12);")
|
||||
database.execSQL("UPDATE _userCodes SET studentId = SUBSTR(studentId, 0, instr(studentId, ',')+instr(studentId, '}')-(instr(studentId, '}')*min(instr(studentId, ','), 1)));")
|
||||
database.execSQL("UPDATE _userCodes SET userCode = schoolName||\":\"||studentId;")
|
||||
database.execSQL("UPDATE profiles SET userCode = (SELECT userCode FROM _userCodes WHERE profileId = profiles.profileId) WHERE profileId IN (SELECT profileId FROM _userCodes);")
|
||||
// MIGRACJA userCode - edudziennik
|
||||
database.execSQL("DROP TABLE IF EXISTS _userCodes;")
|
||||
database.execSQL("CREATE TABLE _userCodes (profileId INTEGER, loginData TEXT, studentData TEXT, userCode TEXT, schoolName TEXT, email TEXT, studentId TEXT);")
|
||||
database.execSQL("DELETE FROM _userCodes;")
|
||||
database.execSQL("INSERT INTO _userCodes SELECT profileId, loginStores.loginStoreData, studentData, \"\", \"\", \"\", \"\" FROM profiles JOIN loginStores ON loginStores.loginStoreId = profiles.loginStoreId WHERE profiles.loginStoreType = 5;")
|
||||
database.execSQL("UPDATE _userCodes SET schoolName = SUBSTR(studentData, instr(studentData, '\"schoolName\":\"')+14);")
|
||||
database.execSQL("UPDATE _userCodes SET schoolName = SUBSTR(schoolName, 0, instr(schoolName, '\",')+instr(schoolName, '\"}')-(instr(schoolName, '\"}')*min(instr(schoolName, '\",'), 1)));")
|
||||
database.execSQL("UPDATE _userCodes SET email = SUBSTR(loginData, instr(loginData, '\"email\":\"')+9);")
|
||||
database.execSQL("UPDATE _userCodes SET email = SUBSTR(email, 0, instr(email, '\",')+instr(email, '\"}')-(instr(email, '\"}')*min(instr(email, '\",'), 1)));")
|
||||
database.execSQL("UPDATE _userCodes SET studentId = SUBSTR(studentData, instr(studentData, '\"studentId\":\"')+13);")
|
||||
database.execSQL("UPDATE _userCodes SET studentId = SUBSTR(studentId, 0, instr(studentId, '\",')+instr(studentId, '\"}')-(instr(studentId, '\"}')*min(instr(studentId, '\",'), 1)));")
|
||||
database.query("SELECT profileId, studentId FROM _userCodes;").use { cursor ->
|
||||
while (cursor.moveToNext()) {
|
||||
val profileId = cursor.getInt(0)
|
||||
val crc = cursor.getString(1).crc32()
|
||||
database.execSQL("UPDATE _userCodes SET studentId = $crc WHERE profileId = $profileId")
|
||||
}
|
||||
}
|
||||
database.execSQL("UPDATE _userCodes SET userCode = schoolName||\":\"||email||\":\"||studentId;")
|
||||
database.execSQL("UPDATE profiles SET userCode = (SELECT userCode FROM _userCodes WHERE profileId = profiles.profileId) WHERE profileId IN (SELECT profileId FROM _userCodes);")
|
||||
database.execSQL("DROP TABLE _userCodes;")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_72_73: Migration = object : Migration(72, 73) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) { // Mark as seen all lucky number metadata.
|
||||
database.execSQL("UPDATE metadata SET seen=1 WHERE thingType=10")
|
||||
database.execSQL("DROP TABLE lessons")
|
||||
database.execSQL("DROP TABLE lessonChanges")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_73_74: Migration = object : Migration(73, 74) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE librusLessons (" +
|
||||
"profileId INTEGER NOT NULL," +
|
||||
"lessonId INTEGER NOT NULL," +
|
||||
"teacherId INTEGER NOT NULL," +
|
||||
"subjectId INTEGER NOT NULL," +
|
||||
"teamId INTEGER," +
|
||||
"PRIMARY KEY(profileId, lessonId));")
|
||||
database.execSQL("CREATE INDEX index_librusLessons_profileId ON librusLessons (profileId);")
|
||||
}
|
||||
}
|
||||
private val MIGRATION_74_75: Migration = object : Migration(74, 75) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE timetable RENAME TO _timetable;")
|
||||
database.execSQL("CREATE TABLE timetable (profileId INTEGER NOT NULL,id INTEGER NOT NULL,type INTEGER NOT NULL,date TEXT DEFAULT NULL,lessonNumber INTEGER DEFAULT NULL,startTime TEXT DEFAULT NULL,endTime TEXT DEFAULT NULL,subjectId INTEGER DEFAULT NULL,teacherId INTEGER DEFAULT NULL,teamId INTEGER DEFAULT NULL,classroom TEXT DEFAULT NULL,oldDate TEXT DEFAULT NULL,oldLessonNumber INTEGER DEFAULT NULL,oldStartTime TEXT DEFAULT NULL,oldEndTime TEXT DEFAULT NULL,oldSubjectId INTEGER DEFAULT NULL,oldTeacherId INTEGER DEFAULT NULL,oldTeamId INTEGER DEFAULT NULL,oldClassroom TEXT DEFAULT NULL,PRIMARY KEY(profileId, id));")
|
||||
database.execSQL("INSERT INTO timetable (profileId, id, type, date, lessonNumber, startTime, endTime, subjectId, teacherId, teamId, classroom, oldDate, oldLessonNumber, oldStartTime, oldEndTime, oldSubjectId, oldTeacherId, oldTeamId, oldClassroom) SELECT profileId, id, type, date, lessonNumber, startTime, endTime, subjectId, teacherId, teamId, classroom, oldDate, oldLessonNumber, oldStartTime, oldEndTime, oldSubjectId, oldTeacherId, oldTeamId, oldClassroom FROM _timetable;")
|
||||
database.execSQL("DROP TABLE _timetable;")
|
||||
database.execSQL("CREATE INDEX index_lessons_profileId_type_date ON timetable (profileId, type, date);")
|
||||
database.execSQL("CREATE INDEX index_lessons_profileId_type_oldDate ON timetable (profileId, type, oldDate);")
|
||||
}
|
||||
}
|
||||
|
||||
operator fun invoke(context: Context) = instance ?: synchronized(LOCK) {
|
||||
instance ?: buildDatabase(context).also { instance = it }
|
||||
}
|
||||
|
||||
private fun buildDatabase(context: Context) = Room.databaseBuilder(
|
||||
context.applicationContext,
|
||||
AppDb::class.java,
|
||||
"edziennik.db"
|
||||
).addMigrations(
|
||||
MIGRATION_11_12,
|
||||
MIGRATION_12_13,
|
||||
MIGRATION_13_14,
|
||||
MIGRATION_14_15,
|
||||
MIGRATION_15_16,
|
||||
MIGRATION_16_17,
|
||||
MIGRATION_17_18,
|
||||
MIGRATION_18_19,
|
||||
MIGRATION_19_20,
|
||||
MIGRATION_20_21,
|
||||
MIGRATION_21_22,
|
||||
MIGRATION_22_23,
|
||||
MIGRATION_23_24,
|
||||
MIGRATION_24_25,
|
||||
MIGRATION_25_26,
|
||||
MIGRATION_26_27,
|
||||
MIGRATION_27_28,
|
||||
MIGRATION_28_29,
|
||||
MIGRATION_29_30,
|
||||
MIGRATION_30_31,
|
||||
MIGRATION_31_32,
|
||||
MIGRATION_32_33,
|
||||
MIGRATION_33_34,
|
||||
MIGRATION_34_35,
|
||||
MIGRATION_35_36,
|
||||
MIGRATION_36_37,
|
||||
MIGRATION_37_38,
|
||||
MIGRATION_38_39,
|
||||
MIGRATION_39_40,
|
||||
MIGRATION_40_41,
|
||||
MIGRATION_41_42,
|
||||
MIGRATION_42_43,
|
||||
MIGRATION_43_44,
|
||||
MIGRATION_44_45,
|
||||
MIGRATION_45_46,
|
||||
MIGRATION_46_47,
|
||||
MIGRATION_47_48,
|
||||
MIGRATION_48_49,
|
||||
MIGRATION_49_50,
|
||||
MIGRATION_50_51,
|
||||
MIGRATION_51_52,
|
||||
MIGRATION_52_53,
|
||||
MIGRATION_53_54,
|
||||
MIGRATION_54_55,
|
||||
MIGRATION_55_56,
|
||||
MIGRATION_56_57,
|
||||
MIGRATION_57_58,
|
||||
MIGRATION_58_59,
|
||||
MIGRATION_59_60,
|
||||
MIGRATION_60_61,
|
||||
MIGRATION_61_62,
|
||||
MIGRATION_62_63,
|
||||
MIGRATION_63_64,
|
||||
MIGRATION_64_65,
|
||||
MIGRATION_65_66,
|
||||
MIGRATION_66_67,
|
||||
MIGRATION_67_68,
|
||||
MIGRATION_68_69,
|
||||
MIGRATION_69_70,
|
||||
MIGRATION_70_71,
|
||||
MIGRATION_71_72,
|
||||
MIGRATION_72_73,
|
||||
MIGRATION_73_74,
|
||||
MIGRATION_74_75
|
||||
).allowMainThreadQueries().build()
|
||||
}
|
||||
}
|
@ -16,8 +16,8 @@ import androidx.sqlite.db.SupportSQLiteQuery;
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Announcement;
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull;
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata;
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_ANNOUNCEMENT;
|
||||
|
||||
@ -69,4 +69,14 @@ public abstract class AnnouncementDao {
|
||||
public List<AnnouncementFull> getNotNotifiedNow(int profileId) {
|
||||
return getAllNow(profileId, "notified = 0");
|
||||
}
|
||||
|
||||
@Query("SELECT " +
|
||||
"*, " +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName " +
|
||||
"FROM announcements " +
|
||||
"LEFT JOIN teachers USING(profileId, teacherId) " +
|
||||
"LEFT JOIN metadata ON announcementId = thingId AND thingType = "+TYPE_ANNOUNCEMENT+" AND metadata.profileId = announcements.profileId " +
|
||||
"WHERE notified = 0 " +
|
||||
"ORDER BY addedDate DESC")
|
||||
public abstract List<AnnouncementFull> getNotNotifiedNow();
|
||||
}
|
||||
|
@ -5,20 +5,20 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.dao;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery;
|
||||
import androidx.sqlite.db.SupportSQLiteQuery;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.RawQuery;
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery;
|
||||
import androidx.sqlite.db.SupportSQLiteQuery;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Attendance;
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_ATTENDANCE;
|
||||
|
||||
@Dao
|
||||
@ -72,6 +72,13 @@ public abstract class AttendanceDao {
|
||||
return getAllNow(profileId, "notified = 0");
|
||||
}
|
||||
|
||||
@Query("SELECT * FROM attendances " +
|
||||
"LEFT JOIN subjects USING(profileId, subjectId) " +
|
||||
"LEFT JOIN metadata ON attendanceId = thingId AND thingType = " + TYPE_ATTENDANCE + " AND metadata.profileId = attendances.profileId " +
|
||||
"WHERE notified = 0 " +
|
||||
"ORDER BY attendanceLessonDate DESC, attendanceStartTime DESC")
|
||||
public abstract List<AttendanceFull> getNotNotifiedNow();
|
||||
|
||||
// only absent and absent_excused count as absences
|
||||
// all the other types are counted as being present
|
||||
@Query("SELECT \n" +
|
||||
|
@ -34,7 +34,7 @@ public abstract class EventDao {
|
||||
public abstract long add(Event event);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract void addAll(List<Event> eventList);
|
||||
public abstract long[] addAll(List<Event> eventList);
|
||||
|
||||
@Query("DELETE FROM events WHERE profileId = :profileId")
|
||||
public abstract void clear(int profileId);
|
||||
@ -44,7 +44,7 @@ public abstract class EventDao {
|
||||
@Query("DELETE FROM metadata WHERE profileId = :profileId AND thingType = :thingType AND thingId = :thingId")
|
||||
public abstract void removeMetadata(int profileId, int thingType, long thingId);
|
||||
@Transaction
|
||||
public void remove(int profileId, int type, long id) {
|
||||
public void remove(int profileId, long type, long id) {
|
||||
remove(profileId, id);
|
||||
removeMetadata(profileId, type == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, id);
|
||||
}
|
||||
@ -88,7 +88,7 @@ public abstract class EventDao {
|
||||
public LiveData<List<EventFull>> getAllWhere(int profileId, String filter) {
|
||||
return getAll(profileId, filter);
|
||||
}
|
||||
public LiveData<List<EventFull>> getAllByType(int profileId, int type, String filter) {
|
||||
public LiveData<List<EventFull>> getAllByType(int profileId, long type, String filter) {
|
||||
return getAll(profileId, "eventType = "+type+" AND "+filter);
|
||||
}
|
||||
public LiveData<List<EventFull>> getAllByDate(int profileId, @NonNull Date date) {
|
||||
@ -125,6 +125,24 @@ public abstract class EventDao {
|
||||
return getAllNow(profileId, "notified = 0");
|
||||
}
|
||||
|
||||
@Query("SELECT eventId FROM events WHERE profileId = :profileId AND eventBlacklisted = 1")
|
||||
public abstract List<Long> getBlacklistedIds(int profileId);
|
||||
@Query("SELECT eventId FROM events WHERE eventBlacklisted = 1")
|
||||
public abstract List<Long> getBlacklistedIds();
|
||||
|
||||
@Query("SELECT " +
|
||||
"*, " +
|
||||
"eventTypes.eventTypeName AS typeName, " +
|
||||
"eventTypes.eventTypeColor AS typeColor " +
|
||||
"FROM events " +
|
||||
"LEFT JOIN subjects USING(profileId, subjectId) " +
|
||||
"LEFT JOIN eventTypes USING(profileId, eventType) " +
|
||||
"LEFT JOIN metadata ON eventId = thingId AND (thingType = " + TYPE_EVENT + " OR thingType = " + TYPE_HOMEWORK + ") AND metadata.profileId = events.profileId " +
|
||||
"WHERE events.eventBlacklisted = 0 AND notified = 0 " +
|
||||
"GROUP BY eventId " +
|
||||
"ORDER BY addedDate ASC")
|
||||
public abstract List<EventFull> getNotNotifiedNow();
|
||||
|
||||
public EventFull getByIdNow(int profileId, long eventId) {
|
||||
List<EventFull> eventList = getAllNow(profileId, "eventId = "+eventId);
|
||||
return eventList.size() == 0 ? null : eventList.get(0);
|
||||
@ -146,13 +164,13 @@ public abstract class EventDao {
|
||||
}
|
||||
|
||||
@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);
|
||||
public abstract void removeFutureWithType(int profileId, Date todayDate, long 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);
|
||||
public abstract void removeFutureExceptType(int profileId, Date todayDate, long exceptType);
|
||||
|
||||
@Transaction
|
||||
public void removeFutureExceptTypes(int profileId, Date todayDate, List<Integer> exceptTypes) {
|
||||
public void removeFutureExceptTypes(int profileId, Date todayDate, List<Long> exceptTypes) {
|
||||
removeFuture(profileId, todayDate, "eventType NOT IN " + exceptTypes.toString().replace('[', '(').replace(']', ')'));
|
||||
}
|
||||
|
||||
|
@ -4,14 +4,14 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.db.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.EventType;
|
||||
|
||||
@Dao
|
||||
@ -26,11 +26,14 @@ public interface EventTypeDao {
|
||||
void clear(int profileId);
|
||||
|
||||
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId AND eventType = :typeId")
|
||||
EventType getByIdNow(int profileId, int typeId);
|
||||
EventType getByIdNow(int profileId, long typeId);
|
||||
|
||||
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId")
|
||||
LiveData<List<EventType>> getAll(int profileId);
|
||||
|
||||
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId")
|
||||
List<EventType> getAllNow(int profileId);
|
||||
|
||||
@Query("SELECT * FROM eventTypes")
|
||||
List<EventType> getAllNow();
|
||||
}
|
||||
|
@ -83,6 +83,13 @@ public abstract class GradeDao {
|
||||
return getAllNow(profileId, "gradeParentId = "+parentId);
|
||||
}
|
||||
|
||||
@Query("SELECT * FROM grades " +
|
||||
"LEFT JOIN subjects USING(profileId, subjectId) " +
|
||||
"LEFT JOIN metadata ON gradeId = thingId AND thingType = " + TYPE_GRADE + " AND metadata.profileId = grades.profileId " +
|
||||
"WHERE notified = 0 " +
|
||||
"ORDER BY addedDate DESC")
|
||||
public abstract List<GradeFull> getNotNotifiedNow();
|
||||
|
||||
@RawQuery
|
||||
abstract GradeFull getNow(SupportSQLiteQuery query);
|
||||
public GradeFull getNow(int profileId, String filter) {
|
||||
|
@ -20,4 +20,7 @@ interface LibrusLessonDao {
|
||||
|
||||
@Query("SELECT * FROM librusLessons WHERE profileId = :profileId")
|
||||
fun getAllNow(profileId: Int): List<LibrusLesson>
|
||||
|
||||
@Query("DELETE FROM librusLessons WHERE profileId = :profileId")
|
||||
fun clear(profileId: Int)
|
||||
}
|
||||
|
@ -17,9 +17,9 @@ import androidx.sqlite.db.SupportSQLiteQuery;
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.LuckyNumber;
|
||||
import pl.szczodrzynski.edziennik.data.db.full.LuckyNumberFull;
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata;
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notice;
|
||||
import pl.szczodrzynski.edziennik.data.db.full.LuckyNumberFull;
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_LUCKY_NUMBER;
|
||||
@ -78,4 +78,10 @@ public abstract class LuckyNumberDao {
|
||||
public List<LuckyNumberFull> getNotNotifiedNow(int profileId) {
|
||||
return getAllNow(profileId, "notified = 0");
|
||||
}
|
||||
|
||||
@Query("SELECT * FROM luckyNumbers\n" +
|
||||
"LEFT JOIN metadata ON luckyNumberDate = thingId AND thingType = "+TYPE_LUCKY_NUMBER+" AND metadata.profileId = luckyNumbers.profileId " +
|
||||
"WHERE notified = 0 " +
|
||||
"ORDER BY addedDate DESC")
|
||||
public abstract List<LuckyNumberFull> getNotNotifiedNow();
|
||||
}
|
||||
|
@ -17,8 +17,8 @@ import androidx.sqlite.db.SupportSQLiteQuery;
|
||||
import java.util.List;
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Message;
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull;
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Metadata;
|
||||
import pl.szczodrzynski.edziennik.data.db.full.MessageFull;
|
||||
|
||||
import static pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_DELETED;
|
||||
import static pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED;
|
||||
@ -101,4 +101,14 @@ public abstract class MessageDao {
|
||||
public List<MessageFull> getReceivedNotNotifiedNow(int profileId) {
|
||||
return getReceivedNow(profileId, "notified = 0");
|
||||
}
|
||||
|
||||
@Query("SELECT " +
|
||||
"*, " +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS senderFullName " +
|
||||
"FROM messages " +
|
||||
"LEFT JOIN teachers ON teachers.profileId = messages.profileId AND teacherId = senderId " +
|
||||
"LEFT JOIN metadata ON messageId = thingId AND thingType = "+TYPE_MESSAGE+" AND metadata.profileId = messages.profileId " +
|
||||
"WHERE messageType = 0 AND notified = 0 " +
|
||||
"ORDER BY addedDate DESC")
|
||||
public abstract List<MessageFull> getReceivedNotNotifiedNow();
|
||||
}
|
||||
|
@ -40,6 +40,9 @@ public abstract class MetadataDao {
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
public abstract void addAllIgnore(List<Metadata> metadataList);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
public abstract void addAllReplace(List<Metadata> metadataList);
|
||||
|
||||
@Query("UPDATE metadata SET seen = :seen WHERE thingId = :thingId AND thingType = :thingType AND profileId = :profileId")
|
||||
abstract void updateSeen(int profileId, int thingType, long thingId, boolean seen);
|
||||
|
||||
@ -165,6 +168,9 @@ public abstract class MetadataDao {
|
||||
@Query("UPDATE metadata SET notified = :notified WHERE profileId = :profileId")
|
||||
public abstract void setAllNotified(int profileId, boolean notified);
|
||||
|
||||
@Query("UPDATE metadata SET notified = :notified")
|
||||
public abstract void setAllNotified(boolean notified);
|
||||
|
||||
|
||||
|
||||
@Query("SELECT count() FROM metadata WHERE profileId = :profileId AND thingType = :thingType AND seen = 0")
|
||||
|
@ -5,13 +5,13 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.dao;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery;
|
||||
import androidx.sqlite.db.SupportSQLiteQuery;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.RawQuery;
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery;
|
||||
import androidx.sqlite.db.SupportSQLiteQuery;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -69,4 +69,14 @@ public abstract class NoticeDao {
|
||||
public List<NoticeFull> getNotNotifiedNow(int profileId) {
|
||||
return getAllNow(profileId, "notified = 0");
|
||||
}
|
||||
|
||||
@Query("SELECT " +
|
||||
"*, " +
|
||||
"teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName " +
|
||||
"FROM notices " +
|
||||
"LEFT JOIN teachers USING(profileId, teacherId) " +
|
||||
"LEFT JOIN metadata ON noticeId = thingId AND thingType = "+TYPE_NOTICE+" AND metadata.profileId = notices.profileId " +
|
||||
"WHERE notified = 0 " +
|
||||
"ORDER BY addedDate DESC")
|
||||
public abstract List<NoticeFull> getNotNotifiedNow();
|
||||
}
|
||||
|
@ -52,9 +52,12 @@ interface ProfileDao {
|
||||
@Query("SELECT profiles.* FROM teams JOIN profiles USING(profileId) WHERE teamCode = :teamCode AND registration = " + Profile.REGISTRATION_ENABLED + " AND enableSharedEvents = 1")
|
||||
fun getByTeamCodeNowWithRegistration(teamCode: String?): List<Profile>
|
||||
|
||||
@get:Query("SELECT profileId FROM profiles WHERE profileId >= 0 ORDER BY profileId ASC LIMIT 1")
|
||||
@get:Query("SELECT profileId FROM profiles WHERE profileId > 0 ORDER BY profileId ASC LIMIT 1")
|
||||
val firstId: Int?
|
||||
|
||||
@get:Query("SELECT profileId FROM profiles WHERE profileId >= 0 ORDER BY profileId DESC LIMIT 1")
|
||||
@get:Query("SELECT profileId FROM profiles WHERE profileId > 0 ORDER BY profileId DESC LIMIT 1")
|
||||
val lastId: Int?
|
||||
|
||||
@Query("UPDATE profiles SET empty = 0")
|
||||
fun setAllNotEmpty()
|
||||
}
|
||||
|
@ -107,4 +107,18 @@ interface TimetableDao {
|
||||
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>
|
||||
|
||||
@Query("""
|
||||
SELECT
|
||||
timetable.*,
|
||||
teachers.teacherName ||" "|| teachers.teacherSurname AS teacherName,
|
||||
oldT.teacherName ||" "|| oldT.teacherSurname AS oldTeacherName,
|
||||
metadata.seen, metadata.notified, metadata.addedDate
|
||||
FROM timetable
|
||||
LEFT JOIN teachers USING(profileId, teacherId)
|
||||
LEFT JOIN teachers AS oldT ON timetable.profileId = oldT.profileId AND timetable.oldTeacherId = oldT.teacherId
|
||||
LEFT JOIN metadata ON id = thingId AND thingType = ${Metadata.TYPE_LESSON_CHANGE} AND metadata.profileId = timetable.profileId
|
||||
WHERE timetable.type NOT IN (${Lesson.TYPE_NORMAL}, ${Lesson.TYPE_NO_LESSONS}, ${Lesson.TYPE_SHIFTED_SOURCE}) AND metadata.notified = 0
|
||||
""")
|
||||
fun getNotNotifiedNow(): List<LessonFull>
|
||||
}
|
||||
|
@ -32,19 +32,19 @@ public class Event {
|
||||
public String topic;
|
||||
@ColumnInfo(name = "eventColor")
|
||||
public int color = -1;
|
||||
public static final int TYPE_UNDEFINED = -2;
|
||||
public static final int TYPE_HOMEWORK = -1;
|
||||
public static final int TYPE_DEFAULT = 0;
|
||||
public static final int TYPE_EXAM = 1;
|
||||
public static final int TYPE_SHORT_QUIZ = 2;
|
||||
public static final int TYPE_ESSAY = 3;
|
||||
public static final int TYPE_PROJECT = 4;
|
||||
public static final int TYPE_PT_MEETING = 5;
|
||||
public static final int TYPE_EXCURSION = 6;
|
||||
public static final int TYPE_READING = 7;
|
||||
public static final int TYPE_CLASS_EVENT = 8;
|
||||
public static final int TYPE_INFORMATION = 9;
|
||||
public static final int TYPE_TEACHER_ABSENCE = 10;
|
||||
public static final long TYPE_UNDEFINED = -2;
|
||||
public static final long TYPE_HOMEWORK = -1;
|
||||
public static final long TYPE_DEFAULT = 0;
|
||||
public static final long TYPE_EXAM = 1;
|
||||
public static final long TYPE_SHORT_QUIZ = 2;
|
||||
public static final long TYPE_ESSAY = 3;
|
||||
public static final long TYPE_PROJECT = 4;
|
||||
public static final long TYPE_PT_MEETING = 5;
|
||||
public static final long TYPE_EXCURSION = 6;
|
||||
public static final long TYPE_READING = 7;
|
||||
public static final long TYPE_CLASS_EVENT = 8;
|
||||
public static final long TYPE_INFORMATION = 9;
|
||||
public static final long TYPE_TEACHER_ABSENCE = 10;
|
||||
public static final int COLOR_HOMEWORK = 0xff795548;
|
||||
public static final int COLOR_DEFAULT = 0xffffc107;
|
||||
public static final int COLOR_EXAM = 0xfff44336;
|
||||
@ -58,7 +58,7 @@ public class Event {
|
||||
public static final int COLOR_INFORMATION = 0xff039be5;
|
||||
public static final int COLOR_TEACHER_ABSENCE = 0xff039be5;
|
||||
@ColumnInfo(name = "eventType")
|
||||
public int type = TYPE_DEFAULT;
|
||||
public long type = TYPE_DEFAULT;
|
||||
@ColumnInfo(name = "eventAddedManually")
|
||||
public boolean addedManually;
|
||||
@ColumnInfo(name = "eventSharedBy")
|
||||
@ -76,7 +76,7 @@ public class Event {
|
||||
@Ignore
|
||||
public Event() {}
|
||||
|
||||
public Event(int profileId, long id, Date eventDate, @Nullable Time startTime, String topic, int color, int type, boolean addedManually, long teacherId, long subjectId, long teamId)
|
||||
public Event(int profileId, long id, Date eventDate, @Nullable Time startTime, String topic, int color, long type, boolean addedManually, long teacherId, long subjectId, long teamId)
|
||||
{
|
||||
this.profileId = profileId;
|
||||
this.id = id;
|
||||
@ -97,8 +97,8 @@ public class Event {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Event clone() throws CloneNotSupportedException {
|
||||
return new Event(
|
||||
public Event clone() {
|
||||
Event event = new Event(
|
||||
profileId,
|
||||
id,
|
||||
eventDate.clone(),
|
||||
@ -111,6 +111,10 @@ public class Event {
|
||||
teacherId,
|
||||
teamId
|
||||
);
|
||||
event.sharedBy = sharedBy;
|
||||
event.sharedByName = sharedByName;
|
||||
event.blacklisted = blacklisted;
|
||||
return event;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -6,7 +6,6 @@ package pl.szczodrzynski.edziennik.data.db.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
|
||||
@ -14,8 +13,9 @@ import pl.szczodrzynski.edziennik.utils.models.Time
|
||||
indices = [
|
||||
Index(value = ["profileId", "type", "date"]),
|
||||
Index(value = ["profileId", "type", "oldDate"])
|
||||
])
|
||||
open class Lesson(val profileId: Int, @PrimaryKey var id: Long) {
|
||||
],
|
||||
primaryKeys = ["profileId", "id"])
|
||||
open class Lesson(val profileId: Int, var id: Long) {
|
||||
companion object {
|
||||
const val TYPE_NO_LESSONS = -1
|
||||
const val TYPE_NORMAL = 0
|
||||
|
@ -4,34 +4,18 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.db.entity
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_AUTO_ARCHIVING
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_ERROR
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_FEEDBACK_MESSAGE
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_GENERAL
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_LUCKY_NUMBER
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_ANNOUNCEMENT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_ATTENDANCE
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_EVENT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_GRADE
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_HOMEWORK
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_MESSAGE
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_NOTICE
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_NEW_SHARED_EVENT
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_SERVER_MESSAGE
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_TIMETABLE_CHANGED
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_TIMETABLE_LESSON_CHANGE
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Notification.Companion.TYPE_UPDATE
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
|
||||
@Entity(tableName = "notifications")
|
||||
data class Notification(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
val id: Int = 0,
|
||||
val id: Long = 0,
|
||||
|
||||
val title: String,
|
||||
val text: String,
|
||||
@ -41,7 +25,7 @@ data class Notification(
|
||||
val profileId: Int?,
|
||||
val profileName: String?,
|
||||
|
||||
var posted: Boolean = false,
|
||||
var posted: Boolean = true,
|
||||
|
||||
var viewId: Int? = null,
|
||||
var extras: JsonObject? = null,
|
||||
@ -59,6 +43,7 @@ data class Notification(
|
||||
const val TYPE_NEW_HOMEWORK = 10
|
||||
const val TYPE_NEW_SHARED_EVENT = 7
|
||||
const val TYPE_NEW_SHARED_HOMEWORK = 12
|
||||
const val TYPE_REMOVED_SHARED_EVENT = 18
|
||||
const val TYPE_NEW_MESSAGE = 8
|
||||
const val TYPE_NEW_NOTICE = 9
|
||||
const val TYPE_NEW_ATTENDANCE = 13
|
||||
@ -67,6 +52,10 @@ data class Notification(
|
||||
const val TYPE_NEW_ANNOUNCEMENT = 15
|
||||
const val TYPE_FEEDBACK_MESSAGE = 16
|
||||
const val TYPE_AUTO_ARCHIVING = 17
|
||||
|
||||
fun buildId(profileId: Int, type: Int, itemId: Long): Long {
|
||||
return 1000000000000 + profileId*10000000000 + type*100000000 + itemId;
|
||||
}
|
||||
}
|
||||
|
||||
fun addExtra(key: String, value: Long?): Notification {
|
||||
@ -100,4 +89,10 @@ data class Notification(
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun getPendingIntent(context: Context): PendingIntent {
|
||||
val intent = Intent(context, MainActivity::class.java)
|
||||
fillIntent(intent)
|
||||
return PendingIntent.getActivity(context, id.toInt(), intent, PendingIntent.FLAG_ONE_SHOT)
|
||||
}
|
||||
}
|
||||
|
@ -30,21 +30,21 @@ open class Profile(
|
||||
val loginStoreId: Int,
|
||||
val loginStoreType: Int,
|
||||
|
||||
override var name: String,
|
||||
override var subname: String?,
|
||||
override var name: String = "",
|
||||
override var subname: String? = null,
|
||||
|
||||
/**
|
||||
* The name of the student.
|
||||
* This doesn't change, no matter if it's a parent or student account.
|
||||
*/
|
||||
var studentNameLong: String,
|
||||
var studentNameShort: String,
|
||||
var studentNameLong: String = "",
|
||||
var studentNameShort: String = "",
|
||||
/**
|
||||
* A full name of the account owner.
|
||||
* If null, then it's a student account.
|
||||
* If not null, then it's a parent account with this name.
|
||||
*/
|
||||
var accountName: String?,
|
||||
var accountName: String? = null,
|
||||
|
||||
val studentData: JsonObject = JsonObject()
|
||||
|
||||
|
@ -45,6 +45,35 @@ public class EventFull extends Event {
|
||||
this.sharedByName = event.sharedByName;
|
||||
this.blacklisted = event.blacklisted;
|
||||
}
|
||||
public EventFull(EventFull event) {
|
||||
super(
|
||||
event.profileId,
|
||||
event.id,
|
||||
event.eventDate.clone(),
|
||||
event.startTime == null ? null : event.startTime.clone(),
|
||||
event.topic,
|
||||
event.color,
|
||||
event.type,
|
||||
event.addedManually,
|
||||
event.teacherId,
|
||||
event.subjectId,
|
||||
event.teamId
|
||||
);
|
||||
|
||||
this.sharedBy = event.sharedBy;
|
||||
this.sharedByName = event.sharedByName;
|
||||
this.blacklisted = event.blacklisted;
|
||||
this.typeName = event.typeName;
|
||||
this.typeColor = event.typeColor;
|
||||
this.teacherFullName = event.teacherFullName;
|
||||
this.subjectLongName = event.subjectLongName;
|
||||
this.subjectShortName = event.subjectShortName;
|
||||
this.teamName = event.teamName;
|
||||
this.teamCode = event.teamCode;
|
||||
this.seen = event.seen;
|
||||
this.notified = event.notified;
|
||||
this.addedDate = event.addedDate;
|
||||
}
|
||||
|
||||
public EventFull(Event event, Metadata metadata) {
|
||||
this(event);
|
||||
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-1-11.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.firebase
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import androidx.legacy.content.WakefulBroadcastReceiver
|
||||
import com.google.gson.JsonObject
|
||||
|
||||
class FirebaseBroadcastReceiver : WakefulBroadcastReceiver() {
|
||||
companion object {
|
||||
private const val TAG = "FirebaseBroadcast"
|
||||
}
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val extras = intent.extras
|
||||
val json = JsonObject()
|
||||
extras?.keySet()?.forEach { key ->
|
||||
extras.get(key)?.let {
|
||||
when (it) {
|
||||
is String -> json.addProperty(key, it)
|
||||
is Int -> json.addProperty(key, it)
|
||||
is Long -> json.addProperty(key, it)
|
||||
is Float -> json.addProperty(key, it)
|
||||
is Boolean -> json.addProperty(key, it)
|
||||
else -> json.addProperty(key, it.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "Intent(action=${intent?.action}, extras=$json)")
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-1-11.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.firebase
|
||||
|
||||
class FirebaseSendException(reason: String?) : Exception(reason) {
|
||||
companion object {
|
||||
const val ERROR_UNKNOWN = 0
|
||||
const val ERROR_INVALID_PARAMETERS = 1
|
||||
const val ERROR_SIZE = 2
|
||||
const val ERROR_TTL_EXCEEDED = 3
|
||||
const val ERROR_TOO_MANY_MESSAGES = 4
|
||||
}
|
||||
|
||||
val errorCode = when (reason) {
|
||||
"service_not_available" -> ERROR_TTL_EXCEEDED
|
||||
"toomanymessages" -> ERROR_TOO_MANY_MESSAGES
|
||||
"invalid_parameters" -> ERROR_INVALID_PARAMETERS
|
||||
"messagetoobig" -> ERROR_SIZE
|
||||
"missing_to" -> ERROR_INVALID_PARAMETERS
|
||||
else -> ERROR_UNKNOWN
|
||||
}
|
||||
}
|
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-1-11.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.data.firebase
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.PendingIntent
|
||||
import android.app.PendingIntent.CanceledException
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import com.google.firebase.iid.zzaq
|
||||
import com.google.firebase.iid.zzv
|
||||
import com.google.firebase.messaging.zzc
|
||||
import com.google.gson.JsonObject
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import java.util.*
|
||||
import com.google.firebase.messaging.zzo.zza as logNotificationOpen
|
||||
import com.google.firebase.messaging.zzo.zza as logNotificationReceived
|
||||
import com.google.firebase.messaging.zzo.zzb as logNotificationDismiss
|
||||
import com.google.firebase.messaging.zzo.zzd as shouldUploadMetrics
|
||||
|
||||
@SuppressLint("Registered")
|
||||
open class FirebaseService : zzc() {
|
||||
companion object {
|
||||
private const val TAG = "FirebaseService"
|
||||
}
|
||||
|
||||
private val messageQueue = ArrayDeque<String>(10)
|
||||
|
||||
open fun onMessageReceived(message: Message) = Unit
|
||||
open fun onDeletedMessages() = Unit
|
||||
open fun onMessageSent(messageId: String?) = Unit
|
||||
open fun onSendError(messageId: String?, exception: Exception) = Unit
|
||||
open fun onNewToken(token: String?) = Unit
|
||||
|
||||
// apparently this gets the correct intent from some
|
||||
// kind of queue inside Firebase's InstanceID Receiver
|
||||
final override fun zza(intent: Intent?) = zzaq.zza()?.zzb()
|
||||
final override fun zzb(intent: Intent?): Boolean {
|
||||
val action = intent?.action
|
||||
if (action == "com.google.firebase.messaging.NOTIFICATION_OPEN") {
|
||||
intent.getParcelableExtra<PendingIntent>("pending_intent")?.let {
|
||||
try {
|
||||
it.send()
|
||||
} catch (e: CanceledException) {
|
||||
Log.e(TAG, "Notification pending intent canceled")
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldUploadMetrics(intent)) {
|
||||
logNotificationOpen(intent)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
final override fun zzc(intent: Intent?) {
|
||||
val action = intent?.action
|
||||
val json = intent?.toJsonObject()
|
||||
Log.d(TAG, "zzc Intent(action=$action, extras=$json)")
|
||||
if (action == null || json == null)
|
||||
return
|
||||
|
||||
when (action) {
|
||||
"com.google.firebase.messaging.NOTIFICATION_DISMISS" -> {
|
||||
if (shouldUploadMetrics(intent)) {
|
||||
logNotificationDismiss(intent)
|
||||
}
|
||||
}
|
||||
"com.google.firebase.messaging.NEW_TOKEN" -> {
|
||||
onNewToken(json.getString("token"))
|
||||
}
|
||||
"com.google.android.c2dm.intent.RECEIVE",
|
||||
"com.google.firebase.messaging.RECEIVE_DIRECT_BOOT" -> {
|
||||
val messageId = json.getString("google.message_id")
|
||||
if (messageId != null) {
|
||||
// send back an acknowledgement to Google Play Services
|
||||
val ackBundle = Bundle(
|
||||
"google.message_id" to messageId
|
||||
)
|
||||
zzv.zza(this).zza(2, ackBundle)
|
||||
}
|
||||
// check for duplicate message
|
||||
// and add it to queue
|
||||
if (messageId.isNotNullNorEmpty()) {
|
||||
if (messageQueue.contains(messageId)) {
|
||||
Log.d(TAG, "Received duplicate message: $messageId")
|
||||
return
|
||||
}
|
||||
if (messageQueue.size >= 10)
|
||||
messageQueue.remove()
|
||||
messageQueue += messageId
|
||||
}
|
||||
// process the received message
|
||||
processMessage(messageId, json, intent)
|
||||
}
|
||||
else -> {
|
||||
Log.d(TAG, "Unknown intent action: $action")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processMessage(messageId: String?, json: JsonObject, intent: Intent) {
|
||||
// remove something that the original FMS removes
|
||||
json.remove("androidx.contentpager.content.wakelockid")
|
||||
|
||||
// get the message type
|
||||
when (val it = json.getString("message_type") ?: "gcm") {
|
||||
"gcm" -> { // 0
|
||||
if (shouldUploadMetrics(intent)) {
|
||||
logNotificationReceived(intent, null)
|
||||
}
|
||||
|
||||
onMessageReceived(Message(messageId, json))
|
||||
}
|
||||
"deleted_messages" -> { // 1
|
||||
onDeletedMessages()
|
||||
}
|
||||
"send_event" -> { // 2
|
||||
onMessageSent(messageId)
|
||||
}
|
||||
"send_error" -> { // 3
|
||||
onSendError(
|
||||
messageId ?: json.getString("message_id"),
|
||||
FirebaseSendException(json.getString("error"))
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
Log.w(TAG, "Received message with unknown type: $it")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Message(val messageId: String?, private val json: JsonObject) {
|
||||
val data = json.deepCopy()
|
||||
val from by lazy { s("test.from") ?: s("from") ?: "" }
|
||||
val to by lazy { s("google.to") }
|
||||
val messageType by lazy { s("message_type") }
|
||||
val collapseKey by lazy { s("collapse_key") }
|
||||
val sentTime by lazy { l("google.sent_time") }
|
||||
val ttl by lazy { i("google.ttl") }
|
||||
val originalPriority by lazy { getPriority(s("google.original_priority") ?: s("priority")) }
|
||||
val priority by lazy { getPriority(
|
||||
s("google.delivered_priority") ?: if (i("google.priority_reduced") == 1)
|
||||
"normal"
|
||||
else s("google.priority")
|
||||
) }
|
||||
val isNotificationMessage by lazy { isNotificationMessage(json) }
|
||||
val notificationTitle by lazy { s("gcm.notification.title") }
|
||||
val notificationText by lazy { s("gcm.notification.body") }
|
||||
|
||||
init {
|
||||
data.also {
|
||||
val toRemove = mutableListOf<String>()
|
||||
it.keySet().forEach { key ->
|
||||
if (key.startsWith("google.")
|
||||
|| key.startsWith("gcm.")
|
||||
|| key == "from"
|
||||
|| key == "message_type"
|
||||
|| key == "collapse_key")
|
||||
toRemove += key
|
||||
}
|
||||
toRemove.forEach { key ->
|
||||
it.remove(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun s(key: String): String? = json.getString(key)
|
||||
private fun l(key: String): Long = json.getLong(key) ?: 0L
|
||||
private fun i(key: String): Int = json.getInt(key) ?: 0
|
||||
private fun isNotificationMessage(json: JsonObject): Boolean {
|
||||
return json.getInt("gcm.n.e") == 1
|
||||
|| json.getInt("gcm.notification.e") == 1
|
||||
|| json.getString("gcm.n.icon") != null
|
||||
|| json.getString("gcm.notification.icon") != null
|
||||
}
|
||||
private fun getPriority(str: String?): Int {
|
||||
return when (str) {
|
||||
"high" -> 1
|
||||
"normal" -> 2
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Message(messageId=$messageId, from=$from, data=$data)"
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user