Optimize code

This commit is contained in:
BinTianqi
2024-12-29 14:03:28 +08:00
parent 4250d47683
commit 937afe9417
15 changed files with 513 additions and 750 deletions

View File

@@ -21,6 +21,7 @@
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/> <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.QUERY_ALL_PACKAGES" tools:ignore="QueryAllPackagesPermission" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <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.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_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"/> <uses-sdk tools:overrideLibrary="rikka.shizuku.provider,rikka.shizuku.api,rikka.shizuku.shared,rikka.shizuku.aidl"/>

View File

@@ -1,6 +1,5 @@
package com.bintianqi.owndroid package com.bintianqi.owndroid
import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
@@ -47,7 +46,6 @@ class InstallAppActivity: FragmentActivity() {
enableEdgeToEdge() enableEdgeToEdge()
WindowCompat.setDecorFitsSystemWindows(window, false) WindowCompat.setDecorFitsSystemWindows(window, false)
val context = applicationContext val context = applicationContext
val sharedPref = applicationContext.getSharedPreferences("data", Context.MODE_PRIVATE)
window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
val uri = this.intent.data!! val uri = this.intent.data!!
var apkInfoText by mutableStateOf(context.getString(R.string.parsing_apk_info)) var apkInfoText by mutableStateOf(context.getString(R.string.parsing_apk_info))
@@ -97,7 +95,9 @@ class InstallAppActivity: FragmentActivity() {
TextButton( TextButton(
onClick = { onClick = {
status = "installing" status = "installing"
uriToStream(applicationContext, this.intent.data) { stream -> installPackage(applicationContext, stream) } intent.data?.let {
uriToStream(applicationContext, it) { stream -> installPackage(applicationContext, stream) }
}
}, },
enabled = status != "installing" enabled = status != "installing"
) { ) {

View File

@@ -24,10 +24,10 @@ import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import com.bintianqi.owndroid.dpm.handleNetworkLogs import com.bintianqi.owndroid.dpm.handleNetworkLogs
import com.bintianqi.owndroid.dpm.handleSecurityLogs
import com.bintianqi.owndroid.dpm.isDeviceAdmin import com.bintianqi.owndroid.dpm.isDeviceAdmin
import com.bintianqi.owndroid.dpm.isDeviceOwner import com.bintianqi.owndroid.dpm.isDeviceOwner
import com.bintianqi.owndroid.dpm.isProfileOwner import com.bintianqi.owndroid.dpm.isProfileOwner
import com.bintianqi.owndroid.dpm.processSecurityLogs
import com.bintianqi.owndroid.dpm.toggleInstallAppActivity import com.bintianqi.owndroid.dpm.toggleInstallAppActivity
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -78,7 +78,13 @@ class Receiver : DeviceAdminReceiver() {
super.onSecurityLogsAvailable(context, intent) super.onSecurityLogsAvailable(context, intent)
if(VERSION.SDK_INT >= 24) { if(VERSION.SDK_INT >= 24) {
CoroutineScope(Dispatchers.IO).launch { 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)
}
} }
} }
} }

View File

@@ -4,7 +4,6 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Build.VERSION import android.os.Build.VERSION
import android.widget.Toast
import androidx.biometric.BiometricManager import androidx.biometric.BiometricManager
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
@@ -44,11 +43,11 @@ import java.security.SecureRandom
@Composable @Composable
fun Settings(navCtrl: NavHostController) { fun Settings(navCtrl: NavHostController) {
MyScaffold(R.string.settings, 0.dp, navCtrl) { MyScaffold(R.string.settings, 0.dp, navCtrl) {
FunctionItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("Options") } FunctionItem(title = R.string.options, icon = R.drawable.tune_fill0) { navCtrl.navigate("Options") }
FunctionItem(R.string.appearance, "", R.drawable.format_paint_fill0) { navCtrl.navigate("Appearance") } FunctionItem(title = R.string.appearance, icon = R.drawable.format_paint_fill0) { navCtrl.navigate("Appearance") }
FunctionItem(R.string.security, "", R.drawable.lock_fill0) { navCtrl.navigate("AuthSettings") } FunctionItem(title = R.string.security, icon = R.drawable.lock_fill0) { navCtrl.navigate("AuthSettings") }
FunctionItem(R.string.api, "", R.drawable.apps_fill0) { navCtrl.navigate("ApiSettings") } FunctionItem(title = R.string.api, icon = R.drawable.apps_fill0) { navCtrl.navigate("ApiSettings") }
FunctionItem(R.string.about, "", R.drawable.info_fill0) { navCtrl.navigate("About") } 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) val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
MyScaffold(R.string.options, 0.dp, navCtrl) { MyScaffold(R.string.options, 0.dp, navCtrl) {
SwitchItem( SwitchItem(
R.string.show_dangerous_features, "", R.drawable.warning_fill0, R.string.show_dangerous_features, icon = R.drawable.warning_fill0,
{ sharedPref.getBoolean("dangerous_features", false) }, getState = { sharedPref.getBoolean("dangerous_features", false) },
{ sharedPref.edit().putBoolean("dangerous_features", it).apply() } 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) { MyScaffold(R.string.appearance, 0.dp, navCtrl) {
if(VERSION.SDK_INT >= 31) { if(VERSION.SDK_INT >= 31) {
SwitchItem( SwitchItem(R.string.material_you_color, state = theme.materialYou, onCheckedChange = { vm.theme.value = theme.copy(materialYou = it) })
R.string.material_you_color, "", null,
theme.materialYou,
{ vm.theme.value = theme.copy(materialYou = it) }
)
} }
Box { Box {
FunctionItem(R.string.dark_theme, stringResource(darkThemeTextID)) { darkThemeMenu = true } 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())) { AnimatedVisibility(theme.darkTheme == true || (theme.darkTheme == null && isSystemInDarkTheme())) {
SwitchItem( SwitchItem(R.string.black_theme, state = theme.blackTheme, onCheckedChange = { vm.theme.value = theme.copy(blackTheme = it) })
R.string.black_theme, "", null,
theme.blackTheme,
{ vm.theme.value = theme.copy(blackTheme = it) }
)
} }
} }
} }
@@ -127,8 +118,8 @@ fun AuthSettings(navCtrl: NavHostController) {
var auth by remember{ mutableStateOf(sharedPref.getBoolean("auth",false)) } var auth by remember{ mutableStateOf(sharedPref.getBoolean("auth",false)) }
MyScaffold(R.string.security, 0.dp, navCtrl) { MyScaffold(R.string.security, 0.dp, navCtrl) {
SwitchItem( SwitchItem(
R.string.lock_owndroid, "", null, auth, R.string.lock_owndroid, state = auth,
{ onCheckedChange = {
sharedPref.edit().putBoolean("auth", it).apply() sharedPref.edit().putBoolean("auth", it).apply()
auth = sharedPref.getBoolean("auth", false) auth = sharedPref.getBoolean("auth", false)
} }
@@ -143,13 +134,13 @@ fun AuthSettings(navCtrl: NavHostController) {
} }
} }
SwitchItem( SwitchItem(
R.string.enable_bio_auth, "", null, bioAuth != 0, R.string.enable_bio_auth, state = bioAuth != 0,
{ bioAuth = if(it) 1 else 0; sharedPref.edit().putInt("biometrics_auth", bioAuth).apply() }, bioAuth != 2 onCheckedChange = { bioAuth = if(it) 1 else 0; sharedPref.edit().putInt("biometrics_auth", bioAuth).apply() }, enabled = bioAuth != 2
) )
SwitchItem( SwitchItem(
R.string.lock_in_background, "", null, R.string.lock_in_background,
{ sharedPref.getBoolean("lock_in_background", false) }, getState = { sharedPref.getBoolean("lock_in_background", false) },
{ sharedPref.edit().putBoolean("lock_in_background", it).apply() } onCheckedChange = { sharedPref.edit().putBoolean("lock_in_background", it).apply() }
) )
} }
} }
@@ -167,7 +158,7 @@ fun ApiSettings(navCtrl: NavHostController) {
if(!enabled) remove("api_key") 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) { if(enabled) {
var key by remember { mutableStateOf("") } var key by remember { mutableStateOf("") }
OutlinedTextField( OutlinedTextField(
@@ -189,7 +180,7 @@ fun ApiSettings(navCtrl: NavHostController) {
modifier = Modifier.fillMaxWidth().padding(bottom = 10.dp), modifier = Modifier.fillMaxWidth().padding(bottom = 10.dp),
onClick = { onClick = {
sharedPref.edit().putString("api_key", key).apply() 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)) Text(stringResource(R.string.apply))

View File

@@ -5,18 +5,13 @@ import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent
import android.net.Uri import android.net.Uri
import android.widget.Toast import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.annotation.StringRes import androidx.annotation.StringRes
import com.bintianqi.owndroid.dpm.addDeviceAdmin 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.FileNotFoundException
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
@@ -27,17 +22,13 @@ import java.time.format.DateTimeFormatter
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
lateinit var getFile: ActivityResultLauncher<Intent>
val fileUriFlow = MutableStateFlow(Uri.parse(""))
var zhCN = true var zhCN = true
fun uriToStream( fun uriToStream(
context: Context, context: Context,
uri: Uri?, uri: Uri,
operation: (stream: InputStream)->Unit operation: (stream: InputStream)->Unit
){ ){
if(uri!=null){
try { try {
val stream = context.contentResolver.openInputStream(uri) val stream = context.contentResolver.openInputStream(uri)
if(stream != null) { operation(stream) } if(stream != null) { operation(stream) }
@@ -46,11 +37,6 @@ fun uriToStream(
catch(_: FileNotFoundException) { Toast.makeText(context, R.string.file_not_exist, Toast.LENGTH_SHORT).show() } 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() } catch(_: IOException) { Toast.makeText(context, R.string.io_exception, Toast.LENGTH_SHORT).show() }
} }
}
fun MutableList<Int>.toggle(status: Boolean, element: Int) {
if(status) add(element) else remove(element)
}
fun writeClipBoard(context: Context, string: String):Boolean{ fun writeClipBoard(context: Context, string: String):Boolean{
val clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
@@ -62,40 +48,13 @@ fun writeClipBoard(context: Context, string: String):Boolean{
return true return true
} }
lateinit var exportFile: ActivityResultLauncher<Intent>
var exportFilePath: String? = null
var isExportingSecurityOrNetworkLogs = false
fun registerActivityResult(context: ComponentActivity) { 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()) {}
addDeviceAdmin = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { addDeviceAdmin = context.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
val dpm = context.applicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager val dpm = context.applicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
if(dpm.isAdminActive(ComponentName(context.applicationContext, Receiver::class.java))) { if(dpm.isAdminActive(ComponentName(context.applicationContext, Receiver::class.java))) {
backToHomeStateFlow.value = true 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 { fun formatFileSize(bytes: Long): String {

View File

@@ -18,6 +18,8 @@ import android.os.Build.VERSION
import android.os.Looper import android.os.Looper
import android.provider.Settings import android.provider.Settings
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.background import androidx.compose.foundation.background
@@ -50,7 +52,6 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateListOf
@@ -82,8 +83,7 @@ import com.bintianqi.owndroid.InstallAppActivity
import com.bintianqi.owndroid.MyViewModel import com.bintianqi.owndroid.MyViewModel
import com.bintianqi.owndroid.PackageInstallerReceiver import com.bintianqi.owndroid.PackageInstallerReceiver
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.fileUriFlow import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.getFile
import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.Animations
import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.InfoCard 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)) { if(VERSION.SDK_INT >= 24 && profileOwner && dpm.isManagedProfile(receiver)) {
Text(text = stringResource(R.string.scope_is_work_profile), textAlign = TextAlign.Center,modifier = Modifier.fillMaxWidth()) 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) val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.setData(Uri.parse("package:$pkgName")) intent.setData(Uri.parse("package:$pkgName"))
startActivity(context, intent, null) startActivity(context, intent, null)
} }
if(VERSION.SDK_INT >= 24) { if(VERSION.SDK_INT >= 24) {
SwitchItem( SwitchItem(
title = R.string.suspend, desc = "", icon = R.drawable.block_fill0, title = R.string.suspend, icon = R.drawable.block_fill0,
state = suspend, state = suspend,
onCheckedChange = { appControlAction = 1; appControl(it) }, onCheckedChange = { appControlAction = 1; appControl(it) },
onClickBlank = { appControlAction = 1; dialogStatus = 4 } onClickBlank = { appControlAction = 1; dialogStatus = 4 }
@@ -220,48 +220,47 @@ private fun Home(navCtrl:NavHostController, pkgName: String) {
onClickBlank = { appControlAction = 2; dialogStatus = 4 } onClickBlank = { appControlAction = 2; dialogStatus = 4 }
) )
SwitchItem( 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, state = blockUninstall,
onCheckedChange = { appControlAction = 3; appControl(it) }, onCheckedChange = { appControlAction = 3; appControl(it) },
onClickBlank = { appControlAction = 3; dialogStatus = 4 } onClickBlank = { appControlAction = 3; dialogStatus = 4 }
) )
if((VERSION.SDK_INT >= 33 && profileOwner) || (VERSION.SDK_INT >= 30 && deviceOwner)) { if((VERSION.SDK_INT >= 33 && profileOwner) || (VERSION.SDK_INT >= 30 && deviceOwner)) {
FunctionItem(R.string.ucd, "", R.drawable.do_not_touch_fill0) { navCtrl.navigate("UserControlDisabled") } FunctionItem(title = R.string.ucd, icon = R.drawable.do_not_touch_fill0) { navCtrl.navigate("UserControlDisabled") }
} }
if(VERSION.SDK_INT>=23) { 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)) { 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) { 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) { 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(title = R.string.permitted_accessibility_services, icon = R.drawable.settings_accessibility_fill0) { navCtrl.navigate("Accessibility") }
FunctionItem(R.string.permitted_ime, "", R.drawable.keyboard_fill0) { navCtrl.navigate("IME") } FunctionItem(title = R.string.permitted_ime, icon = R.drawable.keyboard_fill0) { navCtrl.navigate("IME") }
FunctionItem(R.string.enable_system_app, "", R.drawable.enable_fill0) { FunctionItem(title = R.string.enable_system_app, icon = R.drawable.enable_fill0) {
if(pkgName != "") dialogStatus = 1 if(pkgName != "") dialogStatus = 1
} }
if(VERSION.SDK_INT >= 28 && deviceOwner) { if(VERSION.SDK_INT >= 28 && deviceOwner) {
FunctionItem(R.string.keep_uninstalled_packages, "", R.drawable.delete_fill0) { navCtrl.navigate("KeepUninstalled") } FunctionItem(title = R.string.keep_uninstalled_packages, icon = R.drawable.delete_fill0) { navCtrl.navigate("KeepUninstalled") }
} }
if(VERSION.SDK_INT >= 28) { if(VERSION.SDK_INT >= 28) {
FunctionItem(R.string.clear_app_storage, "", R.drawable.mop_fill0) { FunctionItem(title = R.string.clear_app_storage, icon = R.drawable.mop_fill0) {
if(pkgName != "") dialogStatus = 2 if(pkgName != "") dialogStatus = 2
} }
} }
FunctionItem(R.string.install_app, "", R.drawable.install_mobile_fill0) { navCtrl.navigate("InstallApp") } FunctionItem(title = R.string.install_app, icon = R.drawable.install_mobile_fill0) { navCtrl.navigate("InstallApp") }
FunctionItem(R.string.uninstall_app, "", R.drawable.delete_fill0) { navCtrl.navigate("UninstallApp") } FunctionItem(title = R.string.uninstall_app, icon = R.drawable.delete_fill0) { navCtrl.navigate("UninstallApp") }
if(VERSION.SDK_INT >= 34 && (deviceOwner || dpm.isOrgProfile(receiver))) { if(VERSION.SDK_INT >= 34 && (deviceOwner || dpm.isOrgProfile(receiver))) {
FunctionItem(R.string.set_default_dialer, "", R.drawable.call_fill0) { FunctionItem(title = R.string.set_default_dialer, icon = R.drawable.call_fill0) {
if(pkgName != "") dialogStatus = 3 if(pkgName != "") dialogStatus = 3
} }
} }
Spacer(Modifier.padding(vertical = 30.dp)) Spacer(Modifier.padding(vertical = 30.dp))
LaunchedEffect(Unit) { fileUriFlow.value = Uri.parse("") }
} }
if(dialogStatus == 1) AlertDialog( if(dialogStatus == 1) AlertDialog(
title = { Text(stringResource(R.string.enable_system_app)) }, title = { Text(stringResource(R.string.enable_system_app)) },
@@ -279,7 +278,7 @@ private fun Home(navCtrl:NavHostController, pkgName: String) {
onClick = { onClick = {
try { try {
dpm.enableSystemApp(receiver, pkgName) dpm.enableSystemApp(receiver, pkgName)
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
} catch(_: IllegalArgumentException) { } catch(_: IllegalArgumentException) {
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
} }
@@ -343,7 +342,7 @@ private fun Home(navCtrl:NavHostController, pkgName: String) {
onClick = { onClick = {
try{ try{
dpm.setDefaultDialerApplication(pkgName) dpm.setDefaultDialerApplication(pkgName)
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
} catch(_: IllegalArgumentException) { } catch(_: IllegalArgumentException) {
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show() 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)) Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.credential_manage_policy), style = typography.headlineLarge) Text(text = stringResource(R.string.credential_manage_policy), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
RadioButtonItem( RadioButtonItem(R.string.none, policyType == -1) { policyType = -1 }
R.string.none, RadioButtonItem(R.string.blacklist, policyType == PACKAGE_POLICY_BLOCKLIST) { policyType = PACKAGE_POLICY_BLOCKLIST }
policyType == -1, { policyType = -1 } RadioButtonItem(R.string.whitelist, policyType == PACKAGE_POLICY_ALLOWLIST){ policyType = PACKAGE_POLICY_ALLOWLIST }
)
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( RadioButtonItem(
R.string.whitelist_and_system_app, 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)) Spacer(Modifier.padding(vertical = 5.dp))
AnimatedVisibility(policyType != -1) { AnimatedVisibility(policyType != -1) {
Column { Column {
@@ -699,7 +686,7 @@ private fun CredentialManagePolicy(pkgName: String) {
} else { } else {
dpm.credentialManagerPolicy = null dpm.credentialManagerPolicy = null
} }
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
} catch(_: IllegalArgumentException) { } catch(_: IllegalArgumentException) {
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
} finally { } finally {
@@ -798,8 +785,8 @@ private fun PermittedIME(pkgName: String) {
Text(text = stringResource(R.string.permitted_ime), style = typography.headlineLarge) Text(text = stringResource(R.string.permitted_ime), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
SwitchItem( SwitchItem(
R.string.allow_all, "", null, allowAll, R.string.allow_all, state = allowAll,
{ onCheckedChange = {
dpm.setPermittedInputMethods(receiver, if(it) null else listOf()) dpm.setPermittedInputMethods(receiver, if(it) null else listOf())
refresh() refresh()
}, padding = false }, padding = false
@@ -918,8 +905,11 @@ private fun UninstallApp(pkgName: String) {
private fun InstallApp() { private fun InstallApp() {
val context = LocalContext.current val context = LocalContext.current
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
val selected = fileUriFlow.collectAsState().value != Uri.parse("")
val sharedPrefs = context.getSharedPreferences("data", Context.MODE_PRIVATE) 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())) { Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp)) Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.install_app), style = typography.headlineLarge) Text(text = stringResource(R.string.install_app), style = typography.headlineLarge)
@@ -930,19 +920,19 @@ private fun InstallApp() {
val installApkIntent = Intent(Intent.ACTION_GET_CONTENT) val installApkIntent = Intent(Intent.ACTION_GET_CONTENT)
installApkIntent.setType("application/vnd.android.package-archive") installApkIntent.setType("application/vnd.android.package-archive")
installApkIntent.addCategory(Intent.CATEGORY_OPENABLE) installApkIntent.addCategory(Intent.CATEGORY_OPENABLE)
getFile.launch(installApkIntent) getFileLauncher.launch(installApkIntent)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Text(stringResource(R.string.select_apk)) Text(stringResource(R.string.select_apk))
} }
AnimatedVisibility(selected) { AnimatedVisibility(apkFileUri != null) {
Spacer(Modifier.padding(vertical = 3.dp)) Spacer(Modifier.padding(vertical = 3.dp))
Column(modifier = Modifier.fillMaxWidth()) { Column(modifier = Modifier.fillMaxWidth()) {
Button( Button(
onClick = { onClick = {
val intent = Intent(context, InstallAppActivity::class.java) val intent = Intent(context, InstallAppActivity::class.java)
intent.data = fileUriFlow.value intent.data = apkFileUri
context.startActivity(intent) context.startActivity(intent)
}, },
enabled = !sharedPrefs.getBoolean("dhizuku", false) && context.isDeviceOwner, enabled = !sharedPrefs.getBoolean("dhizuku", false) && context.isDeviceOwner,
@@ -953,7 +943,7 @@ private fun InstallApp() {
Button( Button(
onClick = { onClick = {
val intent = Intent(Intent.ACTION_INSTALL_PACKAGE) val intent = Intent(Intent.ACTION_INSTALL_PACKAGE)
intent.setData(fileUriFlow.value) intent.setData(apkFileUri)
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
context.startActivity(intent) context.startActivity(intent)
}, },

View File

@@ -42,8 +42,8 @@ import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonArray import kotlinx.serialization.json.putJsonArray
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream
lateinit var createManagedProfile: ActivityResultLauncher<Intent>
lateinit var addDeviceAdmin: ActivityResultLauncher<Intent> lateinit var addDeviceAdmin: ActivityResultLauncher<Intent>
val Context.isDeviceOwner: Boolean val Context.isDeviceOwner: Boolean
@@ -356,15 +356,10 @@ fun handleNetworkLogs(context: Context, batchToken: Long) {
} }
@RequiresApi(24) @RequiresApi(24)
fun handleSecurityLogs(context: Context) { fun processSecurityLogs(securityEvents: List<SecurityLog.SecurityEvent>, outputStream: OutputStream) {
val file = context.filesDir.resolve("SecurityLogs.json")
val json = Json { ignoreUnknownKeys = true; explicitNulls = false } val json = Json { ignoreUnknownKeys = true; explicitNulls = false }
val securityEvents = context.getDPM().retrieveSecurityLogs(context.getReceiver()) val buffer = outputStream.bufferedWriter()
securityEvents ?: return
val fileExist = file.exists()
val buffer = file.bufferedWriter()
securityEvents.forEachIndexed { index, event -> securityEvents.forEachIndexed { index, event ->
if(fileExist && index == 0) buffer.write(",")
val item = buildJsonObject { val item = buildJsonObject {
put("time_nanos", event.timeNanos) put("time_nanos", event.timeNanos)
put("tag", event.tag) put("tag", event.tag)

View File

@@ -19,6 +19,8 @@ import android.content.*
import android.os.Binder import android.os.Binder
import android.os.Build.VERSION import android.os.Build.VERSION
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
@@ -38,6 +40,7 @@ import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
@@ -52,6 +55,7 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.CardItem import com.bintianqi.owndroid.ui.CardItem
import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.CopyTextButton import com.bintianqi.owndroid.ui.CopyTextButton
@@ -69,19 +73,19 @@ fun WorkProfile(navCtrl: NavHostController) {
val profileOwner = context.isProfileOwner val profileOwner = context.isProfileOwner
MyScaffold(R.string.work_profile, 0.dp, navCtrl) { MyScaffold(R.string.work_profile, 0.dp, navCtrl) {
if(VERSION.SDK_INT >= 30 && profileOwner && dpm.isManagedProfile(receiver)) { 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))) { 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)) { 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)))) { 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)))) { 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 context = LocalContext.current
val receiver = context.getReceiver() val receiver = context.getReceiver()
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { }
MyScaffold(R.string.create_work_profile, 8.dp, navCtrl) { MyScaffold(R.string.create_work_profile, 8.dp, navCtrl) {
var skipEncrypt by remember { mutableStateOf(false) } var skipEncrypt by remember { mutableStateOf(false) }
var offlineProvisioning by remember { mutableStateOf(true) } var offlineProvisioning by remember { mutableStateOf(true) }
@@ -99,7 +104,7 @@ fun CreateWorkProfile(navCtrl: NavHostController) {
var migrateAccountType by remember { mutableStateOf("") } var migrateAccountType by remember { mutableStateOf("") }
var keepAccount by remember { mutableStateOf(true) } var keepAccount by remember { mutableStateOf(true) }
if(VERSION.SDK_INT >= 22) { if(VERSION.SDK_INT >= 22) {
CheckBoxItem(R.string.migrate_account, migrateAccount, { migrateAccount = it }) CheckBoxItem(R.string.migrate_account, migrateAccount) { migrateAccount = it }
AnimatedVisibility(migrateAccount) { AnimatedVisibility(migrateAccount) {
val fr = FocusRequester() val fr = FocusRequester()
Column(modifier = Modifier.padding(start = 10.dp)) { Column(modifier = Modifier.padding(start = 10.dp)) {
@@ -118,17 +123,13 @@ fun CreateWorkProfile(navCtrl: NavHostController) {
modifier = Modifier.fillMaxWidth().focusRequester(fr) modifier = Modifier.fillMaxWidth().focusRequester(fr)
) )
if(VERSION.SDK_INT >= 26) { 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) { if(VERSION.SDK_INT >= 24) CheckBoxItem(R.string.skip_encryption, skipEncrypt) { skipEncrypt = it }
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 >= 33) {
CheckBoxItem(R.string.offline_provisioning, offlineProvisioning, { offlineProvisioning = it })
}
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
Button( Button(
onClick = { onClick = {
@@ -147,7 +148,7 @@ fun CreateWorkProfile(navCtrl: NavHostController) {
} }
if(VERSION.SDK_INT >= 24) { intent.putExtra(EXTRA_PROVISIONING_SKIP_ENCRYPTION, skipEncrypt) } if(VERSION.SDK_INT >= 24) { intent.putExtra(EXTRA_PROVISIONING_SKIP_ENCRYPTION, skipEncrypt) }
if(VERSION.SDK_INT >= 33) { intent.putExtra(EXTRA_PROVISIONING_ALLOW_OFFLINE, offlineProvisioning) } if(VERSION.SDK_INT >= 33) { intent.putExtra(EXTRA_PROVISIONING_ALLOW_OFFLINE, offlineProvisioning) }
createManagedProfile.launch(intent) launcher.launch(intent)
} catch(_: ActivityNotFoundException) { } catch(_: ActivityNotFoundException) {
Toast.makeText(context, R.string.unsupported, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.unsupported, Toast.LENGTH_SHORT).show()
} }
@@ -188,10 +189,8 @@ fun SuspendPersonalApp(navCtrl: NavHostController) {
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
var suspend by remember { mutableStateOf(dpm.getPersonalAppsSuspendedReasons(receiver) != PERSONAL_APPS_NOT_SUSPENDED) } var suspend by remember { mutableStateOf(dpm.getPersonalAppsSuspendedReasons(receiver) != PERSONAL_APPS_NOT_SUSPENDED) }
MyScaffold(R.string.suspend_personal_app, 8.dp, navCtrl) { MyScaffold(R.string.suspend_personal_app, 8.dp, navCtrl) {
SwitchItem( SwitchItem(R.string.suspend_personal_app, state = suspend,
R.string.suspend_personal_app, "", null, onCheckedChange = {
suspend,
{
dpm.setPersonalAppsSuspended(receiver,it) dpm.setPersonalAppsSuspended(receiver,it)
suspend = dpm.getPersonalAppsSuspendedReasons(receiver) != PERSONAL_APPS_NOT_SUSPENDED suspend = dpm.getPersonalAppsSuspendedReasons(receiver) != PERSONAL_APPS_NOT_SUSPENDED
}, padding = false }, padding = false
@@ -217,7 +216,7 @@ fun SuspendPersonalApp(navCtrl: NavHostController) {
Button( Button(
onClick = { onClick = {
dpm.setManagedProfileMaximumTimeOff(receiver,time.toLong()) dpm.setManagedProfileMaximumTimeOff(receiver,time.toLong())
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
@@ -246,7 +245,7 @@ fun IntentFilter(navCtrl: NavHostController) {
Button( Button(
onClick = { onClick = {
dpm.addCrossProfileIntentFilter(receiver, IntentFilter(action), FLAG_PARENT_CAN_ACCESS_MANAGED) 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() modifier = Modifier.fillMaxWidth()
) { ) {
@@ -255,7 +254,7 @@ fun IntentFilter(navCtrl: NavHostController) {
Button( Button(
onClick = { onClick = {
dpm.addCrossProfileIntentFilter(receiver, IntentFilter(action), FLAG_MANAGED_CAN_ACCESS_PARENT) 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() modifier = Modifier.fillMaxWidth()
) { ) {
@@ -265,7 +264,7 @@ fun IntentFilter(navCtrl: NavHostController) {
Button( Button(
onClick = { onClick = {
dpm.clearCrossProfileIntentFilters(receiver) dpm.clearCrossProfileIntentFilters(receiver)
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
@@ -280,15 +279,14 @@ fun DeleteWorkProfile(navCtrl: NavHostController) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
var flag by remember { mutableIntStateOf(0) }
var warning by remember { mutableStateOf(false) } 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 silent by remember { mutableStateOf(false) }
var reason by remember { mutableStateOf("") } var reason by remember { mutableStateOf("") }
MyScaffold(R.string.delete_work_profile, 8.dp, navCtrl) { MyScaffold(R.string.delete_work_profile, 8.dp, navCtrl) {
CheckBoxItem(R.string.wipe_external_storage, externalStorage, { externalStorage = 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, euicc, { euicc = it }) } 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 }) CheckBoxItem(R.string.wipe_silently, silent) { silent = it }
AnimatedVisibility(!silent && VERSION.SDK_INT >= 28) { AnimatedVisibility(!silent && VERSION.SDK_INT >= 28) {
OutlinedTextField( OutlinedTextField(
value = reason, onValueChange = { reason = it }, value = reason, onValueChange = { reason = it },
@@ -322,9 +320,6 @@ fun DeleteWorkProfile(navCtrl: NavHostController) {
confirmButton = { confirmButton = {
TextButton( TextButton(
onClick = { 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) { if(VERSION.SDK_INT >= 28 && !silent) {
dpm.wipeData(flag, reason) dpm.wipeData(flag, reason)
} else { } else {

View File

@@ -124,10 +124,7 @@ import androidx.core.os.bundleOf
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import com.bintianqi.owndroid.MyViewModel import com.bintianqi.owndroid.MyViewModel
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.exportFile
import com.bintianqi.owndroid.exportFilePath
import com.bintianqi.owndroid.formatFileSize import com.bintianqi.owndroid.formatFileSize
import com.bintianqi.owndroid.isExportingSecurityOrNetworkLogs
import com.bintianqi.owndroid.showOperationResultToast import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.FunctionItem
@@ -157,30 +154,30 @@ fun Network(navCtrl:NavHostController) {
val sharedPref = context.getSharedPreferences("data", Context.MODE_PRIVATE) val sharedPref = context.getSharedPreferences("data", Context.MODE_PRIVATE)
val dhizuku = sharedPref.getBoolean("dhizuku", false) val dhizuku = sharedPref.getBoolean("dhizuku", false)
MyScaffold(R.string.network, 0.dp, navCtrl) { 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) { 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) { 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) { 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) { 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)))) { 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) { 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) { 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) { 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) } var dialog by remember { mutableIntStateOf(0) }
MyScaffold(R.string.options, 0.dp, navCtrl) { MyScaffold(R.string.options, 0.dp, navCtrl) {
if(VERSION.SDK_INT>=30 && (deviceOwner || dpm.isOrgProfile(receiver))) { if(VERSION.SDK_INT>=30 && (deviceOwner || dpm.isOrgProfile(receiver))) {
SwitchItem(R.string.lockdown_admin_configured_network, "", R.drawable.wifi_password_fill0, SwitchItem(R.string.lockdown_admin_configured_network, icon = R.drawable.wifi_password_fill0,
{ dpm.hasLockdownAdminConfiguredNetworks(receiver) }, { dpm.setConfiguredNetworksLockdownState(receiver,it) }, getState = { dpm.hasLockdownAdminConfiguredNetworks(receiver) }, onCheckedChange = { dpm.setConfiguredNetworksLockdownState(receiver,it) },
onClickBlank = { dialog = 1 } onClickBlank = { dialog = 1 }
) )
} }
@@ -280,11 +277,11 @@ fun Wifi(navCtrl: NavHostController) {
} }
} }
if(VERSION.SDK_INT >= 24 && (deviceOwner || orgProfileOwner)) { 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)) { if(VERSION.SDK_INT >= 33 && (deviceOwner || orgProfileOwner)) {
FunctionItem(R.string.min_wifi_security_level, "", null) { navCtrl.navigate("MinWifiSecurityLevel") } FunctionItem(R.string.min_wifi_security_level) { navCtrl.navigate("MinWifiSecurityLevel") }
FunctionItem(R.string.wifi_ssid_policy, "", null) { navCtrl.navigate("WifiSsidPolicy") } FunctionItem(R.string.wifi_ssid_policy) { navCtrl.navigate("WifiSsidPolicy") }
} }
} }
} else if(page == 1) { } else if(page == 1) {
@@ -397,8 +394,7 @@ private fun SavedNetworks(navCtrl: NavHostController) {
) { ) {
Button( Button(
onClick = { onClick = {
val success = wm.enableNetwork(network.networkId, false) context.showOperationResultToast(wm.enableNetwork(network.networkId, false))
Toast.makeText(context, if(success) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
networkDetailsDialog = -1 networkDetailsDialog = -1
refresh() refresh()
}, },
@@ -408,8 +404,7 @@ private fun SavedNetworks(navCtrl: NavHostController) {
} }
Button( Button(
onClick = { onClick = {
val success = wm.disableNetwork(network.networkId) context.showOperationResultToast(wm.disableNetwork(network.networkId))
Toast.makeText(context, if(success) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
networkDetailsDialog = -1 networkDetailsDialog = -1
refresh() refresh()
}, },
@@ -431,8 +426,7 @@ private fun SavedNetworks(navCtrl: NavHostController) {
} }
TextButton( TextButton(
onClick = { onClick = {
val success = wm.removeNetwork(network.networkId) context.showOperationResultToast(wm.removeNetwork(network.networkId))
Toast.makeText(context, if(success) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
networkDetailsDialog = -1 networkDetailsDialog = -1
refresh() refresh()
}, },
@@ -526,7 +520,7 @@ private fun AddNetwork(wifiConfig: WifiConfiguration? = null, navCtrl: NavHostCo
value = ssid, onValueChange = { ssid = it }, label = { Text("SSID") }, value = ssid, onValueChange = { ssid = it }, label = { Text("SSID") },
modifier = Modifier.fillMaxWidth().padding(bottom = 4.dp) 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) { if(VERSION.SDK_INT >= 30) {
// TODO: more protocols // TODO: more protocols
val securityTypeTextMap = mutableMapOf( val securityTypeTextMap = mutableMapOf(
@@ -731,31 +725,15 @@ fun WifiSecurityLevel(navCtrl: NavHostController) {
var selectedWifiSecLevel by remember { mutableIntStateOf(0) } var selectedWifiSecLevel by remember { mutableIntStateOf(0) }
LaunchedEffect(Unit) { selectedWifiSecLevel = dpm.minimumRequiredWifiSecurityLevel } LaunchedEffect(Unit) { selectedWifiSecLevel = dpm.minimumRequiredWifiSecurityLevel }
MyScaffold(R.string.min_wifi_security_level, 8.dp, navCtrl) { MyScaffold(R.string.min_wifi_security_level, 8.dp, navCtrl) {
RadioButtonItem( RadioButtonItem(R.string.wifi_security_open, selectedWifiSecLevel == WIFI_SECURITY_OPEN) { selectedWifiSecLevel = WIFI_SECURITY_OPEN }
R.string.wifi_security_open, RadioButtonItem("WEP, WPA(2)-PSK", selectedWifiSecLevel == WIFI_SECURITY_PERSONAL) { selectedWifiSecLevel = WIFI_SECURITY_PERSONAL }
selectedWifiSecLevel == WIFI_SECURITY_OPEN, RadioButtonItem("WPA-EAP", selectedWifiSecLevel == WIFI_SECURITY_ENTERPRISE_EAP) { selectedWifiSecLevel = WIFI_SECURITY_ENTERPRISE_EAP }
{ selectedWifiSecLevel = WIFI_SECURITY_OPEN } RadioButtonItem("WPA3-192bit", selectedWifiSecLevel == WIFI_SECURITY_ENTERPRISE_192) { selectedWifiSecLevel = WIFI_SECURITY_ENTERPRISE_192 }
)
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)) Spacer(Modifier.padding(vertical = 5.dp))
Button( Button(
onClick = { onClick = {
dpm.minimumRequiredWifiSecurityLevel = selectedWifiSecLevel dpm.minimumRequiredWifiSecurityLevel = selectedWifiSecLevel
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
@@ -781,21 +759,13 @@ fun WifiSsidPolicy(navCtrl: NavHostController) {
ssidList.addAll(policy?.ssids ?: mutableSetOf()) ssidList.addAll(policy?.ssids ?: mutableSetOf())
} }
LaunchedEffect(Unit) { refreshPolicy() } LaunchedEffect(Unit) { refreshPolicy() }
RadioButtonItem( RadioButtonItem(R.string.none, selectedPolicyType == -1) { selectedPolicyType = -1 }
R.string.none, RadioButtonItem(R.string.whitelist, selectedPolicyType == WIFI_SSID_POLICY_TYPE_ALLOWLIST) {
selectedPolicyType == -1, selectedPolicyType = WIFI_SSID_POLICY_TYPE_ALLOWLIST
{ selectedPolicyType = -1 } }
) RadioButtonItem(R.string.blacklist, selectedPolicyType == WIFI_SSID_POLICY_TYPE_DENYLIST) {
RadioButtonItem( selectedPolicyType = WIFI_SSID_POLICY_TYPE_DENYLIST
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) { AnimatedVisibility(selectedPolicyType != -1) {
var inputSsid by remember { mutableStateOf("") } var inputSsid by remember { mutableStateOf("") }
Column { Column {
@@ -838,7 +808,7 @@ fun WifiSsidPolicy(navCtrl: NavHostController) {
WifiSsidPolicy(selectedPolicyType, ssidList.toSet()) WifiSsidPolicy(selectedPolicyType, ssidList.toSet())
} }
refreshPolicy() refreshPolicy()
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
@@ -940,7 +910,7 @@ fun AlwaysOnVPNPackage(navCtrl: NavHostController, vm: MyViewModel) {
val setAlwaysOnVpn: (String?, Boolean)->Boolean = { vpnPkg: String?, lockdownEnabled: Boolean -> val setAlwaysOnVpn: (String?, Boolean)->Boolean = { vpnPkg: String?, lockdownEnabled: Boolean ->
try { try {
dpm.setAlwaysOnVpnPackage(receiver, vpnPkg, lockdownEnabled) dpm.setAlwaysOnVpnPackage(receiver, vpnPkg, lockdownEnabled)
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
true true
} catch(e: UnsupportedOperationException) { } catch(e: UnsupportedOperationException) {
e.printStackTrace() e.printStackTrace()
@@ -971,7 +941,7 @@ fun AlwaysOnVPNPackage(navCtrl: NavHostController, vm: MyViewModel) {
}, },
modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp) 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)) Spacer(Modifier.padding(vertical = 5.dp))
Button( Button(
onClick = { if(setAlwaysOnVpn(pkgName, lockdown)) refresh() }, onClick = { if(setAlwaysOnVpn(pkgName, lockdown)) refresh() },
@@ -1002,9 +972,9 @@ fun RecommendedGlobalProxy(navCtrl: NavHostController) {
var proxyPort by remember { mutableStateOf("") } var proxyPort by remember { mutableStateOf("") }
var exclList by remember { mutableStateOf("") } var exclList by remember { mutableStateOf("") }
MyScaffold(R.string.recommended_global_proxy, 8.dp, navCtrl) { MyScaffold(R.string.recommended_global_proxy, 8.dp, navCtrl) {
RadioButtonItem(R.string.proxy_type_off, proxyType == 0, { proxyType = 0 }) 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_pac, proxyType == 1) { proxyType = 1 }
RadioButtonItem(R.string.proxy_type_direct, proxyType == 2, { proxyType = 2 }) RadioButtonItem(R.string.proxy_type_direct, proxyType == 2) { proxyType = 2 }
AnimatedVisibility(proxyType != 0) { AnimatedVisibility(proxyType != 0) {
OutlinedTextField( OutlinedTextField(
value = proxyUri, value = proxyUri,
@@ -1017,7 +987,7 @@ fun RecommendedGlobalProxy(navCtrl: NavHostController) {
} }
AnimatedVisibility(proxyType == 1 && VERSION.SDK_INT >= 30) { AnimatedVisibility(proxyType == 1 && VERSION.SDK_INT >= 30) {
Box(modifier = Modifier.padding(top = 2.dp)) { 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) { AnimatedVisibility((proxyType == 1 && specifyPort && VERSION.SDK_INT >= 30) || proxyType == 2) {
@@ -1045,7 +1015,7 @@ fun RecommendedGlobalProxy(navCtrl: NavHostController) {
onClick = { onClick = {
if(proxyType == 0) { if(proxyType == 0) {
dpm.setRecommendedGlobalProxy(receiver, null) dpm.setRecommendedGlobalProxy(receiver, null)
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
return@Button return@Button
} }
if(proxyUri == "") { if(proxyUri == "") {
@@ -1076,7 +1046,7 @@ fun RecommendedGlobalProxy(navCtrl: NavHostController) {
return@Button return@Button
} }
dpm.setRecommendedGlobalProxy(receiver, proxyInfo) dpm.setRecommendedGlobalProxy(receiver, proxyInfo)
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
@@ -1094,11 +1064,24 @@ fun NetworkLogging(navCtrl: NavHostController) {
val receiver = context.getReceiver() val receiver = context.getReceiver()
val logFile = context.filesDir.resolve("NetworkLogs.json") val logFile = context.filesDir.resolve("NetworkLogs.json")
var fileSize by remember { mutableLongStateOf(0) } var fileSize by remember { mutableLongStateOf(0) }
LaunchedEffect(Unit) { LaunchedEffect(Unit) { fileSize = logFile.length() }
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) { 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))) Text(stringResource(R.string.log_file_size_is, formatFileSize(fileSize)))
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
Button( Button(
@@ -1107,9 +1090,7 @@ fun NetworkLogging(navCtrl: NavHostController) {
intent.addCategory(Intent.CATEGORY_OPENABLE) intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.setType("application/json") intent.setType("application/json")
intent.putExtra(Intent.EXTRA_TITLE, "NetworkLogs.json") intent.putExtra(Intent.EXTRA_TITLE, "NetworkLogs.json")
exportFilePath = logFile.path exportNetworkLogsLauncher.launch(intent)
isExportingSecurityOrNetworkLogs = true
exportFile.launch(intent)
}, },
enabled = fileSize > 0, enabled = fileSize > 0,
modifier = Modifier.fillMaxWidth(0.49F) modifier = Modifier.fillMaxWidth(0.49F)
@@ -1158,19 +1139,13 @@ fun WifiAuthKeypair(navCtrl: NavHostController) {
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Button( Button(
onClick = { onClick = { context.showOperationResultToast(dpm.grantKeyPairToWifiAuth(keyPair)) },
val result = dpm.grantKeyPairToWifiAuth(keyPair)
Toast.makeText(context, if(result) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
},
modifier = Modifier.fillMaxWidth(0.49F) modifier = Modifier.fillMaxWidth(0.49F)
) { ) {
Text(stringResource(R.string.grant)) Text(stringResource(R.string.grant))
} }
Button( Button(
onClick = { onClick = { context.showOperationResultToast(dpm.revokeKeyPairFromWifiAuth(keyPair)) },
val result = dpm.revokeKeyPairFromWifiAuth(keyPair)
Toast.makeText(context, if(result) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
},
modifier = Modifier.fillMaxWidth(0.96F) modifier = Modifier.fillMaxWidth(0.96F)
) { ) {
Text(stringResource(R.string.revoke)) Text(stringResource(R.string.revoke))
@@ -1221,10 +1196,7 @@ fun PreferentialNetworkService(navCtrl: NavHostController) {
} }
LaunchedEffect(Unit) { initialize() } LaunchedEffect(Unit) { initialize() }
MyScaffold(R.string.preferential_network_service, 8.dp, navCtrl) { MyScaffold(R.string.preferential_network_service, 8.dp, navCtrl) {
SwitchItem( SwitchItem(R.string.enabled, state = masterEnabled, onCheckedChange = { masterEnabled = it }, padding = false)
title = R.string.enabled, desc = "", icon = null,
state = masterEnabled, onCheckedChange = { masterEnabled = it }, padding = false
)
Row( Row(
horizontalArrangement = Arrangement.SpaceAround, horizontalArrangement = Arrangement.SpaceAround,
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
@@ -1272,7 +1244,7 @@ fun PreferentialNetworkService(navCtrl: NavHostController) {
onClick = { onClick = {
try { try {
saveCurrentConfig() saveCurrentConfig()
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
} catch(e: Exception) { } catch(e: Exception) {
e.printStackTrace() e.printStackTrace()
Toast.makeText(context, R.string.failed_to_save_current_config, Toast.LENGTH_SHORT).show() 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)) Icon(imageVector = Icons.Default.Delete, contentDescription = stringResource(R.string.delete_current_config))
} }
} }
SwitchItem( SwitchItem(title = R.string.enabled, state = enabled, onCheckedChange = { enabled = it }, padding = false)
title = R.string.enabled, desc = "", icon = null,
state = enabled, onCheckedChange = { enabled = it }, padding = false
)
OutlinedTextField( OutlinedTextField(
value = networkId, onValueChange = { networkId = it }, value = networkId, onValueChange = { networkId = it },
label = { Text(stringResource(R.string.network_id)) }, label = { Text(stringResource(R.string.network_id)) },
@@ -1304,11 +1273,11 @@ fun PreferentialNetworkService(navCtrl: NavHostController) {
modifier = Modifier.fillMaxWidth().padding(bottom = 6.dp) modifier = Modifier.fillMaxWidth().padding(bottom = 6.dp)
) )
SwitchItem( 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 state = allowFallback, onCheckedChange = { allowFallback = it }, padding = false
) )
if(VERSION.SDK_INT >= 34) SwitchItem( 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 state = blockNonMatching, onCheckedChange = { blockNonMatching = it }, padding = false
) )
OutlinedTextField( OutlinedTextField(
@@ -1328,7 +1297,7 @@ fun PreferentialNetworkService(navCtrl: NavHostController) {
dpm.isPreferentialNetworkServiceEnabled = masterEnabled dpm.isPreferentialNetworkServiceEnabled = masterEnabled
dpm.preferentialNetworkServiceConfigs = configs dpm.preferentialNetworkServiceConfigs = configs
initialize() initialize()
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth().padding(top = 12.dp) modifier = Modifier.fillMaxWidth().padding(top = 12.dp)
) { ) {
@@ -1351,7 +1320,11 @@ fun OverrideAPN(navCtrl: NavHostController) {
MyScaffold(R.string.override_apn_settings, 8.dp, navCtrl) { MyScaffold(R.string.override_apn_settings, 8.dp, navCtrl) {
Text(text = stringResource(id = R.string.developing)) Text(text = stringResource(id = R.string.developing))
Spacer(Modifier.padding(vertical = 5.dp)) 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)) Text(text = stringResource(R.string.total_apn_amount, setting.size))
if(setting.isNotEmpty()) { if(setting.isNotEmpty()) {
Text(text = stringResource(R.string.select_a_apn_or_create, setting.size)) Text(text = stringResource(R.string.select_a_apn_or_create, setting.size))
@@ -1468,10 +1441,10 @@ fun OverrideAPN(navCtrl: NavHostController) {
} }
Text(text = stringResource(R.string.auth_type), style = typography.titleLarge) Text(text = stringResource(R.string.auth_type), style = typography.titleLarge)
RadioButtonItem(R.string.none, selectedAuthType==AUTH_TYPE_NONE , { selectedAuthType=AUTH_TYPE_NONE }) RadioButtonItem(R.string.none, selectedAuthType==AUTH_TYPE_NONE) { selectedAuthType = AUTH_TYPE_NONE }
RadioButtonItem("CHAP", selectedAuthType == AUTH_TYPE_CHAP , { selectedAuthType = AUTH_TYPE_CHAP }) RadioButtonItem("CHAP", selectedAuthType == AUTH_TYPE_CHAP) { selectedAuthType = AUTH_TYPE_CHAP }
RadioButtonItem("PAP", selectedAuthType == AUTH_TYPE_PAP, { selectedAuthType = AUTH_TYPE_PAP }) 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("PAP/CHAP", selectedAuthType == AUTH_TYPE_PAP_OR_CHAP) { selectedAuthType = AUTH_TYPE_PAP_OR_CHAP }
if(VERSION.SDK_INT>=29) { if(VERSION.SDK_INT>=29) {
val ts = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager val ts = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
@@ -1578,10 +1551,10 @@ fun OverrideAPN(navCtrl: NavHostController) {
} }
Text(text = "MVNO", style = typography.titleLarge) Text(text = "MVNO", style = typography.titleLarge)
RadioButtonItem("SPN", mvnoType == MVNO_TYPE_SPN, { mvnoType = MVNO_TYPE_SPN }) RadioButtonItem("SPN", mvnoType == MVNO_TYPE_SPN) { mvnoType = MVNO_TYPE_SPN }
RadioButtonItem("IMSI", mvnoType == MVNO_TYPE_IMSI, { mvnoType = MVNO_TYPE_IMSI }) RadioButtonItem("IMSI", mvnoType == MVNO_TYPE_IMSI) { mvnoType = MVNO_TYPE_IMSI }
RadioButtonItem("GID", mvnoType == MVNO_TYPE_GID, { mvnoType = MVNO_TYPE_GID }) RadioButtonItem("GID", mvnoType == MVNO_TYPE_GID) { mvnoType = MVNO_TYPE_GID }
RadioButtonItem("ICCID", mvnoType == MVNO_TYPE_ICCID, { mvnoType = MVNO_TYPE_ICCID }) RadioButtonItem("ICCID", mvnoType == MVNO_TYPE_ICCID) { mvnoType = MVNO_TYPE_ICCID }
Text(text = stringResource(R.string.network_type), style = typography.titleLarge) Text(text = stringResource(R.string.network_type), style = typography.titleLarge)
TextField( TextField(
@@ -1625,23 +1598,23 @@ fun OverrideAPN(navCtrl: NavHostController) {
} }
Text(text = stringResource(R.string.protocol), style = typography.titleLarge) Text(text = stringResource(R.string.protocol), style = typography.titleLarge)
RadioButtonItem("IPV4", protocol == PROTOCOL_IP, { protocol = PROTOCOL_IP }) RadioButtonItem("IPV4", protocol == PROTOCOL_IP) { protocol = PROTOCOL_IP }
RadioButtonItem("IPV6", protocol == PROTOCOL_IPV6, { protocol = PROTOCOL_IPV6 }) RadioButtonItem("IPV6", protocol == PROTOCOL_IPV6) { protocol = PROTOCOL_IPV6 }
RadioButtonItem("IPV4/IPV6", protocol == PROTOCOL_IPV4V6, { protocol = PROTOCOL_IPV4V6 }) RadioButtonItem("IPV4/IPV6", protocol == PROTOCOL_IPV4V6) { protocol = PROTOCOL_IPV4V6 }
RadioButtonItem("PPP", protocol == PROTOCOL_PPP, { protocol = PROTOCOL_PPP }) RadioButtonItem("PPP", protocol == PROTOCOL_PPP) { protocol = PROTOCOL_PPP }
if(VERSION.SDK_INT>=29) { if(VERSION.SDK_INT>=29) {
RadioButtonItem("non-IP", protocol == PROTOCOL_NON_IP, { protocol = PROTOCOL_NON_IP }) RadioButtonItem("non-IP", protocol == PROTOCOL_NON_IP) { protocol = PROTOCOL_NON_IP }
RadioButtonItem("Unstructured", protocol == PROTOCOL_UNSTRUCTURED, { protocol = PROTOCOL_UNSTRUCTURED }) RadioButtonItem("Unstructured", protocol == PROTOCOL_UNSTRUCTURED) { protocol = PROTOCOL_UNSTRUCTURED }
} }
Text(text = stringResource(R.string.roaming_protocol), style = typography.titleLarge) Text(text = stringResource(R.string.roaming_protocol), style = typography.titleLarge)
RadioButtonItem("IPV4", roamingProtocol == PROTOCOL_IP, { roamingProtocol = PROTOCOL_IP }) RadioButtonItem("IPV4", roamingProtocol == PROTOCOL_IP) { roamingProtocol = PROTOCOL_IP }
RadioButtonItem("IPV6", roamingProtocol == PROTOCOL_IPV6, { roamingProtocol = PROTOCOL_IPV6 }) RadioButtonItem("IPV6", roamingProtocol == PROTOCOL_IPV6) { roamingProtocol = PROTOCOL_IPV6 }
RadioButtonItem("IPV4/IPV6", roamingProtocol == PROTOCOL_IPV4V6, { roamingProtocol = PROTOCOL_IPV4V6 }) RadioButtonItem("IPV4/IPV6", roamingProtocol == PROTOCOL_IPV4V6) { roamingProtocol = PROTOCOL_IPV4V6 }
RadioButtonItem("PPP", roamingProtocol == PROTOCOL_PPP, { roamingProtocol = PROTOCOL_PPP}) RadioButtonItem("PPP", roamingProtocol == PROTOCOL_PPP) { roamingProtocol = PROTOCOL_PPP }
if(VERSION.SDK_INT>=29) { if(VERSION.SDK_INT>=29) {
RadioButtonItem("non-IP", roamingProtocol == PROTOCOL_NON_IP, { roamingProtocol = PROTOCOL_NON_IP }) RadioButtonItem("non-IP", roamingProtocol == PROTOCOL_NON_IP) { roamingProtocol = PROTOCOL_NON_IP }
RadioButtonItem("Unstructured", roamingProtocol == PROTOCOL_UNSTRUCTURED, { roamingProtocol = PROTOCOL_UNSTRUCTURED }) RadioButtonItem("Unstructured", roamingProtocol == PROTOCOL_UNSTRUCTURED) { roamingProtocol = PROTOCOL_UNSTRUCTURED }
} }
var finalStep by remember { mutableStateOf(false) } var finalStep by remember { mutableStateOf(false) }
@@ -1688,20 +1661,14 @@ fun OverrideAPN(navCtrl: NavHostController) {
}else{ }else{
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Button( Button(
onClick = { onClick = { context.showOperationResultToast(dpm.updateOverrideApn(receiver, id, result)) },
val success = dpm.updateOverrideApn(receiver,id,result) modifier = Modifier.fillMaxWidth(0.49F)
Toast.makeText(context, if(success) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
},
Modifier.fillMaxWidth(0.49F)
) { ) {
Text(stringResource(R.string.update)) Text(stringResource(R.string.update))
} }
Button( Button(
onClick = { onClick = { context.showOperationResultToast(dpm.removeOverrideApn(receiver,id)) },
val success = dpm.removeOverrideApn(receiver,id) modifier = Modifier.fillMaxWidth(0.96F)
Toast.makeText(context, if(success) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
},
Modifier.fillMaxWidth(0.96F)
) { ) {
Text(stringResource(R.string.remove)) Text(stringResource(R.string.remove))
} }

View File

@@ -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_FEATURES_NONE
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_IRIS 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_CAMERA
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL 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.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
@@ -70,7 +68,7 @@ import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat.startActivity import androidx.core.content.ContextCompat.startActivity
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import com.bintianqi.owndroid.R 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.CardItem
import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.FunctionItem
@@ -89,34 +87,34 @@ fun Password(navCtrl: NavHostController) {
val profileOwner = context.isProfileOwner val profileOwner = context.isProfileOwner
var dialog by remember { mutableIntStateOf(0) } var dialog by remember { mutableIntStateOf(0) }
MyScaffold(R.string.password_and_keyguard, 0.dp, navCtrl) { 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(sharedPrefs.getBoolean("dangerous_features", false)) {
if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) { 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) { 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)) { 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) { 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) { if(deviceOwner) {
FunctionItem(R.string.max_time_to_lock, "", R.drawable.schedule_fill0) { dialog = 1 } FunctionItem(R.string.max_time_to_lock, icon = R.drawable.schedule_fill0) { dialog = 1 }
FunctionItem(R.string.pwd_expiration_timeout, "", R.drawable.lock_clock_fill0) { dialog = 3 } FunctionItem(R.string.pwd_expiration_timeout, icon = R.drawable.lock_clock_fill0) { dialog = 3 }
FunctionItem(R.string.max_pwd_fail, "", R.drawable.no_encryption_fill0) { dialog = 4 } FunctionItem(R.string.max_pwd_fail, icon = R.drawable.no_encryption_fill0) { dialog = 4 }
} }
if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) { 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){ 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)) { 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) { if(dialog != 0) {
@@ -259,11 +257,7 @@ fun ResetPasswordToken(navCtrl: NavHostController) {
Button( Button(
onClick = { onClick = {
try { try {
Toast.makeText( context.showOperationResultToast(dpm.setResetPasswordToken(receiver, tokenByteArray))
context,
if(dpm.setResetPasswordToken(receiver, tokenByteArray)) R.string.success else R.string.failed,
Toast.LENGTH_SHORT
).show()
} catch(_:SecurityException) { } catch(_:SecurityException) {
Toast.makeText(context, R.string.security_exception, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.security_exception, Toast.LENGTH_SHORT).show()
} }
@@ -289,13 +283,7 @@ fun ResetPasswordToken(navCtrl: NavHostController) {
Text(stringResource(R.string.activate)) Text(stringResource(R.string.activate))
} }
Button( Button(
onClick = { onClick = { context.showOperationResultToast(dpm.clearResetPasswordToken(receiver)) },
Toast.makeText(
context,
if(dpm.clearResetPasswordToken(receiver)) R.string.success else R.string.failed,
Toast.LENGTH_SHORT
).show()
},
modifier = Modifier.fillMaxWidth(0.96F) modifier = Modifier.fillMaxWidth(0.96F)
) { ) {
Text(stringResource(R.string.clear)) Text(stringResource(R.string.clear))
@@ -316,7 +304,7 @@ fun ResetPassword(navCtrl: NavHostController) {
var useToken by remember { mutableStateOf(false) } var useToken by remember { mutableStateOf(false) }
var token by remember { mutableStateOf("") } var token by remember { mutableStateOf("") }
val tokenByteArray = token.toByteArray() val tokenByteArray = token.toByteArray()
val flags = remember { mutableStateListOf<Int>() } var flag by remember { mutableIntStateOf(0) }
var confirmDialog by remember { mutableStateOf(false) } var confirmDialog by remember { mutableStateOf(false) }
MyScaffold(R.string.reset_password, 8.dp, navCtrl) { MyScaffold(R.string.reset_password, 8.dp, navCtrl) {
if(VERSION.SDK_INT >= 26) { if(VERSION.SDK_INT >= 26) {
@@ -342,15 +330,13 @@ fun ResetPassword(navCtrl: NavHostController) {
if(VERSION.SDK_INT >= 23) { if(VERSION.SDK_INT >= 23) {
CheckBoxItem( CheckBoxItem(
R.string.do_not_ask_credentials_on_boot, R.string.do_not_ask_credentials_on_boot,
RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT in flags, flag and RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT != 0
{ flags.toggle(it, RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT) } ) { flag = flag xor RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT }
)
} }
CheckBoxItem( CheckBoxItem(
R.string.reset_password_require_entry, R.string.reset_password_require_entry,
RESET_PASSWORD_REQUIRE_ENTRY in flags, flag and RESET_PASSWORD_REQUIRE_ENTRY != 0
{ flags.toggle(it, RESET_PASSWORD_REQUIRE_ENTRY) } ) { flag = flag xor RESET_PASSWORD_REQUIRE_ENTRY }
)
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
if(VERSION.SDK_INT >= 26) { if(VERSION.SDK_INT >= 26) {
Button( Button(
@@ -402,14 +388,12 @@ fun ResetPassword(navCtrl: NavHostController) {
confirmButton = { confirmButton = {
TextButton( TextButton(
onClick = { onClick = {
var resetFlag = 0
flags.forEach { resetFlag += it }
val success = if(VERSION.SDK_INT >= 26 && useToken) { val success = if(VERSION.SDK_INT >= 26 && useToken) {
dpm.resetPasswordWithToken(receiver, password, tokenByteArray, resetFlag) dpm.resetPasswordWithToken(receiver, password, tokenByteArray, flag)
} else { } 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 = "" password = ""
confirmDialog = false confirmDialog = false
}, },
@@ -443,13 +427,13 @@ fun PasswordComplexity(navCtrl: NavHostController) {
LaunchedEffect(Unit) { selectedItem = dpm.requiredPasswordComplexity } LaunchedEffect(Unit) { selectedItem = dpm.requiredPasswordComplexity }
MyScaffold(R.string.required_password_complexity, 8.dp, navCtrl) { MyScaffold(R.string.required_password_complexity, 8.dp, navCtrl) {
passwordComplexity.forEach { 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)) Spacer(Modifier.padding(vertical = 5.dp))
Button( Button(
onClick = { onClick = {
dpm.requiredPasswordComplexity = selectedItem dpm.requiredPasswordComplexity = selectedItem
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
@@ -470,86 +454,52 @@ fun DisableKeyguardFeatures(navCtrl: NavHostController) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
var state by remember { mutableIntStateOf(-1) } var flag by remember { mutableIntStateOf(0) }
var shortcuts by remember { mutableStateOf(false) } var mode by remember { mutableIntStateOf(0) } // 0:Enable all, 1:Disable all, 2:Custom
var biometrics by remember { mutableStateOf(false) } val flagsLiat = mutableListOf(
var iris by remember { mutableStateOf(false) } R.string.disable_keyguard_features_widgets to KEYGUARD_DISABLE_WIDGETS_ALL,
var face by remember { mutableStateOf(false) } R.string.disable_keyguard_features_camera to KEYGUARD_DISABLE_SECURE_CAMERA,
var remote by remember { mutableStateOf(false) } R.string.disable_keyguard_features_notification to KEYGUARD_DISABLE_SECURE_NOTIFICATIONS,
var fingerprint by remember { mutableStateOf(false) } R.string.disable_keyguard_features_unredacted_notification to KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS,
var agents by remember { mutableStateOf(false) } R.string.disable_keyguard_features_trust_agents to KEYGUARD_DISABLE_TRUST_AGENTS,
var unredacted by remember { mutableStateOf(false) } R.string.disable_keyguard_features_fingerprint to KEYGUARD_DISABLE_FINGERPRINT
var notification by remember { mutableStateOf(false) } )
var camera by remember { mutableStateOf(false) } if(VERSION.SDK_INT >= 28) {
var widgets by remember { mutableStateOf(false) } flagsLiat +=R.string.disable_keyguard_features_face to KEYGUARD_DISABLE_FACE
val calculateCustomFeature = { flagsLiat += R.string.disable_keyguard_features_iris to KEYGUARD_DISABLE_IRIS
var calculate = dpm.getKeyguardDisabledFeatures(receiver) flagsLiat += R.string.disable_keyguard_features_biometrics to KEYGUARD_DISABLE_BIOMETRICS
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 }
} }
} if(VERSION.SDK_INT >= 34) flagsLiat += R.string.disable_keyguard_features_shortcuts to KEYGUARD_DISABLE_SHORTCUTS_ALL
if(state==-1) { fun refresh() {
state = when(dpm.getKeyguardDisabledFeatures(receiver)) { flag = dpm.getKeyguardDisabledFeatures(receiver)
mode = when(flag) {
KEYGUARD_DISABLE_FEATURES_NONE -> 0 KEYGUARD_DISABLE_FEATURES_NONE -> 0
KEYGUARD_DISABLE_FEATURES_ALL -> 1 KEYGUARD_DISABLE_FEATURES_ALL -> 1
else -> 2 else -> 2
} }
calculateCustomFeature()
} }
LaunchedEffect(mode) { if(mode != 2) flag = dpm.getKeyguardDisabledFeatures(receiver) }
LaunchedEffect(Unit) { refresh() }
MyScaffold(R.string.disable_keyguard_features, 8.dp, navCtrl) { MyScaffold(R.string.disable_keyguard_features, 8.dp, navCtrl) {
RadioButtonItem(R.string.enable_all, state == 0, { state = 0 }) RadioButtonItem(R.string.enable_all, mode == 0) { mode = 0 }
RadioButtonItem(R.string.disable_all, state == 1, { state = 1 }) RadioButtonItem(R.string.disable_all, mode == 1) { mode = 1 }
RadioButtonItem(R.string.custom, state == 2 , { state = 2 }) RadioButtonItem(R.string.custom, mode == 2) { mode = 2 }
AnimatedVisibility(state==2) { AnimatedVisibility(mode == 2) {
Column { Column {
CheckBoxItem(R.string.disable_keyguard_features_widgets, widgets, { widgets = it }) flagsLiat.forEach {
CheckBoxItem(R.string.disable_keyguard_features_camera, camera, { camera = it }) CheckBoxItem(it.first, flag and it.second == it.second) { checked ->
CheckBoxItem(R.string.disable_keyguard_features_notification, notification, { notification = it }) flag = if(checked) flag or it.second else flag and (flag xor it.second)
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 })
} }
if(VERSION.SDK_INT >= 34) { CheckBoxItem(R.string.disable_keyguard_features_shortcuts, shortcuts, { shortcuts = it }) }
} }
} }
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
Button( Button(
onClick = { onClick = {
var result = 0 val disabledFeatures = if(mode == 0) KEYGUARD_DISABLE_FEATURES_NONE else if(mode == 1) KEYGUARD_DISABLE_FEATURES_ALL else flag
if(state==0) { result = 0 } dpm.setKeyguardDisabledFeatures(receiver, disabledFeatures)
else if(state==1) { result = KEYGUARD_DISABLE_FEATURES_ALL } refresh()
else{ context.showOperationResultToast(true)
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()
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
@@ -576,13 +526,13 @@ fun PasswordQuality(navCtrl: NavHostController) {
LaunchedEffect(Unit) { selectedItem=dpm.getPasswordQuality(receiver) } LaunchedEffect(Unit) { selectedItem=dpm.getPasswordQuality(receiver) }
MyScaffold(R.string.required_password_quality, 8.dp, navCtrl) { MyScaffold(R.string.required_password_quality, 8.dp, navCtrl) {
passwordQuality.forEach { 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)) Spacer(Modifier.padding(vertical = 5.dp))
Button( Button(
onClick = { onClick = {
dpm.setPasswordQuality(receiver,selectedItem) dpm.setPasswordQuality(receiver,selectedItem)
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {

View File

@@ -31,6 +31,7 @@ import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.backToHomeStateFlow import com.bintianqi.owndroid.backToHomeStateFlow
import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.* import com.bintianqi.owndroid.ui.*
import com.bintianqi.owndroid.writeClipBoard import com.bintianqi.owndroid.writeClipBoard
import com.bintianqi.owndroid.yesOrNo import com.bintianqi.owndroid.yesOrNo
@@ -56,9 +57,9 @@ fun Permissions(navCtrl: NavHostController) {
MyScaffold(R.string.permissions, 0.dp, navCtrl) { MyScaffold(R.string.permissions, 0.dp, navCtrl) {
if(!dpm.isDeviceOwnerApp(context.packageName)) { if(!dpm.isDeviceOwnerApp(context.packageName)) {
SwitchItem( SwitchItem(
R.string.dhizuku, "", null, R.string.dhizuku,
{ sharedPref.getBoolean("dhizuku", false) }, getState = { sharedPref.getBoolean("dhizuku", false) },
{ toggleDhizukuMode(it, context) }, onCheckedChange = { toggleDhizukuMode(it, context) },
onClickBlank = { dialog = 4 } onClickBlank = { dialog = 4 }
) )
} }
@@ -78,7 +79,7 @@ fun Permissions(navCtrl: NavHostController) {
operation = { navCtrl.navigate("DeviceOwner") } operation = { navCtrl.navigate("DeviceOwner") }
) )
} }
FunctionItem(R.string.shizuku,"") { FunctionItem(R.string.shizuku) {
try { try {
if(Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) { navCtrl.navigate("Shizuku") } if(Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) { navCtrl.navigate("Shizuku") }
else if(Shizuku.shouldShowRequestPermissionRationale()) { else if(Shizuku.shouldShowRequestPermissionRationale()) {
@@ -102,24 +103,24 @@ fun Permissions(navCtrl: NavHostController) {
Toast.makeText(context, R.string.shizuku_not_started, Toast.LENGTH_SHORT).show() 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)) { 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)) { 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 != "") { 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))) { 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) { 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)) { 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) { if(dialog != 0) {
@@ -254,7 +255,7 @@ fun LockScreenInfo(navCtrl: NavHostController) {
onClick = { onClick = {
focusMgr.clearFocus() focusMgr.clearFocus()
dpm.setDeviceOwnerLockScreenInfo(receiver,infoText) dpm.setDeviceOwnerLockScreenInfo(receiver,infoText)
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
@@ -264,7 +265,7 @@ fun LockScreenInfo(navCtrl: NavHostController) {
onClick = { onClick = {
focusMgr.clearFocus() focusMgr.clearFocus()
dpm.setDeviceOwnerLockScreenInfo(receiver, null) dpm.setDeviceOwnerLockScreenInfo(receiver, null)
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
@@ -521,7 +522,7 @@ fun SupportMessages(navCtrl: NavHostController) {
onClick = { onClick = {
dpm.setShortSupportMessage(receiver, shortMsg) dpm.setShortSupportMessage(receiver, shortMsg)
refreshMsg() refreshMsg()
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth(0.49F) modifier = Modifier.fillMaxWidth(0.49F)
) { ) {
@@ -531,7 +532,7 @@ fun SupportMessages(navCtrl: NavHostController) {
onClick = { onClick = {
dpm.setShortSupportMessage(receiver, null) dpm.setShortSupportMessage(receiver, null)
refreshMsg() refreshMsg()
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth(0.96F) modifier = Modifier.fillMaxWidth(0.96F)
) { ) {
@@ -552,7 +553,7 @@ fun SupportMessages(navCtrl: NavHostController) {
onClick = { onClick = {
dpm.setLongSupportMessage(receiver, longMsg) dpm.setLongSupportMessage(receiver, longMsg)
refreshMsg() refreshMsg()
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth(0.49F) modifier = Modifier.fillMaxWidth(0.49F)
) { ) {
@@ -562,7 +563,7 @@ fun SupportMessages(navCtrl: NavHostController) {
onClick = { onClick = {
dpm.setLongSupportMessage(receiver, null) dpm.setLongSupportMessage(receiver, null)
refreshMsg() refreshMsg()
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth(0.96F) modifier = Modifier.fillMaxWidth(0.96F)
) { ) {
@@ -615,7 +616,7 @@ fun TransferOwnership(navCtrl: NavHostController) {
val receiver = context.getReceiver() val receiver = context.getReceiver()
try { try {
dpm.transferOwnership(receiver, componentName!!, null) dpm.transferOwnership(receiver, componentName!!, null)
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
dialog = false dialog = false
backToHomeStateFlow.value = true backToHomeStateFlow.value = true
} catch(e: Exception) { } catch(e: Exception) {

View File

@@ -39,6 +39,8 @@ import android.net.Uri
import android.os.Build.VERSION import android.os.Build.VERSION
import android.os.UserManager import android.os.UserManager
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@@ -84,7 +86,6 @@ import androidx.compose.material3.rememberDatePickerState
import androidx.compose.material3.rememberTimePickerState import androidx.compose.material3.rememberTimePickerState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableLongStateOf import androidx.compose.runtime.mutableLongStateOf
@@ -109,14 +110,9 @@ import androidx.navigation.NavHostController
import com.bintianqi.owndroid.MyViewModel import com.bintianqi.owndroid.MyViewModel
import com.bintianqi.owndroid.NotificationUtils import com.bintianqi.owndroid.NotificationUtils
import com.bintianqi.owndroid.R 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.formatFileSize
import com.bintianqi.owndroid.getFile
import com.bintianqi.owndroid.humanReadableDate import com.bintianqi.owndroid.humanReadableDate
import com.bintianqi.owndroid.isExportingSecurityOrNetworkLogs import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.toggle
import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.InfoCard
@@ -125,17 +121,13 @@ import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.RadioButtonItem import com.bintianqi.owndroid.ui.RadioButtonItem
import com.bintianqi.owndroid.ui.SwitchItem import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.uriToStream import com.bintianqi.owndroid.uriToStream
import com.bintianqi.owndroid.yesOrNo
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json import java.io.ByteArrayOutputStream
import kotlinx.serialization.json.addJsonObject
import kotlinx.serialization.json.buildJsonArray
import kotlinx.serialization.json.encodeToStream
import kotlinx.serialization.json.put
import java.util.Date import java.util.Date
import java.util.TimeZone import java.util.TimeZone
import java.util.concurrent.Executors import java.util.concurrent.Executors
import kotlin.math.pow
@SuppressLint("NewApi") @SuppressLint("NewApi")
@Composable @Composable
@@ -151,53 +143,52 @@ fun SystemManage(navCtrl: NavHostController) {
var dialog by remember { mutableIntStateOf(0) } var dialog by remember { mutableIntStateOf(0) }
MyScaffold(R.string.system, 0.dp, navCtrl) { MyScaffold(R.string.system, 0.dp, navCtrl) {
if(deviceOwner || profileOwner) { 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) { 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)) { 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))) { 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_time, icon = R.drawable.schedule_fill0) { navCtrl.navigate("ChangeTime") }
FunctionItem(R.string.change_timezone, "", R.drawable.schedule_fill0) { navCtrl.navigate("ChangeTimeZone") } FunctionItem(R.string.change_timezone, icon = R.drawable.schedule_fill0) { navCtrl.navigate("ChangeTimeZone") }
} }
if(VERSION.SDK_INT >= 23 && (deviceOwner || profileOwner)) { 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) { 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)) { 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) { 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) { 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))) { 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) { 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))) { 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))) { 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))) { 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))) { 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( if(dialog != 0) AlertDialog(
onDismissRequest = { dialog = 0 }, onDismissRequest = { dialog = 0 },
@@ -214,8 +205,7 @@ fun SystemManage(navCtrl: NavHostController) {
if(dialog == 1) { if(dialog == 1) {
dpm.reboot(receiver) dpm.reboot(receiver)
} else { } else {
val result = dpm.requestBugreport(receiver) context.showOperationResultToast(dpm.requestBugreport(receiver))
Toast.makeText(context, if(result) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
} }
dialog = 0 dialog = 0
} }
@@ -238,58 +228,60 @@ fun SystemOptions(navCtrl: NavHostController) {
var dialog by remember { mutableIntStateOf(0) } var dialog by remember { mutableIntStateOf(0) }
MyScaffold(R.string.options, 0.dp, navCtrl) { MyScaffold(R.string.options, 0.dp, navCtrl) {
if(deviceOwner || profileOwner) { if(deviceOwner || profileOwner) {
SwitchItem(R.string.disable_cam,"", R.drawable.photo_camera_fill0, SwitchItem(R.string.disable_cam, icon = R.drawable.photo_camera_fill0,
{ dpm.getCameraDisabled(null) }, { dpm.setCameraDisabled(receiver,it) } getState = { dpm.getCameraDisabled(null) }, onCheckedChange = { dpm.setCameraDisabled(receiver,it) }
) )
} }
if(deviceOwner || profileOwner) { if(deviceOwner || profileOwner) {
SwitchItem(R.string.disable_screen_capture, "", R.drawable.screenshot_fill0, SwitchItem(R.string.disable_screen_capture, icon = R.drawable.screenshot_fill0,
{ dpm.getScreenCaptureDisabled(null) }, { dpm.setScreenCaptureDisabled(receiver,it) } getState = { dpm.getScreenCaptureDisabled(null) }, onCheckedChange = { dpm.setScreenCaptureDisabled(receiver,it) }
) )
} }
if(VERSION.SDK_INT >= 34 && (deviceOwner || (profileOwner && dpm.isAffiliatedUser))) { if(VERSION.SDK_INT >= 34 && (deviceOwner || (profileOwner && dpm.isAffiliatedUser))) {
SwitchItem(R.string.disable_status_bar, "", R.drawable.notifications_fill0, SwitchItem(R.string.disable_status_bar, icon = R.drawable.notifications_fill0,
{ dpm.isStatusBarDisabled}, { dpm.setStatusBarDisabled(receiver,it) } getState = { dpm.isStatusBarDisabled}, onCheckedChange = { dpm.setStatusBarDisabled(receiver,it) }
) )
} }
if(deviceOwner || (VERSION.SDK_INT >= 23 && profileOwner && um.isSystemUser) || dpm.isOrgProfile(receiver)) { if(deviceOwner || (VERSION.SDK_INT >= 23 && profileOwner && um.isSystemUser) || dpm.isOrgProfile(receiver)) {
if(VERSION.SDK_INT >= 30) { if(VERSION.SDK_INT >= 30) {
SwitchItem(R.string.auto_time, "", R.drawable.schedule_fill0, SwitchItem(R.string.auto_time, icon = R.drawable.schedule_fill0,
{ dpm.getAutoTimeEnabled(receiver) }, { dpm.setAutoTimeEnabled(receiver,it) } getState = { dpm.getAutoTimeEnabled(receiver) }, onCheckedChange = { dpm.setAutoTimeEnabled(receiver,it) }
) )
SwitchItem(R.string.auto_timezone, "", R.drawable.globe_fill0, SwitchItem(R.string.auto_timezone, icon = R.drawable.globe_fill0,
{ dpm.getAutoTimeZoneEnabled(receiver) }, { dpm.setAutoTimeZoneEnabled(receiver,it) } getState = { dpm.getAutoTimeZoneEnabled(receiver) }, onCheckedChange = { dpm.setAutoTimeZoneEnabled(receiver,it) }
) )
} else { } else {
SwitchItem(R.string.require_auto_time, "", R.drawable.schedule_fill0, { dpm.autoTimeRequired}, { dpm.setAutoTimeRequired(receiver,it) }, padding = false) 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))))) { if(deviceOwner || (profileOwner && (VERSION.SDK_INT < 24 || (VERSION.SDK_INT >= 24 && !dpm.isManagedProfile(receiver))))) {
SwitchItem(R.string.master_mute, "", R.drawable.volume_up_fill0, SwitchItem(R.string.master_mute, icon = R.drawable.volume_up_fill0,
{ dpm.isMasterVolumeMuted(receiver) }, { dpm.setMasterVolumeMuted(receiver,it) } getState = { dpm.isMasterVolumeMuted(receiver) }, onCheckedChange = { dpm.setMasterVolumeMuted(receiver,it) }
) )
} }
if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) { if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) {
SwitchItem(R.string.backup_service, "", R.drawable.backup_fill0, SwitchItem(R.string.backup_service, icon = R.drawable.backup_fill0,
{ dpm.isBackupServiceEnabled(receiver) }, { dpm.setBackupServiceEnabled(receiver,it) }, getState = { dpm.isBackupServiceEnabled(receiver) }, onCheckedChange = { dpm.setBackupServiceEnabled(receiver,it) },
onClickBlank = { dialog = 1 } onClickBlank = { dialog = 1 }
) )
} }
if(VERSION.SDK_INT >= 24 && profileOwner && dpm.isManagedProfile(receiver)) { if(VERSION.SDK_INT >= 24 && profileOwner && dpm.isManagedProfile(receiver)) {
SwitchItem(R.string.disable_bt_contact_share, "", R.drawable.account_circle_fill0, SwitchItem(R.string.disable_bt_contact_share, icon = R.drawable.account_circle_fill0,
{ dpm.getBluetoothContactSharingDisabled(receiver) }, { dpm.setBluetoothContactSharingDisabled(receiver,it) }, getState = { dpm.getBluetoothContactSharingDisabled(receiver) },
onCheckedChange = { dpm.setBluetoothContactSharingDisabled(receiver,it) }
) )
} }
if(VERSION.SDK_INT >= 30 && deviceOwner) { if(VERSION.SDK_INT >= 30 && deviceOwner) {
SwitchItem(R.string.common_criteria_mode , "",R.drawable.security_fill0, SwitchItem(R.string.common_criteria_mode , icon =R.drawable.security_fill0,
{ dpm.isCommonCriteriaModeEnabled(receiver) }, { dpm.setCommonCriteriaModeEnabled(receiver,it) }, getState = { dpm.isCommonCriteriaModeEnabled(receiver) }, onCheckedChange = { dpm.setCommonCriteriaModeEnabled(receiver,it) },
onClickBlank = { dialog = 2 } onClickBlank = { dialog = 2 }
) )
} }
if(VERSION.SDK_INT >= 31 && (deviceOwner || dpm.isOrgProfile(receiver)) && dpm.canUsbDataSignalingBeDisabled()) { if(VERSION.SDK_INT >= 31 && (deviceOwner || dpm.isOrgProfile(receiver)) && dpm.canUsbDataSignalingBeDisabled()) {
SwitchItem( SwitchItem(
R.string.disable_usb_signal, "", R.drawable.usb_fill0, { !dpm.isUsbDataSignalingEnabled }, R.string.disable_usb_signal, icon = R.drawable.usb_fill0, getState = { !dpm.isUsbDataSignalingEnabled },
{ dpm.isUsbDataSignalingEnabled = !it }, onCheckedChange = { dpm.isUsbDataSignalingEnabled = !it },
) )
} }
} }
@@ -324,18 +316,14 @@ fun Keyguard(navCtrl: NavHostController) {
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Button( Button(
onClick = { onClick = { context.showOperationResultToast(dpm.setKeyguardDisabled(receiver, true)) },
Toast.makeText(context, if(dpm.setKeyguardDisabled(receiver,true)) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
},
enabled = deviceOwner || (VERSION.SDK_INT >= 28 && profileOwner && dpm.isAffiliatedUser), enabled = deviceOwner || (VERSION.SDK_INT >= 28 && profileOwner && dpm.isAffiliatedUser),
modifier = Modifier.fillMaxWidth(0.49F) modifier = Modifier.fillMaxWidth(0.49F)
) { ) {
Text(stringResource(R.string.disable)) Text(stringResource(R.string.disable))
} }
Button( Button(
onClick = { onClick = { context.showOperationResultToast(dpm.setKeyguardDisabled(receiver, false)) },
Toast.makeText(context, if(dpm.setKeyguardDisabled(receiver,false)) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
},
enabled = deviceOwner || (VERSION.SDK_INT >= 28 && profileOwner && dpm.isAffiliatedUser), enabled = deviceOwner || (VERSION.SDK_INT >= 28 && profileOwner && dpm.isAffiliatedUser),
modifier = Modifier.fillMaxWidth(0.96F) modifier = Modifier.fillMaxWidth(0.96F)
) { ) {
@@ -351,9 +339,8 @@ fun Keyguard(navCtrl: NavHostController) {
if(VERSION.SDK_INT >= 26 && profileOwner && dpm.isManagedProfile(receiver)) { if(VERSION.SDK_INT >= 26 && profileOwner && dpm.isManagedProfile(receiver)) {
CheckBoxItem( CheckBoxItem(
R.string.evict_credential_encryptoon_key, R.string.evict_credential_encryptoon_key,
flag == FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY, flag and FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY != 0
{ flag = if(flag==0) {1}else{0} } ) { flag = flag xor FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY }
)
Spacer(Modifier.padding(vertical = 2.dp)) Spacer(Modifier.padding(vertical = 2.dp))
} }
Button( Button(
@@ -453,8 +440,7 @@ fun ChangeTime(navCtrl: NavHostController) {
onClick = { onClick = {
val timeMillis = if(manualInput) inputTime.toLong() val timeMillis = if(manualInput) inputTime.toLong()
else datePickerState.selectedDateMillis!! + timePickerState.hour * 3600000 + timePickerState.minute * 60000 else datePickerState.selectedDateMillis!! + timePickerState.hour * 3600000 + timePickerState.minute * 60000
val result = dpm.setTime(receiver, timeMillis) context.showOperationResultToast(dpm.setTime(receiver, timeMillis))
Toast.makeText(context, if(result) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
}, },
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
enabled = isInputLegal enabled = isInputLegal
@@ -509,8 +495,7 @@ fun ChangeTimeZone(navCtrl: NavHostController) {
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
Button( Button(
onClick = { onClick = {
val result = dpm.setTimeZone(receiver, inputTimezone) context.showOperationResultToast(dpm.setTimeZone(receiver, inputTimezone))
Toast.makeText(context, if(result) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
@@ -555,14 +540,14 @@ fun PermissionPolicy(navCtrl: NavHostController) {
val receiver = context.getReceiver() val receiver = context.getReceiver()
var selectedPolicy by remember { mutableIntStateOf(dpm.getPermissionPolicy(receiver)) } var selectedPolicy by remember { mutableIntStateOf(dpm.getPermissionPolicy(receiver)) }
MyScaffold(R.string.permission_policy, 8.dp, navCtrl) { MyScaffold(R.string.permission_policy, 8.dp, navCtrl) {
RadioButtonItem(R.string.default_stringres, selectedPolicy == PERMISSION_POLICY_PROMPT, { selectedPolicy = PERMISSION_POLICY_PROMPT }) 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_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.auto_deny, selectedPolicy == PERMISSION_POLICY_AUTO_DENY) { selectedPolicy = PERMISSION_POLICY_AUTO_DENY }
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
Button( Button(
onClick = { onClick = {
dpm.setPermissionPolicy(receiver,selectedPolicy) dpm.setPermissionPolicy(receiver,selectedPolicy)
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
@@ -579,26 +564,14 @@ fun MTEPolicy(navCtrl: NavHostController) {
val dpm = context.getDPM() val dpm = context.getDPM()
var selectedMtePolicy by remember { mutableIntStateOf(dpm.mtePolicy) } var selectedMtePolicy by remember { mutableIntStateOf(dpm.mtePolicy) }
MyScaffold(R.string.mte_policy, 8.dp, navCtrl) { MyScaffold(R.string.mte_policy, 8.dp, navCtrl) {
RadioButtonItem( RadioButtonItem(R.string.decide_by_user, selectedMtePolicy == MTE_NOT_CONTROLLED_BY_POLICY) { selectedMtePolicy = MTE_NOT_CONTROLLED_BY_POLICY }
R.string.decide_by_user, RadioButtonItem(R.string.enabled, selectedMtePolicy == MTE_ENABLED) { selectedMtePolicy = MTE_ENABLED }
selectedMtePolicy == MTE_NOT_CONTROLLED_BY_POLICY, RadioButtonItem(R.string.disabled, selectedMtePolicy == MTE_DISABLED) { selectedMtePolicy = MTE_DISABLED }
{ 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( Button(
onClick = { onClick = {
try { try {
dpm.mtePolicy = selectedMtePolicy dpm.mtePolicy = selectedMtePolicy
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
} catch(_: java.lang.UnsupportedOperationException) { } catch(_: java.lang.UnsupportedOperationException) {
Toast.makeText(context, R.string.unsupported, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.unsupported, Toast.LENGTH_SHORT).show()
} }
@@ -624,29 +597,19 @@ fun NearbyStreamingPolicy(navCtrl: NavHostController) {
Spacer(Modifier.padding(vertical = 3.dp)) Spacer(Modifier.padding(vertical = 3.dp))
RadioButtonItem( RadioButtonItem(
R.string.decide_by_user, R.string.decide_by_user,
appPolicy == NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY, appPolicy == NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY
{ 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( RadioButtonItem(R.string.disabled, appPolicy == NEARBY_STREAMING_DISABLED) { appPolicy = NEARBY_STREAMING_DISABLED }
R.string.enabled,
appPolicy == NEARBY_STREAMING_ENABLED,
{ appPolicy = NEARBY_STREAMING_ENABLED }
)
RadioButtonItem(
R.string.disabled,
appPolicy == NEARBY_STREAMING_DISABLED,
{ appPolicy = NEARBY_STREAMING_DISABLED }
)
RadioButtonItem( RadioButtonItem(
R.string.enable_if_secure_enough, 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)) Spacer(Modifier.padding(vertical = 3.dp))
Button( Button(
onClick = { onClick = {
dpm.nearbyAppStreamingPolicy = appPolicy dpm.nearbyAppStreamingPolicy = appPolicy
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
@@ -659,29 +622,25 @@ fun NearbyStreamingPolicy(navCtrl: NavHostController) {
Spacer(Modifier.padding(vertical = 3.dp)) Spacer(Modifier.padding(vertical = 3.dp))
RadioButtonItem( RadioButtonItem(
R.string.decide_by_user, 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( RadioButtonItem(
R.string.enabled, R.string.enabled,
notificationPolicy == NEARBY_STREAMING_ENABLED, notificationPolicy == NEARBY_STREAMING_ENABLED
{ notificationPolicy = NEARBY_STREAMING_ENABLED } ) { notificationPolicy = NEARBY_STREAMING_ENABLED }
)
RadioButtonItem( RadioButtonItem(
R.string.disabled, R.string.disabled,
notificationPolicy == NEARBY_STREAMING_DISABLED, notificationPolicy == NEARBY_STREAMING_DISABLED
{ notificationPolicy = NEARBY_STREAMING_DISABLED } ) { notificationPolicy = NEARBY_STREAMING_DISABLED }
)
RadioButtonItem( RadioButtonItem(
R.string.enable_if_secure_enough, 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)) Spacer(Modifier.padding(vertical = 3.dp))
Button( Button(
onClick = { onClick = {
dpm.nearbyNotificationStreamingPolicy = notificationPolicy dpm.nearbyNotificationStreamingPolicy = notificationPolicy
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
@@ -700,84 +659,58 @@ fun LockTaskMode(navCtrl: NavHostController, vm: MyViewModel) {
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
var appSelectorRequest by rememberSaveable { mutableIntStateOf(0) } var appSelectorRequest by rememberSaveable { mutableIntStateOf(0) }
MyScaffold(R.string.lock_task_mode, 8.dp, navCtrl, false) { MyScaffold(R.string.lock_task_mode, 8.dp, navCtrl, false) {
val lockTaskFeatures = remember { mutableStateListOf<Int>() } var lockTaskFeatures by remember { mutableIntStateOf(0) }
var custom by rememberSaveable { mutableStateOf(false) } var custom by rememberSaveable { mutableStateOf(false) }
val refreshFeature = { fun refreshFeature() {
var calculate = dpm.getLockTaskFeatures(receiver) lockTaskFeatures = dpm.getLockTaskFeatures(receiver)
lockTaskFeatures.clear() custom = lockTaskFeatures != 0
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
}
} }
Spacer(Modifier.padding(vertical = 10.dp)) Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.lock_task_feature), style = typography.headlineLarge) Text(text = stringResource(R.string.lock_task_feature), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
LaunchedEffect(Unit) { refreshFeature() } LaunchedEffect(Unit) { refreshFeature() }
RadioButtonItem(R.string.disable_all, !custom, { custom = false }) RadioButtonItem(R.string.disable_all, !custom) { custom = false }
RadioButtonItem(R.string.custom, custom, { custom = true }) RadioButtonItem(R.string.custom, custom) { custom = true }
AnimatedVisibility(custom) { AnimatedVisibility(custom) {
Column { Column {
CheckBoxItem( CheckBoxItem(
R.string.ltf_sys_info, R.string.ltf_sys_info,
LOCK_TASK_FEATURE_SYSTEM_INFO in lockTaskFeatures, lockTaskFeatures and LOCK_TASK_FEATURE_SYSTEM_INFO != 0
{ lockTaskFeatures.toggle(it, LOCK_TASK_FEATURE_SYSTEM_INFO) } ) { lockTaskFeatures = lockTaskFeatures xor LOCK_TASK_FEATURE_SYSTEM_INFO }
)
CheckBoxItem( CheckBoxItem(
R.string.ltf_notifications, R.string.ltf_notifications,
LOCK_TASK_FEATURE_NOTIFICATIONS in lockTaskFeatures, lockTaskFeatures and LOCK_TASK_FEATURE_NOTIFICATIONS != 0
{ lockTaskFeatures.toggle(it, LOCK_TASK_FEATURE_NOTIFICATIONS) } ) { lockTaskFeatures = lockTaskFeatures xor LOCK_TASK_FEATURE_NOTIFICATIONS }
)
CheckBoxItem( CheckBoxItem(
R.string.ltf_home, R.string.ltf_home,
LOCK_TASK_FEATURE_HOME in lockTaskFeatures, lockTaskFeatures and LOCK_TASK_FEATURE_HOME != 0
{ lockTaskFeatures.toggle(it, LOCK_TASK_FEATURE_HOME) } ) { lockTaskFeatures = lockTaskFeatures xor LOCK_TASK_FEATURE_HOME }
)
CheckBoxItem( CheckBoxItem(
R.string.ltf_overview, R.string.ltf_overview,
LOCK_TASK_FEATURE_OVERVIEW in lockTaskFeatures, lockTaskFeatures and LOCK_TASK_FEATURE_OVERVIEW != 0
{ lockTaskFeatures.toggle(it, LOCK_TASK_FEATURE_OVERVIEW) } ) { lockTaskFeatures = lockTaskFeatures xor LOCK_TASK_FEATURE_OVERVIEW }
)
CheckBoxItem( CheckBoxItem(
R.string.ltf_global_actions, R.string.ltf_global_actions,
LOCK_TASK_FEATURE_GLOBAL_ACTIONS in lockTaskFeatures, lockTaskFeatures and LOCK_TASK_FEATURE_GLOBAL_ACTIONS != 0
{ lockTaskFeatures.toggle(it, LOCK_TASK_FEATURE_GLOBAL_ACTIONS) } ) { lockTaskFeatures = lockTaskFeatures xor LOCK_TASK_FEATURE_GLOBAL_ACTIONS }
)
CheckBoxItem( CheckBoxItem(
R.string.ltf_keyguard, R.string.ltf_keyguard,
LOCK_TASK_FEATURE_KEYGUARD in lockTaskFeatures, lockTaskFeatures and LOCK_TASK_FEATURE_KEYGUARD != 0
{ lockTaskFeatures.toggle(it, LOCK_TASK_FEATURE_KEYGUARD) } ) { lockTaskFeatures = lockTaskFeatures xor LOCK_TASK_FEATURE_KEYGUARD }
)
if(VERSION.SDK_INT >= 30) { if(VERSION.SDK_INT >= 30) {
CheckBoxItem( CheckBoxItem(
R.string.ltf_block_activity_start_in_task, R.string.ltf_block_activity_start_in_task,
LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK in lockTaskFeatures, lockTaskFeatures and LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK != 0
{ lockTaskFeatures.toggle(it, LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK) } ) { lockTaskFeatures = lockTaskFeatures xor LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK }
)
} }
} }
} }
Button( Button(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
onClick = { onClick = {
var result = 0
if(custom) {
lockTaskFeatures.forEach { result += it }
}
try { try {
dpm.setLockTaskFeatures(receiver, result) dpm.setLockTaskFeatures(receiver, lockTaskFeatures)
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
AlertDialog.Builder(context) AlertDialog.Builder(context)
.setTitle(R.string.error) .setTitle(R.string.error)
@@ -846,7 +779,7 @@ fun LockTaskMode(navCtrl: NavHostController, vm: MyViewModel) {
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
onClick = { onClick = {
dpm.setLockTaskPackages(receiver, lockTaskPackages.toTypedArray()) dpm.setLockTaskPackages(receiver, lockTaskPackages.toTypedArray())
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
} }
) { ) {
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))
@@ -884,7 +817,7 @@ fun LockTaskMode(navCtrl: NavHostController, vm: MyViewModel) {
}, },
modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp) 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) { AnimatedVisibility(specifyActivity) {
OutlinedTextField( OutlinedTextField(
value = startLockTaskActivity, value = startLockTaskActivity,
@@ -925,12 +858,11 @@ fun CACert(navCtrl: NavHostController) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
val uri by fileUriFlow.collectAsState()
var exist by remember { mutableStateOf(false) } var exist by remember { mutableStateOf(false) }
val uriPath = uri.path ?: "" var fileUri by remember { mutableStateOf<Uri?>(null) }
var caCertByteArray by remember { mutableStateOf(byteArrayOf()) } var caCertByteArray by remember { mutableStateOf(ByteArray(100000)) }
LaunchedEffect(uri) { val getFileLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if(uri != Uri.parse("")) { result.data?.data?.let { uri ->
uriToStream(context, uri) { uriToStream(context, uri) {
val array = it.readBytes() val array = it.readBytes()
caCertByteArray = if(array.size < 10000) { caCertByteArray = if(array.size < 10000) {
@@ -939,15 +871,12 @@ fun CACert(navCtrl: NavHostController) {
byteArrayOf() byteArrayOf()
} }
} }
exist = dpm.hasCaCertInstalled(receiver, caCertByteArray)
} }
} }
MyScaffold(R.string.ca_cert, 8.dp, navCtrl) { MyScaffold(R.string.ca_cert, 8.dp, navCtrl) {
AnimatedVisibility(uriPath != "") {
Text(text = uriPath)
}
Text( 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() modifier = Modifier.animateContentSize()
) )
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
@@ -956,18 +885,17 @@ fun CACert(navCtrl: NavHostController) {
val caCertIntent = Intent(Intent.ACTION_GET_CONTENT) val caCertIntent = Intent(Intent.ACTION_GET_CONTENT)
caCertIntent.setType("*/*") caCertIntent.setType("*/*")
caCertIntent.addCategory(Intent.CATEGORY_OPENABLE) caCertIntent.addCategory(Intent.CATEGORY_OPENABLE)
getFile.launch(caCertIntent) getFileLauncher.launch(caCertIntent)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Text(stringResource(R.string.select_ca_cert)) Text(stringResource(R.string.select_ca_cert))
} }
AnimatedVisibility(uriPath != "") { AnimatedVisibility(fileUri != null) {
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Button( Button(
onClick = { onClick = {
val result = dpm.installCaCert(receiver, caCertByteArray) context.showOperationResultToast(dpm.installCaCert(receiver, caCertByteArray))
Toast.makeText(context, if(result) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
exist = dpm.hasCaCertInstalled(receiver, caCertByteArray) exist = dpm.hasCaCertInstalled(receiver, caCertByteArray)
}, },
modifier = Modifier.fillMaxWidth(0.49F) modifier = Modifier.fillMaxWidth(0.49F)
@@ -976,12 +904,11 @@ fun CACert(navCtrl: NavHostController) {
} }
Button( Button(
onClick = { onClick = {
if(exist) {
dpm.uninstallCaCert(receiver, caCertByteArray) dpm.uninstallCaCert(receiver, caCertByteArray)
exist = dpm.hasCaCertInstalled(receiver, caCertByteArray) exist = dpm.hasCaCertInstalled(receiver, caCertByteArray)
Toast.makeText(context, if(exist) R.string.failed else R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
} else { Toast.makeText(context, R.string.not_exist, Toast.LENGTH_SHORT).show() }
}, },
enabled = exist,
modifier = Modifier.fillMaxWidth(0.96F) modifier = Modifier.fillMaxWidth(0.96F)
) { ) {
Text(stringResource(R.string.uninstall)) Text(stringResource(R.string.uninstall))
@@ -991,7 +918,7 @@ fun CACert(navCtrl: NavHostController) {
Button( Button(
onClick = { onClick = {
dpm.uninstallAllUserCaCerts(receiver) dpm.uninstallAllUserCaCerts(receiver)
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
@@ -1008,11 +935,31 @@ fun SecurityLogging(navCtrl: NavHostController) {
val receiver = context.getReceiver() val receiver = context.getReceiver()
val logFile = context.filesDir.resolve("SecurityLogs.json") val logFile = context.filesDir.resolve("SecurityLogs.json")
var fileSize by remember { mutableLongStateOf(0) } var fileSize by remember { mutableLongStateOf(0) }
LaunchedEffect(Unit) { LaunchedEffect(Unit) { fileSize = logFile.length() }
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) { 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))) Text(stringResource(R.string.log_file_size_is, formatFileSize(fileSize)))
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
Button( Button(
@@ -1021,9 +968,7 @@ fun SecurityLogging(navCtrl: NavHostController) {
intent.addCategory(Intent.CATEGORY_OPENABLE) intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.setType("application/json") intent.setType("application/json")
intent.putExtra(Intent.EXTRA_TITLE, "SecurityLogs.json") intent.putExtra(Intent.EXTRA_TITLE, "SecurityLogs.json")
exportFilePath = logFile.path exportSecurityLogs.launch(intent)
isExportingSecurityOrNetworkLogs = true
exportFile.launch(intent)
}, },
enabled = fileSize > 0, enabled = fileSize > 0,
modifier = Modifier.fillMaxWidth(0.49F) modifier = Modifier.fillMaxWidth(0.49F)
@@ -1050,29 +995,16 @@ fun SecurityLogging(navCtrl: NavHostController) {
Toast.makeText(context, R.string.no_logs, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.no_logs, Toast.LENGTH_SHORT).show()
return@Button return@Button
} else { } else {
val securityEvents = buildJsonArray { val outputStream = ByteArrayOutputStream()
logs.forEach { event -> outputStream.write("[".encodeToByteArray())
addJsonObject { processSecurityLogs(logs, outputStream)
put("time_nanos", event.timeNanos) outputStream.write("]".encodeToByteArray())
put("tag", event.tag) preRebootSecurityLogs = outputStream.toByteArray()
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 intent = Intent(Intent.ACTION_CREATE_DOCUMENT) val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE) intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.setType("application/json") intent.setType("application/json")
intent.putExtra(Intent.EXTRA_TITLE, "PreRebootSecurityLogs.json") intent.putExtra(Intent.EXTRA_TITLE, "PreRebootSecurityLogs.json")
exportFilePath = preRebootSecurityLogs.path exportPreRebootSecurityLogs.launch(intent)
isExportingSecurityOrNetworkLogs = true
exportFile.launch(intent)
} }
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
@@ -1169,7 +1101,7 @@ fun FRPPolicy(navCtrl: NavHostController) {
} }
AnimatedVisibility(usePolicy) { AnimatedVisibility(usePolicy) {
Column { Column {
CheckBoxItem(R.string.enable_frp, enabled, { enabled = it }) CheckBoxItem(R.string.enable_frp, enabled) { enabled = it }
Text(stringResource(R.string.account_list_is)) Text(stringResource(R.string.account_list_is))
Column(modifier = Modifier.animateContentSize()) { Column(modifier = Modifier.animateContentSize()) {
if(accountList.isEmpty()) Text(stringResource(R.string.none)) 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 userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
val dpm = context.getDPM() val dpm = context.getDPM()
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
var flag by remember { mutableIntStateOf(0) }
var warning by remember { mutableStateOf(false) } var warning by remember { mutableStateOf(false) }
var wipeDevice 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 silent by remember { mutableStateOf(false) }
var reason by remember { mutableStateOf("") } var reason by remember { mutableStateOf("") }
MyScaffold(R.string.wipe_data, 8.dp, navCtrl) { MyScaffold(R.string.wipe_data, 8.dp, navCtrl) {
CheckBoxItem( CheckBoxItem(R.string.wipe_external_storage, flag and WIPE_EXTERNAL_STORAGE != 0) { flag = flag xor WIPE_EXTERNAL_STORAGE }
R.string.wipe_external_storage, if(VERSION.SDK_INT >= 22 && context.isDeviceOwner) CheckBoxItem(
externalStorage, { externalStorage = it } 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 >= 22 && context.isDeviceOwner) { if(VERSION.SDK_INT >= 29) CheckBoxItem(R.string.wipe_silently, silent) { silent = it }
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 }) }
AnimatedVisibility(!silent && VERSION.SDK_INT >= 28) { AnimatedVisibility(!silent && VERSION.SDK_INT >= 28) {
OutlinedTextField( OutlinedTextField(
value = reason, onValueChange = { reason = it }, value = reason, onValueChange = { reason = it },
@@ -1308,11 +1232,7 @@ fun WipeData(navCtrl: NavHostController) {
val timerText = if(timer > 0) "(${timer}s)" else "" val timerText = if(timer > 0) "(${timer}s)" else ""
TextButton( TextButton(
onClick = { onClick = {
var flag = 0 if(silent && VERSION.SDK_INT >= 29) { flag = flag or WIPE_SILENTLY }
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(wipeDevice) { if(wipeDevice) {
dpm.wipeDevice(flag) dpm.wipeDevice(flag)
} else { } else {
@@ -1351,17 +1271,17 @@ fun SystemUpdatePolicy(navCtrl: NavHostController) {
var selectedPolicy by remember { mutableStateOf(dpm.systemUpdatePolicy?.policyType) } var selectedPolicy by remember { mutableStateOf(dpm.systemUpdatePolicy?.policyType) }
RadioButtonItem( RadioButtonItem(
R.string.system_update_policy_automatic, R.string.system_update_policy_automatic,
selectedPolicy == TYPE_INSTALL_AUTOMATIC, { selectedPolicy = TYPE_INSTALL_AUTOMATIC } selectedPolicy == TYPE_INSTALL_AUTOMATIC
) ) { selectedPolicy = TYPE_INSTALL_AUTOMATIC }
RadioButtonItem( RadioButtonItem(
R.string.system_update_policy_install_windowed, R.string.system_update_policy_install_windowed,
selectedPolicy == TYPE_INSTALL_WINDOWED, { selectedPolicy = TYPE_INSTALL_WINDOWED } selectedPolicy == TYPE_INSTALL_WINDOWED
) ) { selectedPolicy = TYPE_INSTALL_WINDOWED }
RadioButtonItem( RadioButtonItem(
R.string.system_update_policy_postpone, R.string.system_update_policy_postpone,
selectedPolicy == TYPE_POSTPONE, { selectedPolicy = TYPE_POSTPONE } selectedPolicy == TYPE_POSTPONE
) ) { selectedPolicy = TYPE_POSTPONE }
RadioButtonItem(R.string.none, selectedPolicy == null, { selectedPolicy = null }) RadioButtonItem(R.string.none, selectedPolicy == null) { selectedPolicy = null }
var windowedPolicyStart by remember { mutableStateOf("") } var windowedPolicyStart by remember { mutableStateOf("") }
var windowedPolicyEnd by remember { mutableStateOf("") } var windowedPolicyEnd by remember { mutableStateOf("") }
AnimatedVisibility(selectedPolicy == 2) { AnimatedVisibility(selectedPolicy == 2) {
@@ -1399,7 +1319,7 @@ fun SystemUpdatePolicy(navCtrl: NavHostController) {
else -> null else -> null
} }
dpm.setSystemUpdatePolicy(receiver,policy) dpm.setSystemUpdatePolicy(receiver,policy)
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth().padding(top = 8.dp) modifier = Modifier.fillMaxWidth().padding(top = 8.dp)
) { ) {
@@ -1447,25 +1367,28 @@ fun InstallSystemUpdate(navCtrl: NavHostController) {
Toast.makeText(context, errMsg, Toast.LENGTH_SHORT).show() 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) { MyScaffold(R.string.install_system_update, 8.dp, navCtrl) {
Button( Button(
onClick = { onClick = {
val intent = Intent(Intent.ACTION_GET_CONTENT) val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.setType("application/zip") intent.setType("application/zip")
intent.addCategory(Intent.CATEGORY_OPENABLE) intent.addCategory(Intent.CATEGORY_OPENABLE)
getFile.launch(intent) getFileLauncher.launch(intent)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Text(stringResource(R.string.select_ota_package)) Text(stringResource(R.string.select_ota_package))
} }
AnimatedVisibility(uri != Uri.parse("")) { AnimatedVisibility(uri != null) {
Button( Button(
onClick = { onClick = {
val executor = Executors.newCachedThreadPool() val executor = Executors.newCachedThreadPool()
try { 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() Toast.makeText(context, R.string.start_install_system_update, Toast.LENGTH_SHORT).show()
} catch(e: Exception) { } catch(e: Exception) {
Toast.makeText( Toast.makeText(

View File

@@ -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)) Text(text = stringResource(R.string.some_features_invalid_in_work_profile), modifier = Modifier.padding(start = 16.dp))
} }
Spacer(Modifier.padding(vertical = 2.dp)) Spacer(Modifier.padding(vertical = 2.dp))
FunctionItem(R.string.network_and_internet, "", R.drawable.wifi_fill0) { navCtrl.navigate("UR-Internet") } FunctionItem(R.string.network_and_internet, icon = R.drawable.wifi_fill0) { navCtrl.navigate("UR-Internet") }
FunctionItem(R.string.connectivity, "", R.drawable.devices_other_fill0) { navCtrl.navigate("UR-Connectivity") } FunctionItem(R.string.connectivity, icon = R.drawable.devices_other_fill0) { navCtrl.navigate("UR-Connectivity") }
FunctionItem(R.string.applications, "", R.drawable.apps_fill0) { navCtrl.navigate("UR-Applications") } FunctionItem(R.string.applications, icon = R.drawable.apps_fill0) { navCtrl.navigate("UR-Applications") }
FunctionItem(R.string.users, "", R.drawable.account_circle_fill0) { navCtrl.navigate("UR-Users") } FunctionItem(R.string.users, icon = 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.media, icon = 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.other, icon = R.drawable.more_horiz_fill0) { navCtrl.navigate("UR-Other") }
} }
} }

View File

@@ -6,7 +6,6 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Binder import android.os.Binder
import android.os.Build.VERSION import android.os.Build.VERSION
import android.os.Process import android.os.Process
@@ -14,6 +13,8 @@ import android.os.UserHandle
import android.os.UserManager import android.os.UserManager
import android.provider.MediaStore import android.provider.MediaStore
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize import androidx.compose.animation.animateContentSize
@@ -40,7 +41,6 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateListOf
@@ -59,10 +59,8 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.fileUriFlow
import com.bintianqi.owndroid.getFile
import com.bintianqi.owndroid.parseTimestamp 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.CardItem
import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.FunctionItem
@@ -82,33 +80,32 @@ fun Users(navCtrl: NavHostController) {
val profileOwner = context.isProfileOwner val profileOwner = context.isProfileOwner
var dialog by remember { mutableIntStateOf(0) } var dialog by remember { mutableIntStateOf(0) }
MyScaffold(R.string.users, 0.dp, navCtrl) { 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) { if(deviceOwner && VERSION.SDK_INT >= 28) {
FunctionItem(R.string.secondary_users, "", R.drawable.list_fill0) { dialog = 1 } FunctionItem(R.string.secondary_users, icon = R.drawable.list_fill0) { dialog = 1 }
FunctionItem(R.string.options, "", R.drawable.tune_fill0) { navCtrl.navigate("UserOptions") } FunctionItem(R.string.options, icon = R.drawable.tune_fill0) { navCtrl.navigate("UserOptions") }
} }
if(deviceOwner) { 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) { 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) { 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) { 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)) { 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) { 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)) { 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( if(dialog != 0 && VERSION.SDK_INT >= 28) AlertDialog(
title = { Text(stringResource(if(dialog == 1) R.string.secondary_users else R.string.logout_current_user)) }, 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() val receiver = context.getReceiver()
MyScaffold(R.string.options, 0.dp, navCtrl) { MyScaffold(R.string.options, 0.dp, navCtrl) {
if(VERSION.SDK_INT >= 28) { 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( Button(
onClick = { onClick = {
focusMgr.clearFocus() focusMgr.clearFocus()
withUserHandle { withUserHandle { context.showOperationResultToast(dpm.switchUser(receiver, it)) }
Toast.makeText(context, if(dpm.switchUser(receiver, it)) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
}
}, },
enabled = legalInput, enabled = legalInput,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
@@ -288,7 +283,7 @@ fun UserOperation(navCtrl: NavHostController) {
focusMgr.clearFocus() focusMgr.clearFocus()
withUserHandle { withUserHandle {
if(dpm.removeUser(receiver, it)) { if(dpm.removeUser(receiver, it)) {
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
idInput = "" idInput = ""
} else { } else {
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
@@ -313,7 +308,7 @@ fun CreateUser(navCtrl: NavHostController) {
val receiver = context.getReceiver() val receiver = context.getReceiver()
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
var userName by remember { mutableStateOf("") } var userName by remember { mutableStateOf("") }
val flags = remember { mutableStateListOf<Int>() } var flag by remember { mutableIntStateOf(0) }
MyScaffold(R.string.create_user, 8.dp, navCtrl) { MyScaffold(R.string.create_user, 8.dp, navCtrl) {
OutlinedTextField( OutlinedTextField(
value = userName, value = userName,
@@ -326,28 +321,25 @@ fun CreateUser(navCtrl: NavHostController) {
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
CheckBoxItem( CheckBoxItem(
R.string.create_user_skip_wizard, R.string.create_user_skip_wizard,
DevicePolicyManager.SKIP_SETUP_WIZARD in flags, flag and DevicePolicyManager.SKIP_SETUP_WIZARD != 0
{ flags.toggle(it, DevicePolicyManager.SKIP_SETUP_WIZARD) } ) { flag = flag xor DevicePolicyManager.SKIP_SETUP_WIZARD }
)
if(VERSION.SDK_INT >= 28) { if(VERSION.SDK_INT >= 28) {
CheckBoxItem( CheckBoxItem(
R.string.create_user_ephemeral_user, R.string.create_user_ephemeral_user,
DevicePolicyManager.MAKE_USER_EPHEMERAL in flags, flag and DevicePolicyManager.MAKE_USER_EPHEMERAL != 0
{ flags.toggle(it, DevicePolicyManager.MAKE_USER_EPHEMERAL) } ) { flag = flag xor DevicePolicyManager.MAKE_USER_EPHEMERAL }
)
CheckBoxItem( CheckBoxItem(
R.string.create_user_enable_all_system_app, R.string.create_user_enable_all_system_app,
DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED in flags, flag and DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED != 0
{ flags.toggle(it, DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED) } ) { flag = flag xor DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED }
)
} }
var newUserHandle: UserHandle? by remember { mutableStateOf(null) } var newUserHandle: UserHandle? by remember { mutableStateOf(null) }
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
Button( Button(
onClick = { onClick = {
focusMgr.clearFocus() focusMgr.clearFocus()
newUserHandle = dpm.createAndManageUser(receiver, userName, receiver, null, flags.sum()) newUserHandle = dpm.createAndManageUser(receiver, userName, receiver, null, flag)
Toast.makeText(context, if(newUserHandle!=null) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show() context.showOperationResultToast(newUserHandle != null)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
@@ -403,7 +395,7 @@ fun AffiliationID(navCtrl: NavHostController) {
onClick = { onClick = {
list.removeAll(listOf("")) list.removeAll(listOf(""))
dpm.setAffiliationIds(receiver, list.toSet()) dpm.setAffiliationIds(receiver, list.toSet())
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
refreshIds() refreshIds()
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
@@ -434,7 +426,7 @@ fun ChangeUsername(navCtrl: NavHostController) {
Button( Button(
onClick = { onClick = {
dpm.setProfileName(receiver, inputUsername) dpm.setProfileName(receiver, inputUsername)
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
@@ -477,7 +469,6 @@ fun UserSessionMessage(navCtrl: NavHostController) {
onClick = { onClick = {
dpm.setStartUserSessionMessage(receiver,start) dpm.setStartUserSessionMessage(receiver,start)
refreshMsg() refreshMsg()
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
}, },
modifier = Modifier.fillMaxWidth(0.49F) modifier = Modifier.fillMaxWidth(0.49F)
) { ) {
@@ -487,7 +478,7 @@ fun UserSessionMessage(navCtrl: NavHostController) {
onClick = { onClick = {
dpm.setStartUserSessionMessage(receiver,null) dpm.setStartUserSessionMessage(receiver,null)
refreshMsg() refreshMsg()
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth(0.96F) modifier = Modifier.fillMaxWidth(0.96F)
) { ) {
@@ -508,7 +499,7 @@ fun UserSessionMessage(navCtrl: NavHostController) {
onClick = { onClick = {
dpm.setEndUserSessionMessage(receiver,end) dpm.setEndUserSessionMessage(receiver,end)
refreshMsg() refreshMsg()
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth(0.49F) modifier = Modifier.fillMaxWidth(0.49F)
) { ) {
@@ -518,7 +509,7 @@ fun UserSessionMessage(navCtrl: NavHostController) {
onClick = { onClick = {
dpm.setEndUserSessionMessage(receiver,null) dpm.setEndUserSessionMessage(receiver,null)
refreshMsg() refreshMsg()
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth(0.96F) modifier = Modifier.fillMaxWidth(0.96F)
) { ) {
@@ -536,22 +527,22 @@ fun ChangeUserIcon(navCtrl: NavHostController) {
val receiver = context.getReceiver() val receiver = context.getReceiver()
var getContent by remember { mutableStateOf(false) } var getContent by remember { mutableStateOf(false) }
var bitmap by remember { mutableStateOf<Bitmap?>(null) } var bitmap by remember { mutableStateOf<Bitmap?>(null) }
val uriState by fileUriFlow.collectAsState() val getFileLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
LaunchedEffect(uriState) { it.data?.data?.let {
if(uriState == Uri.parse("")) return@LaunchedEffect uriToStream(context, it) { stream ->
uriToStream(context, fileUriFlow.value) { stream ->
bitmap = BitmapFactory.decodeStream(stream) bitmap = BitmapFactory.decodeStream(stream)
} }
} }
}
MyScaffold(R.string.change_user_icon, 8.dp, navCtrl) { 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)) Spacer(Modifier.padding(vertical = 5.dp))
Button( Button(
onClick = { onClick = {
val intent = Intent(if(getContent) Intent.ACTION_GET_CONTENT else Intent.ACTION_PICK) val intent = Intent(if(getContent) Intent.ACTION_GET_CONTENT else Intent.ACTION_PICK)
if(getContent) intent.addCategory(Intent.CATEGORY_OPENABLE) if(getContent) intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*") intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")
getFile.launch(intent) getFileLauncher.launch(intent)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
@@ -567,7 +558,7 @@ fun ChangeUserIcon(navCtrl: NavHostController) {
Button( Button(
onClick = { onClick = {
dpm.setUserIcon(receiver, bitmap) dpm.setUserIcon(receiver, bitmap)
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() context.showOperationResultToast(true)
} }
) { ) {
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))

View File

@@ -25,7 +25,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@@ -41,7 +40,7 @@ import kotlinx.coroutines.launch
@Composable @Composable
fun FunctionItem( fun FunctionItem(
@StringRes title: Int, @StringRes title: Int,
desc: String, desc: String? = null,
@DrawableRes icon: Int? = null, @DrawableRes icon: Int? = null,
operation: () -> Unit operation: () -> Unit
) { ) {
@@ -63,7 +62,7 @@ fun FunctionItem(
style = typography.titleLarge, style = typography.titleLarge,
modifier = Modifier.padding(bottom = if(zhCN) 2.dp else 0.dp) 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( fun RadioButtonItem(
@StringRes text: Int, @StringRes text: Int,
selected: Boolean, selected: Boolean,
operation: () -> Unit, operation: () -> Unit
textColor: Color = colorScheme.onBackground
) { ) {
RadioButtonItem(stringResource(text), selected, operation, textColor) RadioButtonItem(stringResource(text), selected, operation)
} }
@Composable @Composable
fun RadioButtonItem( fun RadioButtonItem(
text: String, text: String,
selected: Boolean, selected: Boolean,
operation: () -> Unit, operation: () -> Unit
textColor: Color = colorScheme.onBackground
) { ) {
Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -104,7 +101,7 @@ fun RadioButtonItem(
.clickable(onClick = operation) .clickable(onClick = operation)
) { ) {
RadioButton(selected = selected, 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( fun CheckBoxItem(
@StringRes text: Int, @StringRes text: Int,
checked: Boolean, checked: Boolean,
operation: (Boolean) -> Unit, operation: (Boolean) -> Unit
textColor: Color = colorScheme.onBackground
) { ) {
Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -124,7 +120,7 @@ fun CheckBoxItem(
checked = checked, checked = checked,
onCheckedChange = operation 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 @Composable
fun SwitchItem( fun SwitchItem(
@StringRes title: Int, @StringRes title: Int,
desc: String, desc: String? = null,
@DrawableRes icon: Int?, @DrawableRes icon: Int? = null,
getState: () -> Boolean, getState: () -> Boolean,
onCheckedChange: (Boolean)->Unit, onCheckedChange: (Boolean)->Unit,
enable: Boolean = true, enabled: Boolean = true,
onClickBlank: (() -> Unit)? = null, onClickBlank: (() -> Unit)? = null,
padding: Boolean = true padding: Boolean = true
) { ) {
var state by remember { mutableStateOf(getState()) } 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 @Composable
fun SwitchItem( fun SwitchItem(
@StringRes title: Int, @StringRes title: Int,
desc: String, desc: String? = null,
@DrawableRes icon: Int?, @DrawableRes icon: Int? = null,
state: Boolean, state: Boolean,
onCheckedChange: (Boolean) -> Unit, onCheckedChange: (Boolean) -> Unit,
enable: Boolean = true, enabled: Boolean = true,
onClickBlank: (() -> Unit)? = null, onClickBlank: (() -> Unit)? = null,
padding: Boolean = true padding: Boolean = true
) { ) {
@@ -172,14 +168,12 @@ fun SwitchItem(
) )
Column(modifier = Modifier.padding(end = 60.dp, bottom = if(zhCN) 2.dp else 0.dp)) { Column(modifier = Modifier.padding(end = 60.dp, bottom = if(zhCN) 2.dp else 0.dp)) {
Text(text = stringResource(title), style = typography.titleLarge) Text(text = stringResource(title), style = typography.titleLarge)
if(desc != "") { if(desc != null) Text(text = desc, color = colorScheme.onBackground.copy(alpha = 0.8F))
Text(text = desc, color = colorScheme.onBackground.copy(alpha = 0.8F))
}
} }
} }
Switch( Switch(
checked = state, onCheckedChange = { onCheckedChange(it) }, checked = state, onCheckedChange = { onCheckedChange(it) },
modifier = Modifier.align(Alignment.CenterEnd), enabled = enable modifier = Modifier.align(Alignment.CenterEnd), enabled = enabled
) )
} }
} }