diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0ca96a2..3d7b8ee 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -91,10 +91,6 @@
android:description="@string/app_name"
android:permission="android.permission.BIND_DEVICE_ADMIN">
-
-
diff --git a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt
index 576fecb..60fe69b 100644
--- a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt
+++ b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt
@@ -37,7 +37,6 @@ import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
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
@@ -188,7 +187,6 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
val receiver = context.getReceiver()
val sharedPref = context.getSharedPreferences("data", Context.MODE_PRIVATE)
val focusMgr = LocalFocusManager.current
- val dialogStatus = remember { mutableIntStateOf(0) }
val backToHome by backToHomeStateFlow.collectAsState()
val lifecycleOwner = LocalLifecycleOwner.current
LaunchedEffect(backToHome) {
@@ -227,7 +225,7 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
composable(route = "PermissionPolicy") { PermissionPolicy(navCtrl) }
composable(route = "MTEPolicy") { MTEPolicy(navCtrl) }
composable(route = "NearbyStreamingPolicy") { NearbyStreamingPolicy(navCtrl) }
- composable(route = "LockTaskMode") { LockTaskMode(navCtrl) }
+ composable(route = "LockTaskMode") { LockTaskMode(navCtrl, vm) }
composable(route = "CACert") { CACert(navCtrl) }
composable(route = "SecurityLogging") { SecurityLogging(navCtrl) }
composable(route = "DisableAccountManagement") { DisableAccountManagement(navCtrl) }
@@ -241,7 +239,7 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
composable(route = "MinWifiSecurityLevel") { WifiSecurityLevel(navCtrl) }
composable(route = "WifiSsidPolicy") { WifiSsidPolicy(navCtrl) }
composable(route = "PrivateDNS") { PrivateDNS(navCtrl) }
- composable(route = "AlwaysOnVpn") { AlwaysOnVPNPackage(navCtrl) }
+ composable(route = "AlwaysOnVpn") { AlwaysOnVPNPackage(navCtrl, vm) }
composable(route = "RecommendedGlobalProxy") { RecommendedGlobalProxy(navCtrl) }
composable(route = "NetworkLog") { NetworkLogging(navCtrl) }
composable(route = "WifiAuthKeypair") { WifiAuthKeypair(navCtrl) }
@@ -255,7 +253,7 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
composable(route = "IntentFilter") { IntentFilter(navCtrl) }
composable(route = "DeleteWorkProfile") { DeleteWorkProfile(navCtrl) }
- composable(route = "Applications") { ApplicationManage(navCtrl, dialogStatus) }
+ composable(route = "Applications") { ApplicationManage(navCtrl, vm) }
composable(route = "UserRestriction") { UserRestriction(navCtrl) }
composable(route = "UR-Internet") {
@@ -302,7 +300,7 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
composable(route = "ApiSettings") { ApiSettings(navCtrl) }
composable(route = "About") { About(navCtrl) }
- composable(route = "PackageSelector") { PackageSelector(navCtrl) }
+ composable(route = "PackageSelector") { PackageSelector(navCtrl, vm) }
composable(
route = "Authenticate",
diff --git a/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt b/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt
index b4af388..932dc54 100644
--- a/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt
+++ b/app/src/main/java/com/bintianqi/owndroid/MyViewModel.kt
@@ -10,6 +10,8 @@ import kotlinx.coroutines.launch
class MyViewModel: ViewModel() {
val theme = MutableStateFlow(ThemeSettings())
+ val installedPackages = mutableListOf()
+ val selectedPackage = MutableStateFlow("")
val shizukuBinder = MutableStateFlow(null)
var initialized = false
diff --git a/app/src/main/java/com/bintianqi/owndroid/NotificationUtils.kt b/app/src/main/java/com/bintianqi/owndroid/NotificationUtils.kt
new file mode 100644
index 0000000..13a2033
--- /dev/null
+++ b/app/src/main/java/com/bintianqi/owndroid/NotificationUtils.kt
@@ -0,0 +1,29 @@
+package com.bintianqi.owndroid
+
+import android.Manifest
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.Build
+
+/**
+ * ### Notification channels
+ * - LockTaskMode
+ *
+ * ### Notification IDs
+ * - 1: Stop lock task mode
+ */
+object NotificationUtils {
+ fun checkPermission(context: Context): Boolean {
+ return if(Build.VERSION.SDK_INT >= 33)
+ context.checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED
+ else false
+ }
+ fun registerChannels(context: Context) {
+ if(Build.VERSION.SDK_INT < 26) return
+ val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ val channel = NotificationChannel("LockTaskMode", context.getString(R.string.lock_task_mode), NotificationManager.IMPORTANCE_HIGH)
+ nm.createNotificationChannel(channel)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/bintianqi/owndroid/PkgSelector.kt b/app/src/main/java/com/bintianqi/owndroid/PackageSelector.kt
similarity index 94%
rename from app/src/main/java/com/bintianqi/owndroid/PkgSelector.kt
rename to app/src/main/java/com/bintianqi/owndroid/PackageSelector.kt
index 2f8e542..fef2530 100644
--- a/app/src/main/java/com/bintianqi/owndroid/PkgSelector.kt
+++ b/app/src/main/java/com/bintianqi/owndroid/PackageSelector.kt
@@ -54,24 +54,19 @@ import com.bintianqi.owndroid.ui.NavIcon
import com.google.accompanist.drawablepainter.rememberDrawablePainter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-private data class PkgInfo(
+data class PackageInfo(
val pkgName: String,
val label: String,
val icon: Drawable,
val system: Boolean
)
-private val pkgs = mutableListOf()
-
-val selectedPackage = MutableStateFlow("")
-
@OptIn(ExperimentalMaterial3Api::class)
@Composable
-fun PackageSelector(navCtrl: NavHostController) {
+fun PackageSelector(navCtrl: NavHostController, vm: MyViewModel) {
val context = LocalContext.current
val pm = context.packageManager
val apps = pm.getInstalledApplications(0)
@@ -88,9 +83,9 @@ fun PackageSelector(navCtrl: NavHostController) {
show = false
progress = 0
hideProgress = false
- pkgs.clear()
+ vm.installedPackages.clear()
for(pkg in apps) {
- pkgs += PkgInfo(
+ vm.installedPackages += PackageInfo(
pkg.packageName, pkg.loadLabel(pm).toString(), pkg.loadIcon(pm),
(pkg.flags and ApplicationInfo.FLAG_SYSTEM) != 0
)
@@ -181,14 +176,14 @@ fun PackageSelector(navCtrl: NavHostController) {
}
}
if(show) {
- items(pkgs) {
+ items(vm.installedPackages) {
if(system == it.system) {
if(search != "") {
if(it.pkgName.contains(search, ignoreCase = true) || it.label.contains(search, ignoreCase = true)) {
- PackageItem(it, navCtrl)
+ PackageItem(it, navCtrl, vm)
}
} else {
- PackageItem(it, navCtrl)
+ PackageItem(it, navCtrl, vm)
}
}
}
@@ -201,13 +196,13 @@ fun PackageSelector(navCtrl: NavHostController) {
}
}
LaunchedEffect(Unit) {
- if(pkgs.size == 0) { getPkgList() }
+ if(vm.installedPackages.isEmpty()) { getPkgList() }
}
}
}
@Composable
-private fun PackageItem(pkg: PkgInfo, navCtrl: NavHostController) {
+private fun PackageItem(pkg: PackageInfo, navCtrl: NavHostController, vm: MyViewModel) {
val focusMgr = LocalFocusManager.current
Row(
verticalAlignment = Alignment.CenterVertically,
@@ -215,7 +210,7 @@ private fun PackageItem(pkg: PkgInfo, navCtrl: NavHostController) {
.fillMaxWidth()
.clickable{
focusMgr.clearFocus()
- selectedPackage.value = pkg.pkgName
+ vm.selectedPackage.value = pkg.pkgName
navCtrl.navigateUp()
}
.padding(horizontal = 8.dp, vertical = 10.dp)
diff --git a/app/src/main/java/com/bintianqi/owndroid/Receiver.kt b/app/src/main/java/com/bintianqi/owndroid/Receiver.kt
index 5823f21..bb9cb1f 100644
--- a/app/src/main/java/com/bintianqi/owndroid/Receiver.kt
+++ b/app/src/main/java/com/bintianqi/owndroid/Receiver.kt
@@ -1,9 +1,10 @@
package com.bintianqi.owndroid
-import android.annotation.SuppressLint
import android.app.NotificationManager
+import android.app.PendingIntent
import android.app.admin.DeviceAdminReceiver
import android.content.BroadcastReceiver
+import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInstaller.EXTRA_STATUS
@@ -21,8 +22,7 @@ import android.os.Build.VERSION
import android.os.PersistableBundle
import android.util.Log
import android.widget.Toast
-import com.bintianqi.owndroid.dpm.getDPM
-import com.bintianqi.owndroid.dpm.getReceiver
+import androidx.core.app.NotificationCompat
import com.bintianqi.owndroid.dpm.handleNetworkLogs
import com.bintianqi.owndroid.dpm.handleSecurityLogs
import com.bintianqi.owndroid.dpm.isDeviceAdmin
@@ -35,6 +35,17 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
class Receiver : DeviceAdminReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ super.onReceive(context, intent)
+ if(VERSION.SDK_INT >= 26 && intent.action == "com.bintianqi.owndroid.action.STOP_LOCK_TASK_MODE") {
+ val dpm = getManager(context)
+ val receiver = ComponentName(context, this::class.java)
+ val packages = dpm.getLockTaskPackages(receiver)
+ dpm.setLockTaskPackages(receiver, arrayOf())
+ dpm.setLockTaskPackages(receiver, packages)
+ }
+ }
+
override fun onEnabled(context: Context, intent: Intent) {
super.onEnabled(context, intent)
context.toggleInstallAppActivity()
@@ -78,6 +89,26 @@ class Receiver : DeviceAdminReceiver() {
sp.edit().putBoolean("dhizuku", false).apply()
context.toggleInstallAppActivity()
}
+
+ override fun onLockTaskModeEntering(context: Context, intent: Intent, pkg: String) {
+ super.onLockTaskModeEntering(context, intent, pkg)
+ NotificationUtils.registerChannels(context)
+ val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ val intent = Intent(context, this::class.java).apply { action = "com.bintianqi.owndroid.action.STOP_LOCK_TASK_MODE" }
+ val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
+ val builder = NotificationCompat.Builder(context, "LockTaskMode")
+ .setContentTitle(context.getText(R.string.lock_task_mode))
+ .setSmallIcon(R.drawable.lock_fill0)
+ .addAction(NotificationCompat.Action.Builder(null, context.getString(R.string.stop), pendingIntent).build())
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
+ nm.notify(1, builder.build())
+ }
+
+ override fun onLockTaskModeExiting(context: Context, intent: Intent) {
+ super.onLockTaskModeExiting(context, intent)
+ val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ nm.cancel(1)
+ }
}
val installAppDone = MutableStateFlow(false)
@@ -105,16 +136,3 @@ class PackageInstallerReceiver: BroadcastReceiver() {
}
}
}
-
-class StopLockTaskModeReceiver: BroadcastReceiver() {
- @SuppressLint("NewApi")
- override fun onReceive(context: Context, intent: Intent) {
- val dpm = context.getDPM()
- val receiver = context.getReceiver()
- val packages = dpm.getLockTaskPackages(receiver)
- dpm.setLockTaskPackages(receiver, arrayOf())
- dpm.setLockTaskPackages(receiver, packages)
- val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
- nm.cancel(1)
- }
-}
diff --git a/app/src/main/java/com/bintianqi/owndroid/Utils.kt b/app/src/main/java/com/bintianqi/owndroid/Utils.kt
index 879df73..efdd902 100644
--- a/app/src/main/java/com/bintianqi/owndroid/Utils.kt
+++ b/app/src/main/java/com/bintianqi/owndroid/Utils.kt
@@ -1,15 +1,12 @@
package com.bintianqi.owndroid
-import android.Manifest
import android.app.admin.DevicePolicyManager
import android.content.ClipData
import android.content.ClipboardManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
-import android.content.pm.PackageManager
import android.net.Uri
-import android.os.Build.VERSION
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher
@@ -65,7 +62,6 @@ fun writeClipBoard(context: Context, string: String):Boolean{
return true
}
-lateinit var requestPermission: ActivityResultLauncher
lateinit var exportFile: ActivityResultLauncher
var exportFilePath: String? = null
var isExportingSecurityOrNetworkLogs = false
@@ -83,7 +79,6 @@ fun registerActivityResult(context: ComponentActivity){
backToHomeStateFlow.value = true
}
}
- requestPermission = context.registerForActivityResult(ActivityResultContracts.RequestPermission()) { permissionGranted.value = it }
exportFile = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
val intentData = result.data ?: return@registerForActivityResult
val uriData = intentData.data ?: return@registerForActivityResult
@@ -103,21 +98,6 @@ fun registerActivityResult(context: ComponentActivity){
}
}
-val permissionGranted = MutableStateFlow(null)
-
-suspend fun prepareForNotification(context: Context, action: ()->Unit) {
- if(VERSION.SDK_INT >= 33) {
- if(context.checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) {
- action()
- } else {
- requestPermission.launch(Manifest.permission.POST_NOTIFICATIONS)
- permissionGranted.collect { if(it == true) action() }
- }
- } else {
- action()
- }
-}
-
fun formatFileSize(bytes: Long): String {
val kb = 1024
val mb = kb * 1024
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 3ebb4c7..a395680 100644
--- a/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt
+++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Applications.kt
@@ -50,7 +50,6 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.MutableIntState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
@@ -74,37 +73,37 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat.startActivity
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.bintianqi.owndroid.InstallAppActivity
+import com.bintianqi.owndroid.MyViewModel
import com.bintianqi.owndroid.PackageInstallerReceiver
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.fileUriFlow
import com.bintianqi.owndroid.getFile
-import com.bintianqi.owndroid.selectedPackage
import com.bintianqi.owndroid.ui.Animations
+import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.InfoCard
-import com.bintianqi.owndroid.ui.Information
import com.bintianqi.owndroid.ui.ListItem
import com.bintianqi.owndroid.ui.NavIcon
import com.bintianqi.owndroid.ui.RadioButtonItem
-import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.SwitchItem
import java.util.concurrent.Executors
@OptIn(ExperimentalMaterial3Api::class)
@Composable
-fun ApplicationManage(navCtrl:NavHostController, dialogStatus: MutableIntState) {
+fun ApplicationManage(navCtrl:NavHostController, vm: MyViewModel) {
val focusMgr = LocalFocusManager.current
val localNavCtrl = rememberNavController()
var pkgName by rememberSaveable { mutableStateOf("") }
- val updatePackage by selectedPackage.collectAsState()
+ val updatePackage by vm.selectedPackage.collectAsStateWithLifecycle()
LaunchedEffect(updatePackage) {
if(updatePackage != "") {
pkgName = updatePackage
- selectedPackage.value = ""
+ vm.selectedPackage.value = ""
}
}
Scaffold(
@@ -145,9 +144,7 @@ fun ApplicationManage(navCtrl:NavHostController, dialogStatus: MutableIntState)
popEnterTransition = Animations.navHostPopEnterTransition,
popExitTransition = Animations.navHostPopExitTransition
) {
- composable(route = "Home") {
- Home(localNavCtrl, pkgName, dialogStatus)
- }
+ composable(route = "Home") { Home(localNavCtrl, pkgName) }
composable(route = "UserControlDisabled") { UserCtrlDisabledPkg(pkgName) }
composable(route = "PermissionManage") { PermissionManage(pkgName) }
composable(route = "CrossProfilePackage") { CrossProfilePkg(pkgName) }
@@ -160,23 +157,11 @@ fun ApplicationManage(navCtrl:NavHostController, dialogStatus: MutableIntState)
composable(route = "UninstallApp") { UninstallApp(pkgName) }
}
}
- when(dialogStatus.intValue) {
- 0 -> {}
- 1 -> EnableSystemAppDialog(dialogStatus, pkgName)
- 2 -> ClearAppDataDialog(dialogStatus, pkgName)
- 3 -> DefaultDialerAppDialog(dialogStatus, pkgName)
- }
- LaunchedEffect(dialogStatus.intValue) {
- focusMgr.clearFocus()
- }
}
@Composable
-private fun Home(
- navCtrl:NavHostController,
- pkgName: String,
- dialogStatus: MutableIntState
-) {
+private fun Home(navCtrl:NavHostController, pkgName: String) {
+ var dialogStatus by remember { mutableIntStateOf(0) }
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
@@ -190,7 +175,6 @@ private fun Home(
hide = dpm.isApplicationHidden(receiver, pkgName)
var blockUninstall by remember { mutableStateOf(false) }
blockUninstall = dpm.isUninstallBlocked(receiver,pkgName)
- var appControlDialog by remember { mutableStateOf(false) }
var appControlAction by remember { mutableIntStateOf(0) }
val focusMgr = LocalFocusManager.current
val appControl: (Boolean) -> Unit = {
@@ -226,20 +210,20 @@ private fun Home(
title = R.string.suspend, desc = "", icon = R.drawable.block_fill0,
state = suspend,
onCheckedChange = { appControlAction = 1; appControl(it) },
- onClickBlank = { appControlAction = 1; appControlDialog = true }
+ onClickBlank = { appControlAction = 1; dialogStatus = 4 }
)
}
SwitchItem(
title = R.string.hide, desc = stringResource(R.string.isapphidden_desc), icon = R.drawable.visibility_off_fill0,
state = hide,
onCheckedChange = { appControlAction = 2; appControl(it) },
- onClickBlank = { appControlAction = 2; appControlDialog = true }
+ onClickBlank = { appControlAction = 2; dialogStatus = 4 }
)
SwitchItem(
title = R.string.block_uninstall, desc = "", icon = R.drawable.delete_forever_fill0,
state = blockUninstall,
onCheckedChange = { appControlAction = 3; appControl(it) },
- onClickBlank = { appControlAction = 3; appControlDialog = true }
+ onClickBlank = { appControlAction = 3; dialogStatus = 4 }
)
if((VERSION.SDK_INT >= 33 && profileOwner) || (VERSION.SDK_INT >= 30 && deviceOwner)) {
FunctionItem(R.string.ucd, "", R.drawable.do_not_touch_fill0) { navCtrl.navigate("UserControlDisabled") }
@@ -259,32 +243,124 @@ private fun Home(
FunctionItem(R.string.permitted_accessibility_services, "", R.drawable.settings_accessibility_fill0) { navCtrl.navigate("Accessibility") }
FunctionItem(R.string.permitted_ime, "", R.drawable.keyboard_fill0) { navCtrl.navigate("IME") }
FunctionItem(R.string.enable_system_app, "", R.drawable.enable_fill0) {
- if(pkgName != "") dialogStatus.intValue = 1
+ if(pkgName != "") dialogStatus = 1
}
if(VERSION.SDK_INT >= 28 && deviceOwner) {
FunctionItem(R.string.keep_uninstalled_packages, "", R.drawable.delete_fill0) { navCtrl.navigate("KeepUninstalled") }
}
if(VERSION.SDK_INT >= 28) {
FunctionItem(R.string.clear_app_storage, "", R.drawable.mop_fill0) {
- if(pkgName != "") dialogStatus.intValue = 2
+ if(pkgName != "") dialogStatus = 2
}
}
FunctionItem(R.string.install_app, "", R.drawable.install_mobile_fill0) { navCtrl.navigate("InstallApp") }
FunctionItem(R.string.uninstall_app, "", R.drawable.delete_fill0) { navCtrl.navigate("UninstallApp") }
if(VERSION.SDK_INT >= 34 && (deviceOwner || dpm.isOrgProfile(receiver))) {
FunctionItem(R.string.set_default_dialer, "", R.drawable.call_fill0) {
- if(pkgName != "") dialogStatus.intValue = 3
+ if(pkgName != "") dialogStatus = 3
}
}
Spacer(Modifier.padding(vertical = 30.dp))
LaunchedEffect(Unit) { fileUriFlow.value = Uri.parse("") }
}
- if(appControlDialog) {
+ if(dialogStatus == 1) AlertDialog(
+ title = { Text(stringResource(R.string.enable_system_app)) },
+ text = {
+ Text(stringResource(R.string.enable_system_app_desc) + "\n" + pkgName)
+ },
+ onDismissRequest = { dialogStatus = 0 },
+ dismissButton = {
+ TextButton(onClick = { dialogStatus = 0 }) {
+ Text(stringResource(R.string.cancel))
+ }
+ },
+ confirmButton = {
+ TextButton(
+ onClick = {
+ try {
+ dpm.enableSystemApp(receiver, pkgName)
+ Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
+ } catch(_: IllegalArgumentException) {
+ Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
+ }
+ dialogStatus = 0
+ }
+ ) {
+ Text(stringResource(R.string.confirm))
+ }
+ },
+ modifier = Modifier.fillMaxWidth()
+ )
+ if(dialogStatus == 2 && VERSION.SDK_INT >= 28) AlertDialog(
+ title = { Text(text = stringResource(R.string.clear_app_storage)) },
+ text = {
+ Text(stringResource(R.string.app_storage_will_be_cleared) + "\n" + pkgName)
+ },
+ confirmButton = {
+ TextButton(
+ onClick = {
+ val executor = Executors.newCachedThreadPool()
+ val onClear = DevicePolicyManager.OnClearApplicationUserDataListener { pkg: String, succeed: Boolean ->
+ Looper.prepare()
+ val toastText =
+ if(pkg!="") { "$pkg\n" }else{ "" } +
+ context.getString(R.string.clear_data) +
+ context.getString(if(succeed) R.string.success else R.string.failed )
+ Toast.makeText(context, toastText, Toast.LENGTH_SHORT).show()
+ Looper.loop()
+ }
+ dpm.clearApplicationUserData(receiver, pkgName, executor, onClear)
+ dialogStatus = 0
+ },
+ colors = ButtonDefaults.textButtonColors(contentColor = colorScheme.error)
+ ) {
+ Text(text = stringResource(R.string.clear))
+ }
+ },
+ dismissButton = {
+ TextButton(
+ onClick = { dialogStatus = 0 }
+ ) {
+ Text(text = stringResource(R.string.cancel))
+ }
+ },
+ onDismissRequest = { dialogStatus = 0 },
+ modifier = Modifier.fillMaxWidth()
+ )
+ if(dialogStatus == 3 && VERSION.SDK_INT >= 34) AlertDialog(
+ title = { Text(stringResource(R.string.set_default_dialer)) },
+ text = {
+ Text(stringResource(R.string.app_will_be_default_dialer) + "\n" + pkgName)
+ },
+ onDismissRequest = { dialogStatus = 0 },
+ dismissButton = {
+ TextButton(onClick = { dialogStatus = 0 }) {
+ Text(stringResource(R.string.cancel))
+ }
+ },
+ confirmButton = {
+ TextButton(
+ onClick = {
+ try{
+ dpm.setDefaultDialerApplication(pkgName)
+ Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
+ } catch(_: IllegalArgumentException) {
+ Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
+ }
+ dialogStatus = 0
+ }
+ ) {
+ Text(stringResource(R.string.confirm))
+ }
+ },
+ modifier = Modifier.fillMaxWidth()
+ )
+ if(dialogStatus == 4) {
LaunchedEffect(Unit) {
focusMgr.clearFocus()
}
AlertDialog(
- onDismissRequest = { appControlDialog = false },
+ onDismissRequest = { dialogStatus = 0 },
title = {
Text(
text = stringResource(
@@ -316,7 +392,7 @@ private fun Home(
TextButton(
onClick = {
appControl(true)
- appControlDialog = false
+ dialogStatus = 0
}
) {
Text(text = stringResource(R.string.enable))
@@ -326,7 +402,7 @@ private fun Home(
TextButton(
onClick = {
appControl(false)
- appControlDialog = false
+ dialogStatus = 0
}
) {
Text(text = stringResource(R.string.disable))
@@ -334,6 +410,7 @@ private fun Home(
}
)
}
+ LaunchedEffect(dialogStatus) { focusMgr.clearFocus() }
}
@@ -697,9 +774,7 @@ private fun PermittedAccessibility(pkgName: String) {
}
}
}
- Information {
- Text(stringResource(R.string.system_accessibility_always_allowed))
- }
+ InfoCard(R.string.system_accessibility_always_allowed)
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@@ -755,9 +830,7 @@ private fun PermittedIME(pkgName: String) {
}
}
}
- Information {
- Text(stringResource(R.string.system_ime_always_allowed))
- }
+ InfoCard(R.string.system_ime_always_allowed)
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@@ -892,117 +965,3 @@ private fun InstallApp() {
}
}
}
-
-@SuppressLint("NewApi")
-@Composable
-private fun ClearAppDataDialog(status: MutableIntState, pkgName: String) {
- val context = LocalContext.current
- val dpm = context.getDPM()
- val receiver = context.getReceiver()
- AlertDialog(
- title = { Text(text = stringResource(R.string.clear_app_storage)) },
- text = {
- Text(stringResource(R.string.app_storage_will_be_cleared) + "\n" + pkgName)
- },
- confirmButton = {
- TextButton(
- onClick = {
- val executor = Executors.newCachedThreadPool()
- val onClear = DevicePolicyManager.OnClearApplicationUserDataListener { pkg: String, succeed: Boolean ->
- Looper.prepare()
- val toastText =
- if(pkg!="") { "$pkg\n" }else{ "" } +
- context.getString(R.string.clear_data) +
- context.getString(if(succeed) R.string.success else R.string.failed )
- Toast.makeText(context, toastText, Toast.LENGTH_SHORT).show()
- Looper.loop()
- }
- dpm.clearApplicationUserData(receiver, pkgName, executor, onClear)
- status.intValue = 0
- },
- colors = ButtonDefaults.textButtonColors(contentColor = colorScheme.error)
- ) {
- Text(text = stringResource(R.string.clear))
- }
- },
- dismissButton = {
- TextButton(
- onClick = { status.intValue = 0 }
- ) {
- Text(text = stringResource(R.string.cancel))
- }
- },
- onDismissRequest = { status.intValue = 0 },
- modifier = Modifier.fillMaxWidth()
- )
-}
-
-@SuppressLint("NewApi")
-@Composable
-private fun DefaultDialerAppDialog(status: MutableIntState, pkgName: String) {
- val context = LocalContext.current
- val dpm = context.getDPM()
- AlertDialog(
- title = { Text(stringResource(R.string.set_default_dialer)) },
- text = {
- Text(stringResource(R.string.app_will_be_default_dialer) + "\n" + pkgName)
- },
- onDismissRequest = { status.intValue = 0 },
- dismissButton = {
- TextButton(onClick = { status.intValue = 0 }) {
- Text(stringResource(R.string.cancel))
- }
- },
- confirmButton = {
- TextButton(
- onClick = {
- try{
- dpm.setDefaultDialerApplication(pkgName)
- Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
- }catch(_: IllegalArgumentException) {
- Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
- }
- status.intValue = 0
- }
- ) {
- Text(stringResource(R.string.confirm))
- }
- },
- modifier = Modifier.fillMaxWidth()
- )
-}
-
-@Composable
-private fun EnableSystemAppDialog(status: MutableIntState, pkgName: String) {
- val context = LocalContext.current
- val dpm = context.getDPM()
- val receiver = context.getReceiver()
- AlertDialog(
- title = { Text(stringResource(R.string.enable_system_app)) },
- text = {
- Text(stringResource(R.string.enable_system_app_desc) + "\n" + pkgName)
- },
- onDismissRequest = { status.intValue = 0 },
- dismissButton = {
- TextButton(onClick = { status.intValue = 0 }) {
- Text(stringResource(R.string.cancel))
- }
- },
- confirmButton = {
- TextButton(
- onClick = {
- try {
- dpm.enableSystemApp(receiver, pkgName)
- Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
- } catch(_: IllegalArgumentException) {
- Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
- }
- status.intValue = 0
- }
- ) {
- Text(stringResource(R.string.confirm))
- }
- },
- modifier = Modifier.fillMaxWidth()
- )
-}
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 b7c94f0..e87d024 100644
--- a/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt
+++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt
@@ -92,12 +92,12 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.navigation.NavHostController
+import com.bintianqi.owndroid.MyViewModel
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.exportFile
import com.bintianqi.owndroid.exportFilePath
import com.bintianqi.owndroid.formatFileSize
import com.bintianqi.owndroid.isExportingSecurityOrNetworkLogs
-import com.bintianqi.owndroid.selectedPackage
import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.InfoCard
@@ -400,7 +400,7 @@ fun PrivateDNS(navCtrl: NavHostController) {
@SuppressLint("NewApi")
@Composable
-fun AlwaysOnVPNPackage(navCtrl: NavHostController) {
+fun AlwaysOnVPNPackage(navCtrl: NavHostController, vm: MyViewModel) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
@@ -409,11 +409,11 @@ fun AlwaysOnVPNPackage(navCtrl: NavHostController) {
val focusMgr = LocalFocusManager.current
val refresh = { pkgName = dpm.getAlwaysOnVpnPackage(receiver) ?: "" }
LaunchedEffect(Unit) { refresh() }
- val updatePackage by selectedPackage.collectAsState()
+ val updatePackage by vm.selectedPackage.collectAsState()
LaunchedEffect(updatePackage) {
- if(selectedPackage.value != "") {
- pkgName = selectedPackage.value
- selectedPackage.value = ""
+ if(updatePackage != "") {
+ pkgName = updatePackage
+ vm.selectedPackage.value = ""
}
}
val setAlwaysOnVpn: (String?, Boolean)->Boolean = { vpnPkg: String?, lockdownEnabled: Boolean ->
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 4e486ec..1941b00 100644
--- a/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt
+++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt
@@ -75,7 +75,6 @@ import com.bintianqi.owndroid.ui.CardItem
import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.InfoCard
-import com.bintianqi.owndroid.ui.Information
import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.RadioButtonItem
import com.bintianqi.owndroid.yesOrNo
@@ -303,7 +302,7 @@ fun ResetPasswordToken(navCtrl: NavHostController) {
}
}
Spacer(Modifier.padding(vertical = 5.dp))
- Information{ Text(stringResource(R.string.activate_token_not_required_when_no_password)) }
+ InfoCard(R.string.activate_token_not_required_when_no_password)
}
}
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 ab9dbf0..e622287 100644
--- a/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt
+++ b/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt
@@ -3,9 +3,6 @@ package com.bintianqi.owndroid.dpm
import android.annotation.SuppressLint
import android.app.ActivityOptions
import android.app.AlertDialog
-import android.app.NotificationChannel
-import android.app.NotificationManager
-import android.app.PendingIntent
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
@@ -107,10 +104,11 @@ 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.core.app.NotificationCompat
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavHostController
+import com.bintianqi.owndroid.MyViewModel
+import com.bintianqi.owndroid.NotificationUtils
import com.bintianqi.owndroid.R
-import com.bintianqi.owndroid.StopLockTaskModeReceiver
import com.bintianqi.owndroid.exportFile
import com.bintianqi.owndroid.exportFilePath
import com.bintianqi.owndroid.fileUriFlow
@@ -118,13 +116,10 @@ import com.bintianqi.owndroid.formatFileSize
import com.bintianqi.owndroid.getFile
import com.bintianqi.owndroid.humanReadableDate
import com.bintianqi.owndroid.isExportingSecurityOrNetworkLogs
-import com.bintianqi.owndroid.prepareForNotification
-import com.bintianqi.owndroid.selectedPackage
import com.bintianqi.owndroid.toggle
import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.InfoCard
-import com.bintianqi.owndroid.ui.Information
import com.bintianqi.owndroid.ui.ListItem
import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.RadioButtonItem
@@ -522,9 +517,7 @@ fun ChangeTimeZone(navCtrl: NavHostController) {
Text(stringResource(R.string.apply))
}
Spacer(Modifier.padding(vertical = 10.dp))
- Information {
- Text(stringResource(R.string.disable_auto_time_zone_before_set))
- }
+ InfoCard(R.string.disable_auto_time_zone_before_set)
}
if(dialog) AlertDialog(
text = {
@@ -700,12 +693,11 @@ fun NearbyStreamingPolicy(navCtrl: NavHostController) {
@SuppressLint("NewApi")
@Composable
-fun LockTaskMode(navCtrl: NavHostController) {
+fun LockTaskMode(navCtrl: NavHostController, vm: MyViewModel) {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val focusMgr = LocalFocusManager.current
- val coroutine = rememberCoroutineScope()
var appSelectorRequest by rememberSaveable { mutableIntStateOf(0) }
MyScaffold(R.string.lock_task_mode, 8.dp, navCtrl, false) {
val lockTaskFeatures = remember { mutableStateListOf() }
@@ -784,11 +776,11 @@ fun LockTaskMode(navCtrl: NavHostController) {
lockTaskFeatures.forEach { result += it }
}
try {
- dpm.setLockTaskFeatures(receiver,result)
+ dpm.setLockTaskFeatures(receiver, result)
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
} catch (e: IllegalArgumentException) {
AlertDialog.Builder(context)
- .setTitle("Error")
+ .setTitle(R.string.error)
.setMessage(e.message)
.setPositiveButton(R.string.confirm) { dialog, _ -> dialog.dismiss() }
.show()
@@ -863,11 +855,11 @@ fun LockTaskMode(navCtrl: NavHostController) {
var startLockTaskApp by rememberSaveable { mutableStateOf("") }
var startLockTaskActivity by rememberSaveable { mutableStateOf("") }
var specifyActivity by rememberSaveable { mutableStateOf(false) }
- val updatePackage by selectedPackage.collectAsState()
+ val updatePackage by vm.selectedPackage.collectAsStateWithLifecycle()
LaunchedEffect(updatePackage) {
if(updatePackage != "") {
if(appSelectorRequest == 1) inputLockTaskPkg = updatePackage else startLockTaskApp = updatePackage
- selectedPackage.value = ""
+ vm.selectedPackage.value = ""
}
}
Spacer(Modifier.padding(vertical = 10.dp))
@@ -906,7 +898,8 @@ fun LockTaskMode(navCtrl: NavHostController) {
Button(
modifier = Modifier.fillMaxWidth(),
onClick = {
- if(!dpm.getLockTaskPackages(receiver).contains(startLockTaskApp)) {
+ if(!NotificationUtils.checkPermission(context)) return@Button
+ if(!dpm.isLockTaskPermitted(startLockTaskApp)) {
Toast.makeText(context, R.string.app_not_allowed, Toast.LENGTH_SHORT).show()
return@Button
}
@@ -915,13 +908,7 @@ fun LockTaskMode(navCtrl: NavHostController) {
val launchIntent = if(specifyActivity) Intent().setComponent(ComponentName(startLockTaskApp, startLockTaskActivity))
else packageManager.getLaunchIntentForPackage(startLockTaskApp)
if (launchIntent != null) {
- coroutine.launch {
- prepareForNotification(context) {
- sendStopLockTaskNotification(context)
- context.startActivity(launchIntent, options.toBundle())
- Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
- }
- }
+ context.startActivity(launchIntent, options.toBundle())
} else {
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
}
@@ -1497,23 +1484,3 @@ fun InstallSystemUpdate(navCtrl: NavHostController) {
InfoCard(R.string.auto_reboot_after_install_succeed)
}
}
-
-@SuppressLint("NewApi")
-private fun sendStopLockTaskNotification(context: Context) {
- val nm = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
- if (VERSION.SDK_INT >= 26) {
- val channel = NotificationChannel("LockTaskMode", context.getString(R.string.lock_task_mode), NotificationManager.IMPORTANCE_HIGH).apply {
- description = "Notification channel for stop lock task mode"
- setShowBadge(false)
- }
- nm.createNotificationChannel(channel)
- }
- val intent = Intent(context, StopLockTaskModeReceiver::class.java)
- val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
- val builder = NotificationCompat.Builder(context, "LockTaskMode")
- .setContentTitle(context.getText(R.string.lock_task_mode))
- .setSmallIcon(R.drawable.lock_fill0)
- .addAction(NotificationCompat.Action.Builder(R.drawable.lock_fill0, context.getText(R.string.stop), pendingIntent).build())
- .setPriority(NotificationCompat.PRIORITY_HIGH)
- nm.notify(1, builder.build())
-}
diff --git a/app/src/main/java/com/bintianqi/owndroid/ui/Components.kt b/app/src/main/java/com/bintianqi/owndroid/ui/Components.kt
index c1e8582..0d8e8be 100644
--- a/app/src/main/java/com/bintianqi/owndroid/ui/Components.kt
+++ b/app/src/main/java/com/bintianqi/owndroid/ui/Components.kt
@@ -28,7 +28,6 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
-import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavHostController
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.writeClipBoard
@@ -79,21 +78,6 @@ fun NavIcon(operation: () -> Unit) {
)
}
-@Composable
-fun Information(content: @Composable ()->Unit) {
- Column(modifier = Modifier.fillMaxWidth().padding(start = 5.dp, top = 20.dp)) {
- Icon(
- painter = painterResource(R.drawable.info_fill0),
- contentDescription = "info",
- tint = colorScheme.onBackground.copy(alpha = 0.8F)
- )
- Spacer(Modifier.padding(vertical = 1.dp))
- Column(modifier = Modifier.padding(start = 2.dp)) {
- content()
- }
- }
-}
-
@Composable
fun RadioButtonItem(
@StringRes text: Int,
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 4608d0d..3aa2940 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -63,6 +63,7 @@
Alias
Unknown error
Permission denied
+ Error
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 80d2991..1e42e9c 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -64,6 +64,7 @@
Alias
Unknown error
Permission denied
+ Error
Etkinleştirmek İçin Tıklayın
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 74350c2..a4ab158 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -60,6 +60,7 @@
别名
未知错误
无权限
+ 错误
点击以激活
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index f69941e..83de418 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -64,6 +64,7 @@
Unknown error
Permission denied
API
+ Error
Click to activate