Merge pull request #5955 from k9mail/notification_settings_export
Read notification settings from `NotificationChannel` on settings export
This commit is contained in:
commit
b9efb70d0e
9 changed files with 130 additions and 28 deletions
|
@ -114,4 +114,13 @@ val coreNotificationModule = module {
|
|||
)
|
||||
}
|
||||
factory { NotificationLightDecoder() }
|
||||
factory { NotificationVibrationDecoder() }
|
||||
factory {
|
||||
NotificationConfigurationConverter(notificationLightDecoder = get(), notificationVibrationDecoder = get())
|
||||
}
|
||||
factory {
|
||||
NotificationSettingsUpdater(
|
||||
preferences = get(), notificationChannelManager = get(), notificationConfigurationConverter = get()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package com.fsck.k9.notification
|
||||
|
||||
import com.fsck.k9.Account
|
||||
import com.fsck.k9.NotificationSettings
|
||||
|
||||
/**
|
||||
* Converts the [NotificationConfiguration] read from a `NotificationChannel` into a [NotificationSettings] instance.
|
||||
*/
|
||||
class NotificationConfigurationConverter(
|
||||
private val notificationLightDecoder: NotificationLightDecoder,
|
||||
private val notificationVibrationDecoder: NotificationVibrationDecoder
|
||||
) {
|
||||
fun convert(account: Account, notificationConfiguration: NotificationConfiguration): NotificationSettings {
|
||||
val light = notificationLightDecoder.decode(
|
||||
isBlinkLightsEnabled = notificationConfiguration.isBlinkLightsEnabled,
|
||||
lightColor = notificationConfiguration.lightColor,
|
||||
accountColor = account.chipColor
|
||||
)
|
||||
|
||||
val vibration = notificationVibrationDecoder.decode(
|
||||
isVibrationEnabled = notificationConfiguration.isVibrationEnabled,
|
||||
systemPattern = notificationConfiguration.vibrationPattern
|
||||
)
|
||||
|
||||
return NotificationSettings(
|
||||
isRingEnabled = notificationConfiguration.sound != null,
|
||||
ringtone = notificationConfiguration.sound?.toString(),
|
||||
light = light,
|
||||
vibration = vibration
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package com.fsck.k9.notification
|
||||
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.fsck.k9.Account
|
||||
import com.fsck.k9.Preferences
|
||||
|
||||
/**
|
||||
* Update accounts with notification settings read from their "Messages" `NotificationChannel`.
|
||||
*/
|
||||
class NotificationSettingsUpdater(
|
||||
private val preferences: Preferences,
|
||||
private val notificationChannelManager: NotificationChannelManager,
|
||||
private val notificationConfigurationConverter: NotificationConfigurationConverter
|
||||
) {
|
||||
fun updateNotificationSettings(accountUuids: Collection<String>) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
||||
|
||||
accountUuids
|
||||
.mapNotNull { accountUuid -> preferences.getAccount(accountUuid) }
|
||||
.forEach { account -> updateNotificationSettings(account) }
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
private fun updateNotificationSettings(account: Account) {
|
||||
val notificationConfiguration = notificationChannelManager.getNotificationConfiguration(account)
|
||||
val notificationSettings = notificationConfigurationConverter.convert(account, notificationConfiguration)
|
||||
|
||||
if (notificationSettings != account.notificationSettings) {
|
||||
account.updateNotificationSettings { notificationSettings }
|
||||
preferences.saveAccount(account)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.fsck.k9.notification
|
||||
|
||||
import com.fsck.k9.NotificationVibration
|
||||
import com.fsck.k9.VibratePattern
|
||||
|
||||
/**
|
||||
* Converts the vibration values read from a `NotificationChannel` into [NotificationVibration].
|
||||
*/
|
||||
class NotificationVibrationDecoder {
|
||||
fun decode(isVibrationEnabled: Boolean, systemPattern: List<Long>?): NotificationVibration {
|
||||
if (systemPattern == null || systemPattern.size < 2 || systemPattern.size % 2 != 0) {
|
||||
return NotificationVibration.DEFAULT
|
||||
}
|
||||
|
||||
val systemPatternArray = systemPattern.toLongArray()
|
||||
val repeatCount = systemPattern.size / 2
|
||||
val pattern = VibratePattern.values()
|
||||
.firstOrNull { vibratePattern ->
|
||||
val testPattern = NotificationVibration.getSystemPattern(vibratePattern, repeatCount)
|
||||
|
||||
testPattern.contentEquals(systemPatternArray)
|
||||
} ?: VibratePattern.Default
|
||||
|
||||
return NotificationVibration(isVibrationEnabled, pattern, repeatCount)
|
||||
}
|
||||
}
|
|
@ -11,7 +11,8 @@ val preferencesModule = module {
|
|||
contentResolver = get(),
|
||||
preferences = get(),
|
||||
folderSettingsProvider = get(),
|
||||
folderRepository = get()
|
||||
folderRepository = get(),
|
||||
notificationSettingsUpdater = get()
|
||||
)
|
||||
}
|
||||
factory { FolderSettingsProvider(folderRepository = get()) }
|
||||
|
|
|
@ -10,6 +10,7 @@ import com.fsck.k9.AccountPreferenceSerializer.Companion.IDENTITY_EMAIL_KEY
|
|||
import com.fsck.k9.AccountPreferenceSerializer.Companion.IDENTITY_NAME_KEY
|
||||
import com.fsck.k9.Preferences
|
||||
import com.fsck.k9.mailstore.FolderRepository
|
||||
import com.fsck.k9.notification.NotificationSettingsUpdater
|
||||
import com.fsck.k9.preferences.ServerTypeConverter.fromServerSettingsType
|
||||
import com.fsck.k9.preferences.Settings.InvalidSettingValueException
|
||||
import com.fsck.k9.preferences.Settings.SettingsDescription
|
||||
|
@ -24,10 +25,13 @@ class SettingsExporter(
|
|||
private val contentResolver: ContentResolver,
|
||||
private val preferences: Preferences,
|
||||
private val folderSettingsProvider: FolderSettingsProvider,
|
||||
private val folderRepository: FolderRepository
|
||||
private val folderRepository: FolderRepository,
|
||||
private val notificationSettingsUpdater: NotificationSettingsUpdater
|
||||
) {
|
||||
@Throws(SettingsImportExportException::class)
|
||||
fun exportToUri(includeGlobals: Boolean, accountUuids: Set<String>, uri: Uri) {
|
||||
updateNotificationSettings(accountUuids)
|
||||
|
||||
try {
|
||||
contentResolver.openOutputStream(uri)!!.use { outputStream ->
|
||||
exportPreferences(outputStream, includeGlobals, accountUuids)
|
||||
|
@ -37,6 +41,16 @@ class SettingsExporter(
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateNotificationSettings(accountUuids: Set<String>) {
|
||||
try {
|
||||
notificationSettingsUpdater.updateNotificationSettings(accountUuids)
|
||||
} catch (e: Exception) {
|
||||
// An error here could mean we export notification settings that don't reflect the current configuration
|
||||
// of the notification channels. But we prefer stale data over failing the export.
|
||||
Timber.w(e, "Error while updating accounts with notification configuration from system")
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(SettingsImportExportException::class)
|
||||
fun exportPreferences(outputStream: OutputStream, includeGlobals: Boolean, accountUuids: Set<String>) {
|
||||
try {
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.junit.Assert.assertNotNull
|
|||
import org.junit.Assert.assertNull
|
||||
import org.junit.Test
|
||||
import org.koin.core.component.inject
|
||||
import org.mockito.kotlin.mock
|
||||
import org.robolectric.RuntimeEnvironment
|
||||
|
||||
class SettingsExporterTest : K9RobolectricTest() {
|
||||
|
@ -22,7 +23,8 @@ class SettingsExporterTest : K9RobolectricTest() {
|
|||
contentResolver,
|
||||
preferences,
|
||||
folderSettingsProvider,
|
||||
folderRepository
|
||||
folderRepository,
|
||||
notificationSettingsUpdater = mock()
|
||||
)
|
||||
|
||||
@Test
|
||||
|
|
|
@ -29,6 +29,7 @@ import com.fsck.k9.mailstore.RemoteFolder
|
|||
import com.fsck.k9.notification.NotificationChannelManager
|
||||
import com.fsck.k9.notification.NotificationChannelManager.ChannelType
|
||||
import com.fsck.k9.notification.NotificationLightDecoder
|
||||
import com.fsck.k9.notification.NotificationVibrationDecoder
|
||||
import com.fsck.k9.ui.R
|
||||
import com.fsck.k9.ui.endtoend.AutocryptKeyTransferActivity
|
||||
import com.fsck.k9.ui.settings.onClick
|
||||
|
@ -52,6 +53,7 @@ class AccountSettingsFragment : PreferenceFragmentCompat(), ConfirmationDialogFr
|
|||
private val accountRemover: BackgroundAccountRemover by inject()
|
||||
private val notificationChannelManager: NotificationChannelManager by inject()
|
||||
private val notificationLightDecoder: NotificationLightDecoder by inject()
|
||||
private val notificationVibrationDecoder: NotificationVibrationDecoder by inject()
|
||||
|
||||
private val vibrator by lazy { requireContext().getSystemService<Vibrator>() }
|
||||
private lateinit var dataStore: AccountSettingsDataStore
|
||||
|
@ -267,9 +269,14 @@ class AccountSettingsFragment : PreferenceFragmentCompat(), ConfirmationDialogFr
|
|||
}
|
||||
|
||||
notificationVibrationPreference?.let { preference ->
|
||||
preference.setVibrationFromSystem(
|
||||
val notificationVibration = notificationVibrationDecoder.decode(
|
||||
isVibrationEnabled = notificationConfiguration.isVibrationEnabled,
|
||||
combinedPattern = notificationConfiguration.vibrationPattern
|
||||
systemPattern = notificationConfiguration.vibrationPattern
|
||||
)
|
||||
preference.setVibration(
|
||||
isVibrationEnabled = notificationVibration.isEnabled,
|
||||
vibratePattern = notificationVibration.pattern,
|
||||
vibrationTimes = notificationVibration.repeatCount
|
||||
)
|
||||
preference.isEnabled = true
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import android.content.Context
|
|||
import android.util.AttributeSet
|
||||
import androidx.core.content.res.TypedArrayUtils
|
||||
import androidx.preference.ListPreference
|
||||
import com.fsck.k9.NotificationVibration
|
||||
import com.fsck.k9.VibratePattern
|
||||
import com.fsck.k9.ui.R
|
||||
import com.takisoft.preferencex.PreferenceFragmentCompat
|
||||
|
@ -61,28 +60,6 @@ constructor(
|
|||
updateSummary()
|
||||
}
|
||||
|
||||
fun setVibrationFromSystem(isVibrationEnabled: Boolean, combinedPattern: List<Long>?) {
|
||||
if (combinedPattern == null || combinedPattern.size < 2 || combinedPattern.size % 2 != 0) {
|
||||
setVibration(isVibrationEnabled, DEFAULT_VIBRATE_PATTERN, DEFAULT_VIBRATION_TIMES)
|
||||
return
|
||||
}
|
||||
|
||||
val combinedPatternArray = combinedPattern.toLongArray()
|
||||
val vibrationTimes = combinedPattern.size / 2
|
||||
val vibrationPattern = entryValues.asSequence()
|
||||
.map { entryValue ->
|
||||
val serializedVibratePattern = entryValue.toString().toInt()
|
||||
VibratePattern.deserialize(serializedVibratePattern)
|
||||
}
|
||||
.firstOrNull { vibratePattern ->
|
||||
val testPattern = NotificationVibration.getSystemPattern(vibratePattern, vibrationTimes)
|
||||
|
||||
testPattern.contentEquals(combinedPatternArray)
|
||||
} ?: DEFAULT_VIBRATE_PATTERN
|
||||
|
||||
setVibration(isVibrationEnabled, vibrationPattern, vibrationTimes)
|
||||
}
|
||||
|
||||
private fun updateSummary() {
|
||||
summary = if (isVibrationEnabled) {
|
||||
val index = entryValues.indexOf(vibratePattern.serialize().toString())
|
||||
|
|
Loading…
Reference in a new issue