[Structure] Refactor App class to Kotlin. Rewrite SzkolnyTask and posting notifications. Remove dependency on AppConfig. Update libraries and gradle.

This commit is contained in:
Kuba Szczodrzyński 2020-01-19 19:07:27 +01:00
parent 55c6e40d6d
commit b7fc6fcc38
131 changed files with 2429 additions and 2880 deletions

View File

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

View File

@ -1,13 +1,14 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'
apply plugin: 'io.fabric' apply plugin: 'io.fabric'
android { android {
signingConfigs { signingConfigs {
} }
compileSdkVersion rootProject.ext.compileSdkVersion compileSdkVersion setup.compileSdk
defaultConfig { defaultConfig {
applicationId 'pl.szczodrzynski.edziennik' applicationId 'pl.szczodrzynski.edziennik'
minSdkVersion setup.minSdk minSdkVersion setup.minSdk
@ -103,7 +104,7 @@ tasks.whenTaskAdded { task ->
dependencies { dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
annotationProcessor "androidx.room:room-compiler:${versions.room}" kapt "androidx.room:room-compiler:${versions.room}"
debugImplementation "com.amitshekhar.android:debug-db:1.0.5" debugImplementation "com.amitshekhar.android:debug-db:1.0.5"
implementation "android.arch.navigation:navigation-fragment-ktx:${versions.navigationFragment}" implementation "android.arch.navigation:navigation-fragment-ktx:${versions.navigationFragment}"

View File

@ -28,7 +28,7 @@
-keepclassmembers class pl.szczodrzynski.edziennik.ui.widgets.WidgetConfig { public *; } -keepclassmembers class pl.szczodrzynski.edziennik.ui.widgets.WidgetConfig { public *; }
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.timetable.WidgetTimetableProvider -keepnames class pl.szczodrzynski.edziennik.ui.widgets.timetable.WidgetTimetableProvider
-keepnames class pl.szczodrzynski.edziennik.ui.widgets.notifications.WidgetNotificationsProvider -keepnames class pl.szczodrzynski.edziennik.ui.widgets.notifications.WidgetNotificationsProvider
-keepnames class pl.szczodrzynski.edziennik.widgets.luckynumber.WidgetLuckyNumber -keepnames class pl.szczodrzynski.edziennik.ui.widgets.luckynumber.WidgetLuckyNumberProvider
-keep class .R -keep class .R
-keep class **.R$* { -keep class **.R$* {

View File

@ -80,6 +80,7 @@
<service android:name=".ui.widgets.timetable.WidgetTimetableService" <service android:name=".ui.widgets.timetable.WidgetTimetableService"
android:permission="android.permission.BIND_REMOTEVIEWS" /> android:permission="android.permission.BIND_REMOTEVIEWS" />
<activity android:name=".ui.widgets.LessonDialogActivity" <activity android:name=".ui.widgets.LessonDialogActivity"
android:label=""
android:configChanges="orientation|keyboardHidden" android:configChanges="orientation|keyboardHidden"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:noHistory="true" android:noHistory="true"
@ -141,12 +142,8 @@
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/AppTheme" /> android:theme="@style/AppTheme" />
<activity android:name=".ui.modules.settings.SettingsLicenseActivity" <activity android:name=".ui.modules.settings.SettingsLicenseActivity"
android:configChanges="orientation|keyboardHidden" android:configChanges="orientation|keyboardHidden"
android:theme="@style/AppTheme" /> android:theme="@style/AppTheme" />
<activity android:name=".ui.modules.webpush.WebPushConfigActivity"
android:configChanges="orientation|keyboardHidden"
android:theme="@style/AppTheme.Dark" />
<activity android:name=".ui.modules.webpush.QrScannerActivity" /> <activity android:name=".ui.modules.webpush.QrScannerActivity" />
<activity android:name="com.theartofdev.edmodo.cropper.CropImageActivity" <activity android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
android:configChanges="orientation|keyboardHidden" android:configChanges="orientation|keyboardHidden"
@ -165,20 +162,11 @@
<action android:name="android.intent.action.USER_PRESENT" /> <action android:name="android.intent.action.USER_PRESENT" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver android:name=".receivers.BootReceiver"> <receiver android:name=".sync.UpdateDownloaderService$DownloadProgressReceiver">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" /> <action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter> </intent-filter>
</receiver> </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" <receiver android:name=".receivers.SzkolnyReceiver"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
@ -200,8 +188,6 @@
<action android:name="com.google.firebase.MESSAGING_EVENT" /> <action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter> </intent-filter>
</service>--> </service>-->
<service android:name=".receivers.BootReceiver$NotificationActionService" />
<service android:name=".Notifier$GetDataRetryService" />
<service android:name=".data.api.ApiService" /> <service android:name=".data.api.ApiService" />
<service android:name=".data.firebase.MyFirebaseService" <service android:name=".data.firebase.MyFirebaseService"
android:exported="false"> android:exported="false">
@ -210,6 +196,7 @@
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" /> <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter> </intent-filter>
</service> </service>
<service android:name=".sync.UpdateDownloaderService" />
<!-- <!--
_____ _ _ _____ _ _

View File

@ -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> <ul>
<li><b>Przebudowaliśmy cały moduł synchronizacji</b>, co oznacza większą stabilność aplikacji, szybkosć oraz poprawność pobieranych danych.</li> <li><b>Przebudowaliśmy cały moduł synchronizacji</b>, co oznacza większą stabilność aplikacji, szybkosć oraz poprawność pobieranych danych.</li>
<li><b><u>Wysyłanie wiadomości</u></b> - funkcja, na którą czekał każdy. Od teraz w Szkolnym można wysyłać oraz odpowiadać na wiadomości do nauczycieli &#x1F44F;</li> <li><b><u>Wysyłanie wiadomości</u></b> - funkcja, na którą czekał każdy. Od teraz w Szkolnym można wysyłać oraz odpowiadać na wiadomości do nauczycieli &#x1F44F;</li>
@ -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>Nowa <b>Strona główna</b> - ładniejszy wygląd oraz możliwość przestawiania kart na każdym profilu</li>
<li>Nowy <b>Plan lekcji</b> - z doskonałą obsługą lekcji przesuniętych oraz dwóch lekcji o tej samej godzinie</li> <li>Nowy <b>Plan lekcji</b> - z doskonałą obsługą lekcji przesuniętych oraz dwóch lekcji o tej samej godzinie</li>
<li>Nowe okienka informacji o wydarzeniach oraz lekcjach</li> <li>Nowe okienka informacji o wydarzeniach oraz lekcjach</li>
<li>Nowe, przyjemniejsze powiadomienia</li>
<li>Łatwiejsze dodawanie własnych wydarzeń</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>Dużo poprawek w widoku <b>Wiadomości</b> oraz <b>Ogłoszeń</b></li>
<li>Częściowa <b>Obsługa dziennika EduDziennik</b></li> <li>Częściowa <b>Obsługa dziennika EduDziennik</b></li>
@ -14,7 +15,9 @@
<li>Librus: obsługa Zadań domowych bez posiadania Mobilnych dodatków (przez system Synergia)</li> <li>Librus: obsługa Zadań domowych bez posiadania Mobilnych dodatków (przez system Synergia)</li>
<li>Lepsze <b>przekazywanie powiadomień na komputer</b> oraz łatwiejsze parowanie</li> <li>Lepsze <b>przekazywanie powiadomień na komputer</b> oraz łatwiejsze parowanie</li>
<li>Poprawiliśmy synchronizację w tle na niektórych telefonach</li> <li>Poprawiliśmy synchronizację w tle na niektórych telefonach</li>
<li>Usunąłem denerwujący brak zaznaczenia w lewym menu</li>
<li>Znaczna ilość błędów z poprzednich wersji już nie występuje</li> <li>Znaczna ilość błędów z poprzednich wersji już nie występuje</li>
<li><strike>Występują natomiast nowe błędy, dlatego proszę o ich zgłaszanie :)</strike></li>
</ul> </ul>
<br> <br>
<br> <br>

View File

@ -4,20 +4,93 @@
package pl.szczodrzynski.edziennik 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 android.util.Log
import androidx.appcompat.app.AppCompatDelegate
import androidx.multidex.MultiDexApplication
import androidx.work.Configuration import androidx.work.Configuration
import kotlinx.coroutines.CoroutineScope import cat.ereza.customactivityoncrash.config.CaocConfig
import kotlinx.coroutines.Dispatchers import com.chuckerteam.chucker.api.ChuckerCollector
import kotlinx.coroutines.Job import com.chuckerteam.chucker.api.ChuckerInterceptor
import com.chuckerteam.chucker.api.RetentionManager
import com.google.firebase.FirebaseApp
import com.google.firebase.FirebaseOptions
import com.google.firebase.iid.FirebaseInstanceId
import com.google.firebase.messaging.FirebaseMessaging
import com.google.gson.Gson
import com.hypertrack.hyperlog.HyperLog
import com.mikepenz.iconics.Iconics
import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont
import im.wangchao.mhttp.MHttp
import 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 import kotlin.coroutines.CoroutineContext
class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScope { class App : MultiDexApplication(), Configuration.Provider, CoroutineScope {
companion object { companion object {
@Volatile
lateinit var db: AppDb
val config: Config by lazy { Config(db) }
var profile: Profile by mutableLazy { Profile(0, 0, 0, "") }
val profileId
get() = profile.id
var devMode = false var devMode = false
} }
//lateinit var db: AppDb val notifications by lazy { Notifications() }
//val config by lazy { Config(db); // TODO migrate } 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() private val job = Job()
override val coroutineContext: CoroutineContext override val coroutineContext: CoroutineContext
@ -26,11 +99,10 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
.setMinimumLoggingLevel(Log.VERBOSE) .setMinimumLoggingLevel(Log.VERBOSE)
.build() .build()
/*val preferences by lazy { getSharedPreferences(getString(R.string.preference_file), Context.MODE_PRIVATE) } val preferences by lazy { getSharedPreferences(getString(R.string.preference_file), Context.MODE_PRIVATE) }
val notifier by lazy { Notifier(this) }
val permissionChecker by lazy { PermissionChecker(this) } val permissionChecker by lazy { PermissionChecker(this) }
val networkUtils by lazy { NetworkUtils(this) }
lateinit var profile: ProfileFull val gson by lazy { Gson() }
/* _ _ _______ _______ _____ /* _ _ _______ _______ _____
| | | |__ __|__ __| __ \ | | | |__ __|__ __| __ \
@ -48,13 +120,13 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
.connectTimeout(20, TimeUnit.SECONDS) .connectTimeout(20, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS) .writeTimeout(5, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS)
builder.installHttpsSupport() builder.installHttpsSupport(this)
if (devMode || BuildConfig.DEBUG) { if (devMode || BuildConfig.DEBUG) {
HyperLog.initialize(this) HyperLog.initialize(this)
HyperLog.setLogLevel(Log.VERBOSE) HyperLog.setLogLevel(Log.VERBOSE)
HyperLog.setLogFormat(DebugLogFormat(this)) HyperLog.setLogFormat(DebugLogFormat(this))
val chuckerCollector = ChuckerCollector(this, true, Period.ONE_HOUR) val chuckerCollector = ChuckerCollector(this, true, RetentionManager.Period.ONE_HOUR)
val chuckerInterceptor = ChuckerInterceptor(this, chuckerCollector) val chuckerInterceptor = ChuckerInterceptor(this, chuckerCollector)
builder.addInterceptor(chuckerInterceptor) builder.addInterceptor(chuckerInterceptor)
} }
@ -77,22 +149,7 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
|_____/|_|\__, |_| |_|\__,_|\__|\__,_|_| \___| |_____/|_|\__, |_| |_|\__,_|\__|\__,_|_| \___|
__/ | __/ |
|__*/ |__*/
private val deviceId: String by lazy { Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID) ?: "" } val deviceId: String by lazy { Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID) ?: "" }
private val signature: String by lazy {
var str = ""
try {
val packageInfo: PackageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
for (signature in packageInfo.signatures) {
val signatureBytes = signature.toByteArray()
val md = MessageDigest.getInstance("SHA")
md.update(signatureBytes)
str = Base64.encodeToString(md.digest(), Base64.DEFAULT)
}
} catch (e: Exception) {
e.printStackTrace()
}
str
}
private var unreadBadgesAvailable = true private var unreadBadgesAvailable = true
/* _____ _ /* _____ _
@ -118,27 +175,34 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
.apply() .apply()
Iconics.init(applicationContext) Iconics.init(applicationContext)
Iconics.registerFont(SzkolnyFont) Iconics.registerFont(SzkolnyFont)
db = AppDb.getDatabase(this) App.db = AppDb(this)
Themes.themeInt = config.ui.theme Themes.themeInt = config.ui.theme
MHttp.instance().customOkHttpClient(http) MHttp.instance().customOkHttpClient(http)
if (!profileLoadById(config.lastProfileId)) {
db.profileDao().firstId?.let { profileLoadById(it) }
}
devMode = "f054761fbdb6a238" == deviceId || BuildConfig.DEBUG devMode = "f054761fbdb6a238" == deviceId || BuildConfig.DEBUG
if (config.devModePassword != null)
checkDevModePassword()
Signing.getCert(this) Signing.getCert(this)
launch { async(Dispatchers.Default) { launch {
if (config.sync.enabled) { withContext(Dispatchers.Default) {
scheduleNext(this@App, false) config.migrate(this@App)
} else {
cancelNext(this@App)
}
db.metadataDao().countUnseen().observeForever { count: Int -> if (config.devModePassword != null)
if (unreadBadgesAvailable) checkDevModePassword()
unreadBadgesAvailable = ShortcutBadger.applyCount(this@App, count)
} 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) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
val shortcutManager = getSystemService(ShortcutManager::class.java) val shortcutManager = getSystemService(ShortcutManager::class.java)
@ -187,11 +251,36 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
) )
} // shortcuts - end } // 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) if (config.appInstalledTime == 0L)
try { try {
config.appInstalledTime = packageManager.getPackageInfo(packageName, 0).firstInstallTime config.appInstalledTime = packageManager.getPackageInfo(packageName, 0).firstInstallTime
config.appRateSnackbarTime = config.appInstalledTime + 7*DAY*MS config.appRateSnackbarTime = config.appInstalledTime + 7 * DAY * MS
} catch (e: NameNotFoundException) { } catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace() e.printStackTrace()
} }
@ -252,61 +341,50 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
} catch (e: IllegalStateException) { } catch (e: IllegalStateException) {
e.printStackTrace() e.printStackTrace()
} }
}}
} }
private fun profileLoad(profileId: Int) { db.metadataDao().countUnseen().observeForever { count: Int ->
db.profileDao().getFullByIdNow(profileId)?.also { if (unreadBadgesAvailable)
profile = it unreadBadgesAvailable = ShortcutBadger.applyCount(this@App, count)
} ?: run {
if (!::profile.isInitialized) {
profile = ProfileFull(-1, "", "", -1)
} }
} }
} }
fun profileLoad(profileId: Int, onSuccess: (profile: ProfileFull) -> Unit) {
private fun profileLoadById(profileId: Int): Boolean {
db.profileDao().getByIdNow(profileId)?.also {
App.profile = it
App.config.lastProfileId = it.id
return true
}
return false
}
fun profileLoad(profileId: Int, onSuccess: (profile: Profile) -> Unit) {
launch { launch {
val deferred = async(Dispatchers.Default) { val success = withContext(Dispatchers.Default) {
profileLoad(profileId) profileLoadById(profileId)
} }
deferred.await() if (success)
onSuccess(profile) onSuccess(profile)
else
profileLoadLast(onSuccess)
} }
} }
fun profileLoadLast(onSuccess: (profile: Profile) -> Unit) {
private fun OkHttpClient.Builder.installHttpsSupport() { launch {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) { val success = withContext(Dispatchers.Default) {
try { profileLoadById(db.profileDao().lastId ?: return@withContext false)
try {
ProviderInstaller.installIfNeeded(this@App)
} catch (e: Exception) {
Log.e("OkHttpTLSCompat", "Play Services not found or outdated")
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustManagerFactory.init(null as KeyStore?)
val x509TrustManager = trustManagerFactory.trustManagers.singleOrNull { it is X509TrustManager } as X509TrustManager?
?: return
val sc = SSLContext.getInstance("TLSv1.2")
sc.init(null, null, null)
sslSocketFactory(TLSSocketFactory(sc.socketFactory), x509TrustManager)
val cs: ConnectionSpec = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_0)
.tlsVersions(TlsVersion.TLS_1_1)
.tlsVersions(TlsVersion.TLS_1_2)
.build()
val specs: MutableList<ConnectionSpec> = ArrayList()
specs.add(cs)
specs.add(ConnectionSpec.COMPATIBLE_TLS)
specs.add(ConnectionSpec.CLEARTEXT)
connectionSpecs(specs)
} }
} catch (exc: Exception) { if (!success) {
Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc) EventBus.getDefault().post(ProfileListEmptyEvent())
} }
} }
} }
fun profileSave() = profileSave(profile)
fun profileSave(profile: Profile) {
launch(Dispatchers.Default) {
App.db.profileDao().add(profile)
}
}
fun checkDevModePassword() { fun checkDevModePassword() {
devMode = try { devMode = try {
@ -315,5 +393,5 @@ class Szkolny : /*MultiDexApplication(),*/ Configuration.Provider, CoroutineScop
e.printStackTrace() e.printStackTrace()
false false
} }
}*/ }
} }

View File

@ -1,91 +1,10 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-1-19.
*/
package pl.szczodrzynski.edziennik; package pl.szczodrzynski.edziennik;
import android.content.Context; /*public class AppOld extends androidx.multidex.MultiDexApplication implements Configuration.Provider {
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"; private static final String TAG = "App";
public static int profileId = -1; public static int profileId = -1;
private Context mContext; private Context mContext;
@ -379,36 +298,7 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
} }
} }
/*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( FirebaseApp pushMobidziennikApp = FirebaseApp.initializeApp(
this, this,
@ -498,14 +388,7 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
if (appSharedPrefs.contains("profiles")) { if (appSharedPrefs.contains("profiles")) {
SharedPreferences.Editor appSharedPrefsEditor = appSharedPrefs.edit(); 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.remove("profiles");
appSharedPrefsEditor.apply(); appSharedPrefsEditor.apply();
//profilesSave(); //profilesSave();
@ -539,16 +422,6 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
} }
} }
/*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) { if (appConfig == null) {
appConfig = new AppConfig(this); appConfig = new AppConfig(this);
} }
@ -564,15 +437,7 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
for (Map.Entry<String, JsonElement> entry : appConfigJson.entrySet()) { for (Map.Entry<String, JsonElement> entry : appConfigJson.entrySet()) {
String jsonObj; String jsonObj;
jsonObj = entry.getValue().toString(); 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.putString("app.appConfig." + entry.getKey(), jsonObj);
} }
@ -628,18 +493,10 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
} }
public void profileLoadById(int id, boolean loadedLast) { public void profileLoadById(int id, boolean loadedLast) {
//Log.d(TAG, "Loading ID "+id); //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) { if (profile == null || profile.getId() != id) {
profile = db.profileDao().getByIdNow(id); profile = db.profileDao().getByIdNow(id);
/*if (profile == null) {
profileLoadById(id);
return;
}*/
if (profile != null) { if (profile != null) {
MainActivity.Companion.setUseOldMessages(profile.getLoginStoreType() == LOGIN_TYPE_MOBIDZIENNIK && appConfig.mobidziennikOldMessages == 1); MainActivity.Companion.setUseOldMessages(profile.getLoginStoreType() == LOGIN_TYPE_MOBIDZIENNIK && appConfig.mobidziennikOldMessages == 1);
profileId = profile.getId(); profileId = profile.getId();
@ -655,44 +512,6 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
} }
} }
/*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() { public int profileFirstId() {
Integer id = db.profileDao().getFirstId(); Integer id = db.profileDao().getFirstId();
return id == null ? 1 : id; return id == null ? 1 : id;
@ -719,4 +538,4 @@ public class App extends androidx.multidex.MultiDexApplication implements Config
} }
} }
} }*/

View File

@ -35,6 +35,7 @@ import androidx.core.util.forEach
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import com.google.android.gms.security.ProviderInstaller
import com.google.gson.JsonArray import com.google.gson.JsonArray
import com.google.gson.JsonElement import com.google.gson.JsonElement
import com.google.gson.JsonObject import com.google.gson.JsonObject
@ -42,23 +43,31 @@ import im.wangchao.mhttp.Response
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import okhttp3.ConnectionSpec
import okhttp3.OkHttpClient
import okhttp3.RequestBody import okhttp3.RequestBody
import okhttp3.TlsVersion
import okio.Buffer import okio.Buffer
import pl.szczodrzynski.edziennik.data.db.entity.Notification import pl.szczodrzynski.edziennik.data.db.entity.Notification
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.entity.Team import pl.szczodrzynski.edziennik.data.db.entity.Team
import pl.szczodrzynski.edziennik.network.TLSSocketFactory
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
import java.io.PrintWriter import java.io.PrintWriter
import java.io.StringWriter import java.io.StringWriter
import java.math.BigInteger import java.math.BigInteger
import java.nio.charset.Charset import java.nio.charset.Charset
import java.security.KeyStore
import java.security.MessageDigest import java.security.MessageDigest
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import java.util.zip.CRC32 import java.util.zip.CRC32
import javax.crypto.Mac import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec import javax.crypto.spec.SecretKeySpec
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509TrustManager
import kotlin.Pair import kotlin.Pair
@ -643,6 +652,12 @@ 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 { fun Bundle.toJsonObject(): JsonObject {
val json = JsonObject() val json = JsonObject()
@ -957,6 +972,8 @@ fun Context.getNotificationTitle(type: Int): String {
Notification.TYPE_NEW_EVENT -> R.string.notification_type_new_event Notification.TYPE_NEW_EVENT -> R.string.notification_type_new_event
Notification.TYPE_NEW_HOMEWORK -> R.string.notification_type_new_homework Notification.TYPE_NEW_HOMEWORK -> R.string.notification_type_new_homework
Notification.TYPE_NEW_SHARED_EVENT -> R.string.notification_type_new_shared_event Notification.TYPE_NEW_SHARED_EVENT -> R.string.notification_type_new_shared_event
Notification.TYPE_NEW_SHARED_HOMEWORK -> R.string.notification_type_new_shared_homework
Notification.TYPE_REMOVED_SHARED_EVENT -> R.string.notification_type_removed_shared_event
Notification.TYPE_NEW_MESSAGE -> R.string.notification_type_new_message Notification.TYPE_NEW_MESSAGE -> R.string.notification_type_new_message
Notification.TYPE_NEW_NOTICE -> R.string.notification_type_notice Notification.TYPE_NEW_NOTICE -> R.string.notification_type_notice
Notification.TYPE_NEW_ATTENDANCE -> R.string.notification_type_attendance Notification.TYPE_NEW_ATTENDANCE -> R.string.notification_type_attendance
@ -973,3 +990,37 @@ fun Context.getNotificationTitle(type: Int): String {
fun Cursor?.getString(columnName: String) = this?.getStringOrNull(getColumnIndex(columnName)) fun Cursor?.getString(columnName: String) = this?.getStringOrNull(getColumnIndex(columnName))
fun Cursor?.getInt(columnName: String) = this?.getIntOrNull(getColumnIndex(columnName)) fun Cursor?.getInt(columnName: String) = this?.getIntOrNull(getColumnIndex(columnName))
fun Cursor?.getLong(columnName: String) = this?.getLongOrNull(getColumnIndex(columnName)) fun Cursor?.getLong(columnName: String) = this?.getLongOrNull(getColumnIndex(columnName))
fun OkHttpClient.Builder.installHttpsSupport(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
try {
try {
ProviderInstaller.installIfNeeded(context)
} catch (e: Exception) {
Log.e("OkHttpTLSCompat", "Play Services not found or outdated")
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustManagerFactory.init(null as KeyStore?)
val x509TrustManager = trustManagerFactory.trustManagers.singleOrNull { it is X509TrustManager } as X509TrustManager?
?: return
val sc = SSLContext.getInstance("TLSv1.2")
sc.init(null, null, null)
sslSocketFactory(TLSSocketFactory(sc.socketFactory), x509TrustManager)
val cs: ConnectionSpec = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_0)
.tlsVersions(TlsVersion.TLS_1_1)
.tlsVersions(TlsVersion.TLS_1_2)
.build()
val specs: MutableList<ConnectionSpec> = ArrayList()
specs.add(cs)
specs.add(ConnectionSpec.COMPATIBLE_TLS)
specs.add(ConnectionSpec.CLEARTEXT)
connectionSpecs(specs)
}
} catch (exc: Exception) {
Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc)
}
}
}

View File

@ -1,6 +1,5 @@
package pl.szczodrzynski.edziennik package pl.szczodrzynski.edziennik
import android.app.Activity
import android.app.ActivityManager import android.app.ActivityManager
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
@ -9,7 +8,6 @@ import android.content.IntentFilter
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.BitmapDrawable
import android.os.AsyncTask
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Environment import android.os.Environment
@ -41,15 +39,16 @@ import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
import pl.droidsonroids.gif.GifDrawable import pl.droidsonroids.gif.GifDrawable
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.events.* import pl.szczodrzynski.edziennik.data.api.events.*
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing import pl.szczodrzynski.edziennik.data.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.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Metadata.* import pl.szczodrzynski.edziennik.data.db.entity.Metadata.*
import pl.szczodrzynski.edziennik.databinding.ActivitySzkolnyBinding import pl.szczodrzynski.edziennik.databinding.ActivitySzkolnyBinding
import pl.szczodrzynski.edziennik.sync.AppManagerDetectedEvent import pl.szczodrzynski.edziennik.sync.AppManagerDetectedEvent
import pl.szczodrzynski.edziennik.sync.SyncWorker import pl.szczodrzynski.edziennik.sync.SyncWorker
import pl.szczodrzynski.edziennik.sync.UpdateWorker
import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog
import pl.szczodrzynski.edziennik.ui.dialogs.settings.ProfileRemoveDialog import pl.szczodrzynski.edziennik.ui.dialogs.settings.ProfileRemoveDialog
import pl.szczodrzynski.edziennik.ui.dialogs.sync.SyncViewListDialog import pl.szczodrzynski.edziennik.ui.dialogs.sync.SyncViewListDialog
@ -269,12 +268,21 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
d(TAG, "Activity created")
setTheme(Themes.appTheme) setTheme(Themes.appTheme)
app.config.ui.language?.let { app.config.ui.language?.let {
setLanguage(it) setLanguage(it)
} }
if (App.profileId == 0) {
onProfileListEmptyEvent(ProfileListEmptyEvent())
return
}
d(TAG, "Profile is valid, inflating views")
setContentView(b.root) setContentView(b.root)
Log.d(TAG, Signing.appPassword) Log.d(TAG, Signing.appPassword)
@ -344,8 +352,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
setAccountHeaderBackground(app.config.ui.headerBackground) setAccountHeaderBackground(app.config.ui.headerBackground)
drawerProfileListEmptyListener = { drawerProfileListEmptyListener = {
app.config.loginFinished = false onProfileListEmptyEvent(ProfileListEmptyEvent())
profileListEmptyListener()
} }
drawerItemSelectedListener = { id, position, drawerItem -> drawerItemSelectedListener = { id, position, drawerItem ->
loadTarget(id) loadTarget(id)
@ -374,31 +381,20 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
navTarget = navTargetList[0] navTarget = navTargetList[0]
var profileListEmpty = drawer.profileListEmpty
if (savedInstanceState != null) { if (savedInstanceState != null) {
intent?.putExtras(savedInstanceState) intent?.putExtras(savedInstanceState)
savedInstanceState.clear() savedInstanceState.clear()
} }
if (!profileListEmpty) {
handleIntent(intent?.extras)
}
app.db.profileDao().all.observe(this, Observer { profiles -> app.db.profileDao().all.observe(this, Observer { profiles ->
drawer.setProfileList(profiles.filter { it.id >= 0 }.toMutableList()) drawer.setProfileList(profiles.filter { it.id >= 0 }.toMutableList())
if (profileListEmpty) { drawer.currentProfile = App.profileId
profileListEmpty = false
handleIntent(intent?.extras)
}
else if (app.profile != null) {
drawer.currentProfile = app.profile.id
}
}) })
// if null, getAllFull will load a profile and update drawerItems
if (app.profile != null)
setDrawerItems() setDrawerItems()
handleIntent(intent?.extras)
app.db.metadataDao().unreadCounts.observe(this, Observer { unreadCounters -> app.db.metadataDao().unreadCounts.observe(this, Observer { unreadCounters ->
unreadCounters.map { unreadCounters.map {
it.type = it.thingType it.type = it.thingType
@ -417,6 +413,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
isStoragePermissionGranted() isStoragePermissionGranted()
SyncWorker.scheduleNext(app) SyncWorker.scheduleNext(app)
UpdateWorker.scheduleNext(app)
// APP BACKGROUND // APP BACKGROUND
if (app.config.ui.appBackground != null) { if (app.config.ui.appBackground != null) {
@ -521,13 +518,10 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
} }
} }
var profileListEmptyListener = {
startActivityForResult(Intent(this, LoginActivity::class.java), REQUEST_LOGIN_ACTIVITY)
}
private var profileSettingClickListener = { id: Int, view: View? -> private var profileSettingClickListener = { id: Int, view: View? ->
when (id) { when (id) {
DRAWER_PROFILE_ADD_NEW -> { DRAWER_PROFILE_ADD_NEW -> {
profileListEmptyListener() startActivityForResult(Intent(this, LoginActivity::class.java), REQUEST_LOGIN_ACTIVITY)
} }
DRAWER_PROFILE_SYNC_ALL -> { DRAWER_PROFILE_SYNC_ALL -> {
EdziennikTask.sync().enqueue(this) EdziennikTask.sync().enqueue(this)
@ -587,6 +581,13 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
} }
} }
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)
fun onProfileListEmptyEvent(event: ProfileListEmptyEvent) {
d(TAG, "Profile list is empty. Launch LoginActivity.")
app.config.loginFinished = false
startActivity(Intent(this, LoginActivity::class.java))
finish()
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onApiTaskProgressEvent(event: ApiTaskProgressEvent) { fun onApiTaskProgressEvent(event: ApiTaskProgressEvent) {
if (event.profileId == App.profileId) { if (event.profileId == App.profileId) {
navView.toolbar.apply { navView.toolbar.apply {
@ -630,7 +631,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true) @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onAppManagerDetectedEvent(event: AppManagerDetectedEvent) { fun onAppManagerDetectedEvent(event: AppManagerDetectedEvent) {
EventBus.getDefault().removeStickyEvent(event) EventBus.getDefault().removeStickyEvent(event)
if (app.appConfig.dontShowAppManagerDialog) if (app.config.sync.dontShowAppManagerDialog)
return return
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this)
.setTitle(R.string.app_manager_dialog_title) .setTitle(R.string.app_manager_dialog_title)
@ -652,8 +653,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
} }
} }
.setNeutralButton(R.string.dont_ask_again) { dialog, which -> .setNeutralButton(R.string.dont_ask_again) { dialog, which ->
app.appConfig.dontShowAppManagerDialog = true app.config.sync.dontShowAppManagerDialog = true
app.saveConfig("dontShowAppManagerDialog")
} }
.setCancelable(false) .setCancelable(false)
.show() .show()
@ -698,7 +698,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
if (extras?.containsKey("reloadProfileId") == true) { if (extras?.containsKey("reloadProfileId") == true) {
val reloadProfileId = extras.getInt("reloadProfileId", -1) val reloadProfileId = extras.getInt("reloadProfileId", -1)
extras.remove("reloadProfileId") extras.remove("reloadProfileId")
if (reloadProfileId == -1 || (app.profile != null && app.profile.id == reloadProfileId)) { if (reloadProfileId == -1 || app.profile.id == reloadProfileId) {
reloadTarget() reloadTarget()
return return
} }
@ -728,9 +728,9 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
} }
when { when {
app.profile == null || app.profile.id == -1 -> { app.profile.id == 0 -> {
if (intentProfileId == -1) if (intentProfileId == -1)
intentProfileId = app.appSharedPrefs.getInt("current_profile_id", 1) intentProfileId = app.config.lastProfileId
loadProfile(intentProfileId, intentTargetId, extras) loadProfile(intentProfileId, intentTargetId, extras)
} }
intentProfileId != -1 -> { intentProfileId != -1 -> {
@ -769,7 +769,16 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
startActivity(intent) startActivity(intent)
} }
override fun onStart() {
d(TAG, "Activity started")
super.onStart()
}
override fun onStop() {
d(TAG, "Activity stopped")
super.onStop()
}
override fun onResume() { override fun onResume() {
d(TAG, "Activity resumed")
val filter = IntentFilter() val filter = IntentFilter()
filter.addAction(Intent.ACTION_MAIN) filter.addAction(Intent.ACTION_MAIN)
registerReceiver(intentReceiver, filter) registerReceiver(intentReceiver, filter)
@ -777,10 +786,15 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
super.onResume() super.onResume()
} }
override fun onPause() { override fun onPause() {
d(TAG, "Activity paused")
unregisterReceiver(intentReceiver) unregisterReceiver(intentReceiver)
EventBus.getDefault().unregister(this) EventBus.getDefault().unregister(this)
super.onPause() super.onPause()
} }
override fun onDestroy() {
d(TAG, "Activity destroyed")
super.onDestroy()
}
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
@ -794,10 +808,6 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_LOGIN_ACTIVITY) { if (requestCode == REQUEST_LOGIN_ACTIVITY) {
if (resultCode == Activity.RESULT_CANCELED && false) {
finish()
}
else {
if (!app.config.loginFinished) if (!app.config.loginFinished)
finish() finish()
else { else {
@ -805,7 +815,6 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
} }
} }
} }
}
/* _ _ _ _ _ /* _ _ _ _ _
| | | | | | | | | | | | | | | | | | | |
@ -823,36 +832,25 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
fun loadProfile(id: Int) = loadProfile(id, navTargetId) fun loadProfile(id: Int) = loadProfile(id, navTargetId)
fun loadProfile(id: Int, arguments: Bundle?) = loadProfile(id, navTargetId, arguments) fun loadProfile(id: Int, arguments: Bundle?) = loadProfile(id, navTargetId, arguments)
fun loadProfile(id: Int, drawerSelection: Int, arguments: Bundle? = null) { fun loadProfile(id: Int, drawerSelection: Int, arguments: Bundle? = null) {
//d("NavDebug", "loadProfile(id = $id, drawerSelection = $drawerSelection)") if (App.profileId == id) {
if (app.profile != null && App.profileId == id) {
drawer.currentProfile = app.profile.id drawer.currentProfile = app.profile.id
loadTarget(drawerSelection, arguments) loadTarget(drawerSelection, arguments)
return return
} }
AsyncTask.execute { app.profileLoad(id) {
app.profileLoadById(id)
MessagesFragment.pageSelection = -1 MessagesFragment.pageSelection = -1
MessagesListFragment.tapPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION) MessagesListFragment.tapPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
MessagesListFragment.topPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION) MessagesListFragment.topPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
MessagesListFragment.bottomPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION) MessagesListFragment.bottomPositions = intArrayOf(RecyclerView.NO_POSITION, RecyclerView.NO_POSITION)
this.runOnUiThread {
if (app.profile == null) {
if (app.config.loginFinished) {
// this shouldn't run
profileListEmptyListener()
}
} else {
setDrawerItems() setDrawerItems()
// the drawer profile is updated automatically when the drawer item is clicked // the drawer profile is updated automatically when the drawer item is clicked
// update it manually when switching profiles from other source // update it manually when switching profiles from other source
//if (drawer.currentProfile != app.profile.id) //if (drawer.currentProfile != app.profile.id)
drawer.currentProfile = app.profile.id drawer.currentProfile = app.profileId
loadTarget(drawerSelection, arguments) loadTarget(drawerSelection, arguments)
} }
} }
}
}
fun loadTarget(id: Int, arguments: Bundle? = null) { fun loadTarget(id: Int, arguments: Bundle? = null) {
var loadId = id var loadId = id
if (loadId == -1) { if (loadId == -1) {
@ -995,7 +993,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
* that something has changed in the bottom sheet. * that something has changed in the bottom sheet.
*/ */
fun gainAttention() { fun gainAttention() {
if (app.config.ui.bottomSheetOpened || true) if (app.config.ui.bottomSheetOpened)
return return
b.navView.postDelayed({ b.navView.postDelayed({
navView.gainAttentionOnBottomBar() navView.gainAttentionOnBottomBar()
@ -1044,12 +1042,11 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
} }
fun setDrawerItems() { fun setDrawerItems() {
d("NavDebug", "setDrawerItems() app.profile = ${app.profile ?: "null"}") d("NavDebug", "setDrawerItems() app.profile = ${app.profile}")
val drawerItems = arrayListOf<IDrawerItem<*>>() val drawerItems = arrayListOf<IDrawerItem<*>>()
val drawerProfiles = arrayListOf<ProfileSettingDrawerItem>() val drawerProfiles = arrayListOf<ProfileSettingDrawerItem>()
val supportedFragments = if (app.profile == null) arrayListOf<Int>() val supportedFragments = app.profile.supportedFragments
else app.profile.supportedFragments
targetPopToHomeList.clear() targetPopToHomeList.clear()
@ -1113,7 +1110,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
private var targetHomeId: Int = -1 private var targetHomeId: Int = -1
override fun onBackPressed() { override fun onBackPressed() {
if (!b.navView.onBackPressed()) { if (!b.navView.onBackPressed()) {
if (App.getConfig().ui.openDrawerOnBackPressed) { if (App.config.ui.openDrawerOnBackPressed) {
b.navView.drawer.toggle() b.navView.drawer.toggle()
} else { } else {
navigateUp() navigateUp()

View File

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

View File

@ -16,12 +16,13 @@ import pl.szczodrzynski.edziennik.config.utils.ConfigMigration
import pl.szczodrzynski.edziennik.config.utils.get import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.config.utils.toHashMap import pl.szczodrzynski.edziennik.config.utils.toHashMap
import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update
import pl.szczodrzynski.edziennik.data.db.AppDb import pl.szczodrzynski.edziennik.data.db.AppDb
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
class Config(val db: AppDb) : CoroutineScope, AbstractConfig { class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
companion object { companion object {
const val DATA_VERSION = 2 const val DATA_VERSION = 10
} }
private val job = Job() private val job = Job()
@ -45,6 +46,20 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
get() { mHash = mHash ?: values.get("hash", ""); return mHash ?: "" } get() { mHash = mHash ?: values.get("hash", ""); return mHash ?: "" }
set(value) { set("hash", value); mHash = value } set(value) { set("hash", value); mHash = value }
private var mLastProfileId: Int? = null
var lastProfileId: Int
get() { mLastProfileId = mLastProfileId ?: values.get("lastProfileId", 0); return mLastProfileId ?: 0 }
set(value) { set("lastProfileId", value); mLastProfileId = value }
private var mUpdatesChannel: String? = null
var updatesChannel: String
get() { mUpdatesChannel = mUpdatesChannel ?: values.get("updatesChannel", "release"); return mUpdatesChannel ?: "release" }
set(value) { set("updatesChannel", value); mUpdatesChannel = value }
private var mUpdate: Update? = null
var update: Update?
get() { mUpdate = mUpdate ?: values.get("update", null as Update?); return mUpdate ?: null as Update? }
set(value) { set("update", value); mUpdate = value }
private var mAppVersion: Int? = null private var mAppVersion: Int? = null
var appVersion: Int var appVersion: Int
get() { mAppVersion = mAppVersion ?: values.get("appVersion", BuildConfig.VERSION_CODE); return mAppVersion ?: BuildConfig.VERSION_CODE } get() { mAppVersion = mAppVersion ?: values.get("appVersion", BuildConfig.VERSION_CODE); return mAppVersion ?: BuildConfig.VERSION_CODE }

View File

@ -17,6 +17,6 @@ class ConfigGrades(private val config: Config) {
private var mOrderBy: Int? = null private var mOrderBy: Int? = null
var orderBy: Int var orderBy: Int
get() { mOrderBy = mOrderBy ?: config.values.get("gradesOrderBy", 0); return mOrderBy ?: 0 } get() { mOrderBy = mOrderBy ?: config.values.get("gradesOrderBy", 0); return mOrderBy ?: ORDER_BY_DATE_DESC }
set(value) { config.set("gradesOrderBy", value); mOrderBy = value } set(value) { config.set("gradesOrderBy", value); mOrderBy = value }
} }

View File

@ -9,6 +9,11 @@ import pl.szczodrzynski.edziennik.config.utils.getIntList
import pl.szczodrzynski.edziennik.config.utils.set import pl.szczodrzynski.edziennik.config.utils.set
class ConfigSync(private val config: Config) { class ConfigSync(private val config: Config) {
private var mDontShowAppManagerDialog: Boolean? = null
var dontShowAppManagerDialog: Boolean
get() { mDontShowAppManagerDialog = mDontShowAppManagerDialog ?: config.values.get("dontShowAppManagerDialog", false); return mDontShowAppManagerDialog ?: false }
set(value) { config.set("dontShowAppManagerDialog", value); mDontShowAppManagerDialog = value }
private var mSyncEnabled: Boolean? = null private var mSyncEnabled: Boolean? = null
var enabled: Boolean var enabled: Boolean
get() { mSyncEnabled = mSyncEnabled ?: config.values.get("syncEnabled", true); return mSyncEnabled ?: true } get() { mSyncEnabled = mSyncEnabled ?: config.values.get("syncEnabled", true); return mSyncEnabled ?: true }

View File

@ -45,11 +45,6 @@ class ConfigUI(private val config: Config) {
get() { mOpenDrawerOnBackPressed = mOpenDrawerOnBackPressed ?: config.values.get("openDrawerOnBackPressed", false); return mOpenDrawerOnBackPressed ?: false } get() { mOpenDrawerOnBackPressed = mOpenDrawerOnBackPressed ?: config.values.get("openDrawerOnBackPressed", false); return mOpenDrawerOnBackPressed ?: false }
set(value) { config.set("openDrawerOnBackPressed", value); mOpenDrawerOnBackPressed = value } set(value) { config.set("openDrawerOnBackPressed", value); mOpenDrawerOnBackPressed = value }
private var mAgendaViewType: Int? = null
var agendaViewType: Int
get() { mAgendaViewType = mAgendaViewType ?: config.values.get("agendaViewType", 0); return mAgendaViewType ?: 0 }
set(value) { config.set("agendaViewType", value); mAgendaViewType = value }
private var mHomeCards: List<HomeCardModel>? = null private var mHomeCards: List<HomeCardModel>? = null
var homeCards: List<HomeCardModel> var homeCards: List<HomeCardModel>
get() { mHomeCards = mHomeCards ?: config.values.get("homeCards", listOf(), HomeCardModel::class.java); return mHomeCards ?: listOf() } get() { mHomeCards = mHomeCards ?: config.values.get("homeCards", listOf(), HomeCardModel::class.java); return mHomeCards ?: listOf() }

View File

@ -9,6 +9,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.config.db.ConfigEntry import pl.szczodrzynski.edziennik.config.db.ConfigEntry
import pl.szczodrzynski.edziennik.config.utils.ProfileConfigMigration
import pl.szczodrzynski.edziennik.config.utils.get import pl.szczodrzynski.edziennik.config.utils.get
import pl.szczodrzynski.edziennik.config.utils.set import pl.szczodrzynski.edziennik.config.utils.set
import pl.szczodrzynski.edziennik.config.utils.toHashMap import pl.szczodrzynski.edziennik.config.utils.toHashMap
@ -27,6 +28,7 @@ class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEn
val values: HashMap<String, String?> = hashMapOf() val values: HashMap<String, String?> = hashMapOf()
val grades by lazy { ProfileConfigGrades(this) } val grades by lazy { ProfileConfigGrades(this) }
val ui by lazy { ProfileConfigUI(this) }
/* /*
val sync by lazy { ConfigSync(this) } val sync by lazy { ConfigSync(this) }
val timetable by lazy { ConfigTimetable(this) } val timetable by lazy { ConfigTimetable(this) }
@ -44,8 +46,8 @@ class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List<ConfigEn
init { init {
rawEntries.toHashMap(profileId, values) rawEntries.toHashMap(profileId, values)
/*if (dataVersion < DATA_VERSION) if (dataVersion < DATA_VERSION)
ProfileConfigMigration(this)*/ ProfileConfigMigration(this)
} }
override fun set(key: String, value: String?) { override fun set(key: String, value: String?) {

View File

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

View File

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

View File

@ -0,0 +1,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 }
}
}

View File

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

View File

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

View File

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

View File

@ -12,12 +12,15 @@ import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.events.* import pl.szczodrzynski.edziennik.data.api.events.*
import pl.szczodrzynski.edziennik.data.api.events.requests.ServiceCloseRequest import pl.szczodrzynski.edziennik.data.api.events.requests.ServiceCloseRequest
import pl.szczodrzynski.edziennik.data.api.events.requests.TaskCancelRequest import pl.szczodrzynski.edziennik.data.api.events.requests.TaskCancelRequest
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.api.task.* import pl.szczodrzynski.edziennik.data.api.task.ErrorReportTask
import pl.szczodrzynski.edziennik.data.api.task.IApiTask
import pl.szczodrzynski.edziennik.data.api.task.SzkolnyTask
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
import kotlin.math.min import kotlin.math.min
@ -40,11 +43,8 @@ class ApiService : Service() {
private val syncingProfiles = mutableListOf<Profile>() private val syncingProfiles = mutableListOf<Profile>()
private val finishingTaskQueue = mutableListOf( private var szkolnyTaskFinished = false
SzkolnyTask.sync(syncingProfiles), private val allTaskRequestList = mutableListOf<Any>()
NotifyTask()
)
private val allTaskList = mutableListOf<IApiTask>()
private val taskQueue = mutableListOf<IApiTask>() private val taskQueue = mutableListOf<IApiTask>()
private val errorList = mutableListOf<ApiError>() private val errorList = mutableListOf<ApiError>()
@ -132,14 +132,20 @@ class ApiService : Service() {
checkIfTaskFrozen() checkIfTaskFrozen()
if (taskIsRunning) if (taskIsRunning)
return return
if (taskCancelled || serviceClosed || (taskQueue.isEmpty() && finishingTaskQueue.isEmpty())) { if (taskCancelled || serviceClosed || (taskQueue.isEmpty() && szkolnyTaskFinished)) {
allCompleted() allCompleted()
return return
} }
lastEventTime = System.currentTimeMillis() lastEventTime = System.currentTimeMillis()
val task = if (taskQueue.isEmpty()) finishingTaskQueue.removeAt(0) else taskQueue.removeAt(0) val task = if (taskQueue.isNotEmpty()) {
taskQueue.removeAt(0)
} else {
szkolnyTaskFinished = true
SzkolnyTask(app, syncingProfiles)
}
task.taskId = ++taskMaximumId task.taskId = ++taskMaximumId
task.prepare(app) task.prepare(app)
taskIsRunning = true taskIsRunning = true
@ -162,9 +168,8 @@ class ApiService : Service() {
try { try {
when (task) { when (task) {
is EdziennikTask -> task.run(app, taskCallback) is EdziennikTask -> task.run(app, taskCallback)
is NotifyTask -> task.run(app, taskCallback)
is ErrorReportTask -> task.run(app, taskCallback, notification, errorList) is ErrorReportTask -> task.run(app, taskCallback, notification, errorList)
is SzkolnyTask -> task.run(app, taskCallback) is SzkolnyTask -> task.run(taskCallback)
} }
} catch (e: Exception) { } catch (e: Exception) {
taskCallback.onError(ApiError(TAG, EXCEPTION_API_TASK).withThrowable(e)) taskCallback.onError(ApiError(TAG, EXCEPTION_API_TASK).withThrowable(e))
@ -230,10 +235,12 @@ class ApiService : Service() {
EventBus.getDefault().removeStickyEvent(task) EventBus.getDefault().removeStickyEvent(task)
d(TAG, task.toString()) d(TAG, task.toString())
if (task is EdziennikTask) {
// fix for duplicated tasks, thank you EventBus // fix for duplicated tasks, thank you EventBus
if (task in allTaskList) if (task.request in allTaskRequestList)
return return
allTaskList += task allTaskRequestList += task.request
}
if (task is EdziennikTask) { if (task is EdziennikTask) {
when (task.request) { when (task.request) {

View File

@ -1,206 +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.*
import pl.szczodrzynski.edziennik.data.db.entity.Grade.*
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)) {
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
)
data.notifications += Notification(
title = app.getNotificationTitle(TYPE_NEW_ATTENDANCE),
text = text,
type = TYPE_NEW_ATTENDANCE,
profileId = profileId,
profileName = profileName,
viewId = DRAWER_ITEM_ATTENDANCE,
addedDate = attendance.addedDate
).addExtra("attendanceId", attendance.id).addExtra("attendanceSubjectId", attendance.subjectId)
}
for (announcement in app.db.announcementDao().getNotNotifiedNow(profileId)) {
val text = app.context.getString(R.string.notification_announcement_format, announcement.subject)
data.notifications += Notification(
title = app.getNotificationTitle(TYPE_NEW_ANNOUNCEMENT),
text = text,
type = TYPE_NEW_ANNOUNCEMENT,
profileId = profileId,
profileName = profileName,
viewId = DRAWER_ITEM_ANNOUNCEMENTS,
addedDate = announcement.addedDate
).addExtra("announcementId", announcement.id)
}
for (message in app.db.messageDao().getReceivedNotNotifiedNow(profileId)) {
val text = app.context.getString(R.string.notification_message_format, message.senderFullName, message.subject)
data.notifications += Notification(
title = app.getNotificationTitle(TYPE_NEW_MESSAGE),
text = text,
type = TYPE_NEW_MESSAGE,
profileId = profileId,
profileName = profileName,
viewId = DRAWER_ITEM_MESSAGES,
addedDate = message.addedDate
).addExtra("messageType", Message.TYPE_RECEIVED.toLong()).addExtra("messageId", message.id)
}
val luckyNumbers = app.db.luckyNumberDao().getNotNotifiedNow(profileId)
luckyNumbers?.removeAll { it.date < today }
luckyNumbers?.forEach { luckyNumber ->
val text = when (luckyNumber.date.value) {
todayValue -> // LN for today
app.getString(if (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) R.string.notification_lucky_number_yours_format else R.string.notification_lucky_number_format, luckyNumber.number)
todayValue + 1 -> // LN for tomorrow
app.getString(if (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) R.string.notification_lucky_number_yours_tomorrow_format else R.string.notification_lucky_number_tomorrow_format, luckyNumber.number)
else -> // LN for later
app.getString(if (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) R.string.notification_lucky_number_yours_later_format else R.string.notification_lucky_number_later_format, luckyNumber.date.formattedString, luckyNumber.number)
}
data.notifications += Notification(
title = app.getNotificationTitle(TYPE_LUCKY_NUMBER),
text = text,
type = TYPE_LUCKY_NUMBER,
profileId = profileId,
profileName = profileName,
viewId = DRAWER_ITEM_HOME,
addedDate = luckyNumber.addedDate
)
}
data.db.metadataDao().setAllNotified(profileId, true)
}}
}

View File

@ -1,4 +1,8 @@
package pl.szczodrzynski.edziennik.data.api.task /*
* Copyright (c) Kuba Szczodrzyński 2020-1-16.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik
import com.google.gson.JsonObject import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
@ -12,11 +16,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.template.Template
import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.Vulcan import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.Vulcan
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.api.task.IApiTask
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTask(profileId) { open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTask(profileId) {
companion object { companion object {

View File

@ -15,12 +15,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.login.Edudzienn
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
@ -41,10 +41,8 @@ class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStor
private fun completed() { private fun completed() {
data.saveData() data.saveData()
data.notify {
callback.onCompleted() callback.onCompleted()
} }
}
/* _______ _ _ _ _ _ /* _______ _ _ _ _ _
|__ __| | /\ | | (_) | | | |__ __| | /\ | | (_) | | |

View File

@ -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.ENDPOINT_EDUDZIENNIK_WEB_EXAMS
import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb import pl.szczodrzynski.edziennik.data.api.edziennik.edudziennik.data.EdudziennikWeb
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Event import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.get import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
@ -60,7 +60,7 @@ class EdudziennikWebExams(override val data: DataEdudziennik,
startTime, startTime,
topic, topic,
-1, -1,
eventType.id.toInt(), eventType.id,
false, false,
-1, -1,
subject.id, subject.id,

View File

@ -17,12 +17,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login.IdziennikLo
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
@ -43,10 +43,8 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
private fun completed() { private fun completed() {
data.saveData() data.saveData()
data.notify {
callback.onCompleted() callback.onCompleted()
} }
}
/* _______ _ _ _ _ _ /* _______ _ _ _ _ _
|__ __| | /\ | | (_) | | | |__ __| | /\ | | (_) | | |

View File

@ -19,12 +19,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLogin
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
@ -45,10 +45,8 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va
private fun completed() { private fun completed() {
data.saveData() data.saveData()
data.notify {
callback.onCompleted() callback.onCompleted()
} }
}
/* _______ _ _ _ _ _ /* _______ _ _ _ _ _
|__ __| | /\ | | (_) | | | |__ __| | /\ | | (_) | | |

View File

@ -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.ENDPOINT_LIBRUS_API_EVENTS
import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusApi
import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.data.db.entity.Event import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
@ -34,7 +34,7 @@ class LibrusApiEvents(override val data: DataLibrus,
val id = event.getLong("Id") ?: return@forEach val id = event.getLong("Id") ?: return@forEach
val eventDate = Date.fromY_m_d(event.getString("Date")) val eventDate = Date.fromY_m_d(event.getString("Date"))
val topic = event.getString("Content") ?: "" val topic = event.getString("Content") ?: ""
val type = event.getJsonObject("Category")?.getInt("Id") ?: -1 val type = event.getJsonObject("Category")?.getLong("Id") ?: -1
val teacherId = event.getJsonObject("CreatedBy")?.getLong("Id") ?: -1 val teacherId = event.getJsonObject("CreatedBy")?.getLong("Id") ?: -1
val subjectId = event.getJsonObject("Subject")?.getLong("Id") ?: -1 val subjectId = event.getJsonObject("Subject")?.getLong("Id") ?: -1
val teamId = event.getJsonObject("Class")?.getLong("Id") ?: -1 val teamId = event.getJsonObject("Class")?.getLong("Id") ?: -1

View File

@ -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.EdziennikCallback
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore import pl.szczodrzynski.edziennik.data.db.entity.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
@ -45,10 +45,8 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto
private fun completed() { private fun completed() {
data.saveData() data.saveData()
data.notify {
callback.onCompleted() callback.onCompleted()
} }
}
/* _______ _ _ _ _ _ /* _______ _ _ _ _ _
|__ __| | /\ | | (_) | | | |__ __| | /\ | | (_) | | |

View File

@ -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.models.ApiError
import pl.szczodrzynski.edziennik.data.api.prepare import pl.szczodrzynski.edziennik.data.api.prepare
import pl.szczodrzynski.edziennik.data.api.templateLoginMethods 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.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
class Template(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { class Template(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
@ -40,10 +40,8 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore,
private fun completed() { private fun completed() {
data.saveData() data.saveData()
data.notify {
callback.onCompleted() callback.onCompleted()
} }
}
/* _______ _ _ _ _ _ /* _______ _ _ _ _ _
|__ __| | /\ | | (_) | | | |__ __| | /\ | | (_) | | |

View File

@ -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.prepare
import pl.szczodrzynski.edziennik.data.api.prepareFor import pl.szczodrzynski.edziennik.data.api.prepareFor
import pl.szczodrzynski.edziennik.data.api.vulcanLoginMethods 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.LoginStore
import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
@ -44,10 +44,8 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va
private fun completed() { private fun completed() {
data.saveData() data.saveData()
data.notify {
callback.onCompleted() callback.onCompleted()
} }
}
/* _______ _ _ _ _ _ /* _______ _ _ _ _ _
|__ __| | /\ | | (_) | | | |__ __| | /\ | | (_) | | |

View File

@ -0,0 +1,7 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-1-18.
*/
package pl.szczodrzynski.edziennik.data.api.events
class ProfileListEmptyEvent

View File

@ -194,7 +194,6 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
if (profile == null) if (profile == null)
return // return on first login return // return on first login
profile.empty = false
profile.userCode = generateUserCode() profile.userCode = generateUserCode()
db.profileDao().add(profile) db.profileDao().add(profile)
@ -294,21 +293,6 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt
db.messageRecipientDao().addAllIgnore(messageRecipientIgnoreList) 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) { fun setSyncNext(endpointId: Int, syncIn: Long? = null, viewId: Int? = null, syncAt: Long? = null) {
EndpointTimer(profile?.id EndpointTimer(profile?.id
?: -1, endpointId).apply { ?: -1, endpointId).apply {

View File

@ -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 { companion object {
fun futureExceptType(exceptType: Int) = Events(null, exceptType, null) fun futureExceptType(exceptType: Long) = Events(null, exceptType, null)
fun futureExceptTypes(exceptTypes: List<Int>) = Events(null, null, exceptTypes) fun futureExceptTypes(exceptTypes: List<Long>) = Events(null, null, exceptTypes)
fun futureWithType(type: Int) = Events(type, null, null) fun futureWithType(type: Long) = Events(type, null, null)
} }
fun commit(profileId: Int, dao: EventDao) { fun commit(profileId: Int, dao: EventDao) {

View File

@ -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()
}
}

View File

@ -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.DateAdapter
import pl.szczodrzynski.edziennik.data.api.szkolny.adapter.TimeAdapter 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.interceptor.SignatureInterceptor
import pl.szczodrzynski.edziennik.data.api.szkolny.request.ErrorReportRequest import pl.szczodrzynski.edziennik.data.api.szkolny.request.*
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.response.ApiResponse 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.api.szkolny.response.WebPushResponse
import pl.szczodrzynski.edziennik.data.db.entity.Event 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.entity.Profile
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.md5 import pl.szczodrzynski.edziennik.md5
import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time import pl.szczodrzynski.edziennik.utils.models.Time
@ -56,9 +55,8 @@ class SzkolnyApi(val app: App) {
api = retrofit.create() 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 teams = app.db.teamDao().allNow
val notifications = app.db.notificationDao().getNotPostedNow()
val response = api.serverSync(ServerSyncRequest( val response = api.serverSync(ServerSyncRequest(
deviceId = app.deviceId, deviceId = app.deviceId,
@ -88,8 +86,8 @@ class SzkolnyApi(val app: App) {
val config = app.config.getFor(profile.id) val config = app.config.getFor(profile.id)
val user = ServerSyncRequest.User( val user = ServerSyncRequest.User(
profile.userCode, profile.userCode,
profile.studentNameLong ?: "", profile.studentNameLong,
profile.studentNameShort ?: "", profile.studentNameShort,
profile.loginStoreType, profile.loginStoreType,
teams.filter { it.profileId == profile.id }.map { it.code } teams.filter { it.profileId == profile.id }.map { it.code }
) )
@ -108,17 +106,19 @@ class SzkolnyApi(val app: App) {
val events = mutableListOf<EventFull>() val events = mutableListOf<EventFull>()
response?.data?.events?.forEach { event -> response?.data?.events?.forEach { event ->
teams.filter { it.code == event.teamCode }.forEach { team -> if (event.id in blacklistedIds)
val profile = profiles.firstOrNull { it.id == team.profileId } 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 profileId = team.profileId
teamId = team.id teamId = team.id
addedManually = true addedManually = true
seen = profile?.empty ?: false seen = profile.empty
notified = profile?.empty ?: false notified = profile.empty
if (profile?.userCode == event.sharedBy) sharedBy = "self" if (profile.userCode == event.sharedBy) sharedBy = "self"
}) })
} }
} }
@ -193,4 +193,15 @@ class SzkolnyApi(val app: App) {
errors = errors errors = errors
)).execute().body() )).execute().body()
} }
fun unregisterAppUser(userCode: String): ApiResponse<Nothing>? {
return api.appUser(AppUserRequest(
deviceId = app.deviceId,
userCode = userCode
)).execute().body()
}
fun getUpdate(): ApiResponse<List<Update>>? {
return api.updates().execute().body()
}
} }

View File

@ -4,16 +4,16 @@
package pl.szczodrzynski.edziennik.data.api.szkolny package pl.szczodrzynski.edziennik.data.api.szkolny
import pl.szczodrzynski.edziennik.data.api.szkolny.request.ErrorReportRequest import pl.szczodrzynski.edziennik.data.api.szkolny.request.*
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.response.ApiResponse 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.ServerSyncResponse
import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update
import pl.szczodrzynski.edziennik.data.api.szkolny.response.WebPushResponse import pl.szczodrzynski.edziennik.data.api.szkolny.response.WebPushResponse
import retrofit2.Call import retrofit2.Call
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST import retrofit2.http.POST
import retrofit2.http.Query
interface SzkolnyService { interface SzkolnyService {
@ -28,4 +28,10 @@ interface SzkolnyService {
@POST("errorReport") @POST("errorReport")
fun errorReport(@Body request: ErrorReportRequest): Call<ApiResponse<Nothing>> 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>>>
} }

View File

@ -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
)

View File

@ -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?
)

View File

@ -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;
}
}

View File

@ -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
)
}
}
}

View File

@ -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()
}
}

View File

@ -0,0 +1,169 @@
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 as AppNotification
class PostNotifications(val app: App, nList: MutableList<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 {
val notificationManager = app.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val count = nList.size
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(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)
}
}
}
}

View File

@ -7,39 +7,39 @@ package pl.szczodrzynski.edziennik.data.api.task
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback 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.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 { companion object {
private const val TAG = "SzkolnyTask" 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) { internal fun run(taskCallback: EdziennikCallback) {
taskName = app.getString(R.string.edziennik_szkolny_api_sync_title) 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)*/
} }

View File

@ -16,8 +16,8 @@ import androidx.sqlite.db.SupportSQLiteQuery;
import java.util.List; import java.util.List;
import pl.szczodrzynski.edziennik.data.db.entity.Announcement; 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.entity.Metadata;
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull;
import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_ANNOUNCEMENT; 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) { public List<AnnouncementFull> getNotNotifiedNow(int profileId) {
return getAllNow(profileId, "notified = 0"); 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();
} }

View File

@ -5,20 +5,20 @@
package pl.szczodrzynski.edziennik.data.db.dao; package pl.szczodrzynski.edziennik.data.db.dao;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.sqlite.db.SimpleSQLiteQuery;
import androidx.sqlite.db.SupportSQLiteQuery;
import androidx.room.Dao; import androidx.room.Dao;
import androidx.room.Insert; import androidx.room.Insert;
import androidx.room.OnConflictStrategy; import androidx.room.OnConflictStrategy;
import androidx.room.Query; import androidx.room.Query;
import androidx.room.RawQuery; import androidx.room.RawQuery;
import androidx.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.entity.Attendance;
import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull; import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull;
import pl.szczodrzynski.edziennik.utils.models.Date; import pl.szczodrzynski.edziennik.utils.models.Date;
import java.util.List;
import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_ATTENDANCE; import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_ATTENDANCE;
@Dao @Dao
@ -72,6 +72,13 @@ public abstract class AttendanceDao {
return getAllNow(profileId, "notified = 0"); 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 // only absent and absent_excused count as absences
// all the other types are counted as being present // all the other types are counted as being present
@Query("SELECT \n" + @Query("SELECT \n" +

View File

@ -34,7 +34,7 @@ public abstract class EventDao {
public abstract long add(Event event); public abstract long add(Event event);
@Insert(onConflict = OnConflictStrategy.REPLACE) @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") @Query("DELETE FROM events WHERE profileId = :profileId")
public abstract void clear(int 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") @Query("DELETE FROM metadata WHERE profileId = :profileId AND thingType = :thingType AND thingId = :thingId")
public abstract void removeMetadata(int profileId, int thingType, long thingId); public abstract void removeMetadata(int profileId, int thingType, long thingId);
@Transaction @Transaction
public void remove(int profileId, int type, long id) { public void remove(int profileId, long type, long id) {
remove(profileId, id); remove(profileId, id);
removeMetadata(profileId, type == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, 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) { public LiveData<List<EventFull>> getAllWhere(int profileId, String filter) {
return getAll(profileId, 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); return getAll(profileId, "eventType = "+type+" AND "+filter);
} }
public LiveData<List<EventFull>> getAllByDate(int profileId, @NonNull Date date) { public LiveData<List<EventFull>> getAllByDate(int profileId, @NonNull Date date) {
@ -125,6 +125,24 @@ public abstract class EventDao {
return getAllNow(profileId, "notified = 0"); 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) { public EventFull getByIdNow(int profileId, long eventId) {
List<EventFull> eventList = getAllNow(profileId, "eventId = "+eventId); List<EventFull> eventList = getAllNow(profileId, "eventId = "+eventId);
return eventList.size() == 0 ? null : eventList.get(0); 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") @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") @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 @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(']', ')')); removeFuture(profileId, todayDate, "eventType NOT IN " + exceptTypes.toString().replace('[', '(').replace(']', ')'));
} }

View File

@ -4,14 +4,14 @@
package pl.szczodrzynski.edziennik.data.db.dao; package pl.szczodrzynski.edziennik.data.db.dao;
import java.util.List;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.room.Dao; import androidx.room.Dao;
import androidx.room.Insert; import androidx.room.Insert;
import androidx.room.OnConflictStrategy; import androidx.room.OnConflictStrategy;
import androidx.room.Query; import androidx.room.Query;
import java.util.List;
import pl.szczodrzynski.edziennik.data.db.entity.EventType; import pl.szczodrzynski.edziennik.data.db.entity.EventType;
@Dao @Dao
@ -26,11 +26,14 @@ public interface EventTypeDao {
void clear(int profileId); void clear(int profileId);
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId AND eventType = :typeId") @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") @Query("SELECT * FROM eventTypes WHERE profileId = :profileId")
LiveData<List<EventType>> getAll(int profileId); LiveData<List<EventType>> getAll(int profileId);
@Query("SELECT * FROM eventTypes WHERE profileId = :profileId") @Query("SELECT * FROM eventTypes WHERE profileId = :profileId")
List<EventType> getAllNow(int profileId); List<EventType> getAllNow(int profileId);
@Query("SELECT * FROM eventTypes")
List<EventType> getAllNow();
} }

View File

@ -83,6 +83,13 @@ public abstract class GradeDao {
return getAllNow(profileId, "gradeParentId = "+parentId); 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 @RawQuery
abstract GradeFull getNow(SupportSQLiteQuery query); abstract GradeFull getNow(SupportSQLiteQuery query);
public GradeFull getNow(int profileId, String filter) { public GradeFull getNow(int profileId, String filter) {

View File

@ -20,4 +20,7 @@ interface LibrusLessonDao {
@Query("SELECT * FROM librusLessons WHERE profileId = :profileId") @Query("SELECT * FROM librusLessons WHERE profileId = :profileId")
fun getAllNow(profileId: Int): List<LibrusLesson> fun getAllNow(profileId: Int): List<LibrusLesson>
@Query("DELETE FROM librusLessons WHERE profileId = :profileId")
fun clear(profileId: Int)
} }

View File

@ -17,9 +17,9 @@ import androidx.sqlite.db.SupportSQLiteQuery;
import java.util.List; import java.util.List;
import pl.szczodrzynski.edziennik.data.db.entity.LuckyNumber; 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.Metadata;
import pl.szczodrzynski.edziennik.data.db.entity.Notice; import pl.szczodrzynski.edziennik.data.db.entity.Notice;
import pl.szczodrzynski.edziennik.data.db.full.LuckyNumberFull;
import pl.szczodrzynski.edziennik.utils.models.Date; import pl.szczodrzynski.edziennik.utils.models.Date;
import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_LUCKY_NUMBER; 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) { public List<LuckyNumberFull> getNotNotifiedNow(int profileId) {
return getAllNow(profileId, "notified = 0"); 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();
} }

View File

@ -17,8 +17,8 @@ import androidx.sqlite.db.SupportSQLiteQuery;
import java.util.List; import java.util.List;
import pl.szczodrzynski.edziennik.data.db.entity.Message; import pl.szczodrzynski.edziennik.data.db.entity.Message;
import pl.szczodrzynski.edziennik.data.db.full.MessageFull;
import pl.szczodrzynski.edziennik.data.db.entity.Metadata; 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_DELETED;
import static pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED; 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) { public List<MessageFull> getReceivedNotNotifiedNow(int profileId) {
return getReceivedNow(profileId, "notified = 0"); 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();
} }

View File

@ -40,6 +40,9 @@ public abstract class MetadataDao {
@Insert(onConflict = OnConflictStrategy.IGNORE) @Insert(onConflict = OnConflictStrategy.IGNORE)
public abstract void addAllIgnore(List<Metadata> metadataList); 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") @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); 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") @Query("UPDATE metadata SET notified = :notified WHERE profileId = :profileId")
public abstract void setAllNotified(int profileId, boolean notified); 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") @Query("SELECT count() FROM metadata WHERE profileId = :profileId AND thingType = :thingType AND seen = 0")

View File

@ -5,13 +5,13 @@
package pl.szczodrzynski.edziennik.data.db.dao; package pl.szczodrzynski.edziennik.data.db.dao;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.sqlite.db.SimpleSQLiteQuery;
import androidx.sqlite.db.SupportSQLiteQuery;
import androidx.room.Dao; import androidx.room.Dao;
import androidx.room.Insert; import androidx.room.Insert;
import androidx.room.OnConflictStrategy; import androidx.room.OnConflictStrategy;
import androidx.room.Query; import androidx.room.Query;
import androidx.room.RawQuery; import androidx.room.RawQuery;
import androidx.sqlite.db.SimpleSQLiteQuery;
import androidx.sqlite.db.SupportSQLiteQuery;
import java.util.List; import java.util.List;
@ -69,4 +69,14 @@ public abstract class NoticeDao {
public List<NoticeFull> getNotNotifiedNow(int profileId) { public List<NoticeFull> getNotNotifiedNow(int profileId) {
return getAllNow(profileId, "notified = 0"); 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();
} }

View File

@ -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") @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> 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? 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? val lastId: Int?
@Query("UPDATE profiles SET empty = 0")
fun setAllNotEmpty()
} }

View File

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

View File

@ -32,19 +32,19 @@ public class Event {
public String topic; public String topic;
@ColumnInfo(name = "eventColor") @ColumnInfo(name = "eventColor")
public int color = -1; public int color = -1;
public static final int TYPE_UNDEFINED = -2; public static final long TYPE_UNDEFINED = -2;
public static final int TYPE_HOMEWORK = -1; public static final long TYPE_HOMEWORK = -1;
public static final int TYPE_DEFAULT = 0; public static final long TYPE_DEFAULT = 0;
public static final int TYPE_EXAM = 1; public static final long TYPE_EXAM = 1;
public static final int TYPE_SHORT_QUIZ = 2; public static final long TYPE_SHORT_QUIZ = 2;
public static final int TYPE_ESSAY = 3; public static final long TYPE_ESSAY = 3;
public static final int TYPE_PROJECT = 4; public static final long TYPE_PROJECT = 4;
public static final int TYPE_PT_MEETING = 5; public static final long TYPE_PT_MEETING = 5;
public static final int TYPE_EXCURSION = 6; public static final long TYPE_EXCURSION = 6;
public static final int TYPE_READING = 7; public static final long TYPE_READING = 7;
public static final int TYPE_CLASS_EVENT = 8; public static final long TYPE_CLASS_EVENT = 8;
public static final int TYPE_INFORMATION = 9; public static final long TYPE_INFORMATION = 9;
public static final int TYPE_TEACHER_ABSENCE = 10; public static final long TYPE_TEACHER_ABSENCE = 10;
public static final int COLOR_HOMEWORK = 0xff795548; public static final int COLOR_HOMEWORK = 0xff795548;
public static final int COLOR_DEFAULT = 0xffffc107; public static final int COLOR_DEFAULT = 0xffffc107;
public static final int COLOR_EXAM = 0xfff44336; 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_INFORMATION = 0xff039be5;
public static final int COLOR_TEACHER_ABSENCE = 0xff039be5; public static final int COLOR_TEACHER_ABSENCE = 0xff039be5;
@ColumnInfo(name = "eventType") @ColumnInfo(name = "eventType")
public int type = TYPE_DEFAULT; public long type = TYPE_DEFAULT;
@ColumnInfo(name = "eventAddedManually") @ColumnInfo(name = "eventAddedManually")
public boolean addedManually; public boolean addedManually;
@ColumnInfo(name = "eventSharedBy") @ColumnInfo(name = "eventSharedBy")
@ -76,7 +76,7 @@ public class Event {
@Ignore @Ignore
public Event() {} 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.profileId = profileId;
this.id = id; this.id = id;
@ -97,8 +97,8 @@ public class Event {
} }
@Override @Override
public Event clone() throws CloneNotSupportedException { public Event clone() {
return new Event( Event event = new Event(
profileId, profileId,
id, id,
eventDate.clone(), eventDate.clone(),
@ -111,6 +111,10 @@ public class Event {
teacherId, teacherId,
teamId teamId
); );
event.sharedBy = sharedBy;
event.sharedByName = sharedByName;
event.blacklisted = blacklisted;
return event;
} }
@Override @Override

View File

@ -4,34 +4,18 @@
package pl.szczodrzynski.edziennik.data.db.entity package pl.szczodrzynski.edziennik.data.db.entity
import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.google.gson.JsonObject import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.MainActivity
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
@Entity(tableName = "notifications") @Entity(tableName = "notifications")
data class Notification( data class Notification(
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
val id: Int = 0, val id: Long = 0,
val title: String, val title: String,
val text: String, val text: String,
@ -41,7 +25,7 @@ data class Notification(
val profileId: Int?, val profileId: Int?,
val profileName: String?, val profileName: String?,
var posted: Boolean = false, var posted: Boolean = true,
var viewId: Int? = null, var viewId: Int? = null,
var extras: JsonObject? = null, var extras: JsonObject? = null,
@ -59,6 +43,7 @@ data class Notification(
const val TYPE_NEW_HOMEWORK = 10 const val TYPE_NEW_HOMEWORK = 10
const val TYPE_NEW_SHARED_EVENT = 7 const val TYPE_NEW_SHARED_EVENT = 7
const val TYPE_NEW_SHARED_HOMEWORK = 12 const val TYPE_NEW_SHARED_HOMEWORK = 12
const val TYPE_REMOVED_SHARED_EVENT = 18
const val TYPE_NEW_MESSAGE = 8 const val TYPE_NEW_MESSAGE = 8
const val TYPE_NEW_NOTICE = 9 const val TYPE_NEW_NOTICE = 9
const val TYPE_NEW_ATTENDANCE = 13 const val TYPE_NEW_ATTENDANCE = 13
@ -67,6 +52,10 @@ data class Notification(
const val TYPE_NEW_ANNOUNCEMENT = 15 const val TYPE_NEW_ANNOUNCEMENT = 15
const val TYPE_FEEDBACK_MESSAGE = 16 const val TYPE_FEEDBACK_MESSAGE = 16
const val TYPE_AUTO_ARCHIVING = 17 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 { fun addExtra(key: String, value: Long?): Notification {
@ -100,4 +89,10 @@ data class Notification(
e.printStackTrace() 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)
}
} }

View File

@ -30,21 +30,21 @@ open class Profile(
val loginStoreId: Int, val loginStoreId: Int,
val loginStoreType: Int, val loginStoreType: Int,
override var name: String, override var name: String = "",
override var subname: String?, override var subname: String? = null,
/** /**
* The name of the student. * The name of the student.
* This doesn't change, no matter if it's a parent or student account. * This doesn't change, no matter if it's a parent or student account.
*/ */
var studentNameLong: String, var studentNameLong: String = "",
var studentNameShort: String, var studentNameShort: String = "",
/** /**
* A full name of the account owner. * A full name of the account owner.
* If null, then it's a student account. * If null, then it's a student account.
* If not null, then it's a parent account with this name. * If not null, then it's a parent account with this name.
*/ */
var accountName: String?, var accountName: String? = null,
val studentData: JsonObject = JsonObject() val studentData: JsonObject = JsonObject()

View File

@ -45,6 +45,35 @@ public class EventFull extends Event {
this.sharedByName = event.sharedByName; this.sharedByName = event.sharedByName;
this.blacklisted = event.blacklisted; 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) { public EventFull(Event event, Metadata metadata) {
this(event); this(event);

View File

@ -11,11 +11,14 @@ import android.content.Intent
import android.util.Log import android.util.Log
import com.google.firebase.iid.zzaq import com.google.firebase.iid.zzaq
import com.google.firebase.iid.zzv import com.google.firebase.iid.zzv
import com.google.firebase.messaging.MessagingAnalytics
import com.google.firebase.messaging.zzc import com.google.firebase.messaging.zzc
import com.google.gson.JsonObject import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.*
import java.util.* 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") @SuppressLint("Registered")
open class FirebaseService : zzc() { open class FirebaseService : zzc() {
@ -45,8 +48,8 @@ open class FirebaseService : zzc() {
} }
} }
if (MessagingAnalytics.shouldUploadMetrics(intent)) { if (shouldUploadMetrics(intent)) {
MessagingAnalytics.logNotificationOpen(intent) logNotificationOpen(intent)
} }
return true return true
@ -62,8 +65,8 @@ open class FirebaseService : zzc() {
when (action) { when (action) {
"com.google.firebase.messaging.NOTIFICATION_DISMISS" -> { "com.google.firebase.messaging.NOTIFICATION_DISMISS" -> {
if (MessagingAnalytics.shouldUploadMetrics(intent)) { if (shouldUploadMetrics(intent)) {
MessagingAnalytics.logNotificationDismiss(intent) logNotificationDismiss(intent)
} }
} }
"com.google.firebase.messaging.NEW_TOKEN" -> { "com.google.firebase.messaging.NEW_TOKEN" -> {
@ -106,8 +109,8 @@ open class FirebaseService : zzc() {
// get the message type // get the message type
when (val it = json.getString("message_type") ?: "gcm") { when (val it = json.getString("message_type") ?: "gcm") {
"gcm" -> { // 0 "gcm" -> { // 0
if (MessagingAnalytics.shouldUploadMetrics(intent)) { if (shouldUploadMetrics(intent)) {
MessagingAnalytics.logNotificationReceived(intent) logNotificationReceived(intent, null)
} }
onMessageReceived(Message(messageId, json)) onMessageReceived(Message(messageId, json))

View File

@ -5,36 +5,19 @@
package pl.szczodrzynski.edziennik.data.firebase; package pl.szczodrzynski.edziennik.data.firebase;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.util.Log;
import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage; import com.google.firebase.messaging.RemoteMessage;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.util.List; import java.util.List;
import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.BuildConfig; import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask;
import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask;
import pl.szczodrzynski.edziennik.data.db.entity.Event;
import pl.szczodrzynski.edziennik.data.db.entity.EventType;
import pl.szczodrzynski.edziennik.data.db.entity.FeedbackMessage;
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore; import pl.szczodrzynski.edziennik.data.db.entity.LoginStore;
import pl.szczodrzynski.edziennik.data.db.entity.Profile; import pl.szczodrzynski.edziennik.data.db.entity.Profile;
import pl.szczodrzynski.edziennik.data.db.entity.Team;
import pl.szczodrzynski.edziennik.data.db.full.EventFull;
import pl.szczodrzynski.edziennik.network.ServerRequest;
import pl.szczodrzynski.edziennik.ui.modules.base.DebugFragment;
import pl.szczodrzynski.edziennik.utils.models.Notification;
import static pl.szczodrzynski.edziennik.App.APP_URL;
import static pl.szczodrzynski.edziennik.data.db.entity.Event.TYPE_HOMEWORK;
import static pl.szczodrzynski.edziennik.utils.Utils.d; import static pl.szczodrzynski.edziennik.utils.Utils.d;
import static pl.szczodrzynski.edziennik.utils.Utils.strToInt; import static pl.szczodrzynski.edziennik.utils.Utils.strToInt;
@ -45,16 +28,16 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
public void onNewToken(String s) { public void onNewToken(String s) {
super.onNewToken(s); super.onNewToken(s);
Log.d(TAG, "New token: "+s); /* Log.d(TAG, "New token: "+s);
App app = (App)getApplicationContext(); App app = (App)getApplicationContext();
if (app.config.getSync().getTokenApp() == null || !app.config.getSync().getTokenApp().equals(s)) { if (app.config.getSync().getTokenApp() == null || !app.config.getSync().getTokenApp().equals(s)) {
app.config.getSync().setTokenApp(s); app.config.getSync().setTokenApp(s);
} }*/
} }
@Override @Override
public void onMessageReceived(RemoteMessage remoteMessage) { public void onMessageReceived(RemoteMessage remoteMessage) {
App app = ((App) getApplicationContext()); /*App app = ((App) getApplicationContext());
// Not getting messages here? See why this may be: https://goo.gl/39bRNJ // Not getting messages here? See why this may be: https://goo.gl/39bRNJ
String from = remoteMessage.getFrom(); String from = remoteMessage.getFrom();
@ -78,7 +61,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
processVulcanPush(app, remoteMessage); processVulcanPush(app, remoteMessage);
break; break;
} }
} }*/
} }
private void processMobidziennikPush(App app, RemoteMessage remoteMessage) { private void processMobidziennikPush(App app, RemoteMessage remoteMessage) {
@ -102,14 +85,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
if (profile != null) { if (profile != null) {
if (remoteMessage.getData().get("id_wiadomosci") != null) { if (remoteMessage.getData().get("id_wiadomosci") != null) {
/*app.notifier.add(new Notification(app.getContext(), remoteMessage.getData().get("message"))
.withProfileData(profile.id, profile.name)
.withTitle(remoteMessage.getData().get("title"))
.withType(Notification.TYPE_NEW_MESSAGE)
.withFragmentRedirect(MainActivity.DRAWER_ITEM_MESSAGES)
);
app.notifier.postAll(profile);
app.saveConfig("notifications");*/
d(TAG, "Syncing profile " + profile.getId()); d(TAG, "Syncing profile " + profile.getId());
EdziennikTask.Companion.syncProfile(profile.getId(), null, null).enqueue(app); EdziennikTask.Companion.syncProfile(profile.getId(), null, null).enqueue(app);
} else { } else {
@ -141,7 +117,7 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
private void processAppPush(App app, RemoteMessage remoteMessage) { private void processAppPush(App app, RemoteMessage remoteMessage) {
// Check if message contains a data payload. // Check if message contains a data payload.
String type = remoteMessage.getData().get("type"); /*String type = remoteMessage.getData().get("type");
if (remoteMessage.getData().size() > 0 if (remoteMessage.getData().size() > 0
&& type != null) { && type != null) {
//Log.d(TAG, "Message data payload: " + remoteMessage.sync()); //Log.d(TAG, "Message data payload: " + remoteMessage.sync());
@ -239,90 +215,8 @@ public class MyFirebaseMessagingService extends FirebaseMessagingService {
break; break;
case "ping": case "ping":
// just a ping // just a ping
break; break
/* ______ _ _
| ____| | | | |
| |____ _____ _ __ | |_ ______ ___| |__ __ _ _ __ ___
| __\ \ / / _ \ '_ \| __| |______| / __| '_ \ / _` | '__/ _ \
| |___\ V / __/ | | | |_ \__ \ | | | (_| | | | __/
|______\_/ \___|_| |_|\__| |___/_| |_|\__,_|_| \__*/
case "event":
case "event_removed":
AsyncTask.execute(() -> {
String teamCode = remoteMessage.getData().get("team");
String teamUnshareCode = remoteMessage.getData().get("team_unshare");
while (teamCode != null || teamUnshareCode != null) {
d(TAG, "Got an event for teamCode " + teamCode + " and teamUnshareCode " + teamUnshareCode);
// get the target Profile by the corresponding teamCode
List<Profile> profiles = app.db.profileDao().getByTeamCodeNowWithRegistration(teamCode == null ? teamUnshareCode : teamCode);
for (Profile profile : profiles) {
d(TAG, "Matched profile " + profile.getName());
if (teamCode != null) {
// SHARING
JsonObject jEvent = new JsonParser().parse(remoteMessage.getData().get("data")).getAsJsonObject();
d(TAG, "An event is there! " + jEvent.toString());
// get the target Team from teamCode
Team team = app.db.teamDao().getByCodeNow(profile.getId(), teamCode);
if (team != null) {
d(TAG, "The target team is " + team.name + ", ID " + team.id);
// create the event from Json. Add the missing teamId and !!profileId!!
Event event = app.gson.fromJson(jEvent.toString(), Event.class);
if (jEvent.get("colorDefault") != null) {
event.color = -1;
}
event.profileId = profile.getId();
event.teamId = team.id;
d(TAG, "Created the event! " + event);
// TODO? i guess
Event oldEvent = app.db.eventDao().getByIdNow(profile.getId(), event.id);
if (event.sharedBy != null && event.sharedBy.equals(profile.getUserCode())) {
d(TAG, "Shared by self! Changing name");
event.sharedBy = "self";
event.sharedByName = profile.getStudentNameLong();
}
d(TAG, "Old event found? " + oldEvent);
EventType eventType = app.db.eventTypeDao().getByIdNow(profile.getId(), event.type);
app.notifier.add(new Notification(app.getContext(), app.getString((oldEvent == null ? R.string.notification_shared_event_format : R.string.notification_shared_event_modified_format), event.sharedByName, eventType == null ? "wydarzenie" : eventType.name, event.eventDate.getFormattedString(), event.topic))
.withProfileData(profile.getId(), profile.getName())
.withType(event.type == TYPE_HOMEWORK ? pl.szczodrzynski.edziennik.data.db.entity.Notification.TYPE_NEW_SHARED_HOMEWORK : pl.szczodrzynski.edziennik.data.db.entity.Notification.TYPE_NEW_SHARED_EVENT)
.withFragmentRedirect(event.type == TYPE_HOMEWORK ? MainActivity.DRAWER_ITEM_HOMEWORK : MainActivity.DRAWER_ITEM_AGENDA)
.withLongExtra("eventDate", event.eventDate.getValue())
);
d(TAG, "Finishing adding event " + event);
app.db.eventDao().add(event);
try {
app.db.metadataDao().setBoth(profile.getId(), event, false, true, jEvent.get("addedDate").getAsLong());
} catch (Exception e) {
e.printStackTrace();
}
}
} else {
// UNSHARING
long eventId = Long.parseLong(remoteMessage.getData().get("remove_id"));
EventFull oldEvent = app.db.eventDao().getByIdNow(profile.getId(), eventId);
if (oldEvent != null) {
app.notifier.add(new Notification(app.getContext(), app.getString(R.string.notification_shared_event_removed_format, oldEvent.sharedByName, oldEvent.typeName, oldEvent.eventDate.getFormattedString(), oldEvent.topic))
.withProfileData(profile.getId(), profile.getName())
.withType(oldEvent.type == TYPE_HOMEWORK ? pl.szczodrzynski.edziennik.data.db.entity.Notification.TYPE_NEW_SHARED_HOMEWORK : pl.szczodrzynski.edziennik.data.db.entity.Notification.TYPE_NEW_SHARED_EVENT)
.withFragmentRedirect(oldEvent.type == TYPE_HOMEWORK ? MainActivity.DRAWER_ITEM_HOMEWORK : MainActivity.DRAWER_ITEM_AGENDA)
.withLongExtra("eventDate", oldEvent.eventDate.getValue())
);
app.db.eventDao().remove(oldEvent);
}
}
}
if (teamCode != null) {
teamCode = null;
} else {
teamUnshareCode = null;
}
}
app.notifier.postAll();
app.saveConfig();
});
break;
}
} }
}*/
} }
} }

View File

@ -4,11 +4,129 @@
package pl.szczodrzynski.edziennik.data.firebase package pl.szczodrzynski.edziennik.data.firebase
import pl.szczodrzynski.edziennik.App import com.google.gson.JsonParser
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.task.PostNotifications
import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Notification
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.utils.models.Date
import pl.szczodrzynski.edziennik.utils.models.Time
class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message: FirebaseService.Message) { class SzkolnyAppFirebase(val app: App, val profiles: List<Profile>, val message: FirebaseService.Message) {
init { init {
run {
val type = message.data.getString("type") ?: return@run
when (type) {
"sharedEvent" -> sharedEvent(
message.data.getString("shareTeamCode") ?: return@run,
message.data.getString("event") ?: return@run,
message.data.getString("message") ?: return@run
)
"unsharedEvent" -> unsharedEvent(
message.data.getString("unshareTeamCode") ?: return@run,
message.data.getLong("eventId") ?: return@run,
message.data.getString("message") ?: return@run
)
}
}
}
private fun sharedEvent(teamCode: String, jsonStr: String, message: String) {
val json = JsonParser().parse(jsonStr).asJsonObject
val teams = app.db.teamDao().allNow
val eventTypes = app.db.eventTypeDao().allNow
val events = mutableListOf<Event>()
val metadataList = mutableListOf<Metadata>()
val notificationList = mutableListOf<Notification>()
teams.filter { it.code == teamCode }.distinctBy { it.profileId }.forEach { team ->
val profile = profiles.firstOrNull { it.id == team.profileId }
val event = Event(
team.profileId,
json.getLong("id") ?: return,
json.getInt("eventDate")?.let { Date.fromValue(it) } ?: return,
json.getInt("startTime")?.let { Time.fromValue(it) },
json.getString("topic") ?: "",
json.getInt("color") ?: -1,
json.getLong("type") ?: 0,
true,
json.getLong("teacherId") ?: -1,
json.getLong("subjectId") ?: -1,
team.id
)
// TODO? i guess - this comment is here for like a year
//val oldEvent: Event? = app.db.eventDao().getByIdNow(profile?.id ?: -1, event.id)
event.sharedBy = json.getString("sharedBy")
event.sharedByName = json.getString("sharedByName")
if (profile?.userCode == event.sharedBy) event.sharedBy = "self"
val metadata = Metadata(
event.profileId,
if (event.type == Event.TYPE_HOMEWORK) Metadata.TYPE_HOMEWORK else Metadata.TYPE_EVENT,
event.id,
false,
true,
json.getLong("addedDate") ?: System.currentTimeMillis()
)
//val eventType = eventTypes.firstOrNull { it.profileId == profile?.id && it.id == event.type }
/*val text = app.getString(
if (oldEvent == null)
R.string.notification_shared_event_format
else
R.string.notification_shared_event_modified_format,
event.sharedByName,
eventType?.name ?: "wydarzenie",
event.eventDate.formattedString,
event.topic
)*/
val type = if (event.type == Event.TYPE_HOMEWORK) Notification.TYPE_NEW_SHARED_HOMEWORK else Notification.TYPE_NEW_SHARED_EVENT
val notification = Notification(
id = Notification.buildId(event.profileId, type, event.id),
title = app.getNotificationTitle(type),
text = message,
type = type,
profileId = profile?.id,
profileName = profile?.name,
viewId = if (event.type == Event.TYPE_HOMEWORK) MainActivity.DRAWER_ITEM_HOMEWORK else MainActivity.DRAWER_ITEM_AGENDA,
addedDate = metadata.addedDate
).addExtra("eventId", event.id).addExtra("eventDate", event.eventDate.value.toLong())
events += event
metadataList += metadata
notificationList += notification
}
app.db.eventDao().addAll(events)
app.db.metadataDao().addAllReplace(metadataList)
app.db.notificationDao().addAll(notificationList)
PostNotifications(app, notificationList)
}
private fun unsharedEvent(teamCode: String, eventId: Long, message: String) {
val teams = app.db.teamDao().allNow
val notificationList = mutableListOf<Notification>()
teams.filter { it.code == teamCode }.distinctBy { it.profileId }.forEach { team ->
val profile = profiles.firstOrNull { it.id == team.profileId }
val notification = Notification(
id = Notification.buildId(profile?.id ?: 0, Notification.TYPE_REMOVED_SHARED_EVENT, eventId),
title = app.getNotificationTitle(Notification.TYPE_REMOVED_SHARED_EVENT),
text = message,
type = Notification.TYPE_REMOVED_SHARED_EVENT,
profileId = profile?.id,
profileName = profile?.name,
viewId = MainActivity.DRAWER_ITEM_AGENDA
)
notificationList += notification
app.db.eventDao().remove(team.profileId, eventId)
}
app.db.notificationDao().addAll(notificationList)
PostNotifications(app, notificationList)
} }
} }

View File

@ -5,7 +5,7 @@
package pl.szczodrzynski.edziennik.data.firebase package pl.szczodrzynski.edziennik.data.firebase
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.task.IApiTask import pl.szczodrzynski.edziennik.data.api.task.IApiTask
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.getString import pl.szczodrzynski.edziennik.getString

View File

@ -9,7 +9,7 @@ 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_BEHAVIOUR
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_GRADES import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_GRADES
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.task.IApiTask import pl.szczodrzynski.edziennik.data.api.task.IApiTask
import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED import pl.szczodrzynski.edziennik.data.db.entity.Message.TYPE_RECEIVED
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile

View File

@ -22,7 +22,7 @@ public class NetworkUtils {
public boolean isOnline() { public boolean isOnline() {
assert app != null; assert app != null;
ConnectivityManager cm = ConnectivityManager cm =
(ConnectivityManager) app.getContext().getSystemService(Context.CONNECTIVITY_SERVICE); (ConnectivityManager) app.getSystemService(Context.CONNECTIVITY_SERVICE);
assert cm != null; assert cm != null;
NetworkInfo netInfo = cm.getActiveNetworkInfo(); NetworkInfo netInfo = cm.getActiveNetworkInfo();
return netInfo != null && netInfo.isConnectedOrConnecting(); return netInfo != null && netInfo.isConnectedOrConnecting();
@ -30,7 +30,7 @@ public class NetworkUtils {
public int checkBackgroundDataRestricted() { public int checkBackgroundDataRestricted() {
ConnectivityManager connMgr = (ConnectivityManager) app.getContext().getSystemService(Context.CONNECTIVITY_SERVICE); ConnectivityManager connMgr = (ConnectivityManager) app.getSystemService(Context.CONNECTIVITY_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
assert connMgr != null; assert connMgr != null;

View File

@ -29,7 +29,7 @@ public class ServerRequest {
private String source = ""; private String source = "";
public ServerRequest(App app, String url, String source) { public ServerRequest(App app, String url, String source) {
this(app, url, source, app.profile); this(app, url, source, App.Companion.getProfile());
} }
public ServerRequest(App app, String url, String source, Profile profileFull) { public ServerRequest(App app, String url, String source, Profile profileFull) {
@ -40,7 +40,7 @@ public class ServerRequest {
this.app = app; this.app = app;
this.url = url; this.url = url;
this.params = new ArrayList<>(); this.params = new ArrayList<>();
this.username = (profile != null && profile.getRegistration() == REGISTRATION_ENABLED ? usernameId : app.deviceId); this.username = (profile != null && profile.getRegistration() == REGISTRATION_ENABLED ? usernameId : app.getDeviceId());
this.source = source; this.source = source;
if (profile != null && profile.getRegistration() == REGISTRATION_ENABLED) { if (profile != null && profile.getRegistration() == REGISTRATION_ENABLED) {
this.setBodyParameter("login_type", Integer.toString(loginStoreType)); this.setBodyParameter("login_type", Integer.toString(loginStoreType));
@ -51,7 +51,7 @@ public class ServerRequest {
this.setBodyParameter("team_ids", "UI_THREAD"); this.setBodyParameter("team_ids", "UI_THREAD");
} }
else { else {
this.setBodyParameter("team_ids", app.gson.toJson(app.db.teamDao().getAllCodesNow(profile.getId()))); this.setBodyParameter("team_ids", app.getGson().toJson(App.db.teamDao().getAllCodesNow(profile.getId())));
} }
} }
} }
@ -86,15 +86,15 @@ public class ServerRequest {
.addParameter("app_version_build_type", BuildConfig.BUILD_TYPE) .addParameter("app_version_build_type", BuildConfig.BUILD_TYPE)
.addParameter("app_version_code", Integer.toString(BuildConfig.VERSION_CODE)) .addParameter("app_version_code", Integer.toString(BuildConfig.VERSION_CODE))
.addParameter("app_version", BuildConfig.VERSION_NAME + " " + BuildConfig.BUILD_TYPE + " (" + BuildConfig.VERSION_CODE + ")") .addParameter("app_version", BuildConfig.VERSION_NAME + " " + BuildConfig.BUILD_TYPE + " (" + BuildConfig.VERSION_CODE + ")")
.addParameter("device_id", Settings.Secure.getString(app.getContext().getContentResolver(), Settings.Secure.ANDROID_ID)) .addParameter("device_id", Settings.Secure.getString(app.getContentResolver(), Settings.Secure.ANDROID_ID))
.addParameter("device_model", Build.MANUFACTURER+" "+Build.MODEL) .addParameter("device_model", Build.MANUFACTURER+" "+Build.MODEL)
.addParameter("device_os_version", Build.VERSION.RELEASE) .addParameter("device_os_version", Build.VERSION.RELEASE)
.addParameter("fcm_token", app.config.getSync().getTokenApp()) .addParameter("fcm_token", app.getConfig().getSync().getTokenApp())
.addParameter("signature", sign(app.signature, timestamp)) .addParameter("signature", "TODO")
.addParameter("signature_timestamp", timestamp) .addParameter("signature_timestamp", timestamp)
.addParameter("package_name", "pl.szczodrzynski.edziennik") .addParameter("package_name", "pl.szczodrzynski.edziennik")
.addParameter("source", source) .addParameter("source", source)
.addParameter("update_frequency", app.config.getSync().getEnabled() ? app.config.getSync().getInterval() : -1) .addParameter("update_frequency", app.getConfig().getSync().getEnabled() ? app.getConfig().getSync().getInterval() : -1)
.post() .post()
.callback(new JsonCallbackHandler() { .callback(new JsonCallbackHandler() {
@Override @Override
@ -123,15 +123,15 @@ public class ServerRequest {
.addParameter("app_version_build_type", BuildConfig.BUILD_TYPE) .addParameter("app_version_build_type", BuildConfig.BUILD_TYPE)
.addParameter("app_version_code", Integer.toString(BuildConfig.VERSION_CODE)) .addParameter("app_version_code", Integer.toString(BuildConfig.VERSION_CODE))
.addParameter("app_version", BuildConfig.VERSION_NAME + " " + BuildConfig.BUILD_TYPE + " (" + BuildConfig.VERSION_CODE + ")") .addParameter("app_version", BuildConfig.VERSION_NAME + " " + BuildConfig.BUILD_TYPE + " (" + BuildConfig.VERSION_CODE + ")")
.addParameter("device_id", Settings.Secure.getString(app.getContext().getContentResolver(), Settings.Secure.ANDROID_ID)) .addParameter("device_id", Settings.Secure.getString(app.getContentResolver(), Settings.Secure.ANDROID_ID))
.addParameter("device_model", Build.MANUFACTURER+" "+Build.MODEL) .addParameter("device_model", Build.MANUFACTURER+" "+Build.MODEL)
.addParameter("device_os_version", Build.VERSION.RELEASE) .addParameter("device_os_version", Build.VERSION.RELEASE)
.addParameter("fcm_token", app.config.getSync().getTokenApp()) .addParameter("fcm_token", app.getConfig().getSync().getTokenApp())
.addParameter("signature", sign(app.signature, timestamp)) .addParameter("signature", "TODO")
.addParameter("signature_timestamp", timestamp) .addParameter("signature_timestamp", timestamp)
.addParameter("package_name", "pl.szczodrzynski.edziennik") .addParameter("package_name", "pl.szczodrzynski.edziennik")
.addParameter("source", source) .addParameter("source", source)
.addParameter("update_frequency", app.config.getSync().getEnabled() ? app.config.getSync().getInterval() : -1) .addParameter("update_frequency", app.getConfig().getSync().getEnabled() ? app.getConfig().getSync().getInterval() : -1)
.post() .post()
.build() .build()
.execute(); .execute();

View File

@ -1,287 +0,0 @@
package pl.szczodrzynski.edziennik.receivers;
import android.app.AlarmManager;
import android.app.DownloadManager;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Build;
import android.widget.Toast;
import androidx.core.content.FileProvider;
import java.io.File;
import java.util.List;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.network.ServerRequest;
import pl.szczodrzynski.edziennik.sync.SyncWorker;
import pl.szczodrzynski.edziennik.utils.Utils;
import static android.content.Context.DOWNLOAD_SERVICE;
import static pl.szczodrzynski.edziennik.App.APP_URL;
import static pl.szczodrzynski.edziennik.App.UPDATES_ON_PLAY_STORE;
public class BootReceiver extends BroadcastReceiver {
private static final int NO_INTERNET_RETRY_TIMEOUT = 60;
private static boolean alarmSet = false;
public static long update_download_id;
public static String update_url;
public static String update_filename;
private static final String TAG = "receivers.BootReceiver";
private App app;
@Override
public void onReceive(final Context context, final Intent intent) {
app = (App)context.getApplicationContext();
if (intent.getBooleanExtra("ExecutedByAlarm", false))
{
alarmSet = false;
}
if (intent.getAction() != null && intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
{
long referenceId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
if(referenceId == update_download_id) {
Intent downloadIntent;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (!app.permissionChecker.canRequestApkInstall()) {
app.permissionChecker.requestApkInstall();
return;
}
}
File fileLocation = new File(app.getContext().getExternalFilesDir(null), update_filename);
Uri apkUri = FileProvider.getUriForFile(app.getContext(), app.getContext().getApplicationContext().getPackageName() + ".provider", fileLocation);
downloadIntent = new Intent(Intent.ACTION_VIEW);
downloadIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
downloadIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
downloadIntent.setDataAndType(apkUri, "application/vnd.android.package-archive");
List<ResolveInfo> resInfoList = app.getContext().getPackageManager().queryIntentActivities(downloadIntent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
app.getContext().grantUriPermission(packageName, apkUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
} else {
File fileLocation = new File(app.getContext().getExternalFilesDir(null), update_filename);
downloadIntent = new Intent(Intent.ACTION_VIEW);
downloadIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
downloadIntent.setDataAndType(Uri.fromFile(fileLocation), "application/vnd.android.package-archive");
}
app.getContext().startActivity(downloadIntent);
}
}
else
{
SyncWorker.Companion.scheduleNext(app, false);
if (app.networkUtils.isOnline())
{
checkUpdate(context, intent);
}
else
{
//Toast.makeText(context, "No internet, retrying in "+NO_INTERNET_RETRY_TIMEOUT+" seconds", Toast.LENGTH_SHORT).show();
scheduleUpdateCheck(context, NO_INTERNET_RETRY_TIMEOUT);
}
}
}
private boolean scheduleUpdateCheck(Context context, int secondsLater)
{
Intent alarmIntent = new Intent(context, BootReceiver.class);
alarmIntent.putExtra("ExecutedByAlarm", true);
PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(context, 234324243, alarmIntent, 0);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (alarmManager != null) {
alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (secondsLater * 1000), alarmPendingIntent);
return true;
}
return false;
}
private void checkUpdate(final Context context, final Intent intent)
{
if (!alarmSet) {
//Toast.makeText(context, "Update scheduled for 48 hours since now", Toast.LENGTH_SHORT).show();
alarmSet = scheduleUpdateCheck(context, 48 * 60 * 60);
}
//app.networkUtils.setSelfSignedSSL(app.getContext(), null);
new ServerRequest(app, app.requestScheme + APP_URL + "main.php?get_update", "BootReceiver/UPD")
.run(((e, result) -> {
if (result != null) {
if (result.get("update_available").getAsBoolean()) {
String updateVersion = result.get("update_version").getAsString();
String updateUrl = result.get("update_url").getAsString();
String updateFilename = result.get("update_filename").getAsString();
boolean updateMandatory = result.get("update_mandatory").getAsBoolean();
boolean updateDirect = result.get("update_direct").getAsBoolean();
if (app.appConfig.updateVersion == null || !app.appConfig.updateVersion.equals(updateVersion)) {
app.appConfig.updateVersion = updateVersion;
app.appConfig.updateUrl = updateUrl;
app.appConfig.updateFilename = updateFilename;
app.appConfig.updateMandatory = updateMandatory;
app.appConfig.updateDirect = updateDirect;
app.saveConfig();
}
if (!UPDATES_ON_PLAY_STORE || intent.getBooleanExtra("UserChecked", false)) {
app.notifier.notificationUpdatesShow(
updateVersion,
updateUrl,
updateFilename,
updateDirect);
}
} else {
if (app.appConfig.updateVersion == null || !app.appConfig.updateVersion.equals("")) {
app.appConfig.updateVersion = "";
app.appConfig.updateMandatory = false;
app.saveConfig();
}
app.notifier.notificationUpdatesHide();
if (intent.getBooleanExtra("UserChecked", false)) {
Toast.makeText(context, context.getString(R.string.notification_no_update), Toast.LENGTH_LONG).show();
}
}
}
else
{
//Toast.makeText(context, "Server returned nothing, retrying in "+NO_INTERNET_RETRY_TIMEOUT+" seconds", Toast.LENGTH_SHORT).show();
scheduleUpdateCheck(context, NO_INTERNET_RETRY_TIMEOUT);
}
}));
/*Ion.with(app.getContext())
.load(app.requestScheme + APP_URL + "main.php?get_update")
.setBodyParameter("username", (app.profile.autoRegistrationAllowed ? app.profile.registrationUsername : app.appConfig.deviceId))
.setBodyParameter("app_version_build_type", BuildConfig.BUILD_TYPE)
.setBodyParameter("app_version_code", Integer.toString(BuildConfig.VERSION_CODE))
.setBodyParameter("app_version", BuildConfig.VERSION_NAME + " " + BuildConfig.BUILD_TYPE + " (" + BuildConfig.VERSION_CODE + ")")
.setBodyParameter("device_id", Settings.Secure.getString(app.getContext().getContentResolver(), Settings.Secure.ANDROID_ID))
.setBodyParameter("device_model", Build.MANUFACTURER+" "+Build.MODEL)
.setBodyParameter("device_os_version", Build.VERSION.RELEASE)
.setBodyParameter("fcm_token", app.appConfig.fcmToken)
.asJsonObject()
.setCallback((e, result) -> {
// do stuff with the result or error
if (result != null) {
if (result.get("update_available").getAsBoolean()) {
String updateVersion = result.get("update_version").getAsString();
String updateUrl = result.get("update_url").getAsString();
String updateFilename = result.get("update_filename").getAsString();
if (app.appConfig.updateVersion == null || !app.appConfig.updateVersion.equals(updateVersion)) {
app.appConfig.updateVersion = updateVersion;
app.appConfig.updateUrl = updateUrl;
app.appConfig.updateFilename = updateFilename;
app.saveConfig();
}
app.notifier.notificationUpdatesShow(
updateVersion,
updateUrl,
updateFilename);
} else {
if (app.appConfig.updateVersion == null || !app.appConfig.updateVersion.equals("")) {
app.appConfig.updateVersion = "";
app.saveConfig();
}
app.notifier.notificationUpdatesHide();
if (intent.getBooleanExtra("UserChecked", false)) {
Toast.makeText(context, context.getString(R.string.notification_no_update), Toast.LENGTH_LONG).show();
}
}
}
else
{
//Toast.makeText(context, "Server returned nothing, retrying in "+NO_INTERNET_RETRY_TIMEOUT+" seconds", Toast.LENGTH_SHORT).show();
scheduleUpdateCheck(context, NO_INTERNET_RETRY_TIMEOUT);
}
});*/
}
private static DownloadManager downloadManager;
public static long downloadFile(Context context) {
Toast.makeText(context, R.string.downloading, Toast.LENGTH_SHORT).show();
File dir = new File(context.getExternalFilesDir(null)+""/*, update_filename*/);
/*if (existingFile.exists())
{
existingFile.delete();
}*/
if (dir.isDirectory())
{
String[] children = dir.list();
for (int i = 0; i < children.length; i++)
{
new File(dir, children[i]).delete();
}
}
Uri uri = Uri.parse(update_url);
long downloadReference;
// Create request for android download manager
downloadManager = (DownloadManager)context.getSystemService(DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager.Request(uri);
//Setting title of request
request.setTitle(context.getString(R.string.app_name));
//Setting description of request
request.setDescription(context.getString(R.string.notification_downloading_update));
//Set the local destination for the downloaded file to a path within the application's external files directory
try {
request.setDestinationInExternalFilesDir(context, null, update_filename);
}
catch (java.lang.IllegalStateException e)
{
e.printStackTrace();
Toast.makeText(context, "Failed to get external storage files directory", Toast.LENGTH_SHORT).show();
}
//Enqueue download and save into referenceId
downloadReference = downloadManager.enqueue(request);
return downloadReference;
}
public static class NotificationActionService extends IntentService {
private static final String TAG = "BootReceiver/NAS";
public NotificationActionService() {
super(NotificationActionService.class.getSimpleName());
//IntentFilter filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
//registerReceiver(downloadReceiver, filter);
}
@Override
protected void onHandleIntent(Intent intent) {
if (UPDATES_ON_PLAY_STORE && !intent.getBooleanExtra("update_direct", false)) {
Utils.openGooglePlay(this, "pl.szczodrzynski.edziennik");
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
App app = (App)getApplication();
if (!app.permissionChecker.canRequestApkInstall()) {
app.permissionChecker.requestApkInstall();
app.notifier.notificationUpdatesShow(
intent.getStringExtra("update_version"),
intent.getStringExtra("update_url"),
intent.getStringExtra("update_filename"),
intent.getBooleanExtra("update_direct", false));
return;
}
}
update_url = intent.getStringExtra("update_url");
update_filename = intent.getStringExtra("update_filename");
//update_filename = "Edziennik_update.apk";
update_download_id = downloadFile(this);
}
}
}

View File

@ -8,9 +8,9 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import pl.szczodrzynski.edziennik.data.api.ApiService import pl.szczodrzynski.edziennik.data.api.ApiService
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.events.requests.ServiceCloseRequest import pl.szczodrzynski.edziennik.data.api.events.requests.ServiceCloseRequest
import pl.szczodrzynski.edziennik.data.api.events.requests.TaskCancelRequest import pl.szczodrzynski.edziennik.data.api.events.requests.TaskCancelRequest
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask
class SzkolnyReceiver : BroadcastReceiver() { class SzkolnyReceiver : BroadcastReceiver() {
companion object { companion object {

View File

@ -2,13 +2,9 @@ package pl.szczodrzynski.edziennik.sync
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.os.AsyncTask
import androidx.work.* import androidx.work.*
import androidx.work.impl.WorkManagerImpl
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MINUTE import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask
import pl.szczodrzynski.edziennik.formatDate import pl.szczodrzynski.edziennik.formatDate
import pl.szczodrzynski.edziennik.utils.Utils.d import pl.szczodrzynski.edziennik.utils.Utils.d
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -22,37 +18,10 @@ class SyncWorker(val context: Context, val params: WorkerParameters) : Worker(co
*/ */
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")
fun scheduleNext(app: App, rescheduleIfFailedFound: Boolean = true) { fun scheduleNext(app: App, rescheduleIfFailedFound: Boolean = true) {
AsyncTask.execute { WorkerUtils.scheduleNext(app, rescheduleIfFailedFound) {
val workManager = WorkManager.getInstance(app) as WorkManagerImpl
val scheduledWork = workManager.workDatabase.workSpecDao().scheduledWork
scheduledWork.forEach {
d(TAG, "Work: ${it.id} at ${(it.periodStartTime+it.initialDelay).formatDate()}. State = ${it.state} (finished = ${it.state.isFinished})")
}
// remove finished work and other than SyncWorker
scheduledWork.removeAll { it.workerClassName != SyncWorker::class.java.canonicalName || it.isPeriodic || it.state.isFinished }
d(TAG, "Found ${scheduledWork.size} unfinished work")
// remove all enqueued work that had to (but didn't) run at some point in the past (at least 1min ago)
val failedWork = scheduledWork.filter { it.state == WorkInfo.State.ENQUEUED && it.periodStartTime+it.initialDelay < System.currentTimeMillis() - 1*MINUTE*1000 }
d(TAG, "${failedWork.size} work requests failed to start (out of ${scheduledWork.size} requests)")
if (rescheduleIfFailedFound) {
if (failedWork.isNotEmpty()) {
d(TAG, "App Manager detected!")
EventBus.getDefault().postSticky(AppManagerDetectedEvent(failedWork.map { it.periodStartTime + it.initialDelay }))
}
if (scheduledWork.size - failedWork.size < 1) {
d(TAG, "No pending work found, scheduling next:")
rescheduleNext(app) rescheduleNext(app)
} }
} }
else {
d(TAG, "NOT rescheduling: waiting to open the activity")
if (scheduledWork.size < 1) {
d(TAG, "No work found *at all*, scheduling next:")
rescheduleNext(app)
}
}
}
}
/** /**
* Cancel any existing sync jobs and schedule a new one. * Cancel any existing sync jobs and schedule a new one.

View File

@ -0,0 +1,120 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-1-18.
*/
package pl.szczodrzynski.edziennik.sync
import android.app.DownloadManager
import android.app.IntentService
import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.widget.Toast
import androidx.core.content.FileProvider
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.szkolny.response.Update
import pl.szczodrzynski.edziennik.utils.Utils
import java.io.File
class UpdateDownloaderService : IntentService(UpdateDownloaderService::class.java.simpleName) {
companion object {
private const val TAG = "UpdateDownloaderService"
private var downloadId = 0L
private var downloadFilename = ""
}
private fun tryUpdateWithGooglePlay(update: Update): Boolean {
if (!update.isOnGooglePlay)
return false
return try {
Utils.openGooglePlay(this, application.packageName)
true
}
catch (e: Exception) {
e.printStackTrace()
false
}
}
class DownloadProgressReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
if (intent?.action != DownloadManager.ACTION_DOWNLOAD_COMPLETE)
return
if (intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) != downloadId)
return
val app = context.applicationContext as App
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !app.permissionChecker.canRequestApkInstall()) {
app.permissionChecker.requestApkInstall()
return
}
val file = File(app.getExternalFilesDir(null), downloadFilename)
val installIntent = Intent(Intent.ACTION_VIEW)
installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
installIntent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val apkUri = FileProvider.getUriForFile(app, "${app.packageName}.provider", file)
installIntent.setDataAndType(apkUri, "application/vnd.android.package-archive")
val resInfoList = app.packageManager.queryIntentActivities(installIntent, PackageManager.MATCH_DEFAULT_ONLY)
for (resolveInfo in resInfoList) {
val packageName = resolveInfo.activityInfo.packageName
app.grantUriPermission(packageName, apkUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
}
app.startActivity(installIntent)
}
}
override fun onHandleIntent(intent: Intent?) {
val app = application as App
val update = App.config.update ?: return
if (tryUpdateWithGooglePlay(update))
return
if (update.downloadUrl == null) {
Toast.makeText(app, "Nie można pobrać tej aktualizacji. Pobierz ręcznie z Google Play.", Toast.LENGTH_LONG).show()
return
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !app.permissionChecker.canRequestApkInstall()) {
app.permissionChecker.requestApkInstall()
return
}
(app.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).cancel(app.notifications.updatesId)
val dir: File? = app.getExternalFilesDir(null)
if (dir?.isDirectory == true) {
dir.listFiles()?.forEach {
it.delete()
}
}
val uri = Uri.parse(update.downloadUrl)
downloadFilename = "${update.versionName}.apk"
val downloadManager = app.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
val request = DownloadManager.Request(uri)
request.setTitle(app.getString(R.string.app_name)+" "+update.versionName)
request.setDescription(app.getString(R.string.notification_downloading_update))
try {
request.setDestinationInExternalFilesDir(app, null, downloadFilename)
} catch (e: IllegalStateException) {
e.printStackTrace()
Toast.makeText(app, "Nie można znaleźć katalogu docelowego. Pobierz aktualizację ręcznie z Google Play.", Toast.LENGTH_LONG).show()
return
}
downloadId = downloadManager.enqueue(request)
}
}

View File

@ -0,0 +1,133 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-1-18.
*/
package pl.szczodrzynski.edziennik.sync
import android.annotation.SuppressLint
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.text.Html
import androidx.core.app.NotificationCompat
import androidx.work.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
import pl.szczodrzynski.edziennik.utils.Utils
import java.util.concurrent.TimeUnit
import kotlin.coroutines.CoroutineContext
class UpdateWorker(val context: Context, val params: WorkerParameters) : Worker(context, params), CoroutineScope {
companion object {
const val TAG = "UpdateWorker"
/**
* Schedule the sync job only if it's not already scheduled.
*/
@SuppressLint("RestrictedApi")
fun scheduleNext(app: App, rescheduleIfFailedFound: Boolean = true) {
WorkerUtils.scheduleNext(app, rescheduleIfFailedFound) {
rescheduleNext(app)
}
}
/**
* Cancel any existing sync jobs and schedule a new one.
*
* If [ConfigSync.enabled] is not true, just cancel every job.
*/
fun rescheduleNext(app: App) {
cancelNext(app)
if (!app.config.sync.notifyAboutUpdates) {
return
}
val syncInterval = 4 * DAY;
val syncAt = System.currentTimeMillis() + syncInterval*1000
Utils.d(TAG, "Scheduling work at ${syncAt.formatDate()}")
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
val syncWorkRequest = OneTimeWorkRequestBuilder<UpdateWorker>()
.setInitialDelay(syncInterval, TimeUnit.SECONDS)
.setConstraints(constraints)
.addTag(TAG)
.build()
WorkManager.getInstance(app).enqueue(syncWorkRequest)
}
/**
* Cancel any scheduled sync job.
*/
fun cancelNext(app: App) {
Utils.d(TAG, "Cancelling work by tag $TAG")
WorkManager.getInstance(app).cancelAllWorkByTag(TAG)
}
fun runNow(app: App) {
try {
val api = SzkolnyApi(app)
val response = api.getUpdate()
if (response?.success != true)
return
val updates = response.data
if (updates?.isNotEmpty() != true)
return
val update = updates[0]
app.config.update = update
val notificationIntent = Intent(app, UpdateDownloaderService::class.java)
val pendingIntent = PendingIntent.getService(app, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT)
val notification = NotificationCompat.Builder(app, app.notifications.updatesKey)
.setContentTitle(app.getString(R.string.notification_updates_title))
.setContentText(app.getString(R.string.notification_updates_text, update.versionName))
.setTicker(app.getString(R.string.notification_updates_summary))
.setSmallIcon(R.drawable.ic_notification)
.setStyle(NotificationCompat.BigTextStyle()
.bigText(listOf(
app.getString(R.string.notification_updates_text, update.versionName),
update.releaseNotes?.let { Html.fromHtml(it) }
).concat("\n")))
.setColor(0xff2196f3.toInt())
.setLights(0xFF00FFFF.toInt(), 2000, 2000)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setGroup(app.notifications.updatesKey)
.setContentIntent(pendingIntent)
.setAutoCancel(false)
.build()
(app.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).notify(app.notifications.updatesId, notification)
} catch (ignore: Exception) { }
}
}
private val job = Job()
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Default
override fun doWork(): Result {
Utils.d(TAG, "Running worker ID ${params.id}")
val app = context as App
if (!app.config.sync.notifyAboutUpdates) {
return Result.success()
}
launch {
runNow(app)
}
rescheduleNext(this.context)
return Result.success()
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-1-18.
*/
package pl.szczodrzynski.edziennik.sync
import android.annotation.SuppressLint
import android.os.AsyncTask
import androidx.work.WorkInfo
import androidx.work.WorkManager
import androidx.work.impl.WorkManagerImpl
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MINUTE
import pl.szczodrzynski.edziennik.formatDate
import pl.szczodrzynski.edziennik.utils.Utils
object WorkerUtils {
/**
* Schedule the sync job only if it's not already scheduled.
*/
@SuppressLint("RestrictedApi")
inline fun scheduleNext(app: App, rescheduleIfFailedFound: Boolean = true, crossinline onReschedule: () -> Unit) {
AsyncTask.execute {
val workManager = WorkManager.getInstance(app) as WorkManagerImpl
val scheduledWork = workManager.workDatabase.workSpecDao().scheduledWork
scheduledWork.forEach {
Utils.d("WorkerUtils", "Work: ${it.id} at ${(it.periodStartTime + it.initialDelay).formatDate()}. State = ${it.state} (finished = ${it.state.isFinished})")
}
// remove finished work and other than SyncWorker
scheduledWork.removeAll { it.workerClassName != SyncWorker::class.java.canonicalName || it.isPeriodic || it.state.isFinished }
Utils.d("WorkerUtils", "Found ${scheduledWork.size} unfinished work")
// remove all enqueued work that had to (but didn't) run at some point in the past (at least 1min ago)
val failedWork = scheduledWork.filter { it.state == WorkInfo.State.ENQUEUED && it.periodStartTime + it.initialDelay < System.currentTimeMillis() - 1 * MINUTE * 1000 }
Utils.d("WorkerUtils", "${failedWork.size} work requests failed to start (out of ${scheduledWork.size} requests)")
if (rescheduleIfFailedFound) {
if (failedWork.isNotEmpty()) {
Utils.d("WorkerUtils", "App Manager detected!")
EventBus.getDefault().postSticky(AppManagerDetectedEvent(failedWork.map { it.periodStartTime + it.initialDelay }))
}
if (scheduledWork.size - failedWork.size < 1) {
Utils.d("WorkerUtils", "No pending work found, scheduling next:")
onReschedule()
}
} else {
Utils.d("WorkerUtils", "NOT rescheduling: waiting to open the activity")
if (scheduledWork.size < 1) {
Utils.d("WorkerUtils", "No work found *at all*, scheduling next:")
onReschedule()
}
}
}
}
}

View File

@ -44,7 +44,7 @@ class ChangelogDialog(
val text = app.assets.open("pl-changelog.html").bufferedReader().use { val text = app.assets.open("pl-changelog.html").bufferedReader().use {
it.readText() it.readText()
} }.replace("<li>", "<br><li> - ")
textView.text = Html.fromHtml(text) textView.text = Html.fromHtml(text)
val scrollView = ScrollView(activity) val scrollView = ScrollView(activity)

View File

@ -21,13 +21,8 @@ import kotlinx.coroutines.*
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_AGENDA
import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi import pl.szczodrzynski.edziennik.data.api.szkolny.SzkolnyApi
import pl.szczodrzynski.edziennik.data.db.entity.Event import pl.szczodrzynski.edziennik.data.db.entity.*
import pl.szczodrzynski.edziennik.data.db.full.EventFull import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.entity.EventType
import pl.szczodrzynski.edziennik.data.db.entity.Metadata
import pl.szczodrzynski.edziennik.data.db.entity.Subject
import pl.szczodrzynski.edziennik.data.db.entity.Team
import pl.szczodrzynski.edziennik.data.db.entity.Lesson
import pl.szczodrzynski.edziennik.data.db.full.LessonFull import pl.szczodrzynski.edziennik.data.db.full.LessonFull
import pl.szczodrzynski.edziennik.databinding.DialogEventManualV2Binding import pl.szczodrzynski.edziennik.databinding.DialogEventManualV2Binding
import pl.szczodrzynski.edziennik.utils.Anim import pl.szczodrzynski.edziennik.utils.Anim
@ -43,7 +38,7 @@ class EventManualDialog(
val defaultLesson: LessonFull? = null, val defaultLesson: LessonFull? = null,
val defaultDate: Date? = null, val defaultDate: Date? = null,
val defaultTime: Time? = null, val defaultTime: Time? = null,
val defaultType: Int? = null, val defaultType: Long? = null,
val editingEvent: EventFull? = null, val editingEvent: EventFull? = null,
val onShowListener: ((tag: String) -> Unit)? = null, val onShowListener: ((tag: String) -> Unit)? = null,
val onDismissListener: ((tag: String) -> Unit)? = null val onDismissListener: ((tag: String) -> Unit)? = null
@ -149,7 +144,7 @@ class EventManualDialog(
else -> R.string.dialog_event_manual_share_first_notice else -> R.string.dialog_event_manual_share_first_notice
} }
b.shareDetails.setText(text) b.shareDetails.setText(text, event.sharedByName ?: "")
} }
private fun loadLists() { launch { private fun loadLists() { launch {
@ -588,8 +583,7 @@ class EventManualDialog(
startTime, startTime,
topic, topic,
customColor ?: -1, customColor ?: -1,
type?.toInt() type ?: Event.TYPE_DEFAULT,
?: Event.TYPE_DEFAULT,
true, true,
teacherId ?: -1, teacherId ?: -1,
subjectId ?: -1, subjectId ?: -1,
@ -598,7 +592,7 @@ class EventManualDialog(
val metadataObject = Metadata( val metadataObject = Metadata(
profileId, profileId,
when (type?.toInt()) { when (type) {
Event.TYPE_HOMEWORK -> Metadata.TYPE_HOMEWORK Event.TYPE_HOMEWORK -> Metadata.TYPE_HOMEWORK
else -> Metadata.TYPE_EVENT else -> Metadata.TYPE_EVENT
}, },

View File

@ -32,7 +32,7 @@ public class GradeDetailsDialog {
public GradeDetailsDialog(Context context) { public GradeDetailsDialog(Context context) {
this.context = context; this.context = context;
this.profileId = App.profileId; this.profileId = App.Companion.getProfileId();
} }
public GradeDetailsDialog(Context context, int profileId) { public GradeDetailsDialog(Context context, int profileId) {
this.context = context; this.context = context;
@ -74,7 +74,7 @@ public class GradeDetailsDialog {
b.setGrade(grade); b.setGrade(grade);
int gradeColor; int gradeColor;
if (App.getConfig().getFor(profileId).getGrades().getColorMode() == COLOR_MODE_DEFAULT) { if (App.Companion.getConfig().getFor(profileId).getGrades().getColorMode() == COLOR_MODE_DEFAULT) {
gradeColor = grade.color; gradeColor = grade.color;
} }
else { else {
@ -103,7 +103,7 @@ public class GradeDetailsDialog {
b.setCommentVisible(false); b.setCommentVisible(false);
b.setDevMode(App.devMode); b.setDevMode(App.Companion.getDevMode());
b.gradeName.setTextColor(ColorUtils.calculateLuminance(gradeColor) > 0.25 ? 0xff000000 : 0xffffffff); b.gradeName.setTextColor(ColorUtils.calculateLuminance(gradeColor) > 0.25 ? 0xff000000 : 0xffffffff);
b.gradeName.getBackground().setColorFilter(new PorterDuffColorFilter(gradeColor, PorterDuff.Mode.MULTIPLY)); b.gradeName.getBackground().setColorFilter(new PorterDuffColorFilter(gradeColor, PorterDuff.Mode.MULTIPLY));

View File

@ -19,7 +19,7 @@ public class LessonChangeDialog {
public LessonChangeDialog(Context context) { public LessonChangeDialog(Context context) {
this.context = context; this.context = context;
this.profileId = App.profileId; this.profileId = App.Companion.getProfileId();
} }
public LessonChangeDialog(Context context, int profileId) { public LessonChangeDialog(Context context, int profileId) {
this.context = context; this.context = context;

View File

@ -12,8 +12,6 @@ import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.DialogLessonDetailsBinding import pl.szczodrzynski.edziennik.databinding.DialogLessonDetailsBinding
import pl.szczodrzynski.edziennik.utils.models.Notification
import java.util.*
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
class ProfileRemoveDialog( class ProfileRemoveDialog(
@ -52,26 +50,35 @@ class ProfileRemoveDialog(
val profileObject = app.db.profileDao().getByIdNow(profileId) ?: return@async val profileObject = app.db.profileDao().getByIdNow(profileId) ?: return@async
app.db.announcementDao().clear(profileId) app.db.announcementDao().clear(profileId)
app.db.attendanceDao().clear(profileId) app.db.attendanceDao().clear(profileId)
app.db.eventDao().clear(profileId)
app.db.eventTypeDao().clear(profileId)
app.db.gradeDao().clear(profileId)
app.db.gradeCategoryDao().clear(profileId)
app.db.luckyNumberDao().clear(profileId)
app.db.noticeDao().clear(profileId)
app.db.subjectDao().clear(profileId)
app.db.teacherDao().clear(profileId)
app.db.teamDao().clear(profileId)
app.db.messageRecipientDao().clear(profileId)
app.db.messageDao().clear(profileId)
app.db.endpointTimerDao().clear(profileId)
app.db.attendanceTypeDao().clear(profileId) app.db.attendanceTypeDao().clear(profileId)
app.db.classroomDao().clear(profileId) app.db.classroomDao().clear(profileId)
app.db.configDao().clear(profileId)
app.db.endpointTimerDao().clear(profileId)
app.db.eventDao().clear(profileId)
app.db.eventTypeDao().clear(profileId)
app.db.gradeCategoryDao().clear(profileId)
app.db.gradeDao().clear(profileId)
app.db.lessonRangeDao().clear(profileId) app.db.lessonRangeDao().clear(profileId)
app.db.librusLessonDao().clear(profileId)
app.db.luckyNumberDao().clear(profileId)
app.db.messageDao().clear(profileId)
app.db.messageRecipientDao().clear(profileId)
app.db.noticeDao().clear(profileId)
app.db.noticeTypeDao().clear(profileId) app.db.noticeTypeDao().clear(profileId)
app.db.noticeTypeDao().clear(profileId)
app.db.notificationDao().clear(profileId)
app.db.subjectDao().clear(profileId)
app.db.teacherAbsenceDao().clear(profileId)
app.db.teacherAbsenceDao().clear(profileId) app.db.teacherAbsenceDao().clear(profileId)
app.db.teacherAbsenceTypeDao().clear(profileId) app.db.teacherAbsenceTypeDao().clear(profileId)
app.db.teacherDao().clear(profileId)
app.db.teamDao().clear(profileId)
app.db.timetableDao().clear(profileId) app.db.timetableDao().clear(profileId)
val homeCards = app.config.ui.homeCards.toMutableList()
homeCards.removeAll { it.profileId == profileId }
app.config.ui.homeCards = homeCards
val loginStoreId = profileObject.loginStoreId val loginStoreId = profileObject.loginStoreId
val profilesUsingLoginStore = app.db.profileDao().getIdsByLoginStoreIdNow(loginStoreId) val profilesUsingLoginStore = app.db.profileDao().getIdsByLoginStoreIdNow(loginStoreId)
if (profilesUsingLoginStore.size == 1) { if (profilesUsingLoginStore.size == 1) {
@ -80,19 +87,10 @@ class ProfileRemoveDialog(
app.db.profileDao().remove(profileId) app.db.profileDao().remove(profileId)
app.db.metadataDao().deleteAll(profileId) app.db.metadataDao().deleteAll(profileId)
val toRemove = ArrayList<Notification>() if (App.profileId == profileId) {
for (notification in app.appConfig.notifications) { app.profileLoadLast { }
if (notification.profileId == profileId) {
toRemove.add(notification)
} }
} }
app.appConfig.notifications.removeAll(toRemove)
app.profile = null
App.profileId = -1
app.profileLoadById(app.profileLastId())
}
deferred.await() deferred.await()
dialog.dismiss() dialog.dismiss()
activity.reloadTarget() activity.reloadTarget()

View File

@ -12,7 +12,7 @@ import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.databinding.DialogLessonDetailsBinding import pl.szczodrzynski.edziennik.databinding.DialogLessonDetailsBinding
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesFragment
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext

View File

@ -77,6 +77,9 @@ class LessonDetailsDialog(
) )
} }
if (App.devMode)
b.lessonId.visibility = View.VISIBLE
update() update()
}} }}

View File

@ -78,10 +78,10 @@ public class AgendaFragment extends Fragment {
return null; return null;
app = (App) activity.getApplication(); app = (App) activity.getApplication();
getContext().getTheme().applyStyle(Themes.INSTANCE.getAppTheme(), true); getContext().getTheme().applyStyle(Themes.INSTANCE.getAppTheme(), true);
if (app.profile == null) if (app.getProfile() == null)
return inflater.inflate(R.layout.fragment_loading, container, false); return inflater.inflate(R.layout.fragment_loading, container, false);
// activity, context and profile is valid // activity, context and profile is valid
viewType = app.config.getUi().getAgendaViewType(); viewType = app.getConfig().forProfile().getUi().getAgendaViewType();
if (viewType == AGENDA_DEFAULT) { if (viewType == AGENDA_DEFAULT) {
b_default = DataBindingUtil.inflate(inflater, R.layout.fragment_agenda_default, container, false); b_default = DataBindingUtil.inflate(inflater, R.layout.fragment_agenda_default, container, false);
return b_default.getRoot(); return b_default.getRoot();
@ -94,7 +94,7 @@ public class AgendaFragment extends Fragment {
@Override @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
if (app == null || app.profile == null || activity == null || (b_default == null && b_calendar == null) || !isAdded()) if (app == null || activity == null || b_default == null && b_calendar == null || !isAdded())
return; return;
activity.getBottomSheet().prependItems( activity.getBottomSheet().prependItems(
@ -106,7 +106,7 @@ public class AgendaFragment extends Fragment {
activity.getBottomSheet().close(); activity.getBottomSheet().close();
new EventManualDialog( new EventManualDialog(
activity, activity,
App.profileId, App.Companion.getProfileId(),
null, null,
actualDate, actualDate,
null, null,
@ -122,7 +122,7 @@ public class AgendaFragment extends Fragment {
.withOnClickListener(v3 -> { .withOnClickListener(v3 -> {
activity.getBottomSheet().close(); activity.getBottomSheet().close();
viewType = viewType == AGENDA_DEFAULT ? AGENDA_CALENDAR : AGENDA_DEFAULT; viewType = viewType == AGENDA_DEFAULT ? AGENDA_CALENDAR : AGENDA_DEFAULT;
app.config.getUi().setAgendaViewType(viewType); app.getConfig().forProfile().getUi().setAgendaViewType(viewType);
activity.reloadTarget(); activity.reloadTarget();
}), }),
new BottomSheetSeparatorItem(true), new BottomSheetSeparatorItem(true),
@ -131,7 +131,7 @@ public class AgendaFragment extends Fragment {
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline) .withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
.withOnClickListener(v3 -> { .withOnClickListener(v3 -> {
activity.getBottomSheet().close(); activity.getBottomSheet().close();
AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_EVENT, true)); AsyncTask.execute(() -> App.db.metadataDao().setAllSeen(App.Companion.getProfileId(), TYPE_EVENT, true));
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show(); Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show();
}) })
); );
@ -141,7 +141,7 @@ public class AgendaFragment extends Fragment {
activity.getNavView().bottomBar.setFabIcon(CommunityMaterial.Icon2.cmd_plus); activity.getNavView().bottomBar.setFabIcon(CommunityMaterial.Icon2.cmd_plus);
activity.getNavView().setFabOnClickListener(v -> new EventManualDialog( activity.getNavView().setFabOnClickListener(v -> new EventManualDialog(
activity, activity,
App.profileId, App.Companion.getProfileId(),
null, null,
actualDate, actualDate,
null, null,
@ -167,7 +167,7 @@ public class AgendaFragment extends Fragment {
final Handler handler = new Handler(); final Handler handler = new Handler();
handler.postDelayed(() -> AsyncTask.execute(() -> { handler.postDelayed(() -> AsyncTask.execute(() -> {
if (app == null || app.profile == null || activity == null || b_default == null || !isAdded()) if (app == null || activity == null || b_default == null || !isAdded())
return; return;
List<CalendarEvent> eventList = new ArrayList<>(); List<CalendarEvent> eventList = new ArrayList<>();
@ -193,8 +193,8 @@ public class AgendaFragment extends Fragment {
)); ));
} TODO: Implement new timetable lesson changes */ } TODO: Implement new timetable lesson changes */
if (app.profile.getStudentData("showTeacherAbsences", true)) { if (app.getProfile().getStudentData("showTeacherAbsences", true)) {
List<TeacherAbsenceFull> teacherAbsenceList = app.db.teacherAbsenceDao().getAllFullNow(App.profileId); List<TeacherAbsenceFull> teacherAbsenceList = App.db.teacherAbsenceDao().getAllFullNow(App.Companion.getProfileId());
List<TeacherAbsenceCounter> teacherAbsenceCounters = new ArrayList<>(); List<TeacherAbsenceCounter> teacherAbsenceCounters = new ArrayList<>();
for (TeacherAbsenceFull absence : teacherAbsenceList) { for (TeacherAbsenceFull absence : teacherAbsenceList) {
@ -226,7 +226,7 @@ public class AgendaFragment extends Fragment {
Colors.legibleTextColor(0xffff1744), Colors.legibleTextColor(0xffff1744),
startTime, startTime,
endTime, endTime,
App.profileId, App.Companion.getProfileId(),
date, date,
counter.getTeacherAbsenceCount() counter.getTeacherAbsenceCount()
)); ));
@ -234,7 +234,7 @@ public class AgendaFragment extends Fragment {
} }
List<EventFull> events = app.db.eventDao().getAllNow(App.profileId); List<EventFull> events = App.db.eventDao().getAllNow(App.Companion.getProfileId());
for (EventFull event : events) { for (EventFull event : events) {
Calendar startTime = Calendar.getInstance(); Calendar startTime = Calendar.getInstance();
Calendar endTime = Calendar.getInstance(); Calendar endTime = Calendar.getInstance();
@ -321,7 +321,7 @@ public class AgendaFragment extends Fragment {
actualDate = Date.fromCalendar(calendar); actualDate = Date.fromCalendar(calendar);
int scrolledDate = actualDate.getValue(); int scrolledDate = actualDate.getValue();
if (unreadEventDates.contains(scrolledDate)) { if (unreadEventDates.contains(scrolledDate)) {
AsyncTask.execute(() -> app.db.eventDao().setSeenByDate(App.profileId, Date.fromYmd(intToStr(scrolledDate)), true)); AsyncTask.execute(() -> App.db.eventDao().setSeenByDate(App.Companion.getProfileId(), Date.fromYmd(intToStr(scrolledDate)), true));
unreadEventDates.remove((Integer) scrolledDate); unreadEventDates.remove((Integer) scrolledDate);
} }
} }
@ -342,7 +342,7 @@ public class AgendaFragment extends Fragment {
// new EventListDialogOld(activity).show(app, Date.fromCalendar(calendarEvent.getInstanceDay())); // new EventListDialogOld(activity).show(app, Date.fromCalendar(calendarEvent.getInstanceDay()));
new DayDialog( new DayDialog(
activity, activity,
App.profileId, App.Companion.getProfileId(),
Date.fromCalendar(calendarEvent.getInstanceDay()), Date.fromCalendar(calendarEvent.getInstanceDay()),
null, null,
null null
@ -373,7 +373,7 @@ public class AgendaFragment extends Fragment {
final Handler handler = new Handler(); final Handler handler = new Handler();
handler.postDelayed(() -> AsyncTask.execute(() -> { handler.postDelayed(() -> AsyncTask.execute(() -> {
if (app == null || app.profile == null || activity == null || b_calendar == null || !isAdded()) if (app == null || activity == null || b_calendar == null || !isAdded())
return; return;
Context c = getContext(); Context c = getContext();
Activity a = getActivity(); Activity a = getActivity();
@ -385,7 +385,7 @@ public class AgendaFragment extends Fragment {
List<EventDay> eventList = new ArrayList<>(); List<EventDay> eventList = new ArrayList<>();
List<EventFull> events = app.db.eventDao().getAllNow(App.profileId); List<EventFull> events = App.db.eventDao().getAllNow(App.Companion.getProfileId());
for (EventFull event : events) { for (EventFull event : events) {
if (event.eventDate == null) if (event.eventDate == null)
continue; continue;
@ -434,13 +434,13 @@ public class AgendaFragment extends Fragment {
Date dayDate = Date.fromCalendar(eventDay.getCalendar()); Date dayDate = Date.fromCalendar(eventDay.getCalendar());
int scrolledDate = dayDate.getValue(); int scrolledDate = dayDate.getValue();
if (unreadEventDates.contains(scrolledDate)) { if (unreadEventDates.contains(scrolledDate)) {
AsyncTask.execute(() -> app.db.eventDao().setSeenByDate(App.profileId, Date.fromYmd(intToStr(scrolledDate)), true)); AsyncTask.execute(() -> App.db.eventDao().setSeenByDate(App.Companion.getProfileId(), Date.fromYmd(intToStr(scrolledDate)), true));
unreadEventDates.remove((Integer) scrolledDate); unreadEventDates.remove((Integer) scrolledDate);
} }
new DayDialog( new DayDialog(
activity, activity,
App.profileId, App.Companion.getProfileId(),
dayDate, dayDate,
null, null,
null null

View File

@ -24,8 +24,8 @@ import org.greenrobot.eventbus.ThreadMode;
import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.MainActivity; import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask;
import pl.szczodrzynski.edziennik.data.api.events.AnnouncementGetEvent; import pl.szczodrzynski.edziennik.data.api.events.AnnouncementGetEvent;
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask;
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull; import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull;
import pl.szczodrzynski.edziennik.databinding.DialogAnnouncementBinding; import pl.szczodrzynski.edziennik.databinding.DialogAnnouncementBinding;
import pl.szczodrzynski.edziennik.databinding.FragmentAnnouncementsBinding; import pl.szczodrzynski.edziennik.databinding.FragmentAnnouncementsBinding;
@ -49,8 +49,6 @@ public class AnnouncementsFragment extends Fragment {
return null; return null;
app = (App) activity.getApplication(); app = (App) activity.getApplication();
getContext().getTheme().applyStyle(Themes.INSTANCE.getAppTheme(), true); getContext().getTheme().applyStyle(Themes.INSTANCE.getAppTheme(), true);
if (app.profile == null)
return inflater.inflate(R.layout.fragment_loading, container, false);
// activity, context and profile is valid // activity, context and profile is valid
b = DataBindingUtil.inflate(inflater, R.layout.fragment_announcements, container, false); b = DataBindingUtil.inflate(inflater, R.layout.fragment_announcements, container, false);
b.refreshLayout.setParent(activity.getSwipeRefreshLayout()); b.refreshLayout.setParent(activity.getSwipeRefreshLayout());
@ -61,7 +59,7 @@ public class AnnouncementsFragment extends Fragment {
@Override @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
if (app == null || app.profile == null || activity == null || b == null || !isAdded()) if (app == null || activity == null || b == null || !isAdded())
return; return;
activity.getBottomSheet().prependItems( activity.getBottomSheet().prependItems(
@ -70,10 +68,10 @@ public class AnnouncementsFragment extends Fragment {
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline) .withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
.withOnClickListener(v3 -> { .withOnClickListener(v3 -> {
activity.getBottomSheet().close(); activity.getBottomSheet().close();
if (app.profile.getLoginStoreType() == LOGIN_TYPE_LIBRUS) { if (app.getProfile().getLoginStoreType() == LOGIN_TYPE_LIBRUS) {
EdziennikTask.Companion.announcementsRead(App.profileId).enqueue(requireContext()); EdziennikTask.Companion.announcementsRead(App.Companion.getProfileId()).enqueue(requireContext());
} else { } else {
AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_ANNOUNCEMENT, true)); AsyncTask.execute(() -> App.db.metadataDao().setAllSeen(App.Companion.getProfileId(), TYPE_ANNOUNCEMENT, true));
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show(); Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show();
} }
}) })
@ -92,8 +90,8 @@ public class AnnouncementsFragment extends Fragment {
recyclerView.setLayoutManager(linearLayoutManager); recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.addItemDecoration(new SimpleDividerItemDecoration(view.getContext())); recyclerView.addItemDecoration(new SimpleDividerItemDecoration(view.getContext()));
app.db.announcementDao().getAll(App.profileId).observe(this, announcements -> { app.db.announcementDao().getAll(App.Companion.getProfileId()).observe(this, announcements -> {
if (app == null || app.profile == null || activity == null || b == null || !isAdded()) if (app == null || activity == null || b == null || !isAdded())
return; return;
if (announcements == null) { if (announcements == null) {
@ -109,8 +107,8 @@ public class AnnouncementsFragment extends Fragment {
return; return;
}*/ }*/
AnnouncementsAdapter announcementsAdapter = new AnnouncementsAdapter(activity, announcements, (v, announcement) -> { AnnouncementsAdapter announcementsAdapter = new AnnouncementsAdapter(activity, announcements, (v, announcement) -> {
if (announcement.text == null || (app.profile.getLoginStoreType() == LOGIN_TYPE_LIBRUS && !announcement.seen && app.networkUtils.isOnline())) { if (announcement.text == null || (app.getProfile().getLoginStoreType() == LOGIN_TYPE_LIBRUS && !announcement.seen && app.getNetworkUtils().isOnline())) {
EdziennikTask.Companion.announcementGet(App.profileId, announcement).enqueue(requireContext()); EdziennikTask.Companion.announcementGet(App.Companion.getProfileId(), announcement).enqueue(requireContext());
} else { } else {
showAnnouncementDetailsDialog(announcement); showAnnouncementDetailsDialog(announcement);
} }
@ -157,9 +155,9 @@ public class AnnouncementsFragment extends Fragment {
.show(); .show();
DialogAnnouncementBinding b = DialogAnnouncementBinding.bind(dialog.getCustomView()); DialogAnnouncementBinding b = DialogAnnouncementBinding.bind(dialog.getCustomView());
b.text.setText(announcement.teacherFullName+"\n\n"+ (announcement.startDate != null ? announcement.startDate.getFormattedString() : "-") + (announcement.endDate != null ? " do " + announcement.endDate.getFormattedString() : "")+"\n\n" +announcement.text); b.text.setText(announcement.teacherFullName+"\n\n"+ (announcement.startDate != null ? announcement.startDate.getFormattedString() : "-") + (announcement.endDate != null ? " do " + announcement.endDate.getFormattedString() : "")+"\n\n" +announcement.text);
if (!announcement.seen && app.profile.getLoginStoreType() != LOGIN_TYPE_LIBRUS) { if (!announcement.seen && app.getProfile().getLoginStoreType() != LOGIN_TYPE_LIBRUS) {
announcement.seen = true; announcement.seen = true;
AsyncTask.execute(() -> app.db.metadataDao().setSeen(App.profileId, announcement, true)); AsyncTask.execute(() -> App.db.metadataDao().setSeen(App.Companion.getProfileId(), announcement, true));
if (recyclerView.getAdapter() != null) if (recyclerView.getAdapter() != null)
recyclerView.getAdapter().notifyDataSetChanged(); recyclerView.getAdapter().notifyDataSetChanged();
} }

View File

@ -97,7 +97,7 @@ public class AttendanceAdapter extends RecyclerView.Adapter<AttendanceAdapter.Vi
holder.attendanceLessonTopic.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY)); holder.attendanceLessonTopic.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
attendance.seen = true; attendance.seen = true;
AsyncTask.execute(() -> { AsyncTask.execute(() -> {
app.db.metadataDao().setSeen(App.profileId, attendance, true); App.db.metadataDao().setSeen(App.Companion.getProfileId(), attendance, true);
//Intent i = new Intent("android.intent.action.MAIN").putExtra(MainActivity.ACTION_UPDATE_BADGES, "yes, sure"); //Intent i = new Intent("android.intent.action.MAIN").putExtra(MainActivity.ACTION_UPDATE_BADGES, "yes, sure");
//context.sendBroadcast(i); //context.sendBroadcast(i);
}); });

View File

@ -33,8 +33,8 @@ import antonkozyriatskyi.circularprogressindicator.CircularProgressIndicator;
import pl.szczodrzynski.edziennik.App; import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.MainActivity; import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull;
import pl.szczodrzynski.edziennik.data.db.entity.Subject; import pl.szczodrzynski.edziennik.data.db.entity.Subject;
import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull;
import pl.szczodrzynski.edziennik.databinding.FragmentAttendanceBinding; import pl.szczodrzynski.edziennik.databinding.FragmentAttendanceBinding;
import pl.szczodrzynski.edziennik.utils.Themes; import pl.szczodrzynski.edziennik.utils.Themes;
import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem; import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem;
@ -73,8 +73,6 @@ public class AttendanceFragment extends Fragment {
return null; return null;
app = (App) activity.getApplication(); app = (App) activity.getApplication();
getContext().getTheme().applyStyle(Themes.INSTANCE.getAppTheme(), true); getContext().getTheme().applyStyle(Themes.INSTANCE.getAppTheme(), true);
if (app.profile == null)
return inflater.inflate(R.layout.fragment_loading, container, false);
// activity, context and profile is valid // activity, context and profile is valid
b = DataBindingUtil.inflate(inflater, R.layout.fragment_attendance, container, false); b = DataBindingUtil.inflate(inflater, R.layout.fragment_attendance, container, false);
b.refreshLayout.setParent(activity.getSwipeRefreshLayout()); b.refreshLayout.setParent(activity.getSwipeRefreshLayout());
@ -83,7 +81,7 @@ public class AttendanceFragment extends Fragment {
@Override @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
if (app == null || app.profile == null || activity == null || b == null || !isAdded()) if (app == null || activity == null || b == null || !isAdded())
return; return;
activity.getBottomSheet().prependItems( activity.getBottomSheet().prependItems(
@ -92,7 +90,7 @@ public class AttendanceFragment extends Fragment {
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline) .withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
.withOnClickListener(v3 -> { .withOnClickListener(v3 -> {
activity.getBottomSheet().close(); activity.getBottomSheet().close();
AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_ATTENDANCE, true)); AsyncTask.execute(() -> App.db.metadataDao().setAllSeen(App.Companion.getProfileId(), TYPE_ATTENDANCE, true));
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show(); Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show();
}) })
); );
@ -149,13 +147,13 @@ public class AttendanceFragment extends Fragment {
} }
}*/ }*/
if (app.profile.getLoginStoreType() == LOGIN_TYPE_MOBIDZIENNIK) { if (app.getProfile().getLoginStoreType() == LOGIN_TYPE_MOBIDZIENNIK) {
b.attendanceSummarySubject.setVisibility(View.GONE); b.attendanceSummarySubject.setVisibility(View.GONE);
} }
else { else {
b.attendanceSummarySubject.setOnClickListener((v -> { b.attendanceSummarySubject.setOnClickListener((v -> {
AsyncTask.execute(() -> { AsyncTask.execute(() -> {
List<Subject> subjectList = app.db.subjectDao().getAllNow(App.profileId); List<Subject> subjectList = App.db.subjectDao().getAllNow(App.Companion.getProfileId());
PopupMenu popupMenu = new PopupMenu(activity, b.attendanceSummarySubject, Gravity.END); PopupMenu popupMenu = new PopupMenu(activity, b.attendanceSummarySubject, Gravity.END);
popupMenu.getMenu().add(0, -1, 0, R.string.subject_filter_disabled); popupMenu.getMenu().add(0, -1, 0, R.string.subject_filter_disabled);
int index = 0; int index = 0;
@ -187,8 +185,8 @@ public class AttendanceFragment extends Fragment {
b.attendanceView.setHasFixedSize(true); b.attendanceView.setHasFixedSize(true);
b.attendanceView.setLayoutManager(linearLayoutManager); b.attendanceView.setLayoutManager(linearLayoutManager);
app.db.attendanceDao().getAll(App.profileId).observe(this, attendance -> { App.db.attendanceDao().getAll(App.Companion.getProfileId()).observe(this, attendance -> {
if (app == null || app.profile == null || activity == null || b == null || !isAdded()) if (app == null || activity == null || b == null || !isAdded())
return; return;
if (attendance == null) { if (attendance == null) {
@ -209,7 +207,7 @@ public class AttendanceFragment extends Fragment {
subjectTotalCount = new LongSparseArray<>(); subjectTotalCount = new LongSparseArray<>();
subjectAbsentCount = new LongSparseArray<>(); subjectAbsentCount = new LongSparseArray<>();
for (AttendanceFull attendance: attendanceList) { for (AttendanceFull attendance: attendanceList) {
if (app.profile.getLoginStoreType() == LOGIN_TYPE_VULCAN && attendance.type == TYPE_RELEASED) if (app.getProfile().getLoginStoreType() == LOGIN_TYPE_VULCAN && attendance.type == TYPE_RELEASED)
continue; continue;
int[] subjectTotal = subjectTotalCount.get(attendance.subjectId, new int[3]); int[] subjectTotal = subjectTotalCount.get(attendance.subjectId, new int[3]);
int[] subjectAbsent = subjectAbsentCount.get(attendance.subjectId, new int[3]); int[] subjectAbsent = subjectAbsentCount.get(attendance.subjectId, new int[3]);
@ -228,7 +226,7 @@ public class AttendanceFragment extends Fragment {
} }
private void updateList() { private void updateList() {
if (app == null || app.profile == null || activity == null || b == null || !isAdded()) if (app == null || activity == null || b == null || !isAdded())
return; return;
int presentCount = 0; int presentCount = 0;
@ -306,7 +304,7 @@ public class AttendanceFragment extends Fragment {
float attendancePercentage; float attendancePercentage;
// in Mobidziennik there are no TYPE_PRESENT records so we cannot calculate the percentage // in Mobidziennik there are no TYPE_PRESENT records so we cannot calculate the percentage
if (app.profile.getLoginStoreType() == LOGIN_TYPE_VULCAN) { if (app.getProfile().getLoginStoreType() == LOGIN_TYPE_VULCAN) {
float allCount = presentCount + absentCount + belatedCount; // do not count releases float allCount = presentCount + absentCount + belatedCount; // do not count releases
float present = allCount - absentCount; float present = allCount - absentCount;
attendancePercentage = present / allCount * 100.0f; attendancePercentage = present / allCount * 100.0f;

View File

@ -2,9 +2,9 @@ package pl.szczodrzynski.edziennik.ui.modules.base;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.R;
public class CrashGtfoActivity extends AppCompatActivity { public class CrashGtfoActivity extends AppCompatActivity {
@ -12,7 +12,7 @@ public class CrashGtfoActivity extends AppCompatActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setTheme((((App)getApplication()).getContext() setTheme((getApplication()
.getSharedPreferences(getString(R.string.preference_file_global), Context.MODE_PRIVATE) .getSharedPreferences(getString(R.string.preference_file_global), Context.MODE_PRIVATE)
.getBoolean("dark_theme", false) ? R.style.AppTheme_Dark : R.style.AppTheme)); .getBoolean("dark_theme", false) ? R.style.AppTheme_Dark : R.style.AppTheme));
setContentView(R.layout.activity_gtfo); setContentView(R.layout.activity_gtfo);

View File

@ -1,15 +1,15 @@
package pl.szczodrzynski.edziennik.ui.modules.base; package pl.szczodrzynski.edziennik.ui.modules.base;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.materialdialogs.MaterialDialog;
import com.google.android.material.textfield.TextInputEditText; import com.google.android.material.textfield.TextInputEditText;
import com.google.gson.Gson; import com.google.gson.Gson;
@ -100,7 +100,7 @@ public class DebugFragment extends Fragment {
forceType = "i"; forceType = "i";
parameter = parameter.substring(0, parameter.length()-1); parameter = parameter.substring(0, parameter.length()-1);
} }
SimpleObj obj = app.gson.fromJson("{\"value\":"+parameter+"}", SimpleObj.class); SimpleObj obj = app.getGson().fromJson("{\"value\":"+parameter+"}", SimpleObj.class);
Class type = obj.value.getClass(); Class type = obj.value.getClass();
Object value = obj.value; Object value = obj.value;
if ("d".equals(forceType)) { if ("d".equals(forceType)) {
@ -180,7 +180,7 @@ public class DebugFragment extends Fragment {
getByPath(app, target, path, true); getByPath(app, target, path, true);
// set the value if specified // set the value if specified
if (valueToSet != null) { if (valueToSet != null) {
targetField.set(target, app.gson.fromJson(valueToSet, targetField.getGenericType())); targetField.set(target, app.getGson().fromJson(valueToSet, targetField.getGenericType()));
targetField = null; targetField = null;
} }
if (targetField != null) { if (targetField != null) {
@ -194,7 +194,7 @@ public class DebugFragment extends Fragment {
return Log.getStackTraceString(e); return Log.getStackTraceString(e);
} }
} }
return app.gson.toJson(target); return app.getGson().toJson(target);
} }
@Override @Override
@ -218,13 +218,13 @@ public class DebugFragment extends Fragment {
getView().findViewById(R.id.debugAppconfig).setOnClickListener(v -> { getView().findViewById(R.id.debugAppconfig).setOnClickListener(v -> {
JsonRecyclerView mRecyclerView = getView().findViewById(R.id.rv_json); JsonRecyclerView mRecyclerView = getView().findViewById(R.id.rv_json);
// bind json // bind json
mRecyclerView.bindJson(new Gson().toJson(app.appConfig)); mRecyclerView.bindJson(new Gson().toJson(app.getConfig()));
mRecyclerView.setTextSize(20); mRecyclerView.setTextSize(20);
}); });
getView().findViewById(R.id.debugAppprofile).setOnClickListener(v -> { getView().findViewById(R.id.debugAppprofile).setOnClickListener(v -> {
JsonRecyclerView mRecyclerView = getView().findViewById(R.id.rv_json); JsonRecyclerView mRecyclerView = getView().findViewById(R.id.rv_json);
// bind json // bind json
mRecyclerView.bindJson(new Gson().toJson(app.profile)); mRecyclerView.bindJson(new Gson().toJson(app.getProfile()));
mRecyclerView.setTextSize(20); mRecyclerView.setTextSize(20);
}); });
} }

View File

@ -53,8 +53,6 @@ public class BehaviourFragment extends Fragment {
return null; return null;
app = (App) activity.getApplication(); app = (App) activity.getApplication();
getContext().getTheme().applyStyle(Themes.INSTANCE.getAppTheme(), true); getContext().getTheme().applyStyle(Themes.INSTANCE.getAppTheme(), true);
if (app.profile == null)
return inflater.inflate(R.layout.fragment_loading, container, false);
// activity, context and profile is valid // activity, context and profile is valid
b = DataBindingUtil.inflate(inflater, R.layout.fragment_behaviour, container, false); b = DataBindingUtil.inflate(inflater, R.layout.fragment_behaviour, container, false);
b.refreshLayout.setParent(activity.getSwipeRefreshLayout()); b.refreshLayout.setParent(activity.getSwipeRefreshLayout());
@ -63,7 +61,7 @@ public class BehaviourFragment extends Fragment {
@Override @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
if (app == null || app.profile == null || activity == null || b == null || !isAdded()) if (app == null || activity == null || b == null || !isAdded())
return; return;
activity.getBottomSheet().prependItems( activity.getBottomSheet().prependItems(
@ -72,7 +70,7 @@ public class BehaviourFragment extends Fragment {
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline) .withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
.withOnClickListener(v3 -> { .withOnClickListener(v3 -> {
activity.getBottomSheet().close(); activity.getBottomSheet().close();
AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_NOTICE, true)); AsyncTask.execute(() -> App.db.metadataDao().setAllSeen(App.Companion.getProfileId(), TYPE_NOTICE, true));
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show(); Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show();
}) })
); );
@ -99,8 +97,8 @@ public class BehaviourFragment extends Fragment {
b.noticesView.setHasFixedSize(true); b.noticesView.setHasFixedSize(true);
b.noticesView.setLayoutManager(linearLayoutManager); b.noticesView.setLayoutManager(linearLayoutManager);
app.db.noticeDao().getAll(App.profileId).observe(this, notices -> { app.db.noticeDao().getAll(App.Companion.getProfileId()).observe(this, notices -> {
if (app == null || app.profile == null || activity == null || b == null || !isAdded()) if (app == null || activity == null || b == null || !isAdded())
return; return;
if (notices == null) { if (notices == null) {

View File

@ -1,18 +1,5 @@
package pl.szczodrzynski.edziennik.ui.modules.feedback; package pl.szczodrzynski.edziennik.ui.modules.feedback;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.databinding.ActivityFeedbackBinding;
import pl.szczodrzynski.edziennik.data.db.entity.FeedbackMessage;
import pl.szczodrzynski.edziennik.data.db.full.FeedbackMessageWithCount;
import pl.szczodrzynski.edziennik.network.ServerRequest;
import pl.szczodrzynski.edziennik.utils.Anim;
import pl.szczodrzynski.edziennik.utils.Themes;
import pl.szczodrzynski.edziennik.utils.Utils;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -28,6 +15,9 @@ import android.view.animation.Animation;
import android.widget.PopupMenu; import android.widget.PopupMenu;
import android.widget.Toast; import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.materialdialogs.MaterialDialog;
import com.github.bassaer.chatmessageview.model.IChatUser; import com.github.bassaer.chatmessageview.model.IChatUser;
import com.github.bassaer.chatmessageview.model.Message; import com.github.bassaer.chatmessageview.model.Message;
@ -36,7 +26,16 @@ import com.github.bassaer.chatmessageview.view.ChatView;
import java.util.Calendar; import java.util.Calendar;
import java.util.List; import java.util.List;
import static pl.szczodrzynski.edziennik.App.APP_URL; import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.db.entity.FeedbackMessage;
import pl.szczodrzynski.edziennik.data.db.full.FeedbackMessageWithCount;
import pl.szczodrzynski.edziennik.databinding.ActivityFeedbackBinding;
import pl.szczodrzynski.edziennik.network.ServerRequest;
import pl.szczodrzynski.edziennik.utils.Anim;
import pl.szczodrzynski.edziennik.utils.Themes;
import pl.szczodrzynski.edziennik.utils.Utils;
import static pl.szczodrzynski.edziennik.utils.Utils.crc16; import static pl.szczodrzynski.edziennik.utils.Utils.crc16;
import static pl.szczodrzynski.edziennik.utils.Utils.openUrl; import static pl.szczodrzynski.edziennik.utils.Utils.openUrl;
@ -101,7 +100,7 @@ public class FeedbackActivity extends AppCompatActivity {
.content(R.string.sending_message) .content(R.string.sending_message)
.negativeText(R.string.cancel) .negativeText(R.string.cancel)
.show(); .show();
new ServerRequest(app, app.requestScheme + APP_URL + "main.php?feedback_message", "FeedbackSend") new ServerRequest(app, "https://edziennik.szczodrzynski.pl/app/main.php?feedback_message", "FeedbackSend")
.setBodyParameter("message_text", text) .setBodyParameter("message_text", text)
.setBodyParameter("target_device", deviceToSend == null ? "null" : deviceToSend) .setBodyParameter("target_device", deviceToSend == null ? "null" : deviceToSend)
.run(((e, result) -> { .run(((e, result) -> {
@ -199,7 +198,7 @@ public class FeedbackActivity extends AppCompatActivity {
receiver = new BroadcastReceiver() { receiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
FeedbackMessage message = app.gson.fromJson(intent.getStringExtra("message"), FeedbackMessage.class); FeedbackMessage message = app.getGson().fromJson(intent.getStringExtra("message"), FeedbackMessage.class);
Calendar c = Calendar.getInstance(); Calendar c = Calendar.getInstance();
c.setTimeInMillis(message.sentTime); c.setTimeInMillis(message.sentTime);
Message chatMessage = new Message.Builder() Message chatMessage = new Message.Builder()
@ -238,7 +237,7 @@ public class FeedbackActivity extends AppCompatActivity {
mChatView.setMessageMarginTop(5); mChatView.setMessageMarginTop(5);
mChatView.setMessageMarginBottom(5); mChatView.setMessageMarginBottom(5);
if (App.devMode && app.deviceId.equals("f054761fbdb6a238")) { if (App.Companion.getDevMode() && app.getDeviceId().equals("f054761fbdb6a238")) {
b.targetDeviceLayout.setVisibility(View.VISIBLE); b.targetDeviceLayout.setVisibility(View.VISIBLE);
b.targetDeviceDropDown.setOnClickListener((v -> { b.targetDeviceDropDown.setOnClickListener((v -> {
AsyncTask.execute(() -> { AsyncTask.execute(() -> {

View File

@ -21,11 +21,10 @@ import com.github.bassaer.chatmessageview.model.IChatUser
import com.github.bassaer.chatmessageview.model.Message import com.github.bassaer.chatmessageview.model.Message
import com.github.bassaer.chatmessageview.view.ChatView import com.github.bassaer.chatmessageview.view.ChatView
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.App.APP_URL
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.databinding.FragmentFeedbackBinding import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.FeedbackMessage import pl.szczodrzynski.edziennik.data.db.entity.FeedbackMessage
import pl.szczodrzynski.edziennik.databinding.FragmentFeedbackBinding
import pl.szczodrzynski.edziennik.network.ServerRequest import pl.szczodrzynski.edziennik.network.ServerRequest
import pl.szczodrzynski.edziennik.utils.Anim import pl.szczodrzynski.edziennik.utils.Anim
import pl.szczodrzynski.edziennik.utils.Themes import pl.szczodrzynski.edziennik.utils.Themes
@ -248,7 +247,7 @@ class FeedbackFragment : Fragment() {
.content(R.string.sending_message) .content(R.string.sending_message)
.negativeText(R.string.cancel) .negativeText(R.string.cancel)
.show() .show()
ServerRequest(app, app.requestScheme + APP_URL + "main.php?feedback_message", "FeedbackSend") ServerRequest(app, "https://edziennik.szczodrzynski.pl/app/main.php?feedback_message", "FeedbackSend")
.setBodyParameter("message_text", text) .setBodyParameter("message_text", text)
.setBodyParameter("target_device", if (deviceToSend == null) "null" else deviceToSend) .setBodyParameter("target_device", if (deviceToSend == null) "null" else deviceToSend)
.run { e, result -> .run { e, result ->

View File

@ -16,7 +16,6 @@ import androidx.fragment.app.Fragment;
import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.materialdialogs.MaterialDialog;
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial; import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial;
import java.text.DecimalFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -25,8 +24,8 @@ import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.config.ProfileConfigGrades; import pl.szczodrzynski.edziennik.config.ProfileConfigGrades;
import pl.szczodrzynski.edziennik.data.db.entity.Grade; import pl.szczodrzynski.edziennik.data.db.entity.Grade;
import pl.szczodrzynski.edziennik.data.db.full.GradeFull;
import pl.szczodrzynski.edziennik.data.db.entity.Subject; import pl.szczodrzynski.edziennik.data.db.entity.Subject;
import pl.szczodrzynski.edziennik.data.db.full.GradeFull;
import pl.szczodrzynski.edziennik.databinding.FragmentGradesBinding; import pl.szczodrzynski.edziennik.databinding.FragmentGradesBinding;
import pl.szczodrzynski.edziennik.utils.Themes; import pl.szczodrzynski.edziennik.utils.Themes;
import pl.szczodrzynski.edziennik.utils.models.ItemGradesSubjectModel; import pl.szczodrzynski.edziennik.utils.models.ItemGradesSubjectModel;
@ -56,8 +55,6 @@ public class GradesFragment extends Fragment {
return null; return null;
app = (App) activity.getApplication(); app = (App) activity.getApplication();
getContext().getTheme().applyStyle(Themes.INSTANCE.getAppTheme(), true); getContext().getTheme().applyStyle(Themes.INSTANCE.getAppTheme(), true);
if (app.profile == null)
return inflater.inflate(R.layout.fragment_loading, container, false);
// activity, context and profile is valid // activity, context and profile is valid
b = DataBindingUtil.inflate(inflater, R.layout.fragment_grades, container, false); b = DataBindingUtil.inflate(inflater, R.layout.fragment_grades, container, false);
b.refreshLayout.setParent(activity.getSwipeRefreshLayout()); b.refreshLayout.setParent(activity.getSwipeRefreshLayout());
@ -68,10 +65,8 @@ public class GradesFragment extends Fragment {
ListView listView; ListView listView;
List<ItemGradesSubjectModel> subjectList; List<ItemGradesSubjectModel> subjectList;
private boolean sortModeChanged = false;
private String getRegisterCardAverageModeSubText() { private String getRegisterCardAverageModeSubText() {
switch (App.getConfig().forProfile().getGrades().getYearAverageMode()) { switch (App.Companion.getConfig().forProfile().getGrades().getYearAverageMode()) {
default: default:
case YEAR_1_AVG_2_AVG: case YEAR_1_AVG_2_AVG:
return getString(R.string.settings_register_avg_mode_0_short); return getString(R.string.settings_register_avg_mode_0_short);
@ -88,7 +83,7 @@ public class GradesFragment extends Fragment {
@Override @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
if (app == null || app.profile == null || activity == null || b == null || !isAdded()) if (app == null || activity == null || b == null || !isAdded())
return; return;
/*activity.getBottomSheet().setToggleGroupEnabled(true); /*activity.getBottomSheet().setToggleGroupEnabled(true);
@ -136,7 +131,7 @@ public class GradesFragment extends Fragment {
.withIcon(CommunityMaterial.Icon2.cmd_palette_outline) .withIcon(CommunityMaterial.Icon2.cmd_palette_outline)
.withOnClickListener(v3 -> { .withOnClickListener(v3 -> {
activity.getBottomSheet().close(); activity.getBottomSheet().close();
ProfileConfigGrades config = app.config.getFor(App.profileId).getGrades(); ProfileConfigGrades config = app.getConfig().getFor(App.Companion.getProfileId()).getGrades();
new MaterialDialog.Builder(activity) new MaterialDialog.Builder(activity)
.title(R.string.dialog_grades_color_mode_title) .title(R.string.dialog_grades_color_mode_title)
.items(R.array.dialog_grades_color_modes) .items(R.array.dialog_grades_color_modes)
@ -155,8 +150,8 @@ public class GradesFragment extends Fragment {
new MaterialDialog.Builder(activity) new MaterialDialog.Builder(activity)
.title(R.string.dialog_grades_sort_title) .title(R.string.dialog_grades_sort_title)
.items(R.array.dialog_grades_sort_modes) .items(R.array.dialog_grades_sort_modes)
.itemsCallbackSingleChoice(app.config.getGrades().getOrderBy(), (dialog, view1, which, text) -> { .itemsCallbackSingleChoice(app.getConfig().getGrades().getOrderBy(), (dialog, view1, which, text) -> {
app.config.getGrades().setOrderBy(which); app.getConfig().getGrades().setOrderBy(which);
activity.reloadTarget(); activity.reloadTarget();
return true; return true;
}) })
@ -184,8 +179,8 @@ public class GradesFragment extends Fragment {
.title(getString(R.string.settings_register_avg_mode_dialog_title)) .title(getString(R.string.settings_register_avg_mode_dialog_title))
.content(getString(R.string.settings_register_avg_mode_dialog_text)) .content(getString(R.string.settings_register_avg_mode_dialog_text))
.items(modeNames) .items(modeNames)
.itemsCallbackSingleChoice(modeIds.indexOf(App.getConfig().forProfile().getGrades().getYearAverageMode()), (dialog, itemView, which, text) -> { .itemsCallbackSingleChoice(modeIds.indexOf(App.Companion.getConfig().forProfile().getGrades().getYearAverageMode()), (dialog, itemView, which, text) -> {
App.getConfig().forProfile().getGrades().setYearAverageMode(modeIds.get(which)); App.Companion.getConfig().forProfile().getGrades().setYearAverageMode(modeIds.get(which));
activity.reloadTarget(); activity.reloadTarget();
return true; return true;
}) })
@ -197,7 +192,7 @@ public class GradesFragment extends Fragment {
.withIcon(CommunityMaterial.Icon.cmd_eye_check_outline) .withIcon(CommunityMaterial.Icon.cmd_eye_check_outline)
.withOnClickListener(v3 -> { .withOnClickListener(v3 -> {
activity.getBottomSheet().close(); activity.getBottomSheet().close();
AsyncTask.execute(() -> app.db.metadataDao().setAllSeen(App.profileId, TYPE_GRADE, true)); AsyncTask.execute(() -> App.db.metadataDao().setAllSeen(App.Companion.getProfileId(), TYPE_GRADE, true));
Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show(); Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show();
}) })
); );
@ -227,41 +222,41 @@ public class GradesFragment extends Fragment {
long finalExpandSubjectId = expandSubjectId; long finalExpandSubjectId = expandSubjectId;
String orderBy; String orderBy;
if (app.config.getGrades().getOrderBy() == ORDER_BY_SUBJECT_ASC) { if (app.getConfig().getGrades().getOrderBy() == ORDER_BY_SUBJECT_ASC) {
orderBy = "subjectLongName ASC, addedDate DESC"; orderBy = "subjectLongName ASC, addedDate DESC";
} }
else if (app.config.getGrades().getOrderBy() == ORDER_BY_DATE_DESC) { else if (app.getConfig().getGrades().getOrderBy() == ORDER_BY_DATE_DESC) {
orderBy = "addedDate DESC"; orderBy = "addedDate DESC";
} }
else if (app.config.getGrades().getOrderBy() == ORDER_BY_DATE_ASC) { else if (app.getConfig().getGrades().getOrderBy() == ORDER_BY_DATE_ASC) {
orderBy = "addedDate ASC"; orderBy = "addedDate ASC";
} }
else { else {
orderBy = "subjectLongName DESC, addedDate DESC"; orderBy = "subjectLongName DESC, addedDate DESC";
} }
app.db.gradeDao().getAllOrderBy(App.profileId, orderBy).observe(this, grades -> { App.db.gradeDao().getAllOrderBy(App.Companion.getProfileId(), orderBy).observe(this, grades -> {
if (app == null || app.profile == null || activity == null || b == null || !isAdded()) if (app == null || activity == null || b == null || !isAdded())
return; return;
subjectList = new ArrayList<>(); subjectList = new ArrayList<>();
ProfileConfigGrades config = app.config.getFor(App.profileId).getGrades(); ProfileConfigGrades config = app.getConfig().getFor(App.Companion.getProfileId()).getGrades();
// now we have all grades from the newest to the oldest // now we have all grades from the newest to the oldest
for (GradeFull grade: grades) { for (GradeFull grade: grades) {
ItemGradesSubjectModel model = ItemGradesSubjectModel.searchModelBySubjectId(subjectList, grade.subjectId); ItemGradesSubjectModel model = ItemGradesSubjectModel.searchModelBySubjectId(subjectList, grade.subjectId);
if (model == null) { if (model == null) {
model = new ItemGradesSubjectModel(app.profile, model = new ItemGradesSubjectModel(app.getProfile(),
new Subject(App.profileId, grade.subjectId, grade.subjectLongName, grade.subjectShortName), new Subject(App.Companion.getProfileId(), grade.subjectId, grade.subjectLongName, grade.subjectShortName),
new ArrayList<>(), new ArrayList<>(),
new ArrayList<>()); new ArrayList<>());
subjectList.add(model); subjectList.add(model);
if (model.subject != null && model.subject.id == finalExpandSubjectId) { if (model.subject != null && model.subject.id == finalExpandSubjectId) {
model.expandView = true; model.expandView = true;
} }
model.colorMode = App.getConfig().forProfile().getGrades().getColorMode(); model.colorMode = App.Companion.getConfig().forProfile().getGrades().getColorMode();
model.yearAverageMode = App.getConfig().forProfile().getGrades().getYearAverageMode(); model.yearAverageMode = App.Companion.getConfig().forProfile().getGrades().getYearAverageMode();
} }
if (!grade.seen && grade.semester == 1) { if (!grade.seen && grade.semester == 1) {
model.semester1Unread++; model.semester1Unread++;
@ -380,7 +375,7 @@ public class GradesFragment extends Fragment {
else if (!model.isBehaviourSubject && model.isNormalSubject) { else if (!model.isBehaviourSubject && model.isNormalSubject) {
// applies for normal grades & normal+descriptive grades // applies for normal grades & normal+descriptive grades
// calculate the normal grade average based on the user's setting // calculate the normal grade average based on the user's setting
switch (App.getConfig().forProfile().getGrades().getYearAverageMode()) { switch (App.Companion.getConfig().forProfile().getGrades().getYearAverageMode()) {
case YEAR_1_AVG_2_AVG: case YEAR_1_AVG_2_AVG:
model.yearAverage = (model.semester1Average + model.semester2Average) / 2; model.yearAverage = (model.semester1Average + model.semester2Average) / 2;
break; break;
@ -433,7 +428,7 @@ public class GradesFragment extends Fragment {
} }
public void showAverages() { public void showAverages() {
if (app == null || app.profile == null || activity == null || b == null || !isAdded() || subjectList == null) if (app == null || activity == null || b == null || !isAdded() || subjectList == null)
return; return;
float semester1Sum = 0; float semester1Sum = 0;
@ -529,8 +524,6 @@ public class GradesFragment extends Fragment {
} }
} }
DecimalFormat df = new DecimalFormat("0.00");
String semester1ExpectedAverageStr = semester1Count > semester1ProposedCount || semester1Count > semester1FinalCount ? getString(R.string.dialog_averages_expected_format, 1, semester1Sum / semester1Count) : ""; String semester1ExpectedAverageStr = semester1Count > semester1ProposedCount || semester1Count > semester1FinalCount ? getString(R.string.dialog_averages_expected_format, 1, semester1Sum / semester1Count) : "";
String semester1ProposedAverageStr = semester1ProposedCount > 0 ? getString(R.string.dialog_averages_proposed_format, 1, semester1ProposedSum / semester1ProposedCount) : ""; String semester1ProposedAverageStr = semester1ProposedCount > 0 ? getString(R.string.dialog_averages_proposed_format, 1, semester1ProposedSum / semester1ProposedCount) : "";
String semester1FinalAverageStr = semester1FinalCount > 0 ? getString(R.string.dialog_averages_final_format, 1, semester1FinalSum / semester1FinalCount) : ""; String semester1FinalAverageStr = semester1FinalCount > 0 ? getString(R.string.dialog_averages_final_format, 1, semester1FinalSum / semester1FinalCount) : "";

View File

@ -52,11 +52,11 @@ public class GradesListAdapter extends RecyclerView.Adapter<GradesListAdapter.Vi
GradeFull grade = gradeList.get(position); GradeFull grade = gradeList.get(position);
holder.root.setOnClickListener((v -> { holder.root.setOnClickListener((v -> {
new GradeDetailsDialog(v.getContext(), App.profileId).show(app, grade); new GradeDetailsDialog(v.getContext(), App.Companion.getProfileId()).show(app, grade);
})); }));
int gradeColor; int gradeColor;
if (App.getConfig().forProfile().getGrades().getColorMode() == COLOR_MODE_DEFAULT) { if (App.Companion.getConfig().forProfile().getGrades().getColorMode() == COLOR_MODE_DEFAULT) {
gradeColor = grade.color; gradeColor = grade.color;
} }
else { else {

View File

@ -31,8 +31,8 @@ import pl.szczodrzynski.edziennik.App;
import pl.szczodrzynski.edziennik.MainActivity; import pl.szczodrzynski.edziennik.MainActivity;
import pl.szczodrzynski.edziennik.R; import pl.szczodrzynski.edziennik.R;
import pl.szczodrzynski.edziennik.data.db.AppDb; import pl.szczodrzynski.edziennik.data.db.AppDb;
import pl.szczodrzynski.edziennik.data.db.full.GradeFull;
import pl.szczodrzynski.edziennik.data.db.entity.Subject; import pl.szczodrzynski.edziennik.data.db.entity.Subject;
import pl.szczodrzynski.edziennik.data.db.full.GradeFull;
import pl.szczodrzynski.edziennik.utils.Anim; import pl.szczodrzynski.edziennik.utils.Anim;
import pl.szczodrzynski.edziennik.utils.Colors; import pl.szczodrzynski.edziennik.utils.Colors;
import pl.szczodrzynski.edziennik.utils.Utils; import pl.szczodrzynski.edziennik.utils.Utils;
@ -86,34 +86,34 @@ public class GradesSubjectAdapter extends ArrayAdapter<ItemGradesSubjectModel> i
private boolean gradesSetAsRead(ViewHolder holder, ItemGradesSubjectModel model, int semester) { private boolean gradesSetAsRead(ViewHolder holder, ItemGradesSubjectModel model, int semester) {
boolean somethingChanged = false; boolean somethingChanged = false;
AppDb db = AppDb.getDatabase(null); AppDb db = App.Companion.getDb();
if (semester == 1) { if (semester == 1) {
model.semester1Unread = 0; model.semester1Unread = 0;
for (GradeFull grade : model.grades1) { for (GradeFull grade : model.grades1) {
if (!grade.seen) { if (!grade.seen) {
db.metadataDao().setSeen(App.profileId, grade, somethingChanged = true); db.metadataDao().setSeen(App.Companion.getProfileId(), grade, somethingChanged = true);
} }
} }
if (model.semester1Proposed != null && !model.semester1Proposed.seen) if (model.semester1Proposed != null && !model.semester1Proposed.seen)
db.metadataDao().setSeen(App.profileId, model.semester1Proposed, somethingChanged = true); db.metadataDao().setSeen(App.Companion.getProfileId(), model.semester1Proposed, somethingChanged = true);
if (model.semester1Final != null && !model.semester1Final.seen) if (model.semester1Final != null && !model.semester1Final.seen)
db.metadataDao().setSeen(App.profileId, model.semester1Final, somethingChanged = true); db.metadataDao().setSeen(App.Companion.getProfileId(), model.semester1Final, somethingChanged = true);
} }
else if (semester == 2) { else if (semester == 2) {
model.semester2Unread = 0; model.semester2Unread = 0;
for (GradeFull grade : model.grades2) { for (GradeFull grade : model.grades2) {
if (!grade.seen) { if (!grade.seen) {
db.metadataDao().setSeen(App.profileId, grade, somethingChanged = true); db.metadataDao().setSeen(App.Companion.getProfileId(), grade, somethingChanged = true);
} }
} }
if (model.semester2Proposed != null && !model.semester2Proposed.seen) if (model.semester2Proposed != null && !model.semester2Proposed.seen)
db.metadataDao().setSeen(App.profileId, model.semester2Proposed, somethingChanged = true); db.metadataDao().setSeen(App.Companion.getProfileId(), model.semester2Proposed, somethingChanged = true);
if (model.semester2Final != null && !model.semester2Final.seen) if (model.semester2Final != null && !model.semester2Final.seen)
db.metadataDao().setSeen(App.profileId, model.semester2Final, somethingChanged = true); db.metadataDao().setSeen(App.Companion.getProfileId(), model.semester2Final, somethingChanged = true);
if (model.yearProposed != null && !model.yearProposed.seen) if (model.yearProposed != null && !model.yearProposed.seen)
db.metadataDao().setSeen(App.profileId, model.yearProposed, somethingChanged = true); db.metadataDao().setSeen(App.Companion.getProfileId(), model.yearProposed, somethingChanged = true);
if (model.yearFinal != null && !model.yearFinal.seen) if (model.yearFinal != null && !model.yearFinal.seen)
db.metadataDao().setSeen(App.profileId, model.yearFinal, somethingChanged = true); db.metadataDao().setSeen(App.Companion.getProfileId(), model.yearFinal, somethingChanged = true);
} }
if (somethingChanged) updateSubjectSemesterBadges(holder, model); if (somethingChanged) updateSubjectSemesterBadges(holder, model);
return somethingChanged; return somethingChanged;
@ -219,7 +219,7 @@ public class GradesSubjectAdapter extends ArrayAdapter<ItemGradesSubjectModel> i
layoutParams.setMargins(0, 0, _5dp, 0); layoutParams.setMargins(0, 0, _5dp, 0);
DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics(); DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
int maxWidthPx = displayMetrics.widthPixels - Utils.dpToPx((app.config.getUi().getMiniMenuVisible() ? 72 : 0)/*miniDrawer size*/ + 8 + 8/*left and right offsets*/ + 24/*ellipsize width*/); int maxWidthPx = displayMetrics.widthPixels - Utils.dpToPx((app.getConfig().getUi().getMiniMenuVisible() ? 72 : 0)/*miniDrawer size*/ + 8 + 8/*left and right offsets*/ + 24/*ellipsize width*/);
int totalWidthPx = 0; int totalWidthPx = 0;
boolean ellipsized = false; boolean ellipsized = false;

View File

@ -40,11 +40,11 @@ class HomeFragment : Fragment(), CoroutineScope {
private const val TAG = "HomeFragment" private const val TAG = "HomeFragment"
fun swapCards(fromPosition: Int, toPosition: Int, cardAdapter: HomeCardAdapter) { fun swapCards(fromPosition: Int, toPosition: Int, cardAdapter: HomeCardAdapter) {
val homeCards = App.getConfig().ui.homeCards.toMutableList() val homeCards = App.config.ui.homeCards.toMutableList()
val fromPair = homeCards[fromPosition] val fromPair = homeCards[fromPosition]
homeCards[fromPosition] = homeCards[toPosition] homeCards[fromPosition] = homeCards[toPosition]
homeCards[toPosition] = fromPair homeCards[toPosition] = fromPair
App.getConfig().ui.homeCards = homeCards App.config.ui.homeCards = homeCards
val fromCard = cardAdapter.items[fromPosition] val fromCard = cardAdapter.items[fromPosition]
cardAdapter.items[fromPosition] = cardAdapter.items[toPosition] cardAdapter.items[fromPosition] = cardAdapter.items[toPosition]

View File

@ -20,7 +20,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.databinding.CardHomeDebugBinding import pl.szczodrzynski.edziennik.databinding.CardHomeDebugBinding
import pl.szczodrzynski.edziennik.dp import pl.szczodrzynski.edziennik.dp

View File

@ -29,9 +29,9 @@ import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.MainActivity import pl.szczodrzynski.edziennik.MainActivity
import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.data.db.entity.Grade.* import pl.szczodrzynski.edziennik.data.db.entity.Grade.*
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Subject import pl.szczodrzynski.edziennik.data.db.entity.Subject
import pl.szczodrzynski.edziennik.data.db.full.GradeFull
import pl.szczodrzynski.edziennik.databinding.CardHomeGradesBinding import pl.szczodrzynski.edziennik.databinding.CardHomeGradesBinding
import pl.szczodrzynski.edziennik.dp import pl.szczodrzynski.edziennik.dp
import pl.szczodrzynski.edziennik.ui.modules.home.HomeCard import pl.szczodrzynski.edziennik.ui.modules.home.HomeCard
@ -121,7 +121,7 @@ class HomeGradesCard(
16 /*ellipsize width*/)) / 1.5f 16 /*ellipsize width*/)) / 1.5f
subject.grades1.onEach { grade -> subject.grades1.onEach { grade ->
val gradeColor = when (App.getConfig().forProfile().grades.colorMode) { val gradeColor = when (App.config.forProfile().grades.colorMode) {
Profile.COLOR_MODE_DEFAULT -> grade.color Profile.COLOR_MODE_DEFAULT -> grade.color
else -> Colors.gradeToColor(grade) else -> Colors.gradeToColor(grade)
} }

View File

@ -94,7 +94,7 @@ class HomeLuckyNumberCard(
holder.root.onClick { holder.root.onClick {
StudentNumberDialog(activity, profile, onDismissListener = { StudentNumberDialog(activity, profile, onDismissListener = {
app.profileSaveAsync(profile) app.profileSave(profile)
val newSubTextRes = if (profile.studentNumber == -1) val newSubTextRes = if (profile.studentNumber == -1)
R.string.home_lucky_number_details_click_to_set R.string.home_lucky_number_details_click_to_set
else else

View File

@ -22,11 +22,11 @@ import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.*
import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask
import pl.szczodrzynski.edziennik.data.api.events.ApiTaskAllFinishedEvent import pl.szczodrzynski.edziennik.data.api.events.ApiTaskAllFinishedEvent
import pl.szczodrzynski.edziennik.data.api.task.EdziennikTask
import pl.szczodrzynski.edziennik.data.db.entity.Event import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Lesson import pl.szczodrzynski.edziennik.data.db.entity.Lesson
import pl.szczodrzynski.edziennik.data.db.entity.Profile
import pl.szczodrzynski.edziennik.data.db.full.LessonFull import pl.szczodrzynski.edziennik.data.db.full.LessonFull
import pl.szczodrzynski.edziennik.databinding.CardHomeTimetableBinding import pl.szczodrzynski.edziennik.databinding.CardHomeTimetableBinding
import pl.szczodrzynski.edziennik.ui.dialogs.bell.BellSyncTimeChooseDialog import pl.szczodrzynski.edziennik.ui.dialogs.bell.BellSyncTimeChooseDialog

View File

@ -85,7 +85,7 @@ public class HomeworkAdapter extends RecyclerView.Adapter<HomeworkAdapter.ViewHo
holder.homeworkItemTopic.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY)); holder.homeworkItemTopic.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY));
homework.seen = true; homework.seen = true;
AsyncTask.execute(() -> { AsyncTask.execute(() -> {
app.db.metadataDao().setSeen(App.profileId, homework, true); App.db.metadataDao().setSeen(App.Companion.getProfileId(), homework, true);
}); });
} }
else { else {

View File

@ -78,6 +78,6 @@ public class ChangelogIntroActivity extends IntroActivity {
.build()); .build());
}*/ }*/
app.config.setAppVersion(BuildConfig.VERSION_CODE); app.getConfig().setAppVersion(BuildConfig.VERSION_CODE);
} }
} }

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