mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 11:05:59 +00:00
feat: import/export cross profile intent filters (#240)
Add intent filter presets
This commit is contained in:
@@ -494,7 +494,11 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable<CrossProfileIntentFilter> {
|
composable<CrossProfileIntentFilter> {
|
||||||
CrossProfileIntentFilterScreen(vm::addCrossProfileIntentFilter, ::navigateUp)
|
CrossProfileIntentFilterScreen(
|
||||||
|
vm::addCrossProfileIntentFilter, vm::clearCrossProfileIntentFilters,
|
||||||
|
vm::importCrossProfileIntentFilters, vm::exportCrossProfileIntentFilters,
|
||||||
|
::navigateUp
|
||||||
|
)
|
||||||
}
|
}
|
||||||
composable<DeleteWorkProfile> { DeleteWorkProfileScreen(vm::wipeData, ::navigateUp) }
|
composable<DeleteWorkProfile> { DeleteWorkProfileScreen(vm::wipeData, ::navigateUp) }
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ class ManageSpaceActivity: FragmentActivity() {
|
|||||||
cacheDir.deleteRecursively()
|
cacheDir.deleteRecursively()
|
||||||
codeCacheDir.deleteRecursively()
|
codeCacheDir.deleteRecursively()
|
||||||
if(Build.VERSION.SDK_INT >= 24) {
|
if(Build.VERSION.SDK_INT >= 24) {
|
||||||
|
dataDir.resolve("databases").deleteRecursively()
|
||||||
dataDir.resolve("shared_prefs").deleteRecursively()
|
dataDir.resolve("shared_prefs").deleteRecursively()
|
||||||
} else {
|
} else {
|
||||||
val sharedPref = applicationContext.getSharedPreferences("data", MODE_PRIVATE)
|
val sharedPref = applicationContext.getSharedPreferences("data", MODE_PRIVATE)
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ import android.content.Context
|
|||||||
import android.database.sqlite.SQLiteDatabase
|
import android.database.sqlite.SQLiteDatabase
|
||||||
import android.database.sqlite.SQLiteOpenHelper
|
import android.database.sqlite.SQLiteOpenHelper
|
||||||
|
|
||||||
class MyDbHelper(context: Context): SQLiteOpenHelper(context, "data", null, 4) {
|
class MyDbHelper(context: Context): SQLiteOpenHelper(context, "data", null, 5) {
|
||||||
override fun onCreate(db: SQLiteDatabase) {
|
override fun onCreate(db: SQLiteDatabase) {
|
||||||
db.execSQL(DHIZUKU_CLIENTS_TABLE)
|
db.execSQL(DHIZUKU_CLIENTS_TABLE)
|
||||||
db.execSQL(SECURITY_LOGS_TABLE)
|
db.execSQL(SECURITY_LOGS_TABLE)
|
||||||
db.execSQL(NETWORK_LOGS_TABLE)
|
db.execSQL(NETWORK_LOGS_TABLE)
|
||||||
db.execSQL(APP_GROUPS_TABLE)
|
db.execSQL(APP_GROUPS_TABLE)
|
||||||
|
db.execSQL(CP_INTENTS_TABLE)
|
||||||
}
|
}
|
||||||
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||||
if (oldVersion < 2) {
|
if (oldVersion < 2) {
|
||||||
@@ -21,6 +22,9 @@ class MyDbHelper(context: Context): SQLiteOpenHelper(context, "data", null, 4) {
|
|||||||
if (oldVersion < 4) {
|
if (oldVersion < 4) {
|
||||||
db.execSQL(APP_GROUPS_TABLE)
|
db.execSQL(APP_GROUPS_TABLE)
|
||||||
}
|
}
|
||||||
|
if (oldVersion < 5) {
|
||||||
|
db.execSQL(CP_INTENTS_TABLE)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
companion object {
|
companion object {
|
||||||
const val DHIZUKU_CLIENTS_TABLE = "CREATE TABLE dhizuku_clients (uid INTEGER PRIMARY KEY," +
|
const val DHIZUKU_CLIENTS_TABLE = "CREATE TABLE dhizuku_clients (uid INTEGER PRIMARY KEY," +
|
||||||
@@ -33,5 +37,7 @@ class MyDbHelper(context: Context): SQLiteOpenHelper(context, "data", null, 4) {
|
|||||||
const val APP_GROUPS_TABLE = "CREATE TABLE app_groups(" +
|
const val APP_GROUPS_TABLE = "CREATE TABLE app_groups(" +
|
||||||
"id INTEGER PRIMARY KEY AUTOINCREMENT," +
|
"id INTEGER PRIMARY KEY AUTOINCREMENT," +
|
||||||
"name TEXT, apps TEXT)"
|
"name TEXT, apps TEXT)"
|
||||||
|
const val CP_INTENTS_TABLE = "CREATE TABLE cross_profile_intent_filters (" +
|
||||||
|
"action_str TEXT, category TEXT, mime_type TEXT, direction INTEGER)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,7 @@ import androidx.core.database.getIntOrNull
|
|||||||
import androidx.core.database.getLongOrNull
|
import androidx.core.database.getLongOrNull
|
||||||
import androidx.core.database.getStringOrNull
|
import androidx.core.database.getStringOrNull
|
||||||
import com.bintianqi.owndroid.dpm.AppGroup
|
import com.bintianqi.owndroid.dpm.AppGroup
|
||||||
|
import com.bintianqi.owndroid.dpm.IntentFilterOptions
|
||||||
import com.bintianqi.owndroid.dpm.NetworkLog
|
import com.bintianqi.owndroid.dpm.NetworkLog
|
||||||
import com.bintianqi.owndroid.dpm.SecurityEvent
|
import com.bintianqi.owndroid.dpm.SecurityEvent
|
||||||
import com.bintianqi.owndroid.dpm.SecurityEventWithData
|
import com.bintianqi.owndroid.dpm.SecurityEventWithData
|
||||||
@@ -248,4 +249,29 @@ class MyRepository(val dbHelper: MyDbHelper) {
|
|||||||
fun deleteAppGroup(id: Int) {
|
fun deleteAppGroup(id: Int) {
|
||||||
dbHelper.writableDatabase.delete("app_groups", "id = ?", arrayOf(id.toString()))
|
dbHelper.writableDatabase.delete("app_groups", "id = ?", arrayOf(id.toString()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setCrossProfileIntentFilter(data: IntentFilterOptions) {
|
||||||
|
val cv = ContentValues()
|
||||||
|
cv.put("action_str", data.action)
|
||||||
|
cv.put("category", data.category)
|
||||||
|
cv.put("mime_type", data.mimeType)
|
||||||
|
cv.put("direction", data.direction)
|
||||||
|
dbHelper.writableDatabase.insert("cross_profile_intent_filters", null, cv)
|
||||||
|
}
|
||||||
|
fun getAllCrossProfileIntentFilters(): List<IntentFilterOptions> {
|
||||||
|
val list = mutableListOf<IntentFilterOptions>()
|
||||||
|
dbHelper.readableDatabase.rawQuery(
|
||||||
|
"SELECT * FROM cross_profile_intent_filters", null
|
||||||
|
).use {
|
||||||
|
while (it.moveToNext()) {
|
||||||
|
list += IntentFilterOptions(
|
||||||
|
it.getString(0), it.getString(1), it.getString(2), it.getInt(3)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
fun deleteAllCrossProfileIntentFilters() {
|
||||||
|
dbHelper.writableDatabase.delete("cross_profile_intent_filters", null, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,6 @@ import com.bintianqi.owndroid.dpm.DelegatedAdmin
|
|||||||
import com.bintianqi.owndroid.dpm.DeviceAdmin
|
import com.bintianqi.owndroid.dpm.DeviceAdmin
|
||||||
import com.bintianqi.owndroid.dpm.FrpPolicyInfo
|
import com.bintianqi.owndroid.dpm.FrpPolicyInfo
|
||||||
import com.bintianqi.owndroid.dpm.HardwareProperties
|
import com.bintianqi.owndroid.dpm.HardwareProperties
|
||||||
import com.bintianqi.owndroid.dpm.IntentFilterDirection
|
|
||||||
import com.bintianqi.owndroid.dpm.IntentFilterOptions
|
import com.bintianqi.owndroid.dpm.IntentFilterOptions
|
||||||
import com.bintianqi.owndroid.dpm.IpMode
|
import com.bintianqi.owndroid.dpm.IpMode
|
||||||
import com.bintianqi.owndroid.dpm.KeyguardDisableConfig
|
import com.bintianqi.owndroid.dpm.KeyguardDisableConfig
|
||||||
@@ -1417,13 +1416,28 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
|||||||
val filter = IntentFilter(options.action)
|
val filter = IntentFilter(options.action)
|
||||||
if (options.category.isNotEmpty()) filter.addCategory(options.category)
|
if (options.category.isNotEmpty()) filter.addCategory(options.category)
|
||||||
if (options.mimeType.isNotEmpty()) filter.addDataType(options.mimeType)
|
if (options.mimeType.isNotEmpty()) filter.addDataType(options.mimeType)
|
||||||
val flags = when(options.direction) {
|
DPM.addCrossProfileIntentFilter(DAR, filter, options.direction)
|
||||||
IntentFilterDirection.ToManaged -> DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED
|
myRepo.setCrossProfileIntentFilter(options)
|
||||||
IntentFilterDirection.ToParent -> DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT
|
}
|
||||||
IntentFilterDirection.Both -> DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED or
|
fun clearCrossProfileIntentFilters() {
|
||||||
DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT
|
DPM.clearCrossProfileIntentFilters(DAR)
|
||||||
|
myRepo.deleteAllCrossProfileIntentFilters()
|
||||||
|
}
|
||||||
|
fun importCrossProfileIntentFilters(uri: Uri) {
|
||||||
|
val bytes = application.contentResolver.openInputStream(uri)!!.use {
|
||||||
|
it.readBytes().decodeToString()
|
||||||
|
}
|
||||||
|
val data = Json.decodeFromString<List<IntentFilterOptions>>(bytes)
|
||||||
|
data.forEach {
|
||||||
|
addCrossProfileIntentFilter(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun exportCrossProfileIntentFilters(uri: Uri) {
|
||||||
|
val data = myRepo.getAllCrossProfileIntentFilters()
|
||||||
|
val bytes = Json.encodeToString(data).encodeToByteArray()
|
||||||
|
application.contentResolver.openOutputStream(uri)!!.use {
|
||||||
|
it.write(bytes)
|
||||||
}
|
}
|
||||||
DPM.addCrossProfileIntentFilter(DAR, filter, flags)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val UM = application.getSystemService(Context.USER_SERVICE) as UserManager
|
val UM = application.getSystemService(Context.USER_SERVICE) as UserManager
|
||||||
|
|||||||
@@ -4,34 +4,46 @@ import android.app.admin.DevicePolicyManager
|
|||||||
import android.app.admin.DevicePolicyManager.WIPE_EUICC
|
import android.app.admin.DevicePolicyManager.WIPE_EUICC
|
||||||
import android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE
|
import android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Binder
|
import android.os.Binder
|
||||||
import android.os.Build.VERSION
|
import android.os.Build.VERSION
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.text.KeyboardActions
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.MoreVert
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.Checkbox
|
import androidx.compose.material3.Checkbox
|
||||||
|
import androidx.compose.material3.DropdownMenu
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.ExposedDropdownMenuAnchorType
|
||||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||||
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||||
import androidx.compose.material3.MaterialTheme.typography
|
import androidx.compose.material3.MaterialTheme.typography
|
||||||
import androidx.compose.material3.MenuAnchorType
|
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
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
|
||||||
@@ -45,20 +57,24 @@ import androidx.compose.ui.focus.FocusRequester
|
|||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalFocusManager
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.input.ImeAction
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import com.bintianqi.owndroid.BottomPadding
|
||||||
import com.bintianqi.owndroid.HorizontalPadding
|
import com.bintianqi.owndroid.HorizontalPadding
|
||||||
import com.bintianqi.owndroid.Privilege
|
import com.bintianqi.owndroid.Privilege
|
||||||
import com.bintianqi.owndroid.R
|
import com.bintianqi.owndroid.R
|
||||||
|
import com.bintianqi.owndroid.adaptiveInsets
|
||||||
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.CircularProgressDialog
|
import com.bintianqi.owndroid.ui.CircularProgressDialog
|
||||||
import com.bintianqi.owndroid.ui.FullWidthCheckBoxItem
|
import com.bintianqi.owndroid.ui.FullWidthCheckBoxItem
|
||||||
import com.bintianqi.owndroid.ui.FunctionItem
|
import com.bintianqi.owndroid.ui.FunctionItem
|
||||||
import com.bintianqi.owndroid.ui.MyScaffold
|
import com.bintianqi.owndroid.ui.MyScaffold
|
||||||
|
import com.bintianqi.owndroid.ui.NavIcon
|
||||||
import com.bintianqi.owndroid.ui.Notes
|
import com.bintianqi.owndroid.ui.Notes
|
||||||
import com.bintianqi.owndroid.ui.SwitchItem
|
import com.bintianqi.owndroid.ui.SwitchItem
|
||||||
import com.bintianqi.owndroid.yesOrNo
|
import com.bintianqi.owndroid.yesOrNo
|
||||||
@@ -240,21 +256,24 @@ fun SuspendPersonalAppScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
data class IntentFilterOptions(
|
data class IntentFilterOptions(
|
||||||
val action: String, val category: String, val mimeType: String,
|
val action: String, val category: String, val mimeType: String,
|
||||||
val direction: IntentFilterDirection
|
val direction: Int // 1: private to work, 2: work to private, 3: both
|
||||||
|
)
|
||||||
|
|
||||||
|
val crossProfileIntentFilterPresets = mapOf(
|
||||||
|
R.string.allow_file_sharing to
|
||||||
|
IntentFilterOptions(Intent.ACTION_SEND, Intent.CATEGORY_DEFAULT, "*/*", 3)
|
||||||
)
|
)
|
||||||
enum class IntentFilterDirection(val text: Int) {
|
|
||||||
ToParent(R.string.work_to_personal), ToManaged(R.string.personal_to_work),
|
|
||||||
Both(R.string.both_direction)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable object CrossProfileIntentFilter
|
@Serializable object CrossProfileIntentFilter
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun CrossProfileIntentFilterScreen(
|
fun CrossProfileIntentFilterScreen(
|
||||||
addFilter: (IntentFilterOptions) -> Unit,
|
addFilter: (IntentFilterOptions) -> Unit, clearFilters: () -> Unit,
|
||||||
|
importFilters: (Uri) -> Unit, exportFilters: (Uri) -> Unit,
|
||||||
onNavigateUp: () -> Unit
|
onNavigateUp: () -> Unit
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@@ -265,12 +284,87 @@ fun CrossProfileIntentFilterScreen(
|
|||||||
var customMimeType by remember { mutableStateOf(false) }
|
var customMimeType by remember { mutableStateOf(false) }
|
||||||
var mimeType by remember { mutableStateOf("") }
|
var mimeType by remember { mutableStateOf("") }
|
||||||
var dropdown by remember { mutableStateOf(false) }
|
var dropdown by remember { mutableStateOf(false) }
|
||||||
var direction by remember { mutableStateOf(IntentFilterDirection.Both) }
|
var direction by remember { mutableIntStateOf(3) }
|
||||||
MyScaffold(R.string.intent_filter, onNavigateUp) {
|
var dialog by remember { mutableStateOf(false) }
|
||||||
|
val importLauncher = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) {
|
||||||
|
if (it != null) {
|
||||||
|
importFilters(it)
|
||||||
|
context.showOperationResultToast(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val exportLauncher = rememberLauncherForActivityResult(
|
||||||
|
ActivityResultContracts.CreateDocument("application/json")
|
||||||
|
) {
|
||||||
|
if (it != null) {
|
||||||
|
exportFilters(it)
|
||||||
|
context.showOperationResultToast(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
TopAppBar(
|
||||||
|
title = { Text(stringResource(R.string.intent_filter)) },
|
||||||
|
navigationIcon = { NavIcon(onNavigateUp) },
|
||||||
|
actions = {
|
||||||
|
var menu by remember { mutableStateOf(false) }
|
||||||
|
Box {
|
||||||
|
IconButton({ menu = !menu }) {
|
||||||
|
Icon(Icons.Default.MoreVert, null)
|
||||||
|
}
|
||||||
|
DropdownMenu(menu, { menu = false }) {
|
||||||
|
DropdownMenuItem(
|
||||||
|
{ Text(stringResource(R.string.presets)) },
|
||||||
|
{
|
||||||
|
dialog = true
|
||||||
|
menu = false
|
||||||
|
},
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(painterResource(R.drawable.list_fill0), null)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
DropdownMenuItem(
|
||||||
|
{ Text(stringResource(R.string.import_str)) },
|
||||||
|
{
|
||||||
|
importLauncher.launch(arrayOf("application/json"))
|
||||||
|
menu = false
|
||||||
|
},
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(painterResource(R.drawable.file_open_fill0), null)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
DropdownMenuItem(
|
||||||
|
{ Text(stringResource(R.string.export)) },
|
||||||
|
{
|
||||||
|
exportLauncher.launch("owndroid_intent_filters")
|
||||||
|
menu = false
|
||||||
|
},
|
||||||
|
leadingIcon = {
|
||||||
|
Icon(painterResource(R.drawable.file_export_fill0), null)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
contentWindowInsets = adaptiveInsets()
|
||||||
|
) { paddingValues ->
|
||||||
|
val directionTextMap = mapOf(
|
||||||
|
1 to R.string.personal_to_work,
|
||||||
|
2 to R.string.work_to_personal,
|
||||||
|
3 to R.string.both_direction
|
||||||
|
)
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.padding(paddingValues)
|
||||||
|
.padding(horizontal = HorizontalPadding)
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
|
) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = action, onValueChange = { action = it },
|
value = action, onValueChange = { action = it },
|
||||||
label = { Text("Action") },
|
label = { Text("Action") },
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done),
|
keyboardOptions = KeyboardOptions(
|
||||||
|
keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done),
|
||||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus() }),
|
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus() }),
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
@@ -296,15 +390,17 @@ fun CrossProfileIntentFilterScreen(
|
|||||||
}
|
}
|
||||||
ExposedDropdownMenuBox(dropdown, { dropdown = it }, Modifier.padding(vertical = 5.dp)) {
|
ExposedDropdownMenuBox(dropdown, { dropdown = it }, Modifier.padding(vertical = 5.dp)) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
stringResource(direction.text), {},
|
stringResource(directionTextMap[direction]!!), {},
|
||||||
Modifier.menuAnchor(MenuAnchorType.PrimaryNotEditable).fillMaxWidth(),
|
Modifier
|
||||||
|
.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable)
|
||||||
|
.fillMaxWidth(),
|
||||||
label = { Text(stringResource(R.string.direction)) }, readOnly = true,
|
label = { Text(stringResource(R.string.direction)) }, readOnly = true,
|
||||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(dropdown) }
|
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(dropdown) }
|
||||||
)
|
)
|
||||||
ExposedDropdownMenu(dropdown, { dropdown = false }) {
|
ExposedDropdownMenu(dropdown, { dropdown = false }) {
|
||||||
IntentFilterDirection.entries.forEach {
|
directionTextMap.forEach {
|
||||||
DropdownMenuItem({ Text(stringResource(it.text)) }, {
|
DropdownMenuItem({ Text(stringResource(it.value)) }, {
|
||||||
direction = it
|
direction = it.key
|
||||||
dropdown = false
|
dropdown = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -312,9 +408,7 @@ fun CrossProfileIntentFilterScreen(
|
|||||||
}
|
}
|
||||||
Button(
|
Button(
|
||||||
{
|
{
|
||||||
addFilter(IntentFilterOptions(
|
addFilter(IntentFilterOptions(action, category, mimeType, direction))
|
||||||
action, category, mimeType, direction
|
|
||||||
))
|
|
||||||
context.showOperationResultToast(true)
|
context.showOperationResultToast(true)
|
||||||
},
|
},
|
||||||
Modifier.fillMaxWidth(),
|
Modifier.fillMaxWidth(),
|
||||||
@@ -325,7 +419,7 @@ fun CrossProfileIntentFilterScreen(
|
|||||||
}
|
}
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
Privilege.DPM.clearCrossProfileIntentFilters(Privilege.DAR)
|
clearFilters()
|
||||||
context.showOperationResultToast(true)
|
context.showOperationResultToast(true)
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp)
|
modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp)
|
||||||
@@ -333,6 +427,28 @@ fun CrossProfileIntentFilterScreen(
|
|||||||
Text(stringResource(R.string.clear_cross_profile_filters))
|
Text(stringResource(R.string.clear_cross_profile_filters))
|
||||||
}
|
}
|
||||||
Notes(R.string.info_cross_profile_intent_filter)
|
Notes(R.string.info_cross_profile_intent_filter)
|
||||||
|
Spacer(Modifier.height(BottomPadding))
|
||||||
|
}
|
||||||
|
if (dialog) AlertDialog(
|
||||||
|
title = { Text(stringResource(R.string.presets)) },
|
||||||
|
text = {
|
||||||
|
crossProfileIntentFilterPresets.forEach {
|
||||||
|
Button({
|
||||||
|
addFilter(it.value)
|
||||||
|
context.showOperationResultToast(true)
|
||||||
|
dialog = false
|
||||||
|
}) {
|
||||||
|
Text(stringResource(it.key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton({ dialog = false }) {
|
||||||
|
Text(stringResource(R.string.cancel))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDismissRequest = { dialog = false }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -331,6 +331,8 @@
|
|||||||
<string name="profile_max_time_out_desc">工作资料处于关闭状态的时间达到该限制后会挂起个人应用,0为无限制</string>
|
<string name="profile_max_time_out_desc">工作资料处于关闭状态的时间达到该限制后会挂起个人应用,0为无限制</string>
|
||||||
<string name="personal_app_suspended_because_timeout">个人应用已经因此挂起:%1$s</string>
|
<string name="personal_app_suspended_because_timeout">个人应用已经因此挂起:%1$s</string>
|
||||||
<string name="intent_filter">Intent过滤器</string>
|
<string name="intent_filter">Intent过滤器</string>
|
||||||
|
<string name="presets">预设</string>
|
||||||
|
<string name="allow_file_sharing">允许分享文件</string>
|
||||||
<string name="direction">方向</string>
|
<string name="direction">方向</string>
|
||||||
<string name="both_direction">双向</string>
|
<string name="both_direction">双向</string>
|
||||||
<string name="work_to_personal">工作到个人</string>
|
<string name="work_to_personal">工作到个人</string>
|
||||||
|
|||||||
@@ -365,6 +365,8 @@
|
|||||||
<string name="profile_max_time_out_desc">Personal apps will be suspended after the work profile is closed for this amount of time. 0 means no limit. </string>
|
<string name="profile_max_time_out_desc">Personal apps will be suspended after the work profile is closed for this amount of time. 0 means no limit. </string>
|
||||||
<string name="personal_app_suspended_because_timeout">Personal app suspended because of this: %1$s</string>
|
<string name="personal_app_suspended_because_timeout">Personal app suspended because of this: %1$s</string>
|
||||||
<string name="intent_filter">Intent filter</string>
|
<string name="intent_filter">Intent filter</string>
|
||||||
|
<string name="presets">Presets</string>
|
||||||
|
<string name="allow_file_sharing">Allow file sharing</string>
|
||||||
<string name="direction">Direction</string>
|
<string name="direction">Direction</string>
|
||||||
<string name="both_direction">Both direction</string>
|
<string name="both_direction">Both direction</string>
|
||||||
<string name="work_to_personal">Work to personal</string>
|
<string name="work_to_personal">Work to personal</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user