mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 11:05:59 +00:00
feat: improve intent filter presets
Fix app installer and uninstall app bug
This commit is contained in:
@@ -10,14 +10,20 @@ import androidx.fragment.app.FragmentActivity
|
||||
import com.bintianqi.owndroid.feature.applications.AppInstaller
|
||||
import com.bintianqi.owndroid.feature.applications.AppInstallerViewModel
|
||||
import com.bintianqi.owndroid.ui.theme.OwnDroidTheme
|
||||
import com.bintianqi.owndroid.utils.viewModelFactory
|
||||
|
||||
class AppInstallerActivity : FragmentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
enableEdgeToEdge()
|
||||
super.onCreate(savedInstanceState)
|
||||
val vm by viewModels<AppInstallerViewModel>()
|
||||
val myApp = application as MyApplication
|
||||
val vm by viewModels<AppInstallerViewModel> {
|
||||
viewModelFactory {
|
||||
AppInstallerViewModel(myApp, myApp.container.settingsRepo)
|
||||
}
|
||||
}
|
||||
vm.initialize(intent)
|
||||
val themeState = (application as MyApplication).container.themeState
|
||||
val themeState = myApp.container.themeState
|
||||
setContent {
|
||||
val theme by themeState.collectAsState()
|
||||
OwnDroidTheme(theme) {
|
||||
|
||||
@@ -10,7 +10,24 @@ data class IntentFilterOptions(
|
||||
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)
|
||||
val directionTextMap = mapOf(
|
||||
1 to R.string.personal_to_work,
|
||||
2 to R.string.work_to_personal,
|
||||
3 to R.string.both_direction
|
||||
)
|
||||
|
||||
class IntentFilterPreset(
|
||||
val name: Int, val action: String, val category: String = "", val mimeType: String = ""
|
||||
)
|
||||
|
||||
val crossProfileIntentFilterPresets = listOf(
|
||||
IntentFilterPreset(R.string.open_file, Intent.ACTION_VIEW, Intent.CATEGORY_DEFAULT, "*/*"),
|
||||
IntentFilterPreset(R.string.share, Intent.ACTION_SEND, Intent.CATEGORY_DEFAULT, "*/*"),
|
||||
IntentFilterPreset(R.string.share_multiple, Intent.ACTION_SEND_MULTIPLE),
|
||||
IntentFilterPreset(R.string.edit, Intent.ACTION_EDIT, Intent.CATEGORY_DEFAULT, "*/*"),
|
||||
IntentFilterPreset(R.string.get_content, Intent.ACTION_GET_CONTENT, Intent.CATEGORY_DEFAULT, "*/*"),
|
||||
IntentFilterPreset(R.string.install_app, Intent.ACTION_INSTALL_PACKAGE),
|
||||
IntentFilterPreset(R.string.uninstall_app, Intent.ACTION_UNINSTALL_PACKAGE),
|
||||
IntentFilterPreset(R.string.choose_file, Intent.ACTION_OPEN_DOCUMENT, Intent.CATEGORY_DEFAULT, "*/*"),
|
||||
IntentFilterPreset(R.string.choose_folder, Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||
)
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.bintianqi.owndroid.feature.work_profile
|
||||
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -9,11 +10,13 @@ import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
@@ -26,6 +29,7 @@ import androidx.compose.material3.ExposedDropdownMenuBox
|
||||
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
@@ -39,6 +43,7 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@@ -46,17 +51,17 @@ import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.bintianqi.owndroid.R
|
||||
import com.bintianqi.owndroid.ui.MyLazyScaffold
|
||||
import com.bintianqi.owndroid.ui.NavIcon
|
||||
import com.bintianqi.owndroid.ui.Notes
|
||||
import com.bintianqi.owndroid.utils.BottomPadding
|
||||
import com.bintianqi.owndroid.utils.HorizontalPadding
|
||||
import com.bintianqi.owndroid.utils.adaptiveInsets
|
||||
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun CrossProfileIntentFilterScreen(
|
||||
vm: CrossProfileIntentFilterViewModel, onNavigateUp: () -> Unit
|
||||
vm: CrossProfileIntentFilterViewModel, onNavigateUp: () -> Unit, navigateToPresets: () -> Unit
|
||||
) {
|
||||
val focusMgr = LocalFocusManager.current
|
||||
var action by remember { mutableStateOf("") }
|
||||
@@ -66,7 +71,6 @@ fun CrossProfileIntentFilterScreen(
|
||||
var mimeType by remember { mutableStateOf("") }
|
||||
var dropdown by remember { mutableStateOf(false) }
|
||||
var direction by remember { mutableIntStateOf(3) }
|
||||
var dialog by remember { mutableStateOf(false) }
|
||||
val importLauncher = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) {
|
||||
if (it != null) vm.importFilters(it)
|
||||
}
|
||||
@@ -90,7 +94,7 @@ fun CrossProfileIntentFilterScreen(
|
||||
DropdownMenuItem(
|
||||
{ Text(stringResource(R.string.presets)) },
|
||||
{
|
||||
dialog = true
|
||||
navigateToPresets()
|
||||
menu = false
|
||||
},
|
||||
leadingIcon = {
|
||||
@@ -124,11 +128,6 @@ fun CrossProfileIntentFilterScreen(
|
||||
},
|
||||
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)
|
||||
@@ -205,24 +204,82 @@ fun CrossProfileIntentFilterScreen(
|
||||
Notes(R.string.info_cross_profile_intent_filter)
|
||||
Spacer(Modifier.height(BottomPadding))
|
||||
}
|
||||
if (dialog) AlertDialog(
|
||||
title = { Text(stringResource(R.string.presets)) },
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun CrossProfileIntentFilterPresetsScreen(
|
||||
vm: CrossProfileIntentFilterViewModel, navigateUp: () -> Unit
|
||||
) {
|
||||
var dialog by remember { mutableStateOf<IntentFilterPreset?>(null) }
|
||||
MyLazyScaffold(R.string.presets, navigateUp) {
|
||||
items(crossProfileIntentFilterPresets) {
|
||||
Row(
|
||||
Modifier.padding(start = HorizontalPadding, end = 8.dp, bottom = 2.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Column(Modifier.weight(1F)) {
|
||||
Text(stringResource(it.name))
|
||||
Text(
|
||||
it.action,
|
||||
Modifier.alpha(0.7F),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
IconButton({ dialog = it }) {
|
||||
Icon(Icons.Default.Add, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dialog != null) {
|
||||
var direction by remember { mutableIntStateOf(3) }
|
||||
AlertDialog(
|
||||
title = {
|
||||
Text(stringResource(dialog!!.name))
|
||||
},
|
||||
text = {
|
||||
crossProfileIntentFilterPresets.forEach {
|
||||
Button({
|
||||
vm.addFilter(it.value)
|
||||
dialog = false
|
||||
}) {
|
||||
Text(stringResource(it.key))
|
||||
Column {
|
||||
var dropdown by remember { mutableStateOf(false) }
|
||||
Text(dialog!!.action)
|
||||
ExposedDropdownMenuBox(
|
||||
dropdown, { dropdown = it }, Modifier.padding(top = 5.dp)
|
||||
) {
|
||||
OutlinedTextField(
|
||||
stringResource(directionTextMap[direction]!!), {},
|
||||
Modifier
|
||||
.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryNotEditable)
|
||||
.fillMaxWidth(),
|
||||
textStyle = MaterialTheme.typography.bodyLarge,
|
||||
label = { Text(stringResource(R.string.direction)) }, readOnly = true,
|
||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(dropdown) }
|
||||
)
|
||||
ExposedDropdownMenu(dropdown, { dropdown = false }) {
|
||||
directionTextMap.forEach {
|
||||
DropdownMenuItem({ Text(stringResource(it.value)) }, {
|
||||
direction = it.key
|
||||
dropdown = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton({ dialog = false }) {
|
||||
TextButton({
|
||||
vm.addPreset(dialog!!, direction)
|
||||
dialog = null
|
||||
}) {
|
||||
Text(stringResource(R.string.confirm))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton({ dialog = null }) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
},
|
||||
onDismissRequest = { dialog = false }
|
||||
onDismissRequest = { dialog = null }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,10 @@ class CrossProfileIntentFilterViewModel(
|
||||
toastChannel.sendStatus(true)
|
||||
}
|
||||
|
||||
fun addPreset(preset: IntentFilterPreset, direction: Int) {
|
||||
addFilter(IntentFilterOptions(preset.action, preset.category, preset.mimeType, direction))
|
||||
}
|
||||
|
||||
fun clearFilters() = ph.safeDpmCall {
|
||||
dpm.clearCrossProfileIntentFilters(dar)
|
||||
repo.deleteAllCrossProfileIntentFilters()
|
||||
|
||||
@@ -60,6 +60,7 @@ sealed class Destination : NavKey {
|
||||
@Serializable object CreateWorkProfile : Destination()
|
||||
@Serializable object SuspendPersonalApp : Destination()
|
||||
@Serializable object CrossProfileIntentFilter : Destination()
|
||||
@Serializable object CrossProfileIntentFilterPresets: Destination()
|
||||
@Serializable object DeleteWorkProfile : Destination()
|
||||
|
||||
@Serializable object ApplicationFeatures : Destination()
|
||||
|
||||
@@ -95,6 +95,7 @@ import com.bintianqi.owndroid.feature.users.UserSessionMessageScreen
|
||||
import com.bintianqi.owndroid.feature.users.UsersOptionsScreen
|
||||
import com.bintianqi.owndroid.feature.users.UsersScreen
|
||||
import com.bintianqi.owndroid.feature.work_profile.CreateWorkProfileScreen
|
||||
import com.bintianqi.owndroid.feature.work_profile.CrossProfileIntentFilterPresetsScreen
|
||||
import com.bintianqi.owndroid.feature.work_profile.CrossProfileIntentFilterScreen
|
||||
import com.bintianqi.owndroid.feature.work_profile.DeleteWorkProfileScreen
|
||||
import com.bintianqi.owndroid.feature.work_profile.SuspendPersonalAppScreen
|
||||
@@ -360,7 +361,14 @@ fun myEntryProvider(
|
||||
entry<Destination.CrossProfileIntentFilter> {
|
||||
CrossProfileIntentFilterScreen(
|
||||
viewModel(factory = container.viewModelFactory), ::navigateUp
|
||||
)
|
||||
) {
|
||||
navigate(Destination.CrossProfileIntentFilterPresets)
|
||||
}
|
||||
}
|
||||
entry<Destination.CrossProfileIntentFilterPresets>(
|
||||
metadata = navParentKey(Destination.CrossProfileIntentFilter)
|
||||
) {
|
||||
CrossProfileIntentFilterPresetsScreen(viewModel(), ::navigateUp)
|
||||
}
|
||||
entry<Destination.DeleteWorkProfile>(
|
||||
metadata = navParentKey(Destination.WorkProfile)
|
||||
|
||||
@@ -235,7 +235,9 @@ fun uninstallPackage(
|
||||
val statusExtra = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 999)
|
||||
if (statusExtra == PackageInstaller.STATUS_PENDING_USER_ACTION) {
|
||||
@SuppressWarnings("UnsafeIntentLaunch")
|
||||
context.startActivity(intent.getParcelableExtra(Intent.EXTRA_INTENT) as Intent?)
|
||||
val confirmIntent = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
|
||||
confirmIntent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
context.startActivity(confirmIntent)
|
||||
} else {
|
||||
context.unregisterReceiver(this)
|
||||
if (statusExtra == PackageInstaller.STATUS_SUCCESS) {
|
||||
|
||||
@@ -338,7 +338,12 @@
|
||||
<string name="personal_app_suspended_because_timeout">个人应用已经因此挂起:%1$s</string>
|
||||
<string name="intent_filter">Intent过滤器</string>
|
||||
<string name="presets">预设</string>
|
||||
<string name="allow_file_sharing">允许分享文件</string>
|
||||
<string name="open_file">打开文件</string>
|
||||
<string name="share">分享</string>
|
||||
<string name="share_multiple">分享(多个)</string>
|
||||
<string name="get_content">获取内容</string>
|
||||
<string name="choose_file">选择文件</string>
|
||||
<string name="choose_folder">选择文件夹</string>
|
||||
<string name="direction">方向</string>
|
||||
<string name="both_direction">双向</string>
|
||||
<string name="work_to_personal">工作到个人</string>
|
||||
|
||||
@@ -372,7 +372,12 @@
|
||||
<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="presets">Presets</string>
|
||||
<string name="allow_file_sharing">Allow file sharing</string>
|
||||
<string name="open_file">Open file</string>
|
||||
<string name="share">Share</string>
|
||||
<string name="share_multiple">Share (multiple)</string>
|
||||
<string name="get_content">Get content</string>
|
||||
<string name="choose_file">Choose file</string>
|
||||
<string name="choose_folder">Choose folder</string>
|
||||
<string name="direction">Direction</string>
|
||||
<string name="both_direction">Both direction</string>
|
||||
<string name="work_to_personal">Work to personal</string>
|
||||
|
||||
Reference in New Issue
Block a user