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" }