From 2546a9c3c8e5345b5cc840124bba020672e82f33 Mon Sep 17 00:00:00 2001 From: BinTianqi Date: Sat, 16 Nov 2024 12:48:01 +0800 Subject: [PATCH] Set Dark theme in Settings Add MyViewModel to keep ThemeSettings state Use ListItem to display SSIDs in WiFi SSID Policy Use green color scheme as default color scheme Add Compose BOM dependency, update dependencies --- app/build.gradle.kts | 1 + .../bintianqi/owndroid/InstallAppActivity.kt | 8 +- .../com/bintianqi/owndroid/MainActivity.kt | 18 +- .../bintianqi/owndroid/ManageSpaceActivity.kt | 7 +- .../com/bintianqi/owndroid/MyViewModel.kt | 37 +++++ .../java/com/bintianqi/owndroid/Setting.kt | 73 ++++++--- .../main/java/com/bintianqi/owndroid/Utils.kt | 4 +- .../com/bintianqi/owndroid/dpm/Network.kt | 71 +++----- .../com/bintianqi/owndroid/dpm/UserManager.kt | 4 +- .../com/bintianqi/owndroid/ui/theme/Color.kt | 129 ++++++++------- .../com/bintianqi/owndroid/ui/theme/Theme.kt | 155 +++++++++--------- app/src/main/res/values-ru/strings.xml | 14 +- app/src/main/res/values-tr/strings.xml | 14 +- app/src/main/res/values-zh-rCN/strings.xml | 14 +- app/src/main/res/values/strings.xml | 14 +- gradle/libs.versions.toml | 12 +- 16 files changed, 316 insertions(+), 259 deletions(-) create mode 100644 app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index bce0c28..23d9828 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -76,6 +76,7 @@ gradle.taskGraph.whenReady { dependencies { implementation(libs.androidx.activity.compose) + implementation(platform(libs.androidx.compose.bom)) implementation(libs.accompanist.drawablepainter) implementation(libs.androidx.material3) implementation(libs.androidx.navigation.compose) diff --git a/app/src/main/java/com/bintianqi/owndroid/InstallAppActivity.kt b/app/src/main/java/com/bintianqi/owndroid/InstallAppActivity.kt index 21b9023..1c94198 100644 --- a/app/src/main/java/com/bintianqi/owndroid/InstallAppActivity.kt +++ b/app/src/main/java/com/bintianqi/owndroid/InstallAppActivity.kt @@ -7,6 +7,7 @@ import android.graphics.drawable.ColorDrawable import android.os.Bundle import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.activity.viewModels import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth @@ -64,11 +65,10 @@ class InstallAppActivity: FragmentActivity() { apkInfoText += "${context.getString(R.string.version_code)}: ${apkInfo.versionCode}" } } + val vm by viewModels() + if(!vm.initialized) vm.initialize(applicationContext) setContent { - OwnDroidTheme( - sharedPref.getBoolean("material_you", true), - sharedPref.getBoolean("black_theme", false) - ) { + OwnDroidTheme(vm) { AlertDialog( properties = DialogProperties(dismissOnBackPress = false, dismissOnClickOutside = false), title = { diff --git a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt index 4c65381..3dd3953 100644 --- a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt +++ b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt @@ -7,6 +7,7 @@ import android.os.Bundle import android.widget.Toast import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.activity.viewModels import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.detectTapGestures @@ -30,7 +31,6 @@ import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.MutableState import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf @@ -85,7 +85,7 @@ class MainActivity : FragmentActivity() { enableEdgeToEdge() WindowCompat.setDecorFitsSystemWindows(window, false) super.onCreate(savedInstanceState) - val sharedPref = applicationContext.getSharedPreferences("data", Context.MODE_PRIVATE) + val sharedPref = applicationContext.getSharedPreferences("data", MODE_PRIVATE) if (VERSION.SDK_INT >= 28) HiddenApiBypass.setHiddenApiExemptions("") if(sharedPref.getBoolean("auth", false)) { showAuth.value = true @@ -93,11 +93,11 @@ class MainActivity : FragmentActivity() { val locale = applicationContext.resources?.configuration?.locale zhCN = locale == Locale.SIMPLIFIED_CHINESE || locale == Locale.CHINESE || locale == Locale.CHINA toggleInstallAppActivity() + val vm by viewModels() + if(!vm.initialized) vm.initialize(applicationContext) setContent { - val materialYou = remember { mutableStateOf(sharedPref.getBoolean("material_you", true)) } - val blackTheme = remember { mutableStateOf(sharedPref.getBoolean("black_theme", false)) } - OwnDroidTheme(materialYou.value, blackTheme.value) { - Home(materialYou, blackTheme) + OwnDroidTheme(vm) { + Home(vm) if(showAuth.value) { AuthScreen(this, showAuth) } @@ -107,7 +107,7 @@ class MainActivity : FragmentActivity() { override fun onResume() { super.onResume() - val sharedPref = applicationContext.getSharedPreferences("data", Context.MODE_PRIVATE) + val sharedPref = applicationContext.getSharedPreferences("data", MODE_PRIVATE) if( sharedPref.getBoolean("auth", false) && sharedPref.getBoolean("lock_in_background", false) @@ -128,7 +128,7 @@ class MainActivity : FragmentActivity() { @ExperimentalMaterial3Api @Composable -fun Home(materialYou:MutableState, blackTheme:MutableState) { +fun Home(vm: MyViewModel) { val navCtrl = rememberNavController() val context = LocalContext.current val dpm = context.getDPM() @@ -161,7 +161,7 @@ fun Home(materialYou:MutableState, blackTheme:MutableState) { composable(route = "UserRestriction") { UserRestriction(navCtrl) } composable(route = "UserManage") { UserManage(navCtrl) } composable(route = "Password") { Password(navCtrl) } - composable(route = "AppSetting") { AppSetting(navCtrl, materialYou, blackTheme) } + composable(route = "AppSetting") { AppSetting(navCtrl, vm) } composable(route = "Network") { Network(navCtrl) } composable(route = "PackageSelector") { PackageSelector(navCtrl) } } diff --git a/app/src/main/java/com/bintianqi/owndroid/ManageSpaceActivity.kt b/app/src/main/java/com/bintianqi/owndroid/ManageSpaceActivity.kt index 07a942f..dc1d2a4 100644 --- a/app/src/main/java/com/bintianqi/owndroid/ManageSpaceActivity.kt +++ b/app/src/main/java/com/bintianqi/owndroid/ManageSpaceActivity.kt @@ -3,6 +3,7 @@ package com.bintianqi.owndroid import android.os.Bundle import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.activity.viewModels import androidx.compose.material3.AlertDialog import androidx.compose.material3.Text import androidx.compose.material3.TextButton @@ -17,11 +18,11 @@ class ManageSpaceActivity: FragmentActivity() { WindowCompat.setDecorFitsSystemWindows(window, false) super.onCreate(savedInstanceState) val sharedPref = applicationContext.getSharedPreferences("data", MODE_PRIVATE) - val materialYou = sharedPref.getBoolean("material_you", true) - val blackTheme = sharedPref.getBoolean("black_theme", false) val protected = sharedPref.getBoolean("protect_storage", false) + val vm by viewModels() + if(!vm.initialized) vm.initialize(applicationContext) setContent { - OwnDroidTheme(materialYou, blackTheme) { + OwnDroidTheme(vm) { AlertDialog( title = { Text(stringResource(R.string.clear_storage)) diff --git a/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt b/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt new file mode 100644 index 0000000..0c5dc74 --- /dev/null +++ b/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt @@ -0,0 +1,37 @@ +package com.bintianqi.owndroid + +import android.content.Context +import android.os.Build +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.launch + +class MyViewModel: ViewModel() { + val theme = MutableStateFlow(ThemeSettings()) + + var initialized = false + fun initialize(context: Context) { + val sharedPrefs = context.getSharedPreferences("data", Context.MODE_PRIVATE) + theme.value = ThemeSettings( + materialYou = sharedPrefs.getBoolean("material_you", Build.VERSION.SDK_INT >= 31), + darkTheme = if(sharedPrefs.contains("dark_theme")) sharedPrefs.getBoolean("dark_theme", false) else null, + blackTheme = sharedPrefs.getBoolean("black_theme", false) + ) + viewModelScope.launch { + theme.collect { + val editor = sharedPrefs.edit() + editor.putBoolean("material_you", it.materialYou) + if(it.darkTheme == null) editor.remove("dark_theme") else editor.putBoolean("dark_theme", it.darkTheme) + editor.putBoolean("black_theme", it.blackTheme) + editor.commit() + } + } + } +} + +data class ThemeSettings( + val materialYou: Boolean = false, + val darkTheme: Boolean? = null, + val blackTheme: Boolean = false +) diff --git a/app/src/main/java/com/bintianqi/owndroid/Setting.kt b/app/src/main/java/com/bintianqi/owndroid/Setting.kt index aa7a981..cea4b10 100644 --- a/app/src/main/java/com/bintianqi/owndroid/Setting.kt +++ b/app/src/main/java/com/bintianqi/owndroid/Setting.kt @@ -4,9 +4,10 @@ import android.content.Context import android.content.Intent import android.net.Uri import android.os.Build.VERSION -import android.util.Base64 import android.widget.Toast +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -15,6 +16,8 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Button +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme.typography @@ -22,7 +25,6 @@ import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -31,7 +33,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable @@ -44,7 +48,7 @@ import com.bintianqi.owndroid.ui.TopBar import java.security.SecureRandom @Composable -fun AppSetting(navCtrl:NavHostController, materialYou: MutableState, blackTheme: MutableState) { +fun AppSetting(navCtrl:NavHostController, vm: MyViewModel) { val localNavCtrl = rememberNavController() val backStackEntry by localNavCtrl.currentBackStackEntryAsState() Scaffold( @@ -62,7 +66,7 @@ fun AppSetting(navCtrl:NavHostController, materialYou: MutableState, bl ) { composable(route = "Home") { Home(localNavCtrl) } composable(route = "Options") { Options() } - composable(route = "Theme") { ThemeSettings(materialYou, blackTheme) } + composable(route = "Theme") { ThemeSettings(vm) } composable(route = "Auth") { AuthSettings() } composable(route = "Automation") { Automation() } composable(route = "About") { About() } @@ -94,27 +98,56 @@ private fun Options() { } @Composable -private fun ThemeSettings(materialYou:MutableState, blackTheme:MutableState) { - val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE) - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(start = 20.dp, end = 16.dp)) { +private fun ThemeSettings(vm: MyViewModel) { + val theme by vm.theme.collectAsStateWithLifecycle() + var darkThemeMenu by remember { mutableStateOf(false) } + val darkThemeTextID = when(theme.darkTheme) { + true -> R.string.on + false -> R.string.off + null -> R.string.follow_system + } + Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) { if(VERSION.SDK_INT >= 31) { SwitchItem( - R.string.material_you_color, stringResource(R.string.dynamic_color_desc), null, - { sharedPref.getBoolean("material_you",true) }, - { - sharedPref.edit().putBoolean("material_you", it).apply() - materialYou.value = it - }, padding = false + R.string.material_you_color, "", null, + theme.materialYou, + { vm.theme.value = theme.copy(materialYou = it) } ) } - if(isSystemInDarkTheme()) { + Box { + SubPageItem(R.string.dark_theme, stringResource(darkThemeTextID)) { darkThemeMenu = true } + DropdownMenu( + expanded = darkThemeMenu, onDismissRequest = { darkThemeMenu = false }, + offset = DpOffset(x = 30.dp, y = 0.dp) + ) { + DropdownMenuItem( + text = { Text(stringResource(R.string.follow_system)) }, + onClick = { + vm.theme.value = theme.copy(darkTheme = null) + darkThemeMenu = false + } + ) + DropdownMenuItem( + text = { Text(stringResource(R.string.on)) }, + onClick = { + vm.theme.value = theme.copy(darkTheme = true) + darkThemeMenu = false + } + ) + DropdownMenuItem( + text = { Text(stringResource(R.string.off)) }, + onClick = { + vm.theme.value = theme.copy(darkTheme = false) + darkThemeMenu = false + } + ) + } + } + AnimatedVisibility(theme.darkTheme == true || (theme.darkTheme == null && isSystemInDarkTheme())) { SwitchItem( - R.string.amoled_black, stringResource(R.string.blackTheme_desc), null, - { sharedPref.getBoolean("black_theme",false) }, - { - sharedPref.edit().putBoolean("black_theme", it).apply() - blackTheme.value = it - }, padding = false + R.string.black_theme, "", null, + theme.blackTheme, + { vm.theme.value = theme.copy(blackTheme = it) } ) } } diff --git a/app/src/main/java/com/bintianqi/owndroid/Utils.kt b/app/src/main/java/com/bintianqi/owndroid/Utils.kt index 2707ac1..b7b98bd 100644 --- a/app/src/main/java/com/bintianqi/owndroid/Utils.kt +++ b/app/src/main/java/com/bintianqi/owndroid/Utils.kt @@ -68,7 +68,7 @@ fun registerActivityResult(context: ComponentActivity){ activityResult.data.let { if(it == null){ Toast.makeText(context.applicationContext, R.string.file_not_exist, Toast.LENGTH_SHORT).show() - }else{ + } else { fileUriFlow.value = it.data } } @@ -76,7 +76,7 @@ fun registerActivityResult(context: ComponentActivity){ createManagedProfile = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {} addDeviceAdmin = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { val dpm = context.applicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager - if(dpm.isAdminActive(ComponentName(context.applicationContext, Receiver::class.java))){ + if(dpm.isAdminActive(ComponentName(context.applicationContext, Receiver::class.java))) { backToHomeStateFlow.value = true } } diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt index e4099c8..2f1bd5d 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt @@ -111,6 +111,7 @@ import com.bintianqi.owndroid.formatFileSize import com.bintianqi.owndroid.selectedPackage import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.CheckBoxItem +import com.bintianqi.owndroid.ui.ListItem import com.bintianqi.owndroid.ui.RadioButtonItem import com.bintianqi.owndroid.ui.SubPageItem import com.bintianqi.owndroid.ui.SwitchItem @@ -307,7 +308,7 @@ private fun WifiSsidPolicy() { val policy = dpm.wifiSsidPolicy ssidList.clear() selectedPolicyType = policy?.policyType ?: -1 - (policy?.ssids ?: mutableSetOf()).forEach { ssidList.add(it) } + ssidList.addAll(policy?.ssids ?: mutableSetOf()) } LaunchedEffect(Unit) { refreshPolicy() } Spacer(Modifier.padding(vertical = 10.dp)) @@ -331,68 +332,46 @@ private fun WifiSsidPolicy() { AnimatedVisibility(selectedPolicyType != -1) { var inputSsid by remember { mutableStateOf("") } Column { - Column { - Spacer(Modifier.padding(vertical = 5.dp)) - Text(stringResource(R.string.ssid_list_is)) - SelectionContainer(modifier = Modifier.animateContentSize().horizontalScroll(rememberScrollState())) { - Text(if(ssidList.isEmpty()) stringResource(R.string.none) else ssidList.joinToString(separator = "\n")) + Text(stringResource(R.string.ssid_list_is)) + if(ssidList.isEmpty()) Text(stringResource(R.string.none)) + Column(modifier = Modifier.animateContentSize()) { + for(i in ssidList) { + ListItem(i.bytes.decodeToString()) { ssidList -= i } } } Spacer(Modifier.padding(vertical = 5.dp)) OutlinedTextField( value = inputSsid, label = { Text("SSID") }, - onValueChange = {inputSsid = it }, + onValueChange = { inputSsid = it }, + trailingIcon = { + IconButton( + onClick = { + ssidList += WifiSsid.fromBytes(inputSsid.encodeToByteArray()) + inputSsid = "" + }, + enabled = inputSsid != "" + ) { + Icon(imageVector = Icons.Default.Add, contentDescription = stringResource(R.string.add)) + } + }, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), modifier = Modifier.fillMaxWidth() ) - Spacer(Modifier.padding(vertical = 5.dp)) - Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { - Button( - onClick = { - if(inputSsid == "") { - Toast.makeText(context, R.string.cannot_be_empty, Toast.LENGTH_SHORT).show() - } else if (WifiSsid.fromBytes(inputSsid.toByteArray()) in ssidList) { - Toast.makeText(context, R.string.already_exist, Toast.LENGTH_SHORT).show() - } else { - ssidList.add(WifiSsid.fromBytes(inputSsid.toByteArray())) - } - }, - modifier = Modifier.fillMaxWidth(0.49F) - ) { - Text(stringResource(R.string.add)) - } - Button( - onClick = { - if(inputSsid == "") { - Toast.makeText(context, R.string.cannot_be_empty, Toast.LENGTH_SHORT).show() - } else if (WifiSsid.fromBytes(inputSsid.toByteArray()) in ssidList) { - ssidList.remove(WifiSsid.fromBytes(inputSsid.toByteArray())) - } else { - Toast.makeText(context, R.string.not_exist, Toast.LENGTH_SHORT).show() - } - }, - modifier = Modifier.fillMaxWidth(0.96F) - ) { - Text(stringResource(R.string.remove)) - } - } Spacer(Modifier.padding(vertical = 10.dp)) } } Button( onClick = { focusMgr.clearFocus() - if(selectedPolicyType == -1) { - dpm.wifiSsidPolicy = null - refreshPolicy() - Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() - }else{ - dpm.wifiSsidPolicy = if(ssidList.isEmpty()) { null }else{ WifiSsidPolicy(selectedPolicyType, ssidList.toSet()) } - refreshPolicy() - Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() + dpm.wifiSsidPolicy = if(selectedPolicyType == -1 || ssidList.isEmpty()) { + null + } else { + WifiSsidPolicy(selectedPolicyType, ssidList.toSet()) } + refreshPolicy() + Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() }, modifier = Modifier.fillMaxWidth() ) { diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/UserManager.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/UserManager.kt index 3c025ea..5283e09 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/UserManager.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/UserManager.kt @@ -278,7 +278,7 @@ private fun UserOperation() { }, modifier = Modifier.fillMaxWidth() ) { - Text(stringResource(R.string.user_operation_stop)) + Text(stringResource(R.string.stop)) } } Button( @@ -293,7 +293,7 @@ private fun UserOperation() { }, modifier = Modifier.fillMaxWidth() ) { - Text(stringResource(R.string.user_operation_remove)) + Text(stringResource(R.string.delete)) } Spacer(Modifier.padding(vertical = 30.dp)) } diff --git a/app/src/main/java/com/bintianqi/owndroid/ui/theme/Color.kt b/app/src/main/java/com/bintianqi/owndroid/ui/theme/Color.kt index d45aadb..210021d 100644 --- a/app/src/main/java/com/bintianqi/owndroid/ui/theme/Color.kt +++ b/app/src/main/java/com/bintianqi/owndroid/ui/theme/Color.kt @@ -2,62 +2,75 @@ package com.bintianqi.owndroid.ui.theme import androidx.compose.ui.graphics.Color -val md_theme_light_primary = Color(0xFF006A65) -val md_theme_light_onPrimary = Color(0xFFFFFFFF) -val md_theme_light_primaryContainer = Color(0xFF70F7EE) -val md_theme_light_onPrimaryContainer = Color(0xFF00201E) -val md_theme_light_secondary = Color(0xFF4A6361) -val md_theme_light_onSecondary = Color(0xFFFFFFFF) -val md_theme_light_secondaryContainer = Color(0xFFCCE8E5) -val md_theme_light_onSecondaryContainer = Color(0xFF051F1E) -val md_theme_light_tertiary = Color(0xFF48607B) -val md_theme_light_onTertiary = Color(0xFFFFFFFF) -val md_theme_light_tertiaryContainer = Color(0xFFD0E4FF) -val md_theme_light_onTertiaryContainer = Color(0xFF001D34) -val md_theme_light_error = Color(0xFFBA1A1A) -val md_theme_light_errorContainer = Color(0xFFFFDAD6) -val md_theme_light_onError = Color(0xFFFFFFFF) -val md_theme_light_onErrorContainer = Color(0xFF410002) -val md_theme_light_background = Color(0xFFFAFDFB) -val md_theme_light_onBackground = Color(0xFF191C1C) -val md_theme_light_surface = Color(0xFFFAFDFB) -val md_theme_light_onSurface = Color(0xFF191C1C) -val md_theme_light_surfaceVariant = Color(0xFFDAE5E3) -val md_theme_light_onSurfaceVariant = Color(0xFF3F4947) -val md_theme_light_outline = Color(0xFF6F7978) -val md_theme_light_inverseOnSurface = Color(0xFFEFF1F0) -val md_theme_light_inverseSurface = Color(0xFF2D3130) -val md_theme_light_inversePrimary = Color(0xFF4FDAD1) -val md_theme_light_surfaceTint = Color(0xFF006A65) -val md_theme_light_outlineVariant = Color(0xFFBEC9C7) -val md_theme_light_scrim = Color(0xFF000000) +val primaryLight = Color(0xFF4C662B) +val onPrimaryLight = Color(0xFFFFFFFF) +val primaryContainerLight = Color(0xFFCDEDA3) +val onPrimaryContainerLight = Color(0xFF102000) +val secondaryLight = Color(0xFF586249) +val onSecondaryLight = Color(0xFFFFFFFF) +val secondaryContainerLight = Color(0xFFDCE7C8) +val onSecondaryContainerLight = Color(0xFF151E0B) +val tertiaryLight = Color(0xFF386663) +val onTertiaryLight = Color(0xFFFFFFFF) +val tertiaryContainerLight = Color(0xFFBCECE7) +val onTertiaryContainerLight = Color(0xFF00201E) +val errorLight = Color(0xFFBA1A1A) +val onErrorLight = Color(0xFFFFFFFF) +val errorContainerLight = Color(0xFFFFDAD6) +val onErrorContainerLight = Color(0xFF410002) +val backgroundLight = Color(0xFFF9FAEF) +val onBackgroundLight = Color(0xFF1A1C16) +val surfaceLight = Color(0xFFF9FAEF) +val onSurfaceLight = Color(0xFF1A1C16) +val surfaceVariantLight = Color(0xFFE1E4D5) +val onSurfaceVariantLight = Color(0xFF44483D) +val outlineLight = Color(0xFF75796C) +val outlineVariantLight = Color(0xFFC5C8BA) +val scrimLight = Color(0xFF000000) +val inverseSurfaceLight = Color(0xFF2F312A) +val inverseOnSurfaceLight = Color(0xFFF1F2E6) +val inversePrimaryLight = Color(0xFFB1D18A) +val surfaceDimLight = Color(0xFFDADBD0) +val surfaceBrightLight = Color(0xFFF9FAEF) +val surfaceContainerLowestLight = Color(0xFFFFFFFF) +val surfaceContainerLowLight = Color(0xFFF3F4E9) +val surfaceContainerLight = Color(0xFFEEEFE3) +val surfaceContainerHighLight = Color(0xFFE8E9DE) +val surfaceContainerHighestLight = Color(0xFFE2E3D8) -val md_theme_dark_primary = Color(0xFF4FDAD1) -val md_theme_dark_onPrimary = Color(0xFF003734) -val md_theme_dark_primaryContainer = Color(0xFF00504C) -val md_theme_dark_onPrimaryContainer = Color(0xFF70F7EE) -val md_theme_dark_secondary = Color(0xFFB0CCC9) -val md_theme_dark_onSecondary = Color(0xFF1C3533) -val md_theme_dark_secondaryContainer = Color(0xFF324B49) -val md_theme_dark_onSecondaryContainer = Color(0xFFCCE8E5) -val md_theme_dark_tertiary = Color(0xFFB0C9E7) -val md_theme_dark_onTertiary = Color(0xFF18324A) -val md_theme_dark_tertiaryContainer = Color(0xFF304962) -val md_theme_dark_onTertiaryContainer = Color(0xFFD0E4FF) -val md_theme_dark_error = Color(0xFFFFB4AB) -val md_theme_dark_errorContainer = Color(0xFF93000A) -val md_theme_dark_onError = Color(0xFF690005) -val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6) -val md_theme_dark_background = Color(0xFF000000) -val md_theme_dark_onBackground = Color(0xFFE0E3E2) -val md_theme_dark_surface = Color(0xFF191C1C) -val md_theme_dark_onSurface = Color(0xFFE0E3E2) -val md_theme_dark_surfaceVariant = Color(0xFF3F4947) -val md_theme_dark_onSurfaceVariant = Color(0xFFBEC9C7) -val md_theme_dark_outline = Color(0xFF889391) -val md_theme_dark_inverseOnSurface = Color(0xFF191C1C) -val md_theme_dark_inverseSurface = Color(0xFFE0E3E2) -val md_theme_dark_inversePrimary = Color(0xFF006A65) -val md_theme_dark_surfaceTint = Color(0xFF4FDAD1) -val md_theme_dark_outlineVariant = Color(0xFF3F4947) -val md_theme_dark_scrim = Color(0xFF000000) + +val primaryDark = Color(0xFFB1D18A) +val onPrimaryDark = Color(0xFF1F3701) +val primaryContainerDark = Color(0xFF354E16) +val onPrimaryContainerDark = Color(0xFFCDEDA3) +val secondaryDark = Color(0xFFBFCBAD) +val onSecondaryDark = Color(0xFF2A331E) +val secondaryContainerDark = Color(0xFF404A33) +val onSecondaryContainerDark = Color(0xFFDCE7C8) +val tertiaryDark = Color(0xFFA0D0CB) +val onTertiaryDark = Color(0xFF003735) +val tertiaryContainerDark = Color(0xFF1F4E4B) +val onTertiaryContainerDark = Color(0xFFBCECE7) +val errorDark = Color(0xFFFFB4AB) +val onErrorDark = Color(0xFF690005) +val errorContainerDark = Color(0xFF93000A) +val onErrorContainerDark = Color(0xFFFFDAD6) +val backgroundDark = Color(0xFF12140E) +val onBackgroundDark = Color(0xFFE2E3D8) +val surfaceDark = Color(0xFF12140E) +val onSurfaceDark = Color(0xFFE2E3D8) +val surfaceVariantDark = Color(0xFF44483D) +val onSurfaceVariantDark = Color(0xFFC5C8BA) +val outlineDark = Color(0xFF8F9285) +val outlineVariantDark = Color(0xFF44483D) +val scrimDark = Color(0xFF000000) +val inverseSurfaceDark = Color(0xFFE2E3D8) +val inverseOnSurfaceDark = Color(0xFF2F312A) +val inversePrimaryDark = Color(0xFF4C662B) +val surfaceDimDark = Color(0xFF12140E) +val surfaceBrightDark = Color(0xFF383A32) +val surfaceContainerLowestDark = Color(0xFF0C0F09) +val surfaceContainerLowDark = Color(0xFF1A1C16) +val surfaceContainerDark = Color(0xFF1E201A) +val surfaceContainerHighDark = Color(0xFF282B24) +val surfaceContainerHighestDark = Color(0xFF33362E) diff --git a/app/src/main/java/com/bintianqi/owndroid/ui/theme/Theme.kt b/app/src/main/java/com/bintianqi/owndroid/ui/theme/Theme.kt index 2522880..4c05b4f 100644 --- a/app/src/main/java/com/bintianqi/owndroid/ui/theme/Theme.kt +++ b/app/src/main/java/com/bintianqi/owndroid/ui/theme/Theme.kt @@ -1,102 +1,112 @@ package com.bintianqi.owndroid.ui.theme import android.app.Activity -import android.content.Context import android.os.Build.VERSION import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.getValue import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView import androidx.core.view.WindowCompat +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.bintianqi.owndroid.MyViewModel -private val DarkColorScheme = darkColorScheme( - primary = md_theme_dark_primary, - onPrimary = md_theme_dark_onPrimary, - primaryContainer = md_theme_dark_primaryContainer, - onPrimaryContainer = md_theme_dark_onPrimaryContainer, - secondary = md_theme_dark_secondary, - onSecondary = md_theme_dark_onSecondary, - secondaryContainer = md_theme_dark_secondaryContainer, - onSecondaryContainer = md_theme_dark_onSecondaryContainer, - tertiary = md_theme_dark_tertiary, - onTertiary = md_theme_dark_onTertiary, - tertiaryContainer = md_theme_dark_tertiaryContainer, - onTertiaryContainer = md_theme_dark_onTertiaryContainer, - error = md_theme_dark_error, - errorContainer = md_theme_dark_errorContainer, - onError = md_theme_dark_onError, - onErrorContainer = md_theme_dark_onErrorContainer, - background = md_theme_dark_background, - onBackground = md_theme_dark_onBackground, - surface = md_theme_dark_surface, - onSurface = md_theme_dark_onSurface, - surfaceVariant = md_theme_dark_surfaceVariant, - onSurfaceVariant = md_theme_dark_onSurfaceVariant, - outline = md_theme_dark_outline, - inverseOnSurface = md_theme_dark_inverseOnSurface, - inverseSurface = md_theme_dark_inverseSurface, - inversePrimary = md_theme_dark_inversePrimary, - surfaceTint = md_theme_dark_surfaceTint, - outlineVariant = md_theme_dark_outlineVariant, - scrim = md_theme_dark_scrim +private val lightScheme = lightColorScheme( + primary = primaryLight, + onPrimary = onPrimaryLight, + primaryContainer = primaryContainerLight, + onPrimaryContainer = onPrimaryContainerLight, + secondary = secondaryLight, + onSecondary = onSecondaryLight, + secondaryContainer = secondaryContainerLight, + onSecondaryContainer = onSecondaryContainerLight, + tertiary = tertiaryLight, + onTertiary = onTertiaryLight, + tertiaryContainer = tertiaryContainerLight, + onTertiaryContainer = onTertiaryContainerLight, + error = errorLight, + onError = onErrorLight, + errorContainer = errorContainerLight, + onErrorContainer = onErrorContainerLight, + background = backgroundLight, + onBackground = onBackgroundLight, + surface = surfaceLight, + onSurface = onSurfaceLight, + surfaceVariant = surfaceVariantLight, + onSurfaceVariant = onSurfaceVariantLight, + outline = outlineLight, + outlineVariant = outlineVariantLight, + scrim = scrimLight, + inverseSurface = inverseSurfaceLight, + inverseOnSurface = inverseOnSurfaceLight, + inversePrimary = inversePrimaryLight, + surfaceDim = surfaceDimLight, + surfaceBright = surfaceBrightLight, + surfaceContainerLowest = surfaceContainerLowestLight, + surfaceContainerLow = surfaceContainerLowLight, + surfaceContainer = surfaceContainerLight, + surfaceContainerHigh = surfaceContainerHighLight, + surfaceContainerHighest = surfaceContainerHighestLight, ) -private val LightColorScheme = lightColorScheme( - primary = md_theme_light_primary, - onPrimary = md_theme_light_onPrimary, - primaryContainer = md_theme_light_primaryContainer, - onPrimaryContainer = md_theme_light_onPrimaryContainer, - secondary = md_theme_light_secondary, - onSecondary = md_theme_light_onSecondary, - secondaryContainer = md_theme_light_secondaryContainer, - onSecondaryContainer = md_theme_light_onSecondaryContainer, - tertiary = md_theme_light_tertiary, - onTertiary = md_theme_light_onTertiary, - tertiaryContainer = md_theme_light_tertiaryContainer, - onTertiaryContainer = md_theme_light_onTertiaryContainer, - error = md_theme_light_error, - errorContainer = md_theme_light_errorContainer, - onError = md_theme_light_onError, - onErrorContainer = md_theme_light_onErrorContainer, - background = md_theme_light_background, - onBackground = md_theme_light_onBackground, - surface = md_theme_light_surface, - onSurface = md_theme_light_onSurface, - surfaceVariant = md_theme_light_surfaceVariant, - onSurfaceVariant = md_theme_light_onSurfaceVariant, - outline = md_theme_light_outline, - inverseOnSurface = md_theme_light_inverseOnSurface, - inverseSurface = md_theme_light_inverseSurface, - inversePrimary = md_theme_light_inversePrimary, - surfaceTint = md_theme_light_surfaceTint, - outlineVariant = md_theme_light_outlineVariant, - scrim = md_theme_light_scrim +private val darkScheme = darkColorScheme( + primary = primaryDark, + onPrimary = onPrimaryDark, + primaryContainer = primaryContainerDark, + onPrimaryContainer = onPrimaryContainerDark, + secondary = secondaryDark, + onSecondary = onSecondaryDark, + secondaryContainer = secondaryContainerDark, + onSecondaryContainer = onSecondaryContainerDark, + tertiary = tertiaryDark, + onTertiary = onTertiaryDark, + tertiaryContainer = tertiaryContainerDark, + onTertiaryContainer = onTertiaryContainerDark, + error = errorDark, + onError = onErrorDark, + errorContainer = errorContainerDark, + onErrorContainer = onErrorContainerDark, + background = backgroundDark, + onBackground = onBackgroundDark, + surface = surfaceDark, + onSurface = onSurfaceDark, + surfaceVariant = surfaceVariantDark, + onSurfaceVariant = onSurfaceVariantDark, + outline = outlineDark, + outlineVariant = outlineVariantDark, + scrim = scrimDark, + inverseSurface = inverseSurfaceDark, + inverseOnSurface = inverseOnSurfaceDark, + inversePrimary = inversePrimaryDark, + surfaceDim = surfaceDimDark, + surfaceBright = surfaceBrightDark, + surfaceContainerLowest = surfaceContainerLowestDark, + surfaceContainerLow = surfaceContainerLowDark, + surfaceContainer = surfaceContainerDark, + surfaceContainerHigh = surfaceContainerHighDark, + surfaceContainerHighest = surfaceContainerHighestDark, ) @Composable fun OwnDroidTheme( - materialYou: Boolean, - blackTheme: Boolean, + vm: MyViewModel, content: @Composable () -> Unit ) { - val darkTheme = isSystemInDarkTheme() + val theme by vm.theme.collectAsStateWithLifecycle() + val darkTheme = theme.darkTheme == true || (theme.darkTheme == null && isSystemInDarkTheme()) val context = LocalContext.current - val sharedPref = context.getSharedPreferences("data", Context.MODE_PRIVATE) - if(!sharedPref.contains("dynamicColor") && VERSION.SDK_INT >= 32) { - sharedPref.edit().putBoolean("dynamicColor", true).apply() - } var colorScheme = when { - materialYou && VERSION.SDK_INT>=31 -> { + theme.materialYou && VERSION.SDK_INT >= 31 -> { if(darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) } - darkTheme -> DarkColorScheme - else -> LightColorScheme + darkTheme -> darkScheme + else -> lightScheme } - if(darkTheme&&blackTheme) { + if(darkTheme && theme.blackTheme) { colorScheme = colorScheme.copy(background = Color.Black) } if(!darkTheme) { @@ -108,7 +118,6 @@ fun OwnDroidTheme( window.statusBarColor = Color.Transparent.toArgb() WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme } - MaterialTheme( colorScheme = colorScheme, typography = Typography, diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index c16797b..6b19396 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -38,7 +38,6 @@ Определять пользователем Не поддерживается Разрабатываемая функция - Неизвестный эффект Опции Копировать команду Имя пакета @@ -58,6 +57,8 @@ Нет Предыдущий Следующий + On + Off @@ -222,7 +223,6 @@ Блокировка ети, настроенной администратором Политика SSID Wi-Fi Список SSID: - Не может быть пустым Уже существует Частный DNS Укажите имя хоста @@ -330,7 +330,6 @@ Отключить управление пользователем Если вы установите этот флажок, вы не сможете очистить хранилище этих приложений или принудительно остановить их. Список приложений: - Очистить список Управление разрешениями Кросс-профильный пакет Кросс-профильный виджет @@ -469,8 +468,6 @@ Выйти из текущего пользователя Запустить в фоновом режиме Переключиться - Остановить - Удалить Создать пользователя Имя пользователя Пропустить мастер настройки @@ -478,7 +475,6 @@ Включить все системные приложения Серийный номер этого пользователя: %1$s Идентификатор принадлежности - Включая пустую строку Изменить значок пользователя Использовать выборщик файлов вместо галереи Выберите изображение... @@ -552,13 +548,13 @@ Настройки Показывать опасные функции Цвет Material You - Android 12+ + Dark theme + Follow system + Black theme О приложении Руководство пользователя Исходный код Тема - Черная тема - Требуется включенный темный режим Безопасность Заблокировать OwnDroid diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 11ae189..8aec01c 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -39,7 +39,6 @@ Kullanıcı Tarafından Karar Ver Desteklenmiyor Geliştirilen İşlev - Bilinmeyen Etki Seçenekler Komutu Kopyala Paket Adı @@ -59,6 +58,8 @@ No Previous Next + On + Off Etkinleştirmek İçin Tıklayın @@ -223,7 +224,6 @@ Yönetici tarafından yapılandırılmış ağı kilitle WiFi SSID politikası SSID listesi: - Boş olamaz Zaten mevcut Özel DNS Ana bilgisayar adı sağlayın @@ -329,7 +329,6 @@ Kullanıcı kontrolünü devre dışı bırak Bunu ayarlarsanız, bu uygulamaların depolama alanını temizleyemez veya zorla durduramazsınız. Uygulama listesi: - Listeyi temizle İzin yönetimi Profil geçişli paket Profil geçişli widget @@ -467,8 +466,6 @@ Mevcut kullanıcıyı çıkış yap Arka planda başlat Değiştir - Durdur - Kaldır Kullanıcı oluştur Kullanıcı adı Sihirbazı atla @@ -476,7 +473,6 @@ Tüm sistem uygulamalarını etkinleştir Bu kullanıcının seri numarası: %1$s Bağlılık ID - Boş string dahil et Kullanıcı simgesini değiştir Galeri yerine dosya seçici kullan Resim seç... @@ -547,13 +543,13 @@ Ayarlar Show dangerous features Material You rengi - Android 12+ + Dark theme + Follow system + Black theme Hakkında Kullanıcı rehberi Kaynak kodu Tema - Siyah tema - Karanlık modun açık olmasını gerektirir Güvenlik OwnDroid\'u kilitle diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index cc7e330..4368a4c 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -38,7 +38,6 @@ 由用户决定 不支持 功能开发中 - 效果未知 选项 复制代码 未知状态 @@ -56,6 +55,8 @@ 上一个 下一个 + 开启 + 关闭 点击以激活 @@ -218,7 +219,6 @@ 锁定由管理员配置的网络 WiFi SSID策略 SSID列表: - 不能为空 已经存在 私人DNS 指定主机名 @@ -320,7 +320,6 @@ 禁止用户控制 用户将无法清除这些应用的存储空间或强制停止这些应用 应用列表: - 清空列表 权限管理 跨资料应用 跨资料微件 @@ -459,8 +458,6 @@ 登出当前用户 在后台启动 切换 - 停止 - 移除 创建用户 用户名 跳过创建用户向导 @@ -468,7 +465,6 @@ 启用所有系统应用 新用户的序列号:%1$s 附属用户ID - 有空字符串 更换用户头像 使用文件选择器而不是相册 选择图片... @@ -539,13 +535,13 @@ 设置 显示危险功能 Material you 颜色 - 安卓12+ + 深色主题 + 跟随系统 + 黑色主题 关于 使用教程 源代码 主题 - 纯黑夜间主题 - 需要打开夜间模式 安全 锁定OwnDroid diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0591503..dc28a69 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -39,7 +39,6 @@ Decide by user Unsupported Developing function - Unknown effect Options Copy Command Package name @@ -59,6 +58,8 @@ No Previous Next + On + Off Click to activate @@ -227,7 +228,6 @@ Lockdown admin configured network WiFi SSID policy SSID list: - Cannot be empty Already exist Private DNS Provide hostname @@ -335,7 +335,6 @@ Disable user control If you set this, you cannot clear these apps\' storage or force stop them. App list: - Clear list Permission manage Cross profile package Cross profile widget @@ -472,8 +471,6 @@ Logout current user Start in background Switch - Stop - Remove Create user Username Skip wizard @@ -481,7 +478,6 @@ Enable all system app Serial number of this user: %1$s Affiliation ID - Include empty string Change user icon Use file picker instead of gallery Select image... @@ -552,13 +548,13 @@ Settings Show dangerous features Material you color - Android 12+ + Dark theme + Follow system + Black theme About User guide Source code Theme - Black theme - Require dark mode on Security Lock OwnDroid diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1cfc40c..0f32f42 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,20 +2,20 @@ agp = "8.7.2" kotlin = "2.0.21" -androidx-activity-compose = "1.9.0" -navigation-compose = "2.7.7" -material3 = "1.2.1" +navigation-compose = "2.8.3" +composeBom = "2024.10.01" accompanist-drawablepainter = "0.35.0-alpha" shizuku = "13.1.5" biometric = "1.2.0-alpha05" fragment = "1.8.0-beta01" dhizuku = "2.5.2" hiddenApiBypass = "4.3" -serialization = "1.7.1" +serialization = "1.7.3" [libraries] -androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity-compose" } -androidx-material3 = { module = "androidx.compose.material3:material3", version.ref = "material3" } +androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" } +androidx-activity-compose = { module = "androidx.activity:activity-compose" } +androidx-material3 = { module = "androidx.compose.material3:material3" } androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigation-compose" } accompanist-drawablepainter = { module = "com.google.accompanist:accompanist-drawablepainter", version.ref = "accompanist-drawablepainter" }