Optimize UI

Change version to v6.5(37)
This commit is contained in:
BinTianqi
2025-03-01 13:16:21 +08:00
parent 1de95e336b
commit 02c76ea436
16 changed files with 195 additions and 210 deletions

View File

@@ -24,8 +24,8 @@ android {
applicationId = "com.bintianqi.owndroid" applicationId = "com.bintianqi.owndroid"
minSdk = 21 minSdk = 21
targetSdk = 35 targetSdk = 35
versionCode = 36 versionCode = 37
versionName = "6.4" versionName = "6.5"
multiDexEnabled = false multiDexEnabled = false
} }

View File

@@ -35,7 +35,7 @@ import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.content.edit import androidx.core.content.edit
import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.Notes
import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.SwitchItem import com.bintianqi.owndroid.ui.SwitchItem
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@@ -214,7 +214,7 @@ fun ApiSettings(onNavigateUp: () -> Unit) {
) { ) {
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))
} }
if(sp.apiKey != null) InfoCard(R.string.api_key_exist) if(sp.apiKey != null) Notes(R.string.api_key_exist)
} }
} }
} }

View File

@@ -91,7 +91,7 @@ import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.showOperationResultToast import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.Animations
import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.Notes
import com.bintianqi.owndroid.ui.ListItem import com.bintianqi.owndroid.ui.ListItem
import com.bintianqi.owndroid.ui.NavIcon import com.bintianqi.owndroid.ui.NavIcon
import com.bintianqi.owndroid.ui.RadioButtonItem import com.bintianqi.owndroid.ui.RadioButtonItem
@@ -467,7 +467,7 @@ private fun UserControlDisabledPackagesScreen(pkgName:String) {
) { ) {
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))
} }
InfoCard(R.string.info_disable_user_control) Notes(R.string.info_disable_user_control)
Spacer(Modifier.padding(vertical = 30.dp)) Spacer(Modifier.padding(vertical = 30.dp))
} }
} }
@@ -787,7 +787,7 @@ private fun PermittedAccessibilityServicesScreen(pkgName: String) {
} }
} }
} }
InfoCard(R.string.system_accessibility_always_allowed) Notes(R.string.system_accessibility_always_allowed)
Spacer(Modifier.padding(vertical = 30.dp)) Spacer(Modifier.padding(vertical = 30.dp))
} }
} }
@@ -845,7 +845,7 @@ private fun PermittedInputMethodsScreen(pkgName: String) {
} }
} }
} }
InfoCard(R.string.system_ime_always_allowed) Notes(R.string.system_ime_always_allowed)
Spacer(Modifier.padding(vertical = 30.dp)) Spacer(Modifier.padding(vertical = 30.dp))
} }
} }
@@ -891,7 +891,7 @@ private fun KeepUninstalledPackagesScreen(pkgName: String) {
) { ) {
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))
} }
InfoCard(R.string.info_keep_uninstalled_apps) Notes(R.string.info_keep_uninstalled_apps)
Spacer(Modifier.padding(vertical = 30.dp)) Spacer(Modifier.padding(vertical = 30.dp))
} }
} }

View File

@@ -134,8 +134,9 @@ import com.bintianqi.owndroid.humanReadableDateTime
import com.bintianqi.owndroid.showOperationResultToast import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.ExpandExposedTextFieldIcon import com.bintianqi.owndroid.ui.ExpandExposedTextFieldIcon
import com.bintianqi.owndroid.ui.FullWidthRadioButtonItem
import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.Notes
import com.bintianqi.owndroid.ui.ListItem import com.bintianqi.owndroid.ui.ListItem
import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.MySmallTitleScaffold import com.bintianqi.owndroid.ui.MySmallTitleScaffold
@@ -168,7 +169,7 @@ fun NetworkScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
if(VERSION.SDK_INT >= 30) { if(VERSION.SDK_INT >= 30) {
FunctionItem(R.string.options, icon = R.drawable.tune_fill0) { onNavigate(NetworkOptions) } FunctionItem(R.string.options, icon = R.drawable.tune_fill0) { onNavigate(NetworkOptions) }
} }
if(VERSION.SDK_INT >= 23 && (deviceOwner || profileOwner)) if(VERSION.SDK_INT >= 23 && !dhizuku && (deviceOwner || profileOwner))
FunctionItem(R.string.network_stats, icon = R.drawable.query_stats_fill0) { onNavigate(QueryNetworkStats) } FunctionItem(R.string.network_stats, icon = R.drawable.query_stats_fill0) { onNavigate(QueryNetworkStats) }
if(VERSION.SDK_INT >= 29 && deviceOwner) { if(VERSION.SDK_INT >= 29 && deviceOwner) {
FunctionItem(R.string.private_dns, icon = R.drawable.dns_fill0) { onNavigate(PrivateDns) } FunctionItem(R.string.private_dns, icon = R.drawable.dns_fill0) { onNavigate(PrivateDns) }
@@ -765,22 +766,21 @@ fun WifiSecurityLevelScreen(onNavigateUp: () -> Unit) {
val dpm = context.getDPM() val dpm = context.getDPM()
var selectedWifiSecLevel by remember { mutableIntStateOf(0) } var selectedWifiSecLevel by remember { mutableIntStateOf(0) }
LaunchedEffect(Unit) { selectedWifiSecLevel = dpm.minimumRequiredWifiSecurityLevel } LaunchedEffect(Unit) { selectedWifiSecLevel = dpm.minimumRequiredWifiSecurityLevel }
MyScaffold(R.string.min_wifi_security_level, 8.dp, onNavigateUp) { MySmallTitleScaffold(R.string.min_wifi_security_level, 0.dp, onNavigateUp) {
RadioButtonItem(R.string.wifi_security_open, selectedWifiSecLevel == WIFI_SECURITY_OPEN) { selectedWifiSecLevel = WIFI_SECURITY_OPEN } FullWidthRadioButtonItem(R.string.wifi_security_open, selectedWifiSecLevel == WIFI_SECURITY_OPEN) { selectedWifiSecLevel = WIFI_SECURITY_OPEN }
RadioButtonItem("WEP, WPA(2)-PSK", selectedWifiSecLevel == WIFI_SECURITY_PERSONAL) { selectedWifiSecLevel = WIFI_SECURITY_PERSONAL } FullWidthRadioButtonItem("WEP, WPA(2)-PSK", selectedWifiSecLevel == WIFI_SECURITY_PERSONAL) { selectedWifiSecLevel = WIFI_SECURITY_PERSONAL }
RadioButtonItem("WPA-EAP", selectedWifiSecLevel == WIFI_SECURITY_ENTERPRISE_EAP) { selectedWifiSecLevel = WIFI_SECURITY_ENTERPRISE_EAP } FullWidthRadioButtonItem("WPA-EAP", selectedWifiSecLevel == WIFI_SECURITY_ENTERPRISE_EAP) { selectedWifiSecLevel = WIFI_SECURITY_ENTERPRISE_EAP }
RadioButtonItem("WPA3-192bit", selectedWifiSecLevel == WIFI_SECURITY_ENTERPRISE_192) { selectedWifiSecLevel = WIFI_SECURITY_ENTERPRISE_192 } FullWidthRadioButtonItem("WPA3-192bit", selectedWifiSecLevel == WIFI_SECURITY_ENTERPRISE_192) { selectedWifiSecLevel = WIFI_SECURITY_ENTERPRISE_192 }
Spacer(Modifier.padding(vertical = 5.dp))
Button( Button(
onClick = { onClick = {
dpm.minimumRequiredWifiSecurityLevel = selectedWifiSecLevel dpm.minimumRequiredWifiSecurityLevel = selectedWifiSecLevel
context.showOperationResultToast(true) context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp, horizontal = 8.dp)
) { ) {
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))
} }
InfoCard(R.string.info_minimum_wifi_security_level) Notes(R.string.info_minimum_wifi_security_level, 8.dp)
} }
} }
@@ -792,26 +792,26 @@ fun WifiSsidPolicyScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
MyScaffold(R.string.wifi_ssid_policy, 8.dp, onNavigateUp) { MyScaffold(R.string.wifi_ssid_policy, 0.dp, onNavigateUp) {
var selectedPolicyType by remember { mutableIntStateOf(-1) } var selectedPolicyType by remember { mutableIntStateOf(-1) }
val ssidList = remember { mutableStateListOf<WifiSsid>() } val ssidList = remember { mutableStateListOf<WifiSsid>() }
val refreshPolicy = { fun refreshPolicy() {
val policy = dpm.wifiSsidPolicy val policy = dpm.wifiSsidPolicy
ssidList.clear() ssidList.clear()
selectedPolicyType = policy?.policyType ?: -1 selectedPolicyType = policy?.policyType ?: -1
ssidList.addAll(policy?.ssids ?: mutableSetOf()) ssidList.addAll(policy?.ssids ?: mutableSetOf())
} }
LaunchedEffect(Unit) { refreshPolicy() } LaunchedEffect(Unit) { refreshPolicy() }
RadioButtonItem(R.string.none, selectedPolicyType == -1) { selectedPolicyType = -1 } FullWidthRadioButtonItem(R.string.none, selectedPolicyType == -1) { selectedPolicyType = -1 }
RadioButtonItem(R.string.whitelist, selectedPolicyType == WIFI_SSID_POLICY_TYPE_ALLOWLIST) { FullWidthRadioButtonItem(R.string.whitelist, selectedPolicyType == WIFI_SSID_POLICY_TYPE_ALLOWLIST) {
selectedPolicyType = WIFI_SSID_POLICY_TYPE_ALLOWLIST selectedPolicyType = WIFI_SSID_POLICY_TYPE_ALLOWLIST
} }
RadioButtonItem(R.string.blacklist, selectedPolicyType == WIFI_SSID_POLICY_TYPE_DENYLIST) { FullWidthRadioButtonItem(R.string.blacklist, selectedPolicyType == WIFI_SSID_POLICY_TYPE_DENYLIST) {
selectedPolicyType = WIFI_SSID_POLICY_TYPE_DENYLIST selectedPolicyType = WIFI_SSID_POLICY_TYPE_DENYLIST
} }
AnimatedVisibility(selectedPolicyType != -1) { AnimatedVisibility(selectedPolicyType != -1) {
var inputSsid by remember { mutableStateOf("") } var inputSsid by remember { mutableStateOf("") }
Column { Column(Modifier.padding(horizontal = 8.dp)) {
Text(stringResource(R.string.ssid_list_is)) Text(stringResource(R.string.ssid_list_is))
if(ssidList.isEmpty()) Text(stringResource(R.string.none)) if(ssidList.isEmpty()) Text(stringResource(R.string.none))
Column(modifier = Modifier.animateContentSize()) { Column(modifier = Modifier.animateContentSize()) {
@@ -839,7 +839,6 @@ fun WifiSsidPolicyScreen(onNavigateUp: () -> Unit) {
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
Spacer(Modifier.padding(vertical = 10.dp))
} }
} }
Button( Button(
@@ -853,7 +852,7 @@ fun WifiSsidPolicyScreen(onNavigateUp: () -> Unit) {
refreshPolicy() refreshPolicy()
context.showOperationResultToast(true) context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth().padding(8.dp)
) { ) {
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))
} }
@@ -1291,7 +1290,7 @@ data class NetworkStatsViewer(
fun NetworkStatsViewerScreen(nsv: NetworkStatsViewer, onNavigateUp: () -> Unit) { fun NetworkStatsViewerScreen(nsv: NetworkStatsViewer, onNavigateUp: () -> Unit) {
var index by remember { mutableIntStateOf(0) } var index by remember { mutableIntStateOf(0) }
val size = nsv.stats.size val size = nsv.stats.size
MySmallTitleScaffold(R.string.place_holder, 8.dp, onNavigateUp) { MySmallTitleScaffold(R.string.network_stats, 8.dp, onNavigateUp) {
if(size > 1) Row( if(size > 1) Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.align(Alignment.CenterHorizontally).padding(bottom = 8.dp) modifier = Modifier.align(Alignment.CenterHorizontally).padding(bottom = 8.dp)
@@ -1318,15 +1317,13 @@ fun NetworkStatsViewerScreen(nsv: NetworkStatsViewer, onNavigateUp: () -> Unit)
val txBytes = data.txBytes val txBytes = data.txBytes
Text(stringResource(R.string.transmitted), style = typography.titleLarge) Text(stringResource(R.string.transmitted), style = typography.titleLarge)
Column(modifier = Modifier.padding(start = 8.dp, bottom = 4.dp)) { Column(modifier = Modifier.padding(start = 8.dp, bottom = 4.dp)) {
Text("$txBytes bytes") Text("$txBytes bytes (${formatFileSize(txBytes)})")
Text(formatFileSize(txBytes))
Text(data.txPackets.toString() + " packets") Text(data.txPackets.toString() + " packets")
} }
val rxBytes = data.rxBytes val rxBytes = data.rxBytes
Text(stringResource(R.string.received), style = typography.titleLarge) Text(stringResource(R.string.received), style = typography.titleLarge)
Column(modifier = Modifier.padding(start = 8.dp, bottom = 8.dp)) { Column(modifier = Modifier.padding(start = 8.dp, bottom = 8.dp)) {
Text("$rxBytes bytes") Text("$rxBytes bytes (${formatFileSize(rxBytes)})")
Text(formatFileSize(rxBytes))
Text(data.rxPackets.toString() + " packets") Text(data.rxPackets.toString() + " packets")
} }
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
@@ -1406,7 +1403,7 @@ fun PrivateDnsScreen(onNavigateUp: () -> Unit) {
Text(stringResource(R.string.set_to_opportunistic)) Text(stringResource(R.string.set_to_opportunistic))
} }
} }
InfoCard(R.string.info_private_dns_mode_oppertunistic) Notes(R.string.info_private_dns_mode_oppertunistic)
Spacer(Modifier.padding(vertical = 10.dp)) Spacer(Modifier.padding(vertical = 10.dp))
var inputHost by remember { mutableStateOf(dpm.getGlobalPrivateDnsHost(receiver) ?: "") } var inputHost by remember { mutableStateOf(dpm.getGlobalPrivateDnsHost(receiver) ?: "") }
OutlinedTextField( OutlinedTextField(
@@ -1439,7 +1436,7 @@ fun PrivateDnsScreen(onNavigateUp: () -> Unit) {
) { ) {
Text(stringResource(R.string.set_dns_host)) Text(stringResource(R.string.set_dns_host))
} }
InfoCard(R.string.info_set_private_dns_host) Notes(R.string.info_set_private_dns_host)
} }
} }
@@ -1505,7 +1502,7 @@ fun AlwaysOnVpnPackageScreen(onNavigateUp: () -> Unit) {
) { ) {
Text(stringResource(R.string.clear_current_config)) Text(stringResource(R.string.clear_current_config))
} }
InfoCard(R.string.info_always_on_vpn) Notes(R.string.info_always_on_vpn)
} }
} }
@@ -1604,7 +1601,7 @@ fun RecommendedGlobalProxyScreen(onNavigateUp: () -> Unit) {
) { ) {
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))
} }
InfoCard(R.string.info_recommended_global_proxy) Notes(R.string.info_recommended_global_proxy)
} }
} }
@@ -1656,7 +1653,7 @@ fun NetworkLoggingScreen(onNavigateUp: () -> Unit) {
Text(stringResource(R.string.delete_logs)) Text(stringResource(R.string.delete_logs))
} }
} }
InfoCard(R.string.info_network_log) Notes(R.string.info_network_log)
} }
} }

View File

@@ -73,7 +73,7 @@ import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.FullWidthCheckBoxItem import com.bintianqi.owndroid.ui.FullWidthCheckBoxItem
import com.bintianqi.owndroid.ui.FullWidthRadioButtonItem import com.bintianqi.owndroid.ui.FullWidthRadioButtonItem
import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.Notes
import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.RadioButtonItem import com.bintianqi.owndroid.ui.RadioButtonItem
import com.bintianqi.owndroid.yesOrNo import com.bintianqi.owndroid.yesOrNo
@@ -308,7 +308,7 @@ fun ResetPasswordTokenScreen(onNavigateUp: () -> Unit) {
} }
} }
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
InfoCard(R.string.activate_token_not_required_when_no_password) Notes(R.string.activate_token_not_required_when_no_password)
} }
} }
@@ -386,7 +386,7 @@ fun ResetPasswordScreen(onNavigateUp: () -> Unit) {
Text(stringResource(R.string.reset_password)) Text(stringResource(R.string.reset_password))
} }
} }
InfoCard(R.string.info_reset_password) Notes(R.string.info_reset_password)
} }
if(confirmDialog) { if(confirmDialog) {
var confirmPassword by remember { mutableStateOf("") } var confirmPassword by remember { mutableStateOf("") }
@@ -462,7 +462,7 @@ fun RequiredPasswordComplexityScreen(onNavigateUp: () -> Unit) {
) { ) {
Text(text = stringResource(R.string.apply)) Text(text = stringResource(R.string.apply))
} }
InfoCard(R.string.info_password_complexity, 8.dp) Notes(R.string.info_password_complexity, 8.dp)
} }
} }
@@ -519,7 +519,7 @@ fun KeyguardDisabledFeaturesScreen(onNavigateUp: () -> Unit) {
refresh() refresh()
context.showOperationResultToast(true) context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp) modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp, horizontal = 8.dp)
) { ) {
Text(text = stringResource(R.string.apply)) Text(text = stringResource(R.string.apply))
} }

View File

@@ -306,7 +306,7 @@ fun LockScreenInfoScreen(onNavigateUp: () -> Unit) {
Text(text = stringResource(R.string.reset)) Text(text = stringResource(R.string.reset))
} }
Spacer(Modifier.padding(vertical = 10.dp)) Spacer(Modifier.padding(vertical = 10.dp))
InfoCard(R.string.info_lock_screen_info) Notes(R.string.info_lock_screen_info)
} }
} }
@@ -716,7 +716,7 @@ fun SupportMessageScreen(onNavigateUp: () -> Unit) {
Text(text = stringResource(R.string.reset)) Text(text = stringResource(R.string.reset))
} }
} }
InfoCard(R.string.info_short_support_message) Notes(R.string.info_short_support_message)
Spacer(Modifier.padding(vertical = 8.dp)) Spacer(Modifier.padding(vertical = 8.dp))
OutlinedTextField( OutlinedTextField(
value = longMsg, value = longMsg,
@@ -747,7 +747,7 @@ fun SupportMessageScreen(onNavigateUp: () -> Unit) {
Text(text = stringResource(R.string.reset)) Text(text = stringResource(R.string.reset))
} }
} }
InfoCard(R.string.info_long_support_message) Notes(R.string.info_long_support_message)
} }
} }
@@ -778,7 +778,7 @@ fun TransferOwnershipScreen(onNavigateUp: () -> Unit) {
Text(stringResource(R.string.transfer)) Text(stringResource(R.string.transfer))
} }
Spacer(Modifier.padding(vertical = 10.dp)) Spacer(Modifier.padding(vertical = 10.dp))
InfoCard(R.string.info_transfer_ownership) Notes(R.string.info_transfer_ownership)
} }
if(dialog) AlertDialog( if(dialog) AlertDialog(
text = { text = {

View File

@@ -6,13 +6,6 @@ import android.app.AlertDialog
import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyManager.FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY import android.app.admin.DevicePolicyManager.FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY
import android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback import android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback
import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK
import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS
import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME
import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD
import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS
import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW
import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO
import android.app.admin.DevicePolicyManager.MTE_DISABLED import android.app.admin.DevicePolicyManager.MTE_DISABLED
import android.app.admin.DevicePolicyManager.MTE_ENABLED import android.app.admin.DevicePolicyManager.MTE_ENABLED
import android.app.admin.DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY import android.app.admin.DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY
@@ -46,6 +39,7 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState import androidx.compose.foundation.interaction.collectIsPressedAsState
@@ -56,9 +50,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.HorizontalPager
@@ -134,14 +126,14 @@ import com.bintianqi.owndroid.humanReadableDate
import com.bintianqi.owndroid.parseDate import com.bintianqi.owndroid.parseDate
import com.bintianqi.owndroid.showOperationResultToast import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.FullWidthCheckBoxItem
import com.bintianqi.owndroid.ui.FullWidthRadioButtonItem import com.bintianqi.owndroid.ui.FullWidthRadioButtonItem
import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.InfoCard
import com.bintianqi.owndroid.ui.ListItem import com.bintianqi.owndroid.ui.ListItem
import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.MySmallTitleScaffold import com.bintianqi.owndroid.ui.MySmallTitleScaffold
import com.bintianqi.owndroid.ui.NavIcon import com.bintianqi.owndroid.ui.NavIcon
import com.bintianqi.owndroid.ui.RadioButtonItem import com.bintianqi.owndroid.ui.Notes
import com.bintianqi.owndroid.ui.SwitchItem import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.uriToStream import com.bintianqi.owndroid.uriToStream
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -370,7 +362,7 @@ fun KeyguardScreen(onNavigateUp: () -> Unit) {
Text(stringResource(R.string.enable)) Text(stringResource(R.string.enable))
} }
} }
InfoCard(R.string.info_disable_keyguard) Notes(R.string.info_disable_keyguard)
Spacer(Modifier.padding(vertical = 12.dp)) Spacer(Modifier.padding(vertical = 12.dp))
} }
if(VERSION.SDK_INT >= 23) Text(text = stringResource(R.string.lock_now), style = typography.headlineLarge) if(VERSION.SDK_INT >= 23) Text(text = stringResource(R.string.lock_now), style = typography.headlineLarge)
@@ -393,7 +385,7 @@ fun KeyguardScreen(onNavigateUp: () -> Unit) {
Text(stringResource(R.string.lock_now)) Text(stringResource(R.string.lock_now))
} }
if(VERSION.SDK_INT >= 26 && profileOwner && dpm.isManagedProfile(receiver)) { if(VERSION.SDK_INT >= 26 && profileOwner && dpm.isManagedProfile(receiver)) {
InfoCard(R.string.info_evict_credential_encryption_key) Notes(R.string.info_evict_credential_encryption_key)
} }
} }
} }
@@ -491,10 +483,7 @@ fun ChangeTimeScreen(onNavigateUp: () -> Unit) {
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
val coroutine = rememberCoroutineScope()
val pagerState = rememberPagerState { 2 } val pagerState = rememberPagerState { 2 }
var manualInput by remember { mutableStateOf(false) }
var inputTime by remember { mutableStateOf("")}
var picker by remember { mutableIntStateOf(0) } //0:None, 1:DatePicker, 2:TimePicker var picker by remember { mutableIntStateOf(0) } //0:None, 1:DatePicker, 2:TimePicker
val datePickerState = rememberDatePickerState() val datePickerState = rememberDatePickerState()
val timePickerState = rememberTimePickerState() val timePickerState = rememberTimePickerState()
@@ -502,16 +491,14 @@ fun ChangeTimeScreen(onNavigateUp: () -> Unit) {
val timeInteractionSource = remember { MutableInteractionSource() } val timeInteractionSource = remember { MutableInteractionSource() }
if(dateInteractionSource.collectIsPressedAsState().value) picker = 1 if(dateInteractionSource.collectIsPressedAsState().value) picker = 1
if(timeInteractionSource.collectIsPressedAsState().value) picker = 2 if(timeInteractionSource.collectIsPressedAsState().value) picker = 2
val isInputLegal = (manualInput && (try { inputTime.toLong() } catch(_: Exception) { -1 }) >= 0) ||
(!manualInput && datePickerState.selectedDateMillis != null)
MyScaffold(R.string.change_time, 8.dp, onNavigateUp) { MyScaffold(R.string.change_time, 8.dp, onNavigateUp) {
SingleChoiceSegmentedButtonRow( SingleChoiceSegmentedButtonRow(
modifier = Modifier.fillMaxWidth().padding(top = 4.dp) modifier = Modifier.fillMaxWidth().padding(top = 4.dp)
) { ) {
val coroutine = rememberCoroutineScope()
SegmentedButton( SegmentedButton(
selected = !manualInput, shape = SegmentedButtonDefaults.itemShape(0, 2), selected = pagerState.targetPage == 0, shape = SegmentedButtonDefaults.itemShape(0, 2),
onClick = { onClick = {
manualInput = false
coroutine.launch { coroutine.launch {
pagerState.animateScrollToPage(0) pagerState.animateScrollToPage(0)
} }
@@ -520,9 +507,8 @@ fun ChangeTimeScreen(onNavigateUp: () -> Unit) {
Text(stringResource(R.string.selector)) Text(stringResource(R.string.selector))
} }
SegmentedButton( SegmentedButton(
selected = manualInput, shape = SegmentedButtonDefaults.itemShape(1, 2), selected = pagerState.targetPage == 1, shape = SegmentedButtonDefaults.itemShape(1, 2),
onClick = { onClick = {
manualInput = true
coroutine.launch { coroutine.launch {
pagerState.animateScrollToPage(1) pagerState.animateScrollToPage(1)
} }
@@ -532,10 +518,11 @@ fun ChangeTimeScreen(onNavigateUp: () -> Unit) {
} }
} }
HorizontalPager( HorizontalPager(
state = pagerState, modifier = Modifier.height(140.dp).padding(top = 4.dp), state = pagerState, modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.Top verticalAlignment = Alignment.Top
) { page -> ) { page ->
if(page == 0) Column { Column(Modifier.padding(top = 4.dp)) {
if(page == 0) {
OutlinedTextField( OutlinedTextField(
value = datePickerState.selectedDateMillis?.humanReadableDate ?: "", value = datePickerState.selectedDateMillis?.humanReadableDate ?: "",
onValueChange = {}, readOnly = true, onValueChange = {}, readOnly = true,
@@ -548,31 +535,42 @@ fun ChangeTimeScreen(onNavigateUp: () -> Unit) {
onValueChange = {}, readOnly = true, onValueChange = {}, readOnly = true,
label = { Text(stringResource(R.string.time)) }, label = { Text(stringResource(R.string.time)) },
interactionSource = timeInteractionSource, interactionSource = timeInteractionSource,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp)
) )
Button(
onClick = {
val timeMillis = datePickerState.selectedDateMillis!! + timePickerState.hour * 3600000 + timePickerState.minute * 60000
context.showOperationResultToast(dpm.setTime(receiver, timeMillis))
},
modifier = Modifier.fillMaxWidth(),
enabled = datePickerState.selectedDateMillis != null
) {
Text(stringResource(R.string.apply))
} }
if(page == 1) OutlinedTextField( } else {
var inputTime by remember { mutableStateOf("") }
OutlinedTextField(
value = inputTime, value = inputTime,
label = { Text(stringResource(R.string.time_unit_ms)) }, label = { Text(stringResource(R.string.time_unit_ms)) },
onValueChange = { inputTime = it }, onValueChange = { inputTime = it },
supportingText = { Text(stringResource(R.string.info_change_time)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
}
Button( Button(
onClick = { onClick = {
val timeMillis = if(manualInput) inputTime.toLong() val timeMillis = inputTime.toLong()
else datePickerState.selectedDateMillis!! + timePickerState.hour * 3600000 + timePickerState.minute * 60000
context.showOperationResultToast(dpm.setTime(receiver, timeMillis)) context.showOperationResultToast(dpm.setTime(receiver, timeMillis))
}, },
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp),
enabled = isInputLegal enabled = inputTime.toLongOrNull() != null
) { ) {
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))
} }
} }
}
}
}
if(picker == 1) DatePickerDialog( if(picker == 1) DatePickerDialog(
confirmButton = { confirmButton = {
TextButton(onClick = { picker = 0; focusMgr.clearFocus() } ) { TextButton(onClick = { picker = 0; focusMgr.clearFocus() } ) {
@@ -629,7 +627,7 @@ fun ChangeTimeZoneScreen(onNavigateUp: () -> Unit) {
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))
} }
Spacer(Modifier.padding(vertical = 10.dp)) Spacer(Modifier.padding(vertical = 10.dp))
InfoCard(R.string.disable_auto_time_zone_before_set) Notes(R.string.disable_auto_time_zone_before_set)
} }
if(dialog) AlertDialog( if(dialog) AlertDialog(
text = { text = {
@@ -868,7 +866,7 @@ fun ContentProtectionPolicyScreen(onNavigateUp: () -> Unit) {
) { ) {
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))
} }
InfoCard(R.string.info_content_protection_policy, 8.dp) Notes(R.string.info_content_protection_policy, 8.dp)
} }
} }
@@ -901,7 +899,7 @@ fun PermissionPolicyScreen(onNavigateUp: () -> Unit) {
) { ) {
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))
} }
InfoCard(R.string.info_permission_policy, 8.dp) Notes(R.string.info_permission_policy, 8.dp)
} }
} }
@@ -933,7 +931,7 @@ fun MtePolicyScreen(onNavigateUp: () -> Unit) {
) { ) {
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))
} }
InfoCard(R.string.info_mte_policy, 8.dp) Notes(R.string.info_mte_policy, 8.dp)
} }
} }
@@ -945,7 +943,7 @@ fun NearbyStreamingPolicyScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
var appPolicy by remember { mutableIntStateOf(dpm.nearbyAppStreamingPolicy) } var appPolicy by remember { mutableIntStateOf(dpm.nearbyAppStreamingPolicy) }
MyScaffold(R.string.nearby_streaming_policy, 0.dp, onNavigateUp) { MySmallTitleScaffold(R.string.nearby_streaming_policy, 0.dp, onNavigateUp) {
Text( Text(
stringResource(R.string.nearby_app_streaming), stringResource(R.string.nearby_app_streaming),
Modifier.padding(start = 8.dp, top = 10.dp, bottom = 4.dp), style = typography.titleLarge Modifier.padding(start = 8.dp, top = 10.dp, bottom = 4.dp), style = typography.titleLarge
@@ -970,7 +968,7 @@ fun NearbyStreamingPolicyScreen(onNavigateUp: () -> Unit) {
) { ) {
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))
} }
InfoCard(R.string.info_nearby_app_streaming_policy, 8.dp) Notes(R.string.info_nearby_app_streaming_policy, 8.dp)
var notificationPolicy by remember { mutableIntStateOf(dpm.nearbyNotificationStreamingPolicy) } var notificationPolicy by remember { mutableIntStateOf(dpm.nearbyNotificationStreamingPolicy) }
Text( Text(
stringResource(R.string.nearby_notification_streaming), stringResource(R.string.nearby_notification_streaming),
@@ -1002,7 +1000,7 @@ fun NearbyStreamingPolicyScreen(onNavigateUp: () -> Unit) {
) { ) {
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))
} }
InfoCard(R.string.info_nearby_notification_streaming_policy, 8.dp) Notes(R.string.info_nearby_notification_streaming_policy, 8.dp)
} }
} }
@@ -1043,12 +1041,19 @@ fun LockTaskModeScreen(onNavigateUp: () -> Unit) {
) )
} }
HorizontalPager(pagerState, verticalAlignment = Alignment.Top) { page -> HorizontalPager(pagerState, verticalAlignment = Alignment.Top) { page ->
if(page == 0 || page == 1) {
Column( Column(
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(start = 8.dp, end = 8.dp, bottom = 80.dp) modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(start = 8.dp, end = 8.dp, bottom = 80.dp)
) { ) {
if(page == 0) StartLockTaskMode() if(page == 0) StartLockTaskMode()
else if(page == 1) LockTaskPackages() else LockTaskPackages()
else LockTaskFeatures() }
} else {
Column(
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(bottom = 80.dp)
) {
LockTaskFeatures()
}
} }
} }
} }
@@ -1116,7 +1121,7 @@ private fun ColumnScope.StartLockTaskMode() {
) { ) {
Text(stringResource(R.string.start)) Text(stringResource(R.string.start))
} }
InfoCard(R.string.info_start_lock_task_mode) Notes(R.string.info_start_lock_task_mode)
} }
@RequiresApi(26) @RequiresApi(26)
@@ -1181,7 +1186,7 @@ private fun ColumnScope.LockTaskPackages() {
) { ) {
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))
} }
InfoCard(R.string.info_lock_task_packages) Notes(R.string.info_lock_task_packages)
} }
@RequiresApi(28) @RequiresApi(28)
@@ -1198,44 +1203,28 @@ private fun ColumnScope.LockTaskFeatures() {
} }
LaunchedEffect(Unit) { refresh() } LaunchedEffect(Unit) { refresh() }
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
RadioButtonItem(R.string.disable_all, !custom) { custom = false } FullWidthRadioButtonItem(R.string.disable_all, !custom) { custom = false }
RadioButtonItem(R.string.custom, custom) { custom = true } FullWidthRadioButtonItem(R.string.custom, custom) { custom = true }
AnimatedVisibility(custom) { AnimatedVisibility(custom, Modifier.padding(top = 4.dp)) {
Column { Column {
CheckBoxItem( listOf(
R.string.ltf_sys_info, DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO to R.string.ltf_sys_info,
flags and LOCK_TASK_FEATURE_SYSTEM_INFO != 0 DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS to R.string.ltf_notifications,
) { flags = flags xor LOCK_TASK_FEATURE_SYSTEM_INFO } DevicePolicyManager.LOCK_TASK_FEATURE_HOME to R.string.ltf_home,
CheckBoxItem( DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW to R.string.ltf_overview,
R.string.ltf_notifications, DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS to R.string.ltf_global_actions,
flags and LOCK_TASK_FEATURE_NOTIFICATIONS != 0 DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD to R.string.ltf_keyguard
) { flags = flags xor LOCK_TASK_FEATURE_NOTIFICATIONS } ).let {
CheckBoxItem( if(VERSION.SDK_INT >= 30)
R.string.ltf_home, it.plus(DevicePolicyManager.LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK to R.string.ltf_block_activity_start_in_task)
flags and LOCK_TASK_FEATURE_HOME != 0 else it
) { flags = flags xor LOCK_TASK_FEATURE_HOME } }.forEach { (id, title) ->
CheckBoxItem( FullWidthCheckBoxItem(title, flags and id != 0) { flags = flags xor id }
R.string.ltf_overview,
flags and LOCK_TASK_FEATURE_OVERVIEW != 0
) { flags = flags xor LOCK_TASK_FEATURE_OVERVIEW }
CheckBoxItem(
R.string.ltf_global_actions,
flags and LOCK_TASK_FEATURE_GLOBAL_ACTIONS != 0
) { flags = flags xor LOCK_TASK_FEATURE_GLOBAL_ACTIONS }
CheckBoxItem(
R.string.ltf_keyguard,
flags and LOCK_TASK_FEATURE_KEYGUARD != 0
) { flags = flags xor LOCK_TASK_FEATURE_KEYGUARD }
if(VERSION.SDK_INT >= 30) {
CheckBoxItem(
R.string.ltf_block_activity_start_in_task,
flags and LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK != 0
) { flags = flags xor LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK }
} }
} }
} }
Button( Button(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp, horizontal = 8.dp),
onClick = { onClick = {
try { try {
dpm.setLockTaskFeatures(receiver, flags) dpm.setLockTaskFeatures(receiver, flags)
@@ -1469,7 +1458,7 @@ fun SecurityLoggingScreen(onNavigateUp: () -> Unit) {
Text(stringResource(R.string.delete_logs)) Text(stringResource(R.string.delete_logs))
} }
} }
InfoCard(R.string.info_security_log) Notes(R.string.info_security_log)
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
Button( Button(
onClick = { onClick = {
@@ -1490,7 +1479,7 @@ fun SecurityLoggingScreen(onNavigateUp: () -> Unit) {
) { ) {
Text(stringResource(R.string.pre_reboot_security_logs)) Text(stringResource(R.string.pre_reboot_security_logs))
} }
InfoCard(R.string.info_pre_reboot_security_log) Notes(R.string.info_pre_reboot_security_log)
} }
} }
@@ -1540,7 +1529,7 @@ fun DisableAccountManagementScreen(onNavigateUp: () -> Unit) {
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }) keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() })
) )
Spacer(Modifier.padding(vertical = 10.dp)) Spacer(Modifier.padding(vertical = 10.dp))
InfoCard(R.string.info_disable_account_management) Notes(R.string.info_disable_account_management)
} }
} }
@@ -1559,7 +1548,7 @@ fun FrpPolicyScreen(onNavigateUp: () -> Unit) {
val accountList = remember { mutableStateListOf<String>() } val accountList = remember { mutableStateListOf<String>() }
var inputAccount by remember { mutableStateOf("") } var inputAccount by remember { mutableStateOf("") }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
var policy: FactoryResetProtectionPolicy? = FactoryResetProtectionPolicy.Builder().build() var policy: FactoryResetProtectionPolicy? = null
try { try {
policy = dpm.getFactoryResetProtectionPolicy(receiver) policy = dpm.getFactoryResetProtectionPolicy(receiver)
} catch(_: UnsupportedOperationException) { } catch(_: UnsupportedOperationException) {
@@ -1575,6 +1564,14 @@ fun FrpPolicyScreen(onNavigateUp: () -> Unit) {
} }
} }
MyScaffold(R.string.frp_policy, 8.dp, onNavigateUp) { MyScaffold(R.string.frp_policy, 8.dp, onNavigateUp) {
if(unsupported) {
Column(
Modifier.fillMaxWidth().padding(vertical = 8.dp)
.clip(RoundedCornerShape(8.dp)).background(colorScheme.primaryContainer)
) {
Text(stringResource(R.string.frp_not_supported), Modifier.padding(8.dp), color = colorScheme.onPrimaryContainer)
}
} else {
Row( Row(
horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth().padding(horizontal = 6.dp, vertical = 8.dp) modifier = Modifier.fillMaxWidth().padding(horizontal = 6.dp, vertical = 8.dp)
@@ -1582,6 +1579,7 @@ fun FrpPolicyScreen(onNavigateUp: () -> Unit) {
Text(stringResource(R.string.use_policy), style = typography.titleLarge) Text(stringResource(R.string.use_policy), style = typography.titleLarge)
Switch(checked = usePolicy, onCheckedChange = { usePolicy = it }) Switch(checked = usePolicy, onCheckedChange = { usePolicy = it })
} }
}
AnimatedVisibility(usePolicy) { AnimatedVisibility(usePolicy) {
Column { Column {
CheckBoxItem(R.string.enable_frp, enabled) { enabled = it } CheckBoxItem(R.string.enable_frp, enabled) { enabled = it }
@@ -1611,31 +1609,22 @@ fun FrpPolicyScreen(onNavigateUp: () -> Unit) {
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
Spacer(Modifier.padding(vertical = 2.dp))
} }
} }
Spacer(Modifier.padding(vertical = 5.dp)) if(!unsupported) Button(
Button(
onClick = { onClick = {
focusMgr.clearFocus() focusMgr.clearFocus()
if(unsupported) {
Toast.makeText(context, R.string.unsupported, Toast.LENGTH_SHORT).show()
} else {
val policy = FactoryResetProtectionPolicy.Builder() val policy = FactoryResetProtectionPolicy.Builder()
.setFactoryResetProtectionEnabled(enabled) .setFactoryResetProtectionEnabled(enabled)
.setFactoryResetProtectionAccounts(accountList) .setFactoryResetProtectionAccounts(accountList)
.build() .build()
dpm.setFactoryResetProtectionPolicy(receiver, policy) dpm.setFactoryResetProtectionPolicy(receiver, policy)
}
}, },
modifier = Modifier.width(100.dp).align(Alignment.CenterHorizontally) modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp)
) { ) {
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))
} }
Spacer(Modifier.padding(vertical = 10.dp)) Notes(R.string.info_frp_policy)
if(unsupported) Text(stringResource(R.string.frp_policy_not_supported))
Spacer(Modifier.padding(vertical = 6.dp))
InfoCard(R.string.info_frp_policy)
} }
} }
@@ -1885,6 +1874,6 @@ fun InstallSystemUpdateScreen(onNavigateUp: () -> Unit) {
} }
} }
Spacer(Modifier.padding(vertical = 10.dp)) Spacer(Modifier.padding(vertical = 10.dp))
InfoCard(R.string.auto_reboot_after_install_succeed) Notes(R.string.auto_reboot_after_install_succeed)
} }
} }

View File

@@ -37,6 +37,7 @@ fun UserRestrictionScreen(onNavigateUp: () -> Unit, onNavigate: (Int, List<Restr
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
MyScaffold(R.string.user_restriction, 0.dp, onNavigateUp) { MyScaffold(R.string.user_restriction, 0.dp, onNavigateUp) {
Spacer(Modifier.padding(vertical = 2.dp))
Text(text = stringResource(R.string.switch_to_disable_feature), modifier = Modifier.padding(start = 16.dp)) Text(text = stringResource(R.string.switch_to_disable_feature), modifier = Modifier.padding(start = 16.dp))
if(context.isProfileOwner) { Text(text = stringResource(R.string.profile_owner_is_restricted), modifier = Modifier.padding(start = 16.dp)) } if(context.isProfileOwner) { Text(text = stringResource(R.string.profile_owner_is_restricted), modifier = Modifier.padding(start = 16.dp)) }
if(context.isProfileOwner && dpm.isManagedProfile(receiver)) { if(context.isProfileOwner && dpm.isManagedProfile(receiver)) {

View File

@@ -70,7 +70,7 @@ import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.CardItem import com.bintianqi.owndroid.ui.CardItem
import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.Notes
import com.bintianqi.owndroid.ui.ListItem import com.bintianqi.owndroid.ui.ListItem
import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.SwitchItem import com.bintianqi.owndroid.ui.SwitchItem
@@ -457,7 +457,7 @@ fun AffiliationIdScreen(onNavigateUp: () -> Unit) {
) { ) {
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))
} }
InfoCard(R.string.info_affiliation_id) Notes(R.string.info_affiliation_id)
} }
} }

View File

@@ -59,7 +59,7 @@ import com.bintianqi.owndroid.ui.CardItem
import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.CopyTextButton import com.bintianqi.owndroid.ui.CopyTextButton
import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.Notes
import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.SwitchItem import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.yesOrNo import com.bintianqi.owndroid.yesOrNo
@@ -230,7 +230,7 @@ fun SuspendPersonalAppScreen(onNavigateUp: () -> Unit) {
) { ) {
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))
} }
InfoCard(R.string.info_profile_maximum_time_off) Notes(R.string.info_profile_maximum_time_off)
} }
} }
@@ -280,7 +280,7 @@ fun CrossProfileIntentFilterScreen(onNavigateUp: () -> Unit) {
) { ) {
Text(stringResource(R.string.clear_cross_profile_filters)) Text(stringResource(R.string.clear_cross_profile_filters))
} }
InfoCard(R.string.info_cross_profile_intent_filter) Notes(R.string.info_cross_profile_intent_filter)
} }
} }

View File

@@ -104,13 +104,20 @@ fun FullWidthRadioButtonItem(
text: Int, text: Int,
selected: Boolean, selected: Boolean,
operation: () -> Unit operation: () -> Unit
) = FullWidthRadioButtonItem(stringResource(text), selected, operation)
@Composable
fun FullWidthRadioButtonItem(
text: String,
selected: Boolean,
operation: () -> Unit
) { ) {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth().clickable(onClick = operation) modifier = Modifier.fillMaxWidth().clickable(onClick = operation)
) { ) {
RadioButton(selected = selected, onClick = operation, modifier = Modifier.padding(horizontal = 4.dp)) RadioButton(selected = selected, onClick = operation, modifier = Modifier.padding(horizontal = 4.dp))
Text(text = stringResource(text), modifier = Modifier.padding(bottom = if(zhCN) { 2 } else { 0 }.dp)) Text(text = text, modifier = Modifier.padding(bottom = if(zhCN) { 2 } else { 0 }.dp))
} }
} }
@@ -272,18 +279,12 @@ fun ListItem(text: String, onDelete: () -> Unit) {
} }
@Composable @Composable
fun InfoCard(@StringRes strID: Int, horizonPadding: Dp = 0.dp) { fun Notes(@StringRes strID: Int, horizonPadding: Dp = 0.dp) {
Column( Icon(Icons.Outlined.Info, null, Modifier.padding(horizontal = horizonPadding).padding(top = 4.dp, bottom = 8.dp))
modifier = Modifier Text(
.fillMaxWidth() stringResource(strID), Modifier.padding(horizontal = horizonPadding),
.padding(vertical = 8.dp, horizontal = horizonPadding) color = colorScheme.onSurfaceVariant, style = typography.bodyMedium
.clip(RoundedCornerShape(12.dp)) )
.background(color = colorScheme.tertiaryContainer)
.padding(8.dp)
) {
Icon(imageVector = Icons.Outlined.Info, contentDescription = null, modifier = Modifier.padding(vertical = 4.dp))
Text(stringResource(strID))
}
} }
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)

View File

@@ -211,7 +211,7 @@
<string name="wipe_work_profile_warning">Ваш рабочий профиль будет УДАЛЕН</string> <string name="wipe_work_profile_warning">Ваш рабочий профиль будет УДАЛЕН</string>
<string name="encryption_status">Статус шифрования</string> <string name="encryption_status">Статус шифрования</string>
<string name="frp_policy">Политика FRP</string> <string name="frp_policy">Политика FRP</string>
<string name="frp_policy_not_supported">Политика FRP не поддерживается на этом устройстве</string> <string name="frp_not_supported">Your device does not support FRP</string> <!--TODO-->
<string name="enable_frp">Включить FRP</string> <string name="enable_frp">Включить FRP</string>
<string name="account_list_is">Список аккаунтов: </string> <string name="account_list_is">Список аккаунтов: </string>
@@ -679,7 +679,6 @@
<string name="info_disable_keyguard">Отключение Keyguard имеет такой же эффект как и выбор \"Нет\" в качестве способа блокировки экрана. Однако этот вызов не имеет эффекта, если в данный момент установлен пароль, PIN-код или графический ключ.\nЕсли пароль, PIN-код или графический ключ были установлены после того как Keyguard был выключен, то Keyguard перестаёт быть выключенным.</string> <string name="info_disable_keyguard">Отключение Keyguard имеет такой же эффект как и выбор \"Нет\" в качестве способа блокировки экрана. Однако этот вызов не имеет эффекта, если в данный момент установлен пароль, PIN-код или графический ключ.\nЕсли пароль, PIN-код или графический ключ были установлены после того как Keyguard был выключен, то Keyguard перестаёт быть выключенным.</string>
<string name="info_evict_credential_encryption_key">Удаляет ключ шифрования учетных данных пользователя из связки ключей. Потребуется повторно ввести учетные данные пользователя, чтобы получить ключ шифрования учетных данных, который будет сохранен в связке ключей для дальнейшего использования. В целях защиты пользовательских данных пользователь будет остановлен и перезапущен.</string> <string name="info_evict_credential_encryption_key">Удаляет ключ шифрования учетных данных пользователя из связки ключей. Потребуется повторно ввести учетные данные пользователя, чтобы получить ключ шифрования учетных данных, который будет сохранен в связке ключей для дальнейшего использования. В целях защиты пользовательских данных пользователь будет остановлен и перезапущен.</string>
<string name="info_reboot">Вы не можете использовать данную функцию, если на устройстве есть активный вызов (звонок).</string> <string name="info_reboot">Вы не можете использовать данную функцию, если на устройстве есть активный вызов (звонок).</string>
<string name="info_change_time">Введите время UNIX в миллисекундах</string>
<string name="info_content_protection_policy">Политика защиты контента управляет сканированием на наличие мошеннических приложений.</string> <string name="info_content_protection_policy">Политика защиты контента управляет сканированием на наличие мошеннических приложений.</string>
<string name="info_permission_policy">Выбрать действие по умолчанию для запросов разрешений от приложений.</string> <string name="info_permission_policy">Выбрать действие по умолчанию для запросов разрешений от приложений.</string>
<string name="info_mte_policy">Установить политику Memory Tagging Extension. Перезагрузите устройство, чтобы применить изменения.</string> <string name="info_mte_policy">Установить политику Memory Tagging Extension. Перезагрузите устройство, чтобы применить изменения.</string>

View File

@@ -218,7 +218,7 @@
<string name="wipe_work_profile_warning">Your work profile will be DELETED</string> <!--TODO--> <string name="wipe_work_profile_warning">Your work profile will be DELETED</string> <!--TODO-->
<string name="encryption_status">Encryption status</string> <!--TODO--> <string name="encryption_status">Encryption status</string> <!--TODO-->
<string name="frp_policy">FRP politikası</string> <string name="frp_policy">FRP politikası</string>
<string name="frp_policy_not_supported">FRP politikası bu cihazda desteklenmiyor</string> <string name="frp_not_supported">Your device does not support FRP</string> <!--TODO-->
<string name="enable_frp">FRP\'yi etkinleştir</string> <string name="enable_frp">FRP\'yi etkinleştir</string>
<string name="account_list_is">"Hesap listesi: "</string> <string name="account_list_is">"Hesap listesi: "</string>

View File

@@ -208,7 +208,7 @@
<string name="wipe_work_profile_warning">你的工作资料将会被删除</string> <string name="wipe_work_profile_warning">你的工作资料将会被删除</string>
<string name="encryption_status">加密状态</string> <string name="encryption_status">加密状态</string>
<string name="frp_policy">FRP策略</string> <string name="frp_policy">FRP策略</string>
<string name="frp_policy_not_supported">这个设备不支持恢复出厂设置保护策略</string> <string name="frp_not_supported">你的设备不支持FRP策略</string>
<string name="enable_frp">启用FRP</string> <string name="enable_frp">启用FRP</string>
<string name="account_list_is">账户列表:</string> <string name="account_list_is">账户列表:</string>
@@ -661,7 +661,6 @@
<string name="info_disable_keyguard">禁用锁屏相当于把锁屏方式设置为“无”。在已设置密码或图案时使用这个功能无效。如果在锁屏禁用时设置密码或图案,锁屏将启用</string> <string name="info_disable_keyguard">禁用锁屏相当于把锁屏方式设置为“无”。在已设置密码或图案时使用这个功能无效。如果在锁屏禁用时设置密码或图案,锁屏将启用</string>
<string name="info_evict_credential_encryption_key">从密钥环中移除用户的凭证加密密钥。用户需要再次输入凭证才能将密钥存储回密钥环中。为了保护用户数据,用户将重新启动</string> <string name="info_evict_credential_encryption_key">从密钥环中移除用户的凭证加密密钥。用户需要再次输入凭证才能将密钥存储回密钥环中。为了保护用户数据,用户将重新启动</string>
<string name="info_reboot">打电话时不能使用此功能</string> <string name="info_reboot">打电话时不能使用此功能</string>
<string name="info_change_time">输入以毫秒为单位的UNIX时间</string>
<string name="info_content_protection_policy">内容保护策略控制对欺骗性应用程序的扫描。</string> <string name="info_content_protection_policy">内容保护策略控制对欺骗性应用程序的扫描。</string>
<string name="info_permission_policy">设置应用申请运行时权限时的默认选择</string> <string name="info_permission_policy">设置应用申请运行时权限时的默认选择</string>
<string name="info_mte_policy">设置内存标记扩展(Memory Tagging Extension)策略。重启设备以应用更改。</string> <string name="info_mte_policy">设置内存标记扩展(Memory Tagging Extension)策略。重启设备以应用更改。</string>

View File

@@ -237,7 +237,7 @@
<string name="wipe_work_profile_warning">Your work profile will be DELETED</string> <string name="wipe_work_profile_warning">Your work profile will be DELETED</string>
<string name="encryption_status">Encryption status</string> <string name="encryption_status">Encryption status</string>
<string name="frp_policy">FRP policy</string> <string name="frp_policy">FRP policy</string>
<string name="frp_policy_not_supported">FRP policy is not supported on this device</string> <string name="frp_not_supported">Your device does not support FRP</string>
<string name="enable_frp">Enable FRP</string> <string name="enable_frp">Enable FRP</string>
<string name="account_list_is">"Account list: "</string> <string name="account_list_is">"Account list: "</string>
@@ -701,7 +701,6 @@
<string name="info_disable_keyguard">Setting the keyguard to disabled has the same effect as choosing "None" as the screen lock type. However, this call has no effect if a password, pin or pattern is currently set.\nIf a password, pin or pattern is set after the keyguard was disabled, the keyguard stops being disabled.</string> <string name="info_disable_keyguard">Setting the keyguard to disabled has the same effect as choosing "None" as the screen lock type. However, this call has no effect if a password, pin or pattern is currently set.\nIf a password, pin or pattern is set after the keyguard was disabled, the keyguard stops being disabled.</string>
<string name="info_evict_credential_encryption_key">Evict the user\'s credential encryption key from the keyring. The user\'s credential will need to be entered again in order to derive the credential encryption key that will be stored back in the keyring for future use. In order to secure user data, the user will be stopped and restarted.</string> <string name="info_evict_credential_encryption_key">Evict the user\'s credential encryption key from the keyring. The user\'s credential will need to be entered again in order to derive the credential encryption key that will be stored back in the keyring for future use. In order to secure user data, the user will be stopped and restarted.</string>
<string name="info_reboot">You can\'t use this function if there is an ongoing call on the device.</string> <string name="info_reboot">You can\'t use this function if there is an ongoing call on the device.</string>
<string name="info_change_time">Input UNIX time in milliseconds</string>
<string name="info_content_protection_policy">Content protection policy controls scanning for deceptive apps.</string> <string name="info_content_protection_policy">Content protection policy controls scanning for deceptive apps.</string>
<string name="info_permission_policy">Set the default response for future runtime permission requests by applications.</string> <string name="info_permission_policy">Set the default response for future runtime permission requests by applications.</string>
<string name="info_mte_policy">Set the Memory Tagging Extension policy. Reboot the device to apply changes.</string> <string name="info_mte_policy">Set the Memory Tagging Extension policy. Reboot the device to apply changes.</string>

View File

@@ -1,14 +1,14 @@
[versions] [versions]
agp = "8.8.0" agp = "8.8.2"
kotlin = "2.0.21" kotlin = "2.0.21"
navigation-compose = "2.8.6" navigation-compose = "2.8.8"
composeBom = "2025.01.01" composeBom = "2025.02.00"
accompanist-drawablepainter = "0.35.0-alpha" accompanist-drawablepainter = "0.35.0-alpha"
accompanist-permissions = "0.37.0" accompanist-permissions = "0.37.0"
shizuku = "13.1.5" shizuku = "13.1.5"
biometric = "1.2.0-alpha05" biometric = "1.2.0-alpha05"
fragment = "1.8.5" fragment = "1.8.6"
dhizuku = "2.5.3" dhizuku = "2.5.3"
hiddenApiBypass = "4.3" hiddenApiBypass = "4.3"
serialization = "1.7.3" serialization = "1.7.3"