package com.bintianqi.owndroid.dpm import android.annotation.SuppressLint import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyManager.FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY 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_NONE 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_ENABLED import android.app.admin.DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY import android.app.admin.DevicePolicyManager.NEARBY_STREAMING_DISABLED import android.app.admin.DevicePolicyManager.NEARBY_STREAMING_ENABLED import android.app.admin.DevicePolicyManager.NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY import android.app.admin.DevicePolicyManager.NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY import android.app.admin.DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY import android.app.admin.DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT import android.app.admin.DevicePolicyManager.PERMISSION_POLICY_PROMPT import android.app.admin.DevicePolicyManager.WIPE_EUICC import android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE import android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA import android.app.admin.DevicePolicyManager.WIPE_SILENTLY import android.app.admin.SystemUpdateInfo import android.app.admin.SystemUpdatePolicy import android.app.admin.SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC import android.app.admin.SystemUpdatePolicy.TYPE_INSTALL_WINDOWED import android.app.admin.SystemUpdatePolicy.TYPE_POSTPONE import android.content.ComponentName import android.content.Context import android.content.Intent import android.net.Uri import android.os.Binder import android.os.Build.VERSION import android.os.UserManager import android.util.Log import android.widget.Toast import androidx.activity.ComponentActivity import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateContentSize import androidx.compose.foundation.ScrollState import androidx.compose.foundation.focusable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.foundation.verticalScroll import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold 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 import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import com.bintianqi.owndroid.R import com.bintianqi.owndroid.Receiver import com.bintianqi.owndroid.fileUriFlow import com.bintianqi.owndroid.getFile import com.bintianqi.owndroid.toText import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.Information import com.bintianqi.owndroid.ui.RadioButtonItem import com.bintianqi.owndroid.ui.SubPageItem import com.bintianqi.owndroid.ui.SwitchItem import com.bintianqi.owndroid.ui.TopBar import com.bintianqi.owndroid.uriToStream import java.util.Date @Composable fun SystemManage(navCtrl:NavHostController) { val localNavCtrl = rememberNavController() val backStackEntry by localNavCtrl.currentBackStackEntryAsState() val scrollState = rememberScrollState() val rebootDialog = remember { mutableStateOf(false) } val bugReportDialog = remember { mutableStateOf(false) } Scaffold( topBar = { TopBar(backStackEntry,navCtrl,localNavCtrl) { if(backStackEntry?.destination?.route=="Home"&&scrollState.maxValue>80) { Text( text = stringResource(R.string.system_manage), modifier = Modifier.alpha((maxOf(scrollState.value-30,0)).toFloat()/80) ) } } } ) { NavHost( navController = localNavCtrl, startDestination = "Home", enterTransition = Animations.navHostEnterTransition, exitTransition = Animations.navHostExitTransition, popEnterTransition = Animations.navHostPopEnterTransition, popExitTransition = Animations.navHostPopExitTransition, modifier = Modifier.padding(top = it.calculateTopPadding()) ) { composable(route = "Home") { Home(localNavCtrl, scrollState, rebootDialog, bugReportDialog) } composable(route = "Switches") { Switches() } composable(route = "Keyguard") { Keyguard() } composable(route = "EditTime") { EditTime() } composable(route = "PermissionPolicy") { PermissionPolicy() } composable(route = "MTEPolicy") { MTEPolicy() } composable(route = "NearbyStreamingPolicy") { NearbyStreamingPolicy() } composable(route = "LockTaskFeatures") { LockTaskFeatures() } composable(route = "CaCert") { CaCert() } composable(route = "SecurityLogs") { SecurityLogs() } composable(route = "SystemUpdatePolicy") { SysUpdatePolicy() } composable(route = "InstallSystemUpdate") { InstallSystemUpdate() } composable(route = "WipeData") { WipeData() } } } if(rebootDialog.value) { RebootDialog(rebootDialog) } if(bugReportDialog.value) { BugReportDialog(bugReportDialog) } } @Composable private fun Home(navCtrl: NavHostController, scrollState: ScrollState, rebootDialog: MutableState, bugReportDialog: MutableState) { val context = LocalContext.current val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val receiver = ComponentName(context, Receiver::class.java) Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) { Text( text = stringResource(R.string.system_manage), style = typography.headlineLarge, modifier = Modifier.padding(top = 8.dp, bottom = 5.dp, start = 15.dp) ) if(isDeviceOwner(dpm) || isProfileOwner(dpm)) { SubPageItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("Switches") } } SubPageItem(R.string.keyguard, "", R.drawable.screen_lock_portrait_fill0) { navCtrl.navigate("Keyguard") } if(VERSION.SDK_INT >= 24) { SubPageItem(R.string.bug_report, "", R.drawable.bug_report_fill0) { bugReportDialog.value = true } SubPageItem(R.string.reboot, "", R.drawable.restart_alt_fill0) { rebootDialog.value = true } } if(VERSION.SDK_INT >= 28) { SubPageItem(R.string.edit_time, "", R.drawable.schedule_fill0) { navCtrl.navigate("EditTime") } } if(VERSION.SDK_INT >= 23 && (isDeviceOwner(dpm) || isProfileOwner(dpm))) { SubPageItem(R.string.permission_policy, "", R.drawable.key_fill0) { navCtrl.navigate("PermissionPolicy") } } if(VERSION.SDK_INT >= 34 && isDeviceOwner(dpm)) { SubPageItem(R.string.mte_policy, "", R.drawable.memory_fill0) { navCtrl.navigate("MTEPolicy") } } if(VERSION.SDK_INT >= 31 && (isDeviceOwner(dpm) || isProfileOwner(dpm))) { SubPageItem(R.string.nearby_streaming_policy, "", R.drawable.share_fill0) { navCtrl.navigate("NearbyStreamingPolicy") } } if(VERSION.SDK_INT >= 28 && isDeviceOwner(dpm)) { SubPageItem(R.string.lock_task_feature, "", R.drawable.lock_fill0) { navCtrl.navigate("LockTaskFeatures") } } if(isDeviceOwner(dpm) || isProfileOwner(dpm)) { SubPageItem(R.string.ca_cert, "", R.drawable.license_fill0) { navCtrl.navigate("CaCert") } } if(VERSION.SDK_INT >= 26 && (isDeviceOwner(dpm) || (VERSION.SDK_INT >= 30 && isProfileOwner(dpm) && dpm.isOrganizationOwnedDeviceWithManagedProfile))) { SubPageItem(R.string.security_logs, "", R.drawable.description_fill0) { navCtrl.navigate("SecurityLogs") } } if(VERSION.SDK_INT >= 23 && isDeviceOwner(dpm)) { SubPageItem(R.string.system_update_policy, "", R.drawable.system_update_fill0) { navCtrl.navigate("SystemUpdatePolicy") } } if( VERSION.SDK_INT >= 29 && (isDeviceOwner(dpm) || (VERSION.SDK_INT >= 30 && isProfileOwner(dpm) && dpm.isManagedProfile(receiver) && dpm.isOrganizationOwnedDeviceWithManagedProfile)) ) { SubPageItem(R.string.install_system_update, "", R.drawable.system_update_fill0) { navCtrl.navigate("InstallSystemUpdate") } } SubPageItem(R.string.wipe_data, "", R.drawable.warning_fill0) { navCtrl.navigate("WipeData") } Spacer(Modifier.padding(vertical = 30.dp)) LaunchedEffect(Unit) { fileUriFlow.value = Uri.parse("") } } } @Composable private fun Switches() { val context = LocalContext.current val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val receiver = ComponentName(context, Receiver::class.java) Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) { Spacer(Modifier.padding(vertical = 10.dp)) if(isDeviceOwner(dpm) || isProfileOwner(dpm)) { SwitchItem(R.string.disable_cam,"", R.drawable.photo_camera_fill0, { dpm.getCameraDisabled(null) }, { dpm.setCameraDisabled(receiver,it) } ) } if(isDeviceOwner(dpm) || isProfileOwner(dpm)) { SwitchItem(R.string.disable_screenshot, stringResource(R.string.also_disable_aosp_screen_record), R.drawable.screenshot_fill0, { dpm.getScreenCaptureDisabled(null) }, { dpm.setScreenCaptureDisabled(receiver,it) } ) } if(VERSION.SDK_INT >= 34 && (isDeviceOwner(dpm) || (isProfileOwner(dpm) && dpm.isAffiliatedUser))) { SwitchItem(R.string.disable_status_bar, "", R.drawable.notifications_fill0, { dpm.isStatusBarDisabled}, { dpm.setStatusBarDisabled(receiver,it) } ) } if(isDeviceOwner(dpm) || (VERSION.SDK_INT >= 30 && isProfileOwner(dpm) && dpm.isOrganizationOwnedDeviceWithManagedProfile)) { if(VERSION.SDK_INT >= 30) { SwitchItem(R.string.auto_time, "", R.drawable.schedule_fill0, { dpm.getAutoTimeEnabled(receiver) }, { dpm.setAutoTimeEnabled(receiver,it) } ) SwitchItem(R.string.auto_timezone, "", R.drawable.globe_fill0, { dpm.getAutoTimeZoneEnabled(receiver) }, { dpm.setAutoTimeZoneEnabled(receiver,it) } ) }else{ SwitchItem(R.string.auto_time, "", R.drawable.schedule_fill0, { dpm.autoTimeRequired}, { dpm.setAutoTimeRequired(receiver,it) }) } } if(isDeviceOwner(dpm) || isProfileOwner(dpm)) { SwitchItem(R.string.master_mute, "", R.drawable.volume_up_fill0, { dpm.isMasterVolumeMuted(receiver) }, { dpm.setMasterVolumeMuted(receiver,it) } ) } if(VERSION.SDK_INT >= 26 && (isDeviceOwner(dpm) || isProfileOwner(dpm))) { SwitchItem(R.string.backup_service, "", R.drawable.backup_fill0, { dpm.isBackupServiceEnabled(receiver) }, { dpm.setBackupServiceEnabled(receiver,it) } ) } if(VERSION.SDK_INT >= 23 && (isDeviceOwner(dpm) || isProfileOwner(dpm))) { SwitchItem(R.string.disable_bt_contact_share, "", R.drawable.account_circle_fill0, { dpm.getBluetoothContactSharingDisabled(receiver) }, { dpm.setBluetoothContactSharingDisabled(receiver,it) } ) } if(VERSION.SDK_INT >= 30 && isDeviceOwner(dpm)) { SwitchItem(R.string.common_criteria_mode, stringResource(R.string.common_criteria_mode_desc),R.drawable.security_fill0, { dpm.isCommonCriteriaModeEnabled(receiver) }, { dpm.setCommonCriteriaModeEnabled(receiver,it) } ) } if(VERSION.SDK_INT >= 31 && (isDeviceOwner(dpm) || (VERSION.SDK_INT >= 30 && isProfileOwner(dpm) && dpm.isOrganizationOwnedDeviceWithManagedProfile))) { SwitchItem( R.string.usb_signal, "", R.drawable.usb_fill0, { dpm.isUsbDataSignalingEnabled }, { if(dpm.canUsbDataSignalingBeDisabled()) { dpm.isUsbDataSignalingEnabled = it } else { Toast.makeText(context, R.string.unsupported, Toast.LENGTH_SHORT).show() } } ) } Spacer(Modifier.padding(vertical = 30.dp)) } } @Composable private fun Keyguard() { val context = LocalContext.current val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val receiver = ComponentName(context,Receiver::class.java) Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { Spacer(Modifier.padding(vertical = 10.dp)) Text(text = stringResource(R.string.keyguard), style = typography.headlineLarge) Spacer(Modifier.padding(vertical = 5.dp)) if(VERSION.SDK_INT >= 23) { Button( onClick = { Toast.makeText(context, if(dpm.setKeyguardDisabled(receiver,true)) R.string.success else R.string.fail, Toast.LENGTH_SHORT).show() }, enabled = isDeviceOwner(dpm) || (VERSION.SDK_INT >= 28 && isProfileOwner(dpm) && dpm.isAffiliatedUser), modifier = Modifier.fillMaxWidth() ) { Text(stringResource(R.string.disable)) } Button( onClick = { Toast.makeText(context, if(dpm.setKeyguardDisabled(receiver,false)) R.string.success else R.string.fail, Toast.LENGTH_SHORT).show() }, enabled = isDeviceOwner(dpm) || (VERSION.SDK_INT >= 28 && isProfileOwner(dpm) && dpm.isAffiliatedUser), modifier = Modifier.fillMaxWidth() ) { Text(stringResource(R.string.enable)) } Spacer(Modifier.padding(vertical = 3.dp)) Information{ Text(text = stringResource(R.string.require_no_password_to_disable)) } Spacer(Modifier.padding(vertical = 8.dp)) } var flag by remember { mutableIntStateOf(0) } Button( onClick = { dpm.lockNow() }, enabled = dpm.isAdminActive(receiver), modifier = Modifier.fillMaxWidth() ) { Text(stringResource(R.string.lock_now)) } if(VERSION.SDK_INT >= 26) { CheckBoxItem( stringResource(R.string.require_enter_password_again), { flag==FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY }, { flag = if(flag==0) {1}else{0} } ) } Spacer(Modifier.padding(vertical = 30.dp)) } } @SuppressLint("NewApi") @Composable fun BugReportDialog(status: MutableState) { val context = LocalContext.current val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val receiver = ComponentName(context,Receiver::class.java) AlertDialog( onDismissRequest = { status.value = false }, title = { Text(stringResource(R.string.bug_report)) }, text = { Text(stringResource(R.string.confirm_bug_report)) }, dismissButton = { TextButton(onClick = { status.value = false }) { Text(stringResource(R.string.cancel)) } }, confirmButton = { TextButton( onClick = { val result = dpm.requestBugreport(receiver) Toast.makeText(context, if(result) R.string.success else R.string.fail, Toast.LENGTH_SHORT).show() } ) { Text(stringResource(R.string.confirm)) } }, modifier = Modifier.fillMaxWidth() ) } @SuppressLint("NewApi") @Composable fun RebootDialog(status: MutableState) { val context = LocalContext.current val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val receiver = ComponentName(context,Receiver::class.java) AlertDialog( onDismissRequest = { status.value = false }, title = { Text(stringResource(R.string.reboot)) }, text = { Text(stringResource(R.string.confirm_reboot)) }, dismissButton = { TextButton(onClick = { status.value = false }) { Text(stringResource(R.string.cancel)) } }, confirmButton = { TextButton( onClick = { dpm.reboot(receiver) }, colors = ButtonDefaults.textButtonColors(contentColor = colorScheme.error) ) { Text(stringResource(R.string.reboot)) } }, modifier = Modifier.fillMaxWidth() ) } @SuppressLint("NewApi") @Composable private fun EditTime() { val context = LocalContext.current val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val receiver = ComponentName(context,Receiver::class.java) val focusMgr = LocalFocusManager.current Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { Spacer(Modifier.padding(vertical = 10.dp)) Text(text = stringResource(R.string.edit_time), style = typography.headlineLarge) Spacer(Modifier.padding(vertical = 5.dp)) var inputTime by remember { mutableStateOf("") } Text(text = stringResource(R.string.from_epoch_to_target_time)) Spacer(Modifier.padding(vertical = 5.dp)) OutlinedTextField( value = inputTime, label = { Text(stringResource(R.string.time_unit_ms)) }, onValueChange = { inputTime = it}, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done), keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus() }), enabled = isDeviceOwner(dpm) || (VERSION.SDK_INT >= 30 && isProfileOwner(dpm) && dpm.isOrganizationOwnedDeviceWithManagedProfile), modifier = Modifier.focusable().fillMaxWidth() ) Spacer(Modifier.padding(vertical = 5.dp)) Button( onClick = { dpm.setTime(receiver,inputTime.toLong()) }, modifier = Modifier.fillMaxWidth(), enabled = inputTime!="" && (isDeviceOwner(dpm) || (VERSION.SDK_INT >= 30 && isProfileOwner(dpm) && dpm.isOrganizationOwnedDeviceWithManagedProfile)) ) { Text(stringResource(R.string.apply)) } Button( onClick = { inputTime = System.currentTimeMillis().toString() }, modifier = Modifier.fillMaxWidth() ) { Text(stringResource(R.string.get_current_time)) } } } @SuppressLint("NewApi") @Composable private fun PermissionPolicy() { val context = LocalContext.current val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val receiver = ComponentName(context,Receiver::class.java) Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { var selectedPolicy by remember { mutableIntStateOf(dpm.getPermissionPolicy(receiver)) } Spacer(Modifier.padding(vertical = 10.dp)) Text(text = stringResource(R.string.permission_policy), style = typography.headlineLarge) Spacer(Modifier.padding(vertical = 5.dp)) RadioButtonItem(stringResource(R.string.default_stringres), { selectedPolicy==PERMISSION_POLICY_PROMPT}, { selectedPolicy= PERMISSION_POLICY_PROMPT}) RadioButtonItem(stringResource(R.string.auto_grant), { selectedPolicy==PERMISSION_POLICY_AUTO_GRANT}, { selectedPolicy= PERMISSION_POLICY_AUTO_GRANT}) RadioButtonItem(stringResource(R.string.auto_deny), { selectedPolicy==PERMISSION_POLICY_AUTO_DENY}, { selectedPolicy= PERMISSION_POLICY_AUTO_DENY}) Spacer(Modifier.padding(vertical = 5.dp)) Button( onClick = { dpm.setPermissionPolicy(receiver,selectedPolicy) Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() }, modifier = Modifier.fillMaxWidth() ) { Text(stringResource(R.string.apply)) } } } @SuppressLint("NewApi") @Composable private fun MTEPolicy() { val context = LocalContext.current val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { Spacer(Modifier.padding(vertical = 10.dp)) Text(text = stringResource(R.string.mte_policy), style = typography.headlineLarge) Spacer(Modifier.padding(vertical = 5.dp)) var selectedMtePolicy by remember { mutableIntStateOf(dpm.mtePolicy) } RadioButtonItem( stringResource(R.string.decide_by_user), { selectedMtePolicy == MTE_NOT_CONTROLLED_BY_POLICY }, { selectedMtePolicy = MTE_NOT_CONTROLLED_BY_POLICY } ) RadioButtonItem( stringResource(R.string.enabled), { selectedMtePolicy == MTE_ENABLED }, { selectedMtePolicy = MTE_ENABLED } ) RadioButtonItem( stringResource(R.string.disabled), { selectedMtePolicy == MTE_DISABLED }, { selectedMtePolicy = MTE_DISABLED } ) Button( onClick = { try { dpm.mtePolicy = selectedMtePolicy Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() }catch(e:java.lang.UnsupportedOperationException) { Toast.makeText(context, R.string.unsupported, Toast.LENGTH_SHORT).show() } selectedMtePolicy = dpm.mtePolicy }, modifier = Modifier.fillMaxWidth() ) { Text(stringResource(R.string.apply)) } Spacer(Modifier.padding(vertical = 5.dp)) Information { Text(stringResource(R.string.mte_policy_desc)) } Spacer(Modifier.padding(vertical = 30.dp)) } } @SuppressLint("NewApi") @Composable private fun NearbyStreamingPolicy() { val context = LocalContext.current val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { var appPolicy by remember { mutableIntStateOf(dpm.nearbyAppStreamingPolicy) } Spacer(Modifier.padding(vertical = 10.dp)) Text(text = stringResource(R.string.nearby_app_streaming), style = typography.titleLarge) Spacer(Modifier.padding(vertical = 3.dp)) RadioButtonItem( stringResource(R.string.decide_by_user), { appPolicy == NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY }, { appPolicy = NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY } ) RadioButtonItem( stringResource(R.string.enabled), { appPolicy == NEARBY_STREAMING_ENABLED }, { appPolicy = NEARBY_STREAMING_ENABLED } ) RadioButtonItem( stringResource(R.string.disabled), { appPolicy == NEARBY_STREAMING_DISABLED }, { appPolicy = NEARBY_STREAMING_DISABLED } ) RadioButtonItem( stringResource(R.string.enable_if_secure_enough), { appPolicy == NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY }, { appPolicy = NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY } ) Spacer(Modifier.padding(vertical = 3.dp)) Button( onClick = { dpm.nearbyAppStreamingPolicy = appPolicy Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() }, modifier = Modifier.fillMaxWidth() ) { Text(stringResource(R.string.apply)) } var notificationPolicy by remember { mutableIntStateOf(dpm.nearbyNotificationStreamingPolicy) } Spacer(Modifier.padding(vertical = 10.dp)) Text(text = stringResource(R.string.nearby_notification_streaming), style = typography.titleLarge) Spacer(Modifier.padding(vertical = 3.dp)) RadioButtonItem( stringResource(R.string.decide_by_user), { notificationPolicy == NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY }, { notificationPolicy = NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY } ) RadioButtonItem( stringResource(R.string.enabled), { notificationPolicy == NEARBY_STREAMING_ENABLED }, { notificationPolicy = NEARBY_STREAMING_ENABLED } ) RadioButtonItem( stringResource(R.string.disabled), { notificationPolicy == NEARBY_STREAMING_DISABLED }, { notificationPolicy = NEARBY_STREAMING_DISABLED } ) RadioButtonItem( stringResource(R.string.enable_if_secure_enough), { notificationPolicy == NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY }, { notificationPolicy = NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY } ) Spacer(Modifier.padding(vertical = 3.dp)) Button( onClick = { dpm.nearbyNotificationStreamingPolicy = notificationPolicy Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() }, modifier = Modifier.fillMaxWidth() ) { Text(stringResource(R.string.apply)) } Spacer(Modifier.padding(vertical = 30.dp)) } } @SuppressLint("NewApi") @Composable private fun LockTaskFeatures() { val context = LocalContext.current val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val receiver = ComponentName(context,Receiver::class.java) val focusMgr = LocalFocusManager.current Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { val lockTaskPolicyList = mutableListOf( LOCK_TASK_FEATURE_NONE, LOCK_TASK_FEATURE_SYSTEM_INFO, LOCK_TASK_FEATURE_NOTIFICATIONS, LOCK_TASK_FEATURE_HOME, LOCK_TASK_FEATURE_OVERVIEW, LOCK_TASK_FEATURE_GLOBAL_ACTIONS, LOCK_TASK_FEATURE_KEYGUARD ) var sysInfo by remember { mutableStateOf(false) } var notifications by remember { mutableStateOf(false) } var home by remember { mutableStateOf(false) } var overview by remember { mutableStateOf(false) } var globalAction by remember { mutableStateOf(false) } var keyGuard by remember { mutableStateOf(false) } var blockAct by remember { mutableStateOf(false) } if(VERSION.SDK_INT >= 30) { lockTaskPolicyList.add(LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK) } var inited by remember { mutableStateOf(false) } var custom by remember { mutableStateOf(false) } val refreshFeature = { var calculate = dpm.getLockTaskFeatures(receiver) if(calculate!=0) { if(VERSION.SDK_INT >= 30 && calculate-lockTaskPolicyList[7] >= 0) { blockAct= true; calculate -= lockTaskPolicyList[7] } if(calculate-lockTaskPolicyList[6] >= 0) { keyGuard = true; calculate -= lockTaskPolicyList[6] } if(calculate-lockTaskPolicyList[5] >= 0) { globalAction= true; calculate -= lockTaskPolicyList[5] } if(calculate-lockTaskPolicyList[4] >= 0) { overview= true; calculate -= lockTaskPolicyList[4] } if(calculate-lockTaskPolicyList[3] >= 0) { home= true; calculate -= lockTaskPolicyList[3] } if(calculate-lockTaskPolicyList[2] >= 0) { notifications= true; calculate -= lockTaskPolicyList[2] } if(calculate-lockTaskPolicyList[1] >= 0) { sysInfo= true; calculate -= lockTaskPolicyList[1] } }else{ custom = false } } Spacer(Modifier.padding(vertical = 10.dp)) Text(text = stringResource(R.string.lock_task_feature), style = typography.headlineLarge) Spacer(Modifier.padding(vertical = 5.dp)) if(!inited) { refreshFeature(); custom=dpm.getLockTaskFeatures(receiver)!=0; inited= true } RadioButtonItem(stringResource(R.string.disable_all), { !custom }, { custom=false }) RadioButtonItem(stringResource(R.string.custom), { custom }, { custom= true }) AnimatedVisibility(custom) { Column { CheckBoxItem(stringResource(R.string.ltf_sys_info), { sysInfo }, { sysInfo = !sysInfo }) CheckBoxItem(stringResource(R.string.ltf_notifications), { notifications }, { notifications = !notifications }) CheckBoxItem(stringResource(R.string.ltf_home), { home }, { home = !home }) CheckBoxItem(stringResource(R.string.ltf_overview), { overview }, { overview = !overview }) CheckBoxItem(stringResource(R.string.ltf_global_actions), { globalAction}, {globalAction = !globalAction}) CheckBoxItem(stringResource(R.string.ltf_keyguard), { keyGuard }, { keyGuard = !keyGuard }) if(VERSION.SDK_INT >= 30) { CheckBoxItem(stringResource(R.string.ltf_block_activity_start_in_task), { blockAct }, { blockAct=!blockAct }) } } } Button( modifier = Modifier.fillMaxWidth(), onClick = { var result = lockTaskPolicyList[0] if(custom) { if(blockAct&&VERSION.SDK_INT >= 30) { result += lockTaskPolicyList[7] } if(keyGuard) { result += lockTaskPolicyList[6] } if(globalAction) { result += lockTaskPolicyList[5] } if(overview) { result += lockTaskPolicyList[4] } if(home) { result += lockTaskPolicyList[3] } if(notifications) { result += lockTaskPolicyList[2] } if(sysInfo) { result += lockTaskPolicyList[1] } } dpm.setLockTaskFeatures(receiver,result) refreshFeature() Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() } ) { Text(stringResource(R.string.apply)) } Spacer(Modifier.padding(vertical = 5.dp)) val whitelist = dpm.getLockTaskPackages(receiver).toMutableList() var listText by remember { mutableStateOf("") } var inputPkg by remember { mutableStateOf("") } val refreshWhitelist = { inputPkg = "" listText = "" listText = whitelist.toText() } LaunchedEffect(Unit) { refreshWhitelist() } Text(text = stringResource(R.string.whitelist_app), style = typography.titleLarge) SelectionContainer(modifier = Modifier.animateContentSize()) { Text(text = if(listText == "") stringResource(R.string.none) else listText) } OutlinedTextField( value = inputPkg, onValueChange = { inputPkg = it }, label = { Text(stringResource(R.string.package_name)) }, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), modifier = Modifier.focusable().fillMaxWidth().padding(vertical = 3.dp) ) Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { Button( onClick = { focusMgr.clearFocus() whitelist.add(inputPkg) dpm.setLockTaskPackages(receiver,whitelist.toTypedArray()) Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() refreshWhitelist() }, modifier = Modifier.fillMaxWidth(0.49F) ) { Text(stringResource(R.string.add)) } Button( onClick = { focusMgr.clearFocus() if(inputPkg in whitelist) { whitelist.remove(inputPkg) dpm.setLockTaskPackages(receiver,whitelist.toTypedArray()) Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() } else { Toast.makeText(context, R.string.not_exist, Toast.LENGTH_SHORT).show() } refreshWhitelist() }, modifier = Modifier.fillMaxWidth(0.96F) ) { Text(stringResource(R.string.remove)) } } Spacer(Modifier.padding(vertical = 30.dp)) } } @Composable private fun CaCert() { val context = LocalContext.current val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val receiver = ComponentName(context,Receiver::class.java) val uri by fileUriFlow.collectAsState() var exist by remember { mutableStateOf(false) } val uriPath = uri.path ?: "" var caCertByteArray by remember { mutableStateOf(byteArrayOf()) } LaunchedEffect(uri) { if(uri != Uri.parse("")) { uriToStream(context, uri) { val array = it.readBytes() caCertByteArray = if(array.size < 10000) { array }else{ byteArrayOf() } } exist = dpm.hasCaCertInstalled(receiver, caCertByteArray) } } Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { Spacer(Modifier.padding(vertical = 10.dp)) Text(text = stringResource(R.string.ca_cert), style = typography.headlineLarge) Spacer(Modifier.padding(vertical = 5.dp)) AnimatedVisibility(uriPath != "") { Text(text = uriPath) } Text( text = if(uriPath == "") { stringResource(R.string.please_select_ca_cert) } else { stringResource(R.string.cacert_installed, exist) }, modifier = Modifier.animateContentSize() ) Spacer(Modifier.padding(vertical = 5.dp)) Button( onClick = { val caCertIntent = Intent(Intent.ACTION_GET_CONTENT) caCertIntent.setType("*/*") caCertIntent.addCategory(Intent.CATEGORY_OPENABLE) getFile.launch(caCertIntent) }, modifier = Modifier.fillMaxWidth() ) { Text(stringResource(R.string.select_ca_cert)) } AnimatedVisibility(uriPath != "") { Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { Button( onClick = { val result = dpm.installCaCert(receiver, caCertByteArray) Toast.makeText(context, if(result) R.string.success else R.string.fail, Toast.LENGTH_SHORT).show() exist = dpm.hasCaCertInstalled(receiver, caCertByteArray) }, modifier = Modifier.fillMaxWidth(0.49F) ) { Text(stringResource(R.string.install)) } Button( onClick = { if(exist) { dpm.uninstallCaCert(receiver, caCertByteArray) exist = dpm.hasCaCertInstalled(receiver, caCertByteArray) Toast.makeText(context, if(exist) R.string.fail else R.string.success, Toast.LENGTH_SHORT).show() } else { Toast.makeText(context, R.string.not_exist, Toast.LENGTH_SHORT).show() } }, modifier = Modifier.fillMaxWidth(0.96F) ) { Text(stringResource(R.string.uninstall)) } } } Button( onClick = { dpm.uninstallAllUserCaCerts(receiver) Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() }, modifier = Modifier.fillMaxWidth() ) { Text(stringResource(R.string.uninstall_all_user_ca_cert)) } } } @SuppressLint("NewApi") @Composable private fun SecurityLogs() { val context = LocalContext.current val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val receiver = ComponentName(context, Receiver::class.java) Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { Spacer(Modifier.padding(vertical = 10.dp)) Text(text = stringResource(R.string.security_logs), style = typography.headlineLarge) Spacer(Modifier.padding(vertical = 5.dp)) Text(text = stringResource(R.string.developing)) SwitchItem(R.string.enable, "", null, { dpm.isSecurityLoggingEnabled(receiver) }, { dpm.setSecurityLoggingEnabled(receiver,it) }) Button( onClick = { val log = dpm.retrieveSecurityLogs(receiver) if(log!=null) { for(i in log) { Log.d("SecureLog",i.toString()) } Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() }else{ Log.d("SecureLog", context.getString(R.string.none)) Toast.makeText(context, R.string.no_logs, Toast.LENGTH_SHORT).show() } }, modifier = Modifier.fillMaxWidth() ) { Text(stringResource(R.string.security_logs)) } Button( onClick = { val log = dpm.retrievePreRebootSecurityLogs(receiver) if(log!=null) { for(i in log) { Log.d("SecureLog",i.toString()) } Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() }else{ Log.d("SecureLog", context.getString(R.string.none)) Toast.makeText(context, R.string.no_logs, Toast.LENGTH_SHORT).show() } }, modifier = Modifier.fillMaxWidth() ) { Text(stringResource(R.string.pre_reboot_security_logs)) } } } @Composable private fun WipeData() { val context = LocalContext.current val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val receiver = ComponentName(context,Receiver::class.java) val focusMgr = LocalFocusManager.current Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { var flag by remember { mutableIntStateOf(0) } var confirmed by remember { mutableStateOf(false) } var externalStorage by remember { mutableStateOf(false) } var protectionData by remember { mutableStateOf(false) } var euicc by remember { mutableStateOf(false) } var silent by remember { mutableStateOf(false) } var reason by remember { mutableStateOf("") } Spacer(Modifier.padding(vertical = 10.dp)) Text( text = stringResource(R.string.wipe_data), style = typography.headlineLarge, modifier = Modifier.padding(6.dp),color = colorScheme.error ) Spacer(Modifier.padding(vertical = 5.dp)) CheckBoxItem( stringResource(R.string.wipe_external_storage), { externalStorage }, { externalStorage = !externalStorage; confirmed = false } ) if(VERSION.SDK_INT >= 22 && isDeviceOwner(dpm)) { CheckBoxItem(stringResource(R.string.wipe_reset_protection_data), { protectionData }, { protectionData = !protectionData; confirmed = false} ) } if(VERSION.SDK_INT >= 28) { CheckBoxItem(stringResource(R.string.wipe_euicc), { euicc }, { euicc = !euicc; confirmed = false }) } if(VERSION.SDK_INT >= 29) { CheckBoxItem(stringResource(R.string.wipe_silently), { silent }, { silent = !silent; confirmed = false }) } AnimatedVisibility(!silent && VERSION.SDK_INT >= 28) { OutlinedTextField( value = reason, onValueChange = { reason = it }, label = { Text(stringResource(R.string.reason)) }, enabled = !confirmed, modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp) ) } Spacer(Modifier.padding(vertical = 5.dp)) Button( onClick = { focusMgr.clearFocus() flag = 0 if(externalStorage) { flag += WIPE_EXTERNAL_STORAGE } if(protectionData && VERSION.SDK_INT >= 22) { flag += WIPE_RESET_PROTECTION_DATA } if(euicc && VERSION.SDK_INT >= 28) { flag += WIPE_EUICC } if(reason == "") { silent = true } if(silent && VERSION.SDK_INT >= 29) { flag += WIPE_SILENTLY } confirmed = !confirmed }, colors = ButtonDefaults.buttonColors( containerColor = if(confirmed) colorScheme.primary else colorScheme.error , contentColor = if(confirmed) colorScheme.onPrimary else colorScheme.onError ), enabled = dpm.isAdminActive(receiver), modifier = Modifier.fillMaxWidth() ) { Text(text = stringResource(if(confirmed) R.string.cancel else R.string.confirm)) } Button( onClick = { if(VERSION.SDK_INT >= 28 && reason != "") { dpm.wipeData(flag, reason) }else{ dpm.wipeData(flag) } }, colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError), enabled = confirmed && (VERSION.SDK_INT < 34 || (VERSION.SDK_INT >= 34 && !userManager.isSystemUser)), modifier = Modifier.fillMaxWidth() ) { Text("WipeData") } if (VERSION.SDK_INT >= 34 && (isDeviceOwner(dpm) || (isProfileOwner(dpm) && dpm.isOrganizationOwnedDeviceWithManagedProfile))) { Button( onClick = { dpm.wipeDevice(flag) }, colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError), enabled = confirmed, modifier = Modifier.fillMaxWidth() ) { Text("WipeDevice") } } Spacer(Modifier.padding(vertical = 5.dp)) if(VERSION.SDK_INT >= 24 && isProfileOwner(dpm) && dpm.isManagedProfile(receiver)) { Information{ Text(text = stringResource(R.string.will_delete_work_profile)) } } if(VERSION.SDK_INT >= 34 && Binder.getCallingUid()/100000 == 0) { Information{ Text(text = stringResource(R.string.api34_or_above_wipedata_cannot_in_system_user)) } } Spacer(Modifier.padding(vertical = 30.dp)) } } @Composable private fun SysUpdatePolicy() { val context = LocalContext.current val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val receiver = ComponentName(context,Receiver::class.java) val focusMgr = LocalFocusManager.current Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { Spacer(Modifier.padding(vertical = 10.dp)) if(VERSION.SDK_INT >= 23) { Column { var selectedPolicy by remember { mutableStateOf(dpm.systemUpdatePolicy?.policyType) } Text(text = stringResource(R.string.system_update_policy), style = typography.headlineLarge) Spacer(Modifier.padding(vertical = 5.dp)) RadioButtonItem( stringResource(R.string.system_update_policy_automatic), { selectedPolicy == TYPE_INSTALL_AUTOMATIC }, { selectedPolicy = TYPE_INSTALL_AUTOMATIC } ) RadioButtonItem( stringResource(R.string.system_update_policy_install_windowed), { selectedPolicy == TYPE_INSTALL_WINDOWED }, { selectedPolicy = TYPE_INSTALL_WINDOWED } ) RadioButtonItem( stringResource(R.string.system_update_policy_postpone), { selectedPolicy == TYPE_POSTPONE}, { selectedPolicy = TYPE_POSTPONE } ) RadioButtonItem(stringResource(R.string.none), { selectedPolicy == null }, { selectedPolicy = null }) var windowedPolicyStart by remember { mutableStateOf("") } var windowedPolicyEnd by remember { mutableStateOf("") } if(selectedPolicy == 2) { Spacer(Modifier.padding(vertical = 3.dp)) OutlinedTextField( value = windowedPolicyStart, label = { Text(stringResource(R.string.start_time)) }, onValueChange = { windowedPolicyStart = it }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done), keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), modifier = Modifier.focusable().fillMaxWidth(0.5F) ) Spacer(Modifier.padding(horizontal = 3.dp)) OutlinedTextField( value = windowedPolicyEnd, onValueChange = {windowedPolicyEnd = it }, label = { Text(stringResource(R.string.end_time)) }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done), keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), modifier = Modifier.focusable().fillMaxWidth() ) Spacer(Modifier.padding(vertical = 3.dp)) Text(text = stringResource(R.string.minutes_in_one_day)) } Button( onClick = { val policy = when(selectedPolicy) { TYPE_INSTALL_AUTOMATIC-> SystemUpdatePolicy.createAutomaticInstallPolicy() TYPE_INSTALL_WINDOWED-> SystemUpdatePolicy.createWindowedInstallPolicy(windowedPolicyStart.toInt(), windowedPolicyEnd.toInt()) TYPE_POSTPONE-> SystemUpdatePolicy.createPostponeInstallPolicy() else -> null } dpm.setSystemUpdatePolicy(receiver,policy) Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() }, enabled = isDeviceOwner(dpm), modifier = Modifier.fillMaxWidth() ) { Text(stringResource(R.string.apply)) } } } if(VERSION.SDK_INT >= 26) { Spacer(Modifier.padding(vertical = 10.dp)) val sysUpdateInfo = dpm.getPendingSystemUpdate(receiver) Column { if(sysUpdateInfo != null) { Text(text = stringResource(R.string.update_received_time, Date(sysUpdateInfo.receivedTime))) val securityStateDesc = when(sysUpdateInfo.securityPatchState) { SystemUpdateInfo.SECURITY_PATCH_STATE_UNKNOWN -> stringResource(R.string.unknown) SystemUpdateInfo.SECURITY_PATCH_STATE_TRUE -> "true" else->"false" } Text(text = stringResource(R.string.is_security_patch, securityStateDesc)) }else{ Text(text = stringResource(R.string.no_system_update)) } } } Spacer(Modifier.padding(vertical = 30.dp)) } } @SuppressLint("NewApi") @Composable fun InstallSystemUpdate() { val context = LocalContext.current val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val receiver = ComponentName(context,Receiver::class.java) val callback = object: InstallSystemUpdateCallback() { override fun onInstallUpdateError(errorCode: Int, errorMessage: String) { super.onInstallUpdateError(errorCode, errorMessage) val errDetail = when(errorCode) { UPDATE_ERROR_BATTERY_LOW -> R.string.battery_low UPDATE_ERROR_UPDATE_FILE_INVALID -> R.string.update_file_invalid UPDATE_ERROR_INCORRECT_OS_VERSION -> R.string.incorrect_os_ver UPDATE_ERROR_FILE_NOT_FOUND -> R.string.file_not_exist else -> R.string.unknown } val errMsg = context.getString(R.string.install_system_update_failed) + context.getString(errDetail) Toast.makeText(context, errMsg, Toast.LENGTH_SHORT).show() } } val uri by fileUriFlow.collectAsState() Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { Spacer(Modifier.padding(vertical = 10.dp)) Text(text = stringResource(R.string.install_system_update), style = typography.headlineLarge) Spacer(Modifier.padding(vertical = 5.dp)) Button( onClick = { val intent = Intent(Intent.ACTION_GET_CONTENT) intent.setType("application/zip") intent.addCategory(Intent.CATEGORY_OPENABLE) getFile.launch(intent) }, modifier = Modifier.fillMaxWidth() ) { Text(stringResource(R.string.select_ota_package)) } AnimatedVisibility(uri != Uri.parse("")) { Button( onClick = { try { dpm.installSystemUpdate(receiver, uri, { it.run() }, callback) Toast.makeText(context, R.string.start_install_system_update, Toast.LENGTH_SHORT).show() }catch(e: Exception) { Toast.makeText( context, context.getString(R.string.install_system_update_failed) + e.cause.toString(), Toast.LENGTH_SHORT ).show() } }, modifier = Modifier.fillMaxWidth() ) { Text(stringResource(R.string.install_system_update)) } } Spacer(Modifier.padding(vertical = 10.dp)) Information { Text(stringResource(R.string.auto_reboot_after_install_succeed)) } } }