Merge pull request #2139

This commit is contained in:
Patryk Michalik
2021-05-03 15:44:44 +02:00
committed by GitHub
34 changed files with 392 additions and 130 deletions
+1 -1
View File
@@ -201,7 +201,7 @@
<activity
android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"
android:theme="@style/Theme.AppCompat.DayNight" />
android:theme="@style/Theme.Acknowledgements" />
<activity
android:name="com.google.android.gms.oss.licenses.OssLicensesActivity"
+6
View File
@@ -6,4 +6,10 @@
<item name="android:windowLightStatusBar" tools:targetApi="m">false</item>
<item name="android:windowLightNavigationBar" tools:ignore="NewApi">false</item>
</style>
<style name="Theme.Acknowledgements" parent="Base.Theme.Acknowledgements">
<item name="android:colorBackground">@color/black_900</item>
<item name="android:windowLightStatusBar" tools:targetApi="m">false</item>
<item name="android:windowLightNavigationBar" tools:ignore="NewApi">false</item>
</style>
</resources>
+13 -1
View File
@@ -13,4 +13,16 @@
<item name="android:navigationBarColor" tools:ignore="NewApi">?android:attr/colorBackground</item>
<item name="android:windowLightNavigationBar" tools:ignore="NewApi">true</item>
</style>
</resources>
<style name="Theme.Acknowledgements" parent="Base.Theme.Acknowledgements">
<item name="android:colorBackground">@color/white_50</item>
</style>
<style name="Base.Theme.Acknowledgements" parent="Theme.AppCompat.DayNight">
<item name="android:statusBarColor" tools:ignore="NewApi">?android:attr/colorBackground</item>
<item name="colorPrimary">?android:attr/colorBackground</item>
<item name="android:windowLightStatusBar" tools:ignore="NewApi">true</item>
<item name="android:navigationBarColor" tools:ignore="NewApi">?android:attr/colorBackground</item>
<item name="android:windowLightNavigationBar" tools:ignore="NewApi">true</item>
</style>
</resources>
@@ -19,14 +19,26 @@ package app.lawnchair.ui.preferences
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.navigation.NavGraphBuilder
import app.lawnchair.ui.preferences.components.PreferenceGroup
import app.lawnchair.ui.preferences.components.PreferenceLayout
import app.lawnchair.ui.preferences.components.SliderPreference
import app.lawnchair.util.Meta
import app.lawnchair.util.pageMeta
import app.lawnchair.util.preferences.getAdapter
import app.lawnchair.util.preferences.preferenceManager
import com.android.launcher3.R
@ExperimentalAnimationApi
fun NavGraphBuilder.appDrawerGraph(route: String) {
preferenceGraph(route, { AppDrawerPreferences() })
}
@ExperimentalAnimationApi
@Composable
fun AppDrawerPreferences() {
val prefs = preferenceManager()
pageMeta.provide(Meta(title = stringResource(id = R.string.app_drawer_label)))
PreferenceLayout {
PreferenceGroup(heading = stringResource(id = R.string.general_label), isFirstChild = true) {
SliderPreference(
@@ -19,13 +19,25 @@ package app.lawnchair.ui.preferences
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.navigation.NavGraphBuilder
import app.lawnchair.ui.preferences.components.PreferenceGroup
import app.lawnchair.ui.preferences.components.PreferenceLayout
import app.lawnchair.ui.preferences.components.SliderPreference
import app.lawnchair.util.Meta
import app.lawnchair.util.pageMeta
import app.lawnchair.util.preferences.getAdapter
import app.lawnchair.util.preferences.preferenceManager
import com.android.launcher3.R
@ExperimentalAnimationApi
fun NavGraphBuilder.dockGraph(route: String) {
preferenceGraph(route, { DockPreferences() })
}
@ExperimentalAnimationApi
@Composable
fun DockPreferences() {
pageMeta.provide(Meta(title = stringResource(id = R.string.dock_label)))
PreferenceLayout {
PreferenceGroup(heading = stringResource(id = R.string.grid), isFirstChild = true) {
SliderPreference(
@@ -19,13 +19,25 @@ package app.lawnchair.ui.preferences
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.navigation.NavGraphBuilder
import app.lawnchair.ui.preferences.components.PreferenceGroup
import app.lawnchair.ui.preferences.components.PreferenceLayout
import app.lawnchair.ui.preferences.components.SliderPreference
import app.lawnchair.util.Meta
import app.lawnchair.util.pageMeta
import app.lawnchair.util.preferences.getAdapter
import app.lawnchair.util.preferences.preferenceManager
import com.android.launcher3.R
@ExperimentalAnimationApi
fun NavGraphBuilder.folderGraph(route: String) {
preferenceGraph(route, { FolderPreferences() })
}
@ExperimentalAnimationApi
@Composable
fun FolderPreferences() {
pageMeta.provide(Meta(title = stringResource(id = R.string.folders_label)))
PreferenceLayout {
val prefs = preferenceManager()
PreferenceGroup(heading = stringResource(id = R.string.grid), isFirstChild = true) {
@@ -20,14 +20,34 @@ import android.os.Build
import androidx.compose.animation.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import app.lawnchair.ui.preferences.components.NavigationActionPreference
import app.lawnchair.ui.preferences.components.NotificationDotsPreference
import app.lawnchair.ui.preferences.components.PreferenceGroup
import app.lawnchair.ui.preferences.components.PreferenceLayout
import app.lawnchair.ui.preferences.components.SliderPreference
import app.lawnchair.ui.preferences.components.SwitchPreference
import app.lawnchair.util.Meta
import app.lawnchair.util.pageMeta
import app.lawnchair.util.preferences.*
import com.android.launcher3.R
object GeneralRoutes {
const val ICON_PACK = "iconPack"
}
@ExperimentalAnimationApi
fun NavGraphBuilder.generalGraph(route: String) {
preferenceGraph(route, { GeneralPreferences() }) { subRoute ->
iconPackGraph(route = subRoute(GeneralRoutes.ICON_PACK))
}
}
@ExperimentalAnimationApi
@Composable
fun GeneralPreferences(navController: NavController, interactor: PreferenceInteractor) {
fun GeneralPreferences() {
val prefs = preferenceManager()
pageMeta.provide(Meta(title = stringResource(id = R.string.general_label)))
PreferenceLayout {
PreferenceGroup(isFirstChild = true) {
SwitchPreference(
@@ -35,11 +55,10 @@ fun GeneralPreferences(navController: NavController, interactor: PreferenceInter
label = stringResource(id = R.string.home_screen_rotation_label),
description = stringResource(id = R.string.home_screen_rotaton_description)
)
NotificationDotsPreference(interactor)
NotificationDotsPreference()
NavigationActionPreference(
label = stringResource(id = R.string.icon_pack),
navController = navController,
destination = Routes.ICON_PACK,
destination = subRoute(name = GeneralRoutes.ICON_PACK),
showDivider = false
)
}
@@ -19,14 +19,27 @@ package app.lawnchair.ui.preferences
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.navigation.NavGraphBuilder
import app.lawnchair.ui.preferences.components.PreferenceGroup
import app.lawnchair.ui.preferences.components.PreferenceLayout
import app.lawnchair.ui.preferences.components.SliderPreference
import app.lawnchair.ui.preferences.components.SwitchPreference
import app.lawnchair.util.Meta
import app.lawnchair.util.pageMeta
import app.lawnchair.util.preferences.getAdapter
import app.lawnchair.util.preferences.preferenceManager
import com.android.launcher3.R
@ExperimentalAnimationApi
fun NavGraphBuilder.homeScreenGraph(route: String) {
preferenceGraph(route, { HomeScreenPreferences() })
}
@ExperimentalAnimationApi
@Composable
fun HomeScreenPreferences() {
val prefs = preferenceManager()
pageMeta.provide(Meta(title = stringResource(id = R.string.home_screen_label)))
PreferenceLayout {
PreferenceGroup(heading = "General", isFirstChild = true) {
SwitchPreference(
@@ -25,25 +25,40 @@ import androidx.compose.foundation.layout.*
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap
import androidx.navigation.NavGraphBuilder
import app.lawnchair.ui.preferences.components.PreferenceGroup
import app.lawnchair.ui.preferences.components.PreferenceLayout
import app.lawnchair.ui.preferences.components.PreferenceTemplate
import app.lawnchair.util.Meta
import app.lawnchair.util.pageMeta
import app.lawnchair.util.preferences.getAdapter
import app.lawnchair.util.preferences.preferenceManager
import com.android.launcher3.R
data class IconPackInfo(val name: String, val packageName: String, val icon: Drawable)
@ExperimentalAnimationApi
fun NavGraphBuilder.iconPackGraph(route: String) {
preferenceGraph(route, { IconPackPreferences() })
}
@ExperimentalAnimationApi
@Composable
fun IconPackPreferences(interactor: PreferenceInteractor) {
val iconPacks = interactor.getIconPacks().values.toList()
fun IconPackPreferences() {
val interactor = LocalPreferenceInteractor.current
val iconPacks = remember { interactor.getIconPacks() }
val iconPackPackage = preferenceManager().iconPackPackage.getAdapter()
pageMeta.provide(Meta(title = stringResource(id = R.string.icon_pack)))
PreferenceLayout {
PreferenceGroup(isFirstChild = true) {
// TODO: Use `LazyColumn` if possible.
@@ -21,5 +21,5 @@ import androidx.compose.runtime.State
interface PreferenceInteractor {
val notificationDotsEnabled: State<Boolean>
fun getIconPacks(): MutableMap<String, IconPackInfo>
fun getIconPacks(): List<IconPackInfo>
}
@@ -40,7 +40,7 @@ class PreferenceViewModel(application: Application) : AndroidViewModel(applicati
override val notificationDotsEnabled: MutableState<Boolean> =
mutableStateOf(enabledNotificationListeners?.contains(lawnchairNotificationListener.flattenToString()) == true)
override fun getIconPacks(): MutableMap<String, IconPackInfo> {
override fun getIconPacks(): List<IconPackInfo> {
val pm = getApplication<Application>().packageManager
val iconPacks: MutableMap<String, IconPackInfo> = HashMap()
val list: MutableList<ResolveInfo> = pm.queryIntentActivities(Intent("com.novalauncher.THEME"), 0)
@@ -51,9 +51,6 @@ class PreferenceViewModel(application: Application) : AndroidViewModel(applicati
pm.queryIntentActivities(Intent("android.intent.action.MAIN").addCategory("com.anddoes.launcher.THEME"), 0)
)
iconPacks["system"] =
IconPackInfo("System Icons", "", AppCompatResources.getDrawable(getApplication(), R.drawable.ic_launcher_home)!!)
for (info in list) {
iconPacks[info.activityInfo.packageName] = IconPackInfo(
info.loadLabel(pm).toString(),
@@ -62,6 +59,17 @@ class PreferenceViewModel(application: Application) : AndroidViewModel(applicati
)
}
return iconPacks
val iconPackList = iconPacks.values.toMutableList()
iconPackList.sortBy { it.name }
iconPackList.add(
0,
IconPackInfo(
"System Icons",
"",
AppCompatResources.getDrawable(getApplication(), R.drawable.ic_launcher_home)!!
)
)
return iconPackList
}
}
@@ -25,11 +25,22 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import app.lawnchair.ui.preferences.about.About
import app.lawnchair.ui.preferences.about.aboutGraph
import app.lawnchair.ui.preferences.components.PreferenceCategoryList
import app.lawnchair.ui.preferences.components.SystemUi
import app.lawnchair.ui.preferences.components.TopBar
import app.lawnchair.util.Meta
import app.lawnchair.util.pageMeta
import app.lawnchair.util.preferences.getMajorVersion
import com.android.launcher3.R
@@ -41,21 +52,8 @@ object Routes {
const val DOCK: String = "dock"
const val APP_DRAWER: String = "appDrawer"
const val FOLDERS: String = "folders"
const val ICON_PACK: String = "iconPack"
}
fun getRoutesToLabels(context: Context) =
mapOf(
Routes.PREFERENCES to context.getString(R.string.settings),
Routes.GENERAL to context.getString(R.string.general_label),
Routes.ABOUT to context.getString(R.string.about_label),
Routes.HOME_SCREEN to context.getString(R.string.home_screen_label),
Routes.DOCK to context.getString(R.string.dock_label),
Routes.APP_DRAWER to context.getString(R.string.app_drawer_label),
Routes.FOLDERS to context.getString(R.string.folders_label),
Routes.ICON_PACK to context.getString(R.string.icon_pack)
)
sealed class PreferenceCategory(
val label: String,
val description: String,
@@ -63,42 +61,42 @@ sealed class PreferenceCategory(
val route: String
) {
class General(context: Context) : PreferenceCategory(
label = getRoutesToLabels(context)[Routes.GENERAL]!!,
label = context.getString(R.string.settings),
description = context.getString(R.string.general_description),
iconResource = R.drawable.ic_general,
route = Routes.GENERAL
)
class HomeScreen(context: Context) : PreferenceCategory(
label = getRoutesToLabels(context)[Routes.HOME_SCREEN]!!,
label = context.getString(R.string.home_screen_label),
description = context.getString(R.string.home_screen_description),
iconResource = R.drawable.ic_home_screen,
route = Routes.HOME_SCREEN
)
class Dock(context: Context) : PreferenceCategory(
label = getRoutesToLabels(context)[Routes.DOCK]!!,
label = context.getString(R.string.dock_label),
description = context.getString(R.string.dock_description),
iconResource = R.drawable.ic_dock,
route = Routes.DOCK
)
class AppDrawer(context: Context) : PreferenceCategory(
label = getRoutesToLabels(context)[Routes.APP_DRAWER]!!,
label = context.getString(R.string.app_drawer_label),
description = context.getString(R.string.app_drawer_description),
iconResource = R.drawable.ic_app_drawer,
route = Routes.APP_DRAWER
)
class Folders(context: Context) : PreferenceCategory(
label = getRoutesToLabels(context)[Routes.FOLDERS]!!,
label = context.getString(R.string.folders_label),
description = context.getString(R.string.folders_description),
iconResource = R.drawable.ic_folder,
route = Routes.FOLDERS
)
class About(context: Context) : PreferenceCategory(
label = getRoutesToLabels(context)[Routes.ABOUT]!!,
label = context.getString(R.string.about_label),
description = "${context.getString(R.string.derived_app_name)} ${getMajorVersion(context)}",
iconResource = R.drawable.ic_about,
route = Routes.ABOUT
@@ -114,6 +112,14 @@ fun getPreferenceCategories(context: Context) = listOf(
PreferenceCategory.About(context)
)
val LocalNavController = compositionLocalOf<NavController> {
error("CompositionLocal LocalNavController not present")
}
val LocalPreferenceInteractor = compositionLocalOf<PreferenceInteractor> {
error("CompositionLocal LocalPreferenceInteractor not present")
}
@ExperimentalAnimationApi
@Composable
fun Preferences(interactor: PreferenceInteractor = viewModel<PreferenceViewModel>(), window: Window) {
@@ -125,20 +131,22 @@ fun Preferences(interactor: PreferenceInteractor = viewModel<PreferenceViewModel
.fillMaxSize()
.background(MaterialTheme.colors.background)
) {
TopBar(navController = navController)
NavHost(navController = navController, startDestination = "preferences") {
composable(route = Routes.PREFERENCES) { PreferenceCategoryList(navController) }
composable(route = Routes.HOME_SCREEN) { HomeScreenPreferences() }
composable(route = Routes.ICON_PACK) { IconPackPreferences(interactor = interactor) }
composable(route = Routes.DOCK) { DockPreferences() }
composable(route = Routes.APP_DRAWER) { AppDrawerPreferences() }
composable(route = Routes.FOLDERS) { FolderPreferences() }
composable(route = Routes.ABOUT) { About() }
composable(route = Routes.GENERAL) {
GeneralPreferences(
navController = navController,
interactor = interactor
)
CompositionLocalProvider(
LocalNavController provides navController,
LocalPreferenceInteractor provides interactor,
) {
TopBar()
NavHost(navController = navController, startDestination = "preferences") {
composable(route = Routes.PREFERENCES) {
pageMeta.provide(Meta(title = stringResource(id = R.string.settings)))
PreferenceCategoryList(navController)
}
generalGraph(route = Routes.GENERAL)
homeScreenGraph(route = Routes.HOME_SCREEN)
dockGraph(route = Routes.DOCK)
appDrawerGraph(route = Routes.APP_DRAWER)
folderGraph(route = Routes.FOLDERS)
aboutGraph(route = Routes.ABOUT)
}
}
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package app.lawnchair.ui.preferences
package app.lawnchair.ui.preferences.about
import android.content.Intent
import androidx.annotation.StringRes
@@ -34,6 +34,12 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavGraphBuilder
import app.lawnchair.ui.preferences.components.PreferenceGroup
import app.lawnchair.ui.preferences.components.ClickListenerPreference
import app.lawnchair.ui.preferences.preferenceGraph
import app.lawnchair.util.Meta
import app.lawnchair.util.pageMeta
import app.lawnchair.util.preferences.getFormattedVersionName
import com.android.launcher3.R
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
@@ -109,11 +115,17 @@ val teamMembers = listOf(
),
)
@ExperimentalAnimationApi
fun NavGraphBuilder.aboutGraph(route: String) {
preferenceGraph(route, { About() })
}
@ExperimentalAnimationApi
@Composable
fun About() {
val context = LocalContext.current
pageMeta.provide(Meta(title = stringResource(id = R.string.about_label)))
Column(
modifier = Modifier
.fillMaxWidth()
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package app.lawnchair.ui.preferences
package app.lawnchair.ui.preferences.about
import androidx.compose.foundation.background
import androidx.compose.foundation.border
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package app.lawnchair.ui.preferences
package app.lawnchair.ui.preferences.about
import android.content.Intent
import android.net.Uri
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package app.lawnchair.ui.preferences
package app.lawnchair.ui.preferences.about
import android.content.Intent
import android.net.Uri
@@ -30,6 +30,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import app.lawnchair.ui.preferences.components.PreferenceTemplate
import com.google.accompanist.glide.GlideImage
@Composable
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package app.lawnchair.ui.preferences
package app.lawnchair.ui.preferences.about
import android.content.Intent
import android.net.Uri
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package app.lawnchair.ui.preferences
package app.lawnchair.ui.preferences.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package app.lawnchair.ui.preferences
package app.lawnchair.ui.preferences.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
@@ -28,15 +28,17 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import androidx.navigation.compose.navigate
import app.lawnchair.ui.preferences.LocalNavController
@Composable
fun NavigationActionPreference(
label: String,
subtitle: String? = null,
navController: NavController,
destination: String,
showDivider: Boolean = true
) =
) {
val navController = LocalNavController.current
PreferenceTemplate(height = if (subtitle != null) 72.dp else 52.dp, showDivider = showDivider) {
Column(
verticalArrangement = Arrangement.Center,
@@ -46,11 +48,20 @@ fun NavigationActionPreference(
.clickable { navController.navigate(route = destination) }
.padding(start = 16.dp, end = 16.dp),
) {
Text(text = label, style = MaterialTheme.typography.subtitle1, color = MaterialTheme.colors.onBackground)
Text(
text = label,
style = MaterialTheme.typography.subtitle1,
color = MaterialTheme.colors.onBackground
)
subtitle?.let {
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text(text = it, style = MaterialTheme.typography.body2, color = MaterialTheme.colors.onBackground)
Text(
text = it,
style = MaterialTheme.typography.body2,
color = MaterialTheme.colors.onBackground
)
}
}
}
}
}
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package app.lawnchair.ui.preferences
package app.lawnchair.ui.preferences.components
import android.content.ComponentName
import android.content.Intent
@@ -32,15 +32,17 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.os.bundleOf
import app.lawnchair.ui.preferences.LocalPreferenceInteractor
import com.android.launcher3.R
import com.android.launcher3.notification.NotificationListener
import android.provider.Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS as actionNotificationListenerSettings
@Composable
fun NotificationDotsPreference(interactor: PreferenceInteractor) {
fun NotificationDotsPreference() {
val context = LocalContext.current
val extraFragmentArgKey = ":settings:fragment_args_key"
val extraShowFragmentArgs = ":settings:show_fragment_args"
val interactor = LocalPreferenceInteractor.current
fun onClick() {
val intent = if (interactor.notificationDotsEnabled.value) {
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package app.lawnchair.ui.preferences
package app.lawnchair.ui.preferences.components
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.lazy.LazyColumn
@@ -24,6 +24,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavController
import androidx.navigation.compose.navigate
import app.lawnchair.ui.preferences.getPreferenceCategories
@Composable
fun PreferenceCategoryList(navController: NavController) {
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package app.lawnchair.ui.preferences
package app.lawnchair.ui.preferences.components
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package app.lawnchair.ui.preferences
package app.lawnchair.ui.preferences.components
import androidx.compose.animation.*
import androidx.compose.foundation.layout.*
@@ -1,4 +1,4 @@
package app.lawnchair.ui.preferences
package app.lawnchair.ui.preferences.components
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package app.lawnchair.ui.preferences
package app.lawnchair.ui.preferences.components
import androidx.compose.foundation.layout.*
import androidx.compose.material.Divider
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package app.lawnchair.ui.preferences
package app.lawnchair.ui.preferences.components
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package app.lawnchair.ui.preferences
package app.lawnchair.ui.preferences.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package app.lawnchair.ui.preferences
package app.lawnchair.ui.preferences.components
import android.os.Build
import android.view.View
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package app.lawnchair.ui.preferences
package app.lawnchair.ui.preferences.components
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
@@ -31,52 +31,53 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import androidx.navigation.compose.KEY_ROUTE
import androidx.navigation.compose.currentBackStackEntryAsState
import app.lawnchair.ui.preferences.LocalNavController
import app.lawnchair.ui.preferences.Routes
import app.lawnchair.util.pageMeta
import com.android.launcher3.R
@ExperimentalAnimationApi
@Composable
fun TopBar(navController: NavController) {
fun TopBar() {
val navController = LocalNavController.current
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.arguments?.getString(KEY_ROUTE)
val context = LocalContext.current
val title = getRoutesToLabels(context)[currentRoute]
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)
)
pageMeta.consume { state ->
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,
)
}
}
@@ -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"
}
@@ -0,0 +1,46 @@
package app.lawnchair.util
import androidx.compose.runtime.*
class PropsContainer<P>(var props: P)
class SideEffect<S, P>(
val consume: @Composable (@Composable (S) -> Unit) -> Unit,
val provide: @Composable (P) -> Unit)
inline fun <S, P> createSideEffect(crossinline reducePropsToState: (List<P>) -> S): SideEffect<S, P> {
val mountedInstances = mutableListOf<PropsContainer<P>>()
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 { }
}
}
)
}
@@ -0,0 +1,12 @@
package app.lawnchair.util
class MetaState(val title: String)
class Meta(val title: String?)
private fun getTitleFromPropsList(propsList: List<Meta>): String {
return propsList.lastOrNull()?.title ?: ""
}
val pageMeta = createSideEffect<MetaState, Meta> { propsList ->
MetaState(title = getTitleFromPropsList(propsList))
}
@@ -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<T>(val key: String, private val primaryListener: ChangeListener?) {
interface PrefEntry<T> {
val defaultValue: T
fun get(): T
fun set(newValue: T)
fun addListener(listener: PreferenceChangeListener)
fun removeListener(listener: PreferenceChangeListener)
}
abstract inner class BasePref<T>(val key: String, private val primaryListener: ChangeListener?) : PrefEntry<T> {
protected var loaded = false
private val listeners = WeakHashMap<PreferenceChangeListener, Boolean>()
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<String>(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<Boolean>(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<Int>(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<Float>(key, primaryListener) {
private var currentValue = 0f
@@ -16,7 +16,7 @@ class PreferenceAdapter<T>(
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<Float> {
}
@Composable
fun <T> BasePreferenceManager.BasePref<T>.getAdapter(): PreferenceAdapter<T> {
fun <T> BasePreferenceManager.PrefEntry<T>.getAdapter(): PreferenceAdapter<T> {
return getAdapter(this, ::get, ::set)
}
@Composable
fun <T> BasePreferenceManager.BasePref<T>.observeAsState(): State<T> {
fun <T> BasePreferenceManager.PrefEntry<T>.observeAsState(): State<T> {
return getAdapter().state
}
@Composable
private fun <T> getAdapter(
pref: BasePreferenceManager.BasePref<*>,
pref: BasePreferenceManager.PrefEntry<*>,
get: () -> T,
set: (T) -> Unit
): PreferenceAdapter<T> {