Merge remote-tracking branch 'upstream/main' into feat/materialYouCompat

Signed-off-by: Guillermo Villafuerte <gvillafu@comunidad.unam.mx>

# Conflicts:
#	app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt
#	app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/Custom.kt
#	core/preferences/src/main/java/de/mm20/launcher2/preferences/DataStore.kt
#	core/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt
This commit is contained in:
Guillermo Villafuerte
2024-01-19 18:10:42 -06:00
211 changed files with 4394 additions and 3229 deletions
+2
View File
@@ -4,6 +4,7 @@ plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.protobuf)
alias(libs.plugins.kotlin.plugin.serialization)
}
android {
@@ -57,6 +58,7 @@ protobuf {
dependencies {
implementation(libs.kotlin.stdlib)
implementation(libs.kotlinx.serialization.json)
implementation(libs.androidx.core)
implementation(libs.androidx.appcompat)
api(libs.androidx.datastore)
+2 -2
View File
@@ -1,7 +1,7 @@
-keepclassmembers class de.mm20.launcher2.preferences.Settings {
-keepclassmembers class de.mm20.launcher2.preferences.LegacySettings {
<fields>;
}
-keepclassmembers class de.mm20.launcher2.preferences.Settings$* {
-keepclassmembers class de.mm20.launcher2.preferences.LegacySettings$* {
<fields>;
}
@@ -1,49 +0,0 @@
package de.mm20.launcher2.preferences
import android.content.Context
import androidx.datastore.core.DataMigration
import androidx.datastore.core.DataStore
import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
import androidx.datastore.dataStore
import de.mm20.launcher2.crashreporter.CrashReporter
import de.mm20.launcher2.preferences.migrations.*
typealias LauncherDataStore = DataStore<Settings>
internal val Context.dataStore: LauncherDataStore by dataStore(
fileName = "settings.pb",
serializer = SettingsSerializer,
produceMigrations = {
getMigrations(it)
},
corruptionHandler = ReplaceFileCorruptionHandler {
CrashReporter.logException(it)
Settings.getDefaultInstance()
}
)
internal const val SchemaVersion = 19
internal fun getMigrations(context: Context): List<DataMigration<Settings>> {
return listOf(
FactorySettingsMigration(context),
Migration_1_2(),
Migration_2_3(),
Migration_3_4(),
Migration_4_5(),
Migration_5_6(),
Migration_6_7(),
Migration_7_8(),
Migration_8_9(),
Migration_9_10(),
Migration_10_11(),
Migration_11_12(),
Migration_12_13(),
Migration_13_14(),
Migration_14_15(),
Migration_15_16(),
Migration_16_17(),
Migration_17_18(),
Migration_18_19(),
)
}
@@ -2,59 +2,58 @@ package de.mm20.launcher2.preferences
import android.content.Context
import de.mm20.launcher2.ktx.isAtLeastApiLevel
import de.mm20.launcher2.preferences.Settings.SearchBarSettings.SearchBarColors
import de.mm20.launcher2.preferences.LegacySettings.SearchBarSettings.SearchBarColors
import de.mm20.launcher2.preferences.ktx.toSettingsColorsScheme
import scheme.Scheme
import java.util.UUID
fun createFactorySettings(context: Context): Settings {
return Settings.newBuilder()
fun createFactorySettings(context: Context): LegacySettings {
return LegacySettings.newBuilder()
.setAppearance(
Settings.AppearanceSettings
LegacySettings.AppearanceSettings
.newBuilder()
.setTheme(Settings.AppearanceSettings.Theme.System)
.setTheme(LegacySettings.AppearanceSettings.Theme.System)
.setDimWallpaper(false)
.setBlurWallpaper(true)
.setBlurWallpaperRadius(32)
.setThemeId(UUID(0L, 0L).toString())
.setFont(Settings.AppearanceSettings.Font.Outfit)
.setForceCompatModeSystemColors(false)
.setFont(LegacySettings.AppearanceSettings.Font.Outfit)
.build()
)
.setWeather(
Settings.WeatherSettings
LegacySettings.WeatherSettings
.newBuilder()
.setProvider(Settings.WeatherSettings.WeatherProvider.MetNo)
.setProvider(LegacySettings.WeatherSettings.WeatherProvider.MetNo)
.setImperialUnits(context.resources.getBoolean(R.bool.default_imperialUnits))
.build()
)
.setMusicWidget(
Settings.MusicWidgetSettings
LegacySettings.MusicWidgetSettings
.newBuilder()
.build()
)
.setCalendarWidget(
Settings.CalendarWidgetSettings
LegacySettings.CalendarWidgetSettings
.newBuilder()
.setHideAlldayEvents(false)
)
.setClockWidget(
Settings.ClockWidgetSettings
LegacySettings.ClockWidgetSettings
.newBuilder()
.setLayout(Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical)
.setClockStyle(Settings.ClockWidgetSettings.ClockStyle.DigitalClock1)
.setColor(Settings.ClockWidgetSettings.ClockWidgetColors.Auto)
.setLayout(LegacySettings.ClockWidgetSettings.ClockWidgetLayout.Vertical)
.setClockStyle(LegacySettings.ClockWidgetSettings.ClockStyle.DigitalClock1)
.setColor(LegacySettings.ClockWidgetSettings.ClockWidgetColors.Auto)
.setAlarmPart(true)
.setBatteryPart(true)
.setMusicPart(true)
.setDatePart(true)
.setFavoritesPart(false)
.setFillHeight(true)
.setAlignment(Settings.ClockWidgetSettings.ClockWidgetAlignment.Bottom)
.setAlignment(LegacySettings.ClockWidgetSettings.ClockWidgetAlignment.Bottom)
.build()
)
.setFavorites(
Settings.FavoritesSettings
LegacySettings.FavoritesSettings
.newBuilder()
.setEnabled(true)
.setFrequentlyUsed(true)
@@ -62,7 +61,7 @@ fun createFactorySettings(context: Context): Settings {
.setEditButton(true)
)
.setFileSearch(
Settings.FilesSearchSettings
LegacySettings.FilesSearchSettings
.newBuilder()
.setLocalFiles(true)
.setNextcloud(false)
@@ -71,50 +70,50 @@ fun createFactorySettings(context: Context): Settings {
.setNextcloud(false)
)
.setContactsSearch(
Settings.ContactsSearchSettings
LegacySettings.ContactsSearchSettings
.newBuilder()
.setEnabled(true)
)
.setCalendarSearch(
Settings.CalendarSearchSettings
LegacySettings.CalendarSearchSettings
.newBuilder()
.setEnabled(true)
)
.setAppShortcutSearch(
Settings.AppShortcutSearchSettings
LegacySettings.AppShortcutSearchSettings
.newBuilder()
.setEnabled(true)
)
.setCalculatorSearch(
Settings.CalculatorSearchSettings
LegacySettings.CalculatorSearchSettings
.newBuilder()
.setEnabled(true)
)
.setUnitConverterSearch(
Settings.UnitConverterSearchSettings
LegacySettings.UnitConverterSearchSettings
.newBuilder()
.setEnabled(true)
.setCurrencies(true)
)
.setWikipediaSearch(
Settings.WikipediaSearchSettings
LegacySettings.WikipediaSearchSettings
.newBuilder()
.setEnabled(false)
.setImages(false)
.setCustomUrl("")
)
.setWebsiteSearch(
Settings.WebsiteSearchSettings
LegacySettings.WebsiteSearchSettings
.newBuilder()
.setEnabled(false)
)
.setWebSearch(
Settings.WebSearchSettings
LegacySettings.WebSearchSettings
.newBuilder()
.setEnabled(true)
)
.setBadges(
Settings.BadgeSettings
LegacySettings.BadgeSettings
.newBuilder()
.setNotifications(true)
.setCloudFiles(true)
@@ -122,15 +121,15 @@ fun createFactorySettings(context: Context): Settings {
.setSuspendedApps(true)
)
.setGrid(
Settings.GridSettings.newBuilder()
LegacySettings.GridSettings.newBuilder()
.setColumnCount(context.resources.getInteger(R.integer.config_columnCount))
.setIconSize(48)
.setShowLabels(true)
.build()
)
.setSearchBar(
Settings.SearchBarSettings.newBuilder()
.setSearchBarStyle(Settings.SearchBarSettings.SearchBarStyle.Transparent)
LegacySettings.SearchBarSettings.newBuilder()
.setSearchBarStyle(LegacySettings.SearchBarSettings.SearchBarStyle.Transparent)
.setAutoFocus(true)
.setLaunchOnEnter(true)
.setColor(SearchBarColors.Auto)
@@ -138,67 +137,67 @@ fun createFactorySettings(context: Context): Settings {
.build()
)
.setIcons(
Settings.IconSettings.newBuilder()
LegacySettings.IconSettings.newBuilder()
.setAdaptify(true)
.setShape(Settings.IconSettings.IconShape.PlatformDefault)
.setShape(LegacySettings.IconSettings.IconShape.PlatformDefault)
.setThemedIcons(false)
.setIconPack("")
.setIconPackThemed(true)
)
.setEasterEgg(false)
.setSystemBars(
Settings.SystemBarsSettings.newBuilder()
.setNavBarColor(Settings.SystemBarsSettings.SystemBarColors.Auto)
.setStatusBarColor(Settings.SystemBarsSettings.SystemBarColors.Auto)
LegacySettings.SystemBarsSettings.newBuilder()
.setNavBarColor(LegacySettings.SystemBarsSettings.SystemBarColors.Auto)
.setStatusBarColor(LegacySettings.SystemBarsSettings.SystemBarColors.Auto)
.setHideStatusBar(false)
.setHideNavBar(false)
)
.setCards(
Settings.CardSettings.newBuilder()
LegacySettings.CardSettings.newBuilder()
.setBorderWidth(0)
.setRadius(12)
.setOpacity(1f)
)
.setWidgets(
Settings.WidgetSettings.newBuilder()
LegacySettings.WidgetSettings.newBuilder()
.setEditButton(true)
)
.setLayout(
Settings.LayoutSettings.newBuilder()
.setBaseLayout(Settings.LayoutSettings.Layout.PullDown)
LegacySettings.LayoutSettings.newBuilder()
.setBaseLayout(LegacySettings.LayoutSettings.Layout.PullDown)
.setBottomSearchBar(false)
.setReverseSearchResults(false)
.setFixedRotation(false)
)
.setGestures(
Settings.GestureSettings.newBuilder()
LegacySettings.GestureSettings.newBuilder()
.setDoubleTap(
if (isAtLeastApiLevel(28)) {
Settings.GestureSettings.GestureAction.LockScreen
LegacySettings.GestureSettings.GestureAction.LockScreen
} else {
Settings.GestureSettings.GestureAction.None
LegacySettings.GestureSettings.GestureAction.None
})
.setLongPress(Settings.GestureSettings.GestureAction.None)
.setSwipeDown(Settings.GestureSettings.GestureAction.OpenNotificationDrawer)
.setSwipeLeft(Settings.GestureSettings.GestureAction.None)
.setSwipeRight(Settings.GestureSettings.GestureAction.None)
.setLongPress(LegacySettings.GestureSettings.GestureAction.None)
.setSwipeDown(LegacySettings.GestureSettings.GestureAction.OpenNotificationDrawer)
.setSwipeLeft(LegacySettings.GestureSettings.GestureAction.None)
.setSwipeRight(LegacySettings.GestureSettings.GestureAction.None)
)
.setResultOrdering(
Settings.SearchResultOrderingSettings.newBuilder()
.setOrdering(Settings.SearchResultOrderingSettings.Ordering.Weighted)
.setWeightFactor(Settings.SearchResultOrderingSettings.WeightFactor.Default)
LegacySettings.SearchResultOrderingSettings.newBuilder()
.setOrdering(LegacySettings.SearchResultOrderingSettings.Ordering.Weighted)
.setWeightFactor(LegacySettings.SearchResultOrderingSettings.WeightFactor.Default)
)
.setAnimations(
Settings.AnimationSettings.newBuilder()
LegacySettings.AnimationSettings.newBuilder()
.setCharging(true)
)
.build()
}
internal val DefaultCustomColorsBase: Settings.AppearanceSettings.CustomColors.BaseColors
internal val DefaultCustomColorsBase: LegacySettings.AppearanceSettings.CustomColors.BaseColors
get() {
val scheme = Scheme.light(0xFFACE330.toInt())
return Settings.AppearanceSettings.CustomColors.BaseColors.newBuilder()
return LegacySettings.AppearanceSettings.CustomColors.BaseColors.newBuilder()
.setAccent1(scheme.primary)
.setAccent2(scheme.secondary)
.setAccent3(scheme.tertiary)
@@ -208,13 +207,13 @@ internal val DefaultCustomColorsBase: Settings.AppearanceSettings.CustomColors.B
.build()
}
internal val DefaultLightCustomColorScheme: Settings.AppearanceSettings.CustomColors.Scheme
internal val DefaultLightCustomColorScheme: LegacySettings.AppearanceSettings.CustomColors.Scheme
get() {
val scheme = Scheme.light(0xFFACE330.toInt())
return scheme.toSettingsColorsScheme()
}
internal val DefaultDarkCustomColorScheme: Settings.AppearanceSettings.CustomColors.Scheme
internal val DefaultDarkCustomColorScheme: LegacySettings.AppearanceSettings.CustomColors.Scheme
get() {
val scheme = Scheme.dark(0xFFACE330.toInt())
return scheme.toSettingsColorsScheme()
@@ -1,65 +0,0 @@
package de.mm20.launcher2.preferences
import android.content.Context
import androidx.datastore.core.DataStoreFactory
import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
import de.mm20.launcher2.backup.Backupable
import de.mm20.launcher2.crashreporter.CrashReporter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.firstOrNull
import java.io.File
internal class LauncherStoreBackupComponent(
private val context: Context,
private val dataStore: LauncherDataStore
): Backupable {
override suspend fun backup(toDir: File) {
dataStore.export(toDir)
}
override suspend fun restore(fromDir: File) {
dataStore.import(context, fromDir)
}
}
suspend fun LauncherDataStore.export(toDir: File) {
val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
val backupDataStore = DataStoreFactory.create(
serializer = SettingsSerializer,
produceFile = {
File(toDir, "settings")
},
scope = scope
)
val settings = this.data.firstOrNull() ?: return
backupDataStore.updateData {
settings
}
scope.cancel()
}
suspend fun LauncherDataStore.import(context: Context, fromDir: File) {
val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
val backupDataStore = DataStoreFactory.create(
serializer = SettingsSerializer,
migrations = getMigrations(context),
corruptionHandler = ReplaceFileCorruptionHandler {
CrashReporter.logException(it)
Settings.getDefaultInstance()
},
produceFile = {
File(fromDir, "settings")
},
scope = scope
)
val settings = backupDataStore.data.firstOrNull() ?: return
this.updateData {
settings
}
scope.cancel()
}
@@ -0,0 +1,23 @@
package de.mm20.launcher2.preferences
import android.content.Context
import de.mm20.launcher2.preferences.migrations.Migration1
import de.mm20.launcher2.settings.BaseSettings
internal class LauncherDataStore(
private val context: Context,
legacyDataStore: LegacyDataStore,
): BaseSettings<LauncherSettingsData>(
context,
fileName = "settings.json",
serializer = LauncherSettingsDataSerializer,
migrations = listOf(Migration1(legacyDataStore)),
) {
val data
get() = context.dataStore.data
fun update(block: (LauncherSettingsData) -> LauncherSettingsData) {
updateData(block)
}
}
@@ -0,0 +1,333 @@
package de.mm20.launcher2.preferences
import android.content.Context
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import java.util.UUID
@Serializable
data class LauncherSettingsData(
val schemaVersion: Int = 1,
val uiColorScheme: ColorScheme = ColorScheme.System,
val uiTheme: ThemeDescriptor = ThemeDescriptor.Default,
val uiCompatModeColors: Boolean = false,
val uiFont: Font = Font.Outfit,
val uiBaseLayout: BaseLayout = BaseLayout.PullDown,
val uiOrientation: ScreenOrientation = ScreenOrientation.Auto,
val wallpaperDim: Boolean = false,
val wallpaperBlur: Boolean = true,
val wallpaperBlurRadius: Int = 32,
val mediaAllowList: Set<String> = emptySet(),
val mediaDenyList: Set<String> = emptySet(),
val clockWidgetCompact: Boolean = false,
val clockWidgetStyle: ClockWidgetStyle = ClockWidgetStyle.Digital1(),
val clockWidgetColors: ClockWidgetColors = ClockWidgetColors.Auto,
val clockWidgetAlarmPart: Boolean = true,
val clockWidgetBatteryPart: Boolean = true,
val clockWidgetMusicPart: Boolean = true,
val clockWidgetDatePart: Boolean = true,
val clockWidgetFillHeight: Boolean = true,
val clockWidgetAlignment: ClockWidgetAlignment = ClockWidgetAlignment.Bottom,
val homeScreenDock: Boolean = false,
val favoritesEnabled: Boolean = true,
val favoritesFrequentlyUsed: Boolean = true,
val favoritesFrequentlyUsedRows: Int = 1,
val favoritesEditButton: Boolean = true,
val fileSearchProviders: Set<String> = setOf("local"),
val contactSearchEnabled: Boolean = true,
val calendarSearchEnabled: Boolean = true,
val shortcutSearchEnabled: Boolean = true,
val calculatorEnabled: Boolean = true,
val unitConverterEnabled: Boolean = true,
val unitConverterCurrencies: Boolean = true,
val wikipediaSearchEnabled: Boolean = false,
val wikipediaSearchImages: Boolean = false,
val wikipediaCustomUrl: String? = null,
val websiteSearchEnabled: Boolean = false,
val badgesNotifications: Boolean = true,
val badgesSuspendedApps: Boolean = true,
val badgesCloudFiles: Boolean = true,
val badgesShortcuts: Boolean = true,
val badgesPlugins: Boolean = true,
val gridColumnCount: Int = 5,
val gridIconSize: Int = 48,
val gridLabels: Boolean = true,
val searchBarStyle: SearchBarStyle = SearchBarStyle.Transparent,
val searchBarColors: SearchBarColors = SearchBarColors.Auto,
val searchBarKeyboard: Boolean = true,
val searchLaunchOnEnter: Boolean = true,
val searchBarBottom: Boolean = false,
val searchBarFixed: Boolean = false,
val searchResultsReversed: Boolean = false,
val searchResultOrder: SearchResultOrder = SearchResultOrder.Weighted,
val rankingWeightFactor: WeightFactor = WeightFactor.Default,
val hiddenItemsShowButton: Boolean = true,
val iconsShape: IconShape = IconShape.PlatformDefault,
val iconsAdaptify: Boolean = false,
val iconsThemed: Boolean = false,
val iconsForceThemed: Boolean = false,
val iconsPack: String? = null,
val iconsPackThemed: Boolean = false,
val easterEgg: Boolean = false,
val systemBarsHideStatus: Boolean = false,
val systemBarsHideNav: Boolean = false,
val systemBarsStatusColors: SystemBarColors = SystemBarColors.Auto,
val systemBarsNavColors: SystemBarColors = SystemBarColors.Auto,
val surfacesOpacity: Float = 1f,
val surfacesRadius: Int = 24,
val surfacesBorderWidth: Int = 0,
val surfacesShape: SurfaceShape = SurfaceShape.Rounded,
val widgetsEditButton: Boolean = true,
val gesturesSwipeDown: GestureAction = GestureAction.Notifications,
val gesturesSwipeLeft: GestureAction = GestureAction.NoAction,
val gesturesSwipeRight: GestureAction = GestureAction.NoAction,
val gesturesDoubleTap: GestureAction = GestureAction.ScreenLock,
val gesturesLongPress: GestureAction = GestureAction.NoAction,
val gesturesHomeButton: GestureAction = GestureAction.NoAction,
val animationsCharging: Boolean = true,
val stateTagsMultiline: Boolean = false,
val weatherProvider: String = "metno",
val weatherAutoLocation: Boolean = true,
val weatherLocation: LatLon? = null,
val weatherLocationName: String? = null,
val weatherLastLocation: LatLon? = null,
val weatherLastUpdate: Long = 0L,
val weatherProviderSettings: Map<String, ProviderSettings> = emptyMap(),
val weatherImperialUnits: Boolean = false,
) {
constructor(
context: Context,
) : this(
weatherImperialUnits = context.resources.getBoolean(R.bool.default_imperialUnits),
gridColumnCount = context.resources.getInteger(R.integer.config_columnCount),
)
}
@Serializable
enum class ColorScheme {
Light,
Dark,
System,
}
@Serializable
enum class Font {
Outfit,
System,
}
@Serializable
sealed interface ThemeDescriptor {
@Serializable
@SerialName("default")
data object Default : ThemeDescriptor
@Serializable
@SerialName("bw")
data object BlackAndWhite : ThemeDescriptor
@Serializable
@SerialName("custom")
data class Custom(
val id: String,
) : ThemeDescriptor
}
@Serializable
sealed interface ClockWidgetStyle {
@Serializable
@SerialName("digital1")
data class Digital1(
val outlined: Boolean = false,
val variant: Variant = Variant.Default,
) : ClockWidgetStyle {
@Serializable
enum class Variant {
Default,
MDY,
OnePlus,
}
}
@Serializable
@SerialName("digital2")
data object Digital2 : ClockWidgetStyle
@Serializable
@SerialName("orbit")
data object Orbit : ClockWidgetStyle
@Serializable
@SerialName("analog")
data object Analog : ClockWidgetStyle
@Serializable
@SerialName("binary")
data object Binary : ClockWidgetStyle
@Serializable
@SerialName("empty")
data object Empty : ClockWidgetStyle
}
@Serializable
enum class ClockWidgetColors {
Auto,
Light,
Dark,
}
@Serializable
enum class ClockWidgetAlignment {
Top,
Center,
Bottom,
}
@Serializable
enum class SearchBarStyle {
Transparent,
Solid,
Hidden,
}
@Serializable
enum class SearchBarColors {
Auto,
Light,
Dark,
}
@Serializable
enum class IconShape {
PlatformDefault,
Circle,
Square,
RoundedSquare,
Triangle,
Squircle,
Hexagon,
Pentagon,
Teardrop,
Pebble,
EasterEgg,
}
@Serializable
enum class SystemBarColors {
Auto,
Light,
Dark,
}
@Serializable
enum class SurfaceShape {
Rounded,
Cut,
}
@Serializable
enum class BaseLayout {
PullDown,
Pager,
PagerReversed,
}
@Serializable
enum class ScreenOrientation {
Auto,
Portrait,
Landscape,
}
@Serializable
sealed interface GestureAction {
@Serializable
@SerialName("no_action")
data object NoAction : GestureAction
@Serializable
@SerialName("notifications")
data object Notifications : GestureAction
@Serializable
@SerialName("quick_settings")
data object QuickSettings : GestureAction
@Serializable
@SerialName("screen_lock")
data object ScreenLock : GestureAction
@Serializable
@SerialName("search")
data object Search : GestureAction
@Serializable
@SerialName("power_menu")
data object PowerMenu : GestureAction
@Serializable
@SerialName("recents")
data object Recents : GestureAction
@Serializable
@SerialName("launch_searchable")
data class Launch(val key: String?) : GestureAction
}
@Serializable
enum class SearchResultOrder {
Weighted,
Alphabetical,
LaunchCount,
}
@Serializable
enum class WeightFactor {
Default,
Low,
High,
}
@Serializable
data class LatLon(
val lat: Double,
val lon: Double,
)
@Serializable
data class ProviderSettings(
val locationId: String? = null,
val locationName: String? = null,
)
@@ -0,0 +1,38 @@
package de.mm20.launcher2.preferences
import androidx.datastore.core.CorruptionException
import androidx.datastore.core.Serializer
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import kotlinx.serialization.json.encodeToStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
internal object LauncherSettingsDataSerializer : Serializer<LauncherSettingsData> {
internal val json = Json {
ignoreUnknownKeys = true
encodeDefaults = true
}
override val defaultValue: LauncherSettingsData
get() = LauncherSettingsData(schemaVersion = 0)
override suspend fun readFrom(input: InputStream): LauncherSettingsData {
try {
return json.decodeFromStream(input)
} catch (e: IllegalArgumentException) {
throw (CorruptionException("Cannot read json.", e))
} catch (e: SerializationException) {
throw (CorruptionException("Cannot read json.", e))
} catch (e: IOException) {
throw (CorruptionException("Cannot read json.", e))
}
}
override suspend fun writeTo(t: LauncherSettingsData, output: OutputStream) {
json.encodeToStream(t, output)
}
}
@@ -0,0 +1,52 @@
package de.mm20.launcher2.preferences
import android.content.Context
import androidx.datastore.core.CorruptionException
import androidx.datastore.core.DataMigration
import androidx.datastore.core.DataStore
import androidx.datastore.core.Serializer
import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
import androidx.datastore.dataStore
import com.google.protobuf.InvalidProtocolBufferException
import de.mm20.launcher2.crashreporter.CrashReporter
import de.mm20.launcher2.preferences.migrations.*
import java.io.InputStream
import java.io.OutputStream
internal typealias LegacyDataStore = DataStore<LegacySettings>
internal val Context.legacyDataStore: LegacyDataStore by dataStore(
fileName = "settings.pb",
serializer = LegacySettingsSerializer,
produceMigrations = {
getMigrations(it)
},
corruptionHandler = ReplaceFileCorruptionHandler {
CrashReporter.logException(it)
LegacySettings.getDefaultInstance()
}
)
internal const val SchemaVersion = 18
internal fun getMigrations(context: Context): List<DataMigration<LegacySettings>> {
return listOf()
}
object LegacySettingsSerializer : Serializer<LegacySettings> {
override val defaultValue: LegacySettings = LegacySettings.getDefaultInstance()
override suspend fun readFrom(input: InputStream): LegacySettings {
try {
return LegacySettings.parseFrom(input)
} catch (e: InvalidProtocolBufferException) {
throw CorruptionException("Cannot read proto.", e)
}
}
override suspend fun writeTo(t: LegacySettings, output: OutputStream) {
t.writeTo(output)
}
}
@@ -1,11 +1,50 @@
package de.mm20.launcher2.preferences
import de.mm20.launcher2.backup.Backupable
import de.mm20.launcher2.preferences.search.ContactSearchSettings
import de.mm20.launcher2.preferences.media.MediaSettings
import de.mm20.launcher2.preferences.search.CalculatorSearchSettings
import de.mm20.launcher2.preferences.search.CalendarSearchSettings
import de.mm20.launcher2.preferences.search.FavoritesSettings
import de.mm20.launcher2.preferences.search.FileSearchSettings
import de.mm20.launcher2.preferences.search.RankingSettings
import de.mm20.launcher2.preferences.search.ShortcutSearchSettings
import de.mm20.launcher2.preferences.search.UnitConverterSettings
import de.mm20.launcher2.preferences.search.WebsiteSearchSettings
import de.mm20.launcher2.preferences.search.WikipediaSearchSettings
import de.mm20.launcher2.preferences.ui.BadgeSettings
import de.mm20.launcher2.preferences.ui.ClockWidgetSettings
import de.mm20.launcher2.preferences.ui.GestureSettings
import de.mm20.launcher2.preferences.ui.IconSettings
import de.mm20.launcher2.preferences.ui.SearchUiSettings
import de.mm20.launcher2.preferences.ui.UiSettings
import de.mm20.launcher2.preferences.ui.UiState
import de.mm20.launcher2.preferences.weather.WeatherSettings
import org.koin.android.ext.koin.androidContext
import org.koin.core.qualifier.named
import org.koin.dsl.module
val preferencesModule = module {
single { androidContext().dataStore }
factory<Backupable>(named<LauncherDataStore>()) { LauncherStoreBackupComponent(androidContext(), get()) }
single { androidContext().legacyDataStore }
single { LauncherDataStore(androidContext(), get()) }
factory<Backupable>(named<LauncherDataStore>()) { get<LauncherDataStore>() }
factory { MediaSettings(get()) }
factory { ContactSearchSettings(get()) }
factory { FileSearchSettings(get()) }
factory { UnitConverterSettings(get()) }
factory { BadgeSettings(get()) }
factory { UiSettings(get()) }
factory { ShortcutSearchSettings(get()) }
factory { FavoritesSettings(get()) }
factory { WikipediaSearchSettings(get()) }
factory { IconSettings(get()) }
factory { RankingSettings(get()) }
factory { CalendarSearchSettings(get()) }
factory { WebsiteSearchSettings(get()) }
factory { UiState(get()) }
factory { SearchUiSettings(get()) }
factory { WeatherSettings(get()) }
factory { GestureSettings(get()) }
factory { CalculatorSearchSettings(get()) }
factory { ClockWidgetSettings(get()) }
}
@@ -1,24 +0,0 @@
package de.mm20.launcher2.preferences
import androidx.datastore.core.CorruptionException
import androidx.datastore.core.Serializer
import com.google.protobuf.InvalidProtocolBufferException
import java.io.InputStream
import java.io.OutputStream
object SettingsSerializer : Serializer<Settings> {
override val defaultValue: Settings = Settings.getDefaultInstance()
override suspend fun readFrom(input: InputStream): Settings {
try {
return Settings.parseFrom(input)
} catch (e: InvalidProtocolBufferException) {
throw CorruptionException("Cannot read proto.", e)
}
}
override suspend fun writeTo(t: Settings, output: OutputStream) {
t.writeTo(output)
}
}
@@ -1,11 +1,11 @@
package de.mm20.launcher2.preferences.ktx
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.LegacySettings
import scheme.Scheme
fun Scheme.toSettingsColorsScheme(): Settings.AppearanceSettings.CustomColors.Scheme {
fun Scheme.toSettingsColorsScheme(): LegacySettings.AppearanceSettings.CustomColors.Scheme {
val scheme = this
return Settings.AppearanceSettings.CustomColors.Scheme.newBuilder()
return LegacySettings.AppearanceSettings.CustomColors.Scheme.newBuilder()
.setPrimary(scheme.primary)
.setSurfaceTint(scheme.primary)
.setOnPrimary(scheme.onPrimary)
@@ -0,0 +1,31 @@
package de.mm20.launcher2.preferences.media
import de.mm20.launcher2.preferences.LauncherDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
data class MediaSettingsData(
val allowList: Set<String>,
val denyList: Set<String>,
)
class MediaSettings internal constructor(
private val launcherDataStore: LauncherDataStore
) : Flow<MediaSettingsData> by (launcherDataStore.data.map {
MediaSettingsData(
it.mediaAllowList,
it.mediaDenyList
)
}) {
val allowList
get() = launcherDataStore.data.map { it.mediaAllowList }
fun setLists(allowList: Set<String>, denyList: Set<String>) {
launcherDataStore.update {
it.copy(mediaAllowList = allowList)
}
}
val denyList
get() = launcherDataStore.data.map { it.mediaDenyList }
}
@@ -4,22 +4,22 @@ import android.content.Context
import android.util.Log
import androidx.datastore.core.DataMigration
import de.mm20.launcher2.preferences.SchemaVersion
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.LegacySettings
import de.mm20.launcher2.preferences.createFactorySettings
class FactorySettingsMigration(private val context: Context): DataMigration<Settings> {
class FactorySettingsMigration(private val context: Context): DataMigration<LegacySettings> {
override suspend fun cleanUp() {
}
override suspend fun migrate(currentData: Settings): Settings {
override suspend fun migrate(currentData: LegacySettings): LegacySettings {
Log.d("MM20", "Initializing user settings…")
Log.d("MM20", "Done")
val defaults = createFactorySettings(context)
return defaults.toBuilder().setVersion(SchemaVersion).build()
}
override suspend fun shouldMigrate(currentData: Settings): Boolean {
override suspend fun shouldMigrate(currentData: LegacySettings): Boolean {
return currentData.version == 0
}
}
@@ -0,0 +1,254 @@
package de.mm20.launcher2.preferences.migrations
import androidx.datastore.core.DataMigration
import de.mm20.launcher2.preferences.BaseLayout
import de.mm20.launcher2.preferences.ClockWidgetAlignment
import de.mm20.launcher2.preferences.ClockWidgetColors
import de.mm20.launcher2.preferences.ClockWidgetStyle
import de.mm20.launcher2.preferences.ColorScheme
import de.mm20.launcher2.preferences.Font
import de.mm20.launcher2.preferences.GestureAction
import de.mm20.launcher2.preferences.IconShape
import de.mm20.launcher2.preferences.LauncherSettingsData
import de.mm20.launcher2.preferences.LegacyDataStore
import de.mm20.launcher2.preferences.LegacySettings
import de.mm20.launcher2.preferences.ScreenOrientation
import de.mm20.launcher2.preferences.SearchBarColors
import de.mm20.launcher2.preferences.SearchBarStyle
import de.mm20.launcher2.preferences.SearchResultOrder
import de.mm20.launcher2.preferences.SurfaceShape
import de.mm20.launcher2.preferences.SystemBarColors
import de.mm20.launcher2.preferences.ThemeDescriptor
import de.mm20.launcher2.preferences.WeightFactor
import kotlinx.coroutines.flow.first
import java.util.UUID
class Migration1(
private val legacyDataStore: LegacyDataStore,
) : DataMigration<LauncherSettingsData> {
override suspend fun cleanUp() {
}
override suspend fun shouldMigrate(currentData: LauncherSettingsData): Boolean {
return legacyDataStore.data.first().version > 0 && currentData.schemaVersion < 1
}
override suspend fun migrate(currentData: LauncherSettingsData): LauncherSettingsData {
val legacyData = legacyDataStore.data.first()
return currentData.copy(
schemaVersion = 1,
uiBaseLayout = when (legacyData.layout.baseLayout) {
LegacySettings.LayoutSettings.Layout.Pager -> BaseLayout.PullDown
LegacySettings.LayoutSettings.Layout.PagerReversed -> BaseLayout.PagerReversed
else -> BaseLayout.PullDown
},
gridColumnCount = legacyData.grid.columnCount,
animationsCharging = legacyData.animations.charging,
badgesCloudFiles = legacyData.badges.cloudFiles,
badgesNotifications = legacyData.badges.notifications,
badgesShortcuts = legacyData.badges.shortcuts,
badgesSuspendedApps = legacyData.badges.suspendedApps,
calculatorEnabled = legacyData.calculatorSearch.enabled,
calendarSearchEnabled = legacyData.calendarSearch.enabled,
clockWidgetAlarmPart = legacyData.clockWidget.alarmPart,
clockWidgetBatteryPart = legacyData.clockWidget.batteryPart,
clockWidgetDatePart = legacyData.clockWidget.datePart,
clockWidgetColors = when (legacyData.clockWidget.color) {
LegacySettings.ClockWidgetSettings.ClockWidgetColors.Light -> ClockWidgetColors.Light
LegacySettings.ClockWidgetSettings.ClockWidgetColors.Dark -> ClockWidgetColors.Dark
else -> ClockWidgetColors.Auto
},
clockWidgetAlignment = when (legacyData.clockWidget.alignment) {
LegacySettings.ClockWidgetSettings.ClockWidgetAlignment.Top -> ClockWidgetAlignment.Top
LegacySettings.ClockWidgetSettings.ClockWidgetAlignment.Center -> ClockWidgetAlignment.Center
else -> ClockWidgetAlignment.Bottom
},
homeScreenDock = legacyData.clockWidget.favoritesPart,
clockWidgetFillHeight = legacyData.clockWidget.fillHeight,
clockWidgetCompact = legacyData.clockWidget.layout == LegacySettings.ClockWidgetSettings.ClockWidgetLayout.Horizontal,
clockWidgetMusicPart = legacyData.clockWidget.musicPart,
clockWidgetStyle = when (legacyData.clockWidget.clockStyle) {
LegacySettings.ClockWidgetSettings.ClockStyle.DigitalClock1 -> ClockWidgetStyle.Digital1()
LegacySettings.ClockWidgetSettings.ClockStyle.DigitalClock2 -> ClockWidgetStyle.Digital2
LegacySettings.ClockWidgetSettings.ClockStyle.OrbitClock -> ClockWidgetStyle.Orbit
LegacySettings.ClockWidgetSettings.ClockStyle.BinaryClock -> ClockWidgetStyle.Binary
LegacySettings.ClockWidgetSettings.ClockStyle.AnalogClock -> ClockWidgetStyle.Analog
LegacySettings.ClockWidgetSettings.ClockStyle.EmptyClock -> ClockWidgetStyle.Empty
LegacySettings.ClockWidgetSettings.ClockStyle.DigitalClock1_MDY -> ClockWidgetStyle.Digital1(
variant = ClockWidgetStyle.Digital1.Variant.MDY
)
LegacySettings.ClockWidgetSettings.ClockStyle.DigitalClock1_Outlined -> ClockWidgetStyle.Digital1(
outlined = true
)
LegacySettings.ClockWidgetSettings.ClockStyle.DigitalClock1_OnePlus -> ClockWidgetStyle.Digital1(
variant = ClockWidgetStyle.Digital1.Variant.OnePlus
)
else -> ClockWidgetStyle.Digital1()
},
contactSearchEnabled = legacyData.contactsSearch.enabled,
easterEgg = legacyData.easterEgg,
favoritesEditButton = legacyData.favorites.editButton,
favoritesEnabled = legacyData.favorites.enabled,
favoritesFrequentlyUsed = legacyData.favorites.frequentlyUsed,
favoritesFrequentlyUsedRows = legacyData.favorites.frequentlyUsedRows,
rankingWeightFactor = when (legacyData.resultOrdering.weightFactor) {
LegacySettings.SearchResultOrderingSettings.WeightFactor.Low -> WeightFactor.Low
LegacySettings.SearchResultOrderingSettings.WeightFactor.High -> WeightFactor.High
else -> WeightFactor.Default
},
fileSearchProviders = buildSet {
if (legacyData.fileSearch.localFiles) add("local")
if (legacyData.fileSearch.nextcloud) add("nextcloud")
if (legacyData.fileSearch.gdrive) add("gdrive")
if (legacyData.fileSearch.onedrive) add("onedrive")
},
gesturesDoubleTap = makeGestureSettings(
legacyData.gestures.doubleTap,
legacyData.gestures.doubleTapApp
),
gesturesSwipeDown = makeGestureSettings(
legacyData.gestures.swipeDown,
legacyData.gestures.swipeDownApp
),
gesturesSwipeLeft = makeGestureSettings(
legacyData.gestures.swipeLeft,
legacyData.gestures.swipeLeftApp
),
gesturesSwipeRight = makeGestureSettings(
legacyData.gestures.swipeRight,
legacyData.gestures.swipeRightApp
),
gesturesLongPress = makeGestureSettings(
legacyData.gestures.longPress,
legacyData.gestures.longPressApp
),
gesturesHomeButton = makeGestureSettings(
legacyData.gestures.homeButton,
legacyData.gestures.homeButtonApp
),
gridIconSize = legacyData.grid.iconSize,
gridLabels = legacyData.grid.showLabels,
mediaAllowList = legacyData.musicWidget.allowListList.toSet(),
mediaDenyList = legacyData.musicWidget.denyListList.toSet(),
hiddenItemsShowButton = legacyData.searchBar.hiddenItemsButton,
iconsAdaptify = legacyData.icons.adaptify,
iconsForceThemed = legacyData.icons.forceThemed,
iconsPack = legacyData.icons.iconPack.takeIf { it.isNotBlank() },
iconsPackThemed = legacyData.icons.iconPackThemed,
iconsShape = when (legacyData.icons.shape) {
LegacySettings.IconSettings.IconShape.Circle -> IconShape.Circle
LegacySettings.IconSettings.IconShape.Square -> IconShape.Square
LegacySettings.IconSettings.IconShape.Squircle -> IconShape.Squircle
LegacySettings.IconSettings.IconShape.RoundedSquare -> IconShape.RoundedSquare
LegacySettings.IconSettings.IconShape.EasterEgg -> IconShape.EasterEgg
LegacySettings.IconSettings.IconShape.Hexagon -> IconShape.Hexagon
LegacySettings.IconSettings.IconShape.Triangle -> IconShape.Triangle
LegacySettings.IconSettings.IconShape.Pentagon -> IconShape.Pentagon
LegacySettings.IconSettings.IconShape.Teardrop -> IconShape.Teardrop
LegacySettings.IconSettings.IconShape.Pebble -> IconShape.Pebble
else -> IconShape.PlatformDefault
},
iconsThemed = legacyData.icons.themedIcons,
searchBarBottom = legacyData.layout.bottomSearchBar,
searchBarColors = when (legacyData.systemBars.statusBarColor) {
LegacySettings.SystemBarsSettings.SystemBarColors.Light -> SearchBarColors.Light
LegacySettings.SystemBarsSettings.SystemBarColors.Dark -> SearchBarColors.Dark
else -> SearchBarColors.Auto
},
searchBarFixed = legacyData.layout.bottomSearchBar,
searchBarKeyboard = legacyData.searchBar.autoFocus,
searchBarStyle = when (legacyData.searchBar.searchBarStyle) {
LegacySettings.SearchBarSettings.SearchBarStyle.Hidden -> SearchBarStyle.Hidden
LegacySettings.SearchBarSettings.SearchBarStyle.Solid -> SearchBarStyle.Solid
else -> SearchBarStyle.Transparent
},
searchLaunchOnEnter = legacyData.searchBar.launchOnEnter,
searchResultOrder = when (legacyData.resultOrdering.ordering) {
LegacySettings.SearchResultOrderingSettings.Ordering.Alphabetic -> SearchResultOrder.Alphabetical
LegacySettings.SearchResultOrderingSettings.Ordering.LaunchCount -> SearchResultOrder.LaunchCount
else -> SearchResultOrder.Weighted
},
searchResultsReversed = legacyData.layout.reverseSearchResults,
shortcutSearchEnabled = legacyData.appShortcutSearch.enabled,
stateTagsMultiline = legacyData.ui.searchTagsMultiline,
surfacesBorderWidth = legacyData.cards.borderWidth,
surfacesOpacity = legacyData.cards.opacity,
surfacesRadius = legacyData.cards.radius,
surfacesShape = when (legacyData.cards.shape) {
LegacySettings.CardSettings.Shape.Cut -> SurfaceShape.Cut
else -> SurfaceShape.Rounded
},
systemBarsHideNav = legacyData.systemBars.hideNavBar,
systemBarsHideStatus = legacyData.systemBars.hideStatusBar,
systemBarsNavColors = when (legacyData.systemBars.navBarColor) {
LegacySettings.SystemBarsSettings.SystemBarColors.Light -> SystemBarColors.Light
LegacySettings.SystemBarsSettings.SystemBarColors.Dark -> SystemBarColors.Dark
else -> SystemBarColors.Auto
},
systemBarsStatusColors = when (legacyData.systemBars.statusBarColor) {
LegacySettings.SystemBarsSettings.SystemBarColors.Light -> SystemBarColors.Light
LegacySettings.SystemBarsSettings.SystemBarColors.Dark -> SystemBarColors.Dark
else -> SystemBarColors.Auto
},
uiColorScheme = when (legacyData.appearance.theme) {
LegacySettings.AppearanceSettings.Theme.Light -> ColorScheme.Light
LegacySettings.AppearanceSettings.Theme.Dark -> ColorScheme.Dark
else -> ColorScheme.System
},
uiFont = when (legacyData.appearance.font) {
LegacySettings.AppearanceSettings.Font.SystemDefault -> Font.System
else -> Font.Outfit
},
uiOrientation = when (legacyData.layout.fixedRotation) {
true -> ScreenOrientation.Portrait
else -> ScreenOrientation.Auto
},
uiTheme = when (legacyData.appearance.themeId) {
UUID(0L, 0L).toString() -> ThemeDescriptor.Default
UUID(0L, 1L).toString() -> ThemeDescriptor.BlackAndWhite
else -> ThemeDescriptor.Custom(legacyData.appearance.themeId)
},
unitConverterCurrencies = legacyData.unitConverterSearch.currencies,
unitConverterEnabled = legacyData.unitConverterSearch.enabled,
wallpaperBlur = legacyData.appearance.blurWallpaper,
weatherImperialUnits = legacyData.weather.imperialUnits,
wallpaperBlurRadius = legacyData.appearance.blurWallpaperRadius,
wallpaperDim = legacyData.appearance.dimWallpaper,
weatherProvider = when (legacyData.weather.provider) {
LegacySettings.WeatherSettings.WeatherProvider.MetNo -> "metno"
LegacySettings.WeatherSettings.WeatherProvider.OpenWeatherMap -> "owm"
LegacySettings.WeatherSettings.WeatherProvider.Here -> "here"
LegacySettings.WeatherSettings.WeatherProvider.BrightSky -> "dwd"
else -> "metno"
},
websiteSearchEnabled = legacyData.websiteSearch.enabled,
widgetsEditButton = legacyData.widgets.editButton,
wikipediaCustomUrl = legacyData.wikipediaSearch.customUrl.takeIf { it.isNotBlank() },
wikipediaSearchEnabled = legacyData.wikipediaSearch.enabled,
wikipediaSearchImages = legacyData.wikipediaSearch.images,
)
}
private fun makeGestureSettings(
gesture: LegacySettings.GestureSettings.GestureAction,
key: String
): GestureAction {
return when (gesture) {
LegacySettings.GestureSettings.GestureAction.OpenSearch -> GestureAction.Search
LegacySettings.GestureSettings.GestureAction.OpenNotificationDrawer -> GestureAction.Notifications
LegacySettings.GestureSettings.GestureAction.LockScreen -> GestureAction.ScreenLock
LegacySettings.GestureSettings.GestureAction.OpenQuickSettings -> GestureAction.QuickSettings
LegacySettings.GestureSettings.GestureAction.OpenRecents -> GestureAction.Recents
LegacySettings.GestureSettings.GestureAction.OpenPowerDialog -> GestureAction.PowerMenu
LegacySettings.GestureSettings.GestureAction.LaunchApp -> GestureAction.Launch(
key
)
else -> GestureAction.NoAction
}
}
}
@@ -1,9 +1,9 @@
package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.LegacySettings
class Migration_10_11: VersionedMigration(10, 11) {
override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder {
override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder {
return builder.setAppearance(
builder.appearance.toBuilder()
.setCustomColors(
@@ -1,12 +1,12 @@
package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.ktx.isAtLeastApiLevel
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.Settings.GestureSettings
import de.mm20.launcher2.preferences.Settings.LayoutSettings
import de.mm20.launcher2.preferences.LegacySettings
import de.mm20.launcher2.preferences.LegacySettings.GestureSettings
import de.mm20.launcher2.preferences.LegacySettings.LayoutSettings
class Migration_11_12: VersionedMigration(11, 12) {
override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder {
override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder {
val oldLayout = builder.appearance.layout
when(oldLayout) {
LayoutSettings.Layout.Pager -> {
@@ -1,10 +1,10 @@
package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.Settings.SearchResultOrderingSettings.WeightFactor
import de.mm20.launcher2.preferences.LegacySettings
import de.mm20.launcher2.preferences.LegacySettings.SearchResultOrderingSettings.WeightFactor
class Migration_12_13: VersionedMigration(12, 13) {
override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder {
override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder {
return builder
.setClockWidget(
builder.clockWidget.toBuilder()
@@ -1,9 +1,9 @@
package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.LegacySettings
class Migration_13_14 : VersionedMigration(13, 14) {
override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder {
override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder {
return builder
.setIcons(
builder.icons.toBuilder()
@@ -1,9 +1,9 @@
package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.LegacySettings
class Migration_14_15: VersionedMigration(14, 15) {
override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder {
override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder {
return builder.setSearchBar(
builder.searchBar.toBuilder()
.setHiddenItemsButton(true)
@@ -1,11 +1,11 @@
package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.CustomColors.Scheme
import de.mm20.launcher2.preferences.LegacySettings
import de.mm20.launcher2.preferences.LegacySettings.AppearanceSettings.CustomColors.Scheme
import palettes.TonalPalette
class Migration_15_16 : VersionedMigration(15, 16) {
override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder {
override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder {
return builder.setAppearance(
builder.appearance.toBuilder()
.setCustomColors(
@@ -1,16 +1,16 @@
package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.LegacySettings
import java.util.UUID
class Migration_16_17: VersionedMigration(16, 17) {
override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder {
override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder {
return builder.setAppearance(
builder.appearance.toBuilder()
.setThemeId(
when(builder.appearance.colorScheme) {
Settings.AppearanceSettings.ColorScheme.BlackAndWhite -> UUID(0L, 1L)
Settings.AppearanceSettings.ColorScheme.Custom -> UUID(1L, 1L)
LegacySettings.AppearanceSettings.ColorScheme.BlackAndWhite -> UUID(0L, 1L)
LegacySettings.AppearanceSettings.ColorScheme.Custom -> UUID(1L, 1L)
else -> UUID(0L, 0L)
}.toString()
)
@@ -1,15 +1,15 @@
package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.LegacySettings
class Migration_17_18 : VersionedMigration(17, 18) {
override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder {
override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder {
return builder
.setAppearance(builder.appearance.toBuilder()
.setBlurWallpaperRadius(32)
)
.setClockWidget(builder.clockWidget.toBuilder()
.setAlignment(Settings.ClockWidgetSettings.ClockWidgetAlignment.Bottom)
.setAlignment(LegacySettings.ClockWidgetSettings.ClockWidgetAlignment.Bottom)
)
}
}
@@ -1,12 +0,0 @@
package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.Settings
class Migration_18_19 : VersionedMigration(18, 19) {
override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder {
return builder
.setAppearance(builder.appearance.toBuilder()
.setForceCompatModeSystemColors(false)
)
}
}
@@ -1,10 +1,10 @@
package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.LegacySettings
class Migration_1_2: VersionedMigration(1, 2) {
override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder {
override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder {
return builder.setSystemBars(
builder.systemBars.toBuilder()
.setHideNavBar(false)
@@ -1,9 +1,9 @@
package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.LegacySettings
class Migration_2_3: VersionedMigration(2, 3) {
override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder {
override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder {
return builder.setClockWidget(
builder.clockWidget.toBuilder()
.setAlarmPart(true)
@@ -1,11 +1,11 @@
package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.LegacySettings
class Migration_3_4: VersionedMigration(3, 4) {
override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder {
override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder {
return builder.setAppShortcutSearch(
Settings.AppShortcutSearchSettings.newBuilder()
LegacySettings.AppShortcutSearchSettings.newBuilder()
.setEnabled(true)
)
}
@@ -1,9 +1,9 @@
package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.LegacySettings
class Migration_4_5: VersionedMigration(4, 5) {
override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder {
override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder {
return builder.setGrid(
builder.grid.toBuilder()
.setIconSize(48)
@@ -3,13 +3,14 @@ package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.DefaultCustomColorsBase
import de.mm20.launcher2.preferences.DefaultDarkCustomColorScheme
import de.mm20.launcher2.preferences.DefaultLightCustomColorScheme
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.LegacySettings
class Migration_5_6: VersionedMigration(5, 6) {
override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder {
override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder {
return builder.setAppearance(
builder.appearance.toBuilder()
.setCustomColors(Settings.AppearanceSettings.CustomColors.newBuilder()
.setCustomColors(
LegacySettings.AppearanceSettings.CustomColors.newBuilder()
.setAdvancedMode(false)
.setBaseColors(DefaultCustomColorsBase)
.setLightScheme(DefaultLightCustomColorScheme)
@@ -1,9 +1,9 @@
package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.LegacySettings
class Migration_6_7 : VersionedMigration(6, 7) {
override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder {
override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder {
return builder.setIcons(
builder.icons.toBuilder()
.setAdaptify(true)
@@ -1,9 +1,9 @@
package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.LegacySettings
class Migration_7_8: VersionedMigration(7, 8) {
override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder {
override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder {
return builder.setAppearance(
builder.appearance.toBuilder()
.setBlurWallpaper(true)
@@ -1,9 +1,9 @@
package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.LegacySettings
class Migration_8_9: VersionedMigration(8, 9) {
override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder {
override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder {
return builder
.setClockWidget(
builder.clockWidget.toBuilder()
@@ -1,9 +1,9 @@
package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.LegacySettings
class Migration_9_10 : VersionedMigration(9, 10) {
override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder {
override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder {
return builder
.setFavorites(
builder.favorites.toBuilder()
@@ -12,7 +12,7 @@ class Migration_9_10 : VersionedMigration(9, 10) {
.setEditButton(true)
)
.setWidgets(
Settings.WidgetSettings.newBuilder()
LegacySettings.WidgetSettings.newBuilder()
.setEditButton(true)
)
}
@@ -2,15 +2,15 @@ package de.mm20.launcher2.preferences.migrations
import androidx.datastore.core.DataMigration
import de.mm20.launcher2.preferences.SchemaVersion
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.LegacySettings
abstract class VersionedMigration(
private val fromVersion: Int,
private val toVersion: Int
) : DataMigration<Settings> {
abstract suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder
) : DataMigration<LegacySettings> {
abstract suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder
final override suspend fun migrate(currentData: Settings): Settings {
final override suspend fun migrate(currentData: LegacySettings): LegacySettings {
val builder = currentData.toBuilder()
applyMigrations(builder)
builder.version = toVersion
@@ -19,7 +19,7 @@ abstract class VersionedMigration(
override suspend fun cleanUp() {}
override suspend fun shouldMigrate(currentData: Settings): Boolean {
override suspend fun shouldMigrate(currentData: LegacySettings): Boolean {
return currentData.version <= fromVersion && SchemaVersion >= toVersion
}
}
@@ -0,0 +1,18 @@
package de.mm20.launcher2.preferences.search
import de.mm20.launcher2.preferences.LauncherDataStore
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
class CalculatorSearchSettings internal constructor(
private val dataStore: LauncherDataStore,
){
val enabled
get() = dataStore.data.map { it.calculatorEnabled }.distinctUntilChanged()
fun setEnabled(enabled: Boolean) {
dataStore.update {
it.copy(calculatorEnabled = enabled)
}
}
}
@@ -0,0 +1,17 @@
package de.mm20.launcher2.preferences.search
import de.mm20.launcher2.preferences.LauncherDataStore
import kotlinx.coroutines.flow.map
class CalendarSearchSettings internal constructor(
private val dataStore: LauncherDataStore,
){
val enabled
get() = dataStore.data.map { it.calendarSearchEnabled }
fun setEnabled(enabled: Boolean) {
dataStore.update {
it.copy(calendarSearchEnabled = enabled)
}
}
}
@@ -0,0 +1,15 @@
package de.mm20.launcher2.preferences.search
import de.mm20.launcher2.preferences.LauncherDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
class ContactSearchSettings internal constructor(private val dataStore: LauncherDataStore) {
val enabled: Flow<Boolean>
get() = dataStore.data.map { it.contactSearchEnabled }.distinctUntilChanged()
fun setEnabled(enabled: Boolean) {
dataStore.update { it.copy(contactSearchEnabled = enabled) }
}
}
@@ -0,0 +1,44 @@
package de.mm20.launcher2.preferences.search
import de.mm20.launcher2.preferences.LauncherDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
data class FavoritesSettingsData(
val columns: Int,
val frequentlyUsed: Boolean,
val frequentlyUsedRows: Int,
)
class FavoritesSettings internal constructor(
private val dataStore: LauncherDataStore,
) : Flow<FavoritesSettingsData> by (dataStore.data.map {
FavoritesSettingsData(
columns = it.favoritesFrequentlyUsedRows,
frequentlyUsed = it.favoritesFrequentlyUsed,
frequentlyUsedRows = it.favoritesFrequentlyUsedRows,
)
}.distinctUntilChanged()) {
val showEditButton
get() = dataStore.data.map { it.favoritesEditButton }.distinctUntilChanged()
fun setShowEditButton(showEditButton: Boolean) {
dataStore.update { it.copy(favoritesEditButton = showEditButton) }
}
val frequentlyUsed: Flow<Boolean>
get() = dataStore.data.map { it.favoritesFrequentlyUsed }.distinctUntilChanged()
fun setFrequentlyUsed(frequentlyUsed: Boolean) {
dataStore.update { it.copy(favoritesFrequentlyUsed = frequentlyUsed) }
}
val frequentlyUsedRows: Flow<Int>
get() = dataStore.data.map { it.favoritesFrequentlyUsedRows }.distinctUntilChanged()
fun setFrequentlyUsedRows(frequentlyUsedRows: Int) {
dataStore.update { it.copy(favoritesFrequentlyUsedRows = frequentlyUsedRows) }
}
}
@@ -0,0 +1,77 @@
package de.mm20.launcher2.preferences.search
import de.mm20.launcher2.preferences.LauncherDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
class FileSearchSettings internal constructor(
private val launcherDataStore: LauncherDataStore,
) {
val enabledProviders: Flow<Set<String>>
get() = launcherDataStore.data.map { it.fileSearchProviders }
val localFiles
get() = launcherDataStore.data.map { it.fileSearchProviders.contains("local") }
fun setLocalFiles(localFiles: Boolean) {
launcherDataStore.update {
if (localFiles) {
it.copy(fileSearchProviders = it.fileSearchProviders + "local")
} else {
it.copy(fileSearchProviders = it.fileSearchProviders - "local")
}
}
}
val gdriveFiles
get() = launcherDataStore.data.map { it.fileSearchProviders.contains("gdrive") }
fun setGdriveFiles(gdriveFiles: Boolean) {
launcherDataStore.update {
if (gdriveFiles) {
it.copy(fileSearchProviders = it.fileSearchProviders + "gdrive")
} else {
it.copy(fileSearchProviders = it.fileSearchProviders - "gdrive")
}
}
}
val nextcloudFiles
get() = launcherDataStore.data.map { it.fileSearchProviders.contains("nextcloud") }
fun setNextcloudFiles(nextcloudFiles: Boolean) {
launcherDataStore.update {
if (nextcloudFiles) {
it.copy(fileSearchProviders = it.fileSearchProviders + "nextcloud")
} else {
it.copy(fileSearchProviders = it.fileSearchProviders - "nextcloud")
}
}
}
val owncloudFiles
get() = launcherDataStore.data.map { it.fileSearchProviders.contains("owncloud") }
fun setOwncloudFiles(owncloudFiles: Boolean) {
launcherDataStore.update {
if (owncloudFiles) {
it.copy(fileSearchProviders = it.fileSearchProviders + "owncloud")
} else {
it.copy(fileSearchProviders = it.fileSearchProviders - "owncloud")
}
}
}
val enabledPlugins: Flow<Set<String>>
get() = launcherDataStore.data.map { it.fileSearchProviders - "local" - "gdrive" - "nextcloud" - "owncloud" }
fun setPluginEnabled(authority: String, enabled: Boolean) {
launcherDataStore.update {
if (enabled) {
it.copy(fileSearchProviders = it.fileSearchProviders + authority)
} else {
it.copy(fileSearchProviders = it.fileSearchProviders - authority)
}
}
}
}
@@ -0,0 +1,19 @@
package de.mm20.launcher2.preferences.search
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.WeightFactor
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
class RankingSettings internal constructor(
private val launcherDataStore: LauncherDataStore,
){
val weightFactor
get() = launcherDataStore.data.map { it.rankingWeightFactor }.distinctUntilChanged()
fun setWeightFactor(weightFactor: WeightFactor) {
launcherDataStore.update {
it.copy(rankingWeightFactor = weightFactor)
}
}
}
@@ -0,0 +1,18 @@
package de.mm20.launcher2.preferences.search
import de.mm20.launcher2.preferences.LauncherDataStore
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
class ShortcutSearchSettings internal constructor(
private val dataStore: LauncherDataStore
) {
val enabled
get() = dataStore.data.map { it.shortcutSearchEnabled }.distinctUntilChanged()
fun setEnabled(enabled: Boolean) {
dataStore.update {
it.copy(shortcutSearchEnabled = enabled)
}
}
}
@@ -0,0 +1,34 @@
package de.mm20.launcher2.preferences.search
import de.mm20.launcher2.preferences.LauncherDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
data class UnitConverterSettingsData(
val enabled: Boolean,
val currencies: Boolean,
)
class UnitConverterSettings internal constructor(
private val dataStore: LauncherDataStore,
) : Flow<UnitConverterSettingsData> by (dataStore.data.map {
UnitConverterSettingsData(
enabled = it.unitConverterEnabled,
currencies = it.unitConverterCurrencies,
)
}.distinctUntilChanged()) {
val enabled: Flow<Boolean>
get() = dataStore.data.map { it.unitConverterEnabled }.distinctUntilChanged()
fun setEnabled(enabled: Boolean) {
dataStore.update { it.copy(unitConverterEnabled = enabled) }
}
val currencies: Flow<Boolean>
get() = dataStore.data.map { it.unitConverterCurrencies }.distinctUntilChanged()
fun setCurrencies(currencies: Boolean) {
dataStore.update { it.copy(unitConverterCurrencies = currencies) }
}
}
@@ -0,0 +1,17 @@
package de.mm20.launcher2.preferences.search
import de.mm20.launcher2.preferences.LauncherDataStore
import kotlinx.coroutines.flow.map
class WebsiteSearchSettings internal constructor(
private val dataStore: LauncherDataStore,
){
val enabled
get() = dataStore.data.map { it.websiteSearchEnabled }
fun setEnabled(enabled: Boolean) {
dataStore.update {
it.copy(websiteSearchEnabled = enabled)
}
}
}
@@ -0,0 +1,27 @@
package de.mm20.launcher2.preferences.search
import de.mm20.launcher2.preferences.LauncherDataStore
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
class WikipediaSearchSettings internal constructor(
private val dataStore: LauncherDataStore
) {
val enabled
get() = dataStore.data.map { it.wikipediaSearchEnabled }.distinctUntilChanged()
fun setEnabled(enabled: Boolean) {
dataStore.update {
it.copy(wikipediaSearchEnabled = enabled)
}
}
val customUrl
get() = dataStore.data.map { it.wikipediaCustomUrl }.distinctUntilChanged()
fun setCustomUrl(customUrl: String?) {
dataStore.update {
it.copy(wikipediaCustomUrl = customUrl?.takeIf { it.isNotBlank() })
}
}
}
@@ -0,0 +1,71 @@
package de.mm20.launcher2.preferences.ui
import de.mm20.launcher2.preferences.LauncherDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
data class BadgeSettingsData(
val notifications: Boolean = true,
val suspendedApps: Boolean = true,
val cloudFiles: Boolean = true,
val shortcuts: Boolean = true,
val plugins: Boolean = true,
)
class BadgeSettings internal constructor(
private val launcherDataStore: LauncherDataStore,
) : Flow<BadgeSettingsData> by (launcherDataStore.data.map {
BadgeSettingsData(
notifications = it.badgesNotifications,
suspendedApps = it.badgesSuspendedApps,
cloudFiles = it.badgesCloudFiles,
shortcuts = it.badgesShortcuts,
plugins = it.badgesPlugins,
)
}) {
val notifications
get() = launcherDataStore.data.map { it.badgesNotifications }
fun setNotifications(notifications: Boolean) {
launcherDataStore.update {
it.copy(badgesNotifications = notifications)
}
}
val suspendedApps
get() = launcherDataStore.data.map { it.badgesSuspendedApps }
fun setSuspendedApps(suspendedApps: Boolean) {
launcherDataStore.update {
it.copy(badgesSuspendedApps = suspendedApps)
}
}
val cloudFiles
get() = launcherDataStore.data.map { it.badgesCloudFiles }
fun setCloudFiles(cloudFiles: Boolean) {
launcherDataStore.update {
it.copy(badgesCloudFiles = cloudFiles)
}
}
val shortcuts
get() = launcherDataStore.data.map { it.badgesShortcuts }
fun setShortcuts(shortcuts: Boolean) {
launcherDataStore.update {
it.copy(badgesShortcuts = shortcuts)
}
}
val plugins
get() = launcherDataStore.data.map { it.badgesPlugins }
fun setPlugins(plugins: Boolean) {
launcherDataStore.update {
it.copy(badgesPlugins = plugins)
}
}
}
@@ -0,0 +1,107 @@
package de.mm20.launcher2.preferences.ui
import de.mm20.launcher2.preferences.ClockWidgetAlignment
import de.mm20.launcher2.preferences.ClockWidgetColors
import de.mm20.launcher2.preferences.ClockWidgetStyle
import de.mm20.launcher2.preferences.LauncherDataStore
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
data class ClockWidgetParts(
val date: Boolean,
val music: Boolean = false,
val battery: Boolean = false,
val alarm: Boolean = false,
)
class ClockWidgetSettings internal constructor(
private val launcherDataStore: LauncherDataStore,
) {
val compact
get() = launcherDataStore.data.map { it.clockWidgetCompact }
fun setCompact(compact: Boolean) {
launcherDataStore.update {
it.copy(clockWidgetCompact = compact)
}
}
val parts
get() = launcherDataStore.data.map {
ClockWidgetParts(
date = it.clockWidgetDatePart,
music = it.clockWidgetMusicPart,
battery = it.clockWidgetBatteryPart,
alarm = it.clockWidgetAlarmPart,
)
}.distinctUntilChanged()
fun setDatePart(datePart: Boolean) {
launcherDataStore.update {
it.copy(clockWidgetDatePart = datePart)
}
}
fun setMusicPart(musicPart: Boolean) {
launcherDataStore.update {
it.copy(clockWidgetMusicPart = musicPart)
}
}
fun setBatteryPart(batteryPart: Boolean) {
launcherDataStore.update {
it.copy(clockWidgetBatteryPart = batteryPart)
}
}
fun setAlarmPart(alarmPart: Boolean) {
launcherDataStore.update {
it.copy(clockWidgetAlarmPart = alarmPart)
}
}
val fillHeight
get() = launcherDataStore.data.map { it.clockWidgetFillHeight }
fun setFillHeight(fillHeight: Boolean) {
launcherDataStore.update {
it.copy(clockWidgetFillHeight = fillHeight)
}
}
val dock
get() = launcherDataStore.data.map { it.homeScreenDock }
fun setDock(dock: Boolean) {
launcherDataStore.update {
it.copy(homeScreenDock = dock)
}
}
val alignment
get() = launcherDataStore.data.map { it.clockWidgetAlignment }
fun setAlignment(alignment: ClockWidgetAlignment) {
launcherDataStore.update {
it.copy(clockWidgetAlignment = alignment)
}
}
val clockStyle
get() = launcherDataStore.data.map { it.clockWidgetStyle }
fun setClockStyle(clockStyle: ClockWidgetStyle) {
launcherDataStore.update {
it.copy(clockWidgetStyle = clockStyle)
}
}
fun setColor(color: ClockWidgetColors) {
launcherDataStore.update {
it.copy(clockWidgetColors = color)
}
}
val color
get() = launcherDataStore.data.map { it.clockWidgetColors }
}
@@ -0,0 +1,87 @@
package de.mm20.launcher2.preferences.ui
import de.mm20.launcher2.preferences.GestureAction
import de.mm20.launcher2.preferences.LauncherDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
data class GestureSettingsData(
val swipeDown: GestureAction,
val swipeLeft: GestureAction,
val swipeRight: GestureAction,
val doubleTap: GestureAction,
val longPress: GestureAction,
val homeButton: GestureAction,
)
class GestureSettings internal constructor(
private val dataStore: LauncherDataStore,
): Flow<GestureSettingsData> by (
dataStore.data.map {
GestureSettingsData(
swipeDown = it.gesturesSwipeDown,
swipeLeft = it.gesturesSwipeLeft,
swipeRight = it.gesturesSwipeRight,
doubleTap = it.gesturesDoubleTap,
longPress = it.gesturesLongPress,
homeButton = it.gesturesHomeButton,
)
}.distinctUntilChanged()
) {
val swipeDown: Flow<GestureAction> = dataStore.data.map { it.gesturesSwipeDown }
.distinctUntilChanged()
val swipeLeft: Flow<GestureAction> = dataStore.data.map { it.gesturesSwipeLeft }
.distinctUntilChanged()
val swipeRight: Flow<GestureAction> = dataStore.data.map { it.gesturesSwipeRight }
.distinctUntilChanged()
val doubleTap: Flow<GestureAction> = dataStore.data.map { it.gesturesDoubleTap }
.distinctUntilChanged()
val longPress: Flow<GestureAction> = dataStore.data.map { it.gesturesLongPress }
.distinctUntilChanged()
val homeButton: Flow<GestureAction> = dataStore.data.map { it.gesturesHomeButton }
.distinctUntilChanged()
fun setSwipeDown(action: GestureAction) {
dataStore.update {
it.copy(gesturesSwipeDown = action)
}
}
fun setSwipeLeft(action: GestureAction) {
dataStore.update {
it.copy(gesturesSwipeLeft = action)
}
}
fun setSwipeRight(action: GestureAction) {
dataStore.update {
it.copy(gesturesSwipeRight = action)
}
}
fun setDoubleTap(action: GestureAction) {
dataStore.update {
it.copy(gesturesDoubleTap = action)
}
}
fun setLongPress(action: GestureAction) {
dataStore.update {
it.copy(gesturesLongPress = action)
}
}
fun setHomeButton(action: GestureAction) {
dataStore.update {
it.copy(gesturesHomeButton = action)
}
}
}
@@ -0,0 +1,62 @@
package de.mm20.launcher2.preferences.ui
import de.mm20.launcher2.preferences.LauncherDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
data class IconSettingsData(
val themedIcons: Boolean,
val forceThemed: Boolean,
val adaptify: Boolean,
val iconPack: String?,
val iconPackThemed: Boolean,
)
class IconSettings internal constructor(
private val launcherDataStore: LauncherDataStore,
) : Flow<IconSettingsData> by (
launcherDataStore.data.map {
IconSettingsData(
themedIcons = it.iconsThemed,
forceThemed = it.iconsForceThemed,
adaptify = it.iconsAdaptify,
iconPack = it.iconsPack,
iconPackThemed = it.iconsPackThemed,
)
}
) {
fun setAdaptifyLegacyIcons(adaptify: Boolean) {
launcherDataStore.update {
it.copy(iconsAdaptify = adaptify)
}
}
fun setThemedIcons(themedIcons: Boolean) {
launcherDataStore.update {
it.copy(iconsThemed = themedIcons)
}
}
fun setForceThemedIcons(forceThemed: Boolean) {
launcherDataStore.update {
it.copy(iconsForceThemed = forceThemed)
}
}
fun setIconPack(iconPack: String?) {
launcherDataStore.update {
it.copy(iconsPack = iconPack)
}
}
fun setIconPackThemed(iconPackThemed: Boolean) {
launcherDataStore.update {
it.copy(iconsPackThemed = iconPackThemed)
}
}
}
@@ -0,0 +1,65 @@
package de.mm20.launcher2.preferences.ui
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.SearchResultOrder
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
class SearchUiSettings internal constructor(
private val launcherDataStore: LauncherDataStore,
){
val launchOnEnter
get() = launcherDataStore.data.map { it.searchLaunchOnEnter }.distinctUntilChanged()
fun setLaunchOnEnter(launchOnEnter: Boolean) {
launcherDataStore.update {
it.copy(searchLaunchOnEnter = launchOnEnter)
}
}
val hiddenItemsButton
get() = launcherDataStore.data.map { it.hiddenItemsShowButton }.distinctUntilChanged()
fun setHiddenItemsButton(hiddenItemsButton: Boolean) {
launcherDataStore.update {
it.copy(hiddenItemsShowButton = hiddenItemsButton)
}
}
val favorites
get() = launcherDataStore.data.map { it.favoritesEnabled }.distinctUntilChanged()
fun setFavorites(favorites: Boolean) {
launcherDataStore.update {
it.copy(favoritesEnabled = favorites)
}
}
val resultOrder
get() = launcherDataStore.data.map { it.searchResultOrder }.distinctUntilChanged()
fun setResultOrder(resultOrder: SearchResultOrder) {
launcherDataStore.update {
it.copy(searchResultOrder = resultOrder)
}
}
val openKeyboard
get() = launcherDataStore.data.map { it.searchBarKeyboard }.distinctUntilChanged()
fun setOpenKeyboard(openKeyboard: Boolean) {
launcherDataStore.update {
it.copy(searchBarKeyboard = openKeyboard)
}
}
val reversedResults
get() = launcherDataStore.data.map { it.searchResultsReversed }.distinctUntilChanged()
fun setReversedResults(reversedResults: Boolean) {
launcherDataStore.update {
it.copy(searchResultsReversed = reversedResults)
}
}
}
@@ -0,0 +1,345 @@
package de.mm20.launcher2.preferences.ui
import de.mm20.launcher2.preferences.BaseLayout
import de.mm20.launcher2.preferences.ColorScheme
import de.mm20.launcher2.preferences.Font
import de.mm20.launcher2.preferences.IconShape
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.ScreenOrientation
import de.mm20.launcher2.preferences.SearchBarColors
import de.mm20.launcher2.preferences.SearchBarStyle
import de.mm20.launcher2.preferences.SurfaceShape
import de.mm20.launcher2.preferences.SystemBarColors
import de.mm20.launcher2.preferences.ThemeDescriptor
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
data class CardStyle(
val opacity: Float = 1f,
val cornerRadius: Int = 0,
val shape: SurfaceShape = SurfaceShape.Rounded,
val borderWidth: Int = 0,
)
data class GridSettings(
val columnCount: Int = 5,
val iconSize: Int = 48,
val showLabels: Boolean = true,
)
class UiSettings internal constructor(
private val launcherDataStore: LauncherDataStore,
) {
val favoritesEnabled
get() = launcherDataStore.data.map { it.favoritesEnabled || it.homeScreenDock }
val iconShape
get() = launcherDataStore.data.map {
it.iconsShape
}
fun setIconShape(iconShape: IconShape) {
launcherDataStore.update {
it.copy(iconsShape = iconShape)
}
}
val gridSettings
get() = launcherDataStore.data.map {
GridSettings(
showLabels = it.gridLabels,
iconSize = it.gridIconSize,
columnCount = it.gridColumnCount,
)
}
fun setGridColumnCount(columnCount: Int) {
launcherDataStore.update {
it.copy(gridColumnCount = columnCount)
}
}
fun setGridIconSize(iconSize: Int) {
launcherDataStore.update {
it.copy(gridIconSize = iconSize)
}
}
fun setGridShowLabels(showLabels: Boolean) {
launcherDataStore.update {
it.copy(gridLabels = showLabels)
}
}
val cardStyle
get() = launcherDataStore.data.map {
CardStyle(
opacity = it.surfacesOpacity,
cornerRadius = it.surfacesRadius,
shape = it.surfacesShape,
borderWidth = it.surfacesBorderWidth,
)
}
fun setCardOpacity(opacity: Float) {
launcherDataStore.update {
it.copy(surfacesOpacity = opacity)
}
}
fun setCardRadius(radius: Int) {
launcherDataStore.update {
it.copy(surfacesRadius = radius)
}
}
fun setCardBorderWidth(borderWidth: Int) {
launcherDataStore.update {
it.copy(surfacesBorderWidth = borderWidth)
}
}
fun setCardShape(shape: SurfaceShape) {
launcherDataStore.update {
it.copy(surfacesShape = shape)
}
}
val dimWallpaper
get() = launcherDataStore.data.map {
it.wallpaperDim
}
fun setDimWallpaper(dimWallpaper: Boolean) {
launcherDataStore.update {
it.copy(wallpaperDim = dimWallpaper)
}
}
val blurWallpaper
get() = launcherDataStore.data.map {
it.wallpaperBlur
}.distinctUntilChanged()
fun setBlurWallpaper(blurWallpaper: Boolean) {
launcherDataStore.update {
it.copy(wallpaperBlur = blurWallpaper)
}
}
val wallpaperBlurRadius
get() = launcherDataStore.data.map {
it.wallpaperBlurRadius
}.distinctUntilChanged()
fun setWallpaperBlurRadius(wallpaperBlurRadius: Int) {
launcherDataStore.update {
it.copy(wallpaperBlurRadius = wallpaperBlurRadius)
}
}
val colorScheme
get() = launcherDataStore.data.map {
it.uiColorScheme
}.distinctUntilChanged()
val compatModeColors
get() = launcherDataStore.data.map {
it.uiCompatModeColors
}.distinctUntilChanged()
fun setCompatModeColors(enabled: Boolean) {
launcherDataStore.update {
it.copy(uiCompatModeColors = enabled)
}
}
val statusBarColor
get() = launcherDataStore.data.map {
it.systemBarsStatusColors
}.distinctUntilChanged()
val hideStatusBar
get() = launcherDataStore.data.map {
it.systemBarsHideStatus
}.distinctUntilChanged()
val hideNavigationBar
get() = launcherDataStore.data.map {
it.systemBarsHideNav
}.distinctUntilChanged()
fun setHideStatusBar(hideStatusBar: Boolean) {
launcherDataStore.update {
it.copy(systemBarsHideStatus = hideStatusBar)
}
}
fun setHideNavigationBar(hideNavigationBar: Boolean) {
launcherDataStore.update {
it.copy(systemBarsHideNav = hideNavigationBar)
}
}
val navigationBarColor
get() = launcherDataStore.data.map {
it.systemBarsNavColors
}.distinctUntilChanged()
fun setStatusBarColor(statusBarColor: SystemBarColors) {
launcherDataStore.update {
it.copy(systemBarsStatusColors = statusBarColor)
}
}
fun setNavigationBarColor(navigationBarColor: SystemBarColors) {
launcherDataStore.update {
it.copy(systemBarsNavColors = navigationBarColor)
}
}
val chargingAnimation
get() = launcherDataStore.data.map {
it.animationsCharging
}.distinctUntilChanged()
fun setChargingAnimation(chargingAnimation: Boolean) {
launcherDataStore.update {
it.copy(animationsCharging = chargingAnimation)
}
}
val baseLayout
get() = launcherDataStore.data.map {
it.uiBaseLayout
}.distinctUntilChanged()
fun setBaseLayout(baseLayout: BaseLayout) {
launcherDataStore.update {
it.copy(uiBaseLayout = baseLayout)
}
}
val clockFillScreen
get() = launcherDataStore.data.map {
it.clockWidgetFillHeight
}.distinctUntilChanged()
val searchBarStyle
get() = launcherDataStore.data.map {
it.searchBarStyle
}.distinctUntilChanged()
fun setSearchBarStyle(searchBarStyle: SearchBarStyle) {
launcherDataStore.update {
it.copy(searchBarStyle = searchBarStyle)
}
}
val searchBarColor
get() = launcherDataStore.data.map {
it.searchBarColors
}.distinctUntilChanged()
fun setSearchBarColor(color: SearchBarColors) {
launcherDataStore.update {
it.copy(searchBarColors = color)
}
}
val bottomSearchBar
get() = launcherDataStore.data.map {
it.searchBarBottom
}.distinctUntilChanged()
fun setBottomSearchBar(bottomSearchBar: Boolean) {
launcherDataStore.update {
it.copy(searchBarBottom = bottomSearchBar)
}
}
val reverseSearchResults
get() = launcherDataStore.data.map {
it.searchResultsReversed
}.distinctUntilChanged()
fun setReverseSearchResults(reverseSearchResults: Boolean) {
launcherDataStore.update {
it.copy(searchResultsReversed = reverseSearchResults)
}
}
val fixedSearchBar
get() = launcherDataStore.data.map {
it.searchBarFixed
}.distinctUntilChanged()
fun setFixedSearchBar(fixedSearchBar: Boolean) {
launcherDataStore.update {
it.copy(searchBarFixed = fixedSearchBar)
}
}
val openKeyboardOnSearch
get() = launcherDataStore.data.map {
it.searchBarKeyboard
}.distinctUntilChanged()
val orientation
get() = launcherDataStore.data.map {
it.uiOrientation
}.distinctUntilChanged()
fun setOrientation(orientation: ScreenOrientation) {
launcherDataStore.update {
it.copy(uiOrientation = orientation)
}
}
val theme
get() = launcherDataStore.data.map {
it.uiTheme
}.distinctUntilChanged()
fun setTheme(theme: ThemeDescriptor) {
launcherDataStore.update {
it.copy(uiTheme = theme)
}
}
val font
get() = launcherDataStore.data.map {
it.uiFont
}.distinctUntilChanged()
fun setFont(font: Font) {
launcherDataStore.update {
it.copy(uiFont = font)
}
}
fun setColorScheme(colorScheme: ColorScheme) {
launcherDataStore.update {
it.copy(uiColorScheme = colorScheme)
}
}
val dock
get() = launcherDataStore.data.map {
it.homeScreenDock
}.distinctUntilChanged()
val widgetEditButton
get() = launcherDataStore.data.map {
it.widgetsEditButton
}.distinctUntilChanged()
fun setWidgetEditButton(editButton: Boolean) {
launcherDataStore.update {
it.copy(widgetsEditButton = editButton)
}
}
}
@@ -0,0 +1,18 @@
package de.mm20.launcher2.preferences.ui
import de.mm20.launcher2.preferences.LauncherDataStore
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
class UiState internal constructor(
private val launcherDataStore: LauncherDataStore,
){
val favoritesTagsExpanded
get() = launcherDataStore.data.map { it.stateTagsMultiline }.distinctUntilChanged()
fun setFavoritesTagsExpanded(favoritesTagsExpanded: Boolean) {
launcherDataStore.update {
it.copy(stateTagsMultiline = favoritesTagsExpanded)
}
}
}
@@ -0,0 +1,165 @@
package de.mm20.launcher2.preferences.weather
import de.mm20.launcher2.preferences.LatLon
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.ProviderSettings
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
sealed interface WeatherLocation {
val name: String
data class LatLon(
override val name: String,
val lat: Double,
val lon: Double,
) : WeatherLocation
data class Id(
override val name: String,
val locationId: String,
) : WeatherLocation
}
data class WeatherSettingsData(
val provider: String = "metno",
val autoLocation: Boolean = true,
val location: LatLon? = null,
val locationName: String? = null,
val lastLocation: LatLon? = null,
val lastUpdate: Long = 0L,
val providerSettings: Map<String, ProviderSettings> = emptyMap(),
)
class WeatherSettings internal constructor(
private val launcherDataStore: LauncherDataStore,
) :
Flow<WeatherSettingsData> by (
launcherDataStore.data.map {
WeatherSettingsData(
provider = it.weatherProvider,
autoLocation = it.weatherAutoLocation,
location = it.weatherLocation,
locationName = it.weatherLocationName,
lastLocation = it.weatherLastLocation,
lastUpdate = it.weatherLastUpdate,
providerSettings = it.weatherProviderSettings,
)
}.distinctUntilChanged()
) {
val location = launcherDataStore.data.map {
val providerSettings = it.weatherProviderSettings[it.weatherProvider]
val id = providerSettings?.locationId
val name = providerSettings?.locationName
if (id != null && name != null) {
WeatherLocation.Id(name, id)
} else if (it.weatherLocation != null && it.weatherLocationName != null) {
WeatherLocation.LatLon(
it.weatherLocationName,
it.weatherLocation.lat,
it.weatherLocation.lon
)
} else {
null
}
}.distinctUntilChanged()
val autoLocation = launcherDataStore.data.map { it.weatherAutoLocation }
.distinctUntilChanged()
fun setLocation(location: WeatherLocation) {
launcherDataStore.update {
val providerSettings =
it.weatherProviderSettings.getOrDefault(it.weatherProvider, ProviderSettings())
when (location) {
is WeatherLocation.LatLon -> {
it.copy(
weatherLocation = LatLon(lat = location.lat, lon = location.lon),
weatherLocationName = location.name,
weatherLastUpdate = 0L,
weatherAutoLocation = false,
weatherProviderSettings = it.weatherProviderSettings.toMutableMap().apply {
put(
it.weatherProvider,
providerSettings.copy(
locationId = null,
locationName = null,
)
)
}
)
}
is WeatherLocation.Id -> {
it.copy(
weatherLocation = null,
weatherLocationName = null,
weatherAutoLocation = false,
weatherLastUpdate = 0L,
weatherProviderSettings = it.weatherProviderSettings.toMutableMap().apply {
put(
it.weatherProvider,
providerSettings.copy(
locationId = location.locationId,
locationName = location.name
)
)
}
)
}
}
}
}
fun setLastLocation(location: LatLon) {
launcherDataStore.update {
it.copy(
weatherLastLocation = location,
)
}
}
val lastUpdate = launcherDataStore.data.map { it.weatherLastUpdate }
.distinctUntilChanged()
fun setLastUpdate(lastUpdate: Long) {
launcherDataStore.update {
it.copy(weatherLastUpdate = lastUpdate)
}
}
val providerId = launcherDataStore.data.map { it.weatherProvider }
.distinctUntilChanged()
fun setProvider(provider: String) {
launcherDataStore.update {
it.copy(
weatherProvider = provider,
weatherLastUpdate = 0L,
)
}
}
fun setAutoLocation(autoLocation: Boolean) {
launcherDataStore.update {
it.copy(
weatherAutoLocation = autoLocation,
weatherLastUpdate = 0L,
)
}
}
val imperialUnits = launcherDataStore.data.map { it.weatherImperialUnits }
.distinctUntilChanged()
fun setImperialUnits(imperialUnits: Boolean) {
launcherDataStore.update {
it.copy(
weatherImperialUnits = imperialUnits,
)
}
}
}
@@ -3,7 +3,7 @@ syntax = "proto3";
option java_package = "de.mm20.launcher2.preferences";
option java_multiple_files = true;
message Settings {
message LegacySettings {
uint32 version = 1;
message AppearanceSettings {
enum Theme {
@@ -90,8 +90,6 @@ message Settings {
// UUID of the selected theme
string theme_id = 12;
uint32 blur_wallpaper_radius = 13;
bool force_compat_mode_system_colors = 14;
}
AppearanceSettings appearance = 2;