This commit is contained in:
Felitendo
2025-05-20 15:22:07 +02:00
parent c24d95627e
commit 8a6d5d19db
384 changed files with 7065 additions and 4430 deletions

1
core/datastore/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,18 @@
plugins {
alias(libs.plugins.looker.android.library)
alias(libs.plugins.looker.hilt)
alias(libs.plugins.looker.lint)
alias(libs.plugins.looker.serialization)
}
android {
namespace = "com.looker.core.datastore"
}
dependencies {
modules(Modules.coreCommon, Modules.coreDI)
implementation(libs.androidx.dataStore.core)
implementation(libs.androidx.dataStore.proto)
implementation(libs.kotlinx.coroutines.android)
implementation(libs.kotlinx.datetime)
}

View File

@@ -0,0 +1,216 @@
package com.looker.core.datastore
import android.net.Uri
import android.util.Log
import androidx.datastore.core.DataStore
import androidx.datastore.core.IOException
import androidx.datastore.preferences.core.MutablePreferences
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.longPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.core.stringSetPreferencesKey
import com.looker.core.common.Exporter
import com.looker.core.common.extension.updateAsMutable
import com.looker.core.datastore.model.AutoSync
import com.looker.core.datastore.model.InstallerType
import com.looker.core.datastore.model.ProxyPreference
import com.looker.core.datastore.model.ProxyType
import com.looker.core.datastore.model.SortOrder
import com.looker.core.datastore.model.Theme
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import kotlin.time.Duration
import kotlin.time.Duration.Companion.hours
class PreferenceSettingsRepository(
private val dataStore: DataStore<Preferences>,
private val exporter: Exporter<Settings>,
) : SettingsRepository {
override val data: Flow<Settings> = dataStore.data
.catch { exception ->
if (exception is IOException) {
Log.e("TAG", "Error reading preferences.", exception)
} else {
throw exception
}
}.map(::mapSettings)
override suspend fun getInitial(): Settings {
return data.first()
}
override suspend fun export(target: Uri) {
val currentSettings = getInitial()
exporter.export(currentSettings, target)
}
override suspend fun import(target: Uri) {
val importedSettings = exporter.import(target)
val updatedFavorites = importedSettings.favouriteApps +
getInitial().favouriteApps
val updatedSettings = importedSettings.copy(favouriteApps = updatedFavorites)
dataStore.edit {
it.setting(updatedSettings)
}
}
override suspend fun setLanguage(language: String) =
LANGUAGE.update(language)
override suspend fun enableIncompatibleVersion(enable: Boolean) =
INCOMPATIBLE_VERSIONS.update(enable)
override suspend fun enableNotifyUpdates(enable: Boolean) =
NOTIFY_UPDATES.update(enable)
override suspend fun enableUnstableUpdates(enable: Boolean) =
UNSTABLE_UPDATES.update(enable)
override suspend fun setIgnoreSignature(enable: Boolean) =
IGNORE_SIGNATURE.update(enable)
override suspend fun setTheme(theme: Theme) =
THEME.update(theme.name)
override suspend fun setDynamicTheme(enable: Boolean) =
DYNAMIC_THEME.update(enable)
override suspend fun setInstallerType(installerType: InstallerType) =
INSTALLER_TYPE.update(installerType.name)
override suspend fun setAutoUpdate(allow: Boolean) =
AUTO_UPDATE.update(allow)
override suspend fun setAutoSync(autoSync: AutoSync) =
AUTO_SYNC.update(autoSync.name)
override suspend fun setSortOrder(sortOrder: SortOrder) =
SORT_ORDER.update(sortOrder.name)
override suspend fun setProxyType(proxyType: ProxyType) =
PROXY_TYPE.update(proxyType.name)
override suspend fun setProxyHost(proxyHost: String) =
PROXY_HOST.update(proxyHost)
override suspend fun setProxyPort(proxyPort: Int) =
PROXY_PORT.update(proxyPort)
override suspend fun setCleanUpInterval(interval: Duration) =
CLEAN_UP_INTERVAL.update(interval.inWholeHours)
override suspend fun setCleanupInstant() =
LAST_CLEAN_UP.update(Clock.System.now().toEpochMilliseconds())
override suspend fun setHomeScreenSwiping(value: Boolean) =
HOME_SCREEN_SWIPING.update(value)
override suspend fun toggleFavourites(packageName: String) {
dataStore.edit { preference ->
val currentSet = preference[FAVOURITE_APPS] ?: emptySet()
val newSet = currentSet.updateAsMutable {
if (!add(packageName)) remove(packageName)
}
preference[FAVOURITE_APPS] = newSet
}
}
private fun mapSettings(preferences: Preferences): Settings {
val defaultInstallerName = (InstallerType.Default).name
val language = preferences[LANGUAGE] ?: "system"
val incompatibleVersions = preferences[INCOMPATIBLE_VERSIONS] ?: false
val notifyUpdate = preferences[NOTIFY_UPDATES] ?: true
val unstableUpdate = preferences[UNSTABLE_UPDATES] ?: false
val ignoreSignature = preferences[IGNORE_SIGNATURE] ?: false
val theme = Theme.valueOf(preferences[THEME] ?: Theme.SYSTEM.name)
val dynamicTheme = preferences[DYNAMIC_THEME] ?: false
val installerType =
InstallerType.valueOf(preferences[INSTALLER_TYPE] ?: defaultInstallerName)
val autoUpdate = preferences[AUTO_UPDATE] ?: false
val autoSync = AutoSync.valueOf(preferences[AUTO_SYNC] ?: AutoSync.WIFI_ONLY.name)
val sortOrder = SortOrder.valueOf(preferences[SORT_ORDER] ?: SortOrder.UPDATED.name)
val type = ProxyType.valueOf(preferences[PROXY_TYPE] ?: ProxyType.DIRECT.name)
val host = preferences[PROXY_HOST] ?: "localhost"
val port = preferences[PROXY_PORT] ?: 9050
val proxy = ProxyPreference(type = type, host = host, port = port)
val cleanUpInterval = preferences[CLEAN_UP_INTERVAL]?.hours ?: 12L.hours
val lastCleanup = preferences[LAST_CLEAN_UP]?.let { Instant.fromEpochMilliseconds(it) }
val favouriteApps = preferences[FAVOURITE_APPS] ?: emptySet()
val homeScreenSwiping = preferences[HOME_SCREEN_SWIPING] ?: true
return Settings(
language = language,
incompatibleVersions = incompatibleVersions,
notifyUpdate = notifyUpdate,
unstableUpdate = unstableUpdate,
ignoreSignature = ignoreSignature,
theme = theme,
dynamicTheme = dynamicTheme,
installerType = installerType,
autoUpdate = autoUpdate,
autoSync = autoSync,
sortOrder = sortOrder,
proxy = proxy,
cleanUpInterval = cleanUpInterval,
lastCleanup = lastCleanup,
favouriteApps = favouriteApps,
homeScreenSwiping = homeScreenSwiping,
)
}
private suspend inline fun <T> Preferences.Key<T>.update(newValue: T) {
dataStore.edit { preferences ->
preferences[this] = newValue
}
}
companion object PreferencesKeys {
private val LANGUAGE = stringPreferencesKey("key_language")
private val INCOMPATIBLE_VERSIONS = booleanPreferencesKey("key_incompatible_versions")
private val NOTIFY_UPDATES = booleanPreferencesKey("key_notify_updates")
private val UNSTABLE_UPDATES = booleanPreferencesKey("key_unstable_updates")
private val IGNORE_SIGNATURE = booleanPreferencesKey("key_ignore_signature")
private val THEME = stringPreferencesKey("key_theme")
private val DYNAMIC_THEME = booleanPreferencesKey("key_dynamic_theme")
private val INSTALLER_TYPE = stringPreferencesKey("key_installer_type")
private val AUTO_UPDATE = booleanPreferencesKey("key_auto_updates")
private val AUTO_SYNC = stringPreferencesKey("key_auto_sync")
private val SORT_ORDER = stringPreferencesKey("key_sort_order")
private val PROXY_TYPE = stringPreferencesKey("key_proxy_type")
private val PROXY_HOST = stringPreferencesKey("key_proxy_host")
private val PROXY_PORT = intPreferencesKey("key_proxy_port")
private val CLEAN_UP_INTERVAL = longPreferencesKey("key_clean_up_interval")
private val LAST_CLEAN_UP = longPreferencesKey("key_last_clean_up_time")
private val FAVOURITE_APPS = stringSetPreferencesKey("key_favourite_apps")
private val HOME_SCREEN_SWIPING = booleanPreferencesKey("key_home_swiping")
fun MutablePreferences.setting(settings: Settings): Preferences {
set(LANGUAGE, settings.language)
set(INCOMPATIBLE_VERSIONS, settings.incompatibleVersions)
set(NOTIFY_UPDATES, settings.notifyUpdate)
set(UNSTABLE_UPDATES, settings.unstableUpdate)
set(THEME, settings.theme.name)
set(DYNAMIC_THEME, settings.dynamicTheme)
set(INSTALLER_TYPE, settings.installerType.name)
set(AUTO_UPDATE, settings.autoUpdate)
set(AUTO_SYNC, settings.autoSync.name)
set(SORT_ORDER, settings.sortOrder.name)
set(PROXY_TYPE, settings.proxy.type.name)
set(PROXY_HOST, settings.proxy.host)
set(PROXY_PORT, settings.proxy.port)
set(CLEAN_UP_INTERVAL, settings.cleanUpInterval.inWholeHours)
set(LAST_CLEAN_UP, settings.lastCleanup?.toEpochMilliseconds() ?: 0L)
set(FAVOURITE_APPS, settings.favouriteApps)
set(HOME_SCREEN_SWIPING, settings.homeScreenSwiping)
return this.toPreferences()
}
}
}

View File

@@ -0,0 +1,67 @@
package com.looker.core.datastore
import androidx.datastore.core.Serializer
import com.looker.core.datastore.model.AutoSync
import com.looker.core.datastore.model.InstallerType
import com.looker.core.datastore.model.ProxyPreference
import com.looker.core.datastore.model.SortOrder
import com.looker.core.datastore.model.Theme
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import kotlin.time.Duration
import kotlin.time.Duration.Companion.hours
import kotlinx.datetime.Instant
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import kotlinx.serialization.json.encodeToStream
@Serializable
data class Settings(
val language: String = "system",
val incompatibleVersions: Boolean = false,
val notifyUpdate: Boolean = true,
val unstableUpdate: Boolean = false,
val ignoreSignature: Boolean = false,
val theme: Theme = Theme.SYSTEM,
val dynamicTheme: Boolean = false,
val installerType: InstallerType = InstallerType.Default,
val autoUpdate: Boolean = false,
val autoSync: AutoSync = AutoSync.WIFI_ONLY,
val sortOrder: SortOrder = SortOrder.UPDATED,
val proxy: ProxyPreference = ProxyPreference(),
val cleanUpInterval: Duration = 12.hours,
val lastCleanup: Instant? = null,
val favouriteApps: Set<String> = emptySet(),
val homeScreenSwiping: Boolean = true,
)
@OptIn(ExperimentalSerializationApi::class)
object SettingsSerializer : Serializer<Settings> {
private val json = Json { encodeDefaults = true }
override val defaultValue: Settings = Settings()
override suspend fun readFrom(input: InputStream): Settings {
return try {
json.decodeFromStream(input)
} catch (e: SerializationException) {
e.printStackTrace()
defaultValue
}
}
override suspend fun writeTo(t: Settings, output: OutputStream) {
try {
json.encodeToStream(t, output)
} catch (e: SerializationException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
}
}
}

View File

@@ -0,0 +1,63 @@
package com.looker.core.datastore
import android.net.Uri
import com.looker.core.datastore.model.AutoSync
import com.looker.core.datastore.model.InstallerType
import com.looker.core.datastore.model.ProxyType
import com.looker.core.datastore.model.SortOrder
import com.looker.core.datastore.model.Theme
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlin.time.Duration
interface SettingsRepository {
val data: Flow<Settings>
suspend fun getInitial(): Settings
suspend fun export(target: Uri)
suspend fun import(target: Uri)
suspend fun setLanguage(language: String)
suspend fun enableIncompatibleVersion(enable: Boolean)
suspend fun enableNotifyUpdates(enable: Boolean)
suspend fun enableUnstableUpdates(enable: Boolean)
suspend fun setIgnoreSignature(enable: Boolean)
suspend fun setTheme(theme: Theme)
suspend fun setDynamicTheme(enable: Boolean)
suspend fun setInstallerType(installerType: InstallerType)
suspend fun setAutoUpdate(allow: Boolean)
suspend fun setAutoSync(autoSync: AutoSync)
suspend fun setSortOrder(sortOrder: SortOrder)
suspend fun setProxyType(proxyType: ProxyType)
suspend fun setProxyHost(proxyHost: String)
suspend fun setProxyPort(proxyPort: Int)
suspend fun setCleanUpInterval(interval: Duration)
suspend fun setCleanupInstant()
suspend fun setHomeScreenSwiping(value: Boolean)
suspend fun toggleFavourites(packageName: String)
}
inline fun <T> SettingsRepository.get(crossinline block: suspend Settings.() -> T): Flow<T> {
return data.map(block).distinctUntilChanged()
}

View File

@@ -0,0 +1,82 @@
package com.looker.core.datastore.di
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.core.DataStoreFactory
import androidx.datastore.dataStoreFile
import androidx.datastore.preferences.core.PreferenceDataStoreFactory
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStoreFile
import com.looker.core.common.Exporter
import com.looker.core.datastore.PreferenceSettingsRepository
import com.looker.core.datastore.Settings
import com.looker.core.datastore.SettingsRepository
import com.looker.core.datastore.SettingsSerializer
import com.looker.core.datastore.exporter.SettingsExporter
import com.looker.core.datastore.migration.ProtoToPreferenceMigration
import com.looker.core.di.ApplicationScope
import com.looker.core.di.IoDispatcher
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.serialization.json.Json
import javax.inject.Singleton
private const val PREFERENCES = "settings_file"
private const val SETTINGS = "settings"
@Module
@InstallIn(SingletonComponent::class)
object DatastoreModule {
@Singleton
@Provides
fun provideProtoDatastore(
@ApplicationContext context: Context,
): DataStore<Settings> = DataStoreFactory.create(
serializer = SettingsSerializer,
) {
context.dataStoreFile(PREFERENCES)
}
@Singleton
@Provides
fun providePreferenceDatastore(
@ApplicationContext context: Context,
oldDatastore: DataStore<Settings>,
): DataStore<Preferences> = PreferenceDataStoreFactory.create(
migrations = listOf(
ProtoToPreferenceMigration(oldDatastore)
)
) {
context.preferencesDataStoreFile(SETTINGS)
}
@Singleton
@Provides
fun provideSettingsExporter(
@ApplicationContext context: Context,
@ApplicationScope scope: CoroutineScope,
@IoDispatcher dispatcher: CoroutineDispatcher
): Exporter<Settings> = SettingsExporter(
context = context,
scope = scope,
ioDispatcher = dispatcher,
json = Json {
encodeDefaults = true
prettyPrint = true
}
)
@Singleton
@Provides
fun provideSettingsRepository(
dataStore: DataStore<Preferences>,
exporter: Exporter<Settings>
): SettingsRepository = PreferenceSettingsRepository(dataStore, exporter)
}

View File

@@ -0,0 +1,57 @@
package com.looker.core.datastore.exporter
import android.content.Context
import android.net.Uri
import com.looker.core.common.Exporter
import com.looker.core.datastore.Settings
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import kotlinx.serialization.json.encodeToStream
import java.io.IOException
@OptIn(ExperimentalSerializationApi::class)
class SettingsExporter(
private val context: Context,
private val scope: CoroutineScope,
private val ioDispatcher: CoroutineDispatcher,
private val json: Json
) : Exporter<Settings> {
override suspend fun export(item: Settings, target: Uri) {
scope.launch(ioDispatcher) {
try {
context.contentResolver.openOutputStream(target).use {
if (it != null) json.encodeToStream(item, it)
}
} catch (e: SerializationException) {
e.printStackTrace()
cancel()
} catch (e: IOException) {
e.printStackTrace()
cancel()
}
}
}
override suspend fun import(target: Uri): Settings = withContext(ioDispatcher) {
try {
context.contentResolver.openInputStream(target).use {
checkNotNull(it) { "Null input stream for import file" }
json.decodeFromStream(it)
}
} catch (e: SerializationException) {
e.printStackTrace()
throw IllegalStateException(e.message)
} catch (e: IOException) {
e.printStackTrace()
throw IllegalStateException(e.message)
}
}
}

View File

@@ -0,0 +1,123 @@
package com.looker.core.datastore.extension
import android.content.Context
import android.content.res.Configuration
import com.looker.core.common.R
import com.looker.core.common.R.string as stringRes
import com.looker.core.common.R.style as styleRes
import com.looker.core.common.SdkCheck
import com.looker.core.datastore.model.AutoSync
import com.looker.core.datastore.model.InstallerType
import com.looker.core.datastore.model.ProxyType
import com.looker.core.datastore.model.SortOrder
import com.looker.core.datastore.model.Theme
import kotlin.time.Duration
fun Configuration.getThemeRes(theme: Theme, dynamicTheme: Boolean) = when (theme) {
Theme.SYSTEM -> {
if ((uiMode and Configuration.UI_MODE_NIGHT_YES) != 0) {
if (SdkCheck.isSnowCake && dynamicTheme) {
styleRes.Theme_Main_DynamicDark
} else {
styleRes.Theme_Main_Dark
}
} else {
if (SdkCheck.isSnowCake && dynamicTheme) {
styleRes.Theme_Main_DynamicLight
} else {
styleRes.Theme_Main_Light
}
}
}
Theme.SYSTEM_BLACK -> {
if ((uiMode and Configuration.UI_MODE_NIGHT_YES) != 0) {
if (SdkCheck.isSnowCake && dynamicTheme) {
styleRes.Theme_Main_DynamicAmoled
} else {
styleRes.Theme_Main_Amoled
}
} else {
if (SdkCheck.isSnowCake && dynamicTheme) {
styleRes.Theme_Main_DynamicLight
} else {
styleRes.Theme_Main_Light
}
}
}
Theme.LIGHT -> if (SdkCheck.isSnowCake && dynamicTheme) {
styleRes.Theme_Main_DynamicLight
} else {
styleRes.Theme_Main_Light
}
Theme.DARK -> if (SdkCheck.isSnowCake && dynamicTheme) {
styleRes.Theme_Main_DynamicDark
} else {
styleRes.Theme_Main_Dark
}
Theme.AMOLED -> if (SdkCheck.isSnowCake && dynamicTheme) {
styleRes.Theme_Main_DynamicAmoled
} else {
styleRes.Theme_Main_Amoled
}
}
fun Context?.toTime(duration: Duration): String {
val time = duration.inWholeHours.toInt()
val days = duration.inWholeDays.toInt()
if (duration == Duration.INFINITE) return this?.getString(R.string.never) ?: ""
return if (time >= 24) {
"$days " + this?.resources?.getQuantityString(
R.plurals.days,
days
)
} else {
"$time " + this?.resources?.getQuantityString(R.plurals.hours, time)
}
}
fun Context?.themeName(theme: Theme) = this?.let {
when (theme) {
Theme.SYSTEM -> getString(stringRes.system)
Theme.SYSTEM_BLACK -> getString(stringRes.system) + " " + getString(stringRes.amoled)
Theme.LIGHT -> getString(stringRes.light)
Theme.DARK -> getString(stringRes.dark)
Theme.AMOLED -> getString(stringRes.amoled)
}
} ?: ""
fun Context?.sortOrderName(sortOrder: SortOrder) = this?.let {
when (sortOrder) {
SortOrder.UPDATED -> getString(stringRes.recently_updated)
SortOrder.ADDED -> getString(stringRes.whats_new)
SortOrder.NAME -> getString(stringRes.name)
// SortOrder.SIZE -> getString(stringRes.size)
}
} ?: ""
fun Context?.autoSyncName(autoSync: AutoSync) = this?.let {
when (autoSync) {
AutoSync.NEVER -> getString(stringRes.never)
AutoSync.WIFI_ONLY -> getString(stringRes.only_on_wifi)
AutoSync.WIFI_PLUGGED_IN -> getString(stringRes.only_on_wifi_with_charging)
AutoSync.ALWAYS -> getString(stringRes.always)
}
} ?: ""
fun Context?.proxyName(proxyType: ProxyType) = this?.let {
when (proxyType) {
ProxyType.DIRECT -> getString(stringRes.no_proxy)
ProxyType.HTTP -> getString(stringRes.http_proxy)
ProxyType.SOCKS -> getString(stringRes.socks_proxy)
}
} ?: ""
fun Context?.installerName(installerType: InstallerType) = this?.let {
when (installerType) {
InstallerType.LEGACY -> getString(stringRes.legacy_installer)
InstallerType.SESSION -> getString(stringRes.session_installer)
InstallerType.SHIZUKU -> getString(stringRes.shizuku_installer)
InstallerType.ROOT -> getString(stringRes.root_installer)
}
} ?: ""

View File

@@ -0,0 +1,26 @@
package com.looker.core.datastore.migration
import androidx.datastore.core.DataMigration
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import com.looker.core.datastore.PreferenceSettingsRepository.PreferencesKeys.setting
import com.looker.core.datastore.Settings
import kotlinx.coroutines.flow.first
class ProtoToPreferenceMigration(
private val oldDataStore: DataStore<Settings>
) : DataMigration<Preferences> {
override suspend fun cleanUp() {
}
override suspend fun shouldMigrate(currentData: Preferences): Boolean {
return currentData.asMap().isEmpty()
}
override suspend fun migrate(currentData: Preferences): Preferences {
val settings = oldDataStore.data.first()
val preferences = currentData.toMutablePreferences()
preferences.setting(settings)
return preferences
}
}

View File

@@ -0,0 +1,8 @@
package com.looker.core.datastore.model
enum class AutoSync {
ALWAYS,
WIFI_ONLY,
WIFI_PLUGGED_IN,
NEVER
}

View File

@@ -0,0 +1,19 @@
package com.looker.core.datastore.model
import com.looker.core.common.device.Miui
enum class InstallerType {
LEGACY,
SESSION,
SHIZUKU,
ROOT;
companion object {
val Default: InstallerType
get() = if (Miui.isMiui) {
if (Miui.isMiuiOptimizationDisabled()) SESSION else LEGACY
} else {
SESSION
}
}
}

View File

@@ -0,0 +1,20 @@
package com.looker.core.datastore.model
import kotlinx.serialization.Serializable
@Serializable
data class ProxyPreference(
val type: ProxyType = ProxyType.DIRECT,
val host: String = "localhost",
val port: Int = 9050
) {
fun update(
newType: ProxyType? = null,
newHost: String? = null,
newPort: Int? = null
): ProxyPreference = copy(
type = newType ?: type,
host = newHost ?: host,
port = newPort ?: port
)
}

View File

@@ -0,0 +1,7 @@
package com.looker.core.datastore.model
enum class ProxyType {
DIRECT,
HTTP,
SOCKS
}

View File

@@ -0,0 +1,8 @@
package com.looker.core.datastore.model
// todo: Add Support for sorting by size
enum class SortOrder {
UPDATED,
ADDED,
NAME
}

View File

@@ -0,0 +1,9 @@
package com.looker.core.datastore.model
enum class Theme {
SYSTEM,
SYSTEM_BLACK,
LIGHT,
DARK,
AMOLED
}