Initial migration to new Compose settings activity and DataStore

This commit is contained in:
MM20 2022-01-01 20:16:30 +01:00
parent 912982aba8
commit 89db713e24
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
22 changed files with 659 additions and 41 deletions

View File

@ -21,6 +21,7 @@ import de.mm20.launcher2.websites.websitesModule
import de.mm20.launcher2.widgets.widgetsModule
import de.mm20.launcher2.wikipedia.wikipediaModule
import de.mm20.launcher2.database.databaseModule
import de.mm20.launcher2.preferences.preferencesModule
import de.mm20.launcher2.weather.weatherModule
import kotlinx.coroutines.*
import org.koin.android.ext.koin.androidContext
@ -66,6 +67,7 @@ class LauncherApplication : Application(), CoroutineScope {
hiddenItemsModule,
iconsModule,
musicModule,
preferencesModule,
searchModule,
unitConverterModule,
weatherModule,

View File

@ -11,7 +11,7 @@ object AppLicense {
copyrightNote = "Copyright (C) 2021 MM2-0",
licenseName = R.string.gpl3_name,
licenseText = R.raw.license_gpl_3,
url = "https://github.com/Kvaesitso"
url = "https://github.com/MM2-0/Kvaesitso"
)
}
}

View File

@ -29,6 +29,12 @@
<item name="android:windowLightStatusBar">true</item>
</style>
<style name="SettingsTheme.NoActionBar" parent="BaseTheme">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:statusBarColor">?colorSurface</item>
</style>
<style name="LauncherTheme" parent="BaseTheme">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowShowWallpaper">true</item>

View File

@ -63,6 +63,7 @@ dependencies {
implementation(libs.androidx.preference)
api(libs.androidx.datastore)
api(libs.protobuf.javalite)
implementation(libs.koin.android)
implementation(project(":ktx"))
implementation(project(":i18n"))

View File

@ -4,7 +4,9 @@ import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.dataStore
val Context.dataStore: DataStore<Settings> by dataStore(
typealias LauncherDataStore = DataStore<Settings>
val Context.dataStore: LauncherDataStore by dataStore(
fileName = "settings.pb",
serializer = SettingsSerializer
)

View File

@ -0,0 +1,8 @@
package de.mm20.launcher2.preferences
import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module
val preferencesModule = module {
single { androidContext().dataStore }
}

View File

@ -40,6 +40,24 @@
android:windowSoftInputMode="stateHidden">
</activity>
<activity
android:name=".settings.SettingsActivity"
android:label="@string/title_activity_settings"
android:launchMode="singleTask"
android:exported="true"
android:parentActivityName=".legacy.activity.LauncherActivity"
android:screenOrientation="portrait"
android:taskAffinity="de.mm20.launcher2.settings"
android:theme="@style/SettingsTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.APPLICATION_PREFERENCES" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="de.mm20.launcher2.ui.legacy.activity.LauncherActivity" />
</activity>
</application>
</manifest>

View File

@ -1,12 +1,17 @@
package de.mm20.launcher2.ui.component.preferences
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.Card
import androidx.compose.material.RadioButton
import androidx.compose.material3.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -36,8 +41,10 @@ fun <T> ListPreference(
)
if (showDialog) {
Dialog(onDismissRequest = { showDialog = false }) {
Card(
elevation = 16.dp,
Surface(
tonalElevation = 16.dp,
shadowElevation = 16.dp,
shape = RoundedCornerShape(16.dp),
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp),
@ -47,13 +54,15 @@ fun <T> ListPreference(
) {
Text(
text = title,
style = MaterialTheme.typography.headlineMedium,
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(
start = 24.dp, end = 24.dp, top = 16.dp, bottom = 8.dp
)
)
LazyColumn(
modifier = Modifier.fillMaxWidth().padding(bottom = 16.dp)
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp)
) {
items(items) {
Row(
@ -64,7 +73,7 @@ fun <T> ListPreference(
onValueChanged(it.value)
showDialog = false
}
.padding(horizontal = 24.dp, vertical = 4.dp)
.padding(start = 16.dp, top = 4.dp, bottom = 4.dp, end = 24.dp)
) {
RadioButton(selected = it.value == value, onClick = {
onValueChanged(it.value)

View File

@ -1,8 +1,8 @@
package de.mm20.launcher2.ui.component.preferences
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@ -14,26 +14,25 @@ fun PreferenceCategory(
title: String? = null,
content: @Composable ColumnScope.() -> Unit
) {
Surface(
shadowElevation = 2.dp,
tonalElevation = 2.dp,
modifier = Modifier.padding(bottom = 4.dp).fillMaxWidth()
) {
Column {
if (title != null) {
Row(
modifier = Modifier
.padding(start = 16.dp, top = 16.dp, end = 16.dp)
) {
Text(
modifier = Modifier.padding(start = 56.dp),
text = title,
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.primary
)
}
Column {
if (title != null) {
Row(
modifier = Modifier
.padding(start = 16.dp, top = 16.dp, end = 16.dp)
) {
Text(
modifier = Modifier.padding(start = 56.dp),
text = title,
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.primary
)
}
content()
}
content()
Box(
modifier = Modifier.fillMaxWidth().height(0.5.dp).background(
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f)
)
)
}
}

View File

@ -1,5 +1,6 @@
package de.mm20.launcher2.ui.component.preferences
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
@ -11,6 +12,7 @@ import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import com.google.accompanist.insets.systemBarsPadding
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import de.mm20.launcher2.ui.locals.LocalNavController
@ -26,18 +28,22 @@ fun PreferenceScreen(
val systemUiController = rememberSystemUiController()
systemUiController.setStatusBarColor(MaterialTheme.colorScheme.surface)
systemUiController.setNavigationBarColor(Color.Black)
val activity = LocalContext.current as? AppCompatActivity
Box(
modifier = Modifier.systemBarsPadding()
) {
Scaffold(
topBar = {
SmallTopAppBar(
CenterAlignedTopAppBar(
title = {
Text(title)
},
navigationIcon = {
IconButton(onClick = {
navController?.navigateUp()
if (navController?.navigateUp() != true) {
activity?.onBackPressed()
}
}) {
Icon(imageVector = Icons.Rounded.ArrowBack, contentDescription = "Back")
}

View File

@ -59,6 +59,7 @@ import de.mm20.launcher2.ui.launcher.modals.HiddenItemsView
import de.mm20.launcher2.ui.launcher.search.SearchViewModel
import de.mm20.launcher2.ui.legacy.component.WidgetView
import de.mm20.launcher2.ui.legacy.helper.ThemeHelper
import de.mm20.launcher2.ui.settings.SettingsActivity
import de.mm20.launcher2.weather.WeatherViewModel
import de.mm20.launcher2.widgets.Widget
import de.mm20.launcher2.widgets.WidgetType
@ -295,14 +296,7 @@ class LauncherActivity : AppCompatActivity() {
menu.setOnMenuItemClickListener { item ->
when (item.itemId) {
R.id.menu_item_settings -> {
finish()
startActivity(Intent().also {
it.component = ComponentName(
packageName,
"de.mm20.launcher2.activity.SettingsActivity"
)
it.flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
startActivity(Intent(this, SettingsActivity::class.java))
}
R.id.menu_item_wallpaper -> {
startActivity(
@ -335,6 +329,16 @@ class LauncherActivity : AppCompatActivity() {
}
}
}
R.id.menu_item_settings_old -> {
finish()
startActivity(Intent().also {
it.component = ComponentName(
packageName,
"de.mm20.launcher2.activity.SettingsActivity"
)
it.flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
}
}
true
}

View File

@ -0,0 +1,109 @@
package de.mm20.launcher2.ui.settings
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.navigation.navArgument
import com.google.accompanist.navigation.animation.AnimatedNavHost
import com.google.accompanist.navigation.animation.composable
import com.google.accompanist.navigation.animation.rememberAnimatedNavController
import de.mm20.launcher2.licenses.AppLicense
import de.mm20.launcher2.licenses.OpenSourceLicenses
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.ui.LegacyLauncherTheme
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.legacy.helper.ThemeHelper
import de.mm20.launcher2.ui.locals.LocalNavController
import de.mm20.launcher2.ui.screens.settings.SettingsLicenseScreen
import de.mm20.launcher2.ui.settings.about.AboutScreen
import de.mm20.launcher2.ui.settings.appearance.AppearanceScreen
import de.mm20.launcher2.ui.settings.license.LicenseScreen
import de.mm20.launcher2.ui.settings.main.MainScreen
class SettingsActivity : AppCompatActivity() {
private val viewModel: SettingsActivityVM by viewModels()
@OptIn(ExperimentalAnimationApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val theme = viewModel.getTheme()
when (theme) {
Settings.AppearanceSettings.Theme.Light -> AppCompatDelegate.setDefaultNightMode(
AppCompatDelegate.MODE_NIGHT_NO
)
Settings.AppearanceSettings.Theme.Dark -> AppCompatDelegate.setDefaultNightMode(
AppCompatDelegate.MODE_NIGHT_YES
)
else -> AppCompatDelegate.setDefaultNightMode(
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
)
}
viewModel.theme.observe(this) {
if (it != theme && it != null) recreate()
}
val colorScheme = viewModel.getColorScheme()
val colorSchemeThemeId = when(colorScheme) {
Settings.AppearanceSettings.ColorScheme.BlackAndWhite -> R.style.BlackWhiteColors
else -> R.style.DefaultColors
}
this.theme.applyStyle(colorSchemeThemeId, true)
viewModel.colorScheme.observe(this) {
if (it != colorScheme && it != null) recreate()
}
setContent {
val navController = rememberAnimatedNavController()
CompositionLocalProvider(LocalNavController provides navController) {
LegacyLauncherTheme {
AnimatedNavHost(
navController = navController,
startDestination = "settings",
exitTransition = { fadeOut(tween(300, 300)) },
enterTransition = { fadeIn(tween(200)) },
popEnterTransition = { fadeIn(tween(0)) },
popExitTransition = { fadeOut(tween(200)) },
) {
composable("settings") {
MainScreen()
}
composable("settings/appearance") {
AppearanceScreen()
}
composable("settings/about") {
AboutScreen()
}
composable(
"settings/license?library={libraryName}",
arguments = listOf(navArgument("libraryName") {
nullable = true
})
) {
val libraryName = it.arguments?.getString("libraryName")
val library = remember(libraryName) {
if (libraryName == null) {
AppLicense.get(this@SettingsActivity)
} else {
OpenSourceLicenses.first { it.name == libraryName }
}
}
LicenseScreen(library)
}
}
}
}
}
}
}

View File

@ -0,0 +1,28 @@
package de.mm20.launcher2.ui.settings
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.Settings
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
class SettingsActivityVM: ViewModel(), KoinComponent {
private val dataStore: LauncherDataStore by inject()
val theme = dataStore.data.map { it.appearance.theme }.asLiveData()
fun getTheme(): Settings.AppearanceSettings.Theme = runBlocking {
dataStore.data.map { it.appearance.theme }.first()
}
val colorScheme = dataStore.data.map { it.appearance.colorScheme }.asLiveData()
fun getColorScheme(): Settings.AppearanceSettings.ColorScheme = runBlocking {
dataStore.data.map { it.appearance.colorScheme }.first()
}
}

View File

@ -0,0 +1,12 @@
package de.mm20.launcher2.ui.settings
enum class SettingsScreen {
Main,
Appearance,
Search,
Badges,
Weather,
Calendar,
Services,
About,
}

View File

@ -0,0 +1,107 @@
package de.mm20.launcher2.ui.settings.about
import android.content.Intent
import android.net.Uri
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Info
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.licenses.OpenSourceLicenses
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.preferences.Preference
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
import de.mm20.launcher2.ui.icons.Fdroid
import de.mm20.launcher2.ui.icons.GitHub
import de.mm20.launcher2.ui.icons.Telegram
import de.mm20.launcher2.ui.locals.LocalNavController
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
fun AboutScreen() {
val viewModel: AboutScreenVM = viewModel()
val navController = LocalNavController.current
val context = LocalContext.current
PreferenceScreen(title = stringResource(R.string.preference_screen_about)) {
item {
PreferenceCategory {
var appVersion by remember { mutableStateOf<String?>(null) }
LaunchedEffect(null) {
appVersion = withContext(Dispatchers.IO) {
context.packageManager.getPackageInfo(
context.packageName,
0
).versionName
}
}
Preference(
title = stringResource(R.string.preference_version),
summary = appVersion
)
}
}
item {
PreferenceCategory(title = stringResource(id = R.string.preference_category_license)) {
Preference(
icon = Icons.Rounded.Info,
title = stringResource(id = R.string.preference_about_license),
summary = stringResource(id = R.string.preference_about_license_summary),
onClick = {
navController?.navigate("settings/license")
}
)
}
}
item {
PreferenceCategory(title = stringResource(id = R.string.preference_category_links)) {
Preference(
icon = Icons.Rounded.GitHub,
title = "GitHub",
summary = "github.com/MM2-0/Kvaesitso",
onClick = {
context.startActivity(Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("https://github.com/MM2-0/Kvaesitso")
})
}
)
Preference(
icon = Icons.Rounded.Telegram,
title = stringResource(id = R.string.preference_about_telegram),
summary = "t.me/Kvaesitso",
onClick = {
context.startActivity(Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse("https://t.me/Kvaesitso")
})
}
)
Preference(
icon = Icons.Rounded.Fdroid,
title = stringResource(id = R.string.preference_about_fdroid),
summary = "github.com/MM2-0/fdroid",
onClick = {
context.startActivity(Intent(Intent.ACTION_VIEW).apply {
data =
Uri.parse("https://raw.githubusercontent.com/MM2-0/fdroid/master/fdroid/repo")
})
}
)
}
}
item {
PreferenceCategory(title = stringResource(id = R.string.preference_category_licenses)) {
for (library in OpenSourceLicenses.sortedBy { it.name.lowercase() }) {
Preference(
title = library.name,
summary = library.description,
onClick = {
navController?.navigate("settings/license?library=${library.name}")
}
)
}
}
}
}
}

View File

@ -0,0 +1,5 @@
package de.mm20.launcher2.ui.settings.about
import androidx.lifecycle.ViewModel
class AboutScreenVM : ViewModel()

View File

@ -0,0 +1,53 @@
package de.mm20.launcher2.ui.settings.appearance
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.ColorScheme
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.preferences.ListPreference
import de.mm20.launcher2.ui.component.preferences.Preference
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
import de.mm20.launcher2.ui.locals.LocalNavController
@Composable
fun AppearanceScreen() {
val viewModel: AppearanceScreenVM = viewModel()
PreferenceScreen(title = stringResource(id = R.string.preference_screen_appearance)) {
item {
PreferenceCategory {
val theme by viewModel.theme.observeAsState()
ListPreference(
title = stringResource(id = R.string.preference_theme),
items = listOf(
stringResource(id = R.string.preference_theme_system) to Theme.System,
stringResource(id = R.string.preference_theme_light) to Theme.Light,
stringResource(id = R.string.preference_theme_dark) to Theme.Dark,
),
value = theme,
onValueChanged = { newValue ->
if (newValue == null) return@ListPreference
viewModel.setTheme(newValue)
}
)
val colorScheme by viewModel.colorScheme.observeAsState()
ListPreference(
title = stringResource(id = R.string.preference_screen_colors),
items = listOf(
stringResource(id = R.string.preference_colors_default) to ColorScheme.Default,
stringResource(id = R.string.preference_colors_bw) to ColorScheme.BlackAndWhite,
),
value = colorScheme,
onValueChanged = { newValue ->
if (newValue == null) return@ListPreference
viewModel.setColorScheme(newValue)
}
)
}
}
}
}

View File

@ -0,0 +1,38 @@
package de.mm20.launcher2.ui.settings.appearance
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.ColorScheme
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
class AppearanceScreenVM : ViewModel(), KoinComponent {
private val dataStore: LauncherDataStore by inject()
val theme = dataStore.data.map { it.appearance.theme }.asLiveData()
fun setTheme(theme: Theme) {
viewModelScope.launch {
dataStore.updateData {
it.toBuilder()
.setAppearance(it.appearance.toBuilder().setTheme(theme))
.build()
}
}
}
val colorScheme = dataStore.data.map { it.appearance.colorScheme }.asLiveData()
fun setColorScheme(colorScheme: ColorScheme) {
viewModelScope.launch {
dataStore.updateData {
it.toBuilder()
.setAppearance(it.appearance.toBuilder().setColorScheme(colorScheme))
.build()
}
}
}
}

View File

@ -0,0 +1,118 @@
package de.mm20.launcher2.ui.settings.license
import android.net.Uri
import androidx.browser.customtabs.CustomTabColorSchemeParams
import androidx.browser.customtabs.CustomTabsIntent
import androidx.compose.animation.rememberSplineBasedDecay
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowBack
import androidx.compose.material.icons.rounded.OpenInBrowser
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.licenses.OpenSourceLibrary
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
import de.mm20.launcher2.ui.locals.LocalNavController
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LicenseScreen(library: OpenSourceLibrary) {
val context = LocalContext.current
val viewModel: LicenseScreenVM = viewModel()
val navController = LocalNavController.current
/*PreferenceScreen(title = stringResource(id = R.string.preference_category_license)) {
}*/
val scrollBehavior =
TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberSplineBasedDecay())
Scaffold(
topBar = {
LargeTopAppBar(
title = {
Text(library.name)
},
navigationIcon = {
IconButton(onClick = {
navController?.navigateUp()
}) {
Icon(imageVector = Icons.Rounded.ArrowBack, contentDescription = "Back")
}
},
scrollBehavior = scrollBehavior,
actions = {
val colorScheme = MaterialTheme.colorScheme
IconButton(onClick = {
CustomTabsIntent.Builder()
.setDefaultColorSchemeParams(CustomTabColorSchemeParams.Builder()
.setToolbarColor(colorScheme.primaryContainer.toArgb())
.build())
.build()
.launchUrl(context, Uri.parse(library.url))
}) {
Icon(
imageVector = Icons.Rounded.OpenInBrowser,
contentDescription = stringResource(
R.string.open_webpage
)
)
}
}
)
},
) {
LazyColumn(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
) {
library.description?.let {
item {
PreferenceCategory {
Column(
modifier = Modifier.padding(16.dp)
) {
Text(text = it)
}
}
}
}
item {
PreferenceCategory {
Column(
modifier = Modifier.padding(16.dp)
) {
Text(
text = stringResource(id = library.licenseName),
style = MaterialTheme.typography.titleMedium
)
library.copyrightNote?.let {
Text(
text = it,
modifier = Modifier.padding(vertical = 4.dp),
style = MaterialTheme.typography.bodySmall
)
}
val licenseText by viewModel.getLicenseText(library).observeAsState()
licenseText?.let {
Text(
text = it,
style = MaterialTheme.typography.bodySmall
)
}
}
}
}
}
}
}

View File

@ -0,0 +1,14 @@
package de.mm20.launcher2.ui.settings.license
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.liveData
import de.mm20.launcher2.licenses.OpenSourceLibrary
class LicenseScreenVM(private val context: Application) : AndroidViewModel(context) {
fun getLicenseText(library: OpenSourceLibrary) = liveData<String?> {
val text = context.resources.openRawResource(library.licenseText).reader()
.readText()
emit(text)
}
}

View File

@ -0,0 +1,76 @@
package de.mm20.launcher2.ui.settings.main
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.preferences.Preference
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
import de.mm20.launcher2.ui.icons.NotificationBadge
import de.mm20.launcher2.ui.locals.LocalNavController
@Composable
fun MainScreen() {
val navController = LocalNavController.current
PreferenceScreen(
title = stringResource(R.string.settings),
) {
item {
PreferenceCategory {
PreferenceCategory {
Preference(
icon = Icons.Rounded.Palette,
title = stringResource(id = R.string.preference_screen_appearance),
summary = stringResource(id = R.string.preference_screen_appearance_summary),
onClick = {
navController?.navigate("settings/appearance")
}
)
Preference(
icon = Icons.Rounded.Search,
title = stringResource(id = R.string.preference_screen_search),
summary = stringResource(id = R.string.preference_screen_search_summary)
)
Preference(
icon = Icons.Rounded.NotificationBadge,
title = stringResource(id = R.string.preference_screen_badges),
summary = stringResource(id = R.string.preference_screen_badges_summary),
onClick = {
navController?.navigate("settings/badges")
}
)
Preference(
icon = Icons.Rounded.LightMode,
title = stringResource(id = R.string.preference_screen_weather),
summary = stringResource(id = R.string.preference_screen_weather_summary)
)
Preference(
icon = Icons.Rounded.Today,
title = stringResource(id = R.string.preference_screen_calendar),
summary = stringResource(id = R.string.preference_screen_calendar_summary)
)
Preference(
icon = Icons.Rounded.AccountBox,
title = stringResource(id = R.string.preference_screen_services),
summary = stringResource(id = R.string.preference_screen_services_summary),
onClick = {
navController?.navigate("settings/accounts")
}
)
Preference(
icon = Icons.Rounded.Info,
title = stringResource(id = R.string.preference_screen_about),
summary = stringResource(id = R.string.preference_screen_about_summary),
onClick = {
navController?.navigate("settings/about")
}
)
}
}
}
}
}

View File

@ -12,4 +12,7 @@
<item
android:id="@+id/menu_item_settings"
android:title="@string/settings" />
<item
android:id="@+id/menu_item_settings_old"
android:title="@string/settings" />
</menu>