Cleanup store.

This commit is contained in:
Benoit Marty 2023-04-11 16:48:51 +02:00 committed by Benoit Marty
parent 80268156b5
commit f4a283567e
7 changed files with 40 additions and 137 deletions

View file

@ -16,26 +16,8 @@
package io.element.android.libraries.push.api.store package io.element.android.libraries.push.api.store
import io.element.android.libraries.push.api.model.BackgroundSyncMode
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
interface PushDataStore { interface PushDataStore {
val pushCounterFlow: Flow<Int> val pushCounterFlow: Flow<Int>
// TODO Move all those settings to the per user store...
fun areNotificationEnabledForDevice(): Boolean
fun setNotificationEnabledForDevice(enabled: Boolean)
fun backgroundSyncTimeOut(): Int
fun setBackgroundSyncTimeout(timeInSecond: Int)
fun backgroundSyncDelay(): Int
fun setBackgroundSyncDelay(timeInSecond: Int)
fun isBackgroundSyncEnabled(): Boolean
fun setFdroidSyncBackgroundMode(mode: BackgroundSyncMode)
fun getFdroidSyncBackgroundMode(): BackgroundSyncMode
/**
* Return true if Pin code is disabled, or if user set the settings to see full notification content.
*/
fun useCompleteNotificationFormat(): Boolean
} }

View file

@ -70,7 +70,8 @@ class NotificationDrawerManager @Inject constructor(
private var currentAppNavigationState: AppNavigationState? = null private var currentAppNavigationState: AppNavigationState? = null
private val firstThrottler = FirstThrottler(200) private val firstThrottler = FirstThrottler(200)
private var useCompleteNotificationFormat = pushDataStore.useCompleteNotificationFormat() // TODO EAx add a setting per user for this
private var useCompleteNotificationFormat = true
init { init {
handlerThread.start() handlerThread.start()
@ -111,12 +112,6 @@ class NotificationDrawerManager @Inject constructor(
} }
private fun NotificationEventQueue.onNotifiableEventReceived(notifiableEvent: NotifiableEvent) { private fun NotificationEventQueue.onNotifiableEventReceived(notifiableEvent: NotifiableEvent) {
if (!pushDataStore.areNotificationEnabledForDevice()) {
Timber.i("Notification are disabled for this device")
return
}
// If we support multi session, event list should be per userId
// Currently only manage single session
if (buildMeta.lowPrivacyLoggingEnabled) { if (buildMeta.lowPrivacyLoggingEnabled) {
Timber.d("onNotifiableEventReceived(): $notifiableEvent") Timber.d("onNotifiableEventReceived(): $notifiableEvent")
} else { } else {
@ -185,7 +180,7 @@ class NotificationDrawerManager @Inject constructor(
// TODO EAx Must be per account // TODO EAx Must be per account
fun notificationStyleChanged() { fun notificationStyleChanged() {
updateEvents { updateEvents {
val newSettings = pushDataStore.useCompleteNotificationFormat() val newSettings = true // pushDataStore.useCompleteNotificationFormat()
if (newSettings != useCompleteNotificationFormat) { if (newSettings != useCompleteNotificationFormat) {
// Settings has changed, remove all current notifications // Settings has changed, remove all current notifications
notificationRenderer.cancelAllNotifications() notificationRenderer.cancelAllNotifications()

View file

@ -34,6 +34,7 @@ data class NotificationEventQueue constructor(
* Acts as a notification debouncer to stop already dismissed push notifications from * Acts as a notification debouncer to stop already dismissed push notifications from
* displaying again when the /sync response is delayed. * displaying again when the /sync response is delayed.
*/ */
// TODO Should be per session, so the key must be Pair<SessionId, EventId>.
private val seenEventIds: CircularCache<EventId> private val seenEventIds: CircularCache<EventId>
) { ) {

View file

@ -22,15 +22,12 @@ import android.os.Handler
import android.os.Looper import android.os.Looper
import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.androidutils.network.WifiDetector
import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.push.api.store.PushDataStore
import io.element.android.libraries.push.impl.PushersManager import io.element.android.libraries.push.impl.PushersManager
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
import io.element.android.libraries.push.impl.log.pushLoggerTag import io.element.android.libraries.push.impl.log.pushLoggerTag
import io.element.android.libraries.push.impl.notifications.NotifiableEventResolver import io.element.android.libraries.push.impl.notifications.NotifiableEventResolver
import io.element.android.libraries.push.impl.notifications.NotificationActionIds import io.element.android.libraries.push.impl.notifications.NotificationActionIds
@ -38,6 +35,8 @@ import io.element.android.libraries.push.impl.notifications.NotificationDrawerMa
import io.element.android.libraries.push.impl.store.DefaultPushDataStore import io.element.android.libraries.push.impl.store.DefaultPushDataStore
import io.element.android.libraries.push.providers.api.PushData import io.element.android.libraries.push.providers.api.PushData
import io.element.android.libraries.push.providers.api.PushHandler import io.element.android.libraries.push.providers.api.PushHandler
import io.element.android.libraries.pushstore.api.UserPushStoreFactory
import io.element.android.libraries.pushstore.api.clientsecret.PushClientSecret
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
@ -51,17 +50,16 @@ private val loggerTag = LoggerTag("PushHandler", pushLoggerTag)
class DefaultPushHandler @Inject constructor( class DefaultPushHandler @Inject constructor(
private val notificationDrawerManager: NotificationDrawerManager, private val notificationDrawerManager: NotificationDrawerManager,
private val notifiableEventResolver: NotifiableEventResolver, private val notifiableEventResolver: NotifiableEventResolver,
private val pushDataStore: PushDataStore,
private val defaultPushDataStore: DefaultPushDataStore, private val defaultPushDataStore: DefaultPushDataStore,
private val userPushStoreFactory: UserPushStoreFactory,
private val pushClientSecret: PushClientSecret, private val pushClientSecret: PushClientSecret,
private val actionIds: NotificationActionIds, private val actionIds: NotificationActionIds,
@ApplicationContext private val context: Context, @ApplicationContext private val context: Context,
private val buildMeta: BuildMeta, private val buildMeta: BuildMeta,
private val matrixAuthenticationService: MatrixAuthenticationService, private val matrixAuthenticationService: MatrixAuthenticationService,
): PushHandler { ) : PushHandler {
private val coroutineScope = CoroutineScope(SupervisorJob()) private val coroutineScope = CoroutineScope(SupervisorJob())
private val wifiDetector: WifiDetector = WifiDetector(context)
// UI handler // UI handler
private val mUIHandler by lazy { private val mUIHandler by lazy {
@ -89,12 +87,6 @@ class DefaultPushHandler @Inject constructor(
return return
} }
// TODO EAx Should be per user
if (!pushDataStore.areNotificationEnabledForDevice()) {
Timber.tag(loggerTag.value).i("Notification are disabled for this device")
return
}
mUIHandler.post { mUIHandler.post {
coroutineScope.launch(Dispatchers.IO) { handleInternal(pushData) } coroutineScope.launch(Dispatchers.IO) { handleInternal(pushData) }
} }
@ -139,6 +131,13 @@ class DefaultPushHandler @Inject constructor(
return return
} }
val userPushStore = userPushStoreFactory.create(userId)
if (!userPushStore.areNotificationEnabledForDevice()) {
// TODO We need to check if this is an incoming call
Timber.tag(loggerTag.value).i("Notification are disabled for this device, ignore push.")
return
}
notificationDrawerManager.onNotifiableEventReceived(notificationData) notificationDrawerManager.onNotifiableEventReceived(notificationData)
} catch (e: Exception) { } catch (e: Exception) {
Timber.tag(loggerTag.value).e(e, "## handleInternal() failed") Timber.tag(loggerTag.value).e(e, "## handleInternal() failed")

View file

@ -17,20 +17,15 @@
package io.element.android.libraries.push.impl.store package io.element.android.libraries.push.impl.store
import android.content.Context import android.content.Context
import android.content.SharedPreferences
import androidx.core.content.edit
import androidx.datastore.core.DataStore import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.preferencesDataStore import androidx.datastore.preferences.preferencesDataStore
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.DefaultPreferences
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.push.api.model.BackgroundSyncMode
import io.element.android.libraries.push.api.store.PushDataStore import io.element.android.libraries.push.api.store.PushDataStore
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
@ -42,7 +37,6 @@ private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(na
@ContributesBinding(AppScope::class) @ContributesBinding(AppScope::class)
class DefaultPushDataStore @Inject constructor( class DefaultPushDataStore @Inject constructor(
@ApplicationContext private val context: Context, @ApplicationContext private val context: Context,
@DefaultPreferences private val defaultPrefs: SharedPreferences,
) : PushDataStore { ) : PushDataStore {
private val pushCounter = intPreferencesKey("push_counter") private val pushCounter = intPreferencesKey("push_counter")
@ -56,94 +50,4 @@ class DefaultPushDataStore @Inject constructor(
settings[pushCounter] = currentCounterValue + 1 settings[pushCounter] = currentCounterValue + 1
} }
} }
override fun areNotificationEnabledForDevice(): Boolean {
return defaultPrefs.getBoolean(SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY, true)
}
override fun setNotificationEnabledForDevice(enabled: Boolean) {
defaultPrefs.edit {
putBoolean(SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY, enabled)
}
}
override fun backgroundSyncTimeOut(): Int {
return tryOrNull {
// The xml pref is saved as a string so use getString and parse
defaultPrefs.getString(SETTINGS_SET_SYNC_TIMEOUT_PREFERENCE_KEY, null)?.toInt()
} ?: BackgroundSyncMode.DEFAULT_SYNC_TIMEOUT_SECONDS
}
override fun setBackgroundSyncTimeout(timeInSecond: Int) {
defaultPrefs
.edit()
.putString(SETTINGS_SET_SYNC_TIMEOUT_PREFERENCE_KEY, timeInSecond.toString())
.apply()
}
override fun backgroundSyncDelay(): Int {
return tryOrNull {
// The xml pref is saved as a string so use getString and parse
defaultPrefs.getString(SETTINGS_SET_SYNC_DELAY_PREFERENCE_KEY, null)?.toInt()
} ?: BackgroundSyncMode.DEFAULT_SYNC_DELAY_SECONDS
}
override fun setBackgroundSyncDelay(timeInSecond: Int) {
defaultPrefs
.edit()
.putString(SETTINGS_SET_SYNC_DELAY_PREFERENCE_KEY, timeInSecond.toString())
.apply()
}
override fun isBackgroundSyncEnabled(): Boolean {
return getFdroidSyncBackgroundMode() != BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_DISABLED
}
override fun setFdroidSyncBackgroundMode(mode: BackgroundSyncMode) {
defaultPrefs
.edit()
.putString(SETTINGS_FDROID_BACKGROUND_SYNC_MODE, mode.name)
.apply()
}
override fun getFdroidSyncBackgroundMode(): BackgroundSyncMode {
return try {
val strPref = defaultPrefs
.getString(SETTINGS_FDROID_BACKGROUND_SYNC_MODE, BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_BATTERY.name)
BackgroundSyncMode.values().firstOrNull { it.name == strPref } ?: BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_BATTERY
} catch (e: Throwable) {
BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_BATTERY
}
}
/**
* Return true if Pin code is disabled, or if user set the settings to see full notification content.
*/
override fun useCompleteNotificationFormat(): Boolean {
return true
/*
return !useFlagPinCode() ||
defaultPrefs.getBoolean(SETTINGS_SECURITY_USE_COMPLETE_NOTIFICATIONS_FLAG, true)
*/
}
companion object {
// notifications
const val SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY = "SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY"
const val SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY = "SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY"
// background sync
const val SETTINGS_START_ON_BOOT_PREFERENCE_KEY = "SETTINGS_START_ON_BOOT_PREFERENCE_KEY"
const val SETTINGS_ENABLE_BACKGROUND_SYNC_PREFERENCE_KEY = "SETTINGS_ENABLE_BACKGROUND_SYNC_PREFERENCE_KEY"
const val SETTINGS_SET_SYNC_TIMEOUT_PREFERENCE_KEY = "SETTINGS_SET_SYNC_TIMEOUT_PREFERENCE_KEY"
const val SETTINGS_SET_SYNC_DELAY_PREFERENCE_KEY = "SETTINGS_SET_SYNC_DELAY_PREFERENCE_KEY"
const val SETTINGS_FDROID_BACKGROUND_SYNC_MODE = "SETTINGS_FDROID_BACKGROUND_SYNC_MODE"
const val SETTINGS_BACKGROUND_SYNC_PREFERENCE_KEY = "SETTINGS_BACKGROUND_SYNC_PREFERENCE_KEY"
const val SETTINGS_SECURITY_USE_COMPLETE_NOTIFICATIONS_FLAG = "SETTINGS_SECURITY_USE_COMPLETE_NOTIFICATIONS_FLAG"
// notification method
const val SETTINGS_NOTIFICATION_METHOD_KEY = "SETTINGS_NOTIFICATION_METHOD_KEY"
}
} }

View file

@ -21,12 +21,17 @@ package io.element.android.libraries.pushstore.api
*/ */
interface UserPushStore { interface UserPushStore {
suspend fun getPushProviderName(): String? suspend fun getPushProviderName(): String?
suspend fun setPushProviderName(value: String) suspend fun setPushProviderName(value: String)
suspend fun getCurrentRegisteredPushKey(): String? suspend fun getCurrentRegisteredPushKey(): String?
suspend fun setCurrentRegisteredPushKey(value: String) suspend fun setCurrentRegisteredPushKey(value: String)
suspend fun areNotificationEnabledForDevice(): Boolean
suspend fun setNotificationEnabledForDevice(enabled: Boolean)
/**
* Return true if Pin code is disabled, or if user set the settings to see full notification content.
*/
fun useCompleteNotificationFormat(): Boolean
suspend fun reset() suspend fun reset()
} }

View file

@ -19,9 +19,11 @@ package io.element.android.libraries.pushstore.impl
import android.content.Context import android.content.Context
import androidx.datastore.core.DataStore import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore import androidx.datastore.preferences.preferencesDataStore
import io.element.android.libraries.core.bool.orTrue
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.pushstore.api.UserPushStore import io.element.android.libraries.pushstore.api.UserPushStore
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
@ -36,6 +38,7 @@ class UserPushStoreDataStore(
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "push_store_$userId") private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "push_store_$userId")
private val pushProviderName = stringPreferencesKey("pushProviderName") private val pushProviderName = stringPreferencesKey("pushProviderName")
private val currentPushKey = stringPreferencesKey("currentPushKey") private val currentPushKey = stringPreferencesKey("currentPushKey")
private val notificationEnabled = booleanPreferencesKey("notificationEnabled")
override suspend fun getPushProviderName(): String? { override suspend fun getPushProviderName(): String? {
return context.dataStore.data.first()[pushProviderName] return context.dataStore.data.first()[pushProviderName]
@ -57,6 +60,20 @@ class UserPushStoreDataStore(
} }
} }
override suspend fun areNotificationEnabledForDevice(): Boolean {
return context.dataStore.data.first()[notificationEnabled].orTrue()
}
override suspend fun setNotificationEnabledForDevice(enabled: Boolean) {
context.dataStore.edit {
it[notificationEnabled] = enabled
}
}
override fun useCompleteNotificationFormat(): Boolean {
return true
}
override suspend fun reset() { override suspend fun reset() {
context.dataStore.edit { context.dataStore.edit {
it.clear() it.clear()