Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preferences migrate #1

Draft
wants to merge 1 commit into
base: dev
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -173,6 +173,10 @@ dependencies {
implementation( libs.customactivityoncrash)
implementation( libs.tankery.circularSeekBar)

implementation( libs.androidx.datastore)
implementation( libs.androidx.startup)


debugImplementation(files("libs/lib-decoder-ffmpeg-debug.aar"))
releaseImplementation(files("libs/lib-decoder-ffmpeg-release.aar"))

9 changes: 9 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -48,6 +48,15 @@
android:theme="@style/Theme.RetroMusic.FollowSystem"
android:usesCleartextTraffic="true"
tools:ignore="UnusedAttribute">
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="meloplayer.app.util.ApplicationContextInitializer"
android:value="androidx.startup" />
</provider>
<activity
android:name=".activities.MainActivity"
android:exported="true"
4 changes: 4 additions & 0 deletions app/src/main/java/meloplayer/app/MainModule.kt
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@ import meloplayer.app.network.provideDefaultCache
import meloplayer.app.network.provideLastFmRest
import meloplayer.app.network.provideLastFmRetrofit
import meloplayer.app.network.provideOkHttp
import meloplayer.app.preferences.PreferenceManager
import meloplayer.app.repository.*
import org.koin.android.ext.koin.androidContext
import org.koin.androidx.viewmodel.dsl.viewModel
@@ -81,6 +82,9 @@ private val mainModule = module {
single {
RetroWebServer(get())
}
single {
PreferenceManager(get())
}
}
private val dataModule = module {
single {
5 changes: 5 additions & 0 deletions app/src/main/java/meloplayer/app/logging/Logs.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package meloplayer.app.logging


class Logs {
}
4 changes: 4 additions & 0 deletions app/src/main/java/meloplayer/app/preferences/HomeTabType.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package meloplayer.app.preferences

class HomeTabType {
}
153 changes: 153 additions & 0 deletions app/src/main/java/meloplayer/app/preferences/Preference.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package meloplayer.app.preferences

import android.content.Context
import androidx.datastore.core.DataStore
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.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking


private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")

interface Preference<T> {
fun setValue(value: T)
val key: String
val defaultValue: T
val observableValue: Flow<T>
val value: T
get() = runBlocking {
observableValue.firstOrNull() ?: defaultValue
}

// @Composable
// fun asState() = observableValue.collectAsStateWithLifecycle(initialValue = value)
}

/**
* Handle default values carefully, just like in enumPreference
*/
fun <T, BackingT> customPreference(
context: Context,
backingPref: Preference<BackingT>,
defaultValue: T,
serialize: (T) -> BackingT,
deserialize: (BackingT) -> T
) = object : Preference<T> {

override val key = backingPref.key
override val defaultValue = defaultValue
override val observableValue: Flow<T>
get() = backingPref.observableValue.map(deserialize)

override fun setValue(value: T) {
backingPref.setValue(serialize(value))
}
}

inline fun <reified T : Enum<T>> enumPreference(
context: Context, key: String, defaultValue: T
) = customPreference(context,
backingPref = IntPreference(context, key, Int.MAX_VALUE),
defaultValue,
serialize = { it.ordinal },
deserialize = {
if (it == Int.MAX_VALUE) {
defaultValue
} else {
enumValues<T>().getOrNull(it) ?: defaultValue

}
})

class IntPreference(
private val context: Context,

override val key: String, override val defaultValue: Int
) : Preference<Int> {

private val backingKey = intPreferencesKey(key)
override fun setValue(value: Int) {
runBlocking {
context.dataStore.edit { prefs ->
prefs[backingKey] = value
}
}
}

override val observableValue: Flow<Int>
get() = context.dataStore.data.map { pref ->
pref[backingKey] ?: defaultValue
}
}

class BooleanPreference(
private val context: Context,

override val key: String, override val defaultValue: Boolean
) : Preference<Boolean> {

private val backingKey = booleanPreferencesKey(key)
override fun setValue(value: Boolean) {
runBlocking {
context.dataStore.edit { prefs ->
prefs[backingKey] = value
}
}
}

override val observableValue: Flow<Boolean>
get() = context.dataStore.data.map { pref ->
pref[backingKey] ?: defaultValue
}
}


class LongPreference(
private val context: Context,

override val key: String, override val defaultValue: Long
) : Preference<Long> {

private val backingKey = longPreferencesKey(key)
override fun setValue(value: Long) {
runBlocking {
context.dataStore.edit { prefs ->
prefs[backingKey] = value
}
}
}

override val observableValue: Flow<Long>
get() = context.dataStore.data.map { pref ->
pref[backingKey] ?: defaultValue
}
}

class StringPreference(
private val context: Context,

override val key: String, override val defaultValue: String
) : Preference<String> {

private val backingKey = stringPreferencesKey(key)
override fun setValue(value: String) {
runBlocking {
context.dataStore.edit { prefs ->
prefs[backingKey] = value
}
}
}

override val observableValue: Flow<String>
get() = context.dataStore.data.map { pref ->
pref[backingKey] ?: defaultValue
}
}
38 changes: 38 additions & 0 deletions app/src/main/java/meloplayer/app/preferences/PreferenceManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package meloplayer.app.preferences

import android.content.Context
import android.graphics.Color

const val DefaultTimeFormat = "hh:mm a"
const val DefaultDateFormat = "d MMM, yyyy"
val DefaultColorSchemeSeed = Color.GREEN


enum class ThemeConfig {
FollowSystem, Dark, Light
}

enum class DarkThemeType { Dark, Black }

class PreferenceManager(
context: Context
) {
//Theming related
val themeConfig = enumPreference(
context = context, key = "theme_config", defaultValue = ThemeConfig.FollowSystem
)
val darkThemeType = enumPreference(
context = context, key = "dark_theme_type", defaultValue = DarkThemeType.Dark
)
val followSystemColors = BooleanPreference(
context = context, key = "follow_system_colors", defaultValue = false
)
val colorSchemeSeed = IntPreference(
context = context, key = "color_scheme_type", defaultValue = DefaultColorSchemeSeed
)

//Home tabs to show and their order
//Language
//safSdCardUri
//autoDownloadImagesPolicy
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package meloplayer.app.util


import android.content.Context
import androidx.startup.Initializer
import timber.log.Timber


private var appContext: Context? = null

val applicationContextGlobal
get() = appContext!!


internal class ApplicationContextInitializer : Initializer<Context> {
override fun create(context: Context): Context {
context.applicationContext.also { appContext = it }
Timber.i("init done")
return context.applicationContext
}

override fun dependencies(): List<Class<out Initializer<*>>> = emptyList()
}
Original file line number Diff line number Diff line change
@@ -32,3 +32,4 @@ fun String.runCommand(workingDir: File = File("./")): String?
.directory(workingDir)
.start()
.inputStream.readAllBytes()?.let { String(it) }
?.trim()
8 changes: 5 additions & 3 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ coreSplashscreen = "1.0.1"
customactivityoncrash = "2.4.0"
fadingedgelayout = "1.0.0"
google_featureDelivery_version = "2.1.0"
agp = "8.5.0"
agp = "8.5.1"
dhaval2404_imagepicker_version = "2.1"
chrisbanes_insetter_version = "0.6.1"
jaudiotagger = "2.3.15"
@@ -20,10 +20,10 @@ yslibrary_keyboardvisibilityevent_version = "3.0.0-RC3"
koinAndroid = "3.4.0"
kotlin = "2.0.0"

kotlinxCoroutinesAndroid = "1.8.0"
kotlinxCoroutinesAndroid = "1.8.1"
android_tab_library_version = "2.0.3"
fast_scroll_libraryVersion = "1.3.0"
lifecycle_version = "2.8.0"
lifecycle_version = "2.8.3"
okhttp3_loggingInterceptor_version = "5.0.0-alpha.9"
afollestad_materialCab_version = "2.0.1"
materialIntro = "2.0.0"
@@ -127,6 +127,8 @@ androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", versi
androidx-palette-ktx = { group = "androidx.palette", name = "palette-ktx", version = "1.0.0" }


androidx-datastore= { module = "androidx.datastore:datastore-preferences", version = "1.1.1" }
androidx-startup = { module = "androidx.startup:startup-runtime", version = "1.1.1" }

#buildscripts dependencies
timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }