mirror of
https://github.com/Myzel394/NumberHub.git
synced 2025-06-18 16:25:27 +02:00
Migrated to Themmo and added new screen for themes.
This commit is contained in:
parent
4ecb29d7b5
commit
183c5d47a6
@ -162,4 +162,7 @@ dependencies {
|
||||
|
||||
// Retrofit with Moshi Converter
|
||||
implementation("com.squareup.retrofit2:converter-moshi:2.9.0")
|
||||
|
||||
// Themmo
|
||||
implementation("com.github.sadellie:themmo:0.0.2")
|
||||
}
|
@ -22,17 +22,21 @@ import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import com.sadellie.unitto.data.NavRoutes.ABOUT_SCREEN
|
||||
import com.sadellie.unitto.data.NavRoutes.LEFT_LIST_SCREEN
|
||||
import com.sadellie.unitto.data.NavRoutes.MAIN_SCREEN
|
||||
import com.sadellie.unitto.data.NavRoutes.RIGHT_LIST_SCREEN
|
||||
import com.sadellie.unitto.data.NavRoutes.SETTINGS_SCREEN
|
||||
import com.sadellie.unitto.data.preferences.AppTheme
|
||||
import com.sadellie.unitto.data.NavRoutes.THEMES_SCREEN
|
||||
import com.sadellie.unitto.screens.MainViewModel
|
||||
import com.sadellie.unitto.screens.about.AboutScreen
|
||||
import com.sadellie.unitto.screens.main.MainScreen
|
||||
@ -40,34 +44,58 @@ import com.sadellie.unitto.screens.second.LeftSideScreen
|
||||
import com.sadellie.unitto.screens.second.RightSideScreen
|
||||
import com.sadellie.unitto.screens.second.SecondViewModel
|
||||
import com.sadellie.unitto.screens.setttings.SettingsScreen
|
||||
import com.sadellie.unitto.ui.theme.UnittoTheme
|
||||
import com.sadellie.unitto.screens.theming.ThemesScreen
|
||||
import com.sadellie.unitto.screens.theming.ThemesViewModel
|
||||
import com.sadellie.unitto.ui.theme.AppTypography
|
||||
import com.sadellie.unitto.ui.theme.DarkThemeColors
|
||||
import com.sadellie.unitto.ui.theme.LightThemeColors
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.sadellie.themmo.Themmo
|
||||
import io.github.sadellie.themmo.ThemmoController
|
||||
import io.github.sadellie.themmo.rememberThemmoController
|
||||
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : ComponentActivity() {
|
||||
private val mainViewModel: MainViewModel by viewModels()
|
||||
private val secondViewModel: SecondViewModel by viewModels()
|
||||
private val themesViewModel: ThemesViewModel by viewModels()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContent {
|
||||
val navController = rememberNavController()
|
||||
val currentAppTheme: Int = mainViewModel.currentTheme
|
||||
themesViewModel.collectThemeOptions()
|
||||
|
||||
// We don't draw anything until we know what theme we need to use
|
||||
if (currentAppTheme != AppTheme.NOT_SET) {
|
||||
UnittoTheme(
|
||||
currentAppTheme = currentAppTheme
|
||||
val themmoController = rememberThemmoController(
|
||||
lightColorScheme = LightThemeColors,
|
||||
darkColorScheme = DarkThemeColors,
|
||||
// Anything below will not called if theming mode is still loading from DataStore
|
||||
themingMode = themesViewModel.themingMode ?: return@setContent,
|
||||
dynamicThemeEnabled = themesViewModel.enableDynamic,
|
||||
amoledThemeEnabled = themesViewModel.enableAmoled
|
||||
)
|
||||
val navController = rememberNavController()
|
||||
val sysUiController = rememberSystemUiController()
|
||||
|
||||
Themmo(
|
||||
themmoController = themmoController,
|
||||
typography = AppTypography,
|
||||
animationSpec = tween(150)
|
||||
) {
|
||||
val backgroundColor = MaterialTheme.colorScheme.background
|
||||
|
||||
UnittoApp(
|
||||
navController = navController,
|
||||
mainViewModel = mainViewModel,
|
||||
secondViewModel = secondViewModel
|
||||
secondViewModel = secondViewModel,
|
||||
themesViewModel = themesViewModel,
|
||||
themmoController = it
|
||||
)
|
||||
|
||||
SideEffect { sysUiController.setSystemBarsColor(backgroundColor) }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,7 +109,9 @@ class MainActivity : ComponentActivity() {
|
||||
fun UnittoApp(
|
||||
navController: NavHostController,
|
||||
mainViewModel: MainViewModel,
|
||||
secondViewModel: SecondViewModel
|
||||
secondViewModel: SecondViewModel,
|
||||
themesViewModel: ThemesViewModel,
|
||||
themmoController: ThemmoController
|
||||
) {
|
||||
NavHost(
|
||||
navController = navController,
|
||||
@ -123,6 +153,14 @@ fun UnittoApp(
|
||||
)
|
||||
}
|
||||
|
||||
composable(THEMES_SCREEN) {
|
||||
ThemesScreen(
|
||||
navigateUpAction = { navController.navigateUp() },
|
||||
themmoController = themmoController,
|
||||
viewModel = themesViewModel
|
||||
)
|
||||
}
|
||||
|
||||
composable(ABOUT_SCREEN) {
|
||||
AboutScreen(navigateUpAction = { navController.navigateUp() })
|
||||
}
|
||||
|
@ -28,5 +28,6 @@ object NavRoutes {
|
||||
const val RIGHT_LIST_SCREEN = "RightScreen"
|
||||
|
||||
const val SETTINGS_SCREEN = "SettingsScreen"
|
||||
const val THEMES_SCREEN = "ThemesScreen"
|
||||
const val ABOUT_SCREEN = "AboutScreen"
|
||||
}
|
||||
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Unitto is a unit converter for Android
|
||||
* Copyright (c) 2022-2022 Elshan Agaev
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sadellie.unitto.data.preferences
|
||||
|
||||
import android.os.Build
|
||||
import com.sadellie.unitto.R
|
||||
|
||||
|
||||
/**
|
||||
* All possible state of theme in the app
|
||||
*/
|
||||
object AppTheme {
|
||||
// Used on app launch when we don't know which theme to use
|
||||
const val NOT_SET = 0
|
||||
|
||||
const val AUTO = 1
|
||||
const val LIGHT = 2
|
||||
const val DARK = 3
|
||||
const val LIGHT_DYNAMIC = 4
|
||||
const val DARK_DYNAMIC = 5
|
||||
const val AMOLED = 6
|
||||
}
|
||||
|
||||
/**
|
||||
* Device specific map of available themes. Used in settings
|
||||
*/
|
||||
val APP_THEMES: Map<Int, Int> by lazy {
|
||||
// Dynamic themes are only for Android 8.1 and later
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
mapOf(
|
||||
AppTheme.AUTO to R.string.force_auto_mode,
|
||||
AppTheme.LIGHT to R.string.force_light_mode,
|
||||
AppTheme.DARK to R.string.force_dark_mode,
|
||||
AppTheme.AMOLED to R.string.force_amoled_mode,
|
||||
AppTheme.LIGHT_DYNAMIC to R.string.force_light_dynamic_mode,
|
||||
AppTheme.DARK_DYNAMIC to R.string.force_dark_dynamic_mode,
|
||||
)
|
||||
} else {
|
||||
mapOf(
|
||||
AppTheme.AUTO to R.string.force_auto_mode,
|
||||
AppTheme.LIGHT to R.string.force_light_mode,
|
||||
AppTheme.DARK to R.string.force_dark_mode,
|
||||
AppTheme.AMOLED to R.string.force_amoled_mode,
|
||||
)
|
||||
}
|
||||
}
|
@ -22,19 +22,24 @@ 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.emptyPreferences
|
||||
import androidx.datastore.preferences.core.intPreferencesKey
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import com.sadellie.unitto.data.units.AbstractUnit
|
||||
import com.sadellie.unitto.data.units.MyUnitIDS
|
||||
import io.github.sadellie.themmo.ThemingMode
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.map
|
||||
import okio.IOException
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
/**
|
||||
* Represents user preferences that are user across the app
|
||||
*
|
||||
* @property currentAppTheme Current [AppTheme] to be used
|
||||
* @property themingMode [ThemingMode] from Themmo
|
||||
* @property enableDynamicTheme Use dynamic color scheme
|
||||
* @property enableAmoledTheme Use amoled color scheme
|
||||
* @property digitsPrecision Current [PRECISIONS]. Number of digits in fractional part
|
||||
* @property separator Current [Separator] that used to separate thousands
|
||||
* @property outputFormat Current [OutputFormat] that is applied to converted value (not input)
|
||||
@ -43,7 +48,9 @@ import javax.inject.Inject
|
||||
* @property enableAnalytics Whether or not user wants to share application usage data
|
||||
*/
|
||||
data class UserPreferences(
|
||||
val currentAppTheme: Int,
|
||||
val themingMode: ThemingMode,
|
||||
val enableDynamicTheme: Boolean,
|
||||
val enableAmoledTheme: Boolean,
|
||||
val digitsPrecision: Int,
|
||||
val separator: Int,
|
||||
val outputFormat: Int,
|
||||
@ -61,7 +68,9 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS
|
||||
* Keys for DataStore
|
||||
*/
|
||||
private object PrefsKeys {
|
||||
val CURRENT_APP_THEME = intPreferencesKey("CURRENT_APP_THEME")
|
||||
val THEMING_MODE = stringPreferencesKey("THEMING_MODE_PREF_KEY")
|
||||
val ENABLE_DYNAMIC_THEME = booleanPreferencesKey("ENABLE_DYNAMIC_THEME_PREF_KEY")
|
||||
val ENABLE_AMOLED_THEME = booleanPreferencesKey("ENABLE_AMOLED_THEME_PREF_KEY")
|
||||
val DIGITS_PRECISION = intPreferencesKey("DIGITS_PRECISION_PREF_KEY")
|
||||
val SEPARATOR = intPreferencesKey("SEPARATOR_PREF_KEY")
|
||||
val OUTPUT_FORMAT = intPreferencesKey("OUTPUT_FORMAT_PREF_KEY")
|
||||
@ -71,9 +80,21 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS
|
||||
}
|
||||
|
||||
val userPreferencesFlow: Flow<UserPreferences> = dataStore.data
|
||||
.catch { exception ->
|
||||
if (exception is IOException) {
|
||||
emit(emptyPreferences())
|
||||
} else {
|
||||
throw exception
|
||||
}
|
||||
}
|
||||
.map { preferences ->
|
||||
val currentAppTheme: Int =
|
||||
preferences[PrefsKeys.CURRENT_APP_THEME] ?: AppTheme.AUTO
|
||||
val themingMode: ThemingMode =
|
||||
preferences[PrefsKeys.THEMING_MODE]?.let { ThemingMode.valueOf(it) }
|
||||
?: ThemingMode.AUTO
|
||||
val enableDynamicTheme: Boolean =
|
||||
preferences[PrefsKeys.ENABLE_DYNAMIC_THEME] ?: false
|
||||
val enableAmoledTheme: Boolean =
|
||||
preferences[PrefsKeys.ENABLE_AMOLED_THEME] ?: false
|
||||
val digitsPrecision: Int =
|
||||
preferences[PrefsKeys.DIGITS_PRECISION] ?: 3
|
||||
val separator: Int =
|
||||
@ -88,7 +109,9 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS
|
||||
preferences[PrefsKeys.ENABLE_ANALYTICS] ?: true
|
||||
|
||||
UserPreferences(
|
||||
currentAppTheme = currentAppTheme,
|
||||
themingMode = themingMode,
|
||||
enableDynamicTheme = enableDynamicTheme,
|
||||
enableAmoledTheme = enableAmoledTheme,
|
||||
digitsPrecision = digitsPrecision,
|
||||
separator = separator,
|
||||
outputFormat = outputFormat,
|
||||
@ -98,17 +121,6 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update current theme preference in DataStore
|
||||
*
|
||||
* @param appTheme [AppTheme] to change to
|
||||
*/
|
||||
suspend fun updateCurrentAppTheme(appTheme: Int) {
|
||||
dataStore.edit { preferences ->
|
||||
preferences[PrefsKeys.CURRENT_APP_THEME] = appTheme
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update precision preference in DataStore
|
||||
*
|
||||
@ -166,4 +178,37 @@ class UserPreferencesRepository @Inject constructor(private val dataStore: DataS
|
||||
preferences[PrefsKeys.LATEST_RIGHT_SIDE] = rightSideUnit.unitId
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update [ThemingMode]. Saves value as a string.
|
||||
*
|
||||
* @param themingMode [ThemingMode] to save.
|
||||
*/
|
||||
suspend fun updateThemingMode(themingMode: ThemingMode) {
|
||||
dataStore.edit { preferences ->
|
||||
preferences[PrefsKeys.THEMING_MODE] = themingMode.name
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update preference on whether or not generate color scheme from device wallpaper.
|
||||
*
|
||||
* @param enabled True if user wants to enable this feature.
|
||||
*/
|
||||
suspend fun updateDynamicTheme(enabled: Boolean) {
|
||||
dataStore.edit { preferences ->
|
||||
preferences[PrefsKeys.ENABLE_DYNAMIC_THEME] = enabled
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update preference on whether or not use true black colors.
|
||||
*
|
||||
* @param enabled True if user wants to enable this feature.
|
||||
*/
|
||||
suspend fun updateAmoledTheme(enabled: Boolean) {
|
||||
dataStore.edit { preferences ->
|
||||
preferences[PrefsKeys.ENABLE_AMOLED_THEME] = enabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.sadellie.unitto.data.KEY_0
|
||||
import com.sadellie.unitto.data.KEY_DOT
|
||||
import com.sadellie.unitto.data.KEY_MINUS
|
||||
import com.sadellie.unitto.data.preferences.AppTheme
|
||||
import com.sadellie.unitto.data.preferences.MAX_PRECISION
|
||||
import com.sadellie.unitto.data.preferences.OutputFormat
|
||||
import com.sadellie.unitto.data.preferences.Separator
|
||||
@ -59,8 +58,6 @@ class MainViewModel @Inject constructor(
|
||||
private val allUnitsRepository: AllUnitsRepository
|
||||
) : ViewModel() {
|
||||
|
||||
var currentTheme: Int by mutableStateOf(AppTheme.NOT_SET)
|
||||
private set
|
||||
var precision: Int by mutableStateOf(3)
|
||||
private set
|
||||
var separator: Int by mutableStateOf(Separator.SPACES)
|
||||
@ -70,15 +67,6 @@ class MainViewModel @Inject constructor(
|
||||
var enableAnalytics: Boolean by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
/**
|
||||
* See [UserPreferencesRepository.updateCurrentAppTheme]
|
||||
*/
|
||||
fun updateCurrentAppTheme(appTheme: Int) {
|
||||
viewModelScope.launch {
|
||||
userPrefsRepository.updateCurrentAppTheme(appTheme)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See [UserPreferencesRepository.updateDigitsPrecision]
|
||||
*/
|
||||
@ -407,13 +395,11 @@ class MainViewModel @Inject constructor(
|
||||
*
|
||||
* Any change in user preferences will update mutableStateOf so composables, that rely on this
|
||||
* values recompose when actually needed. For example, when you change output format, composable
|
||||
* in MainActivity will not be recomposed even though it needs currentTheme which is also in
|
||||
* user preferences/
|
||||
* in MainActivity will not be recomposed.
|
||||
*/
|
||||
private fun observePreferenceChanges() {
|
||||
viewModelScope.launch {
|
||||
userPrefsRepository.userPreferencesFlow.collect { userPref ->
|
||||
currentTheme = userPref.currentAppTheme
|
||||
precision = userPref.digitsPrecision
|
||||
separator = userPref.separator.also { Formatter.setSeparator(it) }
|
||||
outputFormat = userPref.outputFormat
|
||||
|
@ -18,7 +18,6 @@
|
||||
|
||||
package com.sadellie.unitto.screens.setttings
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
@ -31,7 +30,6 @@ import com.sadellie.unitto.BuildConfig
|
||||
import com.sadellie.unitto.R
|
||||
import com.sadellie.unitto.data.NavRoutes.ABOUT_SCREEN
|
||||
import com.sadellie.unitto.data.NavRoutes.THEMES_SCREEN
|
||||
import com.sadellie.unitto.data.preferences.APP_THEMES
|
||||
import com.sadellie.unitto.data.preferences.OUTPUT_FORMAT
|
||||
import com.sadellie.unitto.data.preferences.PRECISIONS
|
||||
import com.sadellie.unitto.data.preferences.SEPARATORS
|
||||
@ -91,7 +89,7 @@ fun SettingsScreen(
|
||||
SettingsListItem(
|
||||
stringResource(R.string.theme_setting),
|
||||
stringResource(R.string.theme_setting_support)
|
||||
) { dialogState = DialogState.THEME }
|
||||
) { navControllerAction(THEMES_SCREEN) }
|
||||
}
|
||||
|
||||
// CURRENCY RATE NOTE
|
||||
@ -192,19 +190,6 @@ fun SettingsScreen(
|
||||
supportText = stringResource(id = R.string.output_format_setting_info)
|
||||
)
|
||||
}
|
||||
DialogState.THEME -> {
|
||||
AlertDialogWithList(
|
||||
title = stringResource(id = R.string.theme_setting),
|
||||
listItems = APP_THEMES,
|
||||
selectedItemIndex = mainViewModel.currentTheme,
|
||||
selectAction = { mainViewModel.updateCurrentAppTheme(it) },
|
||||
dismissAction = { resetDialog() },
|
||||
// Show note for users with devices that support custom Dynamic theming
|
||||
supportText = if (Build.VERSION.SDK_INT in (Build.VERSION_CODES.O_MR1..Build.VERSION_CODES.R)) stringResource(
|
||||
id = R.string.theme_setting_info
|
||||
) else null
|
||||
)
|
||||
}
|
||||
DialogState.CURRENCY_RATE -> {
|
||||
AlertDialogWithList(
|
||||
title = stringResource(id = R.string.currency_rates_note_title),
|
||||
@ -222,5 +207,5 @@ fun SettingsScreen(
|
||||
* All possible states for alert dialog that opens when user clicks on settings.
|
||||
*/
|
||||
private enum class DialogState {
|
||||
NONE, PRECISION, SEPARATOR, OUTPUT_FORMAT, THEME, CURRENCY_RATE,
|
||||
NONE, PRECISION, SEPARATOR, OUTPUT_FORMAT, CURRENCY_RATE,
|
||||
}
|
||||
|
@ -25,12 +25,21 @@ import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
@ -121,3 +130,59 @@ fun SettingsListItem(
|
||||
) = BasicSettingsListItem(label, supportText, { onSwitchChange(switchState) }) {
|
||||
Switch(checked = switchState, onCheckedChange = { onSwitchChange(!it) })
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents one item in list on Settings screen with drop-down menu.
|
||||
*
|
||||
* @param label Main text.
|
||||
* @param supportText Text that is located below label.
|
||||
* @param allOptions Options in drop-down menu.
|
||||
* @param selected Selected option.
|
||||
* @param onSelectedChange Action to perform when drop-down menu item is selected.
|
||||
*/
|
||||
@Composable
|
||||
fun <T> SettingsListItem(
|
||||
label: String,
|
||||
supportText: String? = null,
|
||||
allOptions: Collection<T>,
|
||||
selected: T,
|
||||
onSelectedChange: (T) -> Unit
|
||||
) = BasicSettingsListItem(label, supportText, {}) {
|
||||
var dropDownExpanded by rememberSaveable { mutableStateOf(false) }
|
||||
var currentOption by rememberSaveable { mutableStateOf(selected) }
|
||||
|
||||
ExposedDropdownMenuBox(
|
||||
modifier = Modifier,
|
||||
expanded = dropDownExpanded,
|
||||
onExpandedChange = { dropDownExpanded = it }
|
||||
) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.widthIn(1.dp),
|
||||
value = currentOption.toString(),
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
singleLine = true,
|
||||
enabled = false,
|
||||
colors = TextFieldDefaults.outlinedTextFieldColors(
|
||||
disabledBorderColor = MaterialTheme.colorScheme.outline,
|
||||
disabledTextColor = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
)
|
||||
ExposedDropdownMenu(
|
||||
modifier = Modifier.exposedDropdownSize(),
|
||||
expanded = dropDownExpanded,
|
||||
onDismissRequest = { dropDownExpanded = false }
|
||||
) {
|
||||
allOptions.forEach {
|
||||
DropdownMenuItem(
|
||||
text = { Text(it.toString()) },
|
||||
onClick = {
|
||||
currentOption = it
|
||||
onSelectedChange(it)
|
||||
dropDownExpanded = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Unitto is a unit converter for Android
|
||||
* Copyright (c) 2022 Elshan Agaev
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sadellie.unitto.screens.theming
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.sadellie.unitto.R
|
||||
import com.sadellie.unitto.screens.common.UnittoLargeTopAppBar
|
||||
import com.sadellie.unitto.screens.setttings.components.SettingsListItem
|
||||
import io.github.sadellie.themmo.ThemingMode
|
||||
import io.github.sadellie.themmo.ThemmoController
|
||||
|
||||
@Composable
|
||||
fun ThemesScreen(
|
||||
navigateUpAction: () -> Unit = {},
|
||||
themmoController: ThemmoController,
|
||||
viewModel: ThemesViewModel
|
||||
) {
|
||||
UnittoLargeTopAppBar(
|
||||
title = stringResource(id = R.string.theme_setting),
|
||||
navigateUpAction = navigateUpAction
|
||||
) { paddingValues ->
|
||||
LazyColumn(contentPadding = paddingValues) {
|
||||
item {
|
||||
SettingsListItem(
|
||||
label = stringResource(R.string.theme_setting),
|
||||
allOptions = ThemingMode.values().toList(),
|
||||
selected = themmoController.currentThemingMode,
|
||||
onSelectedChange = {
|
||||
themmoController.setThemingMode(it)
|
||||
viewModel.updateThemingMode(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
SettingsListItem(
|
||||
label = stringResource(R.string.enable_dynamic_colors),
|
||||
supportText = stringResource(R.string.enable_dynamic_colors_support),
|
||||
switchState = themmoController.isDynamicThemeEnabled,
|
||||
onSwitchChange = {
|
||||
themmoController.enableDynamicTheme(!it)
|
||||
viewModel.updateDynamicTheme(!it)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
AnimatedVisibility(
|
||||
visible = (themmoController.currentThemingMode != ThemingMode.FORCE_LIGHT),
|
||||
enter = expandVertically() + fadeIn(),
|
||||
exit = shrinkVertically() + fadeOut(),
|
||||
) {
|
||||
SettingsListItem(
|
||||
label = stringResource(R.string.force_amoled_mode),
|
||||
supportText = stringResource(R.string.force_amoled_mode_support),
|
||||
switchState = themmoController.isAmoledThemeEnabled,
|
||||
onSwitchChange = {
|
||||
themmoController.enableAmoledTheme(!it)
|
||||
viewModel.updateAmoledTheme(!it)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Unitto is a unit converter for Android
|
||||
* Copyright (c) 2022 Elshan Agaev
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sadellie.unitto.screens.theming
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.sadellie.unitto.data.preferences.UserPreferencesRepository
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import io.github.sadellie.themmo.ThemingMode
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class ThemesViewModel @Inject constructor(
|
||||
private val userPrefsRepository: UserPreferencesRepository
|
||||
) : ViewModel() {
|
||||
|
||||
/**
|
||||
* @see [UserPreferencesRepository.updateThemingMode]
|
||||
*/
|
||||
fun updateThemingMode(themingMode: ThemingMode) {
|
||||
viewModelScope.launch {
|
||||
userPrefsRepository.updateThemingMode(themingMode)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see [UserPreferencesRepository.updateDynamicTheme]
|
||||
*/
|
||||
fun updateDynamicTheme(enabled: Boolean) {
|
||||
viewModelScope.launch {
|
||||
userPrefsRepository.updateDynamicTheme(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see [UserPreferencesRepository.updateAmoledTheme]
|
||||
*/
|
||||
fun updateAmoledTheme(enabled: Boolean) {
|
||||
viewModelScope.launch {
|
||||
userPrefsRepository.updateAmoledTheme(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
var themingMode: ThemingMode? by mutableStateOf(null)
|
||||
var enableDynamic by mutableStateOf(false)
|
||||
var enableAmoled by mutableStateOf(false)
|
||||
|
||||
/**
|
||||
* Collect saved theming options. Used on app launch.
|
||||
*/
|
||||
fun collectThemeOptions() {
|
||||
viewModelScope.launch {
|
||||
val userPref = userPrefsRepository.userPreferencesFlow.first()
|
||||
themingMode = userPref.themingMode
|
||||
enableDynamic = userPref.enableDynamicTheme
|
||||
enableAmoled = userPref.enableAmoledTheme
|
||||
}
|
||||
}
|
||||
}
|
@ -73,5 +73,3 @@ val md_theme_dark_onSurfaceVariant = Color(0xFFc1c9be)
|
||||
val md_theme_dark_outline = Color(0xFF8b9389)
|
||||
val md_theme_dark_inverseOnSurface = Color(0xFF1a1c19)
|
||||
val md_theme_dark_inverseSurface = Color(0xFFe1e3dd)
|
||||
|
||||
val md_theme_amoled_black = Color(0xFF000000)
|
||||
|
@ -1,180 +0,0 @@
|
||||
/*
|
||||
* Unitto is a unit converter for Android
|
||||
* Copyright (c) 2022-2022 Elshan Agaev
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sadellie.unitto.ui.theme
|
||||
|
||||
import android.app.WallpaperManager
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import androidx.compose.material3.ColorScheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.luminance
|
||||
|
||||
|
||||
/**
|
||||
* Shifts colors to make them brighter
|
||||
*/
|
||||
@Stable
|
||||
private fun Color.shiftTo255(ratio: Float): Color {
|
||||
return this
|
||||
.copy(
|
||||
alpha,
|
||||
red + (1.0f - red) * ratio,
|
||||
green + (1.0f - green) * ratio,
|
||||
blue + (1.0f - blue) * ratio,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shifts colors to make them darker
|
||||
*/
|
||||
@Stable
|
||||
private fun Color.shiftTo0(ratio: Float): Color {
|
||||
return this
|
||||
.copy(
|
||||
alpha,
|
||||
red * (1.0f - ratio),
|
||||
green * (1.0f - ratio),
|
||||
blue * (1.0f - ratio),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides which colors fits the best for the color that is used as a background
|
||||
*/
|
||||
@Stable
|
||||
private fun Color.getAppropriateTextColor(): Color {
|
||||
return if (luminance() > 0.5) Color.Black else Color.White
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to return dynamic light theme. Determines which API to use based on Android version
|
||||
*/
|
||||
fun dynamicLightTheme(context: Context): ColorScheme {
|
||||
return when {
|
||||
// Android 12+ devices use new api
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> dynamicLightColorScheme(context)
|
||||
// Dynamic theming for devices with Android 8.1 up to Android 11
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 -> {
|
||||
// Wallpaper colors can be null. We return default theme for such cases
|
||||
val wallpaperColors = WallpaperManager.getInstance(context)
|
||||
.getWallpaperColors(WallpaperManager.FLAG_SYSTEM)
|
||||
?: return LightThemeColors
|
||||
|
||||
val primary = Color(
|
||||
red = wallpaperColors.primaryColor.red(),
|
||||
green = wallpaperColors.primaryColor.green(),
|
||||
blue = wallpaperColors.primaryColor.blue()
|
||||
)
|
||||
// Secondary color can be null. For such cases we just generate it from primary
|
||||
val secondary: Color = primary.shiftTo255(0.5f)
|
||||
val background = primary.shiftTo255(0.9f)
|
||||
val onBackground = background.getAppropriateTextColor()
|
||||
|
||||
return lightColorScheme(
|
||||
// Settings screen group text, units screen units group text
|
||||
primary = primary,
|
||||
// Switch thumb color
|
||||
onPrimary = primary.getAppropriateTextColor(),
|
||||
onPrimaryContainer = primary.shiftTo0(0.7f),
|
||||
// Selected unit, group, keyboard buttons
|
||||
secondaryContainer = secondary,
|
||||
onSecondaryContainer = secondary.getAppropriateTextColor(),
|
||||
// Background color for all screens
|
||||
background = background,
|
||||
// Main screen input/output text color
|
||||
onBackground = onBackground,
|
||||
// Collapsable top bar background
|
||||
surface = background,
|
||||
// Main screen Unitto text, disabled buttons
|
||||
// Settings screen title and back icon, dialog window title, not selected radio button color
|
||||
// Third party licenses screen title and back icon
|
||||
onSurface = onBackground,
|
||||
surfaceVariant = primary.shiftTo255(0.5f),
|
||||
// Main Screen settings icon, Not selected chips text, short unit names
|
||||
// Settings items secondary text
|
||||
onSurfaceVariant = primary.shiftTo0(0.80f),
|
||||
// Chips outline and cards outline on Third party licenses screen
|
||||
outline = primary.shiftTo0(0.8f),
|
||||
)
|
||||
}
|
||||
// Just in case
|
||||
else -> LightThemeColors
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to return dynamic light theme. Determines which API to use based on Android version
|
||||
*/
|
||||
fun dynamicDarkTheme(context: Context): ColorScheme {
|
||||
return when {
|
||||
// Android 12+ devices use new api
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> dynamicDarkColorScheme(context)
|
||||
// Dynamic theming for devices with Android 8.1 up to Android 11
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 -> {
|
||||
// Wallpaper colors can be null. We return default theme for such cases
|
||||
val wallpaperColors = WallpaperManager.getInstance(context)
|
||||
.getWallpaperColors(WallpaperManager.FLAG_SYSTEM)
|
||||
?: return DarkThemeColors
|
||||
|
||||
val primary = Color(
|
||||
red = wallpaperColors.primaryColor.red(),
|
||||
green = wallpaperColors.primaryColor.green(),
|
||||
blue = wallpaperColors.primaryColor.blue()
|
||||
)
|
||||
// Secondary color can be null. For such cases we just generate it from primary
|
||||
val secondary: Color = primary.shiftTo0(0.7f)
|
||||
val background = primary.shiftTo0(0.9f)
|
||||
val onBackground = background.getAppropriateTextColor()
|
||||
|
||||
return darkColorScheme(
|
||||
// Settings screen group text, units screen units group text
|
||||
primary = primary,
|
||||
// Switch thumb color
|
||||
onPrimary = primary.getAppropriateTextColor(),
|
||||
onPrimaryContainer = primary.shiftTo255(0.7f),
|
||||
// Selected unit, group, keyboard buttons
|
||||
secondaryContainer = secondary,
|
||||
onSecondaryContainer = secondary.getAppropriateTextColor(),
|
||||
// Background color for all screens
|
||||
background = background,
|
||||
// Main screen input/output text color
|
||||
onBackground = onBackground,
|
||||
// Collapsable top bar background
|
||||
surface = background,
|
||||
// Main screen Unitto text, disabled buttons
|
||||
// Settings screen title and back icon, dialog window title, not selected radio button color
|
||||
// Third party licenses screen title and back icon
|
||||
onSurface = onBackground,
|
||||
surfaceVariant = primary.shiftTo0(0.5f),
|
||||
// Main Screen settings icon, Not selected chips text, short unit names
|
||||
// Settings items secondary text
|
||||
onSurfaceVariant = primary.shiftTo255(0.80f),
|
||||
// Chips outline and cards outline on Third party licenses screen
|
||||
outline = primary.shiftTo255(0.8f),
|
||||
)
|
||||
}
|
||||
// Just in case
|
||||
else -> DarkThemeColors
|
||||
}
|
||||
}
|
@ -80,10 +80,3 @@ val DarkThemeColors by lazy {
|
||||
inverseSurface = md_theme_dark_inverseSurface,
|
||||
)
|
||||
}
|
||||
|
||||
val AmoledThemeColors by lazy {
|
||||
DarkThemeColors.copy(
|
||||
background = md_theme_amoled_black,
|
||||
surface = md_theme_amoled_black,
|
||||
)
|
||||
}
|
||||
|
@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Unitto is a unit converter for Android
|
||||
* Copyright (c) 2022-2022 Elshan Agaev
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sadellie.unitto.ui.theme
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import com.sadellie.unitto.data.preferences.AppTheme
|
||||
|
||||
|
||||
@Composable
|
||||
fun UnittoTheme(
|
||||
currentAppTheme: Int,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
// Dynamic color is only for Android 12 and higher
|
||||
val dynamicColor = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1
|
||||
val sysUiController = rememberSystemUiController()
|
||||
|
||||
val colors = when (currentAppTheme) {
|
||||
AppTheme.DARK -> DarkThemeColors
|
||||
AppTheme.LIGHT -> LightThemeColors
|
||||
AppTheme.AMOLED -> AmoledThemeColors
|
||||
AppTheme.LIGHT_DYNAMIC -> dynamicLightTheme(LocalContext.current)
|
||||
AppTheme.DARK_DYNAMIC -> dynamicDarkTheme(LocalContext.current)
|
||||
// When it is set to Auto
|
||||
else -> {
|
||||
when {
|
||||
dynamicColor and !isSystemInDarkTheme() -> dynamicLightTheme(LocalContext.current)
|
||||
dynamicColor and isSystemInDarkTheme() -> dynamicDarkTheme(LocalContext.current)
|
||||
!dynamicColor and !isSystemInDarkTheme() -> LightThemeColors
|
||||
!dynamicColor and isSystemInDarkTheme() -> DarkThemeColors
|
||||
// This case is here just in case, not actually used
|
||||
else -> LightThemeColors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colors,
|
||||
typography = AppTypography,
|
||||
content = content
|
||||
)
|
||||
|
||||
SideEffect {
|
||||
sysUiController.setSystemBarsColor(
|
||||
color = colors.background
|
||||
)
|
||||
}
|
||||
}
|
@ -675,7 +675,7 @@
|
||||
<string name="settings_screen">Settings</string>
|
||||
|
||||
<!--Settings items-->
|
||||
<string name="theme_setting">Theme</string>
|
||||
<string name="theme_setting">Themes</string>
|
||||
<string name="precision_setting">Precision</string>
|
||||
<string name="separator_setting">Separator</string>
|
||||
<string name="output_format_setting">Output format</string>
|
||||
@ -717,8 +717,6 @@
|
||||
<string name="force_light_mode">Light</string>
|
||||
<string name="force_dark_mode">Dark</string>
|
||||
<string name="force_amoled_mode">AMOLED Dark</string>
|
||||
<string name="force_light_dynamic_mode">Dynamic light</string>
|
||||
<string name="force_dark_dynamic_mode">Dynamic dark</string>
|
||||
|
||||
<!--MISC.-->
|
||||
<string name="loading_label">Loading…</string>
|
||||
@ -738,5 +736,8 @@
|
||||
<string name="clear_input_description">Clear input</string>
|
||||
<string name="favorite_button_description">Add or remove unit from favorites</string>
|
||||
<string name="empty_search_result_description">Empty search result</string>
|
||||
<string name="enable_dynamic_colors">Dynamic colors</string>
|
||||
<string name="enable_dynamic_colors_support">Generate theme from your current wallpaper</string>
|
||||
<string name="force_amoled_mode_support">Use black background for dark themes</string>
|
||||
|
||||
</resources>
|
@ -492,7 +492,7 @@
|
||||
<string name="units_screen_from">Перевести из</string>
|
||||
<string name="units_screen_to">Перевести в</string>
|
||||
<string name="settings_screen">Настройки</string>
|
||||
<string name="theme_setting">Тема</string>
|
||||
<string name="theme_setting">Темы</string>
|
||||
<string name="precision_setting">Точность</string>
|
||||
<string name="separator_setting">Разделитель</string>
|
||||
<string name="output_format_setting">Формат вывода</string>
|
||||
@ -523,8 +523,6 @@
|
||||
<string name="force_light_mode">Светлая</string>
|
||||
<string name="force_dark_mode">Темная</string>
|
||||
<string name="force_amoled_mode">Темная AMOLED</string>
|
||||
<string name="force_light_dynamic_mode">Динамическая светлая</string>
|
||||
<string name="force_dark_dynamic_mode">Динамическая темная</string>
|
||||
<string name="loading_label">Загрузка…</string>
|
||||
<string name="error_label">Ошибка</string>
|
||||
<string name="copied">Скопировано %1$s!</string>
|
||||
@ -673,5 +671,8 @@
|
||||
<string name="send_usage_statistics">Отправлять статистику использования</string>
|
||||
<string name="send_usage_statistics_support">Все данные анонимны и зашифрованы</string>
|
||||
<string name="app_version_name_setting">Название версии</string>
|
||||
<string name="enable_dynamic_colors">Динамичные цвета</string>
|
||||
<string name="enable_dynamic_colors_support">Использовать цвета обоев рабочего стола</string>
|
||||
<string name="force_amoled_mode_support">Использовать черный фон в темных темах</string>
|
||||
|
||||
</resources>
|
@ -936,7 +936,7 @@
|
||||
<string name="settings_screen">Settings</string>
|
||||
|
||||
<!--Settings items-->
|
||||
<string name="theme_setting">Theme</string>
|
||||
<string name="theme_setting">Themes</string>
|
||||
<string name="precision_setting">Precision</string>
|
||||
<string name="separator_setting">Separator</string>
|
||||
<string name="output_format_setting">Output format</string>
|
||||
@ -991,8 +991,9 @@
|
||||
<string name="force_light_mode">Light</string>
|
||||
<string name="force_dark_mode">Dark</string>
|
||||
<string name="force_amoled_mode">AMOLED Dark</string>
|
||||
<string name="force_light_dynamic_mode">Dynamic light</string>
|
||||
<string name="force_dark_dynamic_mode">Dynamic dark</string>
|
||||
<string name="force_amoled_mode_support">Use black background for dark themes</string>
|
||||
<string name="enable_dynamic_colors">Dynamic colors</string>
|
||||
<string name="enable_dynamic_colors_support">Generate theme from your current wallpaper</string>
|
||||
|
||||
<!--MISC.-->
|
||||
<string name="loading_label">Loading…</string>
|
||||
|
@ -3,6 +3,7 @@ dependencyResolutionManagement {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven(url = "https://jitpack.io")
|
||||
}
|
||||
}
|
||||
rootProject.name = "Unitto"
|
||||
|
Loading…
x
Reference in New Issue
Block a user