diff --git a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt index 3dc3486..3ba2c7f 100644 --- a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt +++ b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt @@ -602,7 +602,7 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) { } composable { ManagedConfigurationScreen( - it.toRoute(), vm.appRestrictions, vm::getAppRestrictions, vm::setAppRestrictions, + it.toRoute(), vm.appRestrictions, vm::setAppRestrictions, vm::clearAppRestrictions, ::navigateUp ) } diff --git a/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt b/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt index 83f0606..5bfdcca 100644 --- a/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt +++ b/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt @@ -518,12 +518,11 @@ class MyViewModel(application: Application): AndroidViewModel(application) { @RequiresApi(23) fun getAppRestrictions(name: String) { - val rm = application.getSystemService(Context.RESTRICTIONS_SERVICE) as RestrictionsManager + val rm = application.getSystemService(RestrictionsManager::class.java) val bundle = DPM.getApplicationRestrictions(DAR, name) - println(bundle.keySet()) - appRestrictions.value = rm.getManifestRestrictions(name).mapNotNull { + appRestrictions.value = rm.getManifestRestrictions(name)?.mapNotNull { transformRestrictionEntry(it) - }.map { + }?.map { if (bundle.containsKey(it.key)) { when (it) { is AppRestriction.BooleanItem -> it.value = bundle.getBoolean(it.key) @@ -534,17 +533,16 @@ class MyViewModel(application: Application): AndroidViewModel(application) { } } it - } + } ?: emptyList() } @RequiresApi(23) fun setAppRestrictions(name: String, item: AppRestriction) { viewModelScope.launch(Dispatchers.IO) { - appRestrictions.value = emptyList() - DPM.setApplicationRestrictions( - DAR, name, - transformAppRestriction(appRestrictions.value.filter { it.key != item.key }.plus(item)) + val bundle = transformAppRestriction( + appRestrictions.value.filter { it.key != item.key }.plus(item) ) + DPM.setApplicationRestrictions(DAR, name, bundle) getAppRestrictions(name) } } diff --git a/app/src/main/java/com/bintianqi/owndroid/PackageChooser.kt b/app/src/main/java/com/bintianqi/owndroid/PackageChooser.kt index ec7b1f7..271e234 100644 --- a/app/src/main/java/com/bintianqi/owndroid/PackageChooser.kt +++ b/app/src/main/java/com/bintianqi/owndroid/PackageChooser.kt @@ -22,6 +22,7 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.List +import androidx.compose.material.icons.outlined.Clear import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -122,15 +123,12 @@ fun AppChooserScreen( keyboardActions = KeyboardActions { focusMgr.clearFocus() }, placeholder = { Text(stringResource(R.string.search)) }, trailingIcon = { - Icon( - painter = painterResource(R.drawable.close_fill0), - contentDescription = null, - modifier = Modifier.clickable { - focusMgr.clearFocus() - query = "" - searchMode = false - } - ) + IconButton({ + query = "" + searchMode = false + }) { + Icon(Icons.Outlined.Clear, null) + } }, textStyle = typography.bodyLarge, modifier = Modifier.fillMaxWidth().focusRequester(fr) 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 3426cd7..201b66b 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt @@ -40,7 +40,9 @@ import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Clear import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.outlined.CheckCircle +import androidx.compose.material.icons.outlined.Clear import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material.icons.outlined.Search import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialogDefaults import androidx.compose.material3.Button @@ -79,6 +81,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext @@ -258,7 +262,11 @@ fun ApplicationDetailsScreen( var dialog by rememberSaveable { mutableIntStateOf(0) } // 1: clear storage, 2: uninstall val info = vm.getAppInfo(packageName) val status by vm.appStatus.collectAsStateWithLifecycle() - LaunchedEffect(Unit) { vm.getAppStatus(packageName) } + val appRestrictions by vm.appRestrictions.collectAsStateWithLifecycle() + LaunchedEffect(Unit) { + vm.getAppStatus(packageName) + vm.getAppRestrictions(packageName) + } MySmallTitleScaffold(R.string.place_holder, onNavigateUp, 0.dp) { Column(Modifier.align(Alignment.CenterHorizontally).padding(top = 16.dp), horizontalAlignment = Alignment.CenterHorizontally) { Image(rememberDrawablePainter(info.icon), null, Modifier.size(50.dp)) @@ -295,7 +303,7 @@ fun ApplicationDetailsScreen( state = status.keepUninstalled, onCheckedChange = { vm.adSetPackageKu(packageName, it) } ) - if (VERSION.SDK_INT >= 23) { + if (VERSION.SDK_INT >= 23 && appRestrictions.isNotEmpty()) { FunctionItem(R.string.managed_configuration, icon = R.drawable.description_fill0) { onNavigate(ManagedConfiguration(packageName)) } @@ -1003,35 +1011,74 @@ fun EditAppGroupScreen( @Composable fun ManagedConfigurationScreen( params: ManagedConfiguration, appRestrictions: StateFlow>, - getRestriction: (String) -> Unit, setRestriction: (String, AppRestriction) -> Unit, - clearRestriction: (String) -> Unit, navigateUp: () -> Unit + setRestriction: (String, AppRestriction) -> Unit, clearRestriction: (String) -> Unit, + navigateUp: () -> Unit ) { val restrictions by appRestrictions.collectAsStateWithLifecycle() - var dialog by remember { mutableIntStateOf(-1) } - var clearRestrictionDialog by remember { mutableStateOf(false) } - LaunchedEffect(Unit) { - getRestriction(params.packageName) + var searchMode by remember { mutableStateOf(false) } + var searchKeyword by remember { mutableStateOf("") } + val displayRestrictions = if (searchKeyword.isEmpty()) { + restrictions + } else { + restrictions.filter { + it.key.contentEquals(searchKeyword, true) || + it.title?.contains(searchKeyword, true) ?: true + } } + var dialog by remember { mutableStateOf(null) } + var clearRestrictionDialog by remember { mutableStateOf(false) } Scaffold( topBar = { TopAppBar( - { Text(stringResource(R.string.managed_configuration)) }, + { + if (searchMode) { + val fr = remember { FocusRequester() } + LaunchedEffect(Unit) { + fr.requestFocus() + } + OutlinedTextField( + searchKeyword, { searchKeyword = it }, + Modifier.fillMaxWidth().focusRequester(fr), + textStyle = typography.bodyLarge, + placeholder = { Text(stringResource(R.string.search)) }, + trailingIcon = { + IconButton({ + searchKeyword = "" + searchMode = false + }) { + Icon(Icons.Outlined.Clear, null) + } + }, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done) + ) + } else { + Text(stringResource(R.string.managed_configuration)) + } + }, navigationIcon = { NavIcon(navigateUp) }, actions = { - IconButton({ - clearRestrictionDialog = true - }) { - Icon(Icons.Outlined.Delete, null) + if (!searchMode) { + IconButton({ + searchMode = true + }) { + Icon(Icons.Outlined.Search, null) + } + IconButton({ + clearRestrictionDialog = true + }) { + Icon(Icons.Outlined.Delete, null) + } } } ) - } + }, + contentWindowInsets = adaptiveInsets() ) { paddingValues -> LazyColumn(Modifier.padding(paddingValues)) { - itemsIndexed(restrictions) { index, entry -> + items(displayRestrictions, { it.key }) { entry -> Row( Modifier.fillMaxWidth().clickable { - dialog = index + dialog = entry }.padding(HorizontalPadding, 8.dp), verticalAlignment = Alignment.CenterVertically ) { @@ -1055,7 +1102,13 @@ fun ManagedConfigurationScreen( is AppRestriction.StringItem -> entry.value?.take(30) is AppRestriction.BooleanItem -> entry.value?.toString() is AppRestriction.ChoiceItem -> entry.value - is AppRestriction.MultiSelectItem -> entry.value?.joinToString(limit = 30) + is AppRestriction.MultiSelectItem -> { + if (entry.value != null) { + entry.entryValues + .filter { entry.value?.contains(it) ?: false } + .joinToString(limit = 30) + } else null + } } Text( text ?: "null", Modifier.alpha(0.7F), @@ -1070,8 +1123,8 @@ fun ManagedConfigurationScreen( } } } - if (dialog != -1) Dialog({ - dialog = -1 + if (dialog != null) Dialog({ + dialog = null }) { Surface( color = AlertDialogDefaults.containerColor, @@ -1079,11 +1132,11 @@ fun ManagedConfigurationScreen( tonalElevation = AlertDialogDefaults.TonalElevation, ) { Column(Modifier.verticalScroll(rememberScrollState()).padding(12.dp)) { - ManagedConfigurationDialog(restrictions[dialog]) { + ManagedConfigurationDialog(dialog!!) { if (it != null) { setRestriction(params.packageName, it) } - dialog = -1 + dialog = null } } }