From 20a422ba51edcda8d1b2b624150e122641f27b60 Mon Sep 17 00:00:00 2001 From: BinTianqi Date: Sat, 27 Dec 2025 14:45:09 +0800 Subject: [PATCH] feat: a snackbar to undo removal in PackageFunctionScreen (#216) Remove redundant SDK version check --- .../owndroid/AppInstallerActivity.kt | 1 + .../owndroid/AppInstallerViewModel.kt | 18 +++--- .../com/bintianqi/owndroid/MainActivity.kt | 4 -- .../com/bintianqi/owndroid/MyViewModel.kt | 62 ++++++------------- .../bintianqi/owndroid/dpm/Applications.kt | 31 ++++++++-- .../com/bintianqi/owndroid/dpm/Network.kt | 5 +- .../com/bintianqi/owndroid/dpm/Password.kt | 10 ++- .../java/com/bintianqi/owndroid/dpm/System.kt | 22 +++---- .../java/com/bintianqi/owndroid/dpm/Users.kt | 39 ++++++------ .../com/bintianqi/owndroid/dpm/WorkProfile.kt | 48 +++++++------- app/src/main/res/values-zh-rCN/strings.xml | 2 + app/src/main/res/values/strings.xml | 2 + 12 files changed, 115 insertions(+), 129 deletions(-) diff --git a/app/src/main/java/com/bintianqi/owndroid/AppInstallerActivity.kt b/app/src/main/java/com/bintianqi/owndroid/AppInstallerActivity.kt index ca6b948..7fbb400 100644 --- a/app/src/main/java/com/bintianqi/owndroid/AppInstallerActivity.kt +++ b/app/src/main/java/com/bintianqi/owndroid/AppInstallerActivity.kt @@ -16,6 +16,7 @@ class AppInstallerActivity:FragmentActivity() { super.onCreate(savedInstanceState) val vm by viewModels() vm.initialize(intent) + vm.registerInstallerReceiver(this) val theme = ThemeSettings(SP.materialYou, SP.darkTheme, SP.blackTheme) setContent { OwnDroidTheme(theme) { diff --git a/app/src/main/java/com/bintianqi/owndroid/AppInstallerViewModel.kt b/app/src/main/java/com/bintianqi/owndroid/AppInstallerViewModel.kt index db89514..75cc03c 100644 --- a/app/src/main/java/com/bintianqi/owndroid/AppInstallerViewModel.kt +++ b/app/src/main/java/com/bintianqi/owndroid/AppInstallerViewModel.kt @@ -36,13 +36,19 @@ class AppInstallerViewModel(application: Application): AndroidViewModel(applicat intent.getParcelableExtra(Intent.EXTRA_STREAM)?.let { list += it } intent.getParcelableArrayExtra(Intent.EXTRA_STREAM)?.forEach { list += it as Uri } intent.clipData?.let { clipData -> - for(i in 0..clipData.itemCount - 1) { + for(i in 0..) { uiState.update { it.copy(packages = it.packages.plus(packages).distinct()) @@ -93,17 +99,14 @@ class AppInstallerViewModel(application: Application): AndroidViewModel(applicat uiState.update { it.copy(installing = false, packageWriting = -1) } return } - ContextCompat.registerReceiver( - application, Receiver(), IntentFilter(ACTION), null, - null, ContextCompat.RECEIVER_EXPORTED - ) + val intent = Intent(ACTION).setPackage(application.packageName) val pi = if(Build.VERSION.SDK_INT >= 34) { PendingIntent.getBroadcast( - application, sessionId, Intent(ACTION), + application, sessionId, intent, PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT or PendingIntent.FLAG_MUTABLE ).intentSender } else { - PendingIntent.getBroadcast(application, sessionId, Intent(ACTION), PendingIntent.FLAG_MUTABLE).intentSender + PendingIntent.getBroadcast(application, sessionId, intent, PendingIntent.FLAG_MUTABLE).intentSender } session.commit(pi) } @@ -119,7 +122,6 @@ class AppInstallerViewModel(application: Application): AndroidViewModel(applicat ) } else { uiState.update { it.copy(result = intent) } - context.unregisterReceiver(this) } } } diff --git a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt index 0324052..c35d8ca 100644 --- a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt +++ b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt @@ -269,10 +269,6 @@ class MainActivity : FragmentActivity() { } } - override fun onResume() { - super.onResume() - } - } @ExperimentalMaterial3Api diff --git a/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt b/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt index 496f61e..59f4e21 100644 --- a/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt +++ b/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt @@ -276,7 +276,6 @@ class MyViewModel(application: Application): AndroidViewModel(application) { } val packagePermissions = MutableStateFlow(emptyMap()) - @RequiresApi(23) fun getPackagePermissions(name: String) { if (name.isValidPackageName) { packagePermissions.value = runtimePermissions.associate { @@ -286,7 +285,6 @@ class MyViewModel(application: Application): AndroidViewModel(application) { packagePermissions.value = emptyMap() } } - @RequiresApi(23) fun setPackagePermission(name: String, permission: String, status: Int): Boolean { val result = DPM.setPermissionGrantState(DAR, name, permission, status) getPackagePermissions(name) @@ -366,6 +364,7 @@ class MyViewModel(application: Application): AndroidViewModel(application) { } fun uninstallPackage(packageName: String, onComplete: (String?) -> Unit) { + val action = "com.bintianqi.owndroid.action.PACKAGE_UNINSTALLED" val receiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val statusExtra = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 999) @@ -383,16 +382,17 @@ class MyViewModel(application: Application): AndroidViewModel(application) { } } ContextCompat.registerReceiver( - application, receiver, IntentFilter(AppInstallerViewModel.ACTION), null, - null, ContextCompat.RECEIVER_EXPORTED + application, receiver, IntentFilter(action), null, + null, ContextCompat.RECEIVER_NOT_EXPORTED ) + val intent = Intent(action).setPackage(application.packageName) val pi = if(VERSION.SDK_INT >= 34) { PendingIntent.getBroadcast( - application, 0, Intent(AppInstallerViewModel.ACTION), + application, 0, intent, PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT or PendingIntent.FLAG_MUTABLE ).intentSender } else { - PendingIntent.getBroadcast(application, 0, Intent(AppInstallerViewModel.ACTION), PendingIntent.FLAG_MUTABLE).intentSender + PendingIntent.getBroadcast(application, 0, intent, PendingIntent.FLAG_MUTABLE).intentSender } application.getPackageInstaller().uninstall(packageName, pi) } @@ -545,7 +545,6 @@ class MyViewModel(application: Application): AndroidViewModel(application) { val appRestrictions = MutableStateFlow(emptyList()) - @RequiresApi(23) fun getAppRestrictions(name: String) { val rm = application.getSystemService(RestrictionsManager::class.java) try { @@ -569,7 +568,6 @@ class MyViewModel(application: Application): AndroidViewModel(application) { } } - @RequiresApi(23) fun setAppRestrictions(name: String, item: AppRestriction) { viewModelScope.launch(Dispatchers.IO) { val bundle = transformAppRestriction( @@ -580,7 +578,6 @@ class MyViewModel(application: Application): AndroidViewModel(application) { } } - @RequiresApi(23) fun clearAppRestrictions(name: String) { viewModelScope.launch(Dispatchers.IO) { DPM.setApplicationRestrictions(DAR, name, Bundle()) @@ -699,7 +696,7 @@ class MyViewModel(application: Application): AndroidViewModel(application) { autoTimeRequired = if (VERSION.SDK_INT < 30) DPM.autoTimeRequired else false, masterVolumeMuted = DPM.isMasterVolumeMuted(DAR), backupServiceEnabled = if (VERSION.SDK_INT >= 26) DPM.isBackupServiceEnabled(DAR) else false, - btContactSharingDisabled = if (VERSION.SDK_INT >= 23 && privilege.work) + btContactSharingDisabled = if (privilege.work) DPM.getBluetoothContactSharingDisabled(DAR) else false, commonCriteriaMode = if (VERSION.SDK_INT >= 30 && privilege.run { device || org }) DPM.isCommonCriteriaModeEnabled(DAR) else false, @@ -718,7 +715,6 @@ class MyViewModel(application: Application): AndroidViewModel(application) { it.copy(screenCaptureDisabled = DPM.getScreenCaptureDisabled(null)) } } - @RequiresApi(23) fun setStatusBarDisabled(disabled: Boolean) { val result = DPM.setStatusBarDisabled(DAR, disabled) if (result) systemOptionsStatus.update { it.copy(statusBarDisabled = disabled) } @@ -752,7 +748,6 @@ class MyViewModel(application: Application): AndroidViewModel(application) { it.copy(backupServiceEnabled = DPM.isBackupServiceEnabled(DAR)) } } - @RequiresApi(23) fun setBtContactSharingDisabled(disabled: Boolean) { DPM.setBluetoothContactSharingDisabled(DAR, disabled) systemOptionsStatus.update { @@ -771,7 +766,6 @@ class MyViewModel(application: Application): AndroidViewModel(application) { DPM.isUsbDataSignalingEnabled = enabled systemOptionsStatus.update { it.copy(usbSignalEnabled = DPM.isUsbDataSignalingEnabled) } } - @RequiresApi(23) fun setKeyguardDisabled(disabled: Boolean): Boolean { return DPM.setKeyguardDisabled(DAR, disabled) } @@ -841,11 +835,9 @@ class MyViewModel(application: Application): AndroidViewModel(application) { fun setContentProtectionPolicy(policy: Int) { DPM.setContentProtectionPolicy(DAR, policy) } - @RequiresApi(23) fun getPermissionPolicy(): Int { return DPM.getPermissionPolicy(DAR) } - @RequiresApi(23) fun setPermissionPolicy(policy: Int) { DPM.setPermissionPolicy(DAR, policy) } @@ -1031,14 +1023,12 @@ class MyViewModel(application: Application): AndroidViewModel(application) { } } } - @RequiresApi(23) fun getSystemUpdatePolicy(): SystemUpdatePolicyInfo { val policy = DPM.systemUpdatePolicy return SystemUpdatePolicyInfo( policy?.policyType ?: -1, policy?.installWindowStart ?: 0, policy?.installWindowEnd ?: 0 ) } - @RequiresApi(23) fun setSystemUpdatePolicy(info: SystemUpdatePolicyInfo) { val policy = when (info.type) { SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC -> SystemUpdatePolicy.createAutomaticInstallPolicy() @@ -1352,28 +1342,19 @@ class MyViewModel(application: Application): AndroidViewModel(application) { } fun createWorkProfile(options: CreateWorkProfileOptions): Intent { val intent = Intent(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE) - if (VERSION.SDK_INT >= 23) { + intent.putExtra( + DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME, + MyAdminComponent + ) + intent.putExtra( + DevicePolicyManager.EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE, + Account(options.accountName, options.accountType) + ) + if (VERSION.SDK_INT >= 26) { intent.putExtra( - DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME, - MyAdminComponent + DevicePolicyManager.EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION, + options.keepAccount ) - } else { - intent.putExtra( - DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME, - application.packageName - ) - } - if (options.migrateAccount && VERSION.SDK_INT >= 22) { - intent.putExtra( - DevicePolicyManager.EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE, - Account(options.accountName, options.accountType) - ) - if (VERSION.SDK_INT >= 26) { - intent.putExtra( - DevicePolicyManager.EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION, - options.keepAccount - ) - } } if (VERSION.SDK_INT >= 24) { intent.putExtra( @@ -1440,10 +1421,10 @@ class MyViewModel(application: Application): AndroidViewModel(application) { return UserInformation( if (VERSION.SDK_INT >= 24) UserManager.supportsMultipleUsers() else false, if (VERSION.SDK_INT >= 31) UserManager.isHeadlessSystemUserMode() else false, - if (VERSION.SDK_INT >= 23) UM.isSystemUser else false, + UM.isSystemUser, if (VERSION.SDK_INT >= 34) UM.isAdminUser else false, if (VERSION.SDK_INT >= 25) UM.isDemoUser else false, - if (VERSION.SDK_INT >= 23) UM.getUserCreationTime(uh) else 0, + UM.getUserCreationTime(uh), if (VERSION.SDK_INT >= 28) DPM.isLogoutEnabled else false, if (VERSION.SDK_INT >= 28) DPM.isEphemeralUser(DAR) else false, if (VERSION.SDK_INT >= 28) DPM.isAffiliatedUser else false, @@ -1518,7 +1499,6 @@ class MyViewModel(application: Application): AndroidViewModel(application) { fun setProfileName(name: String) { DPM.setProfileName(DAR, name) } - @RequiresApi(23) fun setUserIcon(bitmap: Bitmap) { DPM.setUserIcon(DAR, bitmap) } @@ -1656,7 +1636,6 @@ class MyViewModel(application: Application): AndroidViewModel(application) { return PM.getPackageUid(name, 0) } var networkStatsData = emptyList() - @RequiresApi(23) fun readNetworkStats(stats: NetworkStats): List { val list = mutableListOf() while (stats.hasNextBucket()) { @@ -1667,7 +1646,6 @@ class MyViewModel(application: Application): AndroidViewModel(application) { stats.close() return list } - @RequiresApi(23) fun readNetworkStatsBucket(bucket: NetworkStats.Bucket): NetworkStatsData { return NetworkStatsData( bucket.rxBytes, bucket.rxPackets, bucket.txBytes, bucket.txPackets, diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt index 02d1426..cbae39f 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt @@ -65,6 +65,10 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.SegmentedButton import androidx.compose.material3.SegmentedButtonDefaults import androidx.compose.material3.SingleChoiceSegmentedButtonRow +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.SnackbarResult import androidx.compose.material3.Surface import androidx.compose.material3.Switch import androidx.compose.material3.Text @@ -78,6 +82,7 @@ import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -121,6 +126,7 @@ import com.google.accompanist.drawablepainter.rememberDrawablePainter import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch import kotlinx.serialization.Serializable import sh.calvin.reorderable.ReorderableItem import sh.calvin.reorderable.rememberReorderableLazyListState @@ -213,9 +219,7 @@ fun ApplicationsFeaturesScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Un if(VERSION.SDK_INT >= 30 && (privilege.device || (VERSION.SDK_INT >= 33 && privilege.profile))) { FunctionItem(R.string.disable_user_control, icon = R.drawable.do_not_touch_fill0) { onNavigate(DisableUserControl) } } - if(VERSION.SDK_INT >= 23) { - FunctionItem(R.string.permissions, icon = R.drawable.shield_fill0) { onNavigate(PermissionsManager()) } - } + FunctionItem(R.string.permissions, icon = R.drawable.shield_fill0) { onNavigate(PermissionsManager()) } if(VERSION.SDK_INT >= 28) { FunctionItem(R.string.disable_metered_data, icon = R.drawable.money_off_fill0) { onNavigate(DisableMeteredData) } } @@ -278,7 +282,7 @@ fun ApplicationDetailsScreen( val appRestrictions by vm.appRestrictions.collectAsStateWithLifecycle() LaunchedEffect(Unit) { vm.getAppStatus(packageName) - if (VERSION.SDK_INT >= 23) vm.getAppRestrictions(packageName) + vm.getAppRestrictions(packageName) } MySmallTitleScaffold(R.string.place_holder, onNavigateUp, 0.dp) { Column(Modifier @@ -320,7 +324,7 @@ fun ApplicationDetailsScreen( state = status.keepUninstalled, onCheckedChange = { vm.adSetPackageKu(packageName, it) } ) - if (VERSION.SDK_INT >= 23 && appRestrictions.isNotEmpty()) { + if (appRestrictions.isNotEmpty()) { FunctionItem(R.string.managed_configuration, icon = R.drawable.description_fill0) { onNavigate(ManagedConfiguration(packageName)) } @@ -347,7 +351,6 @@ fun ApplicationDetailsScreen( @Serializable data class PermissionsManager(val packageName: String? = null) -@RequiresApi(23) @Composable fun PermissionsManagerScreen( packagePermissions: MutableStateFlow>, getPackagePermissions: (String) -> Unit, @@ -806,12 +809,15 @@ fun PackageFunctionScreen( chosenPackage: Channel, onChoosePackage: () -> Unit, navigateToGroups: () -> Unit, appGroups: StateFlow>, notes: Int? = null ) { + val context = LocalContext.current val groups by appGroups.collectAsStateWithLifecycle() val packages by packagesState.collectAsStateWithLifecycle() var input by rememberSaveable { mutableStateOf("") } val inputPackages = parsePackageNames(input) var dialog by remember { mutableStateOf(false) } var selectedGroup by remember { mutableStateOf(null) } + val snackbar = remember { SnackbarHostState() } + val coroutine = rememberCoroutineScope() LaunchedEffect(Unit) { onGet() input = chosenPackage.receive() @@ -852,12 +858,25 @@ fun PackageFunctionScreen( } } ) + }, + snackbarHost = { + SnackbarHost(snackbar) } ) { paddingValues -> LazyColumn(Modifier.padding(paddingValues)) { items(packages, { it.name }) { ApplicationItem(it) { onSet(listOf(it.name), false) + coroutine.launch { + val result = snackbar.showSnackbar( + context.getString(R.string.package_removed, it.name), + context.getString(R.string.undo), + true, SnackbarDuration.Short + ) + if (result == SnackbarResult.ActionPerformed) { + onSet(listOf(it.name), true) + } + } } } item { 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 8d6b2da..8723f40 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt @@ -152,7 +152,7 @@ fun NetworkScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) { if(VERSION.SDK_INT >= 30) { FunctionItem(R.string.options, icon = R.drawable.tune_fill0) { onNavigate(NetworkOptions) } } - if (VERSION.SDK_INT >= 23 && !privilege.dhizuku) + if (!privilege.dhizuku) FunctionItem(R.string.network_stats, icon = R.drawable.query_stats_fill0) { onNavigate(QueryNetworkStats) } if(VERSION.SDK_INT >= 29 && privilege.device) { FunctionItem(R.string.private_dns, icon = R.drawable.dns_fill0) { onNavigate(PrivateDns) } @@ -937,7 +937,6 @@ enum class NetworkStatsState(val id: Int, val text: Int) { Default(NetworkStats.Bucket.STATE_DEFAULT, R.string.default_str), Foreground(NetworkStats.Bucket.STATE_FOREGROUND, R.string.foreground) } -@RequiresApi(23) enum class NetworkStatsUID(val uid: Int, val text: Int) { All(NetworkStats.Bucket.UID_ALL, R.string.all), Removed(NetworkStats.Bucket.UID_REMOVED, R.string.uninstalled), @@ -952,7 +951,6 @@ data class QueryNetworkStatsParams( @Serializable object QueryNetworkStats @OptIn(ExperimentalMaterial3Api::class) -@RequiresApi(23) @Composable fun NetworkStatsScreen( chosenPackage: Channel, onChoosePackage: () -> Unit, getUid: (String) -> Int, @@ -1270,7 +1268,6 @@ data class NetworkStatsData( @Serializable object NetworkStatsViewer -@RequiresApi(23) @Composable fun NetworkStatsViewerScreen( data: List, clearData: () -> Unit, onNavigateUp: () -> Unit diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt index fea9381..1ee9166 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt @@ -333,12 +333,10 @@ fun ResetPasswordScreen(resetPassword: (String, String, Int) -> Boolean, onNavig visualTransformation = PasswordVisualTransformation() ) Spacer(Modifier.padding(vertical = 5.dp)) - if(VERSION.SDK_INT >= 23) { - CheckBoxItem( - R.string.do_not_ask_credentials_on_boot, - flags and RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT != 0 - ) { flags = flags xor RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT } - } + CheckBoxItem( + R.string.do_not_ask_credentials_on_boot, + flags and RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT != 0 + ) { flags = flags xor RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT } CheckBoxItem( R.string.reset_password_require_entry, flags and RESET_PASSWORD_REQUIRE_ENTRY != 0 diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt index c6eaa0c..8f0fd09 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt @@ -171,9 +171,7 @@ fun SystemManagerScreen( FunctionItem(R.string.key_pairs, icon = R.drawable.key_vertical_fill0) { navCtrl.navigate("KeyPairs") }*/ if(VERSION.SDK_INT >= 35 && (privilege.device || (privilege.profile && privilege.affiliated))) FunctionItem(R.string.content_protection_policy, icon = R.drawable.search_fill0) { onNavigate(ContentProtectionPolicy) } - if(VERSION.SDK_INT >= 23) { - FunctionItem(R.string.permission_policy, icon = R.drawable.key_fill0) { onNavigate(PermissionPolicy) } - } + FunctionItem(R.string.permission_policy, icon = R.drawable.key_fill0) { onNavigate(PermissionPolicy) } if(VERSION.SDK_INT >= 34 && privilege.device) { FunctionItem(R.string.mte_policy, icon = R.drawable.memory_fill0) { onNavigate(MtePolicy) } } @@ -204,7 +202,7 @@ fun SystemManagerScreen( FunctionItem(R.string.support_messages, icon = R.drawable.chat_fill0) { onNavigate(SupportMessage) } } FunctionItem(R.string.disable_account_management, icon = R.drawable.account_circle_fill0) { onNavigate(DisableAccountManagement) } - if(VERSION.SDK_INT >= 23 && (privilege.device || privilege.org)) { + if (privilege.device || privilege.org) { FunctionItem(R.string.system_update_policy, icon = R.drawable.system_update_fill0) { onNavigate(SetSystemUpdatePolicy) } } if(VERSION.SDK_INT >= 29 && (privilege.device || privilege.org)) { @@ -368,7 +366,7 @@ fun SystemOptionsScreen(vm: MyViewModel, onNavigateUp: () -> Unit) { SwitchItem(R.string.enable_usb_signal, status.usbSignalEnabled, vm::setUsbSignalEnabled, R.drawable.usb_fill0) } - if (VERSION.SDK_INT >= 23 && VERSION.SDK_INT < 34) { + if (VERSION.SDK_INT < 34) { Row( Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding), verticalAlignment = Alignment.CenterVertically @@ -413,8 +411,8 @@ fun KeyguardScreen( val context = LocalContext.current val privilege by Privilege.status.collectAsStateWithLifecycle() MyScaffold(R.string.keyguard, onNavigateUp) { - if (VERSION.SDK_INT >= 23 && (privilege.device || - (VERSION.SDK_INT >= 28 && privilege.profile && privilege.affiliated))) { + if (privilege.device || + (VERSION.SDK_INT >= 28 && privilege.profile && privilege.affiliated)) { Row( horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth() @@ -435,7 +433,7 @@ fun KeyguardScreen( Notes(R.string.info_disable_keyguard) Spacer(Modifier.padding(vertical = 12.dp)) } - if(VERSION.SDK_INT >= 23) Text(text = stringResource(R.string.lock_now), style = typography.headlineLarge) + Text(text = stringResource(R.string.lock_now), style = typography.headlineLarge) Spacer(Modifier.padding(vertical = 2.dp)) var evictKey by rememberSaveable { mutableStateOf(false) } Button( @@ -1007,7 +1005,6 @@ fun ContentProtectionPolicyScreen( @Serializable object PermissionPolicy -@RequiresApi(23) @Composable fun PermissionPolicyScreen( getPolicy: () -> Int, setPolicy: (Int) -> Unit, onNavigateUp: () -> Unit @@ -1215,7 +1212,7 @@ private fun StartLockTaskMode( var activity by rememberSaveable { mutableStateOf("") } var specifyActivity by rememberSaveable { mutableStateOf(false) } var clearTask by rememberSaveable { mutableStateOf(true) } - var showNotification by rememberSaveable() { mutableStateOf(true) } + var showNotification by rememberSaveable { mutableStateOf(true) } LaunchedEffect(Unit) { packageName = chosenPackage.receive() } @@ -1785,7 +1782,7 @@ fun WipeDataScreen( FullWidthCheckBoxItem(R.string.wipe_external_storage, flag and WIPE_EXTERNAL_STORAGE != 0) { flag = flag xor WIPE_EXTERNAL_STORAGE } - if(VERSION.SDK_INT >= 22 && privilege.device) FullWidthCheckBoxItem( + if (privilege.device) FullWidthCheckBoxItem( R.string.wipe_reset_protection_data, flag and WIPE_RESET_PROTECTION_DATA != 0) { flag = flag xor WIPE_RESET_PROTECTION_DATA } @@ -1839,7 +1836,7 @@ fun WipeDataScreen( text = { Text( text = stringResource( - if(VERSION.SDK_INT >= 23 && userManager.isSystemUser) R.string.wipe_data_warning + if (userManager.isSystemUser) R.string.wipe_data_warning else R.string.info_wipe_data_in_managed_user ), color = colorScheme.error @@ -1880,7 +1877,6 @@ data class PendingSystemUpdateInfo(val exists: Boolean, val time: Long, val secu @Serializable object SetSystemUpdatePolicy -@RequiresApi(23) @Composable fun SystemUpdatePolicyScreen( getPolicy: () -> SystemUpdatePolicyInfo, setPolicy: (SystemUpdatePolicyInfo) -> Unit, diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Users.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Users.kt index 2177563..18776a3 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Users.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Users.kt @@ -30,12 +30,12 @@ import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExposedDropdownMenuAnchorType import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.FilledTonalIconButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton -import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.SegmentedButton import androidx.compose.material3.SegmentedButtonDefaults @@ -105,25 +105,23 @@ fun UsersScreen(vm: MyViewModel, onNavigateUp: () -> Unit, onNavigate: (Any) -> FunctionItem(R.string.create_user, icon = R.drawable.person_add_fill0) { onNavigate(CreateUser) } } FunctionItem(R.string.change_username, icon = R.drawable.edit_fill0) { onNavigate(ChangeUsername) } - if(VERSION.SDK_INT >= 23) { - var changeUserIconDialog by remember { mutableStateOf(false) } - var bitmap: Bitmap? by remember { mutableStateOf(null) } - val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { - if(it != null) uriToStream(context, it) { stream -> - bitmap = BitmapFactory.decodeStream(stream) - if(bitmap != null) changeUserIconDialog = true - } + var changeUserIconDialog by remember { mutableStateOf(false) } + var bitmap: Bitmap? by remember { mutableStateOf(null) } + val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { + if(it != null) uriToStream(context, it) { stream -> + bitmap = BitmapFactory.decodeStream(stream) + if(bitmap != null) changeUserIconDialog = true } - FunctionItem(R.string.change_user_icon, icon = R.drawable.account_circle_fill0) { - context.popToast(R.string.select_an_image) - launcher.launch("image/*") - } - if (changeUserIconDialog) ChangeUserIconDialog( - bitmap!!, { - vm.setUserIcon(bitmap!!) - changeUserIconDialog = false - }) { changeUserIconDialog = false } } + FunctionItem(R.string.change_user_icon, icon = R.drawable.account_circle_fill0) { + context.popToast(R.string.select_an_image) + launcher.launch("image/*") + } + if (changeUserIconDialog) ChangeUserIconDialog( + bitmap!!, { + vm.setUserIcon(bitmap!!) + changeUserIconDialog = false + }) { changeUserIconDialog = false } if(VERSION.SDK_INT >= 28 && privilege.device) { FunctionItem(R.string.user_session_msg, icon = R.drawable.notifications_fill0) { onNavigate(UserSessionMessage) } } @@ -191,7 +189,7 @@ fun UserInfoScreen(getInfo: () -> UserInformation, onNavigateUp: () -> Unit) { if (VERSION.SDK_INT >= 24) InfoItem(R.string.support_multiuser, info.multiUser.yesOrNo) if (VERSION.SDK_INT >= 31) InfoItem(R.string.headless_system_user_mode, info.headless.yesOrNo, true) { infoDialog = 1 } Spacer(Modifier.height(8.dp)) - if (VERSION.SDK_INT >= 23) InfoItem(R.string.system_user, info.system.yesOrNo) + InfoItem(R.string.system_user, info.system.yesOrNo) if (VERSION.SDK_INT >= 34) InfoItem(R.string.admin_user, info.admin.yesOrNo) if (VERSION.SDK_INT >= 25) InfoItem(R.string.demo_user, info.demo.yesOrNo) if (info.time != 0L) InfoItem(R.string.creation_time, formatDate(info.time)) @@ -264,7 +262,7 @@ fun UserOperationScreen( input, { input = it }, Modifier .fillMaxWidth() - .menuAnchor(MenuAnchorType.PrimaryEditable) + .menuAnchor(ExposedDropdownMenuAnchorType.PrimaryEditable) .padding(top = 4.dp, bottom = 8.dp), label = { Text(stringResource(if(useUserId) R.string.user_id else R.string.serial_number)) @@ -586,7 +584,6 @@ fun UserSessionMessageScreen( } } -@RequiresApi(23) @Composable private fun ChangeUserIconDialog(bitmap: Bitmap, onSet: () -> Unit, onClose: () -> Unit) { AlertDialog( diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/WorkProfile.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/WorkProfile.kt index 180deed..beb3cb3 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/WorkProfile.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/WorkProfile.kt @@ -101,31 +101,29 @@ fun CreateWorkProfileScreen( var migrateAccountName by remember { mutableStateOf("") } var migrateAccountType by remember { mutableStateOf("") } var keepAccount by remember { mutableStateOf(true) } - if (VERSION.SDK_INT >= 22) { - FullWidthCheckBoxItem(R.string.migrate_account, migrateAccount) { migrateAccount = it } - AnimatedVisibility(migrateAccount) { - val fr = FocusRequester() - Column(modifier = Modifier.padding(start = 10.dp)) { - OutlinedTextField( - value = migrateAccountName, onValueChange = { migrateAccountName = it }, - label = { Text(stringResource(R.string.account_name)) }, - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), - keyboardActions = KeyboardActions { fr.requestFocus() }, - modifier = Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding) - ) - OutlinedTextField( - value = migrateAccountType, onValueChange = { migrateAccountType = it }, - label = { Text(stringResource(R.string.account_type)) }, - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), - keyboardActions = KeyboardActions { focusMgr.clearFocus() }, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = HorizontalPadding) - .focusRequester(fr) - ) - if(VERSION.SDK_INT >= 26) { - FullWidthCheckBoxItem(R.string.keep_account, keepAccount) { keepAccount = it } - } + FullWidthCheckBoxItem(R.string.migrate_account, migrateAccount) { migrateAccount = it } + AnimatedVisibility(migrateAccount) { + val fr = FocusRequester() + Column(modifier = Modifier.padding(start = 10.dp)) { + OutlinedTextField( + value = migrateAccountName, onValueChange = { migrateAccountName = it }, + label = { Text(stringResource(R.string.account_name)) }, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), + keyboardActions = KeyboardActions { fr.requestFocus() }, + modifier = Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding) + ) + OutlinedTextField( + value = migrateAccountType, onValueChange = { migrateAccountType = it }, + label = { Text(stringResource(R.string.account_type)) }, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + keyboardActions = KeyboardActions { focusMgr.clearFocus() }, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = HorizontalPadding) + .focusRequester(fr) + ) + if(VERSION.SDK_INT >= 26) { + FullWidthCheckBoxItem(R.string.keep_account, keepAccount) { keepAccount = it } } } } diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 1be5e09..cf5b18f 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -68,6 +68,7 @@ 超时 继续 退出 + 撤销 Profile owner @@ -381,6 +382,7 @@ 托管配置 清除配置 指定值 + 移除了 %1$s 用户限制 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8ac92ed..9df041d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -73,6 +73,7 @@ Timeout Continue Exit + Undo Profile owner @@ -415,6 +416,7 @@ Managed configuration Clear configurations Specify value + Removed %1$s User restriction