diff --git a/app-android/app/build.gradle b/app-android/app/build.gradle index ad269708220d..83657f74964b 100644 --- a/app-android/app/build.gradle +++ b/app-android/app/build.gradle @@ -20,6 +20,7 @@ android { // https://issuetracker.google.com/issues/181593646 ksp { arg("room.schemaLocation", "$projectDir/schemas".toString()) + arg("room.generateKotlin", "true") } externalNativeBuild { cmake { @@ -138,16 +139,14 @@ dependencies { implementation "de.tutao:tutasdk" - // Important: cannot be updated without additional measures as Android 6 and 7 do not have Java 9 - //noinspection GradleDependency implementation 'commons-io:commons-io:2.16.1' implementation 'androidx.core:core-ktx:1.13.1' implementation "androidx.activity:activity-ktx:$activity_version" - implementation "androidx.browser:browser:1.8.0" + implementation "androidx.browser:browser:1.8.0" implementation "androidx.biometric:biometric:1.1.0" - implementation "androidx.core:core-splashscreen:1.0.1" - implementation "androidx.datastore:datastore-preferences:1.1.1" + implementation "androidx.core:core-splashscreen:1.0.1" + implementation "androidx.datastore:datastore-preferences:1.1.1" if (file("../libs/android-database-sqlcipher-4.5.0.aar").exists()) { implementation fileTree(include: ['*.aar'], dir: '../libs') @@ -156,7 +155,7 @@ dependencies { } implementation 'androidx.sqlite:sqlite-ktx:2.4.0' - implementation "androidx.room:room-runtime:$room_version" + implementation "androidx.room:room-ktx:$room_version" ksp "androidx.room:room-compiler:$room_version" if (file("../libs/android-database-sqlcipher-4.5.0.aar").exists()) { @@ -167,11 +166,10 @@ dependencies { implementation 'androidx.sqlite:sqlite-ktx:2.4.0' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.3' - implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1' implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" // TLS1.3 backwards compatibility for Android < 10 implementation 'org.conscrypt:conscrypt-android:2.5.2' @@ -184,8 +182,8 @@ dependencies { testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.13' testImplementation 'org.mockito.kotlin:mockito-kotlin:5.4.0' - // JVM-based unit tests (that don't need a real device or emulator) - testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version" + // JVM-based unit tests (that don't need a real device or emulator) + testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version" androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' androidTestImplementation 'androidx.test:runner:1.6.1' diff --git a/app-android/app/src/main/java/de/tutao/tutanota/AndroidThemeFacade.kt b/app-android/app/src/main/java/de/tutao/tutanota/AndroidThemeFacade.kt index 715d39a6d5e4..d11404b3e475 100644 --- a/app-android/app/src/main/java/de/tutao/tutanota/AndroidThemeFacade.kt +++ b/app-android/app/src/main/java/de/tutao/tutanota/AndroidThemeFacade.kt @@ -6,8 +6,8 @@ import android.content.res.Configuration.UI_MODE_NIGHT_MASK import android.content.res.Configuration.UI_MODE_NIGHT_YES import android.graphics.drawable.ColorDrawable import android.util.Log -import android.view.View import androidx.annotation.ColorInt +import androidx.core.view.WindowInsetsControllerCompat import de.tutao.tutanota.ipc.ThemeFacade import org.json.JSONException import org.json.JSONObject @@ -105,15 +105,16 @@ class AndroidThemeFacade( // It is not an accident that navBg and headerBg seem to be swapped, the original color scheme was reused in // this way. - val decorView = activity.window.decorView val navBg = getColor(theme, "header_bg") @ColorInt val navColor = parseColor(navBg) val isNavBarLight = navBg.isLightHexColor() - var visibilityFlags = 0 activity.window.navigationBarColor = navColor + + val windowInsetController = WindowInsetsControllerCompat(activity.window, activity.window.decorView) + if (isNavBarLight) { - visibilityFlags = visibilityFlags or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR + windowInsetController.isAppearanceLightNavigationBars = true } val headerBg = getColor(theme, "navigation_bg") @@ -128,10 +129,8 @@ class AndroidThemeFacade( // we change lightStatusBar flag accordingly. activity.window.statusBarColor = statusBarColor if (isStatusBarLight) { - visibilityFlags = visibilityFlags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR + windowInsetController.isAppearanceLightStatusBars = true } - - decorView.systemUiVisibility = visibilityFlags } private fun getColor(theme: Map, key: String): String = diff --git a/app-android/app/src/main/java/de/tutao/tutanota/LifecycleJobService.kt b/app-android/app/src/main/java/de/tutao/tutanota/LifecycleJobService.kt index 612bd1132eaf..84e06024a6aa 100644 --- a/app-android/app/src/main/java/de/tutao/tutanota/LifecycleJobService.kt +++ b/app-android/app/src/main/java/de/tutao/tutanota/LifecycleJobService.kt @@ -25,4 +25,7 @@ abstract class LifecycleJobService : JobService(), LifecycleOwner { lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY) super.onDestroy() } + + override val lifecycle: Lifecycle + get() = lifecycleRegistry } \ No newline at end of file diff --git a/app-android/app/src/main/java/de/tutao/tutanota/MainActivity.kt b/app-android/app/src/main/java/de/tutao/tutanota/MainActivity.kt index 3985b663f46a..b28e48d3cbc6 100644 --- a/app-android/app/src/main/java/de/tutao/tutanota/MainActivity.kt +++ b/app-android/app/src/main/java/de/tutao/tutanota/MainActivity.kt @@ -31,6 +31,7 @@ import android.webkit.WebView import android.webkit.WebView.HitTestResult import android.webkit.WebViewClient import android.widget.Toast +import androidx.activity.addCallback import androidx.annotation.MainThread import androidx.annotation.RequiresPermission import androidx.browser.customtabs.CustomTabsIntent @@ -39,6 +40,8 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.view.ViewCompat.setSystemGestureExclusionRects import androidx.core.view.doOnLayout import androidx.fragment.app.FragmentActivity +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import de.tutao.tutanota.alarms.AlarmNotificationsManager import de.tutao.tutanota.alarms.SystemAlarmFacade @@ -87,7 +90,6 @@ interface WebauthnHandler { } - class MainActivity : FragmentActivity() { lateinit var webView: WebView private set @@ -116,8 +118,8 @@ class MainActivity : FragmentActivity() { val db = AppDatabase.getDatabase(this, false) sseStorage = SseStorage( - db, - createAndroidKeyStoreFacade() + db, + createAndroidKeyStoreFacade() ) val localNotificationsFacade = LocalNotificationsFacade(this, sseStorage) val fileFacade = @@ -283,6 +285,11 @@ class MainActivity : FragmentActivity() { setContentView(webView) + // Set callback for back press + onBackPressedDispatcher.addCallback(this) { + onBackPressedCallback() + } + lifecycleScope.launch { val queryParameters = mutableMapOf() // If opened from notifications, tell Web app to not login automatically, we will pass @@ -291,15 +298,18 @@ class MainActivity : FragmentActivity() { queryParameters["noAutoLogin"] = "true" } - webView.post { // use webView.post to switch to main thread again to be able to observe sseStorage - sseStorage.observeUsers().observe(this@MainActivity) { userInfos -> - if (userInfos!!.isEmpty()) { - Log.d(TAG, "invalidateAlarms") - lifecycleScope.launchWhenCreated { + + // Start observing SSE users in the background. + // If there are no users we need to tell web part to invalidate alarms. + launch { + sseStorage.observeUsers() + .flowWithLifecycle(lifecycle, Lifecycle.State.STARTED) + .collect { userInfos -> + if (userInfos.isEmpty()) { + Log.d(TAG, "invalidateAlarms") commonNativeFacade.invalidateAlarms() } } - } } startWebApp(queryParameters) @@ -375,7 +385,7 @@ class MainActivity : FragmentActivity() { override fun onStart() { super.onStart() Log.d(TAG, "onStart") - lifecycleScope.launchWhenCreated { + lifecycleScope.launch { mobileFacade.visibilityChange(true) } } @@ -421,35 +431,35 @@ class MainActivity : FragmentActivity() { handleIntent(intent) } - private fun handleIntent(intent: Intent) = lifecycleScope.launchWhenCreated { - // When we redirect to the app from outside, for example after doing payment verification, - // we don't want to do any kind of intent handling - val data = intent.data + private fun handleIntent(intent: Intent) = lifecycleScope.launch { + // When we redirect to the app from outside, for example after doing payment verification, + // we don't want to do any kind of intent handling + val data = intent.data - if (data != null && data.scheme == "tutanota" && data.host == "webauthn") { - handleWebauthn(intent, data) - } + if (data != null && data.scheme == "tutanota" && data.host == "webauthn") { + handleWebauthn(intent, data) + } - if (data != null && data.toString().startsWith("tutanota://")) { - return@launchWhenCreated - } + if (data != null && data.toString().startsWith("tutanota://")) { + return@launch + } - if (intent.action != null && !intent.getBooleanExtra(ALREADY_HANDLED_INTENT, false)) { - when (intent.action) { - Intent.ACTION_SEND, Intent.ACTION_SEND_MULTIPLE, Intent.ACTION_SENDTO -> share( + if (intent.action != null && !intent.getBooleanExtra(ALREADY_HANDLED_INTENT, false)) { + when (intent.action) { + Intent.ACTION_SEND, Intent.ACTION_SEND_MULTIPLE, Intent.ACTION_SENDTO -> share( intent - ) + ) - OPEN_USER_MAILBOX_ACTION -> openMailbox(intent) - OPEN_CALENDAR_ACTION -> openCalendar(intent) - Intent.ACTION_VIEW -> { - when (intent.scheme) { - "mailto" -> share(intent) - "file" -> view(intent) - "content" -> view(intent) + OPEN_USER_MAILBOX_ACTION -> openMailbox(intent) + OPEN_CALENDAR_ACTION -> openCalendar(intent) + Intent.ACTION_VIEW -> { + when (intent.scheme) { + "mailto" -> share(intent) + "file" -> view(intent) + "content" -> view(intent) + } } } - } } } @@ -552,7 +562,6 @@ class MainActivity : FragmentActivity() { } } - @Deprecated("Deprecated in Java") override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) val continuation = activityRequests.remove(requestCode) @@ -642,11 +651,9 @@ class MainActivity : FragmentActivity() { mailToUrlString ) } catch (e: RemoteExecutionException) { - val name = if (e.message != null) { - val element = Json.parseToJsonElement(e.message!!) + val name = e.message?.let { message -> + val element = Json.parseToJsonElement(message) element.jsonObject["name"]?.jsonPrimitive?.content - } else { - null } Log.d(TAG, "failed to create a mail editor because of a ${name ?: "unknown error"}") } @@ -715,13 +722,9 @@ class MainActivity : FragmentActivity() { commonNativeFacade.openCalendar(userId) } - // this still works, but there's onBackPressedDispatcher.addCallback - // it should work on all API levels we support: - // https://stackoverflow.com/questions/72634225/onbackpressed-is-deprecated-what-is-the-alternative - @Deprecated("Deprecated in Java") - override fun onBackPressed() { + private fun onBackPressedCallback() { if (commonSystemFacade.initialized) { - lifecycleScope.launchWhenCreated { + lifecycleScope.launch { val result = mobileFacade.handleBackPress() try { if (!result) { diff --git a/app-android/app/src/main/java/de/tutao/tutanota/ModelTypes.kt b/app-android/app/src/main/java/de/tutao/tutanota/ModelTypes.kt index 38a671f48b0d..68af08f5e7aa 100644 --- a/app-android/app/src/main/java/de/tutao/tutanota/ModelTypes.kt +++ b/app-android/app/src/main/java/de/tutao/tutanota/ModelTypes.kt @@ -29,7 +29,6 @@ class IdTuple( ) { @OptIn(ExperimentalSerializationApi::class) - @Serializer(forClass = IdTuple::class) companion object IdTupleSerializer : KSerializer { override val descriptor: SerialDescriptor = listSerialDescriptor() diff --git a/app-android/app/src/main/java/de/tutao/tutanota/credentials/AndroidNativeCredentialsFacade.kt b/app-android/app/src/main/java/de/tutao/tutanota/credentials/AndroidNativeCredentialsFacade.kt index 6ab41f0b747d..7677db4d7696 100644 --- a/app-android/app/src/main/java/de/tutao/tutanota/credentials/AndroidNativeCredentialsFacade.kt +++ b/app-android/app/src/main/java/de/tutao/tutanota/credentials/AndroidNativeCredentialsFacade.kt @@ -30,7 +30,7 @@ class AndroidNativeCredentialsFacade( } override suspend fun loadAll(): List { - return db.credentialsDao().allPersistedCredentials.map { e -> e.toObject() } + return db.credentialsDao().allPersistedCredentials().map { e -> e.toObject() } } override suspend fun store(credentials: UnencryptedCredentials) { @@ -61,7 +61,7 @@ class AndroidNativeCredentialsFacade( Log.d(TAG, "Encryption mode migration complete") } val encryptedCredentials = - db.credentialsDao().allPersistedCredentials.firstOrNull { e -> e.userId == id }?.toObject() + db.credentialsDao().allPersistedCredentials().firstOrNull { e -> e.userId == id }?.toObject() return if (encryptedCredentials != null) this.decryptCredentials( encryptedCredentials, credentialsKey diff --git a/app-android/app/src/main/java/de/tutao/tutanota/credentials/CredentialsDao.kt b/app-android/app/src/main/java/de/tutao/tutanota/credentials/CredentialsDao.kt index 7cccc9fa3887..78756f4407a3 100644 --- a/app-android/app/src/main/java/de/tutao/tutanota/credentials/CredentialsDao.kt +++ b/app-android/app/src/main/java/de/tutao/tutanota/credentials/CredentialsDao.kt @@ -10,8 +10,8 @@ interface CredentialsDao { @Insert(onConflict = OnConflictStrategy.REPLACE) fun insertPersistedCredentials(persistedCredentials: PersistedCredentialsEntity) - @get:Query("SELECT * FROM PersistedCredentials") - val allPersistedCredentials: List + @Query("SELECT * FROM PersistedCredentials") + fun allPersistedCredentials(): List @Query("DELETE FROM PersistedCredentials WHERE userId = :userId") fun deletePersistedCredentials(userId: String) diff --git a/app-android/app/src/main/java/de/tutao/tutanota/data/AlarmInfoDao.kt b/app-android/app/src/main/java/de/tutao/tutanota/data/AlarmInfoDao.kt index a8550d847ec4..62c87fe7e756 100644 --- a/app-android/app/src/main/java/de/tutao/tutanota/data/AlarmInfoDao.kt +++ b/app-android/app/src/main/java/de/tutao/tutanota/data/AlarmInfoDao.kt @@ -11,8 +11,8 @@ interface AlarmInfoDao { @Insert(onConflict = OnConflictStrategy.REPLACE) fun insertAlarmNotification(alarmNotification: AlarmNotificationEntity) - @get:Query("SELECT * FROM AlarmNotification") - val alarmNotifications: List + @Query("SELECT * FROM AlarmNotification") + fun alarmNotifications(): List @Query("DELETE FROM AlarmNotification WHERE identifier = :identifier") fun deleteAlarmNotification(identifier: String) diff --git a/app-android/app/src/main/java/de/tutao/tutanota/data/UserInfoDao.kt b/app-android/app/src/main/java/de/tutao/tutanota/data/UserInfoDao.kt index ced380471111..822cc99be0ef 100644 --- a/app-android/app/src/main/java/de/tutao/tutanota/data/UserInfoDao.kt +++ b/app-android/app/src/main/java/de/tutao/tutanota/data/UserInfoDao.kt @@ -2,14 +2,15 @@ package de.tutao.tutanota.data import androidx.lifecycle.LiveData import androidx.room.* +import kotlinx.coroutines.flow.Flow @Dao abstract class UserInfoDao { - @get:Query("SELECT * FROM User") - abstract val users: List + @Query("SELECT * FROM User") + abstract fun users(): List @Query("SELECT * FROM User") - abstract fun observeUsers(): LiveData> + abstract fun observeUsers(): Flow> @Query("SELECT * FROM PushIdentifierKey WHERE pushIdentifierId = :pushIdentifier") abstract fun getPushIdentifierKey(pushIdentifier: String): PushIdentifierKey? diff --git a/app-android/app/src/main/java/de/tutao/tutanota/push/LocalNotificationsFacade.kt b/app-android/app/src/main/java/de/tutao/tutanota/push/LocalNotificationsFacade.kt index 17092103c328..29ba4b87f1a3 100644 --- a/app-android/app/src/main/java/de/tutao/tutanota/push/LocalNotificationsFacade.kt +++ b/app-android/app/src/main/java/de/tutao/tutanota/push/LocalNotificationsFacade.kt @@ -160,12 +160,6 @@ class LocalNotificationsFacade(private val context: Context, private val sseStor Manifest.permission.POST_NOTIFICATIONS ) != PackageManager.PERMISSION_GRANTED ) { - // FIXME: Do we want to ask permission here? - // here to request the missing permissions, and then overriding - // public void onRequestPermissionsResult(int requestCode, String[] permissions, - // int[] grantResults) - // to handle the case where the user grants the permission. See the documentation - // for ActivityCompat#requestPermissions for more details. return } notificationManager.notify(mailNotificationId("downloads"), notification) diff --git a/app-android/app/src/main/java/de/tutao/tutanota/push/NetworkObserver.kt b/app-android/app/src/main/java/de/tutao/tutanota/push/NetworkObserver.kt index 142cca5356fa..f0e9fcc9e187 100644 --- a/app-android/app/src/main/java/de/tutao/tutanota/push/NetworkObserver.kt +++ b/app-android/app/src/main/java/de/tutao/tutanota/push/NetworkObserver.kt @@ -5,19 +5,37 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.net.ConnectivityManager +import android.net.ConnectivityManager.NetworkCallback +import android.net.Network import android.util.Log +import androidx.core.content.getSystemService import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import kotlinx.coroutines.launch class NetworkObserver internal constructor(private val context: Context, lifecycleOwner: LifecycleOwner) : - BroadcastReceiver(), DefaultLifecycleObserver { - private val connectivityManager: ConnectivityManager = - context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + BroadcastReceiver(), DefaultLifecycleObserver { + private val connectivityManager: ConnectivityManager = context.getSystemService()!! private var networkConnectivityListener: NetworkConnectivityListener? = null + private val networkObserverCallback = object : NetworkCallback() { + override fun onAvailable(network: Network) { + networkConnectivityListener?.onNetworkConnectivityChange(true) + } + + override fun onLost(network: Network) { + networkConnectivityListener?.onNetworkConnectivityChange(false) + } + } init { - context.registerReceiver(this, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)) - lifecycleOwner.lifecycle.addObserver(this) + lifecycleOwner.lifecycleScope.launch { + lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) { + connectivityManager.registerDefaultNetworkCallback(networkObserverCallback) + } + } } override fun onReceive(context: Context, intent: Intent) { @@ -33,8 +51,7 @@ class NetworkObserver internal constructor(private val context: Context, lifecyc } fun hasNetworkConnection(): Boolean { - val networkInfo = connectivityManager.activeNetworkInfo - return networkInfo != null && networkInfo.isConnectedOrConnecting + return connectivityManager.activeNetwork != null } fun setNetworkConnectivityListener(networkConnectivityListener: NetworkConnectivityListener) { @@ -46,7 +63,7 @@ class NetworkObserver internal constructor(private val context: Context, lifecyc } override fun onDestroy(owner: LifecycleOwner) { - context.unregisterReceiver(this) + connectivityManager.unregisterNetworkCallback(this.networkObserverCallback) } companion object { diff --git a/app-android/app/src/main/java/de/tutao/tutanota/push/PushNotificationService.kt b/app-android/app/src/main/java/de/tutao/tutanota/push/PushNotificationService.kt index 5c9685e057e7..5eb846ccca35 100644 --- a/app-android/app/src/main/java/de/tutao/tutanota/push/PushNotificationService.kt +++ b/app-android/app/src/main/java/de/tutao/tutanota/push/PushNotificationService.kt @@ -22,6 +22,7 @@ import de.tutao.tutanota.ipc.NativeCredentialsFacade import de.tutao.tutanota.push.SseClient.SseListener import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import okhttp3.OkHttpClient import java.util.concurrent.TimeUnit @@ -58,7 +59,7 @@ private enum class State { * * SSE has its own event loop, we are just listening for events here and mediating between it and SSE storage. */ -class PushNotificationService(override val lifecycle: Lifecycle) : LifecycleJobService() { +class PushNotificationService : LifecycleJobService() { @Volatile private var jobParameters: JobParameters? = null private lateinit var localNotificationsFacade: LocalNotificationsFacade @@ -104,25 +105,27 @@ class PushNotificationService(override val lifecycle: Lifecycle) : LifecycleJobS ), NetworkUtils.defaultClient ) - sseStorage.observeUsers().observeForever { userInfos -> - Log.d(TAG, "sse storage updated " + userInfos.size) - // Closing the connection sends RST packets over network and it triggers StrictMode - // violations so we dispatch it to another thread. - lifecycleScope.launch(Dispatchers.IO) { - val userIds = userInfos.mapTo(HashSet()) { it.userId } - - if (userIds.isEmpty()) { - sseClient.stopConnection() - removeForegroundNotification() - finishJobIfNeeded() - } else { - sseClient.restartConnectionIfNeeded( - SseInfo( - sseStorage.getPushIdentifier()!!, - userIds, - sseStorage.getSseOrigin()!! + lifecycleScope.launch { + sseStorage.observeUsers().collect { userInfos -> + Log.d(TAG, "sse storage updated " + userInfos.size) + // Closing the connection sends RST packets over network and it triggers StrictMode + // violations so we dispatch it to another thread. + withContext(Dispatchers.IO) { + val userIds = userInfos.mapTo(HashSet()) { it.userId } + + if (userIds.isEmpty()) { + sseClient.stopConnection() + removeForegroundNotification() + finishJobIfNeeded() + } else { + sseClient.restartConnectionIfNeeded( + SseInfo( + sseStorage.getPushIdentifier()!!, + userIds, + sseStorage.getSseOrigin()!! + ) ) - ) + } } } } @@ -131,6 +134,7 @@ class PushNotificationService(override val lifecycle: Lifecycle) : LifecycleJobS } + @Suppress("DEPRECATION") private fun removeForegroundNotification() { Log.d(TAG, "removeForegroundNotification") if (atLeastTiramisu()) { diff --git a/app-android/app/src/main/java/de/tutao/tutanota/push/SseStorage.kt b/app-android/app/src/main/java/de/tutao/tutanota/push/SseStorage.kt index c36070f061bd..f95292b71a28 100644 --- a/app-android/app/src/main/java/de/tutao/tutanota/push/SseStorage.kt +++ b/app-android/app/src/main/java/de/tutao/tutanota/push/SseStorage.kt @@ -9,6 +9,7 @@ import de.tutao.tutanota.data.AppDatabase import de.tutao.tutanota.data.PushIdentifierKey import de.tutao.tutanota.data.User import de.tutao.tutanota.ipc.ExtendedNotificationMode +import kotlinx.coroutines.flow.Flow import java.security.KeyStoreException import java.security.UnrecoverableEntryException import java.util.Date @@ -54,12 +55,12 @@ class SseStorage( return keyStoreFacade.decryptKey(userInfo.deviceEncPushIdentifierKey!!) } - fun observeUsers(): LiveData> { + fun observeUsers(): Flow> { return db.userInfoDao().observeUsers() } fun readAlarmNotifications(): List { - return db.alarmInfoDao().alarmNotifications + return db.alarmInfoDao().alarmNotifications() } fun insertAlarmNotification(alarmNotification: AlarmNotificationEntity) { @@ -109,7 +110,7 @@ class SseStorage( ?: DEFAULT_EXTENDED_NOTIFCATION_MODE } - fun getUsers(): List = db.userInfoDao().users + fun getUsers(): List = db.userInfoDao().users() companion object { private const val LAST_PROCESSED_NOTIFICATION_ID = "lastProcessedNotificationId"