diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c9b8903..6d92713 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,8 +6,6 @@ on: - '**.md' tags-ignore: - '**' - branches-ignore: - - 'master' jobs: build: @@ -63,7 +61,7 @@ jobs: upload-telegram: name: Upload Builds - if: ${{ success() }} + if: ${{ success() && github.ref_name == 'dev' }} runs-on: ubuntu-latest needs: - build diff --git a/Readme.md b/Readme.md index 7d8e2db..38a8dc6 100644 --- a/Readme.md +++ b/Readme.md @@ -31,7 +31,7 @@ - 应用:禁止安装/卸载应用... - 用户:禁止添加/删除/切换用户... - 媒体:禁止调整亮度、禁止调整音量... - - 其他:禁止修改账号、禁止修改语言、禁止恢复出场设置、禁用调试功能... + - 其他:禁止修改账号、禁止修改语言、禁止恢复出厂设置、禁用调试功能... - 用户管理 - 用户信息 - 启动/切换/停止/删除用户 diff --git a/app/build.gradle.kts b/app/build.gradle.kts index fed4743..c77bc25 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -24,8 +24,8 @@ android { applicationId = "com.bintianqi.owndroid" minSdk = 21 targetSdk = 34 - versionCode = 34 - versionName = "6.2" + versionCode = 35 + versionName = "6.3" multiDexEnabled = false } 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 0817898..58be73a 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt @@ -1,6 +1,5 @@ package com.bintianqi.owndroid.dpm -import android.annotation.SuppressLint import android.app.PendingIntent import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT @@ -20,6 +19,7 @@ import android.provider.Settings import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.RequiresApi import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateContentSize import androidx.compose.foundation.background @@ -413,7 +413,7 @@ private fun Home(navCtrl:NavHostController, pkgName: String) { } -@SuppressLint("NewApi") +@RequiresApi(30) @Composable private fun UserCtrlDisabledPkg(pkgName:String) { val context = LocalContext.current @@ -456,7 +456,7 @@ private fun UserCtrlDisabledPkg(pkgName:String) { } } -@SuppressLint("NewApi") +@RequiresApi(23) @Composable private fun PermissionManage(pkgName: String) { val context = LocalContext.current @@ -556,7 +556,7 @@ private fun PermissionManage(pkgName: String) { } } -@SuppressLint("NewApi") +@RequiresApi(30) @Composable private fun CrossProfilePkg(pkgName: String) { val context = LocalContext.current @@ -636,7 +636,7 @@ private fun CrossProfileWidget(pkgName: String) { } } -@SuppressLint("NewApi") +@RequiresApi(34) @Composable private fun CredentialManagePolicy(pkgName: String) { val context = LocalContext.current @@ -822,7 +822,7 @@ private fun PermittedIME(pkgName: String) { } } -@SuppressLint("NewApi") +@RequiresApi(28) @Composable private fun KeepUninstalledApp(pkgName: String) { val context = LocalContext.current diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt index da69100..03c06b8 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt @@ -1,7 +1,6 @@ package com.bintianqi.owndroid.dpm import android.accounts.Account -import android.annotation.SuppressLint import android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE import android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE import android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ALLOW_OFFLINE @@ -21,6 +20,7 @@ import android.os.Build.VERSION import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.RequiresApi import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -59,8 +59,8 @@ import com.bintianqi.owndroid.showOperationResultToast import com.bintianqi.owndroid.ui.CardItem import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CopyTextButton -import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.FunctionItem +import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.SwitchItem import com.bintianqi.owndroid.yesOrNo @@ -160,7 +160,7 @@ fun CreateWorkProfile(navCtrl: NavHostController) { } } -@SuppressLint("NewApi") +@RequiresApi(30) @Composable fun OrgOwnedProfile(navCtrl: NavHostController) { val context = LocalContext.current @@ -180,7 +180,7 @@ fun OrgOwnedProfile(navCtrl: NavHostController) { } } -@SuppressLint("NewApi") +@RequiresApi(30) @Composable fun SuspendPersonalApp(navCtrl: NavHostController) { val context = LocalContext.current 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 a54d8ca..baabd51 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt @@ -1,7 +1,6 @@ package com.bintianqi.owndroid.dpm import android.Manifest -import android.annotation.SuppressLint import android.app.AlertDialog import android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_OFF import android.app.admin.DevicePolicyManager.PRIVATE_DNS_MODE_OPPORTUNISTIC @@ -51,6 +50,7 @@ import android.telephony.data.ApnSetting.PROTOCOL_UNSTRUCTURED import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.RequiresApi import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateContentSize import androidx.compose.foundation.background @@ -717,7 +717,7 @@ private fun AddNetwork(wifiConfig: WifiConfiguration? = null, navCtrl: NavHostCo } } -@SuppressLint("NewApi") +@RequiresApi(33) @Composable fun WifiSecurityLevel(navCtrl: NavHostController) { val context = LocalContext.current @@ -743,7 +743,7 @@ fun WifiSecurityLevel(navCtrl: NavHostController) { } } -@SuppressLint("NewApi") +@RequiresApi(33) @Composable fun WifiSsidPolicy(navCtrl: NavHostController) { val context = LocalContext.current @@ -817,7 +817,7 @@ fun WifiSsidPolicy(navCtrl: NavHostController) { } } -@SuppressLint("NewApi") +@RequiresApi(29) @Composable fun PrivateDNS(navCtrl: NavHostController) { val context = LocalContext.current @@ -889,7 +889,7 @@ fun PrivateDNS(navCtrl: NavHostController) { } } -@SuppressLint("NewApi") +@RequiresApi(24) @Composable fun AlwaysOnVPNPackage(navCtrl: NavHostController, vm: MyViewModel) { val context = LocalContext.current @@ -1056,7 +1056,7 @@ fun RecommendedGlobalProxy(navCtrl: NavHostController) { } } -@SuppressLint("NewApi") +@RequiresApi(26) @Composable fun NetworkLogging(navCtrl: NavHostController) { val context = LocalContext.current @@ -1112,7 +1112,7 @@ fun NetworkLogging(navCtrl: NavHostController) { } } -@SuppressLint("NewApi") +@RequiresApi(31) @Composable fun WifiAuthKeypair(navCtrl: NavHostController) { val context = LocalContext.current @@ -1154,7 +1154,7 @@ fun WifiAuthKeypair(navCtrl: NavHostController) { } } -@SuppressLint("NewApi") +@RequiresApi(33) @Composable fun PreferentialNetworkService(navCtrl: NavHostController) { val focusMgr = LocalFocusManager.current @@ -1306,7 +1306,7 @@ fun PreferentialNetworkService(navCtrl: NavHostController) { } } -@SuppressLint("NewApi") +@RequiresApi(28) @Composable fun OverrideAPN(navCtrl: NavHostController) { val context = LocalContext.current 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 1681d18..10af2b1 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt @@ -33,6 +33,7 @@ import android.content.Intent import android.os.Build.VERSION import android.os.UserManager import android.widget.Toast +import androidx.annotation.RequiresApi import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -232,7 +233,7 @@ fun PasswordInfo(navCtrl: NavHostController) { } } -@SuppressLint("NewApi") +@RequiresApi(26) @Composable fun ResetPasswordToken(navCtrl: NavHostController) { val context = LocalContext.current @@ -412,7 +413,7 @@ fun ResetPassword(navCtrl: NavHostController) { } } -@SuppressLint("NewApi") +@RequiresApi(31) @Composable fun PasswordComplexity(navCtrl: NavHostController) { val context = LocalContext.current diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt index 314c7ec..29ac872 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt @@ -12,6 +12,7 @@ import android.os.Build.VERSION import android.os.RemoteException import android.os.UserManager import android.widget.Toast +import androidx.annotation.RequiresApi import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.* import androidx.compose.foundation.text.KeyboardActions @@ -234,7 +235,7 @@ private fun toggleDhizukuMode(status: Boolean, context: Context) { } } -@SuppressLint("NewApi") +@RequiresApi(24) @Composable fun LockScreenInfo(navCtrl: NavHostController) { val context = LocalContext.current @@ -496,7 +497,7 @@ fun DeviceInfo(navCtrl: NavHostController) { ) } -@SuppressLint("NewApi") +@RequiresApi(24) @Composable fun SupportMessages(navCtrl: NavHostController) { val context = LocalContext.current @@ -574,7 +575,7 @@ fun SupportMessages(navCtrl: NavHostController) { } } -@SuppressLint("NewApi") +@RequiresApi(28) @Composable fun TransferOwnership(navCtrl: NavHostController) { val context = LocalContext.current 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 8957821..6440e9f 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt @@ -50,8 +50,10 @@ import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsPressedAsState import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope 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.height import androidx.compose.foundation.layout.padding @@ -60,9 +62,11 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.List import androidx.compose.material.icons.filled.Add @@ -77,14 +81,18 @@ import androidx.compose.material3.IconButton 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.SegmentedButton import androidx.compose.material3.SegmentedButtonDefaults import androidx.compose.material3.SingleChoiceSegmentedButtonRow import androidx.compose.material3.Slider import androidx.compose.material3.Switch +import androidx.compose.material3.Tab +import androidx.compose.material3.TabRow import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.TimePicker +import androidx.compose.material3.TopAppBar import androidx.compose.material3.rememberDatePickerState import androidx.compose.material3.rememberTimePickerState import androidx.compose.runtime.Composable @@ -123,6 +131,7 @@ import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.ListItem import com.bintianqi.owndroid.ui.MyScaffold +import com.bintianqi.owndroid.ui.NavIcon import com.bintianqi.owndroid.ui.RadioButtonItem import com.bintianqi.owndroid.ui.SwitchItem import com.bintianqi.owndroid.uriToStream @@ -133,9 +142,9 @@ import java.io.ByteArrayOutputStream import java.util.Date import java.util.TimeZone import java.util.concurrent.Executors +import kotlin.collections.addAll import kotlin.math.roundToLong -@SuppressLint("NewApi") @Composable fun SystemManage(navCtrl: NavHostController) { val context = LocalContext.current @@ -198,7 +207,7 @@ fun SystemManage(navCtrl: NavHostController) { FunctionItem(R.string.wipe_data, icon = R.drawable.device_reset_fill0) { navCtrl.navigate("WipeData") } } } - if(dialog != 0) AlertDialog( + if(dialog != 0 &&VERSION.SDK_INT >= 24) AlertDialog( onDismissRequest = { dialog = 0 }, title = { Text(stringResource(if(dialog == 1) R.string.reboot else R.string.bug_report)) }, text = { Text(stringResource(if(dialog == 1) R.string.info_reboot else R.string.confirm_bug_report)) }, @@ -448,7 +457,7 @@ fun HardwareMonitor(navCtrl: NavHostController) { } @OptIn(ExperimentalMaterial3Api::class) -@SuppressLint("NewApi") +@RequiresApi(28) @Composable fun ChangeTime(navCtrl: NavHostController) { val context = LocalContext.current @@ -558,7 +567,7 @@ fun ChangeTime(navCtrl: NavHostController) { ) } -@SuppressLint("NewApi") +@RequiresApi(28) @Composable fun ChangeTimeZone(navCtrl: NavHostController) { val context = LocalContext.current @@ -621,7 +630,7 @@ fun ChangeTimeZone(navCtrl: NavHostController) { ) } -@SuppressLint("NewApi") +@RequiresApi(23) @Composable fun PermissionPolicy(navCtrl: NavHostController) { val context = LocalContext.current @@ -646,7 +655,7 @@ fun PermissionPolicy(navCtrl: NavHostController) { } } -@SuppressLint("NewApi") +@RequiresApi(34) @Composable fun MTEPolicy(navCtrl: NavHostController) { val context = LocalContext.current @@ -674,7 +683,7 @@ fun MTEPolicy(navCtrl: NavHostController) { } } -@SuppressLint("NewApi") +@RequiresApi(31) @Composable fun NearbyStreamingPolicy(navCtrl: NavHostController) { val context = LocalContext.current @@ -739,206 +748,261 @@ fun NearbyStreamingPolicy(navCtrl: NavHostController) { } } -@SuppressLint("NewApi") +@OptIn(ExperimentalMaterial3Api::class) +@RequiresApi(28) @Composable fun LockTaskMode(navCtrl: NavHostController, vm: MyViewModel) { + val coroutine = rememberCoroutineScope() + val pagerState = rememberPagerState { 3 } + var tabIndex by remember { mutableIntStateOf(0) } + tabIndex = pagerState.targetPage + Scaffold( + topBar = { + TopAppBar( + title = { Text(stringResource(R.string.lock_task_mode)) }, + navigationIcon = { NavIcon { navCtrl.navigateUp() } } + ) + } + ) { paddingValues -> + Column( + modifier = Modifier.fillMaxSize().padding(paddingValues) + ) { + TabRow(tabIndex) { + Tab( + tabIndex == 0, onClick = { coroutine.launch { pagerState.animateScrollToPage(0) } }, + text = { Text(stringResource(R.string.start)) } + ) + Tab( + tabIndex == 1, onClick = { coroutine.launch { pagerState.animateScrollToPage(1) } }, + text = { Text(stringResource(R.string.applications)) } + ) + Tab( + tabIndex == 2, onClick = { coroutine.launch { pagerState.animateScrollToPage(2) } }, + text = { Text(stringResource(R.string.features)) } + ) + } + HorizontalPager(pagerState, verticalAlignment = Alignment.Top) { page -> + Column( + modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(start = 8.dp, end = 8.dp, bottom = 80.dp) + ) { + if(page == 0) StartLockTaskMode(navCtrl, vm) + else if(page == 1) LockTaskPackages(navCtrl, vm) + else LockTaskFeatures() + } + } + } + } +} + +@RequiresApi(28) +@Composable +private fun ColumnScope.StartLockTaskMode(navCtrl: NavHostController, vm: MyViewModel) { + val context = LocalContext.current + val dpm = context.getDPM() + val focusMgr = LocalFocusManager.current + var startLockTaskApp by rememberSaveable { mutableStateOf("") } + var startLockTaskActivity by rememberSaveable { mutableStateOf("") } + var specifyActivity by rememberSaveable { mutableStateOf(false) } + val updatePackage by vm.selectedPackage.collectAsStateWithLifecycle() + LaunchedEffect(updatePackage) { + if(updatePackage != "") { + startLockTaskApp = updatePackage + vm.selectedPackage.value = "" + } + } + Spacer(Modifier.padding(vertical = 5.dp)) + OutlinedTextField( + value = startLockTaskApp, + onValueChange = { startLockTaskApp = it }, + label = { Text(stringResource(R.string.package_name)) }, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), + trailingIcon = { + Icon(painter = painterResource(R.drawable.list_fill0), contentDescription = null, + modifier = Modifier + .clip(RoundedCornerShape(50)) + .clickable(onClick = { + focusMgr.clearFocus() + navCtrl.navigate("PackageSelector") + }) + .padding(3.dp)) + }, + modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp) + ) + CheckBoxItem(R.string.specify_activity, specifyActivity) { specifyActivity = it } + AnimatedVisibility(specifyActivity) { + OutlinedTextField( + value = startLockTaskActivity, + onValueChange = { startLockTaskActivity = it }, + label = { Text("Activity") }, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), + modifier = Modifier.fillMaxWidth().padding(bottom = 5.dp) + ) + } + Button( + modifier = Modifier.fillMaxWidth(), + onClick = { + if(!NotificationUtils.checkPermission(context)) return@Button + if(!dpm.isLockTaskPermitted(startLockTaskApp)) { + Toast.makeText(context, R.string.app_not_allowed, Toast.LENGTH_SHORT).show() + return@Button + } + val options = ActivityOptions.makeBasic().setLockTaskEnabled(true) + val packageManager = context.packageManager + val launchIntent = if(specifyActivity) Intent().setComponent(ComponentName(startLockTaskApp, startLockTaskActivity)) + else packageManager.getLaunchIntentForPackage(startLockTaskApp) + if (launchIntent != null) { + context.startActivity(launchIntent, options.toBundle()) + } else { + Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show() + } + } + ) { + Text(stringResource(R.string.start)) + } + InfoCard(R.string.info_start_lock_task_mode) +} + +@RequiresApi(26) +@Composable +private fun ColumnScope.LockTaskPackages(navCtrl: NavHostController, vm: MyViewModel) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() val focusMgr = LocalFocusManager.current - var appSelectorRequest by rememberSaveable { mutableIntStateOf(0) } - MyScaffold(R.string.lock_task_mode, 8.dp, navCtrl, false) { - var lockTaskFeatures by remember { mutableIntStateOf(0) } - var custom by rememberSaveable { mutableStateOf(false) } - fun refreshFeature() { - lockTaskFeatures = dpm.getLockTaskFeatures(receiver) - custom = lockTaskFeatures != 0 + val lockTaskPackages = remember { mutableStateListOf() } + var input by rememberSaveable { mutableStateOf("") } + val updatePackage by vm.selectedPackage.collectAsStateWithLifecycle() + LaunchedEffect(updatePackage) { + if(updatePackage != "") { + input = updatePackage + vm.selectedPackage.value = "" } - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.lock_task_feature), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) - LaunchedEffect(Unit) { refreshFeature() } - RadioButtonItem(R.string.disable_all, !custom) { custom = false } - RadioButtonItem(R.string.custom, custom) { custom = true } - AnimatedVisibility(custom) { - Column { - CheckBoxItem( - R.string.ltf_sys_info, - lockTaskFeatures and LOCK_TASK_FEATURE_SYSTEM_INFO != 0 - ) { lockTaskFeatures = lockTaskFeatures xor LOCK_TASK_FEATURE_SYSTEM_INFO } - CheckBoxItem( - R.string.ltf_notifications, - lockTaskFeatures and LOCK_TASK_FEATURE_NOTIFICATIONS != 0 - ) { lockTaskFeatures = lockTaskFeatures xor LOCK_TASK_FEATURE_NOTIFICATIONS } - CheckBoxItem( - R.string.ltf_home, - lockTaskFeatures and LOCK_TASK_FEATURE_HOME != 0 - ) { lockTaskFeatures = lockTaskFeatures xor LOCK_TASK_FEATURE_HOME } - CheckBoxItem( - R.string.ltf_overview, - lockTaskFeatures and LOCK_TASK_FEATURE_OVERVIEW != 0 - ) { lockTaskFeatures = lockTaskFeatures xor LOCK_TASK_FEATURE_OVERVIEW } - CheckBoxItem( - R.string.ltf_global_actions, - lockTaskFeatures and LOCK_TASK_FEATURE_GLOBAL_ACTIONS != 0 - ) { lockTaskFeatures = lockTaskFeatures xor LOCK_TASK_FEATURE_GLOBAL_ACTIONS } - CheckBoxItem( - R.string.ltf_keyguard, - lockTaskFeatures and LOCK_TASK_FEATURE_KEYGUARD != 0 - ) { lockTaskFeatures = lockTaskFeatures xor LOCK_TASK_FEATURE_KEYGUARD } - if(VERSION.SDK_INT >= 30) { - CheckBoxItem( - R.string.ltf_block_activity_start_in_task, - lockTaskFeatures and LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK != 0 - ) { lockTaskFeatures = lockTaskFeatures xor LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK } - } - } + } + LaunchedEffect(Unit) { lockTaskPackages.addAll(dpm.getLockTaskPackages(receiver)) } + Spacer(Modifier.padding(vertical = 5.dp)) + if(lockTaskPackages.isEmpty()) Text(text = stringResource(R.string.none)) + for(i in lockTaskPackages) { + ListItem(i) { lockTaskPackages -= i } + } + OutlinedTextField( + value = input, + onValueChange = { input = it }, + label = { Text(stringResource(R.string.package_name)) }, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), + trailingIcon = { + Icon(painter = painterResource(R.drawable.list_fill0), contentDescription = null, + modifier = Modifier + .clip(RoundedCornerShape(50)) + .clickable(onClick = { + focusMgr.clearFocus() + navCtrl.navigate("PackageSelector") + }) + .padding(3.dp)) + }, + modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp) + ) + Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { + Button( + onClick = { + lockTaskPackages.add(input) + input = "" + }, + modifier = Modifier.fillMaxWidth(0.49F) + ) { + Text(stringResource(R.string.add)) } Button( - modifier = Modifier.fillMaxWidth(), onClick = { - try { - dpm.setLockTaskFeatures(receiver, lockTaskFeatures) - context.showOperationResultToast(true) - } catch (e: IllegalArgumentException) { - AlertDialog.Builder(context) - .setTitle(R.string.error) - .setMessage(e.message) - .setPositiveButton(R.string.confirm) { dialog, _ -> dialog.dismiss() } - .show() - } - refreshFeature() - } + lockTaskPackages.remove(input) + input = "" + }, + modifier = Modifier.fillMaxWidth(0.96F) ) { - Text(stringResource(R.string.apply)) + Text(stringResource(R.string.remove)) } + } + Button( + modifier = Modifier.fillMaxWidth(), + onClick = { + dpm.setLockTaskPackages(receiver, lockTaskPackages.toTypedArray()) + context.showOperationResultToast(true) + } + ) { + Text(stringResource(R.string.apply)) + } + InfoCard(R.string.info_lock_task_packages) +} - val lockTaskPackages = remember { mutableStateListOf() } - var inputLockTaskPkg by rememberSaveable { mutableStateOf("") } - LaunchedEffect(Unit) { lockTaskPackages.addAll(dpm.getLockTaskPackages(receiver)) } - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.lock_task_packages), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) - Column(modifier = Modifier.animateContentSize()) { - if(lockTaskPackages.isEmpty()) Text(text = stringResource(R.string.none)) - for(i in lockTaskPackages) { - ListItem(i) { lockTaskPackages -= i } +@RequiresApi(28) +@Composable +private fun ColumnScope.LockTaskFeatures() { + val context = LocalContext.current + val dpm = context.getDPM() + val receiver = context.getReceiver() + var flags by remember { mutableIntStateOf(0) } + var custom by rememberSaveable { mutableStateOf(false) } + fun refresh() { + flags = dpm.getLockTaskFeatures(receiver) + custom = flags != 0 + } + LaunchedEffect(Unit) { refresh() } + Spacer(Modifier.padding(vertical = 5.dp)) + RadioButtonItem(R.string.disable_all, !custom) { custom = false } + RadioButtonItem(R.string.custom, custom) { custom = true } + AnimatedVisibility(custom) { + Column { + CheckBoxItem( + R.string.ltf_sys_info, + flags and LOCK_TASK_FEATURE_SYSTEM_INFO != 0 + ) { flags = flags xor LOCK_TASK_FEATURE_SYSTEM_INFO } + CheckBoxItem( + R.string.ltf_notifications, + flags and LOCK_TASK_FEATURE_NOTIFICATIONS != 0 + ) { flags = flags xor LOCK_TASK_FEATURE_NOTIFICATIONS } + CheckBoxItem( + R.string.ltf_home, + flags and LOCK_TASK_FEATURE_HOME != 0 + ) { flags = flags xor LOCK_TASK_FEATURE_HOME } + CheckBoxItem( + 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 } } } - OutlinedTextField( - value = inputLockTaskPkg, - onValueChange = { inputLockTaskPkg = it }, - label = { Text(stringResource(R.string.package_name)) }, - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), - keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), - trailingIcon = { - Icon(painter = painterResource(R.drawable.list_fill0), contentDescription = null, - modifier = Modifier - .clip(RoundedCornerShape(50)) - .clickable(onClick = { - focusMgr.clearFocus() - appSelectorRequest = 1 - navCtrl.navigate("PackageSelector") - }) - .padding(3.dp)) - }, - modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp) - ) - Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { - Button( - onClick = { - lockTaskPackages.add(inputLockTaskPkg) - inputLockTaskPkg = "" - }, - modifier = Modifier.fillMaxWidth(0.49F) - ) { - Text(stringResource(R.string.add)) - } - Button( - onClick = { - lockTaskPackages.remove(inputLockTaskPkg) - inputLockTaskPkg = "" - }, - modifier = Modifier.fillMaxWidth(0.96F) - ) { - Text(stringResource(R.string.remove)) - } - } - Button( - modifier = Modifier.fillMaxWidth(), - onClick = { - dpm.setLockTaskPackages(receiver, lockTaskPackages.toTypedArray()) + } + Button( + modifier = Modifier.fillMaxWidth(), + onClick = { + try { + dpm.setLockTaskFeatures(receiver, flags) context.showOperationResultToast(true) + } catch (e: IllegalArgumentException) { + AlertDialog.Builder(context) + .setTitle(R.string.error) + .setMessage(e.message) + .setPositiveButton(R.string.confirm) { dialog, _ -> dialog.dismiss() } + .show() } - ) { - Text(stringResource(R.string.apply)) + refresh() } - InfoCard(R.string.info_lock_task_packages) - var startLockTaskApp by rememberSaveable { mutableStateOf("") } - var startLockTaskActivity by rememberSaveable { mutableStateOf("") } - var specifyActivity by rememberSaveable { mutableStateOf(false) } - val updatePackage by vm.selectedPackage.collectAsStateWithLifecycle() - LaunchedEffect(updatePackage) { - if(updatePackage != "") { - if(appSelectorRequest == 1) inputLockTaskPkg = updatePackage else startLockTaskApp = updatePackage - vm.selectedPackage.value = "" - } - } - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.start_lock_task_mode), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) - OutlinedTextField( - value = startLockTaskApp, - onValueChange = { startLockTaskApp = it }, - label = { Text(stringResource(R.string.package_name)) }, - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), - keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), - trailingIcon = { - Icon(painter = painterResource(R.drawable.list_fill0), contentDescription = null, - modifier = Modifier - .clip(RoundedCornerShape(50)) - .clickable(onClick = { - focusMgr.clearFocus() - appSelectorRequest = 2 - navCtrl.navigate("PackageSelector") - }) - .padding(3.dp)) - }, - modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp) - ) - CheckBoxItem(R.string.specify_activity, specifyActivity) { specifyActivity = it } - AnimatedVisibility(specifyActivity) { - OutlinedTextField( - value = startLockTaskActivity, - onValueChange = { startLockTaskActivity = it }, - label = { Text("Activity") }, - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), - keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), - modifier = Modifier.fillMaxWidth().padding(bottom = 5.dp) - ) - } - Button( - modifier = Modifier.fillMaxWidth(), - onClick = { - if(!NotificationUtils.checkPermission(context)) return@Button - if(!dpm.isLockTaskPermitted(startLockTaskApp)) { - Toast.makeText(context, R.string.app_not_allowed, Toast.LENGTH_SHORT).show() - return@Button - } - val options = ActivityOptions.makeBasic().setLockTaskEnabled(true) - val packageManager = context.packageManager - val launchIntent = if(specifyActivity) Intent().setComponent(ComponentName(startLockTaskApp, startLockTaskActivity)) - else packageManager.getLaunchIntentForPackage(startLockTaskApp) - if (launchIntent != null) { - context.startActivity(launchIntent, options.toBundle()) - } else { - Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show() - } - } - ) { - Text(stringResource(R.string.start)) - } - InfoCard(R.string.info_start_lock_task_mode) + ) { + Text(stringResource(R.string.apply)) } } @@ -1016,7 +1080,7 @@ fun CACert(navCtrl: NavHostController) { } } -@SuppressLint("NewApi") +@RequiresApi(24) @Composable fun SecurityLogging(navCtrl: NavHostController) { val context = LocalContext.current @@ -1152,7 +1216,7 @@ fun DisableAccountManagement(navCtrl: NavHostController) { } } -@SuppressLint("NewApi") +@RequiresApi(30) @Composable fun FRPPolicy(navCtrl: NavHostController) { val context = LocalContext.current @@ -1244,7 +1308,6 @@ fun FRPPolicy(navCtrl: NavHostController) { } } -@SuppressLint("NewApi") @Composable fun WipeData(navCtrl: NavHostController) { val context = LocalContext.current @@ -1305,7 +1368,10 @@ fun WipeData(navCtrl: NavHostController) { }, text = { Text( - text = stringResource(if(userManager.isSystemUser) R.string.wipe_data_warning else R.string.info_wipe_data_in_managed_user), + text = stringResource( + if(VERSION.SDK_INT >= 23 && userManager.isSystemUser) R.string.wipe_data_warning + else R.string.info_wipe_data_in_managed_user + ), color = colorScheme.error ) }, @@ -1322,7 +1388,7 @@ fun WipeData(navCtrl: NavHostController) { TextButton( onClick = { if(silent && VERSION.SDK_INT >= 29) { flag = flag or WIPE_SILENTLY } - if(wipeDevice) { + if(wipeDevice && VERSION.SDK_INT >= 34) { dpm.wipeDevice(flag) } else { if(VERSION.SDK_INT >= 28 && reason != "") { diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/UserRestriction.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/UserRestriction.kt index 80b0036..a693c78 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/UserRestriction.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/UserRestriction.kt @@ -1,10 +1,10 @@ package com.bintianqi.owndroid.dpm -import android.annotation.SuppressLint import android.os.Build.VERSION import android.os.UserManager import android.widget.Toast import androidx.annotation.DrawableRes +import androidx.annotation.RequiresApi import androidx.annotation.StringRes import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Spacer @@ -48,7 +48,7 @@ fun UserRestriction(navCtrl:NavHostController) { } } -@SuppressLint("NewApi") +@RequiresApi(24) @Composable fun UserRestrictionItem(restriction: Restriction) { val context = LocalContext.current 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 7ce62c0..3de39c4 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Users.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Users.kt @@ -1,6 +1,5 @@ package com.bintianqi.owndroid.dpm -import android.annotation.SuppressLint import android.app.admin.DevicePolicyManager import android.content.Context import android.content.Intent @@ -15,6 +14,7 @@ import android.provider.MediaStore import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.RequiresApi import androidx.annotation.StringRes import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateContentSize @@ -299,7 +299,7 @@ fun UserOperation(navCtrl: NavHostController) { } } -@SuppressLint("NewApi") +@RequiresApi(24) @Composable fun CreateUser(navCtrl: NavHostController) { val context = LocalContext.current @@ -350,7 +350,7 @@ fun CreateUser(navCtrl: NavHostController) { } } -@SuppressLint("NewApi") +@RequiresApi(26) @Composable fun AffiliationID(navCtrl: NavHostController) { val context = LocalContext.current @@ -441,7 +441,7 @@ fun ChangeUsername(navCtrl: NavHostController) { } } -@SuppressLint("NewApi") +@RequiresApi(28) @Composable fun UserSessionMessage(navCtrl: NavHostController) { val context = LocalContext.current @@ -519,7 +519,7 @@ fun UserSessionMessage(navCtrl: NavHostController) { } } -@SuppressLint("NewApi") +@RequiresApi(23) @Composable fun ChangeUserIcon(navCtrl: NavHostController) { val context = LocalContext.current diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 5cde12b..1038d6f 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -65,6 +65,9 @@ Permission denied Error Status + Edit + Overview + Features @@ -170,10 +173,7 @@ Политика потоковой передачи уведомлений Nearby Только для одной управляемой учетной записи Режим закрепления задачи - Функция закрепления задачи - Закрепленные пакеты Указать Acitvity - Запустить режим закрепления задачи Приложение не разрешено Отключить все @@ -553,7 +553,6 @@ Отключить неотредактированные уведомления Отключить доверенные агенты Отключить отпечаток пальца - Отключить удаленный ввод Отключить распознавание лица Отключить сканер радужной оболочки Отключить биометрию diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 96bfcc3..8a9dca7 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -66,6 +66,9 @@ Permission denied Error Status + Edit + Overview + Features Etkinleştirmek İçin Tıklayın @@ -171,10 +174,7 @@ Yakındaki bildirim akış politikası Yalnızca aynı yönetilen hesap Lock task mode - Görev kilitleme özelliği - Lock task packages Specify Activity - Start lock task mode App is not allowed Hepsini devre dışı bırak @@ -549,7 +549,6 @@ Sansürsüz bildirimleri devre dışı bırak Güvenilir ajanları devre dışı bırak Parmak izini devre dışı bırak - Uzaktan girişleri devre dışı bırak Yüz tanımayı devre dışı bırak İris tarayıcısını devre dışı bırak Biyometrikleri devre dışı bırak diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index bce5a97..44e260b 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -62,6 +62,9 @@ 无权限 错误 状态 + 编辑 + 概览 + 功能 点击以激活 @@ -163,10 +166,7 @@ 附近通知传输 在足够安全时启用 锁定任务模式 - 锁定任务功能 - 锁定任务应用 指定Activity - 启动锁定任务模式 应用未被允许 禁用全部 允许状态栏信息 @@ -537,7 +537,6 @@ 禁用未经编辑的通知 禁用可信代理 禁用指纹解锁 - 禁止远程输入(弃用) 禁用人脸解锁 禁用虹膜解锁(?) 禁用生物识别 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f54c1a5..92a9c79 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -69,6 +69,7 @@ Status Edit Overview + Features Click to activate @@ -177,10 +178,7 @@ Nearby notification streaming policy Same managed account only Lock task mode - Lock task feature - Lock task packages Specify Activity - Start lock task mode App is not allowed Disable all @@ -556,7 +554,6 @@ Disable unredacted notification Disable trust agents Disable fingerprint - Disable remote input Disable face Disable iris Disable biometrics