From 001d013b0c393dbcdf252a3929c753ea36e03881 Mon Sep 17 00:00:00 2001 From: BinTianqi Date: Wed, 26 Nov 2025 22:20:26 +0800 Subject: [PATCH] Fix bugs of application functions (#205, #206, #207) --- .../com/bintianqi/owndroid/MainActivity.kt | 3 ++ .../com/bintianqi/owndroid/MyViewModel.kt | 50 +++++++++++-------- .../main/java/com/bintianqi/owndroid/Utils.kt | 19 ++++++- .../bintianqi/owndroid/dpm/Applications.kt | 37 ++++++++++---- 4 files changed, 78 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt index 3ba2c7f..f7c81d2 100644 --- a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt +++ b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt @@ -255,6 +255,9 @@ class MainActivity : FragmentActivity() { val launcher = registerForActivityResult(ActivityResultContracts.RequestPermission()) {} launcher.launch(Manifest.permission.POST_NOTIFICATIONS) } + registerPackageRemovedReceiver(this) { + vm.onPackageRemoved(it) + } setContent { var appLockDialog by rememberSaveable { mutableStateOf(false) } val theme by vm.theme.collectAsStateWithLifecycle() diff --git a/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt b/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt index 6f02c1a..625d204 100644 --- a/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt +++ b/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt @@ -200,6 +200,11 @@ class MyViewModel(application: Application): AndroidViewModel(application) { } } } + fun onPackageRemoved(name: String) { + installedPackages.update { list -> + list.filter { it.name != name } + } + } fun getAppInfo(info: ApplicationInfo) = AppInfo(info.packageName, info.loadLabel(PM).toString(), info.loadIcon(PM), info.flags) fun getAppInfo(name: String): AppInfo { @@ -361,9 +366,6 @@ class MyViewModel(application: Application): AndroidViewModel(application) { context.unregisterReceiver(this) if (statusExtra == PackageInstaller.STATUS_SUCCESS) { onComplete(null) - installedPackages.update { pkg -> - pkg.filter { it.name != packageName } - } } else { onComplete(parsePackageInstallerMessage(context, intent)) } @@ -464,14 +466,18 @@ class MyViewModel(application: Application): AndroidViewModel(application) { DPM.isUninstallBlocked(DAR, name), if (VERSION.SDK_INT >= 30) name in DPM.getUserControlDisabledPackages(DAR) else false, if (VERSION.SDK_INT >= 28) name in DPM.getMeteredDataDisabledPackages(DAR) else false, - if (VERSION.SDK_INT >= 28) DPM.getKeepUninstalledPackages(DAR)?.contains(name) == true else false + if (VERSION.SDK_INT >= 28 && Privilege.status.value.device) + DPM.getKeepUninstalledPackages(DAR)?.contains(name) == true + else false ) } // Application details @RequiresApi(24) fun adSetPackageSuspended(name: String, status: Boolean) { - DPM.setPackagesSuspended(DAR, arrayOf(name), status) - appStatus.update { it.copy(suspend = DPM.isPackageSuspended(DAR, name)) } + try { + DPM.setPackagesSuspended(DAR, arrayOf(name), status) + appStatus.update { it.copy(suspend = DPM.isPackageSuspended(DAR, name)) } + } catch (_: Exception) {} } fun adSetPackageHidden(name: String, status: Boolean) { DPM.setApplicationHidden(DAR, name, status) @@ -522,21 +528,25 @@ class MyViewModel(application: Application): AndroidViewModel(application) { @RequiresApi(23) fun getAppRestrictions(name: String) { val rm = application.getSystemService(RestrictionsManager::class.java) - val bundle = DPM.getApplicationRestrictions(DAR, name) - appRestrictions.value = rm.getManifestRestrictions(name)?.mapNotNull { - transformRestrictionEntry(it) - }?.map { - if (bundle.containsKey(it.key)) { - when (it) { - is AppRestriction.BooleanItem -> it.value = bundle.getBoolean(it.key) - is AppRestriction.StringItem -> it.value = bundle.getString(it.key) - is AppRestriction.IntItem -> it.value = bundle.getInt(it.key) - is AppRestriction.ChoiceItem -> it.value = bundle.getString(it.key) - is AppRestriction.MultiSelectItem -> it.value = bundle.getStringArray(it.key) + try { + val bundle = DPM.getApplicationRestrictions(DAR, name) + appRestrictions.value = rm.getManifestRestrictions(name)?.mapNotNull { + transformRestrictionEntry(it) + }?.map { + if (bundle.containsKey(it.key)) { + when (it) { + is AppRestriction.BooleanItem -> it.value = bundle.getBoolean(it.key) + is AppRestriction.StringItem -> it.value = bundle.getString(it.key) + is AppRestriction.IntItem -> it.value = bundle.getInt(it.key) + is AppRestriction.ChoiceItem -> it.value = bundle.getString(it.key) + is AppRestriction.MultiSelectItem -> it.value = bundle.getStringArray(it.key) + } } - } - it - } ?: emptyList() + it + } ?: emptyList() + } catch (e: Exception) { + e.printStackTrace() + } } @RequiresApi(23) diff --git a/app/src/main/java/com/bintianqi/owndroid/Utils.kt b/app/src/main/java/com/bintianqi/owndroid/Utils.kt index 700658d..8dd3858 100644 --- a/app/src/main/java/com/bintianqi/owndroid/Utils.kt +++ b/app/src/main/java/com/bintianqi/owndroid/Utils.kt @@ -1,9 +1,12 @@ package com.bintianqi.owndroid +import android.content.BroadcastReceiver import android.content.ClipData import android.content.ClipboardManager import android.content.ComponentName import android.content.Context +import android.content.Intent +import android.content.IntentFilter import android.content.pm.PackageInfo import android.net.Uri import android.os.Build @@ -159,4 +162,18 @@ fun Modifier.clickableTextField(onClick: () -> Unit) = fun adaptiveInsets(): WindowInsets { val navbar = WindowInsets.navigationBars.only(WindowInsetsSides.Horizontal) return WindowInsets.ime.union(navbar).union(WindowInsets.displayCutout) -} \ No newline at end of file +} + +fun registerPackageRemovedReceiver( + ctx: Context, callback: (String) -> Unit +) { + val br = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + callback(intent.data!!.schemeSpecificPart) + } + } + val filter = IntentFilter() + filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED) + filter.addDataScheme("package") + ctx.registerReceiver(br, filter) +} 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 fad0691..c058038 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt @@ -14,7 +14,6 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box 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 @@ -63,6 +62,9 @@ import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.RadioButton import androidx.compose.material3.Scaffold +import androidx.compose.material3.SegmentedButton +import androidx.compose.material3.SegmentedButtonDefaults +import androidx.compose.material3.SingleChoiceSegmentedButtonRow import androidx.compose.material3.Surface import androidx.compose.material3.Switch import androidx.compose.material3.Text @@ -121,7 +123,6 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.serialization.Serializable import sh.calvin.reorderable.ReorderableItem import sh.calvin.reorderable.rememberReorderableLazyListState -import kotlin.collections.indexOf val String.isValidPackageName get() = Regex("""^(?:[a-zA-Z]\w*\.)+[a-zA-Z]\w*$""").matches(this) @@ -540,20 +541,23 @@ private fun UninstallAppDialog( confirmButton = { TextButton( { - uninstalling = true - onUninstall(packageName) { - uninstalling = false - if (it == null) onClose(true) else errorMessage = it + if (errorMessage == null) { + uninstalling = true + onUninstall(packageName) { + uninstalling = false + if (it == null) onClose(true) else errorMessage = it + } + } else { + onClose(false) } }, - enabled = !uninstalling, - colors = ButtonDefaults.textButtonColors(contentColor = colorScheme.error) + enabled = !uninstalling ) { Text(stringResource(R.string.confirm)) } }, dismissButton = { - TextButton({ + if (errorMessage == null) TextButton({ onClose(false) }, enabled = !uninstalling) { Text(stringResource(R.string.cancel)) } }, @@ -1254,7 +1258,20 @@ fun ManagedConfigurationDialog( ) } is AppRestriction.BooleanItem -> item { - Switch(inputState, { inputState = it }) + SingleChoiceSegmentedButtonRow(Modifier.fillMaxWidth()) { + SegmentedButton( + inputState, { inputState = true }, + SegmentedButtonDefaults.itemShape(0, 2) + ) { + Text("true") + } + SegmentedButton( + !inputState, { inputState = false }, + SegmentedButtonDefaults.itemShape(1, 2) + ) { + Text("false") + } + } } is AppRestriction.ChoiceItem -> itemsIndexed(restriction.entryValues) { index, value -> val label = restriction.entries.getOrNull(index)