Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,9 @@ enum class LockScreenNotificationVisibility {
APP_NAME,
NOTHING,
}

enum class AnimationPreference {
ON,
OFF,
FOLLOW_SYSTEM,
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package net.thunderbird.core.preference.display.visualSettings

import net.thunderbird.core.preference.AnimationPreference
import net.thunderbird.core.preference.BodyContentType
import net.thunderbird.core.preference.display.visualSettings.message.list.DisplayMessageListSettings

const val DISPLAY_SETTINGS_DEFAULT_IS_USE_MESSAGE_VIEW_FIXED_WIDTH_FONT = false
const val DISPLAY_SETTINGS_DEFAULT_IS_AUTO_FIT_WIDTH = true
const val DISPLAY_SETTINGS_DEFAULT_IS_SHOW_ANIMATION = true
val DISPLAY_SETTINGS_DEFAULT_ANIMATION_PREFERENCE = AnimationPreference.FOLLOW_SYSTEM
val DISPLAY_SETTINGS_DEFAULT_BODY_CONTENT_TYPE = BodyContentType.TEXT_HTML
const val DISPLAY_SETTINGS_DEFAULT_DRAWER_EXPAND_ALL_FOLDER = false
const val DISPLAY_SETTINGS_DEFAULT_MESSAGE_VIEW_ARCHIVE_ACTION_VISIBLE = false
Expand All @@ -15,7 +16,7 @@ const val DISPLAY_SETTINGS_DEFAULT_MESSAGE_VIEW_COPY_ACTION_VISIBLE = false
const val DISPLAY_SETTINGS_DEFAULT_MESSAGE_VIEW_SPAM_ACTION_VISIBLE = false

data class DisplayVisualSettings(
val isShowAnimations: Boolean = DISPLAY_SETTINGS_DEFAULT_IS_SHOW_ANIMATION,
val animationPreference: AnimationPreference = DISPLAY_SETTINGS_DEFAULT_ANIMATION_PREFERENCE,
val isUseMessageViewFixedWidthFont: Boolean = DISPLAY_SETTINGS_DEFAULT_IS_USE_MESSAGE_VIEW_FIXED_WIDTH_FONT,
val isAutoFitWidth: Boolean = DISPLAY_SETTINGS_DEFAULT_IS_AUTO_FIT_WIDTH,
val bodyContentType: BodyContentType = DISPLAY_SETTINGS_DEFAULT_BODY_CONTENT_TYPE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ class DefaultDisplayVisualSettingsPreferenceManager(
KEY_AUTO_FIT_WIDTH,
DISPLAY_SETTINGS_DEFAULT_IS_AUTO_FIT_WIDTH,
),
isShowAnimations = storage.getBoolean(
animationPreference = storage.getEnumOrDefault(
KEY_ANIMATION,
DISPLAY_SETTINGS_DEFAULT_IS_SHOW_ANIMATION,
DISPLAY_SETTINGS_DEFAULT_ANIMATION_PREFERENCE,
),
bodyContentType = storage.getEnumOrDefault(
KEY_MESSAGE_VIEW_BODY_CONTENT_TYPE,
Expand Down Expand Up @@ -103,7 +103,7 @@ class DefaultDisplayVisualSettingsPreferenceManager(
logger.debug(TAG) { "writeConfig() called with: config = $config" }
scope.launch(ioDispatcher) {
mutex.withLock {
storageEditor.putBoolean(KEY_ANIMATION, config.isShowAnimations)
storageEditor.putEnum(KEY_ANIMATION, config.animationPreference)
storageEditor.putBoolean(
KEY_MESSAGE_VIEW_FIXED_WIDTH_FONT,
config.isUseMessageViewFixedWidthFont,
Expand Down
11 changes: 11 additions & 0 deletions core/ui/animation/manager/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
plugins {
id(ThunderbirdPlugins.Library.android)
}

android {
namespace = "net.thunderbird.core.ui.animation.manager"
}

dependencies {
implementation(projects.core.preference.api)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package net.thunderbird.core.ui.animation.manager

import android.animation.ValueAnimator
import android.os.Build
import net.thunderbird.core.preference.AnimationPreference
import net.thunderbird.core.preference.display.visualSettings.DisplayVisualSettingsPreferenceManager

interface AnimationManager {
fun shouldShowAnimations(): Boolean
}

class DefaultAnimationManager(
private val visualSettingsPreferenceManager: DisplayVisualSettingsPreferenceManager,
) : AnimationManager {
override fun shouldShowAnimations(): Boolean {
return when (visualSettingsPreferenceManager.getConfig().animationPreference) {
AnimationPreference.ON -> true
AnimationPreference.OFF -> false
AnimationPreference.FOLLOW_SYSTEM -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ValueAnimator.areAnimatorsEnabled()
} else {
true
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@
import com.fsck.k9.preferences.upgrader.GeneralSettingsUpgraderTo69;
import com.fsck.k9.preferences.upgrader.GeneralSettingsUpgraderTo79;
import com.fsck.k9.preferences.upgrader.GeneralSettingsUpgraderTo89;
import com.fsck.k9.preferences.upgrader.GeneralSettingsUpgraderTo111;
import net.thunderbird.core.android.account.AccountDefaultsProvider;
import net.thunderbird.core.android.account.SortType;
import net.thunderbird.core.common.action.SwipeAction;
import net.thunderbird.core.preference.AnimationPreference;
import net.thunderbird.core.preference.AppTheme;
import net.thunderbird.core.preference.BackgroundOps;
import net.thunderbird.core.preference.GeneralSettingsManager;
Expand All @@ -42,6 +44,7 @@
import net.thunderbird.core.preference.SplitViewMode;
import net.thunderbird.core.preference.SubTheme;
import net.thunderbird.core.preference.display.coreSettings.DisplayCoreSettingsKt;
import net.thunderbird.core.preference.display.visualSettings.DisplayVisualSettingsKt;
import net.thunderbird.core.preference.display.visualSettings.message.list.MessageListDateTimeFormat;
import net.thunderbird.core.preference.interaction.PostRemoveNavigation;
import net.thunderbird.core.preference.network.NetworkSettingsKt;
Expand All @@ -57,7 +60,6 @@
import static net.thunderbird.core.preference.display.miscSettings.DisplayMiscSettingsKt.DISPLAY_SETTINGS_DEFAULT_SHOW_RECENT_CHANGES;
import static net.thunderbird.core.preference.display.visualSettings.DisplayVisualSettingsKt.DISPLAY_SETTINGS_DEFAULT_IS_AUTO_FIT_WIDTH;
import static net.thunderbird.core.preference.display.visualSettings.message.list.DisplayMessageListSettingsKt.MESSAGE_LIST_SETTINGS_DEFAULT_IS_CHANGE_CONTACT_NAME_COLOR;
import static net.thunderbird.core.preference.display.visualSettings.DisplayVisualSettingsKt.DISPLAY_SETTINGS_DEFAULT_IS_SHOW_ANIMATION;
import static net.thunderbird.core.preference.display.visualSettings.message.list.DisplayMessageListSettingsKt.MESSAGE_LIST_SETTINGS_DEFAULT_IS_SHOW_CONTACT_NAME;
import static net.thunderbird.core.preference.display.visualSettings.message.list.DisplayMessageListSettingsKt.MESSAGE_LIST_SETTINGS_DEFAULT_IS_SHOW_CONTACT_PICTURE;
import static net.thunderbird.core.preference.display.visualSettings.message.list.DisplayMessageListSettingsKt.MESSAGE_LIST_SETTINGS_DEFAULT_IS_SHOW_CORRESPONDENT_NAMES;
Expand Down Expand Up @@ -85,7 +87,8 @@ class GeneralSettingsDescriptions {
*/

s.put("animations", Settings.versions(
new V(1, new BooleanSetting(DISPLAY_SETTINGS_DEFAULT_IS_SHOW_ANIMATION))
new V(1, new BooleanSetting(true)),
new V(111, new EnumSetting<>(AnimationPreference.class, AnimationPreference.FOLLOW_SYSTEM))
));
s.put("backgroundOperations", Settings.versions(
new V(1, new EnumSetting<>(BackgroundOps.class, BackgroundOps.WHEN_CHECKED_AUTO_SYNC)),
Expand Down Expand Up @@ -361,6 +364,7 @@ class GeneralSettingsDescriptions {
u.put(69, new GeneralSettingsUpgraderTo69());
u.put(79, new GeneralSettingsUpgraderTo79());
u.put(89, new GeneralSettingsUpgraderTo89());
u.put(111, new GeneralSettingsUpgraderTo111());

UPGRADERS = Collections.unmodifiableMap(u);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class Settings {
*
* @see SettingsExporter
*/
public static final int VERSION = 110;
public static final int VERSION = 111;

static Map<String, Object> validate(int version, Map<String, TreeMap<Integer, SettingsDescription<?>>> settings,
Map<String, String> importedSettings, boolean useDefaultValues) {
Expand Down
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upgraders must be covered by a test.

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.fsck.k9.preferences.upgrader;


import java.util.Map;

import com.fsck.k9.preferences.SettingsUpgrader;
import net.thunderbird.core.preference.AnimationPreference;


/**
* Convert <em>animations</em> from boolean to {@link AnimationPreference} enum.
*
* {@code true} maps to {@link AnimationPreference#ON}, {@code false} maps to {@link AnimationPreference#OFF}.
*/
public class GeneralSettingsUpgraderTo111 implements SettingsUpgrader {

@Override
public void upgrade(Map<String, Object> settings) {
Object animations = settings.get("animations");
if (animations instanceof Boolean) {
boolean value = (Boolean) animations;
settings.put("animations", value ? AnimationPreference.ON : AnimationPreference.OFF);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@
<item>follow_system</item>
</string-array>

<string-array name="animation_values" translatable="false">
<item>ON</item>
<item>OFF</item>
<item>FOLLOW_SYSTEM</item>
</string-array>

<string-array name="message_theme_values" translatable="false">
<item>light</item>
<item>dark</item>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.fsck.k9.preferences.upgrader

import assertk.assertThat
import assertk.assertions.doesNotContainKey
import assertk.assertions.isEqualTo
import kotlin.test.Test
import net.thunderbird.core.preference.AnimationPreference

class GeneralSettingsUpgraderTo111Test {

private val upgrader = GeneralSettingsUpgraderTo111()

@Test
fun `should convert animations=true to ON`() {
val settings = mutableMapOf<String, Any?>(ANIMATIONS_KEY to true)

upgrader.upgrade(settings)

assertThat(settings[ANIMATIONS_KEY]).isEqualTo(AnimationPreference.ON)
}

@Test
fun `should convert animations=false to OFF`() {
val settings = mutableMapOf<String, Any?>(ANIMATIONS_KEY to false)

upgrader.upgrade(settings)

assertThat(settings[ANIMATIONS_KEY]).isEqualTo(AnimationPreference.OFF)
}

@Test
fun `should not insert animations key when missing`() {
val settings = mutableMapOf<String, Any?>()

upgrader.upgrade(settings)

assertThat(settings).doesNotContainKey(ANIMATIONS_KEY)
}

@Test
fun `should leave existing AnimationPreference value unchanged`() {
val settings = mutableMapOf<String, Any?>(ANIMATIONS_KEY to AnimationPreference.FOLLOW_SYSTEM)

upgrader.upgrade(settings)

assertThat(settings[ANIMATIONS_KEY]).isEqualTo(AnimationPreference.FOLLOW_SYSTEM)
}

private companion object {
const val ANIMATIONS_KEY = "animations"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import net.thunderbird.core.preference.storage.StorageUpdater;

public class K9StoragePersister implements StoragePersister {
private static final int DB_VERSION = 29;
private static final int DB_VERSION = 30;
private static final String DB_NAME = "preferences_storage";

private final Context context;
Expand Down
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Migrations must be covered by a test.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.fsck.k9.preferences.migration

import android.database.sqlite.SQLiteDatabase

private const val ANIMATION_KEY = "animations"

/**
* Migrate the "animations" setting from boolean string to AnimationPreference enum name.
*
* - "true" -> "ON" (user explicitly enabled animations)
* - "false" -> "OFF" (user explicitly disabled animations)
* - missing -> no change (new default FOLLOW_SYSTEM will apply)
*/
class StorageMigrationTo30(
private val db: SQLiteDatabase,
private val migrationsHelper: StorageMigrationHelper,
) {
fun migrateAnimationSetting() {
val currentValue = migrationsHelper.readValue(db, ANIMATION_KEY)
when (currentValue) {
"true" -> migrationsHelper.writeValue(db, ANIMATION_KEY, "ON")
"false" -> migrationsHelper.writeValue(db, ANIMATION_KEY, "OFF")
// If missing or already migrated, do nothing
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@ internal object StorageMigrations {
if (oldVersion < 27) StorageMigrationTo27(db, migrationsHelper).addAvatarMonogram()
if (oldVersion < 28) StorageMigrationTo28(db, migrationsHelper).ensureAvatarSet()
if (oldVersion < 29) StorageMigrationTo29(db, migrationsHelper).renameAutoSelectFolderPreference()
if (oldVersion < 30) StorageMigrationTo30(db, migrationsHelper).migrateAnimationSetting()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.fsck.k9.preferences.migration

import assertk.assertThat
import assertk.assertions.doesNotContainKey
import assertk.assertions.isEqualTo
import assertk.assertions.key
import com.fsck.k9.preferences.createPreferencesDatabase
import kotlin.test.Test
import net.thunderbird.core.logging.legacy.Log
import net.thunderbird.core.logging.testing.TestLogger
import org.junit.After
import org.junit.Before
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
class StorageMigrationTo30Test {
private val database = createPreferencesDatabase()
private val migrationHelper = DefaultStorageMigrationHelper()
private val migration = StorageMigrationTo30(database, migrationHelper)

@Before
fun setUp() {
Log.logger = TestLogger()
}

@After
fun tearDown() {
database.close()
}

@Test
fun `migration should convert animations=true to ON`() {
migrationHelper.insertValue(database, ANIMATIONS_KEY, "true")

migration.migrateAnimationSetting()

assertThat(migrationHelper.readAllValues(database)).key(ANIMATIONS_KEY).isEqualTo("ON")
}

@Test
fun `migration should convert animations=false to OFF`() {
migrationHelper.insertValue(database, ANIMATIONS_KEY, "false")

migration.migrateAnimationSetting()

assertThat(migrationHelper.readAllValues(database)).key(ANIMATIONS_KEY).isEqualTo("OFF")
}

@Test
fun `migration should not insert animations key when missing`() {
migration.migrateAnimationSetting()

assertThat(migrationHelper.readAllValues(database)).doesNotContainKey(ANIMATIONS_KEY)
}

@Test
fun `migration should leave already migrated ON value unchanged`() {
migrationHelper.insertValue(database, ANIMATIONS_KEY, "ON")

migration.migrateAnimationSetting()

assertThat(migrationHelper.readAllValues(database)).key(ANIMATIONS_KEY).isEqualTo("ON")
}

@Test
fun `migration should leave already migrated OFF value unchanged`() {
migrationHelper.insertValue(database, ANIMATIONS_KEY, "OFF")

migration.migrateAnimationSetting()

assertThat(migrationHelper.readAllValues(database)).key(ANIMATIONS_KEY).isEqualTo("OFF")
}

private companion object {
const val ANIMATIONS_KEY = "animations"
}
}
1 change: 1 addition & 0 deletions legacy/ui/base/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ dependencies {
implementation(projects.legacy.core)

api(projects.core.ui.theme.manager)
api(projects.core.ui.animation.manager)

api(libs.androidx.appcompat)
api(libs.androidx.activity)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.fsck.k9.ui.base

import com.fsck.k9.ui.base.locale.SystemLocaleManager
import net.thunderbird.core.ui.animation.manager.AnimationManager
import net.thunderbird.core.ui.animation.manager.DefaultAnimationManager
import net.thunderbird.core.ui.theme.manager.ThemeManager
import org.koin.core.qualifier.named
import org.koin.dsl.bind
Expand All @@ -16,6 +18,9 @@ val uiBaseModule = module {
appCoroutineScope = get(named("AppCoroutineScope")),
)
} bind ThemeManagerApi::class
single<AnimationManager> {
DefaultAnimationManager(visualSettingsPreferenceManager = get())
}
single { AppLanguageManager(systemLocaleManager = get(), displayCoreSettingsPreferenceManager = get()) }
single { SystemLocaleManager(context = get()) }
}
Loading