Optimize start/stop lock task mode flow

Optimize Package selector
This commit is contained in:
BinTianqi
2024-12-21 20:07:32 +08:00
parent 873896ec10
commit 84c1dff9e6
16 changed files with 216 additions and 285 deletions

View File

@@ -91,10 +91,6 @@
android:description="@string/app_name" android:description="@string/app_name"
android:permission="android.permission.BIND_DEVICE_ADMIN"> android:permission="android.permission.BIND_DEVICE_ADMIN">
</receiver> </receiver>
<receiver
android:name=".StopLockTaskModeReceiver"
android:description="@string/app_name">
</receiver>
<receiver <receiver
android:name=".ApiReceiver" android:name=".ApiReceiver"
android:exported="true"> android:exported="true">

View File

@@ -37,7 +37,6 @@ import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
@@ -188,7 +187,6 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
val receiver = context.getReceiver() val receiver = context.getReceiver()
val sharedPref = context.getSharedPreferences("data", Context.MODE_PRIVATE) val sharedPref = context.getSharedPreferences("data", Context.MODE_PRIVATE)
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
val dialogStatus = remember { mutableIntStateOf(0) }
val backToHome by backToHomeStateFlow.collectAsState() val backToHome by backToHomeStateFlow.collectAsState()
val lifecycleOwner = LocalLifecycleOwner.current val lifecycleOwner = LocalLifecycleOwner.current
LaunchedEffect(backToHome) { LaunchedEffect(backToHome) {
@@ -227,7 +225,7 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
composable(route = "PermissionPolicy") { PermissionPolicy(navCtrl) } composable(route = "PermissionPolicy") { PermissionPolicy(navCtrl) }
composable(route = "MTEPolicy") { MTEPolicy(navCtrl) } composable(route = "MTEPolicy") { MTEPolicy(navCtrl) }
composable(route = "NearbyStreamingPolicy") { NearbyStreamingPolicy(navCtrl) } composable(route = "NearbyStreamingPolicy") { NearbyStreamingPolicy(navCtrl) }
composable(route = "LockTaskMode") { LockTaskMode(navCtrl) } composable(route = "LockTaskMode") { LockTaskMode(navCtrl, vm) }
composable(route = "CACert") { CACert(navCtrl) } composable(route = "CACert") { CACert(navCtrl) }
composable(route = "SecurityLogging") { SecurityLogging(navCtrl) } composable(route = "SecurityLogging") { SecurityLogging(navCtrl) }
composable(route = "DisableAccountManagement") { DisableAccountManagement(navCtrl) } composable(route = "DisableAccountManagement") { DisableAccountManagement(navCtrl) }
@@ -241,7 +239,7 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
composable(route = "MinWifiSecurityLevel") { WifiSecurityLevel(navCtrl) } composable(route = "MinWifiSecurityLevel") { WifiSecurityLevel(navCtrl) }
composable(route = "WifiSsidPolicy") { WifiSsidPolicy(navCtrl) } composable(route = "WifiSsidPolicy") { WifiSsidPolicy(navCtrl) }
composable(route = "PrivateDNS") { PrivateDNS(navCtrl) } composable(route = "PrivateDNS") { PrivateDNS(navCtrl) }
composable(route = "AlwaysOnVpn") { AlwaysOnVPNPackage(navCtrl) } composable(route = "AlwaysOnVpn") { AlwaysOnVPNPackage(navCtrl, vm) }
composable(route = "RecommendedGlobalProxy") { RecommendedGlobalProxy(navCtrl) } composable(route = "RecommendedGlobalProxy") { RecommendedGlobalProxy(navCtrl) }
composable(route = "NetworkLog") { NetworkLogging(navCtrl) } composable(route = "NetworkLog") { NetworkLogging(navCtrl) }
composable(route = "WifiAuthKeypair") { WifiAuthKeypair(navCtrl) } composable(route = "WifiAuthKeypair") { WifiAuthKeypair(navCtrl) }
@@ -255,7 +253,7 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
composable(route = "IntentFilter") { IntentFilter(navCtrl) } composable(route = "IntentFilter") { IntentFilter(navCtrl) }
composable(route = "DeleteWorkProfile") { DeleteWorkProfile(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 = "UserRestriction") { UserRestriction(navCtrl) }
composable(route = "UR-Internet") { composable(route = "UR-Internet") {
@@ -302,7 +300,7 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
composable(route = "ApiSettings") { ApiSettings(navCtrl) } composable(route = "ApiSettings") { ApiSettings(navCtrl) }
composable(route = "About") { About(navCtrl) } composable(route = "About") { About(navCtrl) }
composable(route = "PackageSelector") { PackageSelector(navCtrl) } composable(route = "PackageSelector") { PackageSelector(navCtrl, vm) }
composable( composable(
route = "Authenticate", route = "Authenticate",

View File

@@ -10,6 +10,8 @@ import kotlinx.coroutines.launch
class MyViewModel: ViewModel() { class MyViewModel: ViewModel() {
val theme = MutableStateFlow(ThemeSettings()) val theme = MutableStateFlow(ThemeSettings())
val installedPackages = mutableListOf<PackageInfo>()
val selectedPackage = MutableStateFlow("")
val shizukuBinder = MutableStateFlow<IBinder?>(null) val shizukuBinder = MutableStateFlow<IBinder?>(null)
var initialized = false var initialized = false

View File

@@ -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)
}
}

View File

@@ -54,24 +54,19 @@ import com.bintianqi.owndroid.ui.NavIcon
import com.google.accompanist.drawablepainter.rememberDrawablePainter import com.google.accompanist.drawablepainter.rememberDrawablePainter
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
private data class PkgInfo( data class PackageInfo(
val pkgName: String, val pkgName: String,
val label: String, val label: String,
val icon: Drawable, val icon: Drawable,
val system: Boolean val system: Boolean
) )
private val pkgs = mutableListOf<PkgInfo>()
val selectedPackage = MutableStateFlow("")
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun PackageSelector(navCtrl: NavHostController) { fun PackageSelector(navCtrl: NavHostController, vm: MyViewModel) {
val context = LocalContext.current val context = LocalContext.current
val pm = context.packageManager val pm = context.packageManager
val apps = pm.getInstalledApplications(0) val apps = pm.getInstalledApplications(0)
@@ -88,9 +83,9 @@ fun PackageSelector(navCtrl: NavHostController) {
show = false show = false
progress = 0 progress = 0
hideProgress = false hideProgress = false
pkgs.clear() vm.installedPackages.clear()
for(pkg in apps) { for(pkg in apps) {
pkgs += PkgInfo( vm.installedPackages += PackageInfo(
pkg.packageName, pkg.loadLabel(pm).toString(), pkg.loadIcon(pm), pkg.packageName, pkg.loadLabel(pm).toString(), pkg.loadIcon(pm),
(pkg.flags and ApplicationInfo.FLAG_SYSTEM) != 0 (pkg.flags and ApplicationInfo.FLAG_SYSTEM) != 0
) )
@@ -181,14 +176,14 @@ fun PackageSelector(navCtrl: NavHostController) {
} }
} }
if(show) { if(show) {
items(pkgs) { items(vm.installedPackages) {
if(system == it.system) { if(system == it.system) {
if(search != "") { if(search != "") {
if(it.pkgName.contains(search, ignoreCase = true) || it.label.contains(search, ignoreCase = true)) { if(it.pkgName.contains(search, ignoreCase = true) || it.label.contains(search, ignoreCase = true)) {
PackageItem(it, navCtrl) PackageItem(it, navCtrl, vm)
} }
} else { } else {
PackageItem(it, navCtrl) PackageItem(it, navCtrl, vm)
} }
} }
} }
@@ -201,13 +196,13 @@ fun PackageSelector(navCtrl: NavHostController) {
} }
} }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
if(pkgs.size == 0) { getPkgList() } if(vm.installedPackages.isEmpty()) { getPkgList() }
} }
} }
} }
@Composable @Composable
private fun PackageItem(pkg: PkgInfo, navCtrl: NavHostController) { private fun PackageItem(pkg: PackageInfo, navCtrl: NavHostController, vm: MyViewModel) {
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
@@ -215,7 +210,7 @@ private fun PackageItem(pkg: PkgInfo, navCtrl: NavHostController) {
.fillMaxWidth() .fillMaxWidth()
.clickable{ .clickable{
focusMgr.clearFocus() focusMgr.clearFocus()
selectedPackage.value = pkg.pkgName vm.selectedPackage.value = pkg.pkgName
navCtrl.navigateUp() navCtrl.navigateUp()
} }
.padding(horizontal = 8.dp, vertical = 10.dp) .padding(horizontal = 8.dp, vertical = 10.dp)

View File

@@ -1,9 +1,10 @@
package com.bintianqi.owndroid package com.bintianqi.owndroid
import android.annotation.SuppressLint
import android.app.NotificationManager import android.app.NotificationManager
import android.app.PendingIntent
import android.app.admin.DeviceAdminReceiver import android.app.admin.DeviceAdminReceiver
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageInstaller.EXTRA_STATUS import android.content.pm.PackageInstaller.EXTRA_STATUS
@@ -21,8 +22,7 @@ import android.os.Build.VERSION
import android.os.PersistableBundle import android.os.PersistableBundle
import android.util.Log import android.util.Log
import android.widget.Toast import android.widget.Toast
import com.bintianqi.owndroid.dpm.getDPM import androidx.core.app.NotificationCompat
import com.bintianqi.owndroid.dpm.getReceiver
import com.bintianqi.owndroid.dpm.handleNetworkLogs import com.bintianqi.owndroid.dpm.handleNetworkLogs
import com.bintianqi.owndroid.dpm.handleSecurityLogs import com.bintianqi.owndroid.dpm.handleSecurityLogs
import com.bintianqi.owndroid.dpm.isDeviceAdmin import com.bintianqi.owndroid.dpm.isDeviceAdmin
@@ -35,6 +35,17 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class Receiver : DeviceAdminReceiver() { 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) { override fun onEnabled(context: Context, intent: Intent) {
super.onEnabled(context, intent) super.onEnabled(context, intent)
context.toggleInstallAppActivity() context.toggleInstallAppActivity()
@@ -78,6 +89,26 @@ class Receiver : DeviceAdminReceiver() {
sp.edit().putBoolean("dhizuku", false).apply() sp.edit().putBoolean("dhizuku", false).apply()
context.toggleInstallAppActivity() 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) 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)
}
}

View File

@@ -1,15 +1,12 @@
package com.bintianqi.owndroid package com.bintianqi.owndroid
import android.Manifest
import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyManager
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Build.VERSION
import android.widget.Toast import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
@@ -65,7 +62,6 @@ fun writeClipBoard(context: Context, string: String):Boolean{
return true return true
} }
lateinit var requestPermission: ActivityResultLauncher<String>
lateinit var exportFile: ActivityResultLauncher<Intent> lateinit var exportFile: ActivityResultLauncher<Intent>
var exportFilePath: String? = null var exportFilePath: String? = null
var isExportingSecurityOrNetworkLogs = false var isExportingSecurityOrNetworkLogs = false
@@ -83,7 +79,6 @@ fun registerActivityResult(context: ComponentActivity){
backToHomeStateFlow.value = true backToHomeStateFlow.value = true
} }
} }
requestPermission = context.registerForActivityResult(ActivityResultContracts.RequestPermission()) { permissionGranted.value = it }
exportFile = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> exportFile = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
val intentData = result.data ?: return@registerForActivityResult val intentData = result.data ?: return@registerForActivityResult
val uriData = intentData.data ?: return@registerForActivityResult val uriData = intentData.data ?: return@registerForActivityResult
@@ -103,21 +98,6 @@ fun registerActivityResult(context: ComponentActivity){
} }
} }
val permissionGranted = MutableStateFlow<Boolean?>(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 { fun formatFileSize(bytes: Long): String {
val kb = 1024 val kb = 1024
val mb = kb * 1024 val mb = kb * 1024

View File

@@ -50,7 +50,6 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableIntState
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf 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.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat.startActivity import androidx.core.content.ContextCompat.startActivity
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.bintianqi.owndroid.InstallAppActivity import com.bintianqi.owndroid.InstallAppActivity
import com.bintianqi.owndroid.MyViewModel
import com.bintianqi.owndroid.PackageInstallerReceiver import com.bintianqi.owndroid.PackageInstallerReceiver
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.fileUriFlow import com.bintianqi.owndroid.fileUriFlow
import com.bintianqi.owndroid.getFile import com.bintianqi.owndroid.getFile
import com.bintianqi.owndroid.selectedPackage
import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.Animations
import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.InfoCard
import com.bintianqi.owndroid.ui.Information
import com.bintianqi.owndroid.ui.ListItem import com.bintianqi.owndroid.ui.ListItem
import com.bintianqi.owndroid.ui.NavIcon import com.bintianqi.owndroid.ui.NavIcon
import com.bintianqi.owndroid.ui.RadioButtonItem import com.bintianqi.owndroid.ui.RadioButtonItem
import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.SwitchItem import com.bintianqi.owndroid.ui.SwitchItem
import java.util.concurrent.Executors import java.util.concurrent.Executors
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun ApplicationManage(navCtrl:NavHostController, dialogStatus: MutableIntState) { fun ApplicationManage(navCtrl:NavHostController, vm: MyViewModel) {
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
val localNavCtrl = rememberNavController() val localNavCtrl = rememberNavController()
var pkgName by rememberSaveable { mutableStateOf("") } var pkgName by rememberSaveable { mutableStateOf("") }
val updatePackage by selectedPackage.collectAsState() val updatePackage by vm.selectedPackage.collectAsStateWithLifecycle()
LaunchedEffect(updatePackage) { LaunchedEffect(updatePackage) {
if(updatePackage != "") { if(updatePackage != "") {
pkgName = updatePackage pkgName = updatePackage
selectedPackage.value = "" vm.selectedPackage.value = ""
} }
} }
Scaffold( Scaffold(
@@ -145,9 +144,7 @@ fun ApplicationManage(navCtrl:NavHostController, dialogStatus: MutableIntState)
popEnterTransition = Animations.navHostPopEnterTransition, popEnterTransition = Animations.navHostPopEnterTransition,
popExitTransition = Animations.navHostPopExitTransition popExitTransition = Animations.navHostPopExitTransition
) { ) {
composable(route = "Home") { composable(route = "Home") { Home(localNavCtrl, pkgName) }
Home(localNavCtrl, pkgName, dialogStatus)
}
composable(route = "UserControlDisabled") { UserCtrlDisabledPkg(pkgName) } composable(route = "UserControlDisabled") { UserCtrlDisabledPkg(pkgName) }
composable(route = "PermissionManage") { PermissionManage(pkgName) } composable(route = "PermissionManage") { PermissionManage(pkgName) }
composable(route = "CrossProfilePackage") { CrossProfilePkg(pkgName) } composable(route = "CrossProfilePackage") { CrossProfilePkg(pkgName) }
@@ -160,23 +157,11 @@ fun ApplicationManage(navCtrl:NavHostController, dialogStatus: MutableIntState)
composable(route = "UninstallApp") { UninstallApp(pkgName) } 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 @Composable
private fun Home( private fun Home(navCtrl:NavHostController, pkgName: String) {
navCtrl:NavHostController, var dialogStatus by remember { mutableIntStateOf(0) }
pkgName: String,
dialogStatus: MutableIntState
) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
@@ -190,7 +175,6 @@ private fun Home(
hide = dpm.isApplicationHidden(receiver, pkgName) hide = dpm.isApplicationHidden(receiver, pkgName)
var blockUninstall by remember { mutableStateOf(false) } var blockUninstall by remember { mutableStateOf(false) }
blockUninstall = dpm.isUninstallBlocked(receiver,pkgName) blockUninstall = dpm.isUninstallBlocked(receiver,pkgName)
var appControlDialog by remember { mutableStateOf(false) }
var appControlAction by remember { mutableIntStateOf(0) } var appControlAction by remember { mutableIntStateOf(0) }
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
val appControl: (Boolean) -> Unit = { val appControl: (Boolean) -> Unit = {
@@ -226,20 +210,20 @@ private fun Home(
title = R.string.suspend, desc = "", icon = R.drawable.block_fill0, title = R.string.suspend, desc = "", icon = R.drawable.block_fill0,
state = suspend, state = suspend,
onCheckedChange = { appControlAction = 1; appControl(it) }, onCheckedChange = { appControlAction = 1; appControl(it) },
onClickBlank = { appControlAction = 1; appControlDialog = true } onClickBlank = { appControlAction = 1; dialogStatus = 4 }
) )
} }
SwitchItem( SwitchItem(
title = R.string.hide, desc = stringResource(R.string.isapphidden_desc), icon = R.drawable.visibility_off_fill0, title = R.string.hide, desc = stringResource(R.string.isapphidden_desc), icon = R.drawable.visibility_off_fill0,
state = hide, state = hide,
onCheckedChange = { appControlAction = 2; appControl(it) }, onCheckedChange = { appControlAction = 2; appControl(it) },
onClickBlank = { appControlAction = 2; appControlDialog = true } onClickBlank = { appControlAction = 2; dialogStatus = 4 }
) )
SwitchItem( SwitchItem(
title = R.string.block_uninstall, desc = "", icon = R.drawable.delete_forever_fill0, title = R.string.block_uninstall, desc = "", icon = R.drawable.delete_forever_fill0,
state = blockUninstall, state = blockUninstall,
onCheckedChange = { appControlAction = 3; appControl(it) }, 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)) { if((VERSION.SDK_INT >= 33 && profileOwner) || (VERSION.SDK_INT >= 30 && deviceOwner)) {
FunctionItem(R.string.ucd, "", R.drawable.do_not_touch_fill0) { navCtrl.navigate("UserControlDisabled") } 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_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.permitted_ime, "", R.drawable.keyboard_fill0) { navCtrl.navigate("IME") }
FunctionItem(R.string.enable_system_app, "", R.drawable.enable_fill0) { 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) { if(VERSION.SDK_INT >= 28 && deviceOwner) {
FunctionItem(R.string.keep_uninstalled_packages, "", R.drawable.delete_fill0) { navCtrl.navigate("KeepUninstalled") } FunctionItem(R.string.keep_uninstalled_packages, "", R.drawable.delete_fill0) { navCtrl.navigate("KeepUninstalled") }
} }
if(VERSION.SDK_INT >= 28) { if(VERSION.SDK_INT >= 28) {
FunctionItem(R.string.clear_app_storage, "", R.drawable.mop_fill0) { 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.install_app, "", R.drawable.install_mobile_fill0) { navCtrl.navigate("InstallApp") }
FunctionItem(R.string.uninstall_app, "", R.drawable.delete_fill0) { navCtrl.navigate("UninstallApp") } FunctionItem(R.string.uninstall_app, "", R.drawable.delete_fill0) { navCtrl.navigate("UninstallApp") }
if(VERSION.SDK_INT >= 34 && (deviceOwner || dpm.isOrgProfile(receiver))) { if(VERSION.SDK_INT >= 34 && (deviceOwner || dpm.isOrgProfile(receiver))) {
FunctionItem(R.string.set_default_dialer, "", R.drawable.call_fill0) { 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)) Spacer(Modifier.padding(vertical = 30.dp))
LaunchedEffect(Unit) { fileUriFlow.value = Uri.parse("") } 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) { LaunchedEffect(Unit) {
focusMgr.clearFocus() focusMgr.clearFocus()
} }
AlertDialog( AlertDialog(
onDismissRequest = { appControlDialog = false }, onDismissRequest = { dialogStatus = 0 },
title = { title = {
Text( Text(
text = stringResource( text = stringResource(
@@ -316,7 +392,7 @@ private fun Home(
TextButton( TextButton(
onClick = { onClick = {
appControl(true) appControl(true)
appControlDialog = false dialogStatus = 0
} }
) { ) {
Text(text = stringResource(R.string.enable)) Text(text = stringResource(R.string.enable))
@@ -326,7 +402,7 @@ private fun Home(
TextButton( TextButton(
onClick = { onClick = {
appControl(false) appControl(false)
appControlDialog = false dialogStatus = 0
} }
) { ) {
Text(text = stringResource(R.string.disable)) 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 { InfoCard(R.string.system_accessibility_always_allowed)
Text(stringResource(R.string.system_accessibility_always_allowed))
}
Spacer(Modifier.padding(vertical = 30.dp)) Spacer(Modifier.padding(vertical = 30.dp))
} }
} }
@@ -755,9 +830,7 @@ private fun PermittedIME(pkgName: String) {
} }
} }
} }
Information { InfoCard(R.string.system_ime_always_allowed)
Text(stringResource(R.string.system_ime_always_allowed))
}
Spacer(Modifier.padding(vertical = 30.dp)) 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()
)
}

View File

@@ -92,12 +92,12 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import com.bintianqi.owndroid.MyViewModel
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.exportFile import com.bintianqi.owndroid.exportFile
import com.bintianqi.owndroid.exportFilePath import com.bintianqi.owndroid.exportFilePath
import com.bintianqi.owndroid.formatFileSize import com.bintianqi.owndroid.formatFileSize
import com.bintianqi.owndroid.isExportingSecurityOrNetworkLogs import com.bintianqi.owndroid.isExportingSecurityOrNetworkLogs
import com.bintianqi.owndroid.selectedPackage
import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.InfoCard
@@ -400,7 +400,7 @@ fun PrivateDNS(navCtrl: NavHostController) {
@SuppressLint("NewApi") @SuppressLint("NewApi")
@Composable @Composable
fun AlwaysOnVPNPackage(navCtrl: NavHostController) { fun AlwaysOnVPNPackage(navCtrl: NavHostController, vm: MyViewModel) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
@@ -409,11 +409,11 @@ fun AlwaysOnVPNPackage(navCtrl: NavHostController) {
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
val refresh = { pkgName = dpm.getAlwaysOnVpnPackage(receiver) ?: "" } val refresh = { pkgName = dpm.getAlwaysOnVpnPackage(receiver) ?: "" }
LaunchedEffect(Unit) { refresh() } LaunchedEffect(Unit) { refresh() }
val updatePackage by selectedPackage.collectAsState() val updatePackage by vm.selectedPackage.collectAsState()
LaunchedEffect(updatePackage) { LaunchedEffect(updatePackage) {
if(selectedPackage.value != "") { if(updatePackage != "") {
pkgName = selectedPackage.value pkgName = updatePackage
selectedPackage.value = "" vm.selectedPackage.value = ""
} }
} }
val setAlwaysOnVpn: (String?, Boolean)->Boolean = { vpnPkg: String?, lockdownEnabled: Boolean -> val setAlwaysOnVpn: (String?, Boolean)->Boolean = { vpnPkg: String?, lockdownEnabled: Boolean ->

View File

@@ -75,7 +75,6 @@ import com.bintianqi.owndroid.ui.CardItem
import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.InfoCard
import com.bintianqi.owndroid.ui.Information
import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.RadioButtonItem import com.bintianqi.owndroid.ui.RadioButtonItem
import com.bintianqi.owndroid.yesOrNo import com.bintianqi.owndroid.yesOrNo
@@ -303,7 +302,7 @@ fun ResetPasswordToken(navCtrl: NavHostController) {
} }
} }
Spacer(Modifier.padding(vertical = 5.dp)) 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)
} }
} }

View File

@@ -3,9 +3,6 @@ package com.bintianqi.owndroid.dpm
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.ActivityOptions import android.app.ActivityOptions
import android.app.AlertDialog 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.FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY
import android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback import android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback
import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_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.ImeAction
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.app.NotificationCompat import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import com.bintianqi.owndroid.MyViewModel
import com.bintianqi.owndroid.NotificationUtils
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.StopLockTaskModeReceiver
import com.bintianqi.owndroid.exportFile import com.bintianqi.owndroid.exportFile
import com.bintianqi.owndroid.exportFilePath import com.bintianqi.owndroid.exportFilePath
import com.bintianqi.owndroid.fileUriFlow import com.bintianqi.owndroid.fileUriFlow
@@ -118,13 +116,10 @@ import com.bintianqi.owndroid.formatFileSize
import com.bintianqi.owndroid.getFile import com.bintianqi.owndroid.getFile
import com.bintianqi.owndroid.humanReadableDate import com.bintianqi.owndroid.humanReadableDate
import com.bintianqi.owndroid.isExportingSecurityOrNetworkLogs import com.bintianqi.owndroid.isExportingSecurityOrNetworkLogs
import com.bintianqi.owndroid.prepareForNotification
import com.bintianqi.owndroid.selectedPackage
import com.bintianqi.owndroid.toggle import com.bintianqi.owndroid.toggle
import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.InfoCard
import com.bintianqi.owndroid.ui.Information
import com.bintianqi.owndroid.ui.ListItem import com.bintianqi.owndroid.ui.ListItem
import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.RadioButtonItem import com.bintianqi.owndroid.ui.RadioButtonItem
@@ -522,9 +517,7 @@ fun ChangeTimeZone(navCtrl: NavHostController) {
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))
} }
Spacer(Modifier.padding(vertical = 10.dp)) Spacer(Modifier.padding(vertical = 10.dp))
Information { InfoCard(R.string.disable_auto_time_zone_before_set)
Text(stringResource(R.string.disable_auto_time_zone_before_set))
}
} }
if(dialog) AlertDialog( if(dialog) AlertDialog(
text = { text = {
@@ -700,12 +693,11 @@ fun NearbyStreamingPolicy(navCtrl: NavHostController) {
@SuppressLint("NewApi") @SuppressLint("NewApi")
@Composable @Composable
fun LockTaskMode(navCtrl: NavHostController) { fun LockTaskMode(navCtrl: NavHostController, vm: MyViewModel) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
val coroutine = rememberCoroutineScope()
var appSelectorRequest by rememberSaveable { mutableIntStateOf(0) } var appSelectorRequest by rememberSaveable { mutableIntStateOf(0) }
MyScaffold(R.string.lock_task_mode, 8.dp, navCtrl, false) { MyScaffold(R.string.lock_task_mode, 8.dp, navCtrl, false) {
val lockTaskFeatures = remember { mutableStateListOf<Int>() } val lockTaskFeatures = remember { mutableStateListOf<Int>() }
@@ -784,11 +776,11 @@ fun LockTaskMode(navCtrl: NavHostController) {
lockTaskFeatures.forEach { result += it } lockTaskFeatures.forEach { result += it }
} }
try { try {
dpm.setLockTaskFeatures(receiver,result) dpm.setLockTaskFeatures(receiver, result)
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
AlertDialog.Builder(context) AlertDialog.Builder(context)
.setTitle("Error") .setTitle(R.string.error)
.setMessage(e.message) .setMessage(e.message)
.setPositiveButton(R.string.confirm) { dialog, _ -> dialog.dismiss() } .setPositiveButton(R.string.confirm) { dialog, _ -> dialog.dismiss() }
.show() .show()
@@ -863,11 +855,11 @@ fun LockTaskMode(navCtrl: NavHostController) {
var startLockTaskApp by rememberSaveable { mutableStateOf("") } var startLockTaskApp by rememberSaveable { mutableStateOf("") }
var startLockTaskActivity by rememberSaveable { mutableStateOf("") } var startLockTaskActivity by rememberSaveable { mutableStateOf("") }
var specifyActivity by rememberSaveable { mutableStateOf(false) } var specifyActivity by rememberSaveable { mutableStateOf(false) }
val updatePackage by selectedPackage.collectAsState() val updatePackage by vm.selectedPackage.collectAsStateWithLifecycle()
LaunchedEffect(updatePackage) { LaunchedEffect(updatePackage) {
if(updatePackage != "") { if(updatePackage != "") {
if(appSelectorRequest == 1) inputLockTaskPkg = updatePackage else startLockTaskApp = updatePackage if(appSelectorRequest == 1) inputLockTaskPkg = updatePackage else startLockTaskApp = updatePackage
selectedPackage.value = "" vm.selectedPackage.value = ""
} }
} }
Spacer(Modifier.padding(vertical = 10.dp)) Spacer(Modifier.padding(vertical = 10.dp))
@@ -906,7 +898,8 @@ fun LockTaskMode(navCtrl: NavHostController) {
Button( Button(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
onClick = { 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() Toast.makeText(context, R.string.app_not_allowed, Toast.LENGTH_SHORT).show()
return@Button return@Button
} }
@@ -915,13 +908,7 @@ fun LockTaskMode(navCtrl: NavHostController) {
val launchIntent = if(specifyActivity) Intent().setComponent(ComponentName(startLockTaskApp, startLockTaskActivity)) val launchIntent = if(specifyActivity) Intent().setComponent(ComponentName(startLockTaskApp, startLockTaskActivity))
else packageManager.getLaunchIntentForPackage(startLockTaskApp) else packageManager.getLaunchIntentForPackage(startLockTaskApp)
if (launchIntent != null) { if (launchIntent != null) {
coroutine.launch {
prepareForNotification(context) {
sendStopLockTaskNotification(context)
context.startActivity(launchIntent, options.toBundle()) context.startActivity(launchIntent, options.toBundle())
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
}
}
} else { } else {
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show() 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) 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())
}

View File

@@ -28,7 +28,6 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.writeClipBoard 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 @Composable
fun RadioButtonItem( fun RadioButtonItem(
@StringRes text: Int, @StringRes text: Int,

View File

@@ -63,6 +63,7 @@
<string name="alias">Alias</string> <string name="alias">Alias</string>
<string name="unknown_error">Unknown error</string> <string name="unknown_error">Unknown error</string>
<string name="permission_denied">Permission denied</string> <string name="permission_denied">Permission denied</string>
<string name="error">Error</string>
<!--Разрешения--> <!--Разрешения-->

View File

@@ -64,6 +64,7 @@
<string name="alias">Alias</string> <string name="alias">Alias</string>
<string name="unknown_error">Unknown error</string> <string name="unknown_error">Unknown error</string>
<string name="permission_denied">Permission denied</string> <string name="permission_denied">Permission denied</string>
<string name="error">Error</string>
<!--Permissions--> <!--Permissions-->
<string name="click_to_activate">Etkinleştirmek İçin Tıklayın</string> <string name="click_to_activate">Etkinleştirmek İçin Tıklayın</string>

View File

@@ -60,6 +60,7 @@
<string name="alias">别名</string> <string name="alias">别名</string>
<string name="unknown_error">未知错误</string> <string name="unknown_error">未知错误</string>
<string name="permission_denied">无权限</string> <string name="permission_denied">无权限</string>
<string name="error">错误</string>
<!--Permissions--> <!--Permissions-->
<string name="click_to_activate">点击以激活</string> <string name="click_to_activate">点击以激活</string>

View File

@@ -64,6 +64,7 @@
<string name="unknown_error">Unknown error</string> <string name="unknown_error">Unknown error</string>
<string name="permission_denied">Permission denied</string> <string name="permission_denied">Permission denied</string>
<string name="api" translatable="false">API</string> <string name="api" translatable="false">API</string>
<string name="error">Error</string>
<!--Permissions--> <!--Permissions-->
<string name="click_to_activate">Click to activate</string> <string name="click_to_activate">Click to activate</string>