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.AppInstaller
|
||||||
import com.bintianqi.owndroid.feature.applications.AppInstallerViewModel
|
import com.bintianqi.owndroid.feature.applications.AppInstallerViewModel
|
||||||
import com.bintianqi.owndroid.ui.theme.OwnDroidTheme
|
import com.bintianqi.owndroid.ui.theme.OwnDroidTheme
|
||||||
|
import com.bintianqi.owndroid.utils.viewModelFactory
|
||||||
|
|
||||||
class AppInstallerActivity : FragmentActivity() {
|
class AppInstallerActivity : FragmentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
super.onCreate(savedInstanceState)
|
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)
|
vm.initialize(intent)
|
||||||
val themeState = (application as MyApplication).container.themeState
|
val themeState = myApp.container.themeState
|
||||||
setContent {
|
setContent {
|
||||||
val theme by themeState.collectAsState()
|
val theme by themeState.collectAsState()
|
||||||
OwnDroidTheme(theme) {
|
OwnDroidTheme(theme) {
|
||||||
|
|||||||
@@ -10,7 +10,24 @@ data class IntentFilterOptions(
|
|||||||
val direction: Int // 1: private to work, 2: work to private, 3: both
|
val direction: Int // 1: private to work, 2: work to private, 3: both
|
||||||
)
|
)
|
||||||
|
|
||||||
val crossProfileIntentFilterPresets = mapOf(
|
val directionTextMap = mapOf(
|
||||||
R.string.allow_file_sharing to
|
1 to R.string.personal_to_work,
|
||||||
IntentFilterOptions(Intent.ACTION_SEND, Intent.CATEGORY_DEFAULT, "*/*", 3)
|
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.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
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
|
||||||
@@ -9,11 +10,13 @@ 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.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.rememberScrollState
|
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.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Add
|
||||||
import androidx.compose.material.icons.filled.MoreVert
|
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
|
||||||
@@ -26,6 +29,7 @@ import androidx.compose.material3.ExposedDropdownMenuBox
|
|||||||
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -39,6 +43,7 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.platform.LocalFocusManager
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
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.text.input.KeyboardType
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.bintianqi.owndroid.R
|
import com.bintianqi.owndroid.R
|
||||||
|
import com.bintianqi.owndroid.ui.MyLazyScaffold
|
||||||
import com.bintianqi.owndroid.ui.NavIcon
|
import com.bintianqi.owndroid.ui.NavIcon
|
||||||
import com.bintianqi.owndroid.ui.Notes
|
import com.bintianqi.owndroid.ui.Notes
|
||||||
import com.bintianqi.owndroid.utils.BottomPadding
|
import com.bintianqi.owndroid.utils.BottomPadding
|
||||||
import com.bintianqi.owndroid.utils.HorizontalPadding
|
import com.bintianqi.owndroid.utils.HorizontalPadding
|
||||||
import com.bintianqi.owndroid.utils.adaptiveInsets
|
import com.bintianqi.owndroid.utils.adaptiveInsets
|
||||||
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun CrossProfileIntentFilterScreen(
|
fun CrossProfileIntentFilterScreen(
|
||||||
vm: CrossProfileIntentFilterViewModel, onNavigateUp: () -> Unit
|
vm: CrossProfileIntentFilterViewModel, onNavigateUp: () -> Unit, navigateToPresets: () -> Unit
|
||||||
) {
|
) {
|
||||||
val focusMgr = LocalFocusManager.current
|
val focusMgr = LocalFocusManager.current
|
||||||
var action by remember { mutableStateOf("") }
|
var action by remember { mutableStateOf("") }
|
||||||
@@ -66,7 +71,6 @@ fun CrossProfileIntentFilterScreen(
|
|||||||
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 { mutableIntStateOf(3) }
|
var direction by remember { mutableIntStateOf(3) }
|
||||||
var dialog by remember { mutableStateOf(false) }
|
|
||||||
val importLauncher = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) {
|
val importLauncher = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) {
|
||||||
if (it != null) vm.importFilters(it)
|
if (it != null) vm.importFilters(it)
|
||||||
}
|
}
|
||||||
@@ -90,7 +94,7 @@ fun CrossProfileIntentFilterScreen(
|
|||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
{ Text(stringResource(R.string.presets)) },
|
{ Text(stringResource(R.string.presets)) },
|
||||||
{
|
{
|
||||||
dialog = true
|
navigateToPresets()
|
||||||
menu = false
|
menu = false
|
||||||
},
|
},
|
||||||
leadingIcon = {
|
leadingIcon = {
|
||||||
@@ -124,11 +128,6 @@ fun CrossProfileIntentFilterScreen(
|
|||||||
},
|
},
|
||||||
contentWindowInsets = adaptiveInsets()
|
contentWindowInsets = adaptiveInsets()
|
||||||
) { paddingValues ->
|
) { 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(
|
Column(
|
||||||
Modifier
|
Modifier
|
||||||
.padding(paddingValues)
|
.padding(paddingValues)
|
||||||
@@ -205,24 +204,82 @@ fun CrossProfileIntentFilterScreen(
|
|||||||
Notes(R.string.info_cross_profile_intent_filter)
|
Notes(R.string.info_cross_profile_intent_filter)
|
||||||
Spacer(Modifier.height(BottomPadding))
|
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 = {
|
text = {
|
||||||
crossProfileIntentFilterPresets.forEach {
|
Column {
|
||||||
Button({
|
var dropdown by remember { mutableStateOf(false) }
|
||||||
vm.addFilter(it.value)
|
Text(dialog!!.action)
|
||||||
dialog = false
|
ExposedDropdownMenuBox(
|
||||||
}) {
|
dropdown, { dropdown = it }, Modifier.padding(top = 5.dp)
|
||||||
Text(stringResource(it.key))
|
) {
|
||||||
|
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 = {
|
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))
|
Text(stringResource(R.string.cancel))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onDismissRequest = { dialog = false }
|
onDismissRequest = { dialog = null }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ class CrossProfileIntentFilterViewModel(
|
|||||||
toastChannel.sendStatus(true)
|
toastChannel.sendStatus(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun addPreset(preset: IntentFilterPreset, direction: Int) {
|
||||||
|
addFilter(IntentFilterOptions(preset.action, preset.category, preset.mimeType, direction))
|
||||||
|
}
|
||||||
|
|
||||||
fun clearFilters() = ph.safeDpmCall {
|
fun clearFilters() = ph.safeDpmCall {
|
||||||
dpm.clearCrossProfileIntentFilters(dar)
|
dpm.clearCrossProfileIntentFilters(dar)
|
||||||
repo.deleteAllCrossProfileIntentFilters()
|
repo.deleteAllCrossProfileIntentFilters()
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ sealed class Destination : NavKey {
|
|||||||
@Serializable object CreateWorkProfile : Destination()
|
@Serializable object CreateWorkProfile : Destination()
|
||||||
@Serializable object SuspendPersonalApp : Destination()
|
@Serializable object SuspendPersonalApp : Destination()
|
||||||
@Serializable object CrossProfileIntentFilter : Destination()
|
@Serializable object CrossProfileIntentFilter : Destination()
|
||||||
|
@Serializable object CrossProfileIntentFilterPresets: Destination()
|
||||||
@Serializable object DeleteWorkProfile : Destination()
|
@Serializable object DeleteWorkProfile : Destination()
|
||||||
|
|
||||||
@Serializable object ApplicationFeatures : 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.UsersOptionsScreen
|
||||||
import com.bintianqi.owndroid.feature.users.UsersScreen
|
import com.bintianqi.owndroid.feature.users.UsersScreen
|
||||||
import com.bintianqi.owndroid.feature.work_profile.CreateWorkProfileScreen
|
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.CrossProfileIntentFilterScreen
|
||||||
import com.bintianqi.owndroid.feature.work_profile.DeleteWorkProfileScreen
|
import com.bintianqi.owndroid.feature.work_profile.DeleteWorkProfileScreen
|
||||||
import com.bintianqi.owndroid.feature.work_profile.SuspendPersonalAppScreen
|
import com.bintianqi.owndroid.feature.work_profile.SuspendPersonalAppScreen
|
||||||
@@ -360,7 +361,14 @@ fun myEntryProvider(
|
|||||||
entry<Destination.CrossProfileIntentFilter> {
|
entry<Destination.CrossProfileIntentFilter> {
|
||||||
CrossProfileIntentFilterScreen(
|
CrossProfileIntentFilterScreen(
|
||||||
viewModel(factory = container.viewModelFactory), ::navigateUp
|
viewModel(factory = container.viewModelFactory), ::navigateUp
|
||||||
)
|
) {
|
||||||
|
navigate(Destination.CrossProfileIntentFilterPresets)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entry<Destination.CrossProfileIntentFilterPresets>(
|
||||||
|
metadata = navParentKey(Destination.CrossProfileIntentFilter)
|
||||||
|
) {
|
||||||
|
CrossProfileIntentFilterPresetsScreen(viewModel(), ::navigateUp)
|
||||||
}
|
}
|
||||||
entry<Destination.DeleteWorkProfile>(
|
entry<Destination.DeleteWorkProfile>(
|
||||||
metadata = navParentKey(Destination.WorkProfile)
|
metadata = navParentKey(Destination.WorkProfile)
|
||||||
|
|||||||
@@ -235,7 +235,9 @@ fun uninstallPackage(
|
|||||||
val statusExtra = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 999)
|
val statusExtra = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 999)
|
||||||
if (statusExtra == PackageInstaller.STATUS_PENDING_USER_ACTION) {
|
if (statusExtra == PackageInstaller.STATUS_PENDING_USER_ACTION) {
|
||||||
@SuppressWarnings("UnsafeIntentLaunch")
|
@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 {
|
} else {
|
||||||
context.unregisterReceiver(this)
|
context.unregisterReceiver(this)
|
||||||
if (statusExtra == PackageInstaller.STATUS_SUCCESS) {
|
if (statusExtra == PackageInstaller.STATUS_SUCCESS) {
|
||||||
|
|||||||
@@ -338,7 +338,12 @@
|
|||||||
<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="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="direction">方向</string>
|
||||||
<string name="both_direction">双向</string>
|
<string name="both_direction">双向</string>
|
||||||
<string name="work_to_personal">工作到个人</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="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="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="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