mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-26 03:56:00 +00:00
Optimize code
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/>
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" tools:ignore="QueryAllPackagesPermission" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
|
||||
<uses-sdk tools:overrideLibrary="rikka.shizuku.provider,rikka.shizuku.api,rikka.shizuku.shared,rikka.shizuku.aidl"/>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.bintianqi.owndroid
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
@@ -47,7 +46,6 @@ class InstallAppActivity: FragmentActivity() {
|
||||
enableEdgeToEdge()
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
val context = applicationContext
|
||||
val sharedPref = applicationContext.getSharedPreferences("data", Context.MODE_PRIVATE)
|
||||
window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
val uri = this.intent.data!!
|
||||
var apkInfoText by mutableStateOf(context.getString(R.string.parsing_apk_info))
|
||||
@@ -97,7 +95,9 @@ class InstallAppActivity: FragmentActivity() {
|
||||
TextButton(
|
||||
onClick = {
|
||||
status = "installing"
|
||||
uriToStream(applicationContext, this.intent.data) { stream -> installPackage(applicationContext, stream) }
|
||||
intent.data?.let {
|
||||
uriToStream(applicationContext, it) { stream -> installPackage(applicationContext, stream) }
|
||||
}
|
||||
},
|
||||
enabled = status != "installing"
|
||||
) {
|
||||
|
||||
@@ -24,10 +24,10 @@ import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.NotificationCompat
|
||||
import com.bintianqi.owndroid.dpm.handleNetworkLogs
|
||||
import com.bintianqi.owndroid.dpm.handleSecurityLogs
|
||||
import com.bintianqi.owndroid.dpm.isDeviceAdmin
|
||||
import com.bintianqi.owndroid.dpm.isDeviceOwner
|
||||
import com.bintianqi.owndroid.dpm.isProfileOwner
|
||||
import com.bintianqi.owndroid.dpm.processSecurityLogs
|
||||
import com.bintianqi.owndroid.dpm.toggleInstallAppActivity
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -78,7 +78,13 @@ class Receiver : DeviceAdminReceiver() {
|
||||
super.onSecurityLogsAvailable(context, intent)
|
||||
if(VERSION.SDK_INT >= 24) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
handleSecurityLogs(context)
|
||||
val events = getManager(context).retrieveSecurityLogs(ComponentName(context, this@Receiver::class.java)) ?: return@launch
|
||||
val file = context.filesDir.resolve("SecurityLogs.json")
|
||||
val fileExists = file.exists()
|
||||
file.outputStream().use {
|
||||
if(fileExists) it.write(",".encodeToByteArray())
|
||||
processSecurityLogs(events, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build.VERSION
|
||||
import android.widget.Toast
|
||||
import androidx.biometric.BiometricManager
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
@@ -44,11 +43,11 @@ import java.security.SecureRandom
|
||||
@Composable
|
||||
fun Settings(navCtrl: NavHostController) {
|
||||
MyScaffold(R.string.settings, 0.dp, navCtrl) {
|
||||
FunctionItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("Options") }
|
||||
FunctionItem(R.string.appearance, "", R.drawable.format_paint_fill0) { navCtrl.navigate("Appearance") }
|
||||
FunctionItem(R.string.security, "", R.drawable.lock_fill0) { navCtrl.navigate("AuthSettings") }
|
||||
FunctionItem(R.string.api, "", R.drawable.apps_fill0) { navCtrl.navigate("ApiSettings") }
|
||||
FunctionItem(R.string.about, "", R.drawable.info_fill0) { navCtrl.navigate("About") }
|
||||
FunctionItem(title = R.string.options, icon = R.drawable.tune_fill0) { navCtrl.navigate("Options") }
|
||||
FunctionItem(title = R.string.appearance, icon = R.drawable.format_paint_fill0) { navCtrl.navigate("Appearance") }
|
||||
FunctionItem(title = R.string.security, icon = R.drawable.lock_fill0) { navCtrl.navigate("AuthSettings") }
|
||||
FunctionItem(title = R.string.api, icon = R.drawable.apps_fill0) { navCtrl.navigate("ApiSettings") }
|
||||
FunctionItem(title = R.string.about, icon = R.drawable.info_fill0) { navCtrl.navigate("About") }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,9 +56,9 @@ fun SettingsOptions(navCtrl: NavHostController) {
|
||||
val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
|
||||
MyScaffold(R.string.options, 0.dp, navCtrl) {
|
||||
SwitchItem(
|
||||
R.string.show_dangerous_features, "", R.drawable.warning_fill0,
|
||||
{ sharedPref.getBoolean("dangerous_features", false) },
|
||||
{ sharedPref.edit().putBoolean("dangerous_features", it).apply() }
|
||||
R.string.show_dangerous_features, icon = R.drawable.warning_fill0,
|
||||
getState = { sharedPref.getBoolean("dangerous_features", false) },
|
||||
onCheckedChange = { sharedPref.edit().putBoolean("dangerous_features", it).apply() }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -75,11 +74,7 @@ fun Appearance(navCtrl: NavHostController, vm: MyViewModel) {
|
||||
}
|
||||
MyScaffold(R.string.appearance, 0.dp, navCtrl) {
|
||||
if(VERSION.SDK_INT >= 31) {
|
||||
SwitchItem(
|
||||
R.string.material_you_color, "", null,
|
||||
theme.materialYou,
|
||||
{ vm.theme.value = theme.copy(materialYou = it) }
|
||||
)
|
||||
SwitchItem(R.string.material_you_color, state = theme.materialYou, onCheckedChange = { vm.theme.value = theme.copy(materialYou = it) })
|
||||
}
|
||||
Box {
|
||||
FunctionItem(R.string.dark_theme, stringResource(darkThemeTextID)) { darkThemeMenu = true }
|
||||
@@ -111,11 +106,7 @@ fun Appearance(navCtrl: NavHostController, vm: MyViewModel) {
|
||||
}
|
||||
}
|
||||
AnimatedVisibility(theme.darkTheme == true || (theme.darkTheme == null && isSystemInDarkTheme())) {
|
||||
SwitchItem(
|
||||
R.string.black_theme, "", null,
|
||||
theme.blackTheme,
|
||||
{ vm.theme.value = theme.copy(blackTheme = it) }
|
||||
)
|
||||
SwitchItem(R.string.black_theme, state = theme.blackTheme, onCheckedChange = { vm.theme.value = theme.copy(blackTheme = it) })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,8 +118,8 @@ fun AuthSettings(navCtrl: NavHostController) {
|
||||
var auth by remember{ mutableStateOf(sharedPref.getBoolean("auth",false)) }
|
||||
MyScaffold(R.string.security, 0.dp, navCtrl) {
|
||||
SwitchItem(
|
||||
R.string.lock_owndroid, "", null, auth,
|
||||
{
|
||||
R.string.lock_owndroid, state = auth,
|
||||
onCheckedChange = {
|
||||
sharedPref.edit().putBoolean("auth", it).apply()
|
||||
auth = sharedPref.getBoolean("auth", false)
|
||||
}
|
||||
@@ -143,13 +134,13 @@ fun AuthSettings(navCtrl: NavHostController) {
|
||||
}
|
||||
}
|
||||
SwitchItem(
|
||||
R.string.enable_bio_auth, "", null, bioAuth != 0,
|
||||
{ bioAuth = if(it) 1 else 0; sharedPref.edit().putInt("biometrics_auth", bioAuth).apply() }, bioAuth != 2
|
||||
R.string.enable_bio_auth, state = bioAuth != 0,
|
||||
onCheckedChange = { bioAuth = if(it) 1 else 0; sharedPref.edit().putInt("biometrics_auth", bioAuth).apply() }, enabled = bioAuth != 2
|
||||
)
|
||||
SwitchItem(
|
||||
R.string.lock_in_background, "", null,
|
||||
{ sharedPref.getBoolean("lock_in_background", false) },
|
||||
{ sharedPref.edit().putBoolean("lock_in_background", it).apply() }
|
||||
R.string.lock_in_background,
|
||||
getState = { sharedPref.getBoolean("lock_in_background", false) },
|
||||
onCheckedChange = { sharedPref.edit().putBoolean("lock_in_background", it).apply() }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -167,7 +158,7 @@ fun ApiSettings(navCtrl: NavHostController) {
|
||||
if(!enabled) remove("api_key")
|
||||
}
|
||||
}
|
||||
SwitchItem(R.string.enable, "", null, enabled, { enabled = it }, padding = false)
|
||||
SwitchItem(R.string.enable, state = enabled, onCheckedChange = { enabled = it }, padding = false)
|
||||
if(enabled) {
|
||||
var key by remember { mutableStateOf("") }
|
||||
OutlinedTextField(
|
||||
@@ -189,7 +180,7 @@ fun ApiSettings(navCtrl: NavHostController) {
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 10.dp),
|
||||
onClick = {
|
||||
sharedPref.edit().putString("api_key", key).apply()
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
|
||||
@@ -5,18 +5,13 @@ import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.annotation.StringRes
|
||||
import com.bintianqi.owndroid.dpm.addDeviceAdmin
|
||||
import com.bintianqi.owndroid.dpm.createManagedProfile
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
@@ -27,29 +22,20 @@ import java.time.format.DateTimeFormatter
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
lateinit var getFile: ActivityResultLauncher<Intent>
|
||||
val fileUriFlow = MutableStateFlow(Uri.parse(""))
|
||||
|
||||
var zhCN = true
|
||||
|
||||
fun uriToStream(
|
||||
context: Context,
|
||||
uri: Uri?,
|
||||
uri: Uri,
|
||||
operation: (stream: InputStream)->Unit
|
||||
){
|
||||
if(uri!=null){
|
||||
try {
|
||||
val stream = context.contentResolver.openInputStream(uri)
|
||||
if(stream != null) { operation(stream) }
|
||||
stream?.close()
|
||||
}
|
||||
catch(_: FileNotFoundException) { Toast.makeText(context, R.string.file_not_exist, Toast.LENGTH_SHORT).show() }
|
||||
catch(_: IOException) { Toast.makeText(context, R.string.io_exception, Toast.LENGTH_SHORT).show() }
|
||||
try {
|
||||
val stream = context.contentResolver.openInputStream(uri)
|
||||
if(stream != null) { operation(stream) }
|
||||
stream?.close()
|
||||
}
|
||||
}
|
||||
|
||||
fun MutableList<Int>.toggle(status: Boolean, element: Int) {
|
||||
if(status) add(element) else remove(element)
|
||||
catch(_: FileNotFoundException) { Toast.makeText(context, R.string.file_not_exist, Toast.LENGTH_SHORT).show() }
|
||||
catch(_: IOException) { Toast.makeText(context, R.string.io_exception, Toast.LENGTH_SHORT).show() }
|
||||
}
|
||||
|
||||
fun writeClipBoard(context: Context, string: String):Boolean{
|
||||
@@ -62,40 +48,13 @@ fun writeClipBoard(context: Context, string: String):Boolean{
|
||||
return true
|
||||
}
|
||||
|
||||
lateinit var exportFile: ActivityResultLauncher<Intent>
|
||||
var exportFilePath: String? = null
|
||||
var isExportingSecurityOrNetworkLogs = false
|
||||
|
||||
fun registerActivityResult(context: ComponentActivity){
|
||||
getFile = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult ->
|
||||
activityResult.data.let {
|
||||
if(it != null) fileUriFlow.value = it.data
|
||||
}
|
||||
}
|
||||
createManagedProfile = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {}
|
||||
fun registerActivityResult(context: ComponentActivity) {
|
||||
addDeviceAdmin = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
val dpm = context.applicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
if(dpm.isAdminActive(ComponentName(context.applicationContext, Receiver::class.java))) {
|
||||
backToHomeStateFlow.value = true
|
||||
}
|
||||
}
|
||||
exportFile = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
val intentData = result.data ?: return@registerForActivityResult
|
||||
val uriData = intentData.data ?: return@registerForActivityResult
|
||||
val path = exportFilePath ?: return@registerForActivityResult
|
||||
context.contentResolver.openOutputStream(uriData).use { outStream ->
|
||||
if(outStream != null) {
|
||||
if(isExportingSecurityOrNetworkLogs) outStream.write("[".encodeToByteArray())
|
||||
File(path).inputStream().use { inStream ->
|
||||
inStream.copyTo(outStream)
|
||||
}
|
||||
if(isExportingSecurityOrNetworkLogs) outStream.write("]".encodeToByteArray())
|
||||
Toast.makeText(context.applicationContext, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
isExportingSecurityOrNetworkLogs = false
|
||||
exportFilePath = null
|
||||
}
|
||||
}
|
||||
|
||||
fun formatFileSize(bytes: Long): String {
|
||||
|
||||
@@ -18,6 +18,8 @@ import android.os.Build.VERSION
|
||||
import android.os.Looper
|
||||
import android.provider.Settings
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.background
|
||||
@@ -50,7 +52,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.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
@@ -82,8 +83,7 @@ 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.showOperationResultToast
|
||||
import com.bintianqi.owndroid.ui.Animations
|
||||
import com.bintianqi.owndroid.ui.FunctionItem
|
||||
import com.bintianqi.owndroid.ui.InfoCard
|
||||
@@ -200,14 +200,14 @@ private fun Home(navCtrl:NavHostController, pkgName: String) {
|
||||
if(VERSION.SDK_INT >= 24 && profileOwner && dpm.isManagedProfile(receiver)) {
|
||||
Text(text = stringResource(R.string.scope_is_work_profile), textAlign = TextAlign.Center,modifier = Modifier.fillMaxWidth())
|
||||
}
|
||||
FunctionItem(R.string.app_info,"", R.drawable.open_in_new) {
|
||||
FunctionItem(title = R.string.app_info, icon = R.drawable.open_in_new) {
|
||||
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||
intent.setData(Uri.parse("package:$pkgName"))
|
||||
startActivity(context, intent, null)
|
||||
}
|
||||
if(VERSION.SDK_INT >= 24) {
|
||||
SwitchItem(
|
||||
title = R.string.suspend, desc = "", icon = R.drawable.block_fill0,
|
||||
title = R.string.suspend, icon = R.drawable.block_fill0,
|
||||
state = suspend,
|
||||
onCheckedChange = { appControlAction = 1; appControl(it) },
|
||||
onClickBlank = { appControlAction = 1; dialogStatus = 4 }
|
||||
@@ -220,48 +220,47 @@ private fun Home(navCtrl:NavHostController, pkgName: String) {
|
||||
onClickBlank = { appControlAction = 2; dialogStatus = 4 }
|
||||
)
|
||||
SwitchItem(
|
||||
title = R.string.block_uninstall, desc = "", icon = R.drawable.delete_forever_fill0,
|
||||
title = R.string.block_uninstall, icon = R.drawable.delete_forever_fill0,
|
||||
state = blockUninstall,
|
||||
onCheckedChange = { appControlAction = 3; appControl(it) },
|
||||
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") }
|
||||
FunctionItem(title = R.string.ucd, icon = R.drawable.do_not_touch_fill0) { navCtrl.navigate("UserControlDisabled") }
|
||||
}
|
||||
if(VERSION.SDK_INT>=23) {
|
||||
FunctionItem(R.string.permission_manage, "", R.drawable.key_fill0) { navCtrl.navigate("PermissionManage") }
|
||||
FunctionItem(title = R.string.permission_manage, icon = R.drawable.key_fill0) { navCtrl.navigate("PermissionManage") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 30 && profileOwner && dpm.isManagedProfile(receiver)) {
|
||||
FunctionItem(R.string.cross_profile_package, "", R.drawable.work_fill0) { navCtrl.navigate("CrossProfilePackage") }
|
||||
FunctionItem(title = R.string.cross_profile_package, icon = R.drawable.work_fill0) { navCtrl.navigate("CrossProfilePackage") }
|
||||
}
|
||||
if(profileOwner) {
|
||||
FunctionItem(R.string.cross_profile_widget, "", R.drawable.widgets_fill0) { navCtrl.navigate("CrossProfileWidget") }
|
||||
FunctionItem(title = R.string.cross_profile_widget, icon = R.drawable.widgets_fill0) { navCtrl.navigate("CrossProfileWidget") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 34 && deviceOwner) {
|
||||
FunctionItem(R.string.credential_manage_policy, "", R.drawable.license_fill0) { navCtrl.navigate("CredentialManagePolicy") }
|
||||
FunctionItem(title = R.string.credential_manage_policy, icon = R.drawable.license_fill0) { navCtrl.navigate("CredentialManagePolicy") }
|
||||
}
|
||||
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) {
|
||||
FunctionItem(title = R.string.permitted_accessibility_services, icon = R.drawable.settings_accessibility_fill0) { navCtrl.navigate("Accessibility") }
|
||||
FunctionItem(title = R.string.permitted_ime, icon = R.drawable.keyboard_fill0) { navCtrl.navigate("IME") }
|
||||
FunctionItem(title = R.string.enable_system_app, icon = R.drawable.enable_fill0) {
|
||||
if(pkgName != "") dialogStatus = 1
|
||||
}
|
||||
if(VERSION.SDK_INT >= 28 && deviceOwner) {
|
||||
FunctionItem(R.string.keep_uninstalled_packages, "", R.drawable.delete_fill0) { navCtrl.navigate("KeepUninstalled") }
|
||||
FunctionItem(title = R.string.keep_uninstalled_packages, icon = R.drawable.delete_fill0) { navCtrl.navigate("KeepUninstalled") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 28) {
|
||||
FunctionItem(R.string.clear_app_storage, "", R.drawable.mop_fill0) {
|
||||
FunctionItem(title = R.string.clear_app_storage, icon = R.drawable.mop_fill0) {
|
||||
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") }
|
||||
FunctionItem(title = R.string.install_app, icon = R.drawable.install_mobile_fill0) { navCtrl.navigate("InstallApp") }
|
||||
FunctionItem(title = R.string.uninstall_app, icon = 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) {
|
||||
FunctionItem(title = R.string.set_default_dialer, icon = R.drawable.call_fill0) {
|
||||
if(pkgName != "") dialogStatus = 3
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
LaunchedEffect(Unit) { fileUriFlow.value = Uri.parse("") }
|
||||
}
|
||||
if(dialogStatus == 1) AlertDialog(
|
||||
title = { Text(stringResource(R.string.enable_system_app)) },
|
||||
@@ -279,7 +278,7 @@ private fun Home(navCtrl:NavHostController, pkgName: String) {
|
||||
onClick = {
|
||||
try {
|
||||
dpm.enableSystemApp(receiver, pkgName)
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
} catch(_: IllegalArgumentException) {
|
||||
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
@@ -343,7 +342,7 @@ private fun Home(navCtrl:NavHostController, pkgName: String) {
|
||||
onClick = {
|
||||
try{
|
||||
dpm.setDefaultDialerApplication(pkgName)
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
} catch(_: IllegalArgumentException) {
|
||||
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
@@ -656,25 +655,13 @@ private fun CredentialManagePolicy(pkgName: String) {
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.credential_manage_policy), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
RadioButtonItem(
|
||||
R.string.none,
|
||||
policyType == -1, { policyType = -1 }
|
||||
)
|
||||
RadioButtonItem(
|
||||
R.string.blacklist,
|
||||
policyType == PACKAGE_POLICY_BLOCKLIST,
|
||||
{ policyType = PACKAGE_POLICY_BLOCKLIST }
|
||||
)
|
||||
RadioButtonItem(
|
||||
R.string.whitelist,
|
||||
policyType == PACKAGE_POLICY_ALLOWLIST,
|
||||
{ policyType = PACKAGE_POLICY_ALLOWLIST }
|
||||
)
|
||||
RadioButtonItem(R.string.none, policyType == -1) { policyType = -1 }
|
||||
RadioButtonItem(R.string.blacklist, policyType == PACKAGE_POLICY_BLOCKLIST) { policyType = PACKAGE_POLICY_BLOCKLIST }
|
||||
RadioButtonItem(R.string.whitelist, policyType == PACKAGE_POLICY_ALLOWLIST){ policyType = PACKAGE_POLICY_ALLOWLIST }
|
||||
RadioButtonItem(
|
||||
R.string.whitelist_and_system_app,
|
||||
policyType == PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM,
|
||||
{ policyType = PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM }
|
||||
)
|
||||
policyType == PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM
|
||||
) { policyType = PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM }
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
AnimatedVisibility(policyType != -1) {
|
||||
Column {
|
||||
@@ -699,7 +686,7 @@ private fun CredentialManagePolicy(pkgName: String) {
|
||||
} else {
|
||||
dpm.credentialManagerPolicy = null
|
||||
}
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
} catch(_: IllegalArgumentException) {
|
||||
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
} finally {
|
||||
@@ -798,8 +785,8 @@ private fun PermittedIME(pkgName: String) {
|
||||
Text(text = stringResource(R.string.permitted_ime), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
SwitchItem(
|
||||
R.string.allow_all, "", null, allowAll,
|
||||
{
|
||||
R.string.allow_all, state = allowAll,
|
||||
onCheckedChange = {
|
||||
dpm.setPermittedInputMethods(receiver, if(it) null else listOf())
|
||||
refresh()
|
||||
}, padding = false
|
||||
@@ -918,8 +905,11 @@ private fun UninstallApp(pkgName: String) {
|
||||
private fun InstallApp() {
|
||||
val context = LocalContext.current
|
||||
val focusMgr = LocalFocusManager.current
|
||||
val selected = fileUriFlow.collectAsState().value != Uri.parse("")
|
||||
val sharedPrefs = context.getSharedPreferences("data", Context.MODE_PRIVATE)
|
||||
var apkFileUri by remember { mutableStateOf<Uri?>(null) }
|
||||
val getFileLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
result.data.also { if(it != null) apkFileUri = it.data }
|
||||
}
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.install_app), style = typography.headlineLarge)
|
||||
@@ -930,19 +920,19 @@ private fun InstallApp() {
|
||||
val installApkIntent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
installApkIntent.setType("application/vnd.android.package-archive")
|
||||
installApkIntent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
getFile.launch(installApkIntent)
|
||||
getFileLauncher.launch(installApkIntent)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.select_apk))
|
||||
}
|
||||
AnimatedVisibility(selected) {
|
||||
AnimatedVisibility(apkFileUri != null) {
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
Button(
|
||||
onClick = {
|
||||
val intent = Intent(context, InstallAppActivity::class.java)
|
||||
intent.data = fileUriFlow.value
|
||||
intent.data = apkFileUri
|
||||
context.startActivity(intent)
|
||||
},
|
||||
enabled = !sharedPrefs.getBoolean("dhizuku", false) && context.isDeviceOwner,
|
||||
@@ -953,7 +943,7 @@ private fun InstallApp() {
|
||||
Button(
|
||||
onClick = {
|
||||
val intent = Intent(Intent.ACTION_INSTALL_PACKAGE)
|
||||
intent.setData(fileUriFlow.value)
|
||||
intent.setData(apkFileUri)
|
||||
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
context.startActivity(intent)
|
||||
},
|
||||
|
||||
@@ -42,8 +42,8 @@ import kotlinx.serialization.json.put
|
||||
import kotlinx.serialization.json.putJsonArray
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
lateinit var createManagedProfile: ActivityResultLauncher<Intent>
|
||||
lateinit var addDeviceAdmin: ActivityResultLauncher<Intent>
|
||||
|
||||
val Context.isDeviceOwner: Boolean
|
||||
@@ -356,15 +356,10 @@ fun handleNetworkLogs(context: Context, batchToken: Long) {
|
||||
}
|
||||
|
||||
@RequiresApi(24)
|
||||
fun handleSecurityLogs(context: Context) {
|
||||
val file = context.filesDir.resolve("SecurityLogs.json")
|
||||
fun processSecurityLogs(securityEvents: List<SecurityLog.SecurityEvent>, outputStream: OutputStream) {
|
||||
val json = Json { ignoreUnknownKeys = true; explicitNulls = false }
|
||||
val securityEvents = context.getDPM().retrieveSecurityLogs(context.getReceiver())
|
||||
securityEvents ?: return
|
||||
val fileExist = file.exists()
|
||||
val buffer = file.bufferedWriter()
|
||||
val buffer = outputStream.bufferedWriter()
|
||||
securityEvents.forEachIndexed { index, event ->
|
||||
if(fileExist && index == 0) buffer.write(",")
|
||||
val item = buildJsonObject {
|
||||
put("time_nanos", event.timeNanos)
|
||||
put("tag", event.tag)
|
||||
|
||||
@@ -19,6 +19,8 @@ import android.content.*
|
||||
import android.os.Binder
|
||||
import android.os.Build.VERSION
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
@@ -38,6 +40,7 @@ import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
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
|
||||
@@ -52,6 +55,7 @@ import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavHostController
|
||||
import com.bintianqi.owndroid.R
|
||||
import com.bintianqi.owndroid.showOperationResultToast
|
||||
import com.bintianqi.owndroid.ui.CardItem
|
||||
import com.bintianqi.owndroid.ui.CheckBoxItem
|
||||
import com.bintianqi.owndroid.ui.CopyTextButton
|
||||
@@ -69,19 +73,19 @@ fun WorkProfile(navCtrl: NavHostController) {
|
||||
val profileOwner = context.isProfileOwner
|
||||
MyScaffold(R.string.work_profile, 0.dp, navCtrl) {
|
||||
if(VERSION.SDK_INT >= 30 && profileOwner && dpm.isManagedProfile(receiver)) {
|
||||
FunctionItem(R.string.org_owned_work_profile, "", R.drawable.corporate_fare_fill0) { navCtrl.navigate("OrgOwnedWorkProfile") }
|
||||
FunctionItem(R.string.org_owned_work_profile, icon = R.drawable.corporate_fare_fill0) { navCtrl.navigate("OrgOwnedWorkProfile") }
|
||||
}
|
||||
if(VERSION.SDK_INT<24 || (VERSION.SDK_INT>=24 && dpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE))) {
|
||||
FunctionItem(R.string.create_work_profile, "", R.drawable.work_fill0) { navCtrl.navigate("CreateWorkProfile") }
|
||||
FunctionItem(R.string.create_work_profile, icon = R.drawable.work_fill0) { navCtrl.navigate("CreateWorkProfile") }
|
||||
}
|
||||
if(dpm.isOrgProfile(receiver)) {
|
||||
FunctionItem(R.string.suspend_personal_app, "", R.drawable.block_fill0) { navCtrl.navigate("SuspendPersonalApp") }
|
||||
FunctionItem(R.string.suspend_personal_app, icon = R.drawable.block_fill0) { navCtrl.navigate("SuspendPersonalApp") }
|
||||
}
|
||||
if(profileOwner && (VERSION.SDK_INT < 24 || (VERSION.SDK_INT >= 24 && dpm.isManagedProfile(receiver)))) {
|
||||
FunctionItem(R.string.intent_filter, "", R.drawable.filter_alt_fill0) { navCtrl.navigate("IntentFilter") }
|
||||
FunctionItem(R.string.intent_filter, icon = R.drawable.filter_alt_fill0) { navCtrl.navigate("IntentFilter") }
|
||||
}
|
||||
if(profileOwner && (VERSION.SDK_INT < 24 || (VERSION.SDK_INT >= 24 && dpm.isManagedProfile(receiver)))) {
|
||||
FunctionItem(R.string.delete_work_profile, "", R.drawable.delete_forever_fill0) { navCtrl.navigate("DeleteWorkProfile") }
|
||||
FunctionItem(R.string.delete_work_profile, icon = R.drawable.delete_forever_fill0) { navCtrl.navigate("DeleteWorkProfile") }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,6 +95,7 @@ fun CreateWorkProfile(navCtrl: NavHostController) {
|
||||
val context = LocalContext.current
|
||||
val receiver = context.getReceiver()
|
||||
val focusMgr = LocalFocusManager.current
|
||||
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { }
|
||||
MyScaffold(R.string.create_work_profile, 8.dp, navCtrl) {
|
||||
var skipEncrypt by remember { mutableStateOf(false) }
|
||||
var offlineProvisioning by remember { mutableStateOf(true) }
|
||||
@@ -99,7 +104,7 @@ fun CreateWorkProfile(navCtrl: NavHostController) {
|
||||
var migrateAccountType by remember { mutableStateOf("") }
|
||||
var keepAccount by remember { mutableStateOf(true) }
|
||||
if(VERSION.SDK_INT >= 22) {
|
||||
CheckBoxItem(R.string.migrate_account, migrateAccount, { migrateAccount = it })
|
||||
CheckBoxItem(R.string.migrate_account, migrateAccount) { migrateAccount = it }
|
||||
AnimatedVisibility(migrateAccount) {
|
||||
val fr = FocusRequester()
|
||||
Column(modifier = Modifier.padding(start = 10.dp)) {
|
||||
@@ -118,23 +123,19 @@ fun CreateWorkProfile(navCtrl: NavHostController) {
|
||||
modifier = Modifier.fillMaxWidth().focusRequester(fr)
|
||||
)
|
||||
if(VERSION.SDK_INT >= 26) {
|
||||
CheckBoxItem(R.string.keep_account, keepAccount, { keepAccount = it })
|
||||
CheckBoxItem(R.string.keep_account, keepAccount) { keepAccount = it }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(VERSION.SDK_INT >= 24) {
|
||||
CheckBoxItem(R.string.skip_encryption, skipEncrypt, { skipEncrypt = it })
|
||||
}
|
||||
if(VERSION.SDK_INT >= 33) {
|
||||
CheckBoxItem(R.string.offline_provisioning, offlineProvisioning, { offlineProvisioning = it })
|
||||
}
|
||||
if(VERSION.SDK_INT >= 24) CheckBoxItem(R.string.skip_encryption, skipEncrypt) { skipEncrypt = it }
|
||||
if(VERSION.SDK_INT >= 33) CheckBoxItem(R.string.offline_provisioning, offlineProvisioning) { offlineProvisioning = it }
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
try {
|
||||
val intent = Intent(ACTION_PROVISION_MANAGED_PROFILE)
|
||||
if(VERSION.SDK_INT>=23) {
|
||||
if(VERSION.SDK_INT >= 23) {
|
||||
intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME,receiver)
|
||||
} else {
|
||||
intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME, context.packageName)
|
||||
@@ -147,8 +148,8 @@ fun CreateWorkProfile(navCtrl: NavHostController) {
|
||||
}
|
||||
if(VERSION.SDK_INT >= 24) { intent.putExtra(EXTRA_PROVISIONING_SKIP_ENCRYPTION, skipEncrypt) }
|
||||
if(VERSION.SDK_INT >= 33) { intent.putExtra(EXTRA_PROVISIONING_ALLOW_OFFLINE, offlineProvisioning) }
|
||||
createManagedProfile.launch(intent)
|
||||
} catch(_:ActivityNotFoundException) {
|
||||
launcher.launch(intent)
|
||||
} catch(_: ActivityNotFoundException) {
|
||||
Toast.makeText(context, R.string.unsupported, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
@@ -188,10 +189,8 @@ fun SuspendPersonalApp(navCtrl: NavHostController) {
|
||||
val focusMgr = LocalFocusManager.current
|
||||
var suspend by remember { mutableStateOf(dpm.getPersonalAppsSuspendedReasons(receiver) != PERSONAL_APPS_NOT_SUSPENDED) }
|
||||
MyScaffold(R.string.suspend_personal_app, 8.dp, navCtrl) {
|
||||
SwitchItem(
|
||||
R.string.suspend_personal_app, "", null,
|
||||
suspend,
|
||||
{
|
||||
SwitchItem(R.string.suspend_personal_app, state = suspend,
|
||||
onCheckedChange = {
|
||||
dpm.setPersonalAppsSuspended(receiver,it)
|
||||
suspend = dpm.getPersonalAppsSuspendedReasons(receiver) != PERSONAL_APPS_NOT_SUSPENDED
|
||||
}, padding = false
|
||||
@@ -217,7 +216,7 @@ fun SuspendPersonalApp(navCtrl: NavHostController) {
|
||||
Button(
|
||||
onClick = {
|
||||
dpm.setManagedProfileMaximumTimeOff(receiver,time.toLong())
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
@@ -246,7 +245,7 @@ fun IntentFilter(navCtrl: NavHostController) {
|
||||
Button(
|
||||
onClick = {
|
||||
dpm.addCrossProfileIntentFilter(receiver, IntentFilter(action), FLAG_PARENT_CAN_ACCESS_MANAGED)
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
@@ -255,7 +254,7 @@ fun IntentFilter(navCtrl: NavHostController) {
|
||||
Button(
|
||||
onClick = {
|
||||
dpm.addCrossProfileIntentFilter(receiver, IntentFilter(action), FLAG_MANAGED_CAN_ACCESS_PARENT)
|
||||
Toast.makeText(context, R.string.success,Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
@@ -265,7 +264,7 @@ fun IntentFilter(navCtrl: NavHostController) {
|
||||
Button(
|
||||
onClick = {
|
||||
dpm.clearCrossProfileIntentFilters(receiver)
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
@@ -280,15 +279,14 @@ fun DeleteWorkProfile(navCtrl: NavHostController) {
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
val focusMgr = LocalFocusManager.current
|
||||
var flag by remember { mutableIntStateOf(0) }
|
||||
var warning by remember { mutableStateOf(false) }
|
||||
var externalStorage by remember { mutableStateOf(false) }
|
||||
var euicc by remember { mutableStateOf(false) }
|
||||
var silent by remember { mutableStateOf(false) }
|
||||
var reason by remember { mutableStateOf("") }
|
||||
MyScaffold(R.string.delete_work_profile, 8.dp, navCtrl) {
|
||||
CheckBoxItem(R.string.wipe_external_storage, externalStorage, { externalStorage = it })
|
||||
if(VERSION.SDK_INT >= 28) { CheckBoxItem(R.string.wipe_euicc, euicc, { euicc = it }) }
|
||||
CheckBoxItem(R.string.wipe_silently, silent, { silent = it })
|
||||
CheckBoxItem(R.string.wipe_external_storage, flag and WIPE_EXTERNAL_STORAGE != 0) { flag = flag xor WIPE_EXTERNAL_STORAGE }
|
||||
if(VERSION.SDK_INT >= 28) CheckBoxItem(R.string.wipe_euicc, flag and WIPE_EUICC != 0) { flag = flag xor WIPE_EUICC }
|
||||
CheckBoxItem(R.string.wipe_silently, silent) { silent = it }
|
||||
AnimatedVisibility(!silent && VERSION.SDK_INT >= 28) {
|
||||
OutlinedTextField(
|
||||
value = reason, onValueChange = { reason = it },
|
||||
@@ -322,9 +320,6 @@ fun DeleteWorkProfile(navCtrl: NavHostController) {
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
var flag = 0
|
||||
if(externalStorage) { flag += WIPE_EXTERNAL_STORAGE }
|
||||
if(euicc && VERSION.SDK_INT >= 28) { flag += WIPE_EUICC }
|
||||
if(VERSION.SDK_INT >= 28 && !silent) {
|
||||
dpm.wipeData(flag, reason)
|
||||
} else {
|
||||
|
||||
@@ -124,10 +124,7 @@ import androidx.core.os.bundleOf
|
||||
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.showOperationResultToast
|
||||
import com.bintianqi.owndroid.ui.CheckBoxItem
|
||||
import com.bintianqi.owndroid.ui.FunctionItem
|
||||
@@ -157,30 +154,30 @@ fun Network(navCtrl:NavHostController) {
|
||||
val sharedPref = context.getSharedPreferences("data", Context.MODE_PRIVATE)
|
||||
val dhizuku = sharedPref.getBoolean("dhizuku", false)
|
||||
MyScaffold(R.string.network, 0.dp, navCtrl) {
|
||||
if(!dhizuku) FunctionItem(R.string.wifi, "", R.drawable.wifi_fill0) { navCtrl.navigate("Wifi") }
|
||||
if(!dhizuku) FunctionItem(R.string.wifi, icon = R.drawable.wifi_fill0) { navCtrl.navigate("Wifi") }
|
||||
if(VERSION.SDK_INT >= 30) {
|
||||
FunctionItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("NetworkOptions") }
|
||||
FunctionItem(R.string.options, icon = R.drawable.tune_fill0) { navCtrl.navigate("NetworkOptions") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 29 && deviceOwner) {
|
||||
FunctionItem(R.string.private_dns, "", R.drawable.dns_fill0) { navCtrl.navigate("PrivateDNS") }
|
||||
FunctionItem(R.string.private_dns, icon = R.drawable.dns_fill0) { navCtrl.navigate("PrivateDNS") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 24) {
|
||||
FunctionItem(R.string.always_on_vpn, "", R.drawable.vpn_key_fill0) { navCtrl.navigate("AlwaysOnVpn") }
|
||||
FunctionItem(R.string.always_on_vpn, icon = R.drawable.vpn_key_fill0) { navCtrl.navigate("AlwaysOnVpn") }
|
||||
}
|
||||
if(deviceOwner) {
|
||||
FunctionItem(R.string.recommended_global_proxy, "", R.drawable.vpn_key_fill0) { navCtrl.navigate("RecommendedGlobalProxy") }
|
||||
FunctionItem(R.string.recommended_global_proxy, icon = R.drawable.vpn_key_fill0) { navCtrl.navigate("RecommendedGlobalProxy") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 26 && !dhizuku && (deviceOwner || (profileOwner && dpm.isManagedProfile(receiver)))) {
|
||||
FunctionItem(R.string.network_logging, "", R.drawable.description_fill0) { navCtrl.navigate("NetworkLog") }
|
||||
FunctionItem(R.string.network_logging, icon = R.drawable.description_fill0) { navCtrl.navigate("NetworkLog") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 31) {
|
||||
FunctionItem(R.string.wifi_auth_keypair, "", R.drawable.key_fill0) { navCtrl.navigate("WifiAuthKeypair") }
|
||||
FunctionItem(R.string.wifi_auth_keypair, icon = R.drawable.key_fill0) { navCtrl.navigate("WifiAuthKeypair") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 33) {
|
||||
FunctionItem(R.string.preferential_network_service, "", R.drawable.globe_fill0) { navCtrl.navigate("PreferentialNetworkService") }
|
||||
FunctionItem(R.string.preferential_network_service, icon = R.drawable.globe_fill0) { navCtrl.navigate("PreferentialNetworkService") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 28 && deviceOwner) {
|
||||
FunctionItem(R.string.override_apn_settings, "", R.drawable.cell_tower_fill0) { navCtrl.navigate("OverrideAPN") }
|
||||
FunctionItem(R.string.override_apn_settings, icon = R.drawable.cell_tower_fill0) { navCtrl.navigate("OverrideAPN") }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -194,8 +191,8 @@ fun NetworkOptions(navCtrl: NavHostController) {
|
||||
var dialog by remember { mutableIntStateOf(0) }
|
||||
MyScaffold(R.string.options, 0.dp, navCtrl) {
|
||||
if(VERSION.SDK_INT>=30 && (deviceOwner || dpm.isOrgProfile(receiver))) {
|
||||
SwitchItem(R.string.lockdown_admin_configured_network, "", R.drawable.wifi_password_fill0,
|
||||
{ dpm.hasLockdownAdminConfiguredNetworks(receiver) }, { dpm.setConfiguredNetworksLockdownState(receiver,it) },
|
||||
SwitchItem(R.string.lockdown_admin_configured_network, icon = R.drawable.wifi_password_fill0,
|
||||
getState = { dpm.hasLockdownAdminConfiguredNetworks(receiver) }, onCheckedChange = { dpm.setConfiguredNetworksLockdownState(receiver,it) },
|
||||
onClickBlank = { dialog = 1 }
|
||||
)
|
||||
}
|
||||
@@ -280,11 +277,11 @@ fun Wifi(navCtrl: NavHostController) {
|
||||
}
|
||||
}
|
||||
if(VERSION.SDK_INT >= 24 && (deviceOwner || orgProfileOwner)) {
|
||||
FunctionItem(R.string.wifi_mac_address, "", null) { wifiMacDialog = true }
|
||||
FunctionItem(R.string.wifi_mac_address) { wifiMacDialog = true }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 33 && (deviceOwner || orgProfileOwner)) {
|
||||
FunctionItem(R.string.min_wifi_security_level, "", null) { navCtrl.navigate("MinWifiSecurityLevel") }
|
||||
FunctionItem(R.string.wifi_ssid_policy, "", null) { navCtrl.navigate("WifiSsidPolicy") }
|
||||
FunctionItem(R.string.min_wifi_security_level) { navCtrl.navigate("MinWifiSecurityLevel") }
|
||||
FunctionItem(R.string.wifi_ssid_policy) { navCtrl.navigate("WifiSsidPolicy") }
|
||||
}
|
||||
}
|
||||
} else if(page == 1) {
|
||||
@@ -397,8 +394,7 @@ private fun SavedNetworks(navCtrl: NavHostController) {
|
||||
) {
|
||||
Button(
|
||||
onClick = {
|
||||
val success = wm.enableNetwork(network.networkId, false)
|
||||
Toast.makeText(context, if(success) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(wm.enableNetwork(network.networkId, false))
|
||||
networkDetailsDialog = -1
|
||||
refresh()
|
||||
},
|
||||
@@ -408,8 +404,7 @@ private fun SavedNetworks(navCtrl: NavHostController) {
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
val success = wm.disableNetwork(network.networkId)
|
||||
Toast.makeText(context, if(success) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(wm.disableNetwork(network.networkId))
|
||||
networkDetailsDialog = -1
|
||||
refresh()
|
||||
},
|
||||
@@ -431,8 +426,7 @@ private fun SavedNetworks(navCtrl: NavHostController) {
|
||||
}
|
||||
TextButton(
|
||||
onClick = {
|
||||
val success = wm.removeNetwork(network.networkId)
|
||||
Toast.makeText(context, if(success) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(wm.removeNetwork(network.networkId))
|
||||
networkDetailsDialog = -1
|
||||
refresh()
|
||||
},
|
||||
@@ -526,7 +520,7 @@ private fun AddNetwork(wifiConfig: WifiConfiguration? = null, navCtrl: NavHostCo
|
||||
value = ssid, onValueChange = { ssid = it }, label = { Text("SSID") },
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 4.dp)
|
||||
)
|
||||
CheckBoxItem(R.string.hidden_ssid, hiddenSsid, { hiddenSsid = it })
|
||||
CheckBoxItem(R.string.hidden_ssid, hiddenSsid) { hiddenSsid = it }
|
||||
if(VERSION.SDK_INT >= 30) {
|
||||
// TODO: more protocols
|
||||
val securityTypeTextMap = mutableMapOf(
|
||||
@@ -731,31 +725,15 @@ fun WifiSecurityLevel(navCtrl: NavHostController) {
|
||||
var selectedWifiSecLevel by remember { mutableIntStateOf(0) }
|
||||
LaunchedEffect(Unit) { selectedWifiSecLevel = dpm.minimumRequiredWifiSecurityLevel }
|
||||
MyScaffold(R.string.min_wifi_security_level, 8.dp, navCtrl) {
|
||||
RadioButtonItem(
|
||||
R.string.wifi_security_open,
|
||||
selectedWifiSecLevel == WIFI_SECURITY_OPEN,
|
||||
{ selectedWifiSecLevel = WIFI_SECURITY_OPEN }
|
||||
)
|
||||
RadioButtonItem(
|
||||
"WEP, WPA(2)-PSK",
|
||||
selectedWifiSecLevel == WIFI_SECURITY_PERSONAL,
|
||||
{ selectedWifiSecLevel = WIFI_SECURITY_PERSONAL }
|
||||
)
|
||||
RadioButtonItem(
|
||||
"WPA-EAP",
|
||||
selectedWifiSecLevel == WIFI_SECURITY_ENTERPRISE_EAP,
|
||||
{ selectedWifiSecLevel = WIFI_SECURITY_ENTERPRISE_EAP }
|
||||
)
|
||||
RadioButtonItem(
|
||||
"WPA3-192bit",
|
||||
selectedWifiSecLevel == WIFI_SECURITY_ENTERPRISE_192,
|
||||
{ selectedWifiSecLevel = WIFI_SECURITY_ENTERPRISE_192 }
|
||||
)
|
||||
RadioButtonItem(R.string.wifi_security_open, selectedWifiSecLevel == WIFI_SECURITY_OPEN) { selectedWifiSecLevel = WIFI_SECURITY_OPEN }
|
||||
RadioButtonItem("WEP, WPA(2)-PSK", selectedWifiSecLevel == WIFI_SECURITY_PERSONAL) { selectedWifiSecLevel = WIFI_SECURITY_PERSONAL }
|
||||
RadioButtonItem("WPA-EAP", selectedWifiSecLevel == WIFI_SECURITY_ENTERPRISE_EAP) { selectedWifiSecLevel = WIFI_SECURITY_ENTERPRISE_EAP }
|
||||
RadioButtonItem("WPA3-192bit", selectedWifiSecLevel == WIFI_SECURITY_ENTERPRISE_192) { selectedWifiSecLevel = WIFI_SECURITY_ENTERPRISE_192 }
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
dpm.minimumRequiredWifiSecurityLevel = selectedWifiSecLevel
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
@@ -781,21 +759,13 @@ fun WifiSsidPolicy(navCtrl: NavHostController) {
|
||||
ssidList.addAll(policy?.ssids ?: mutableSetOf())
|
||||
}
|
||||
LaunchedEffect(Unit) { refreshPolicy() }
|
||||
RadioButtonItem(
|
||||
R.string.none,
|
||||
selectedPolicyType == -1,
|
||||
{ selectedPolicyType = -1 }
|
||||
)
|
||||
RadioButtonItem(
|
||||
R.string.whitelist,
|
||||
selectedPolicyType == WIFI_SSID_POLICY_TYPE_ALLOWLIST,
|
||||
{ selectedPolicyType = WIFI_SSID_POLICY_TYPE_ALLOWLIST }
|
||||
)
|
||||
RadioButtonItem(
|
||||
R.string.blacklist,
|
||||
selectedPolicyType == WIFI_SSID_POLICY_TYPE_DENYLIST,
|
||||
{ selectedPolicyType = WIFI_SSID_POLICY_TYPE_DENYLIST }
|
||||
)
|
||||
RadioButtonItem(R.string.none, selectedPolicyType == -1) { selectedPolicyType = -1 }
|
||||
RadioButtonItem(R.string.whitelist, selectedPolicyType == WIFI_SSID_POLICY_TYPE_ALLOWLIST) {
|
||||
selectedPolicyType = WIFI_SSID_POLICY_TYPE_ALLOWLIST
|
||||
}
|
||||
RadioButtonItem(R.string.blacklist, selectedPolicyType == WIFI_SSID_POLICY_TYPE_DENYLIST) {
|
||||
selectedPolicyType = WIFI_SSID_POLICY_TYPE_DENYLIST
|
||||
}
|
||||
AnimatedVisibility(selectedPolicyType != -1) {
|
||||
var inputSsid by remember { mutableStateOf("") }
|
||||
Column {
|
||||
@@ -838,7 +808,7 @@ fun WifiSsidPolicy(navCtrl: NavHostController) {
|
||||
WifiSsidPolicy(selectedPolicyType, ssidList.toSet())
|
||||
}
|
||||
refreshPolicy()
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
@@ -940,7 +910,7 @@ fun AlwaysOnVPNPackage(navCtrl: NavHostController, vm: MyViewModel) {
|
||||
val setAlwaysOnVpn: (String?, Boolean)->Boolean = { vpnPkg: String?, lockdownEnabled: Boolean ->
|
||||
try {
|
||||
dpm.setAlwaysOnVpnPackage(receiver, vpnPkg, lockdownEnabled)
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
true
|
||||
} catch(e: UnsupportedOperationException) {
|
||||
e.printStackTrace()
|
||||
@@ -971,7 +941,7 @@ fun AlwaysOnVPNPackage(navCtrl: NavHostController, vm: MyViewModel) {
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp)
|
||||
)
|
||||
SwitchItem(R.string.enable_lockdown, "", null, lockdown, { lockdown = it }, padding = false)
|
||||
SwitchItem(R.string.enable_lockdown, state = lockdown, onCheckedChange = { lockdown = it }, padding = false)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = { if(setAlwaysOnVpn(pkgName, lockdown)) refresh() },
|
||||
@@ -1002,9 +972,9 @@ fun RecommendedGlobalProxy(navCtrl: NavHostController) {
|
||||
var proxyPort by remember { mutableStateOf("") }
|
||||
var exclList by remember { mutableStateOf("") }
|
||||
MyScaffold(R.string.recommended_global_proxy, 8.dp, navCtrl) {
|
||||
RadioButtonItem(R.string.proxy_type_off, proxyType == 0, { proxyType = 0 })
|
||||
RadioButtonItem(R.string.proxy_type_pac, proxyType == 1, { proxyType = 1 })
|
||||
RadioButtonItem(R.string.proxy_type_direct, proxyType == 2, { proxyType = 2 })
|
||||
RadioButtonItem(R.string.proxy_type_off, proxyType == 0) { proxyType = 0 }
|
||||
RadioButtonItem(R.string.proxy_type_pac, proxyType == 1) { proxyType = 1 }
|
||||
RadioButtonItem(R.string.proxy_type_direct, proxyType == 2) { proxyType = 2 }
|
||||
AnimatedVisibility(proxyType != 0) {
|
||||
OutlinedTextField(
|
||||
value = proxyUri,
|
||||
@@ -1017,7 +987,7 @@ fun RecommendedGlobalProxy(navCtrl: NavHostController) {
|
||||
}
|
||||
AnimatedVisibility(proxyType == 1 && VERSION.SDK_INT >= 30) {
|
||||
Box(modifier = Modifier.padding(top = 2.dp)) {
|
||||
CheckBoxItem(R.string.specify_port, specifyPort, { specifyPort = it })
|
||||
CheckBoxItem(R.string.specify_port, specifyPort) { specifyPort = it }
|
||||
}
|
||||
}
|
||||
AnimatedVisibility((proxyType == 1 && specifyPort && VERSION.SDK_INT >= 30) || proxyType == 2) {
|
||||
@@ -1045,7 +1015,7 @@ fun RecommendedGlobalProxy(navCtrl: NavHostController) {
|
||||
onClick = {
|
||||
if(proxyType == 0) {
|
||||
dpm.setRecommendedGlobalProxy(receiver, null)
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
return@Button
|
||||
}
|
||||
if(proxyUri == "") {
|
||||
@@ -1076,7 +1046,7 @@ fun RecommendedGlobalProxy(navCtrl: NavHostController) {
|
||||
return@Button
|
||||
}
|
||||
dpm.setRecommendedGlobalProxy(receiver, proxyInfo)
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
@@ -1094,11 +1064,24 @@ fun NetworkLogging(navCtrl: NavHostController) {
|
||||
val receiver = context.getReceiver()
|
||||
val logFile = context.filesDir.resolve("NetworkLogs.json")
|
||||
var fileSize by remember { mutableLongStateOf(0) }
|
||||
LaunchedEffect(Unit) {
|
||||
fileSize = logFile.length()
|
||||
LaunchedEffect(Unit) { fileSize = logFile.length() }
|
||||
val exportNetworkLogsLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
result.data?.data?.let { uri ->
|
||||
context.contentResolver.openOutputStream(uri)?.use { outStream ->
|
||||
outStream.write("[".encodeToByteArray())
|
||||
logFile.inputStream().use { it.copyTo(outStream) }
|
||||
outStream.write("]".encodeToByteArray())
|
||||
context.showOperationResultToast(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
MyScaffold(R.string.network_logging, 8.dp, navCtrl) {
|
||||
SwitchItem(R.string.enable, "", null, { dpm.isNetworkLoggingEnabled(receiver) }, { dpm.setNetworkLoggingEnabled(receiver,it) }, padding = false)
|
||||
SwitchItem(
|
||||
R.string.enable,
|
||||
getState = { dpm.isNetworkLoggingEnabled(receiver) },
|
||||
onCheckedChange = { dpm.setNetworkLoggingEnabled(receiver,it) },
|
||||
padding = false
|
||||
)
|
||||
Text(stringResource(R.string.log_file_size_is, formatFileSize(fileSize)))
|
||||
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
|
||||
Button(
|
||||
@@ -1107,9 +1090,7 @@ fun NetworkLogging(navCtrl: NavHostController) {
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
intent.setType("application/json")
|
||||
intent.putExtra(Intent.EXTRA_TITLE, "NetworkLogs.json")
|
||||
exportFilePath = logFile.path
|
||||
isExportingSecurityOrNetworkLogs = true
|
||||
exportFile.launch(intent)
|
||||
exportNetworkLogsLauncher.launch(intent)
|
||||
},
|
||||
enabled = fileSize > 0,
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
@@ -1158,19 +1139,13 @@ fun WifiAuthKeypair(navCtrl: NavHostController) {
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
Button(
|
||||
onClick = {
|
||||
val result = dpm.grantKeyPairToWifiAuth(keyPair)
|
||||
Toast.makeText(context, if(result) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
onClick = { context.showOperationResultToast(dpm.grantKeyPairToWifiAuth(keyPair)) },
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
) {
|
||||
Text(stringResource(R.string.grant))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
val result = dpm.revokeKeyPairFromWifiAuth(keyPair)
|
||||
Toast.makeText(context, if(result) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
onClick = { context.showOperationResultToast(dpm.revokeKeyPairFromWifiAuth(keyPair)) },
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
Text(stringResource(R.string.revoke))
|
||||
@@ -1221,10 +1196,7 @@ fun PreferentialNetworkService(navCtrl: NavHostController) {
|
||||
}
|
||||
LaunchedEffect(Unit) { initialize() }
|
||||
MyScaffold(R.string.preferential_network_service, 8.dp, navCtrl) {
|
||||
SwitchItem(
|
||||
title = R.string.enabled, desc = "", icon = null,
|
||||
state = masterEnabled, onCheckedChange = { masterEnabled = it }, padding = false
|
||||
)
|
||||
SwitchItem(R.string.enabled, state = masterEnabled, onCheckedChange = { masterEnabled = it }, padding = false)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceAround,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
@@ -1272,7 +1244,7 @@ fun PreferentialNetworkService(navCtrl: NavHostController) {
|
||||
onClick = {
|
||||
try {
|
||||
saveCurrentConfig()
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
} catch(e: Exception) {
|
||||
e.printStackTrace()
|
||||
Toast.makeText(context, R.string.failed_to_save_current_config, Toast.LENGTH_SHORT).show()
|
||||
@@ -1292,10 +1264,7 @@ fun PreferentialNetworkService(navCtrl: NavHostController) {
|
||||
Icon(imageVector = Icons.Default.Delete, contentDescription = stringResource(R.string.delete_current_config))
|
||||
}
|
||||
}
|
||||
SwitchItem(
|
||||
title = R.string.enabled, desc = "", icon = null,
|
||||
state = enabled, onCheckedChange = { enabled = it }, padding = false
|
||||
)
|
||||
SwitchItem(title = R.string.enabled, state = enabled, onCheckedChange = { enabled = it }, padding = false)
|
||||
OutlinedTextField(
|
||||
value = networkId, onValueChange = { networkId = it },
|
||||
label = { Text(stringResource(R.string.network_id)) },
|
||||
@@ -1304,11 +1273,11 @@ fun PreferentialNetworkService(navCtrl: NavHostController) {
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 6.dp)
|
||||
)
|
||||
SwitchItem(
|
||||
title = R.string.allow_fallback_to_default_connection, desc = "", icon = null,
|
||||
title = R.string.allow_fallback_to_default_connection,
|
||||
state = allowFallback, onCheckedChange = { allowFallback = it }, padding = false
|
||||
)
|
||||
if(VERSION.SDK_INT >= 34) SwitchItem(
|
||||
title = R.string.block_non_matching_networks, desc = "", icon = null,
|
||||
title = R.string.block_non_matching_networks,
|
||||
state = blockNonMatching, onCheckedChange = { blockNonMatching = it }, padding = false
|
||||
)
|
||||
OutlinedTextField(
|
||||
@@ -1328,7 +1297,7 @@ fun PreferentialNetworkService(navCtrl: NavHostController) {
|
||||
dpm.isPreferentialNetworkServiceEnabled = masterEnabled
|
||||
dpm.preferentialNetworkServiceConfigs = configs
|
||||
initialize()
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 12.dp)
|
||||
) {
|
||||
@@ -1351,7 +1320,11 @@ fun OverrideAPN(navCtrl: NavHostController) {
|
||||
MyScaffold(R.string.override_apn_settings, 8.dp, navCtrl) {
|
||||
Text(text = stringResource(id = R.string.developing))
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
SwitchItem(R.string.enable, "", null, { dpm.isOverrideApnEnabled(receiver) }, { dpm.setOverrideApnsEnabled(receiver,it) }, padding = false)
|
||||
SwitchItem(
|
||||
R.string.enable,
|
||||
getState = { dpm.isOverrideApnEnabled(receiver) }, onCheckedChange = { dpm.setOverrideApnsEnabled(receiver,it) },
|
||||
padding = false
|
||||
)
|
||||
Text(text = stringResource(R.string.total_apn_amount, setting.size))
|
||||
if(setting.isNotEmpty()) {
|
||||
Text(text = stringResource(R.string.select_a_apn_or_create, setting.size))
|
||||
@@ -1468,11 +1441,11 @@ fun OverrideAPN(navCtrl: NavHostController) {
|
||||
}
|
||||
|
||||
Text(text = stringResource(R.string.auth_type), style = typography.titleLarge)
|
||||
RadioButtonItem(R.string.none, selectedAuthType==AUTH_TYPE_NONE , { selectedAuthType=AUTH_TYPE_NONE })
|
||||
RadioButtonItem("CHAP", selectedAuthType == AUTH_TYPE_CHAP , { selectedAuthType = AUTH_TYPE_CHAP })
|
||||
RadioButtonItem("PAP", selectedAuthType == AUTH_TYPE_PAP, { selectedAuthType = AUTH_TYPE_PAP })
|
||||
RadioButtonItem("PAP/CHAP", selectedAuthType == AUTH_TYPE_PAP_OR_CHAP, { selectedAuthType = AUTH_TYPE_PAP_OR_CHAP })
|
||||
|
||||
RadioButtonItem(R.string.none, selectedAuthType==AUTH_TYPE_NONE) { selectedAuthType = AUTH_TYPE_NONE }
|
||||
RadioButtonItem("CHAP", selectedAuthType == AUTH_TYPE_CHAP) { selectedAuthType = AUTH_TYPE_CHAP }
|
||||
RadioButtonItem("PAP", selectedAuthType == AUTH_TYPE_PAP) { selectedAuthType = AUTH_TYPE_PAP }
|
||||
RadioButtonItem("PAP/CHAP", selectedAuthType == AUTH_TYPE_PAP_OR_CHAP) { selectedAuthType = AUTH_TYPE_PAP_OR_CHAP }
|
||||
|
||||
if(VERSION.SDK_INT>=29) {
|
||||
val ts = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
|
||||
carrierId = ts.simCarrierId.toString()
|
||||
@@ -1578,11 +1551,11 @@ fun OverrideAPN(navCtrl: NavHostController) {
|
||||
}
|
||||
|
||||
Text(text = "MVNO", style = typography.titleLarge)
|
||||
RadioButtonItem("SPN", mvnoType == MVNO_TYPE_SPN, { mvnoType = MVNO_TYPE_SPN })
|
||||
RadioButtonItem("IMSI", mvnoType == MVNO_TYPE_IMSI, { mvnoType = MVNO_TYPE_IMSI })
|
||||
RadioButtonItem("GID", mvnoType == MVNO_TYPE_GID, { mvnoType = MVNO_TYPE_GID })
|
||||
RadioButtonItem("ICCID", mvnoType == MVNO_TYPE_ICCID, { mvnoType = MVNO_TYPE_ICCID })
|
||||
|
||||
RadioButtonItem("SPN", mvnoType == MVNO_TYPE_SPN) { mvnoType = MVNO_TYPE_SPN }
|
||||
RadioButtonItem("IMSI", mvnoType == MVNO_TYPE_IMSI) { mvnoType = MVNO_TYPE_IMSI }
|
||||
RadioButtonItem("GID", mvnoType == MVNO_TYPE_GID) { mvnoType = MVNO_TYPE_GID }
|
||||
RadioButtonItem("ICCID", mvnoType == MVNO_TYPE_ICCID) { mvnoType = MVNO_TYPE_ICCID }
|
||||
|
||||
Text(text = stringResource(R.string.network_type), style = typography.titleLarge)
|
||||
TextField(
|
||||
value = networkTypeBitmask,
|
||||
@@ -1625,23 +1598,23 @@ fun OverrideAPN(navCtrl: NavHostController) {
|
||||
}
|
||||
|
||||
Text(text = stringResource(R.string.protocol), style = typography.titleLarge)
|
||||
RadioButtonItem("IPV4", protocol == PROTOCOL_IP, { protocol = PROTOCOL_IP })
|
||||
RadioButtonItem("IPV6", protocol == PROTOCOL_IPV6, { protocol = PROTOCOL_IPV6 })
|
||||
RadioButtonItem("IPV4/IPV6", protocol == PROTOCOL_IPV4V6, { protocol = PROTOCOL_IPV4V6 })
|
||||
RadioButtonItem("PPP", protocol == PROTOCOL_PPP, { protocol = PROTOCOL_PPP })
|
||||
RadioButtonItem("IPV4", protocol == PROTOCOL_IP) { protocol = PROTOCOL_IP }
|
||||
RadioButtonItem("IPV6", protocol == PROTOCOL_IPV6) { protocol = PROTOCOL_IPV6 }
|
||||
RadioButtonItem("IPV4/IPV6", protocol == PROTOCOL_IPV4V6) { protocol = PROTOCOL_IPV4V6 }
|
||||
RadioButtonItem("PPP", protocol == PROTOCOL_PPP) { protocol = PROTOCOL_PPP }
|
||||
if(VERSION.SDK_INT>=29) {
|
||||
RadioButtonItem("non-IP", protocol == PROTOCOL_NON_IP, { protocol = PROTOCOL_NON_IP })
|
||||
RadioButtonItem("Unstructured", protocol == PROTOCOL_UNSTRUCTURED, { protocol = PROTOCOL_UNSTRUCTURED })
|
||||
RadioButtonItem("non-IP", protocol == PROTOCOL_NON_IP) { protocol = PROTOCOL_NON_IP }
|
||||
RadioButtonItem("Unstructured", protocol == PROTOCOL_UNSTRUCTURED) { protocol = PROTOCOL_UNSTRUCTURED }
|
||||
}
|
||||
|
||||
Text(text = stringResource(R.string.roaming_protocol), style = typography.titleLarge)
|
||||
RadioButtonItem("IPV4", roamingProtocol == PROTOCOL_IP, { roamingProtocol = PROTOCOL_IP })
|
||||
RadioButtonItem("IPV6", roamingProtocol == PROTOCOL_IPV6, { roamingProtocol = PROTOCOL_IPV6 })
|
||||
RadioButtonItem("IPV4/IPV6", roamingProtocol == PROTOCOL_IPV4V6, { roamingProtocol = PROTOCOL_IPV4V6 })
|
||||
RadioButtonItem("PPP", roamingProtocol == PROTOCOL_PPP, { roamingProtocol = PROTOCOL_PPP})
|
||||
RadioButtonItem("IPV4", roamingProtocol == PROTOCOL_IP) { roamingProtocol = PROTOCOL_IP }
|
||||
RadioButtonItem("IPV6", roamingProtocol == PROTOCOL_IPV6) { roamingProtocol = PROTOCOL_IPV6 }
|
||||
RadioButtonItem("IPV4/IPV6", roamingProtocol == PROTOCOL_IPV4V6) { roamingProtocol = PROTOCOL_IPV4V6 }
|
||||
RadioButtonItem("PPP", roamingProtocol == PROTOCOL_PPP) { roamingProtocol = PROTOCOL_PPP }
|
||||
if(VERSION.SDK_INT>=29) {
|
||||
RadioButtonItem("non-IP", roamingProtocol == PROTOCOL_NON_IP, { roamingProtocol = PROTOCOL_NON_IP })
|
||||
RadioButtonItem("Unstructured", roamingProtocol == PROTOCOL_UNSTRUCTURED, { roamingProtocol = PROTOCOL_UNSTRUCTURED })
|
||||
RadioButtonItem("non-IP", roamingProtocol == PROTOCOL_NON_IP) { roamingProtocol = PROTOCOL_NON_IP }
|
||||
RadioButtonItem("Unstructured", roamingProtocol == PROTOCOL_UNSTRUCTURED) { roamingProtocol = PROTOCOL_UNSTRUCTURED }
|
||||
}
|
||||
|
||||
var finalStep by remember { mutableStateOf(false) }
|
||||
@@ -1688,20 +1661,14 @@ fun OverrideAPN(navCtrl: NavHostController) {
|
||||
}else{
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
Button(
|
||||
onClick = {
|
||||
val success = dpm.updateOverrideApn(receiver,id,result)
|
||||
Toast.makeText(context, if(success) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
Modifier.fillMaxWidth(0.49F)
|
||||
onClick = { context.showOperationResultToast(dpm.updateOverrideApn(receiver, id, result)) },
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
) {
|
||||
Text(stringResource(R.string.update))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
val success = dpm.removeOverrideApn(receiver,id)
|
||||
Toast.makeText(context, if(success) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
Modifier.fillMaxWidth(0.96F)
|
||||
onClick = { context.showOperationResultToast(dpm.removeOverrideApn(receiver,id)) },
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
Text(stringResource(R.string.remove))
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL
|
||||
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE
|
||||
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT
|
||||
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_IRIS
|
||||
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT
|
||||
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA
|
||||
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS
|
||||
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL
|
||||
@@ -55,7 +54,6 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
@@ -70,7 +68,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
import androidx.navigation.NavHostController
|
||||
import com.bintianqi.owndroid.R
|
||||
import com.bintianqi.owndroid.toggle
|
||||
import com.bintianqi.owndroid.showOperationResultToast
|
||||
import com.bintianqi.owndroid.ui.CardItem
|
||||
import com.bintianqi.owndroid.ui.CheckBoxItem
|
||||
import com.bintianqi.owndroid.ui.FunctionItem
|
||||
@@ -89,34 +87,34 @@ fun Password(navCtrl: NavHostController) {
|
||||
val profileOwner = context.isProfileOwner
|
||||
var dialog by remember { mutableIntStateOf(0) }
|
||||
MyScaffold(R.string.password_and_keyguard, 0.dp, navCtrl) {
|
||||
FunctionItem(R.string.password_info, "", R.drawable.info_fill0) { navCtrl.navigate("PasswordInfo") }
|
||||
FunctionItem(R.string.password_info, icon = R.drawable.info_fill0) { navCtrl.navigate("PasswordInfo") }
|
||||
if(sharedPrefs.getBoolean("dangerous_features", false)) {
|
||||
if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) {
|
||||
FunctionItem(R.string.reset_password_token, "", R.drawable.key_vertical_fill0) { navCtrl.navigate("ResetPasswordToken") }
|
||||
FunctionItem(R.string.reset_password_token, icon = R.drawable.key_vertical_fill0) { navCtrl.navigate("ResetPasswordToken") }
|
||||
}
|
||||
if(deviceAdmin || deviceOwner || profileOwner) {
|
||||
FunctionItem(R.string.reset_password, "", R.drawable.lock_reset_fill0) { navCtrl.navigate("ResetPassword") }
|
||||
FunctionItem(R.string.reset_password, icon = R.drawable.lock_reset_fill0) { navCtrl.navigate("ResetPassword") }
|
||||
}
|
||||
}
|
||||
if(VERSION.SDK_INT >= 31 && (deviceOwner || profileOwner)) {
|
||||
FunctionItem(R.string.required_password_complexity, "", R.drawable.password_fill0) { navCtrl.navigate("RequirePasswordComplexity") }
|
||||
FunctionItem(R.string.required_password_complexity, icon = R.drawable.password_fill0) { navCtrl.navigate("RequirePasswordComplexity") }
|
||||
}
|
||||
if(deviceAdmin) {
|
||||
FunctionItem(R.string.disable_keyguard_features, "", R.drawable.screen_lock_portrait_fill0) { navCtrl.navigate("DisableKeyguardFeatures") }
|
||||
FunctionItem(R.string.disable_keyguard_features, icon = R.drawable.screen_lock_portrait_fill0) { navCtrl.navigate("DisableKeyguardFeatures") }
|
||||
}
|
||||
if(deviceOwner) {
|
||||
FunctionItem(R.string.max_time_to_lock, "", R.drawable.schedule_fill0) { dialog = 1 }
|
||||
FunctionItem(R.string.pwd_expiration_timeout, "", R.drawable.lock_clock_fill0) { dialog = 3 }
|
||||
FunctionItem(R.string.max_pwd_fail, "", R.drawable.no_encryption_fill0) { dialog = 4 }
|
||||
FunctionItem(R.string.max_time_to_lock, icon = R.drawable.schedule_fill0) { dialog = 1 }
|
||||
FunctionItem(R.string.pwd_expiration_timeout, icon = R.drawable.lock_clock_fill0) { dialog = 3 }
|
||||
FunctionItem(R.string.max_pwd_fail, icon = R.drawable.no_encryption_fill0) { dialog = 4 }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) {
|
||||
FunctionItem(R.string.required_strong_auth_timeout, "", R.drawable.fingerprint_off_fill0) { dialog = 2 }
|
||||
FunctionItem(R.string.required_strong_auth_timeout, icon = R.drawable.fingerprint_off_fill0) { dialog = 2 }
|
||||
}
|
||||
if(deviceAdmin){
|
||||
FunctionItem(R.string.pwd_history, "", R.drawable.history_fill0) { dialog = 5 }
|
||||
FunctionItem(R.string.pwd_history, icon = R.drawable.history_fill0) { dialog = 5 }
|
||||
}
|
||||
if(VERSION.SDK_INT < 31 && (deviceOwner || profileOwner)) {
|
||||
FunctionItem(R.string.required_password_quality, "", R.drawable.password_fill0) { navCtrl.navigate("RequirePasswordQuality") }
|
||||
FunctionItem(R.string.required_password_quality, icon = R.drawable.password_fill0) { navCtrl.navigate("RequirePasswordQuality") }
|
||||
}
|
||||
}
|
||||
if(dialog != 0) {
|
||||
@@ -259,12 +257,8 @@ fun ResetPasswordToken(navCtrl: NavHostController) {
|
||||
Button(
|
||||
onClick = {
|
||||
try {
|
||||
Toast.makeText(
|
||||
context,
|
||||
if(dpm.setResetPasswordToken(receiver, tokenByteArray)) R.string.success else R.string.failed,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}catch(_:SecurityException) {
|
||||
context.showOperationResultToast(dpm.setResetPasswordToken(receiver, tokenByteArray))
|
||||
} catch(_:SecurityException) {
|
||||
Toast.makeText(context, R.string.security_exception, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
@@ -289,13 +283,7 @@ fun ResetPasswordToken(navCtrl: NavHostController) {
|
||||
Text(stringResource(R.string.activate))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
Toast.makeText(
|
||||
context,
|
||||
if(dpm.clearResetPasswordToken(receiver)) R.string.success else R.string.failed,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
},
|
||||
onClick = { context.showOperationResultToast(dpm.clearResetPasswordToken(receiver)) },
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
Text(stringResource(R.string.clear))
|
||||
@@ -316,7 +304,7 @@ fun ResetPassword(navCtrl: NavHostController) {
|
||||
var useToken by remember { mutableStateOf(false) }
|
||||
var token by remember { mutableStateOf("") }
|
||||
val tokenByteArray = token.toByteArray()
|
||||
val flags = remember { mutableStateListOf<Int>() }
|
||||
var flag by remember { mutableIntStateOf(0) }
|
||||
var confirmDialog by remember { mutableStateOf(false) }
|
||||
MyScaffold(R.string.reset_password, 8.dp, navCtrl) {
|
||||
if(VERSION.SDK_INT >= 26) {
|
||||
@@ -342,15 +330,13 @@ fun ResetPassword(navCtrl: NavHostController) {
|
||||
if(VERSION.SDK_INT >= 23) {
|
||||
CheckBoxItem(
|
||||
R.string.do_not_ask_credentials_on_boot,
|
||||
RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT in flags,
|
||||
{ flags.toggle(it, RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT) }
|
||||
)
|
||||
flag and RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT != 0
|
||||
) { flag = flag xor RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT }
|
||||
}
|
||||
CheckBoxItem(
|
||||
R.string.reset_password_require_entry,
|
||||
RESET_PASSWORD_REQUIRE_ENTRY in flags,
|
||||
{ flags.toggle(it, RESET_PASSWORD_REQUIRE_ENTRY) }
|
||||
)
|
||||
flag and RESET_PASSWORD_REQUIRE_ENTRY != 0
|
||||
) { flag = flag xor RESET_PASSWORD_REQUIRE_ENTRY }
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
if(VERSION.SDK_INT >= 26) {
|
||||
Button(
|
||||
@@ -402,14 +388,12 @@ fun ResetPassword(navCtrl: NavHostController) {
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
var resetFlag = 0
|
||||
flags.forEach { resetFlag += it }
|
||||
val success = if(VERSION.SDK_INT >= 26 && useToken) {
|
||||
dpm.resetPasswordWithToken(receiver, password, tokenByteArray, resetFlag)
|
||||
dpm.resetPasswordWithToken(receiver, password, tokenByteArray, flag)
|
||||
} else {
|
||||
dpm.resetPassword(password, resetFlag)
|
||||
dpm.resetPassword(password, flag)
|
||||
}
|
||||
Toast.makeText(context, if(success) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(success)
|
||||
password = ""
|
||||
confirmDialog = false
|
||||
},
|
||||
@@ -443,13 +427,13 @@ fun PasswordComplexity(navCtrl: NavHostController) {
|
||||
LaunchedEffect(Unit) { selectedItem = dpm.requiredPasswordComplexity }
|
||||
MyScaffold(R.string.required_password_complexity, 8.dp, navCtrl) {
|
||||
passwordComplexity.forEach {
|
||||
RadioButtonItem(it.value, selectedItem == it.key, { selectedItem = it.key })
|
||||
RadioButtonItem(it.value, selectedItem == it.key) { selectedItem = it.key }
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
dpm.requiredPasswordComplexity = selectedItem
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
@@ -470,86 +454,52 @@ fun DisableKeyguardFeatures(navCtrl: NavHostController) {
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
val receiver = context.getReceiver()
|
||||
var state by remember { mutableIntStateOf(-1) }
|
||||
var shortcuts by remember { mutableStateOf(false) }
|
||||
var biometrics by remember { mutableStateOf(false) }
|
||||
var iris by remember { mutableStateOf(false) }
|
||||
var face by remember { mutableStateOf(false) }
|
||||
var remote by remember { mutableStateOf(false) }
|
||||
var fingerprint by remember { mutableStateOf(false) }
|
||||
var agents by remember { mutableStateOf(false) }
|
||||
var unredacted by remember { mutableStateOf(false) }
|
||||
var notification by remember { mutableStateOf(false) }
|
||||
var camera by remember { mutableStateOf(false) }
|
||||
var widgets by remember { mutableStateOf(false) }
|
||||
val calculateCustomFeature = {
|
||||
var calculate = dpm.getKeyguardDisabledFeatures(receiver)
|
||||
if(calculate==0) {state=0}
|
||||
else{
|
||||
if(calculate-KEYGUARD_DISABLE_SHORTCUTS_ALL >= 0 && VERSION.SDK_INT >= 34) { shortcuts=true; calculate-= KEYGUARD_DISABLE_SHORTCUTS_ALL }
|
||||
if(calculate-KEYGUARD_DISABLE_BIOMETRICS >= 0 && VERSION.SDK_INT >= 28) { biometrics=true; calculate -= KEYGUARD_DISABLE_BIOMETRICS }
|
||||
if(calculate-KEYGUARD_DISABLE_IRIS >= 0 && VERSION.SDK_INT >= 28) { iris=true; calculate -= KEYGUARD_DISABLE_IRIS }
|
||||
if(calculate-KEYGUARD_DISABLE_FACE >= 0 && VERSION.SDK_INT >= 28) { face=true; calculate -= KEYGUARD_DISABLE_FACE }
|
||||
if(calculate-KEYGUARD_DISABLE_REMOTE_INPUT >= 0 && VERSION.SDK_INT >= 24) { remote=true; calculate -= KEYGUARD_DISABLE_REMOTE_INPUT }
|
||||
if(calculate-KEYGUARD_DISABLE_FINGERPRINT >= 0) { fingerprint=true; calculate -= KEYGUARD_DISABLE_FINGERPRINT }
|
||||
if(calculate-KEYGUARD_DISABLE_TRUST_AGENTS >= 0) { agents=true; calculate -= KEYGUARD_DISABLE_TRUST_AGENTS }
|
||||
if(calculate-KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS >= 0) { unredacted=true; calculate -= KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS }
|
||||
if(calculate-KEYGUARD_DISABLE_SECURE_NOTIFICATIONS >= 0) { notification=true; calculate -= KEYGUARD_DISABLE_SECURE_NOTIFICATIONS }
|
||||
if(calculate-KEYGUARD_DISABLE_SECURE_CAMERA >= 0) { camera=true; calculate -= KEYGUARD_DISABLE_SECURE_CAMERA }
|
||||
if(calculate-KEYGUARD_DISABLE_WIDGETS_ALL >= 0) { widgets=true; calculate -= KEYGUARD_DISABLE_WIDGETS_ALL }
|
||||
}
|
||||
var flag by remember { mutableIntStateOf(0) }
|
||||
var mode by remember { mutableIntStateOf(0) } // 0:Enable all, 1:Disable all, 2:Custom
|
||||
val flagsLiat = mutableListOf(
|
||||
R.string.disable_keyguard_features_widgets to KEYGUARD_DISABLE_WIDGETS_ALL,
|
||||
R.string.disable_keyguard_features_camera to KEYGUARD_DISABLE_SECURE_CAMERA,
|
||||
R.string.disable_keyguard_features_notification to KEYGUARD_DISABLE_SECURE_NOTIFICATIONS,
|
||||
R.string.disable_keyguard_features_unredacted_notification to KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS,
|
||||
R.string.disable_keyguard_features_trust_agents to KEYGUARD_DISABLE_TRUST_AGENTS,
|
||||
R.string.disable_keyguard_features_fingerprint to KEYGUARD_DISABLE_FINGERPRINT
|
||||
)
|
||||
if(VERSION.SDK_INT >= 28) {
|
||||
flagsLiat +=R.string.disable_keyguard_features_face to KEYGUARD_DISABLE_FACE
|
||||
flagsLiat += R.string.disable_keyguard_features_iris to KEYGUARD_DISABLE_IRIS
|
||||
flagsLiat += R.string.disable_keyguard_features_biometrics to KEYGUARD_DISABLE_BIOMETRICS
|
||||
}
|
||||
if(state==-1) {
|
||||
state = when(dpm.getKeyguardDisabledFeatures(receiver)) {
|
||||
if(VERSION.SDK_INT >= 34) flagsLiat += R.string.disable_keyguard_features_shortcuts to KEYGUARD_DISABLE_SHORTCUTS_ALL
|
||||
fun refresh() {
|
||||
flag = dpm.getKeyguardDisabledFeatures(receiver)
|
||||
mode = when(flag) {
|
||||
KEYGUARD_DISABLE_FEATURES_NONE -> 0
|
||||
KEYGUARD_DISABLE_FEATURES_ALL -> 1
|
||||
else -> 2
|
||||
}
|
||||
calculateCustomFeature()
|
||||
}
|
||||
LaunchedEffect(mode) { if(mode != 2) flag = dpm.getKeyguardDisabledFeatures(receiver) }
|
||||
LaunchedEffect(Unit) { refresh() }
|
||||
MyScaffold(R.string.disable_keyguard_features, 8.dp, navCtrl) {
|
||||
RadioButtonItem(R.string.enable_all, state == 0, { state = 0 })
|
||||
RadioButtonItem(R.string.disable_all, state == 1, { state = 1 })
|
||||
RadioButtonItem(R.string.custom, state == 2 , { state = 2 })
|
||||
AnimatedVisibility(state==2) {
|
||||
RadioButtonItem(R.string.enable_all, mode == 0) { mode = 0 }
|
||||
RadioButtonItem(R.string.disable_all, mode == 1) { mode = 1 }
|
||||
RadioButtonItem(R.string.custom, mode == 2) { mode = 2 }
|
||||
AnimatedVisibility(mode == 2) {
|
||||
Column {
|
||||
CheckBoxItem(R.string.disable_keyguard_features_widgets, widgets, { widgets = it })
|
||||
CheckBoxItem(R.string.disable_keyguard_features_camera, camera, { camera = it })
|
||||
CheckBoxItem(R.string.disable_keyguard_features_notification, notification, { notification = it })
|
||||
CheckBoxItem(R.string.disable_keyguard_features_unredacted_notification, unredacted, { unredacted = it })
|
||||
CheckBoxItem(R.string.disable_keyguard_features_trust_agents, agents, { agents = it })
|
||||
CheckBoxItem(R.string.disable_keyguard_features_fingerprint, fingerprint, { fingerprint = it })
|
||||
if(VERSION.SDK_INT >= 24) { CheckBoxItem(R.string.disable_keyguard_features_remote_input, remote , { remote = it }) }
|
||||
if(VERSION.SDK_INT >= 28) {
|
||||
CheckBoxItem(R.string.disable_keyguard_features_face, face, { face = it })
|
||||
CheckBoxItem(R.string.disable_keyguard_features_iris, iris, { iris = it })
|
||||
CheckBoxItem(R.string.disable_keyguard_features_biometrics, biometrics, { biometrics = it })
|
||||
flagsLiat.forEach {
|
||||
CheckBoxItem(it.first, flag and it.second == it.second) { checked ->
|
||||
flag = if(checked) flag or it.second else flag and (flag xor it.second)
|
||||
}
|
||||
}
|
||||
if(VERSION.SDK_INT >= 34) { CheckBoxItem(R.string.disable_keyguard_features_shortcuts, shortcuts, { shortcuts = it }) }
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
var result = 0
|
||||
if(state==0) { result = 0 }
|
||||
else if(state==1) { result = KEYGUARD_DISABLE_FEATURES_ALL }
|
||||
else{
|
||||
if(shortcuts && VERSION.SDK_INT >= 34) { result+=KEYGUARD_DISABLE_SHORTCUTS_ALL }
|
||||
if(biometrics && VERSION.SDK_INT >= 28) { result+=KEYGUARD_DISABLE_BIOMETRICS }
|
||||
if(iris && VERSION.SDK_INT >= 28) { result+=KEYGUARD_DISABLE_IRIS }
|
||||
if(face && VERSION.SDK_INT >= 28) { result+=KEYGUARD_DISABLE_FACE }
|
||||
if(remote && VERSION.SDK_INT >= 24) { result+=KEYGUARD_DISABLE_REMOTE_INPUT }
|
||||
if(fingerprint) { result+=KEYGUARD_DISABLE_FINGERPRINT }
|
||||
if(agents) { result+=KEYGUARD_DISABLE_TRUST_AGENTS }
|
||||
if(unredacted) { result+=KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS }
|
||||
if(notification) { result+=KEYGUARD_DISABLE_SECURE_NOTIFICATIONS }
|
||||
if(camera) { result+=KEYGUARD_DISABLE_SECURE_CAMERA }
|
||||
if(widgets) { result+=KEYGUARD_DISABLE_WIDGETS_ALL }
|
||||
}
|
||||
dpm.setKeyguardDisabledFeatures(receiver,result)
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
calculateCustomFeature()
|
||||
val disabledFeatures = if(mode == 0) KEYGUARD_DISABLE_FEATURES_NONE else if(mode == 1) KEYGUARD_DISABLE_FEATURES_ALL else flag
|
||||
dpm.setKeyguardDisabledFeatures(receiver, disabledFeatures)
|
||||
refresh()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
@@ -576,13 +526,13 @@ fun PasswordQuality(navCtrl: NavHostController) {
|
||||
LaunchedEffect(Unit) { selectedItem=dpm.getPasswordQuality(receiver) }
|
||||
MyScaffold(R.string.required_password_quality, 8.dp, navCtrl) {
|
||||
passwordQuality.forEach {
|
||||
RadioButtonItem(it.value, selectedItem == it.key, { selectedItem = it.key })
|
||||
RadioButtonItem(it.value, selectedItem == it.key) { selectedItem = it.key }
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
dpm.setPasswordQuality(receiver,selectedItem)
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
|
||||
@@ -31,6 +31,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavHostController
|
||||
import com.bintianqi.owndroid.R
|
||||
import com.bintianqi.owndroid.backToHomeStateFlow
|
||||
import com.bintianqi.owndroid.showOperationResultToast
|
||||
import com.bintianqi.owndroid.ui.*
|
||||
import com.bintianqi.owndroid.writeClipBoard
|
||||
import com.bintianqi.owndroid.yesOrNo
|
||||
@@ -56,9 +57,9 @@ fun Permissions(navCtrl: NavHostController) {
|
||||
MyScaffold(R.string.permissions, 0.dp, navCtrl) {
|
||||
if(!dpm.isDeviceOwnerApp(context.packageName)) {
|
||||
SwitchItem(
|
||||
R.string.dhizuku, "", null,
|
||||
{ sharedPref.getBoolean("dhizuku", false) },
|
||||
{ toggleDhizukuMode(it, context) },
|
||||
R.string.dhizuku,
|
||||
getState = { sharedPref.getBoolean("dhizuku", false) },
|
||||
onCheckedChange = { toggleDhizukuMode(it, context) },
|
||||
onClickBlank = { dialog = 4 }
|
||||
)
|
||||
}
|
||||
@@ -78,7 +79,7 @@ fun Permissions(navCtrl: NavHostController) {
|
||||
operation = { navCtrl.navigate("DeviceOwner") }
|
||||
)
|
||||
}
|
||||
FunctionItem(R.string.shizuku,"") {
|
||||
FunctionItem(R.string.shizuku) {
|
||||
try {
|
||||
if(Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) { navCtrl.navigate("Shizuku") }
|
||||
else if(Shizuku.shouldShowRequestPermissionRationale()) {
|
||||
@@ -102,24 +103,24 @@ fun Permissions(navCtrl: NavHostController) {
|
||||
Toast.makeText(context, R.string.shizuku_not_started, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
FunctionItem(R.string.device_info, "", R.drawable.perm_device_information_fill0) { navCtrl.navigate("DeviceInfo") }
|
||||
FunctionItem(R.string.device_info, icon = R.drawable.perm_device_information_fill0) { navCtrl.navigate("DeviceInfo") }
|
||||
if((VERSION.SDK_INT >= 26 && deviceOwner) || (VERSION.SDK_INT>=24 && profileOwner)) {
|
||||
FunctionItem(R.string.org_name, "", R.drawable.corporate_fare_fill0) { dialog = 2 }
|
||||
FunctionItem(R.string.org_name, icon = R.drawable.corporate_fare_fill0) { dialog = 2 }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 31 && (profileOwner || deviceOwner)) {
|
||||
FunctionItem(R.string.org_id, "", R.drawable.corporate_fare_fill0) { dialog = 3 }
|
||||
FunctionItem(R.string.org_id, icon = R.drawable.corporate_fare_fill0) { dialog = 3 }
|
||||
}
|
||||
if(enrollmentSpecificId != "") {
|
||||
FunctionItem(R.string.enrollment_specific_id, "", R.drawable.id_card_fill0) { dialog = 1 }
|
||||
FunctionItem(R.string.enrollment_specific_id, icon = R.drawable.id_card_fill0) { dialog = 1 }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 24 && (deviceOwner || dpm.isOrgProfile(receiver))) {
|
||||
FunctionItem(R.string.lock_screen_info, "", R.drawable.screen_lock_portrait_fill0) { navCtrl.navigate("LockScreenInfo") }
|
||||
FunctionItem(R.string.lock_screen_info, icon = R.drawable.screen_lock_portrait_fill0) { navCtrl.navigate("LockScreenInfo") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 24 && deviceAdmin) {
|
||||
FunctionItem(R.string.support_messages, "", R.drawable.chat_fill0) { navCtrl.navigate("SupportMessages") }
|
||||
FunctionItem(R.string.support_messages, icon = R.drawable.chat_fill0) { navCtrl.navigate("SupportMessages") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 28 && (deviceOwner || profileOwner)) {
|
||||
FunctionItem(R.string.transfer_ownership, "", R.drawable.admin_panel_settings_fill0) { navCtrl.navigate("TransferOwnership") }
|
||||
FunctionItem(R.string.transfer_ownership, icon = R.drawable.admin_panel_settings_fill0) { navCtrl.navigate("TransferOwnership") }
|
||||
}
|
||||
}
|
||||
if(dialog != 0) {
|
||||
@@ -254,7 +255,7 @@ fun LockScreenInfo(navCtrl: NavHostController) {
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
dpm.setDeviceOwnerLockScreenInfo(receiver,infoText)
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
@@ -263,8 +264,8 @@ fun LockScreenInfo(navCtrl: NavHostController) {
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
dpm.setDeviceOwnerLockScreenInfo(receiver,null)
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
dpm.setDeviceOwnerLockScreenInfo(receiver, null)
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
@@ -521,7 +522,7 @@ fun SupportMessages(navCtrl: NavHostController) {
|
||||
onClick = {
|
||||
dpm.setShortSupportMessage(receiver, shortMsg)
|
||||
refreshMsg()
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
) {
|
||||
@@ -531,7 +532,7 @@ fun SupportMessages(navCtrl: NavHostController) {
|
||||
onClick = {
|
||||
dpm.setShortSupportMessage(receiver, null)
|
||||
refreshMsg()
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
@@ -552,7 +553,7 @@ fun SupportMessages(navCtrl: NavHostController) {
|
||||
onClick = {
|
||||
dpm.setLongSupportMessage(receiver, longMsg)
|
||||
refreshMsg()
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
) {
|
||||
@@ -562,7 +563,7 @@ fun SupportMessages(navCtrl: NavHostController) {
|
||||
onClick = {
|
||||
dpm.setLongSupportMessage(receiver, null)
|
||||
refreshMsg()
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
@@ -615,7 +616,7 @@ fun TransferOwnership(navCtrl: NavHostController) {
|
||||
val receiver = context.getReceiver()
|
||||
try {
|
||||
dpm.transferOwnership(receiver, componentName!!, null)
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
dialog = false
|
||||
backToHomeStateFlow.value = true
|
||||
} catch(e: Exception) {
|
||||
|
||||
@@ -39,6 +39,8 @@ import android.net.Uri
|
||||
import android.os.Build.VERSION
|
||||
import android.os.UserManager
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.clickable
|
||||
@@ -84,7 +86,6 @@ import androidx.compose.material3.rememberDatePickerState
|
||||
import androidx.compose.material3.rememberTimePickerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableLongStateOf
|
||||
@@ -109,14 +110,9 @@ import androidx.navigation.NavHostController
|
||||
import com.bintianqi.owndroid.MyViewModel
|
||||
import com.bintianqi.owndroid.NotificationUtils
|
||||
import com.bintianqi.owndroid.R
|
||||
import com.bintianqi.owndroid.exportFile
|
||||
import com.bintianqi.owndroid.exportFilePath
|
||||
import com.bintianqi.owndroid.fileUriFlow
|
||||
import com.bintianqi.owndroid.formatFileSize
|
||||
import com.bintianqi.owndroid.getFile
|
||||
import com.bintianqi.owndroid.humanReadableDate
|
||||
import com.bintianqi.owndroid.isExportingSecurityOrNetworkLogs
|
||||
import com.bintianqi.owndroid.toggle
|
||||
import com.bintianqi.owndroid.showOperationResultToast
|
||||
import com.bintianqi.owndroid.ui.CheckBoxItem
|
||||
import com.bintianqi.owndroid.ui.FunctionItem
|
||||
import com.bintianqi.owndroid.ui.InfoCard
|
||||
@@ -125,17 +121,13 @@ import com.bintianqi.owndroid.ui.MyScaffold
|
||||
import com.bintianqi.owndroid.ui.RadioButtonItem
|
||||
import com.bintianqi.owndroid.ui.SwitchItem
|
||||
import com.bintianqi.owndroid.uriToStream
|
||||
import com.bintianqi.owndroid.yesOrNo
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.addJsonObject
|
||||
import kotlinx.serialization.json.buildJsonArray
|
||||
import kotlinx.serialization.json.encodeToStream
|
||||
import kotlinx.serialization.json.put
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.util.Date
|
||||
import java.util.TimeZone
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.math.pow
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Composable
|
||||
@@ -151,53 +143,52 @@ fun SystemManage(navCtrl: NavHostController) {
|
||||
var dialog by remember { mutableIntStateOf(0) }
|
||||
MyScaffold(R.string.system, 0.dp, navCtrl) {
|
||||
if(deviceOwner || profileOwner) {
|
||||
FunctionItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("SystemOptions") }
|
||||
FunctionItem(R.string.options, icon = R.drawable.tune_fill0) { navCtrl.navigate("SystemOptions") }
|
||||
}
|
||||
FunctionItem(R.string.keyguard, "", R.drawable.screen_lock_portrait_fill0) { navCtrl.navigate("Keyguard") }
|
||||
FunctionItem(R.string.keyguard, icon = R.drawable.screen_lock_portrait_fill0) { navCtrl.navigate("Keyguard") }
|
||||
if(VERSION.SDK_INT >= 24 && deviceOwner) {
|
||||
FunctionItem(R.string.reboot, "", R.drawable.restart_alt_fill0) { dialog = 1 }
|
||||
FunctionItem(R.string.reboot, icon = R.drawable.restart_alt_fill0) { dialog = 1 }
|
||||
}
|
||||
if(deviceOwner && ((VERSION.SDK_INT >= 28 && dpm.isAffiliatedUser) || VERSION.SDK_INT >= 24)) {
|
||||
FunctionItem(R.string.bug_report, "", R.drawable.bug_report_fill0) { dialog = 2 }
|
||||
FunctionItem(R.string.bug_report, icon = R.drawable.bug_report_fill0) { dialog = 2 }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 28 && (deviceOwner || dpm.isOrgProfile(receiver))) {
|
||||
FunctionItem(R.string.change_time, "", R.drawable.schedule_fill0) { navCtrl.navigate("ChangeTime") }
|
||||
FunctionItem(R.string.change_timezone, "", R.drawable.schedule_fill0) { navCtrl.navigate("ChangeTimeZone") }
|
||||
FunctionItem(R.string.change_time, icon = R.drawable.schedule_fill0) { navCtrl.navigate("ChangeTime") }
|
||||
FunctionItem(R.string.change_timezone, icon = R.drawable.schedule_fill0) { navCtrl.navigate("ChangeTimeZone") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 23 && (deviceOwner || profileOwner)) {
|
||||
FunctionItem(R.string.permission_policy, "", R.drawable.key_fill0) { navCtrl.navigate("PermissionPolicy") }
|
||||
FunctionItem(R.string.permission_policy, icon = R.drawable.key_fill0) { navCtrl.navigate("PermissionPolicy") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 34 && deviceOwner) {
|
||||
FunctionItem(R.string.mte_policy, "", R.drawable.memory_fill0) { navCtrl.navigate("MTEPolicy") }
|
||||
FunctionItem(R.string.mte_policy, icon = R.drawable.memory_fill0) { navCtrl.navigate("MTEPolicy") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 31 && (deviceOwner || profileOwner)) {
|
||||
FunctionItem(R.string.nearby_streaming_policy, "", R.drawable.share_fill0) { navCtrl.navigate("NearbyStreamingPolicy") }
|
||||
FunctionItem(R.string.nearby_streaming_policy, icon = R.drawable.share_fill0) { navCtrl.navigate("NearbyStreamingPolicy") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 28 && deviceOwner) {
|
||||
FunctionItem(R.string.lock_task_mode, "", R.drawable.lock_fill0) { navCtrl.navigate("LockTaskMode") }
|
||||
FunctionItem(R.string.lock_task_mode, icon = R.drawable.lock_fill0) { navCtrl.navigate("LockTaskMode") }
|
||||
}
|
||||
if(deviceOwner || profileOwner) {
|
||||
FunctionItem(R.string.ca_cert, "", R.drawable.license_fill0) { navCtrl.navigate("CACert") }
|
||||
FunctionItem(R.string.ca_cert, icon = R.drawable.license_fill0) { navCtrl.navigate("CACert") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 26 && !dhizuku && (deviceOwner || dpm.isOrgProfile(receiver))) {
|
||||
FunctionItem(R.string.security_logging, "", R.drawable.description_fill0) { navCtrl.navigate("SecurityLogging") }
|
||||
FunctionItem(R.string.security_logging, icon = R.drawable.description_fill0) { navCtrl.navigate("SecurityLogging") }
|
||||
}
|
||||
if(deviceOwner || profileOwner) {
|
||||
FunctionItem(R.string.disable_account_management, "", R.drawable.account_circle_fill0) { navCtrl.navigate("DisableAccountManagement") }
|
||||
FunctionItem(R.string.disable_account_management, icon = R.drawable.account_circle_fill0) { navCtrl.navigate("DisableAccountManagement") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 23 && (deviceOwner || dpm.isOrgProfile(receiver))) {
|
||||
FunctionItem(R.string.system_update_policy, "", R.drawable.system_update_fill0) { navCtrl.navigate("SystemUpdatePolicy") }
|
||||
FunctionItem(R.string.system_update_policy, icon = R.drawable.system_update_fill0) { navCtrl.navigate("SystemUpdatePolicy") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 29 && (deviceOwner || dpm.isOrgProfile(receiver))) {
|
||||
FunctionItem(R.string.install_system_update, "", R.drawable.system_update_fill0) { navCtrl.navigate("InstallSystemUpdate") }
|
||||
FunctionItem(R.string.install_system_update, icon = R.drawable.system_update_fill0) { navCtrl.navigate("InstallSystemUpdate") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 30 && (deviceOwner || dpm.isOrgProfile(receiver))) {
|
||||
FunctionItem(R.string.frp_policy, "", R.drawable.device_reset_fill0) { navCtrl.navigate("FRPPolicy") }
|
||||
FunctionItem(R.string.frp_policy, icon = R.drawable.device_reset_fill0) { navCtrl.navigate("FRPPolicy") }
|
||||
}
|
||||
if(dangerousFeatures && context.isDeviceAdmin && !(VERSION.SDK_INT >= 24 && profileOwner && dpm.isManagedProfile(receiver))) {
|
||||
FunctionItem(R.string.wipe_data, "", R.drawable.device_reset_fill0) { navCtrl.navigate("WipeData") }
|
||||
FunctionItem(R.string.wipe_data, icon = R.drawable.device_reset_fill0) { navCtrl.navigate("WipeData") }
|
||||
}
|
||||
LaunchedEffect(Unit) { fileUriFlow.value = Uri.parse("") }
|
||||
}
|
||||
if(dialog != 0) AlertDialog(
|
||||
onDismissRequest = { dialog = 0 },
|
||||
@@ -214,8 +205,7 @@ fun SystemManage(navCtrl: NavHostController) {
|
||||
if(dialog == 1) {
|
||||
dpm.reboot(receiver)
|
||||
} else {
|
||||
val result = dpm.requestBugreport(receiver)
|
||||
Toast.makeText(context, if(result) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(dpm.requestBugreport(receiver))
|
||||
}
|
||||
dialog = 0
|
||||
}
|
||||
@@ -238,58 +228,60 @@ fun SystemOptions(navCtrl: NavHostController) {
|
||||
var dialog by remember { mutableIntStateOf(0) }
|
||||
MyScaffold(R.string.options, 0.dp, navCtrl) {
|
||||
if(deviceOwner || profileOwner) {
|
||||
SwitchItem(R.string.disable_cam,"", R.drawable.photo_camera_fill0,
|
||||
{ dpm.getCameraDisabled(null) }, { dpm.setCameraDisabled(receiver,it) }
|
||||
SwitchItem(R.string.disable_cam, icon = R.drawable.photo_camera_fill0,
|
||||
getState = { dpm.getCameraDisabled(null) }, onCheckedChange = { dpm.setCameraDisabled(receiver,it) }
|
||||
)
|
||||
}
|
||||
if(deviceOwner || profileOwner) {
|
||||
SwitchItem(R.string.disable_screen_capture, "", R.drawable.screenshot_fill0,
|
||||
{ dpm.getScreenCaptureDisabled(null) }, { dpm.setScreenCaptureDisabled(receiver,it) }
|
||||
SwitchItem(R.string.disable_screen_capture, icon = R.drawable.screenshot_fill0,
|
||||
getState = { dpm.getScreenCaptureDisabled(null) }, onCheckedChange = { dpm.setScreenCaptureDisabled(receiver,it) }
|
||||
)
|
||||
}
|
||||
if(VERSION.SDK_INT >= 34 && (deviceOwner || (profileOwner && dpm.isAffiliatedUser))) {
|
||||
SwitchItem(R.string.disable_status_bar, "", R.drawable.notifications_fill0,
|
||||
{ dpm.isStatusBarDisabled}, { dpm.setStatusBarDisabled(receiver,it) }
|
||||
SwitchItem(R.string.disable_status_bar, icon = R.drawable.notifications_fill0,
|
||||
getState = { dpm.isStatusBarDisabled}, onCheckedChange = { dpm.setStatusBarDisabled(receiver,it) }
|
||||
)
|
||||
}
|
||||
if(deviceOwner || (VERSION.SDK_INT >= 23 && profileOwner && um.isSystemUser) || dpm.isOrgProfile(receiver)) {
|
||||
if(VERSION.SDK_INT >= 30) {
|
||||
SwitchItem(R.string.auto_time, "", R.drawable.schedule_fill0,
|
||||
{ dpm.getAutoTimeEnabled(receiver) }, { dpm.setAutoTimeEnabled(receiver,it) }
|
||||
SwitchItem(R.string.auto_time, icon = R.drawable.schedule_fill0,
|
||||
getState = { dpm.getAutoTimeEnabled(receiver) }, onCheckedChange = { dpm.setAutoTimeEnabled(receiver,it) }
|
||||
)
|
||||
SwitchItem(R.string.auto_timezone, "", R.drawable.globe_fill0,
|
||||
{ dpm.getAutoTimeZoneEnabled(receiver) }, { dpm.setAutoTimeZoneEnabled(receiver,it) }
|
||||
SwitchItem(R.string.auto_timezone, icon = R.drawable.globe_fill0,
|
||||
getState = { dpm.getAutoTimeZoneEnabled(receiver) }, onCheckedChange = { dpm.setAutoTimeZoneEnabled(receiver,it) }
|
||||
)
|
||||
}else{
|
||||
SwitchItem(R.string.require_auto_time, "", R.drawable.schedule_fill0, { dpm.autoTimeRequired}, { dpm.setAutoTimeRequired(receiver,it) }, padding = false)
|
||||
} else {
|
||||
SwitchItem(R.string.require_auto_time, icon = R.drawable.schedule_fill0,
|
||||
getState = { dpm.autoTimeRequired}, onCheckedChange = { dpm.setAutoTimeRequired(receiver,it) }, padding = false)
|
||||
}
|
||||
}
|
||||
if(deviceOwner || (profileOwner && (VERSION.SDK_INT < 24 || (VERSION.SDK_INT >= 24 && !dpm.isManagedProfile(receiver))))) {
|
||||
SwitchItem(R.string.master_mute, "", R.drawable.volume_up_fill0,
|
||||
{ dpm.isMasterVolumeMuted(receiver) }, { dpm.setMasterVolumeMuted(receiver,it) }
|
||||
SwitchItem(R.string.master_mute, icon = R.drawable.volume_up_fill0,
|
||||
getState = { dpm.isMasterVolumeMuted(receiver) }, onCheckedChange = { dpm.setMasterVolumeMuted(receiver,it) }
|
||||
)
|
||||
}
|
||||
if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) {
|
||||
SwitchItem(R.string.backup_service, "", R.drawable.backup_fill0,
|
||||
{ dpm.isBackupServiceEnabled(receiver) }, { dpm.setBackupServiceEnabled(receiver,it) },
|
||||
SwitchItem(R.string.backup_service, icon = R.drawable.backup_fill0,
|
||||
getState = { dpm.isBackupServiceEnabled(receiver) }, onCheckedChange = { dpm.setBackupServiceEnabled(receiver,it) },
|
||||
onClickBlank = { dialog = 1 }
|
||||
)
|
||||
}
|
||||
if(VERSION.SDK_INT >= 24 && profileOwner && dpm.isManagedProfile(receiver)) {
|
||||
SwitchItem(R.string.disable_bt_contact_share, "", R.drawable.account_circle_fill0,
|
||||
{ dpm.getBluetoothContactSharingDisabled(receiver) }, { dpm.setBluetoothContactSharingDisabled(receiver,it) },
|
||||
SwitchItem(R.string.disable_bt_contact_share, icon = R.drawable.account_circle_fill0,
|
||||
getState = { dpm.getBluetoothContactSharingDisabled(receiver) },
|
||||
onCheckedChange = { dpm.setBluetoothContactSharingDisabled(receiver,it) }
|
||||
)
|
||||
}
|
||||
if(VERSION.SDK_INT >= 30 && deviceOwner) {
|
||||
SwitchItem(R.string.common_criteria_mode , "",R.drawable.security_fill0,
|
||||
{ dpm.isCommonCriteriaModeEnabled(receiver) }, { dpm.setCommonCriteriaModeEnabled(receiver,it) },
|
||||
SwitchItem(R.string.common_criteria_mode , icon =R.drawable.security_fill0,
|
||||
getState = { dpm.isCommonCriteriaModeEnabled(receiver) }, onCheckedChange = { dpm.setCommonCriteriaModeEnabled(receiver,it) },
|
||||
onClickBlank = { dialog = 2 }
|
||||
)
|
||||
}
|
||||
if(VERSION.SDK_INT >= 31 && (deviceOwner || dpm.isOrgProfile(receiver)) && dpm.canUsbDataSignalingBeDisabled()) {
|
||||
SwitchItem(
|
||||
R.string.disable_usb_signal, "", R.drawable.usb_fill0, { !dpm.isUsbDataSignalingEnabled },
|
||||
{ dpm.isUsbDataSignalingEnabled = !it },
|
||||
R.string.disable_usb_signal, icon = R.drawable.usb_fill0, getState = { !dpm.isUsbDataSignalingEnabled },
|
||||
onCheckedChange = { dpm.isUsbDataSignalingEnabled = !it },
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -324,18 +316,14 @@ fun Keyguard(navCtrl: NavHostController) {
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Button(
|
||||
onClick = {
|
||||
Toast.makeText(context, if(dpm.setKeyguardDisabled(receiver,true)) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
onClick = { context.showOperationResultToast(dpm.setKeyguardDisabled(receiver, true)) },
|
||||
enabled = deviceOwner || (VERSION.SDK_INT >= 28 && profileOwner && dpm.isAffiliatedUser),
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
) {
|
||||
Text(stringResource(R.string.disable))
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
Toast.makeText(context, if(dpm.setKeyguardDisabled(receiver,false)) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
onClick = { context.showOperationResultToast(dpm.setKeyguardDisabled(receiver, false)) },
|
||||
enabled = deviceOwner || (VERSION.SDK_INT >= 28 && profileOwner && dpm.isAffiliatedUser),
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
@@ -351,9 +339,8 @@ fun Keyguard(navCtrl: NavHostController) {
|
||||
if(VERSION.SDK_INT >= 26 && profileOwner && dpm.isManagedProfile(receiver)) {
|
||||
CheckBoxItem(
|
||||
R.string.evict_credential_encryptoon_key,
|
||||
flag == FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY,
|
||||
{ flag = if(flag==0) {1}else{0} }
|
||||
)
|
||||
flag and FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY != 0
|
||||
) { flag = flag xor FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY }
|
||||
Spacer(Modifier.padding(vertical = 2.dp))
|
||||
}
|
||||
Button(
|
||||
@@ -453,8 +440,7 @@ fun ChangeTime(navCtrl: NavHostController) {
|
||||
onClick = {
|
||||
val timeMillis = if(manualInput) inputTime.toLong()
|
||||
else datePickerState.selectedDateMillis!! + timePickerState.hour * 3600000 + timePickerState.minute * 60000
|
||||
val result = dpm.setTime(receiver, timeMillis)
|
||||
Toast.makeText(context, if(result) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(dpm.setTime(receiver, timeMillis))
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
enabled = isInputLegal
|
||||
@@ -509,8 +495,7 @@ fun ChangeTimeZone(navCtrl: NavHostController) {
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
val result = dpm.setTimeZone(receiver, inputTimezone)
|
||||
Toast.makeText(context, if(result) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(dpm.setTimeZone(receiver, inputTimezone))
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
@@ -555,14 +540,14 @@ fun PermissionPolicy(navCtrl: NavHostController) {
|
||||
val receiver = context.getReceiver()
|
||||
var selectedPolicy by remember { mutableIntStateOf(dpm.getPermissionPolicy(receiver)) }
|
||||
MyScaffold(R.string.permission_policy, 8.dp, navCtrl) {
|
||||
RadioButtonItem(R.string.default_stringres, selectedPolicy == PERMISSION_POLICY_PROMPT, { selectedPolicy = PERMISSION_POLICY_PROMPT })
|
||||
RadioButtonItem(R.string.auto_grant, selectedPolicy == PERMISSION_POLICY_AUTO_GRANT, { selectedPolicy = PERMISSION_POLICY_AUTO_GRANT })
|
||||
RadioButtonItem(R.string.auto_deny, selectedPolicy == PERMISSION_POLICY_AUTO_DENY, { selectedPolicy = PERMISSION_POLICY_AUTO_DENY })
|
||||
RadioButtonItem(R.string.default_stringres, selectedPolicy == PERMISSION_POLICY_PROMPT) { selectedPolicy = PERMISSION_POLICY_PROMPT }
|
||||
RadioButtonItem(R.string.auto_grant, selectedPolicy == PERMISSION_POLICY_AUTO_GRANT) { selectedPolicy = PERMISSION_POLICY_AUTO_GRANT }
|
||||
RadioButtonItem(R.string.auto_deny, selectedPolicy == PERMISSION_POLICY_AUTO_DENY) { selectedPolicy = PERMISSION_POLICY_AUTO_DENY }
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
dpm.setPermissionPolicy(receiver,selectedPolicy)
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
@@ -579,26 +564,14 @@ fun MTEPolicy(navCtrl: NavHostController) {
|
||||
val dpm = context.getDPM()
|
||||
var selectedMtePolicy by remember { mutableIntStateOf(dpm.mtePolicy) }
|
||||
MyScaffold(R.string.mte_policy, 8.dp, navCtrl) {
|
||||
RadioButtonItem(
|
||||
R.string.decide_by_user,
|
||||
selectedMtePolicy == MTE_NOT_CONTROLLED_BY_POLICY,
|
||||
{ selectedMtePolicy = MTE_NOT_CONTROLLED_BY_POLICY }
|
||||
)
|
||||
RadioButtonItem(
|
||||
R.string.enabled,
|
||||
selectedMtePolicy == MTE_ENABLED,
|
||||
{ selectedMtePolicy = MTE_ENABLED }
|
||||
)
|
||||
RadioButtonItem(
|
||||
R.string.disabled,
|
||||
selectedMtePolicy == MTE_DISABLED,
|
||||
{ selectedMtePolicy = MTE_DISABLED }
|
||||
)
|
||||
RadioButtonItem(R.string.decide_by_user, selectedMtePolicy == MTE_NOT_CONTROLLED_BY_POLICY) { selectedMtePolicy = MTE_NOT_CONTROLLED_BY_POLICY }
|
||||
RadioButtonItem(R.string.enabled, selectedMtePolicy == MTE_ENABLED) { selectedMtePolicy = MTE_ENABLED }
|
||||
RadioButtonItem(R.string.disabled, selectedMtePolicy == MTE_DISABLED) { selectedMtePolicy = MTE_DISABLED }
|
||||
Button(
|
||||
onClick = {
|
||||
try {
|
||||
dpm.mtePolicy = selectedMtePolicy
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
} catch(_: java.lang.UnsupportedOperationException) {
|
||||
Toast.makeText(context, R.string.unsupported, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
@@ -624,29 +597,19 @@ fun NearbyStreamingPolicy(navCtrl: NavHostController) {
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
RadioButtonItem(
|
||||
R.string.decide_by_user,
|
||||
appPolicy == NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY,
|
||||
{ appPolicy = NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY }
|
||||
)
|
||||
RadioButtonItem(
|
||||
R.string.enabled,
|
||||
appPolicy == NEARBY_STREAMING_ENABLED,
|
||||
{ appPolicy = NEARBY_STREAMING_ENABLED }
|
||||
)
|
||||
RadioButtonItem(
|
||||
R.string.disabled,
|
||||
appPolicy == NEARBY_STREAMING_DISABLED,
|
||||
{ appPolicy = NEARBY_STREAMING_DISABLED }
|
||||
)
|
||||
appPolicy == NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY
|
||||
) { appPolicy = NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY }
|
||||
RadioButtonItem(R.string.enabled, appPolicy == NEARBY_STREAMING_ENABLED) { appPolicy = NEARBY_STREAMING_ENABLED }
|
||||
RadioButtonItem(R.string.disabled, appPolicy == NEARBY_STREAMING_DISABLED) { appPolicy = NEARBY_STREAMING_DISABLED }
|
||||
RadioButtonItem(
|
||||
R.string.enable_if_secure_enough,
|
||||
appPolicy == NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY,
|
||||
{ appPolicy = NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY }
|
||||
)
|
||||
appPolicy == NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY
|
||||
) { appPolicy = NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY }
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
dpm.nearbyAppStreamingPolicy = appPolicy
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
@@ -659,29 +622,25 @@ fun NearbyStreamingPolicy(navCtrl: NavHostController) {
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
RadioButtonItem(
|
||||
R.string.decide_by_user,
|
||||
notificationPolicy == NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY,
|
||||
{ notificationPolicy = NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY }
|
||||
)
|
||||
notificationPolicy == NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY
|
||||
) { notificationPolicy = NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY }
|
||||
RadioButtonItem(
|
||||
R.string.enabled,
|
||||
notificationPolicy == NEARBY_STREAMING_ENABLED,
|
||||
{ notificationPolicy = NEARBY_STREAMING_ENABLED }
|
||||
)
|
||||
notificationPolicy == NEARBY_STREAMING_ENABLED
|
||||
) { notificationPolicy = NEARBY_STREAMING_ENABLED }
|
||||
RadioButtonItem(
|
||||
R.string.disabled,
|
||||
notificationPolicy == NEARBY_STREAMING_DISABLED,
|
||||
{ notificationPolicy = NEARBY_STREAMING_DISABLED }
|
||||
)
|
||||
notificationPolicy == NEARBY_STREAMING_DISABLED
|
||||
) { notificationPolicy = NEARBY_STREAMING_DISABLED }
|
||||
RadioButtonItem(
|
||||
R.string.enable_if_secure_enough,
|
||||
notificationPolicy == NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY,
|
||||
{ notificationPolicy = NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY }
|
||||
)
|
||||
notificationPolicy == NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY
|
||||
) { notificationPolicy = NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY }
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
dpm.nearbyNotificationStreamingPolicy = notificationPolicy
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
@@ -700,84 +659,58 @@ fun LockTaskMode(navCtrl: NavHostController, vm: MyViewModel) {
|
||||
val focusMgr = LocalFocusManager.current
|
||||
var appSelectorRequest by rememberSaveable { mutableIntStateOf(0) }
|
||||
MyScaffold(R.string.lock_task_mode, 8.dp, navCtrl, false) {
|
||||
val lockTaskFeatures = remember { mutableStateListOf<Int>() }
|
||||
var lockTaskFeatures by remember { mutableIntStateOf(0) }
|
||||
var custom by rememberSaveable { mutableStateOf(false) }
|
||||
val refreshFeature = {
|
||||
var calculate = dpm.getLockTaskFeatures(receiver)
|
||||
lockTaskFeatures.clear()
|
||||
if(calculate != 0) {
|
||||
var sq = 10
|
||||
while(sq >= 1) {
|
||||
val current = (2).toDouble().pow(sq.toDouble()).toInt()
|
||||
if(calculate - current >= 0) {
|
||||
lockTaskFeatures += current
|
||||
calculate -= current
|
||||
}
|
||||
sq--
|
||||
}
|
||||
if(calculate - 1 >= 0) { lockTaskFeatures += 1 }
|
||||
custom = true
|
||||
} else {
|
||||
custom = false
|
||||
}
|
||||
fun refreshFeature() {
|
||||
lockTaskFeatures = dpm.getLockTaskFeatures(receiver)
|
||||
custom = lockTaskFeatures != 0
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.lock_task_feature), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
LaunchedEffect(Unit) { refreshFeature() }
|
||||
RadioButtonItem(R.string.disable_all, !custom, { custom = false })
|
||||
RadioButtonItem(R.string.custom, custom, { custom = true })
|
||||
RadioButtonItem(R.string.disable_all, !custom) { custom = false }
|
||||
RadioButtonItem(R.string.custom, custom) { custom = true }
|
||||
AnimatedVisibility(custom) {
|
||||
Column {
|
||||
CheckBoxItem(
|
||||
R.string.ltf_sys_info,
|
||||
LOCK_TASK_FEATURE_SYSTEM_INFO in lockTaskFeatures,
|
||||
{ lockTaskFeatures.toggle(it, LOCK_TASK_FEATURE_SYSTEM_INFO) }
|
||||
)
|
||||
lockTaskFeatures and LOCK_TASK_FEATURE_SYSTEM_INFO != 0
|
||||
) { lockTaskFeatures = lockTaskFeatures xor LOCK_TASK_FEATURE_SYSTEM_INFO }
|
||||
CheckBoxItem(
|
||||
R.string.ltf_notifications,
|
||||
LOCK_TASK_FEATURE_NOTIFICATIONS in lockTaskFeatures,
|
||||
{ lockTaskFeatures.toggle(it, LOCK_TASK_FEATURE_NOTIFICATIONS) }
|
||||
)
|
||||
lockTaskFeatures and LOCK_TASK_FEATURE_NOTIFICATIONS != 0
|
||||
) { lockTaskFeatures = lockTaskFeatures xor LOCK_TASK_FEATURE_NOTIFICATIONS }
|
||||
CheckBoxItem(
|
||||
R.string.ltf_home,
|
||||
LOCK_TASK_FEATURE_HOME in lockTaskFeatures,
|
||||
{ lockTaskFeatures.toggle(it, LOCK_TASK_FEATURE_HOME) }
|
||||
)
|
||||
lockTaskFeatures and LOCK_TASK_FEATURE_HOME != 0
|
||||
) { lockTaskFeatures = lockTaskFeatures xor LOCK_TASK_FEATURE_HOME }
|
||||
CheckBoxItem(
|
||||
R.string.ltf_overview,
|
||||
LOCK_TASK_FEATURE_OVERVIEW in lockTaskFeatures,
|
||||
{ lockTaskFeatures.toggle(it, LOCK_TASK_FEATURE_OVERVIEW) }
|
||||
)
|
||||
lockTaskFeatures and LOCK_TASK_FEATURE_OVERVIEW != 0
|
||||
) { lockTaskFeatures = lockTaskFeatures xor LOCK_TASK_FEATURE_OVERVIEW }
|
||||
CheckBoxItem(
|
||||
R.string.ltf_global_actions,
|
||||
LOCK_TASK_FEATURE_GLOBAL_ACTIONS in lockTaskFeatures,
|
||||
{ lockTaskFeatures.toggle(it, LOCK_TASK_FEATURE_GLOBAL_ACTIONS) }
|
||||
)
|
||||
lockTaskFeatures and LOCK_TASK_FEATURE_GLOBAL_ACTIONS != 0
|
||||
) { lockTaskFeatures = lockTaskFeatures xor LOCK_TASK_FEATURE_GLOBAL_ACTIONS }
|
||||
CheckBoxItem(
|
||||
R.string.ltf_keyguard,
|
||||
LOCK_TASK_FEATURE_KEYGUARD in lockTaskFeatures,
|
||||
{ lockTaskFeatures.toggle(it, LOCK_TASK_FEATURE_KEYGUARD) }
|
||||
)
|
||||
lockTaskFeatures and LOCK_TASK_FEATURE_KEYGUARD != 0
|
||||
) { lockTaskFeatures = lockTaskFeatures xor LOCK_TASK_FEATURE_KEYGUARD }
|
||||
if(VERSION.SDK_INT >= 30) {
|
||||
CheckBoxItem(
|
||||
R.string.ltf_block_activity_start_in_task,
|
||||
LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK in lockTaskFeatures,
|
||||
{ lockTaskFeatures.toggle(it, LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK) }
|
||||
)
|
||||
lockTaskFeatures and LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK != 0
|
||||
) { lockTaskFeatures = lockTaskFeatures xor LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK }
|
||||
}
|
||||
}
|
||||
}
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = {
|
||||
var result = 0
|
||||
if(custom) {
|
||||
lockTaskFeatures.forEach { result += it }
|
||||
}
|
||||
try {
|
||||
dpm.setLockTaskFeatures(receiver, result)
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
dpm.setLockTaskFeatures(receiver, lockTaskFeatures)
|
||||
context.showOperationResultToast(true)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
AlertDialog.Builder(context)
|
||||
.setTitle(R.string.error)
|
||||
@@ -846,7 +779,7 @@ fun LockTaskMode(navCtrl: NavHostController, vm: MyViewModel) {
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = {
|
||||
dpm.setLockTaskPackages(receiver, lockTaskPackages.toTypedArray())
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
@@ -884,7 +817,7 @@ fun LockTaskMode(navCtrl: NavHostController, vm: MyViewModel) {
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp)
|
||||
)
|
||||
CheckBoxItem(R.string.specify_activity, specifyActivity, { specifyActivity = it })
|
||||
CheckBoxItem(R.string.specify_activity, specifyActivity) { specifyActivity = it }
|
||||
AnimatedVisibility(specifyActivity) {
|
||||
OutlinedTextField(
|
||||
value = startLockTaskActivity,
|
||||
@@ -925,29 +858,25 @@ fun CACert(navCtrl: NavHostController) {
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
val receiver = context.getReceiver()
|
||||
val uri by fileUriFlow.collectAsState()
|
||||
var exist by remember { mutableStateOf(false) }
|
||||
val uriPath = uri.path ?: ""
|
||||
var caCertByteArray by remember { mutableStateOf(byteArrayOf()) }
|
||||
LaunchedEffect(uri) {
|
||||
if(uri != Uri.parse("")) {
|
||||
var fileUri by remember { mutableStateOf<Uri?>(null) }
|
||||
var caCertByteArray by remember { mutableStateOf(ByteArray(100000)) }
|
||||
val getFileLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
result.data?.data?.let { uri ->
|
||||
uriToStream(context, uri) {
|
||||
val array = it.readBytes()
|
||||
caCertByteArray = if(array.size < 10000) {
|
||||
array
|
||||
}else{
|
||||
} else {
|
||||
byteArrayOf()
|
||||
}
|
||||
}
|
||||
exist = dpm.hasCaCertInstalled(receiver, caCertByteArray)
|
||||
}
|
||||
}
|
||||
MyScaffold(R.string.ca_cert, 8.dp, navCtrl) {
|
||||
AnimatedVisibility(uriPath != "") {
|
||||
Text(text = uriPath)
|
||||
}
|
||||
Text(
|
||||
text = if(uriPath == "") { stringResource(R.string.please_select_ca_cert) } else { stringResource(R.string.cert_installed, exist) },
|
||||
text = if(fileUri == null) { stringResource(R.string.please_select_ca_cert) }
|
||||
else { stringResource(R.string.cert_installed, stringResource(exist.yesOrNo)) },
|
||||
modifier = Modifier.animateContentSize()
|
||||
)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
@@ -956,18 +885,17 @@ fun CACert(navCtrl: NavHostController) {
|
||||
val caCertIntent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
caCertIntent.setType("*/*")
|
||||
caCertIntent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
getFile.launch(caCertIntent)
|
||||
getFileLauncher.launch(caCertIntent)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.select_ca_cert))
|
||||
}
|
||||
AnimatedVisibility(uriPath != "") {
|
||||
AnimatedVisibility(fileUri != null) {
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
Button(
|
||||
onClick = {
|
||||
val result = dpm.installCaCert(receiver, caCertByteArray)
|
||||
Toast.makeText(context, if(result) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(dpm.installCaCert(receiver, caCertByteArray))
|
||||
exist = dpm.hasCaCertInstalled(receiver, caCertByteArray)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
@@ -976,12 +904,11 @@ fun CACert(navCtrl: NavHostController) {
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
if(exist) {
|
||||
dpm.uninstallCaCert(receiver, caCertByteArray)
|
||||
exist = dpm.hasCaCertInstalled(receiver, caCertByteArray)
|
||||
Toast.makeText(context, if(exist) R.string.failed else R.string.success, Toast.LENGTH_SHORT).show()
|
||||
} else { Toast.makeText(context, R.string.not_exist, Toast.LENGTH_SHORT).show() }
|
||||
dpm.uninstallCaCert(receiver, caCertByteArray)
|
||||
exist = dpm.hasCaCertInstalled(receiver, caCertByteArray)
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
enabled = exist,
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
Text(stringResource(R.string.uninstall))
|
||||
@@ -991,7 +918,7 @@ fun CACert(navCtrl: NavHostController) {
|
||||
Button(
|
||||
onClick = {
|
||||
dpm.uninstallAllUserCaCerts(receiver)
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
@@ -1008,11 +935,31 @@ fun SecurityLogging(navCtrl: NavHostController) {
|
||||
val receiver = context.getReceiver()
|
||||
val logFile = context.filesDir.resolve("SecurityLogs.json")
|
||||
var fileSize by remember { mutableLongStateOf(0) }
|
||||
LaunchedEffect(Unit) {
|
||||
fileSize = logFile.length()
|
||||
LaunchedEffect(Unit) { fileSize = logFile.length() }
|
||||
var preRebootSecurityLogs by remember { mutableStateOf(byteArrayOf()) }
|
||||
val exportPreRebootSecurityLogs = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
result.data?.data?.let { uri ->
|
||||
context.contentResolver.openOutputStream(uri)?.use { outStream ->
|
||||
preRebootSecurityLogs.inputStream().copyTo(outStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
val exportSecurityLogs = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
result.data?.data?.let { uri ->
|
||||
context.contentResolver.openOutputStream(uri)?.use { outStream ->
|
||||
outStream.write("[".toByteArray())
|
||||
logFile.inputStream().use { it.copyTo(outStream) }
|
||||
outStream.write("]".toByteArray())
|
||||
context.showOperationResultToast(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
MyScaffold(R.string.security_logging, 8.dp, navCtrl) {
|
||||
SwitchItem(R.string.enable, "", null, { dpm.isSecurityLoggingEnabled(receiver) }, { dpm.setSecurityLoggingEnabled(receiver, it) }, padding = false)
|
||||
SwitchItem(
|
||||
R.string.enable,
|
||||
getState = { dpm.isSecurityLoggingEnabled(receiver) }, onCheckedChange = { dpm.setSecurityLoggingEnabled(receiver, it) },
|
||||
padding = false
|
||||
)
|
||||
Text(stringResource(R.string.log_file_size_is, formatFileSize(fileSize)))
|
||||
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
|
||||
Button(
|
||||
@@ -1021,9 +968,7 @@ fun SecurityLogging(navCtrl: NavHostController) {
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
intent.setType("application/json")
|
||||
intent.putExtra(Intent.EXTRA_TITLE, "SecurityLogs.json")
|
||||
exportFilePath = logFile.path
|
||||
isExportingSecurityOrNetworkLogs = true
|
||||
exportFile.launch(intent)
|
||||
exportSecurityLogs.launch(intent)
|
||||
},
|
||||
enabled = fileSize > 0,
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
@@ -1050,29 +995,16 @@ fun SecurityLogging(navCtrl: NavHostController) {
|
||||
Toast.makeText(context, R.string.no_logs, Toast.LENGTH_SHORT).show()
|
||||
return@Button
|
||||
} else {
|
||||
val securityEvents = buildJsonArray {
|
||||
logs.forEach { event ->
|
||||
addJsonObject {
|
||||
put("time_nanos", event.timeNanos)
|
||||
put("tag", event.tag)
|
||||
if(VERSION.SDK_INT >= 28) put("level", event.logLevel)
|
||||
if(VERSION.SDK_INT >= 28) put("id", event.id)
|
||||
parseSecurityEventData(event).let { if(it != null) put("data", it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
val preRebootSecurityLogs = context.filesDir.resolve("PreRebootSecurityLogs")
|
||||
preRebootSecurityLogs.outputStream().use {
|
||||
val json = Json { ignoreUnknownKeys = true; explicitNulls = false }
|
||||
json.encodeToStream(securityEvents, it)
|
||||
}
|
||||
val outputStream = ByteArrayOutputStream()
|
||||
outputStream.write("[".encodeToByteArray())
|
||||
processSecurityLogs(logs, outputStream)
|
||||
outputStream.write("]".encodeToByteArray())
|
||||
preRebootSecurityLogs = outputStream.toByteArray()
|
||||
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
intent.setType("application/json")
|
||||
intent.putExtra(Intent.EXTRA_TITLE, "PreRebootSecurityLogs.json")
|
||||
exportFilePath = preRebootSecurityLogs.path
|
||||
isExportingSecurityOrNetworkLogs = true
|
||||
exportFile.launch(intent)
|
||||
exportPreRebootSecurityLogs.launch(intent)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
@@ -1169,7 +1101,7 @@ fun FRPPolicy(navCtrl: NavHostController) {
|
||||
}
|
||||
AnimatedVisibility(usePolicy) {
|
||||
Column {
|
||||
CheckBoxItem(R.string.enable_frp, enabled, { enabled = it })
|
||||
CheckBoxItem(R.string.enable_frp, enabled) { enabled = it }
|
||||
Text(stringResource(R.string.account_list_is))
|
||||
Column(modifier = Modifier.animateContentSize()) {
|
||||
if(accountList.isEmpty()) Text(stringResource(R.string.none))
|
||||
@@ -1230,25 +1162,17 @@ fun WipeData(navCtrl: NavHostController) {
|
||||
val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
|
||||
val dpm = context.getDPM()
|
||||
val focusMgr = LocalFocusManager.current
|
||||
var flag by remember { mutableIntStateOf(0) }
|
||||
var warning by remember { mutableStateOf(false) }
|
||||
var wipeDevice by remember { mutableStateOf(false) }
|
||||
var externalStorage by remember { mutableStateOf(false) }
|
||||
var protectionData by remember { mutableStateOf(false) }
|
||||
var euicc by remember { mutableStateOf(false) }
|
||||
var silent by remember { mutableStateOf(false) }
|
||||
var reason by remember { mutableStateOf("") }
|
||||
MyScaffold(R.string.wipe_data, 8.dp, navCtrl) {
|
||||
CheckBoxItem(
|
||||
R.string.wipe_external_storage,
|
||||
externalStorage, { externalStorage = it }
|
||||
)
|
||||
if(VERSION.SDK_INT >= 22 && context.isDeviceOwner) {
|
||||
CheckBoxItem(R.string.wipe_reset_protection_data,
|
||||
protectionData, { protectionData = it }
|
||||
)
|
||||
}
|
||||
if(VERSION.SDK_INT >= 28) { CheckBoxItem(R.string.wipe_euicc, euicc, { euicc = it }) }
|
||||
if(VERSION.SDK_INT >= 29) { CheckBoxItem(R.string.wipe_silently, silent, { silent = it }) }
|
||||
CheckBoxItem(R.string.wipe_external_storage, flag and WIPE_EXTERNAL_STORAGE != 0) { flag = flag xor WIPE_EXTERNAL_STORAGE }
|
||||
if(VERSION.SDK_INT >= 22 && context.isDeviceOwner) CheckBoxItem(
|
||||
R.string.wipe_reset_protection_data, flag and WIPE_RESET_PROTECTION_DATA != 0) { flag = flag xor WIPE_RESET_PROTECTION_DATA }
|
||||
if(VERSION.SDK_INT >= 28) CheckBoxItem(R.string.wipe_euicc, flag and WIPE_EUICC != 0) { flag = flag xor WIPE_EUICC }
|
||||
if(VERSION.SDK_INT >= 29) CheckBoxItem(R.string.wipe_silently, silent) { silent = it }
|
||||
AnimatedVisibility(!silent && VERSION.SDK_INT >= 28) {
|
||||
OutlinedTextField(
|
||||
value = reason, onValueChange = { reason = it },
|
||||
@@ -1308,11 +1232,7 @@ fun WipeData(navCtrl: NavHostController) {
|
||||
val timerText = if(timer > 0) "(${timer}s)" else ""
|
||||
TextButton(
|
||||
onClick = {
|
||||
var flag = 0
|
||||
if(externalStorage) { flag += WIPE_EXTERNAL_STORAGE }
|
||||
if(protectionData && VERSION.SDK_INT >= 22) { flag += WIPE_RESET_PROTECTION_DATA }
|
||||
if(euicc && VERSION.SDK_INT >= 28) { flag += WIPE_EUICC }
|
||||
if(silent && VERSION.SDK_INT >= 29) { flag += WIPE_SILENTLY }
|
||||
if(silent && VERSION.SDK_INT >= 29) { flag = flag or WIPE_SILENTLY }
|
||||
if(wipeDevice) {
|
||||
dpm.wipeDevice(flag)
|
||||
} else {
|
||||
@@ -1351,17 +1271,17 @@ fun SystemUpdatePolicy(navCtrl: NavHostController) {
|
||||
var selectedPolicy by remember { mutableStateOf(dpm.systemUpdatePolicy?.policyType) }
|
||||
RadioButtonItem(
|
||||
R.string.system_update_policy_automatic,
|
||||
selectedPolicy == TYPE_INSTALL_AUTOMATIC, { selectedPolicy = TYPE_INSTALL_AUTOMATIC }
|
||||
)
|
||||
selectedPolicy == TYPE_INSTALL_AUTOMATIC
|
||||
) { selectedPolicy = TYPE_INSTALL_AUTOMATIC }
|
||||
RadioButtonItem(
|
||||
R.string.system_update_policy_install_windowed,
|
||||
selectedPolicy == TYPE_INSTALL_WINDOWED, { selectedPolicy = TYPE_INSTALL_WINDOWED }
|
||||
)
|
||||
selectedPolicy == TYPE_INSTALL_WINDOWED
|
||||
) { selectedPolicy = TYPE_INSTALL_WINDOWED }
|
||||
RadioButtonItem(
|
||||
R.string.system_update_policy_postpone,
|
||||
selectedPolicy == TYPE_POSTPONE, { selectedPolicy = TYPE_POSTPONE }
|
||||
)
|
||||
RadioButtonItem(R.string.none, selectedPolicy == null, { selectedPolicy = null })
|
||||
selectedPolicy == TYPE_POSTPONE
|
||||
) { selectedPolicy = TYPE_POSTPONE }
|
||||
RadioButtonItem(R.string.none, selectedPolicy == null) { selectedPolicy = null }
|
||||
var windowedPolicyStart by remember { mutableStateOf("") }
|
||||
var windowedPolicyEnd by remember { mutableStateOf("") }
|
||||
AnimatedVisibility(selectedPolicy == 2) {
|
||||
@@ -1399,7 +1319,7 @@ fun SystemUpdatePolicy(navCtrl: NavHostController) {
|
||||
else -> null
|
||||
}
|
||||
dpm.setSystemUpdatePolicy(receiver,policy)
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 8.dp)
|
||||
) {
|
||||
@@ -1447,27 +1367,30 @@ fun InstallSystemUpdate(navCtrl: NavHostController) {
|
||||
Toast.makeText(context, errMsg, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
val uri by fileUriFlow.collectAsState()
|
||||
var uri by remember { mutableStateOf<Uri?>(null) }
|
||||
val getFileLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
uri = it.data?.data
|
||||
}
|
||||
MyScaffold(R.string.install_system_update, 8.dp, navCtrl) {
|
||||
Button(
|
||||
onClick = {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
intent.setType("application/zip")
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
getFile.launch(intent)
|
||||
getFileLauncher.launch(intent)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.select_ota_package))
|
||||
}
|
||||
AnimatedVisibility(uri != Uri.parse("")) {
|
||||
AnimatedVisibility(uri != null) {
|
||||
Button(
|
||||
onClick = {
|
||||
val executor = Executors.newCachedThreadPool()
|
||||
try {
|
||||
dpm.installSystemUpdate(receiver, uri, executor, callback)
|
||||
dpm.installSystemUpdate(receiver, uri!!, executor, callback)
|
||||
Toast.makeText(context, R.string.start_install_system_update, Toast.LENGTH_SHORT).show()
|
||||
}catch(e: Exception) {
|
||||
} catch(e: Exception) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.install_system_update_failed) + e.cause.toString(),
|
||||
|
||||
@@ -39,12 +39,12 @@ fun UserRestriction(navCtrl:NavHostController) {
|
||||
Text(text = stringResource(R.string.some_features_invalid_in_work_profile), modifier = Modifier.padding(start = 16.dp))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 2.dp))
|
||||
FunctionItem(R.string.network_and_internet, "", R.drawable.wifi_fill0) { navCtrl.navigate("UR-Internet") }
|
||||
FunctionItem(R.string.connectivity, "", R.drawable.devices_other_fill0) { navCtrl.navigate("UR-Connectivity") }
|
||||
FunctionItem(R.string.applications, "", R.drawable.apps_fill0) { navCtrl.navigate("UR-Applications") }
|
||||
FunctionItem(R.string.users, "", R.drawable.account_circle_fill0) { navCtrl.navigate("UR-Users") }
|
||||
FunctionItem(R.string.media, "", R.drawable.volume_up_fill0) { navCtrl.navigate("UR-Media") }
|
||||
FunctionItem(R.string.other, "", R.drawable.more_horiz_fill0) { navCtrl.navigate("UR-Other") }
|
||||
FunctionItem(R.string.network_and_internet, icon = R.drawable.wifi_fill0) { navCtrl.navigate("UR-Internet") }
|
||||
FunctionItem(R.string.connectivity, icon = R.drawable.devices_other_fill0) { navCtrl.navigate("UR-Connectivity") }
|
||||
FunctionItem(R.string.applications, icon = R.drawable.apps_fill0) { navCtrl.navigate("UR-Applications") }
|
||||
FunctionItem(R.string.users, icon = R.drawable.account_circle_fill0) { navCtrl.navigate("UR-Users") }
|
||||
FunctionItem(R.string.media, icon = R.drawable.volume_up_fill0) { navCtrl.navigate("UR-Media") }
|
||||
FunctionItem(R.string.other, icon = R.drawable.more_horiz_fill0) { navCtrl.navigate("UR-Other") }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import android.os.Binder
|
||||
import android.os.Build.VERSION
|
||||
import android.os.Process
|
||||
@@ -14,6 +13,8 @@ import android.os.UserHandle
|
||||
import android.os.UserManager
|
||||
import android.provider.MediaStore
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.animateContentSize
|
||||
@@ -40,7 +41,6 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
@@ -59,10 +59,8 @@ import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavHostController
|
||||
import com.bintianqi.owndroid.R
|
||||
import com.bintianqi.owndroid.fileUriFlow
|
||||
import com.bintianqi.owndroid.getFile
|
||||
import com.bintianqi.owndroid.parseTimestamp
|
||||
import com.bintianqi.owndroid.toggle
|
||||
import com.bintianqi.owndroid.showOperationResultToast
|
||||
import com.bintianqi.owndroid.ui.CardItem
|
||||
import com.bintianqi.owndroid.ui.CheckBoxItem
|
||||
import com.bintianqi.owndroid.ui.FunctionItem
|
||||
@@ -82,33 +80,32 @@ fun Users(navCtrl: NavHostController) {
|
||||
val profileOwner = context.isProfileOwner
|
||||
var dialog by remember { mutableIntStateOf(0) }
|
||||
MyScaffold(R.string.users, 0.dp, navCtrl) {
|
||||
FunctionItem(R.string.user_info, "", R.drawable.person_fill0) { navCtrl.navigate("UserInfo") }
|
||||
FunctionItem(R.string.user_info, icon = R.drawable.person_fill0) { navCtrl.navigate("UserInfo") }
|
||||
if(deviceOwner && VERSION.SDK_INT >= 28) {
|
||||
FunctionItem(R.string.secondary_users, "", R.drawable.list_fill0) { dialog = 1 }
|
||||
FunctionItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("UserOptions") }
|
||||
FunctionItem(R.string.secondary_users, icon = R.drawable.list_fill0) { dialog = 1 }
|
||||
FunctionItem(R.string.options, icon = R.drawable.tune_fill0) { navCtrl.navigate("UserOptions") }
|
||||
}
|
||||
if(deviceOwner) {
|
||||
FunctionItem(R.string.user_operation, "", R.drawable.sync_alt_fill0) { navCtrl.navigate("UserOperation") }
|
||||
FunctionItem(R.string.user_operation, icon = R.drawable.sync_alt_fill0) { navCtrl.navigate("UserOperation") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 24 && deviceOwner) {
|
||||
FunctionItem(R.string.create_user, "", R.drawable.person_add_fill0) { navCtrl.navigate("CreateUser") }
|
||||
FunctionItem(R.string.create_user, icon = R.drawable.person_add_fill0) { navCtrl.navigate("CreateUser") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 28 && profileOwner && dpm.isAffiliatedUser) {
|
||||
FunctionItem(R.string.logout_current_user, "", R.drawable.logout_fill0) { dialog = 2 }
|
||||
FunctionItem(R.string.logout_current_user, icon = R.drawable.logout_fill0) { dialog = 2 }
|
||||
}
|
||||
if(deviceOwner || profileOwner) {
|
||||
FunctionItem(R.string.change_username, "", R.drawable.edit_fill0) { navCtrl.navigate("ChangeUsername") }
|
||||
FunctionItem(R.string.change_username, icon = R.drawable.edit_fill0) { navCtrl.navigate("ChangeUsername") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 23 && (deviceOwner || profileOwner)) {
|
||||
FunctionItem(R.string.change_user_icon, "", R.drawable.account_circle_fill0) { navCtrl.navigate("ChangeUserIcon") }
|
||||
FunctionItem(R.string.change_user_icon, icon = R.drawable.account_circle_fill0) { navCtrl.navigate("ChangeUserIcon") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 28 && deviceOwner) {
|
||||
FunctionItem(R.string.user_session_msg, "", R.drawable.notifications_fill0) { navCtrl.navigate("UserSessionMessage") }
|
||||
FunctionItem(R.string.user_session_msg, icon = R.drawable.notifications_fill0) { navCtrl.navigate("UserSessionMessage") }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) {
|
||||
FunctionItem(R.string.affiliation_id, "", R.drawable.id_card_fill0) { navCtrl.navigate("AffiliationID") }
|
||||
FunctionItem(R.string.affiliation_id, icon = R.drawable.id_card_fill0) { navCtrl.navigate("AffiliationID") }
|
||||
}
|
||||
LaunchedEffect(Unit) { fileUriFlow.value = Uri.parse("") }
|
||||
}
|
||||
if(dialog != 0 && VERSION.SDK_INT >= 28) AlertDialog(
|
||||
title = { Text(stringResource(if(dialog == 1) R.string.secondary_users else R.string.logout_current_user)) },
|
||||
@@ -157,7 +154,7 @@ fun UserOptions(navCtrl: NavHostController) {
|
||||
val receiver = context.getReceiver()
|
||||
MyScaffold(R.string.options, 0.dp, navCtrl) {
|
||||
if(VERSION.SDK_INT >= 28) {
|
||||
SwitchItem(R.string.enable_logout, "", null, { dpm.isLogoutEnabled }, { dpm.setLogoutEnabled(receiver, it) })
|
||||
SwitchItem(R.string.enable_logout, getState = { dpm.isLogoutEnabled }, onCheckedChange = { dpm.setLogoutEnabled(receiver, it) })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -259,9 +256,7 @@ fun UserOperation(navCtrl: NavHostController) {
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
withUserHandle {
|
||||
Toast.makeText(context, if(dpm.switchUser(receiver, it)) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
withUserHandle { context.showOperationResultToast(dpm.switchUser(receiver, it)) }
|
||||
},
|
||||
enabled = legalInput,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
@@ -288,7 +283,7 @@ fun UserOperation(navCtrl: NavHostController) {
|
||||
focusMgr.clearFocus()
|
||||
withUserHandle {
|
||||
if(dpm.removeUser(receiver, it)) {
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
idInput = ""
|
||||
} else {
|
||||
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
@@ -313,7 +308,7 @@ fun CreateUser(navCtrl: NavHostController) {
|
||||
val receiver = context.getReceiver()
|
||||
val focusMgr = LocalFocusManager.current
|
||||
var userName by remember { mutableStateOf("") }
|
||||
val flags = remember { mutableStateListOf<Int>() }
|
||||
var flag by remember { mutableIntStateOf(0) }
|
||||
MyScaffold(R.string.create_user, 8.dp, navCtrl) {
|
||||
OutlinedTextField(
|
||||
value = userName,
|
||||
@@ -326,28 +321,25 @@ fun CreateUser(navCtrl: NavHostController) {
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
CheckBoxItem(
|
||||
R.string.create_user_skip_wizard,
|
||||
DevicePolicyManager.SKIP_SETUP_WIZARD in flags,
|
||||
{ flags.toggle(it, DevicePolicyManager.SKIP_SETUP_WIZARD) }
|
||||
)
|
||||
flag and DevicePolicyManager.SKIP_SETUP_WIZARD != 0
|
||||
) { flag = flag xor DevicePolicyManager.SKIP_SETUP_WIZARD }
|
||||
if(VERSION.SDK_INT >= 28) {
|
||||
CheckBoxItem(
|
||||
R.string.create_user_ephemeral_user,
|
||||
DevicePolicyManager.MAKE_USER_EPHEMERAL in flags,
|
||||
{ flags.toggle(it, DevicePolicyManager.MAKE_USER_EPHEMERAL) }
|
||||
)
|
||||
flag and DevicePolicyManager.MAKE_USER_EPHEMERAL != 0
|
||||
) { flag = flag xor DevicePolicyManager.MAKE_USER_EPHEMERAL }
|
||||
CheckBoxItem(
|
||||
R.string.create_user_enable_all_system_app,
|
||||
DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED in flags,
|
||||
{ flags.toggle(it, DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED) }
|
||||
)
|
||||
flag and DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED != 0
|
||||
) { flag = flag xor DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED }
|
||||
}
|
||||
var newUserHandle: UserHandle? by remember { mutableStateOf(null) }
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
newUserHandle = dpm.createAndManageUser(receiver, userName, receiver, null, flags.sum())
|
||||
Toast.makeText(context, if(newUserHandle!=null) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
newUserHandle = dpm.createAndManageUser(receiver, userName, receiver, null, flag)
|
||||
context.showOperationResultToast(newUserHandle != null)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
@@ -403,7 +395,7 @@ fun AffiliationID(navCtrl: NavHostController) {
|
||||
onClick = {
|
||||
list.removeAll(listOf(""))
|
||||
dpm.setAffiliationIds(receiver, list.toSet())
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
refreshIds()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
@@ -434,7 +426,7 @@ fun ChangeUsername(navCtrl: NavHostController) {
|
||||
Button(
|
||||
onClick = {
|
||||
dpm.setProfileName(receiver, inputUsername)
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
@@ -477,7 +469,6 @@ fun UserSessionMessage(navCtrl: NavHostController) {
|
||||
onClick = {
|
||||
dpm.setStartUserSessionMessage(receiver,start)
|
||||
refreshMsg()
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
) {
|
||||
@@ -487,7 +478,7 @@ fun UserSessionMessage(navCtrl: NavHostController) {
|
||||
onClick = {
|
||||
dpm.setStartUserSessionMessage(receiver,null)
|
||||
refreshMsg()
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
@@ -508,7 +499,7 @@ fun UserSessionMessage(navCtrl: NavHostController) {
|
||||
onClick = {
|
||||
dpm.setEndUserSessionMessage(receiver,end)
|
||||
refreshMsg()
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
) {
|
||||
@@ -518,7 +509,7 @@ fun UserSessionMessage(navCtrl: NavHostController) {
|
||||
onClick = {
|
||||
dpm.setEndUserSessionMessage(receiver,null)
|
||||
refreshMsg()
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(0.96F)
|
||||
) {
|
||||
@@ -536,22 +527,22 @@ fun ChangeUserIcon(navCtrl: NavHostController) {
|
||||
val receiver = context.getReceiver()
|
||||
var getContent by remember { mutableStateOf(false) }
|
||||
var bitmap by remember { mutableStateOf<Bitmap?>(null) }
|
||||
val uriState by fileUriFlow.collectAsState()
|
||||
LaunchedEffect(uriState) {
|
||||
if(uriState == Uri.parse("")) return@LaunchedEffect
|
||||
uriToStream(context, fileUriFlow.value) { stream ->
|
||||
bitmap = BitmapFactory.decodeStream(stream)
|
||||
val getFileLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
it.data?.data?.let {
|
||||
uriToStream(context, it) { stream ->
|
||||
bitmap = BitmapFactory.decodeStream(stream)
|
||||
}
|
||||
}
|
||||
}
|
||||
MyScaffold(R.string.change_user_icon, 8.dp, navCtrl) {
|
||||
CheckBoxItem(R.string.file_picker_instead_gallery, getContent, { getContent = it })
|
||||
CheckBoxItem(R.string.file_picker_instead_gallery, getContent) { getContent = it }
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
val intent = Intent(if(getContent) Intent.ACTION_GET_CONTENT else Intent.ACTION_PICK)
|
||||
if(getContent) intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")
|
||||
getFile.launch(intent)
|
||||
getFileLauncher.launch(intent)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
@@ -567,7 +558,7 @@ fun ChangeUserIcon(navCtrl: NavHostController) {
|
||||
Button(
|
||||
onClick = {
|
||||
dpm.setUserIcon(receiver, bitmap)
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(true)
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
|
||||
@@ -25,7 +25,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@@ -41,7 +40,7 @@ import kotlinx.coroutines.launch
|
||||
@Composable
|
||||
fun FunctionItem(
|
||||
@StringRes title: Int,
|
||||
desc: String,
|
||||
desc: String? = null,
|
||||
@DrawableRes icon: Int? = null,
|
||||
operation: () -> Unit
|
||||
) {
|
||||
@@ -63,7 +62,7 @@ fun FunctionItem(
|
||||
style = typography.titleLarge,
|
||||
modifier = Modifier.padding(bottom = if(zhCN) 2.dp else 0.dp)
|
||||
)
|
||||
if(desc != "") { Text(text = desc, color = colorScheme.onBackground.copy(alpha = 0.8F)) }
|
||||
if(desc != null) { Text(text = desc, color = colorScheme.onBackground.copy(alpha = 0.8F)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,18 +84,16 @@ fun NavIcon(operation: () -> Unit) {
|
||||
fun RadioButtonItem(
|
||||
@StringRes text: Int,
|
||||
selected: Boolean,
|
||||
operation: () -> Unit,
|
||||
textColor: Color = colorScheme.onBackground
|
||||
operation: () -> Unit
|
||||
) {
|
||||
RadioButtonItem(stringResource(text), selected, operation, textColor)
|
||||
RadioButtonItem(stringResource(text), selected, operation)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RadioButtonItem(
|
||||
text: String,
|
||||
selected: Boolean,
|
||||
operation: () -> Unit,
|
||||
textColor: Color = colorScheme.onBackground
|
||||
operation: () -> Unit
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -104,7 +101,7 @@ fun RadioButtonItem(
|
||||
.clickable(onClick = operation)
|
||||
) {
|
||||
RadioButton(selected = selected, onClick = operation)
|
||||
Text(text = text, color = textColor, modifier = Modifier.padding(bottom = if(zhCN) { 2 } else { 0 }.dp))
|
||||
Text(text = text, modifier = Modifier.padding(bottom = if(zhCN) { 2 } else { 0 }.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,8 +109,7 @@ fun RadioButtonItem(
|
||||
fun CheckBoxItem(
|
||||
@StringRes text: Int,
|
||||
checked: Boolean,
|
||||
operation: (Boolean) -> Unit,
|
||||
textColor: Color = colorScheme.onBackground
|
||||
operation: (Boolean) -> Unit
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -124,7 +120,7 @@ fun CheckBoxItem(
|
||||
checked = checked,
|
||||
onCheckedChange = operation
|
||||
)
|
||||
Text(text = stringResource(text), color = textColor, modifier = Modifier.padding(bottom = if(zhCN) { 2 } else { 0 }.dp))
|
||||
Text(text = stringResource(text), modifier = Modifier.padding(bottom = if(zhCN) { 2 } else { 0 }.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,26 +128,26 @@ fun CheckBoxItem(
|
||||
@Composable
|
||||
fun SwitchItem(
|
||||
@StringRes title: Int,
|
||||
desc: String,
|
||||
@DrawableRes icon: Int?,
|
||||
getState: ()->Boolean,
|
||||
desc: String? = null,
|
||||
@DrawableRes icon: Int? = null,
|
||||
getState: () -> Boolean,
|
||||
onCheckedChange: (Boolean)->Unit,
|
||||
enable: Boolean = true,
|
||||
enabled: Boolean = true,
|
||||
onClickBlank: (() -> Unit)? = null,
|
||||
padding: Boolean = true
|
||||
) {
|
||||
var state by remember { mutableStateOf(getState()) }
|
||||
SwitchItem(title, desc, icon, state, { onCheckedChange(it); state = getState() }, enable, onClickBlank, padding)
|
||||
SwitchItem(title, desc, icon, state, { onCheckedChange(it); state = getState() }, enabled, onClickBlank, padding)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SwitchItem(
|
||||
@StringRes title: Int,
|
||||
desc: String,
|
||||
@DrawableRes icon: Int?,
|
||||
desc: String? = null,
|
||||
@DrawableRes icon: Int? = null,
|
||||
state: Boolean,
|
||||
onCheckedChange: (Boolean)->Unit,
|
||||
enable: Boolean = true,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
enabled: Boolean = true,
|
||||
onClickBlank: (() -> Unit)? = null,
|
||||
padding: Boolean = true
|
||||
) {
|
||||
@@ -172,14 +168,12 @@ fun SwitchItem(
|
||||
)
|
||||
Column(modifier = Modifier.padding(end = 60.dp, bottom = if(zhCN) 2.dp else 0.dp)) {
|
||||
Text(text = stringResource(title), style = typography.titleLarge)
|
||||
if(desc != "") {
|
||||
Text(text = desc, color = colorScheme.onBackground.copy(alpha = 0.8F))
|
||||
}
|
||||
if(desc != null) Text(text = desc, color = colorScheme.onBackground.copy(alpha = 0.8F))
|
||||
}
|
||||
}
|
||||
Switch(
|
||||
checked = state, onCheckedChange = { onCheckedChange(it) },
|
||||
modifier = Modifier.align(Alignment.CenterEnd), enabled = enable
|
||||
modifier = Modifier.align(Alignment.CenterEnd), enabled = enabled
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user