+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(56.dp)
+ .background(MaterialTheme.colors.background)
+ ) {
+ AnimatedVisibility(visible = currentRoute != Routes.PREFERENCES && currentRoute != null) {
+ Box(
+ contentAlignment = Alignment.Center,
+ modifier = Modifier
+ .padding(start = 8.dp)
+ .height(40.dp)
+ .width(40.dp)
+ .clip(CircleShape)
+ .clickable { navController.popBackStack() }
+ ) {
+ Image(
+ painter = painterResource(id = R.drawable.ic_arrow_back),
+ contentDescription = null,
+ colorFilter = ColorFilter.tint(MaterialTheme.colors.onBackground)
+ )
+ }
}
+ Text(
+ text = state.title,
+ style = MaterialTheme.typography.h6,
+ modifier = Modifier.padding(start = 16.dp),
+ color = MaterialTheme.colors.onSurface,
+ )
}
- Text(
- text = title ?: "",
- style = MaterialTheme.typography.h6,
- modifier = Modifier.padding(start = 16.dp),
- color = MaterialTheme.colors.onSurface,
- )
}
}
\ No newline at end of file
diff --git a/lawnchair/src/app/lawnchair/ui/preferences/preferenceGraph.kt b/lawnchair/src/app/lawnchair/ui/preferences/preferenceGraph.kt
new file mode 100644
index 0000000000..7e46287641
--- /dev/null
+++ b/lawnchair/src/app/lawnchair/ui/preferences/preferenceGraph.kt
@@ -0,0 +1,38 @@
+package app.lawnchair.ui.preferences
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.compositionLocalOf
+import androidx.navigation.NavGraphBuilder
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.navigation
+
+inline fun NavGraphBuilder.preferenceGraph(
+ route: String,
+ crossinline root: @Composable () -> Unit,
+ crossinline block: NavGraphBuilder.(subRoute: (String) -> String) -> Unit = { }
+) {
+ val subRoute: (String) -> String = { name -> "$route/$name" }
+ val rootRoute = subRoute("root")
+ navigation(startDestination = rootRoute, route) {
+ composable(route = rootRoute) {
+ CompositionLocalProvider(LocalRoute provides route) {
+ root()
+ }
+ }
+ block(subRoute)
+ }
+}
+
+inline fun NavGraphBuilder.root(crossinline content: @Composable () -> Unit) {
+ composable(route = "") {
+ content()
+ }
+}
+
+val LocalRoute = compositionLocalOf { "" }
+
+@Composable
+fun subRoute(name: String): String {
+ return "${LocalRoute.current}/$name"
+}
diff --git a/lawnchair/src/app/lawnchair/util/createSideEffect.kt b/lawnchair/src/app/lawnchair/util/createSideEffect.kt
new file mode 100644
index 0000000000..4854f97151
--- /dev/null
+++ b/lawnchair/src/app/lawnchair/util/createSideEffect.kt
@@ -0,0 +1,46 @@
+package app.lawnchair.util
+
+import androidx.compose.runtime.*
+
+class PropsContainer(var props: P)
+
+class SideEffect(
+ val consume: @Composable (@Composable (S) -> Unit) -> Unit,
+ val provide: @Composable (P) -> Unit)
+
+inline fun createSideEffect(crossinline reducePropsToState: (List
) -> S): SideEffect {
+ val mountedInstances = mutableListOf>()
+ var currentState = reducePropsToState(emptyList())
+ var onChangeHandler: (() -> Unit)? = null
+
+ val emitChange = {
+ currentState = reducePropsToState(mountedInstances.map { it.props })
+ onChangeHandler?.invoke()
+ }
+
+ return SideEffect(
+ consume = @Composable { block ->
+ val localState = remember { mutableStateOf(currentState) }
+ DisposableEffect(null) {
+ onChangeHandler = { localState.value = currentState }
+ onDispose { onChangeHandler = null }
+ }
+ block(localState.value)
+ },
+ provide = @Composable { props ->
+ val currentProps = remember { PropsContainer(props) }
+ DisposableEffect(key1 = currentProps) {
+ mountedInstances.add(currentProps)
+ onDispose {
+ mountedInstances.remove(currentProps)
+ emitChange()
+ }
+ }
+ DisposableEffect(key1 = props) {
+ currentProps.props = props
+ emitChange()
+ onDispose { }
+ }
+ }
+ )
+}
diff --git a/lawnchair/src/app/lawnchair/util/pageMeta.kt b/lawnchair/src/app/lawnchair/util/pageMeta.kt
new file mode 100644
index 0000000000..045f8be470
--- /dev/null
+++ b/lawnchair/src/app/lawnchair/util/pageMeta.kt
@@ -0,0 +1,12 @@
+package app.lawnchair.util
+
+class MetaState(val title: String)
+class Meta(val title: String?)
+
+private fun getTitleFromPropsList(propsList: List): String {
+ return propsList.lastOrNull()?.title ?: ""
+}
+
+val pageMeta = createSideEffect { propsList ->
+ MetaState(title = getTitleFromPropsList(propsList))
+}
diff --git a/lawnchair/src/app/lawnchair/util/preferences/BasePreferenceManager.kt b/lawnchair/src/app/lawnchair/util/preferences/BasePreferenceManager.kt
index 2852fdd156..cb101eba1f 100644
--- a/lawnchair/src/app/lawnchair/util/preferences/BasePreferenceManager.kt
+++ b/lawnchair/src/app/lawnchair/util/preferences/BasePreferenceManager.kt
@@ -2,8 +2,6 @@ package app.lawnchair.util.preferences
import android.content.Context
import android.content.SharedPreferences
-import android.util.Log
-import androidx.compose.runtime.currentComposer
import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.Utilities
import java.lang.ClassCastException
@@ -26,16 +24,23 @@ abstract class BasePreferenceManager(context: Context) : SharedPreferences.OnSha
}
interface PreferenceChangeListener {
- fun onPreferenceChange(pref: BasePref<*>)
+ fun onPreferenceChange(pref: PrefEntry<*>)
}
- abstract inner class BasePref(val key: String, private val primaryListener: ChangeListener?) {
+ interface PrefEntry {
+ val defaultValue: T
+
+ fun get(): T
+ fun set(newValue: T)
+
+ fun addListener(listener: PreferenceChangeListener)
+ fun removeListener(listener: PreferenceChangeListener)
+ }
+
+ abstract inner class BasePref(val key: String, private val primaryListener: ChangeListener?) : PrefEntry {
protected var loaded = false
private val listeners = WeakHashMap()
- abstract fun get(): T
- abstract fun set(newValue: T)
-
fun onSharedPreferenceChange() {
loaded = false
primaryListener?.invoke()
@@ -44,18 +49,18 @@ abstract class BasePreferenceManager(context: Context) : SharedPreferences.OnSha
}
}
- fun addListener(listener: PreferenceChangeListener) {
+ override fun addListener(listener: PreferenceChangeListener) {
listeners[listener] = true
}
- fun removeListener(listener: PreferenceChangeListener) {
+ override fun removeListener(listener: PreferenceChangeListener) {
listeners.remove(listener)
}
}
inner class StringPref(
key: String,
- private val defaultValue: String,
+ override val defaultValue: String,
primaryListener: ChangeListener? = null
) : BasePref(key, primaryListener) {
private var currentValue = ""
@@ -80,7 +85,7 @@ abstract class BasePreferenceManager(context: Context) : SharedPreferences.OnSha
inner class BoolPref(
key: String,
- private val defaultValue: Boolean,
+ override val defaultValue: Boolean,
primaryListener: ChangeListener? = null
) : BasePref(key, primaryListener) {
private var currentValue = false
@@ -105,9 +110,10 @@ abstract class BasePreferenceManager(context: Context) : SharedPreferences.OnSha
open inner class IntPref(
key: String,
- private val defaultValue: Int,
+ private val defaultValueInternal: Int,
primaryListener: ChangeListener? = null
) : BasePref(key, primaryListener) {
+ override val defaultValue = defaultValueInternal
private var currentValue = 0
init {
@@ -117,9 +123,9 @@ abstract class BasePreferenceManager(context: Context) : SharedPreferences.OnSha
override fun get(): Int {
if (!loaded) {
currentValue = try {
- sp.getInt(key, defaultValue)
+ sp.getInt(key, defaultValueInternal)
} catch (e: ClassCastException) {
- sp.getFloat(key, defaultValue.toFloat()).toInt()
+ sp.getFloat(key, defaultValueInternal.toFloat()).toInt()
}
loaded = true
}
@@ -134,30 +140,45 @@ abstract class BasePreferenceManager(context: Context) : SharedPreferences.OnSha
inner class IdpIntPref(
key: String,
- private val defaultValue: InvariantDeviceProfile.() -> Int,
+ private val selectDefaultValue: InvariantDeviceProfile.() -> Int,
primaryListener: ChangeListener? = null
) : IntPref(key, -1, primaryListener) {
+ override val defaultValue: Int
+ get() = error("unsupported")
+
+ override fun get(): Int {
+ error("unsupported")
+ }
+
+ override fun set(newValue: Int) {
+ error("unsupported")
+ }
+
+ fun defaultValue(idp: InvariantDeviceProfile): Int {
+ return selectDefaultValue(idp)
+ }
+
fun get(idp: InvariantDeviceProfile): Int {
- val value = get()
+ val value = super.get()
return if (value == -1) {
- defaultValue(idp)
+ selectDefaultValue(idp)
} else {
value
}
}
fun set(newValue: Int, idp: InvariantDeviceProfile) {
- if (newValue == defaultValue(idp)) {
- set(-1)
+ if (newValue == selectDefaultValue(idp)) {
+ super.set(-1)
} else {
- set(newValue)
+ super.set(newValue)
}
}
}
inner class FloatPref(
key: String,
- private val defaultValue: Float,
+ override val defaultValue: Float,
primaryListener: ChangeListener? = null
) : BasePref(key, primaryListener) {
private var currentValue = 0f
diff --git a/lawnchair/src/app/lawnchair/util/preferences/PreferenceAdapter.kt b/lawnchair/src/app/lawnchair/util/preferences/PreferenceAdapter.kt
index 18a079f303..e4f1de9fe2 100644
--- a/lawnchair/src/app/lawnchair/util/preferences/PreferenceAdapter.kt
+++ b/lawnchair/src/app/lawnchair/util/preferences/PreferenceAdapter.kt
@@ -16,7 +16,7 @@ class PreferenceAdapter(
state.value = newValue
}
- override fun onPreferenceChange(pref: BasePreferenceManager.BasePref<*>) {
+ override fun onPreferenceChange(pref: BasePreferenceManager.PrefEntry<*>) {
state.value = get()
}
}
@@ -29,18 +29,18 @@ fun BasePreferenceManager.IdpIntPref.getAdapter(): PreferenceAdapter {
}
@Composable
-fun BasePreferenceManager.BasePref.getAdapter(): PreferenceAdapter {
+fun BasePreferenceManager.PrefEntry.getAdapter(): PreferenceAdapter {
return getAdapter(this, ::get, ::set)
}
@Composable
-fun BasePreferenceManager.BasePref.observeAsState(): State {
+fun BasePreferenceManager.PrefEntry.observeAsState(): State {
return getAdapter().state
}
@Composable
private fun getAdapter(
- pref: BasePreferenceManager.BasePref<*>,
+ pref: BasePreferenceManager.PrefEntry<*>,
get: () -> T,
set: (T) -> Unit
): PreferenceAdapter {