Add a proper type for the vibration pattern the user can select
This commit is contained in:
parent
c58e357030
commit
0fb6bd9198
9 changed files with 95 additions and 61 deletions
|
@ -138,7 +138,7 @@ class AccountPreferenceSerializer(
|
|||
showPictures = getEnumStringPref<ShowPictures>(storage, "$accountUuid.showPicturesEnum", ShowPictures.NEVER)
|
||||
|
||||
notificationSettings.isVibrateEnabled = storage.getBoolean("$accountUuid.vibrate", false)
|
||||
notificationSettings.vibratePattern = storage.getInt("$accountUuid.vibratePattern", 0)
|
||||
notificationSettings.vibratePattern = VibratePattern.deserialize(storage.getInt("$accountUuid.vibratePattern", 0))
|
||||
notificationSettings.vibrateTimes = storage.getInt("$accountUuid.vibrateTimes", 5)
|
||||
notificationSettings.isRingEnabled = storage.getBoolean("$accountUuid.ring", true)
|
||||
notificationSettings.ringtone = storage.getString(
|
||||
|
@ -323,7 +323,7 @@ class AccountPreferenceSerializer(
|
|||
editor.putBoolean("$accountUuid.alwaysShowCcBcc", isAlwaysShowCcBcc)
|
||||
|
||||
editor.putBoolean("$accountUuid.vibrate", notificationSettings.isVibrateEnabled)
|
||||
editor.putInt("$accountUuid.vibratePattern", notificationSettings.vibratePattern)
|
||||
editor.putInt("$accountUuid.vibratePattern", notificationSettings.vibratePattern.serialize())
|
||||
editor.putInt("$accountUuid.vibrateTimes", notificationSettings.vibrateTimes)
|
||||
editor.putBoolean("$accountUuid.ring", notificationSettings.isRingEnabled)
|
||||
editor.putString("$accountUuid.ringtone", notificationSettings.ringtone)
|
||||
|
@ -606,7 +606,7 @@ class AccountPreferenceSerializer(
|
|||
|
||||
with(notificationSettings) {
|
||||
isVibrateEnabled = false
|
||||
vibratePattern = 0
|
||||
vibratePattern = VibratePattern.Default
|
||||
vibrateTimes = 5
|
||||
isRingEnabled = true
|
||||
ringtone = "content://settings/system/notification_sound"
|
||||
|
|
|
@ -26,34 +26,18 @@ class NotificationSettings {
|
|||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var vibratePattern = 0
|
||||
var vibratePattern = VibratePattern.Default
|
||||
|
||||
@get:Synchronized
|
||||
@set:Synchronized
|
||||
var vibrateTimes = 0
|
||||
|
||||
val vibration: LongArray
|
||||
get() = getVibration(vibratePattern, vibrateTimes)
|
||||
val vibrationPattern: LongArray
|
||||
get() = getVibrationPattern(vibratePattern, vibrateTimes)
|
||||
|
||||
companion object {
|
||||
// These are "off, on" patterns, specified in milliseconds
|
||||
private val defaultPattern = longArrayOf(300, 200)
|
||||
private val pattern1 = longArrayOf(100, 200)
|
||||
private val pattern2 = longArrayOf(100, 500)
|
||||
private val pattern3 = longArrayOf(200, 200)
|
||||
private val pattern4 = longArrayOf(200, 500)
|
||||
private val pattern5 = longArrayOf(500, 500)
|
||||
|
||||
fun getVibration(pattern: Int, times: Int): LongArray {
|
||||
val selectedPattern = when (pattern) {
|
||||
1 -> pattern1
|
||||
2 -> pattern2
|
||||
3 -> pattern3
|
||||
4 -> pattern4
|
||||
5 -> pattern5
|
||||
else -> defaultPattern
|
||||
}
|
||||
|
||||
fun getVibrationPattern(vibratePattern: VibratePattern, times: Int): LongArray {
|
||||
val selectedPattern = vibratePattern.vibrationPattern
|
||||
val repeatedPattern = LongArray(selectedPattern.size * times)
|
||||
for (n in 0 until times) {
|
||||
System.arraycopy(selectedPattern, 0, repeatedPattern, n * selectedPattern.size, selectedPattern.size)
|
||||
|
@ -66,3 +50,38 @@ class NotificationSettings {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class VibratePattern(
|
||||
/**
|
||||
* These are "off, on" patterns, specified in milliseconds.
|
||||
*/
|
||||
val vibrationPattern: LongArray
|
||||
) {
|
||||
Default(vibrationPattern = longArrayOf(300, 200)),
|
||||
Pattern1(vibrationPattern = longArrayOf(100, 200)),
|
||||
Pattern2(vibrationPattern = longArrayOf(100, 500)),
|
||||
Pattern3(vibrationPattern = longArrayOf(200, 200)),
|
||||
Pattern4(vibrationPattern = longArrayOf(200, 500)),
|
||||
Pattern5(vibrationPattern = longArrayOf(500, 500));
|
||||
|
||||
fun serialize(): Int = when (this) {
|
||||
Default -> 0
|
||||
Pattern1 -> 1
|
||||
Pattern2 -> 2
|
||||
Pattern3 -> 3
|
||||
Pattern4 -> 4
|
||||
Pattern5 -> 5
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun deserialize(value: Int): VibratePattern = when (value) {
|
||||
0 -> Default
|
||||
1 -> Pattern1
|
||||
2 -> Pattern2
|
||||
3 -> Pattern3
|
||||
4 -> Pattern4
|
||||
5 -> Pattern5
|
||||
else -> error("Unknown VibratePattern value: $value")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ internal class BaseNotificationDataCreator {
|
|||
|
||||
private fun createNotificationAppearance(account: Account): NotificationAppearance {
|
||||
return with(account.notificationSettings) {
|
||||
val vibrationPattern = if (isVibrateEnabled) vibration else null
|
||||
val vibrationPattern = if (isVibrateEnabled) vibrationPattern else null
|
||||
NotificationAppearance(ringtone, vibrationPattern, ledColor)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -194,7 +194,7 @@ class NotificationChannelManager(
|
|||
private fun NotificationChannel.matches(notificationSettings: NotificationSettings): Boolean {
|
||||
return lightColor == notificationSettings.ledColor &&
|
||||
shouldVibrate() == notificationSettings.isVibrateEnabled &&
|
||||
vibrationPattern.contentEquals(notificationSettings.vibration)
|
||||
vibrationPattern.contentEquals(notificationSettings.vibrationPattern)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
|
@ -213,7 +213,7 @@ class NotificationChannelManager(
|
|||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
private fun NotificationChannel.copyPropertiesFrom(notificationSettings: NotificationSettings) {
|
||||
lightColor = notificationSettings.ledColor
|
||||
vibrationPattern = notificationSettings.vibration
|
||||
vibrationPattern = notificationSettings.vibrationPattern
|
||||
enableVibration(notificationSettings.isVibrateEnabled)
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.fsck.k9.Identity
|
|||
import com.fsck.k9.K9
|
||||
import com.fsck.k9.K9.LockScreenNotificationVisibility
|
||||
import com.fsck.k9.NotificationSettings
|
||||
import com.fsck.k9.VibratePattern
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Test
|
||||
import org.mockito.kotlin.mock
|
||||
|
@ -149,13 +150,15 @@ class BaseNotificationDataCreatorTest {
|
|||
@Test
|
||||
fun `vibration pattern`() {
|
||||
account.notificationSettings.isVibrateEnabled = true
|
||||
account.notificationSettings.vibratePattern = 3
|
||||
account.notificationSettings.vibratePattern = VibratePattern.Pattern3
|
||||
account.notificationSettings.vibrateTimes = 2
|
||||
val notificationData = createNotificationData()
|
||||
|
||||
val result = notificationDataCreator.createBaseNotificationData(notificationData)
|
||||
|
||||
assertThat(result.appearance.vibrationPattern).isEqualTo(NotificationSettings.getVibration(3, 2))
|
||||
assertThat(result.appearance.vibrationPattern).isEqualTo(
|
||||
NotificationSettings.getVibrationPattern(VibratePattern.Pattern3, 2)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -10,3 +10,8 @@ inline fun <reified T : Enum<T>> Bundle.getEnum(key: String, defaultValue: T): T
|
|||
val value = getString(key) ?: return defaultValue
|
||||
return enumValueOf(value)
|
||||
}
|
||||
|
||||
inline fun <reified T : Enum<T>> Bundle.getEnum(key: String): T {
|
||||
val value = getString(key) ?: error("Missing enum value for key '$key'")
|
||||
return enumValueOf(value)
|
||||
}
|
||||
|
|
|
@ -246,7 +246,7 @@ class AccountSettingsDataStore(
|
|||
private fun getCombinedVibrationValue(): String {
|
||||
return VibrationPreference.encode(
|
||||
isVibrationEnabled = account.notificationSettings.isVibrateEnabled,
|
||||
vibrationPattern = account.notificationSettings.vibratePattern,
|
||||
vibratePattern = account.notificationSettings.vibratePattern,
|
||||
vibrationTimes = account.notificationSettings.vibrateTimes
|
||||
)
|
||||
}
|
||||
|
|
|
@ -17,7 +17,10 @@ import androidx.appcompat.widget.SwitchCompat
|
|||
import androidx.core.content.getSystemService
|
||||
import androidx.preference.PreferenceDialogFragmentCompat
|
||||
import com.fsck.k9.NotificationSettings
|
||||
import com.fsck.k9.VibratePattern
|
||||
import com.fsck.k9.ui.R
|
||||
import com.fsck.k9.ui.getEnum
|
||||
import com.fsck.k9.ui.putEnum
|
||||
|
||||
class VibrationDialogFragment : PreferenceDialogFragmentCompat() {
|
||||
private val vibrator by lazy { requireContext().getSystemService<Vibrator>() ?: error("Vibrator service missing") }
|
||||
|
@ -31,15 +34,15 @@ class VibrationDialogFragment : PreferenceDialogFragmentCompat() {
|
|||
val context = requireContext()
|
||||
|
||||
val isVibrationEnabled: Boolean
|
||||
val vibrationPattern: Int
|
||||
val vibratePattern: VibratePattern
|
||||
val vibrationTimes: Int
|
||||
if (savedInstanceState != null) {
|
||||
isVibrationEnabled = savedInstanceState.getBoolean(STATE_VIBRATE)
|
||||
vibrationPattern = savedInstanceState.getInt(STATE_VIBRATION_PATTERN)
|
||||
vibratePattern = savedInstanceState.getEnum(STATE_VIBRATE_PATTERN)
|
||||
vibrationTimes = savedInstanceState.getInt(STATE_VIBRATION_TIMES)
|
||||
} else {
|
||||
isVibrationEnabled = vibrationPreference.isVibrationEnabled
|
||||
vibrationPattern = vibrationPreference.vibrationPattern
|
||||
vibratePattern = vibrationPreference.vibratePattern
|
||||
vibrationTimes = vibrationPreference.vibrationTimes
|
||||
}
|
||||
|
||||
|
@ -47,7 +50,7 @@ class VibrationDialogFragment : PreferenceDialogFragmentCompat() {
|
|||
isVibrationEnabled,
|
||||
entries = vibrationPreference.entries.map { it.toString() },
|
||||
entryValues = vibrationPreference.entryValues.map { it.toString().toInt() },
|
||||
vibrationPattern,
|
||||
vibratePattern,
|
||||
vibrationTimes
|
||||
)
|
||||
|
||||
|
@ -62,7 +65,7 @@ class VibrationDialogFragment : PreferenceDialogFragmentCompat() {
|
|||
if (positiveResult) {
|
||||
vibrationPreference.setVibration(
|
||||
isVibrationEnabled = adapter.isVibrationEnabled,
|
||||
vibrationPattern = adapter.vibrationPattern,
|
||||
vibratePattern = adapter.vibratePattern,
|
||||
vibrationTimes = adapter.vibrationTimes
|
||||
)
|
||||
}
|
||||
|
@ -71,21 +74,21 @@ class VibrationDialogFragment : PreferenceDialogFragmentCompat() {
|
|||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putBoolean(STATE_VIBRATE, adapter.isVibrationEnabled)
|
||||
outState.putInt(STATE_VIBRATION_PATTERN, adapter.vibrationPattern)
|
||||
outState.putEnum(STATE_VIBRATE_PATTERN, adapter.vibratePattern)
|
||||
outState.putInt(STATE_VIBRATION_TIMES, adapter.vibrationTimes)
|
||||
}
|
||||
|
||||
private fun playVibration() {
|
||||
val vibrationPattern = adapter.vibrationPattern
|
||||
val vibratePattern = adapter.vibratePattern
|
||||
val vibrationTimes = adapter.vibrationTimes
|
||||
val combinedPattern = NotificationSettings.getVibration(vibrationPattern, vibrationTimes)
|
||||
val vibrationPattern = NotificationSettings.getVibrationPattern(vibratePattern, vibrationTimes)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val vibrationEffect = VibrationEffect.createWaveform(combinedPattern, -1)
|
||||
val vibrationEffect = VibrationEffect.createWaveform(vibrationPattern, -1)
|
||||
vibrator.vibrate(vibrationEffect)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
vibrator.vibrate(combinedPattern, -1)
|
||||
vibrator.vibrate(vibrationPattern, -1)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,13 +96,13 @@ class VibrationDialogFragment : PreferenceDialogFragmentCompat() {
|
|||
var isVibrationEnabled: Boolean,
|
||||
private val entries: List<String>,
|
||||
private val entryValues: List<Int>,
|
||||
initialVibrationPattern: Int,
|
||||
initialVibratePattern: VibratePattern,
|
||||
initialVibrationTimes: Int
|
||||
) : BaseAdapter() {
|
||||
private var checkedEntryIndex = entryValues.indexOf(initialVibrationPattern).takeIf { it != -1 } ?: 0
|
||||
private var checkedEntryIndex = entryValues.indexOf(initialVibratePattern.serialize()).takeIf { it != -1 } ?: 0
|
||||
|
||||
val vibrationPattern: Int
|
||||
get() = entryValues[checkedEntryIndex]
|
||||
val vibratePattern: VibratePattern
|
||||
get() = VibratePattern.deserialize(entryValues[checkedEntryIndex])
|
||||
|
||||
var vibrationTimes = initialVibrationTimes
|
||||
|
||||
|
@ -209,7 +212,7 @@ class VibrationDialogFragment : PreferenceDialogFragmentCompat() {
|
|||
|
||||
companion object {
|
||||
private const val STATE_VIBRATE = "vibrate"
|
||||
private const val STATE_VIBRATION_PATTERN = "vibrationPattern"
|
||||
private const val STATE_VIBRATE_PATTERN = "vibratePattern"
|
||||
private const val STATE_VIBRATION_TIMES = "vibrationTimes"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.util.AttributeSet
|
|||
import androidx.core.content.res.TypedArrayUtils
|
||||
import androidx.preference.ListPreference
|
||||
import com.fsck.k9.NotificationSettings
|
||||
import com.fsck.k9.VibratePattern
|
||||
import com.fsck.k9.ui.R
|
||||
import com.takisoft.preferencex.PreferenceFragmentCompat
|
||||
|
||||
|
@ -28,7 +29,7 @@ constructor(
|
|||
internal var isVibrationEnabled: Boolean = false
|
||||
private set
|
||||
|
||||
internal var vibrationPattern: Int = DEFAULT_VIBRATION_PATTERN
|
||||
internal var vibratePattern = DEFAULT_VIBRATE_PATTERN
|
||||
private set
|
||||
|
||||
internal var vibrationTimes: Int = DEFAULT_VIBRATION_TIMES
|
||||
|
@ -39,7 +40,7 @@ constructor(
|
|||
val (isVibrationEnabled, vibrationPattern, vibrationTimes) = decode(encoded)
|
||||
|
||||
this.isVibrationEnabled = isVibrationEnabled
|
||||
this.vibrationPattern = vibrationPattern
|
||||
this.vibratePattern = vibrationPattern
|
||||
this.vibrationTimes = vibrationTimes
|
||||
|
||||
updateSummary()
|
||||
|
@ -49,12 +50,12 @@ constructor(
|
|||
preferenceManager.showDialog(this)
|
||||
}
|
||||
|
||||
fun setVibration(isVibrationEnabled: Boolean, vibrationPattern: Int, vibrationTimes: Int) {
|
||||
fun setVibration(isVibrationEnabled: Boolean, vibratePattern: VibratePattern, vibrationTimes: Int) {
|
||||
this.isVibrationEnabled = isVibrationEnabled
|
||||
this.vibrationPattern = vibrationPattern
|
||||
this.vibratePattern = vibratePattern
|
||||
this.vibrationTimes = vibrationTimes
|
||||
|
||||
val encoded = encode(isVibrationEnabled, vibrationPattern, vibrationTimes)
|
||||
val encoded = encode(isVibrationEnabled, vibratePattern, vibrationTimes)
|
||||
persistString(encoded)
|
||||
|
||||
updateSummary()
|
||||
|
@ -62,26 +63,29 @@ constructor(
|
|||
|
||||
fun setVibrationFromSystem(isVibrationEnabled: Boolean, combinedPattern: List<Long>?) {
|
||||
if (combinedPattern == null || combinedPattern.size < 2 || combinedPattern.size % 2 != 0) {
|
||||
setVibration(isVibrationEnabled, DEFAULT_VIBRATION_PATTERN, DEFAULT_VIBRATION_TIMES)
|
||||
setVibration(isVibrationEnabled, DEFAULT_VIBRATE_PATTERN, DEFAULT_VIBRATION_TIMES)
|
||||
return
|
||||
}
|
||||
|
||||
val combinedPatternArray = combinedPattern.toLongArray()
|
||||
val vibrationTimes = combinedPattern.size / 2
|
||||
val vibrationPattern = entryValues.asSequence()
|
||||
.map { entryValue -> entryValue.toString().toInt() }
|
||||
.firstOrNull { vibrationPattern ->
|
||||
val testPattern = NotificationSettings.getVibration(vibrationPattern, vibrationTimes)
|
||||
.map { entryValue ->
|
||||
val serializedVibratePattern = entryValue.toString().toInt()
|
||||
VibratePattern.deserialize(serializedVibratePattern)
|
||||
}
|
||||
.firstOrNull { vibratePattern ->
|
||||
val testPattern = NotificationSettings.getVibrationPattern(vibratePattern, vibrationTimes)
|
||||
|
||||
testPattern.contentEquals(combinedPatternArray)
|
||||
} ?: DEFAULT_VIBRATION_PATTERN
|
||||
} ?: DEFAULT_VIBRATE_PATTERN
|
||||
|
||||
setVibration(isVibrationEnabled, vibrationPattern, vibrationTimes)
|
||||
}
|
||||
|
||||
private fun updateSummary() {
|
||||
summary = if (isVibrationEnabled) {
|
||||
val index = entryValues.indexOf(vibrationPattern.toString())
|
||||
val index = entryValues.indexOf(vibratePattern.serialize().toString())
|
||||
entries[index]
|
||||
} else {
|
||||
context.getString(R.string.account_settings_vibrate_summary_disabled)
|
||||
|
@ -89,7 +93,7 @@ constructor(
|
|||
}
|
||||
|
||||
companion object {
|
||||
private const val DEFAULT_VIBRATION_PATTERN = 0
|
||||
private val DEFAULT_VIBRATE_PATTERN = VibratePattern.Default
|
||||
private const val DEFAULT_VIBRATION_TIMES = 1
|
||||
|
||||
init {
|
||||
|
@ -98,14 +102,14 @@ constructor(
|
|||
)
|
||||
}
|
||||
|
||||
fun encode(isVibrationEnabled: Boolean, vibrationPattern: Int, vibrationTimes: Int): String {
|
||||
return "$isVibrationEnabled|$vibrationPattern|$vibrationTimes"
|
||||
fun encode(isVibrationEnabled: Boolean, vibratePattern: VibratePattern, vibrationTimes: Int): String {
|
||||
return "$isVibrationEnabled|${vibratePattern.name}|$vibrationTimes"
|
||||
}
|
||||
|
||||
fun decode(encoded: String): Triple<Boolean, Int, Int> {
|
||||
fun decode(encoded: String): Triple<Boolean, VibratePattern, Int> {
|
||||
val parts = encoded.split('|')
|
||||
val isVibrationEnabled = parts[0].toBoolean()
|
||||
val vibrationPattern = parts[1].toInt()
|
||||
val vibrationPattern = VibratePattern.valueOf(parts[1])
|
||||
val vibrationTimes = parts[2].toInt()
|
||||
return Triple(isVibrationEnabled, vibrationPattern, vibrationTimes)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue