mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 11:05:59 +00:00
Fix crash caused by DelegatedScope
Use LargeTopAppBar in most screens Optimize Package Chooser Install existing app
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
package com.bintianqi.owndroid
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Intent
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
@@ -32,7 +31,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.MaterialTheme.typography
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
@@ -56,9 +55,8 @@ import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.bintianqi.owndroid.ui.theme.OwnDroidTheme
|
||||
import com.google.accompanist.drawablepainter.rememberDrawablePainter
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -70,46 +68,41 @@ import kotlinx.coroutines.withContext
|
||||
class PackageChooserActivity: ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val myVm by viewModels<MyViewModel>()
|
||||
val vm by viewModels<PackageChooserViewModel>()
|
||||
vm.initialize()
|
||||
val vm by viewModels<MyViewModel>()
|
||||
if(getPackagesProgress.value < 1F) getPackages()
|
||||
setContent {
|
||||
val theme by myVm.theme.collectAsStateWithLifecycle()
|
||||
val theme by vm.theme.collectAsStateWithLifecycle()
|
||||
OwnDroidTheme(theme) {
|
||||
val packages by vm.packages.collectAsStateWithLifecycle()
|
||||
val progress by vm.progress.collectAsStateWithLifecycle()
|
||||
PackageChooserScreen(packages, progress, vm::getPackages) {
|
||||
val packages by installedPackages.collectAsStateWithLifecycle()
|
||||
val progress by getPackagesProgress.collectAsStateWithLifecycle()
|
||||
PackageChooserScreen(packages, progress, ::getPackages) {
|
||||
setResult(0, Intent().putExtra("package", it))
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PackageChooserViewModel(application: Application): AndroidViewModel(application) {
|
||||
val packages = MutableStateFlow(emptyList<PackageInfo>())
|
||||
val progress = MutableStateFlow(0F)
|
||||
val flags = if(Build.VERSION.SDK_INT >= 24) PackageManager.MATCH_DISABLED_COMPONENTS or PackageManager.MATCH_UNINSTALLED_PACKAGES else 0
|
||||
fun initialize() {
|
||||
if(progress.value < 1F) getPackages()
|
||||
}
|
||||
fun getPackages() {
|
||||
packages.value = emptyList()
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val pm = getApplication<Application>().packageManager
|
||||
installedPackages.value = emptyList()
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val pm = packageManager
|
||||
val apps = pm.getInstalledApplications(flags)
|
||||
for(pkg in apps) {
|
||||
packages.update {
|
||||
installedPackages.update {
|
||||
it + PackageInfo(
|
||||
pkg.packageName, pkg.loadLabel(pm).toString(), pkg.loadIcon(pm),
|
||||
(pkg.flags and ApplicationInfo.FLAG_SYSTEM) != 0
|
||||
)
|
||||
}
|
||||
withContext(Dispatchers.Main) { progress.value = packages.value.size.toFloat() / apps.size }
|
||||
withContext(Dispatchers.Main) { getPackagesProgress.value = installedPackages.value.size.toFloat() / apps.size }
|
||||
}
|
||||
}
|
||||
}
|
||||
companion object {
|
||||
val installedPackages = MutableStateFlow(emptyList<PackageInfo>())
|
||||
val getPackagesProgress = MutableStateFlow(0F)
|
||||
}
|
||||
}
|
||||
|
||||
data class PackageInfo(
|
||||
@@ -186,7 +179,7 @@ private fun PackageChooserScreen(
|
||||
Icon(Icons.AutoMirrored.Default.ArrowBack, null)
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(containerColor = colorScheme.background)
|
||||
colors = TopAppBarDefaults.topAppBarColors(MaterialTheme.colorScheme.surfaceContainer)
|
||||
)
|
||||
}
|
||||
) { paddingValues->
|
||||
|
||||
@@ -10,7 +10,6 @@ import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Process
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.result.contract.ActivityResultContract
|
||||
@@ -97,6 +96,9 @@ fun parseDate(date: Date)
|
||||
val Long.humanReadableDate: String
|
||||
get() = SimpleDateFormat("yyyy/MM/dd", Locale.getDefault()).format(Date(this))
|
||||
|
||||
val Long.humanReadableDateTime: String
|
||||
get() = SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault()).format(Date(this))
|
||||
|
||||
fun Context.showOperationResultToast(success: Boolean) {
|
||||
Toast.makeText(this, if(success) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.bintianqi.owndroid.dpm
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.app.PendingIntent
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT
|
||||
import android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED
|
||||
import android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED
|
||||
@@ -15,7 +14,6 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageInstaller
|
||||
import android.content.pm.PackageManager.NameNotFoundException
|
||||
import android.net.Uri
|
||||
import android.os.Build.VERSION
|
||||
import android.os.Looper
|
||||
@@ -50,11 +48,11 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||
import androidx.compose.material3.MaterialTheme.typography
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -88,8 +86,8 @@ import androidx.navigation.compose.rememberNavController
|
||||
import com.bintianqi.owndroid.APK_MIME
|
||||
import com.bintianqi.owndroid.AppInstallerActivity
|
||||
import com.bintianqi.owndroid.AppInstallerViewModel
|
||||
import com.bintianqi.owndroid.R
|
||||
import com.bintianqi.owndroid.ChoosePackageContract
|
||||
import com.bintianqi.owndroid.R
|
||||
import com.bintianqi.owndroid.showOperationResultToast
|
||||
import com.bintianqi.owndroid.ui.Animations
|
||||
import com.bintianqi.owndroid.ui.FunctionItem
|
||||
@@ -117,13 +115,13 @@ fun ApplicationsScreen(onNavigateUp: () -> Unit) {
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
TextField(
|
||||
OutlinedTextField(
|
||||
value = pkgName,
|
||||
onValueChange = { pkgName = it },
|
||||
label = { Text(stringResource(R.string.package_name)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
keyboardActions = KeyboardActions { focusMgr.clearFocus() },
|
||||
trailingIcon = {
|
||||
IconButton({
|
||||
focusMgr.clearFocus()
|
||||
@@ -137,7 +135,7 @@ fun ApplicationsScreen(onNavigateUp: () -> Unit) {
|
||||
)
|
||||
},
|
||||
navigationIcon = { NavIcon(onNavigateUp) },
|
||||
colors = TopAppBarDefaults.topAppBarColors(containerColor = colorScheme.background)
|
||||
colors = TopAppBarDefaults.topAppBarColors(colorScheme.surfaceContainer)
|
||||
)
|
||||
}
|
||||
) { paddingValues->
|
||||
@@ -167,6 +165,7 @@ fun ApplicationsScreen(onNavigateUp: () -> Unit) {
|
||||
|
||||
@Composable
|
||||
private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
|
||||
/** 1:Enable system app, 2:Clear app storage, 3:Set default dialer, 4:App control, 5:Install existing app */
|
||||
var dialogStatus by remember { mutableIntStateOf(0) }
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
@@ -176,31 +175,26 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
|
||||
var suspend by remember { mutableStateOf(false) }
|
||||
var hide by remember { mutableStateOf(false) }
|
||||
var blockUninstall by remember { mutableStateOf(false) }
|
||||
var appControlAction by remember { mutableIntStateOf(0) }
|
||||
var appControlAction by remember { mutableIntStateOf(0) } // 1:Suspend, 2:Hide, 3:Block uninstall
|
||||
val focusMgr = LocalFocusManager.current
|
||||
val appControl: (Boolean) -> Unit = {
|
||||
when(appControlAction) {
|
||||
1 -> if(VERSION.SDK_INT >= 24) dpm.setPackagesSuspended(receiver, arrayOf(pkgName), it)
|
||||
2 -> dpm.setApplicationHidden(receiver, pkgName, it)
|
||||
3 -> dpm.setUninstallBlocked(receiver, pkgName, it)
|
||||
fun refresh() {
|
||||
if(VERSION.SDK_INT >= 24) {
|
||||
try {
|
||||
suspend = dpm.isPackageSuspended(receiver, pkgName)
|
||||
} catch(_: Exception) {}
|
||||
}
|
||||
when(appControlAction) {
|
||||
1 -> {
|
||||
suspend = try{ if(VERSION.SDK_INT >= 24) dpm.isPackageSuspended(receiver, pkgName) else false }
|
||||
catch(_: NameNotFoundException) { false }
|
||||
catch(_: IllegalArgumentException) { false }
|
||||
}
|
||||
2 -> hide = dpm.isApplicationHidden(receiver,pkgName)
|
||||
3 -> blockUninstall = dpm.isUninstallBlocked(receiver,pkgName)
|
||||
}
|
||||
}
|
||||
LaunchedEffect(pkgName) {
|
||||
suspend = try{ if(VERSION.SDK_INT >= 24) dpm.isPackageSuspended(receiver, pkgName) else false }
|
||||
catch(_: NameNotFoundException) { false }
|
||||
catch(_: IllegalArgumentException) { false }
|
||||
hide = dpm.isApplicationHidden(receiver, pkgName)
|
||||
blockUninstall = dpm.isUninstallBlocked(receiver, pkgName)
|
||||
}
|
||||
fun appControl(status: Boolean) {
|
||||
when(appControlAction) {
|
||||
1 -> if(VERSION.SDK_INT >= 24) dpm.setPackagesSuspended(receiver, arrayOf(pkgName), status)
|
||||
2 -> dpm.setApplicationHidden(receiver, pkgName, status)
|
||||
3 -> dpm.setUninstallBlocked(receiver, pkgName, status)
|
||||
}
|
||||
refresh()
|
||||
}
|
||||
LaunchedEffect(pkgName) { refresh() }
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())
|
||||
) {
|
||||
@@ -222,7 +216,7 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
|
||||
)
|
||||
}
|
||||
SwitchItem(
|
||||
title = R.string.hide, desc = stringResource(R.string.isapphidden_desc), icon = R.drawable.visibility_off_fill0,
|
||||
title = R.string.hide, icon = R.drawable.visibility_off_fill0,
|
||||
state = hide,
|
||||
onCheckedChange = { appControlAction = 2; appControl(it) },
|
||||
onClickBlank = { appControlAction = 2; dialogStatus = 4 }
|
||||
@@ -270,8 +264,10 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
|
||||
startActivity(context, intent, null)
|
||||
}
|
||||
FunctionItem(title = R.string.install_app, icon = R.drawable.install_mobile_fill0) {
|
||||
Toast.makeText(context, R.string.choose_apk_file, Toast.LENGTH_SHORT).show()
|
||||
chooseApks.launch(APK_MIME)
|
||||
}
|
||||
if(VERSION.SDK_INT >= 28) FunctionItem(R.string.install_existing_app, icon = R.drawable.install_mobile_fill0) { dialogStatus = 5 }
|
||||
FunctionItem(title = R.string.uninstall_app, icon = R.drawable.delete_fill0) { onNavigate(UninstallPackage) }
|
||||
if(VERSION.SDK_INT >= 34 && (deviceOwner || dpm.isOrgProfile(receiver))) {
|
||||
FunctionItem(title = R.string.set_default_dialer, icon = R.drawable.call_fill0) {
|
||||
@@ -287,13 +283,12 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
|
||||
},
|
||||
onDismissRequest = { dialogStatus = 0 },
|
||||
dismissButton = {
|
||||
TextButton(onClick = { dialogStatus = 0 }) {
|
||||
TextButton({ dialogStatus = 0 }) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
TextButton({
|
||||
try {
|
||||
dpm.enableSystemApp(receiver, pkgName)
|
||||
context.showOperationResultToast(true)
|
||||
@@ -301,8 +296,7 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
|
||||
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
dialogStatus = 0
|
||||
}
|
||||
) {
|
||||
}) {
|
||||
Text(stringResource(R.string.confirm))
|
||||
}
|
||||
},
|
||||
@@ -317,7 +311,7 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
|
||||
TextButton(
|
||||
onClick = {
|
||||
val executor = Executors.newCachedThreadPool()
|
||||
val onClear = DevicePolicyManager.OnClearApplicationUserDataListener { pkg: String, succeed: Boolean ->
|
||||
dpm.clearApplicationUserData(receiver, pkgName, executor) { pkg: String, succeed: Boolean ->
|
||||
Looper.prepare()
|
||||
val toastText =
|
||||
if(pkg != "") { "$pkg\n" } else { "" } +
|
||||
@@ -326,7 +320,6 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
|
||||
Toast.makeText(context, toastText, Toast.LENGTH_SHORT).show()
|
||||
Looper.loop()
|
||||
}
|
||||
dpm.clearApplicationUserData(receiver, pkgName, executor, onClear)
|
||||
dialogStatus = 0
|
||||
},
|
||||
colors = ButtonDefaults.textButtonColors(contentColor = colorScheme.error)
|
||||
@@ -335,9 +328,7 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = { dialogStatus = 0 }
|
||||
) {
|
||||
TextButton({ dialogStatus = 0 }) {
|
||||
Text(text = stringResource(R.string.cancel))
|
||||
}
|
||||
},
|
||||
@@ -351,22 +342,20 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
|
||||
},
|
||||
onDismissRequest = { dialogStatus = 0 },
|
||||
dismissButton = {
|
||||
TextButton(onClick = { dialogStatus = 0 }) {
|
||||
TextButton({ dialogStatus = 0 }) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
TextButton({
|
||||
try {
|
||||
dpm.setDefaultDialerApplication(pkgName)
|
||||
context.showOperationResultToast(true)
|
||||
} catch(_: IllegalArgumentException) {
|
||||
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
context.showOperationResultToast(false)
|
||||
}
|
||||
dialogStatus = 0
|
||||
}
|
||||
) {
|
||||
}) {
|
||||
Text(stringResource(R.string.confirm))
|
||||
}
|
||||
},
|
||||
@@ -379,8 +368,7 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { dialogStatus = 0 },
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(
|
||||
Text(stringResource(
|
||||
when(appControlAction) {
|
||||
1 -> R.string.suspend
|
||||
2 -> R.string.hide
|
||||
@@ -388,9 +376,7 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
|
||||
4 -> R.string.always_on_vpn
|
||||
else -> R.string.unknown
|
||||
}
|
||||
),
|
||||
style = typography.headlineMedium
|
||||
)
|
||||
))
|
||||
},
|
||||
text = {
|
||||
val enabled = when(appControlAction){
|
||||
@@ -406,27 +392,38 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
TextButton({
|
||||
appControl(true)
|
||||
dialogStatus = 0
|
||||
}
|
||||
) {
|
||||
}) {
|
||||
Text(text = stringResource(R.string.enable))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
TextButton({
|
||||
appControl(false)
|
||||
dialogStatus = 0
|
||||
}
|
||||
) {
|
||||
}) {
|
||||
Text(text = stringResource(R.string.disable))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
if(dialogStatus == 5 && VERSION.SDK_INT >= 28) AlertDialog(
|
||||
text = { Text(stringResource(R.string.info_install_existing_app)) },
|
||||
confirmButton = {
|
||||
TextButton({
|
||||
context.showOperationResultToast(dpm.installExistingPackage(receiver, pkgName))
|
||||
dialogStatus = 0
|
||||
}) {
|
||||
Text(stringResource(R.string.confirm))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton({ dialogStatus = 0 }) { Text(stringResource(R.string.cancel)) }
|
||||
},
|
||||
onDismissRequest = { dialogStatus = 0 }
|
||||
)
|
||||
LaunchedEffect(dialogStatus) { focusMgr.clearFocus() }
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,7 @@ import androidx.compose.material3.TabRow
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberDatePickerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -129,6 +130,7 @@ import com.bintianqi.owndroid.R
|
||||
import com.bintianqi.owndroid.SharedPrefs
|
||||
import com.bintianqi.owndroid.formatFileSize
|
||||
import com.bintianqi.owndroid.humanReadableDate
|
||||
import com.bintianqi.owndroid.humanReadableDateTime
|
||||
import com.bintianqi.owndroid.showOperationResultToast
|
||||
import com.bintianqi.owndroid.ui.CheckBoxItem
|
||||
import com.bintianqi.owndroid.ui.ExpandExposedTextFieldIcon
|
||||
@@ -136,6 +138,7 @@ import com.bintianqi.owndroid.ui.FunctionItem
|
||||
import com.bintianqi.owndroid.ui.InfoCard
|
||||
import com.bintianqi.owndroid.ui.ListItem
|
||||
import com.bintianqi.owndroid.ui.MyScaffold
|
||||
import com.bintianqi.owndroid.ui.MySmallTitleScaffold
|
||||
import com.bintianqi.owndroid.ui.NavIcon
|
||||
import com.bintianqi.owndroid.ui.RadioButtonItem
|
||||
import com.bintianqi.owndroid.ui.SwitchItem
|
||||
@@ -231,7 +234,8 @@ fun WifiScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNavigateTo
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.wifi)) },
|
||||
navigationIcon = { NavIcon(onNavigateUp) }
|
||||
navigationIcon = { NavIcon(onNavigateUp) },
|
||||
colors = TopAppBarDefaults.topAppBarColors(MaterialTheme.colorScheme.surfaceContainer)
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
@@ -466,7 +470,7 @@ object AddNetwork
|
||||
|
||||
@Composable
|
||||
fun AddNetworkScreen(data: Bundle, onNavigateUp: () -> Unit) {
|
||||
MyScaffold(R.string.update_network, 0.dp, onNavigateUp, false) {
|
||||
MySmallTitleScaffold(R.string.update_network, 0.dp, onNavigateUp) {
|
||||
AddNetworkScreen(data.getParcelable("wifi_configuration"), onNavigateUp)
|
||||
}
|
||||
}
|
||||
@@ -1213,8 +1217,6 @@ fun NetworkStatsScreen(onNavigateUp: () -> Unit, onNavigateToViewer: (NetworkSta
|
||||
context.showOperationResultToast(false)
|
||||
}
|
||||
} else {
|
||||
val bundle = Bundle()
|
||||
bundle.putInt("size", buckets.size)
|
||||
val stats = buckets.map {
|
||||
NetworkStatsViewer.Data(
|
||||
it.rxBytes, it.rxPackets, it.txBytes, it.txPackets,
|
||||
@@ -1289,7 +1291,7 @@ data class NetworkStatsViewer(
|
||||
fun NetworkStatsViewerScreen(nsv: NetworkStatsViewer, onNavigateUp: () -> Unit) {
|
||||
var index by remember { mutableIntStateOf(0) }
|
||||
val size = nsv.stats.size
|
||||
MyScaffold(R.string.place_holder, 8.dp, onNavigateUp, false) {
|
||||
MySmallTitleScaffold(R.string.place_holder, 8.dp, onNavigateUp) {
|
||||
if(size > 1) Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally).padding(bottom = 8.dp)
|
||||
@@ -1310,7 +1312,7 @@ fun NetworkStatsViewerScreen(nsv: NetworkStatsViewer, onNavigateUp: () -> Unit)
|
||||
}
|
||||
val data = nsv.stats[index]
|
||||
Text(
|
||||
data.startTime.humanReadableDate + " ~ " + data.endTime.humanReadableDate,
|
||||
data.startTime.humanReadableDateTime + " ~ " + data.endTime.humanReadableDateTime,
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally).padding(bottom = 8.dp)
|
||||
)
|
||||
val txBytes = data.txBytes
|
||||
@@ -1556,10 +1558,11 @@ fun RecommendedGlobalProxyScreen(onNavigateUp: () -> Unit) {
|
||||
label = { Text(stringResource(R.string.excluded_hosts)) },
|
||||
maxLines = 5,
|
||||
minLines = 2,
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions { focusMgr.clearFocus() },
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 2.dp)
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 4.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
if(proxyType == 0) {
|
||||
@@ -1597,7 +1600,7 @@ fun RecommendedGlobalProxyScreen(onNavigateUp: () -> Unit) {
|
||||
dpm.setRecommendedGlobalProxy(receiver, proxyInfo)
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
@@ -1716,7 +1719,7 @@ fun PreferentialNetworkServiceScreen(onNavigateUp: () -> Unit, onNavigate: (AddP
|
||||
configs.addAll(dpm.preferentialNetworkServiceConfigs)
|
||||
}
|
||||
LaunchedEffect(Unit) { refresh() }
|
||||
MyScaffold(R.string.preferential_network_service, 0.dp, onNavigateUp, false) {
|
||||
MySmallTitleScaffold(R.string.preferential_network_service, 0.dp, onNavigateUp) {
|
||||
SwitchItem(R.string.enabled, state = masterEnabled, onCheckedChange = {
|
||||
dpm.isPreferentialNetworkServiceEnabled = it
|
||||
refresh()
|
||||
@@ -1780,7 +1783,7 @@ fun AddPreferentialNetworkServiceConfigScreen(route: AddPreferentialNetworkServi
|
||||
var blockNonMatching by remember { mutableStateOf(route.blockNonMatching) }
|
||||
var excludedUids by remember { mutableStateOf(route.excludedUids.joinToString("\n")) }
|
||||
var includedUids by remember { mutableStateOf(route.includedUids.joinToString("\n")) }
|
||||
MyScaffold(R.string.preferential_network_service, 8.dp, onNavigateUp, false) {
|
||||
MySmallTitleScaffold(R.string.preferential_network_service, 8.dp, onNavigateUp) {
|
||||
SwitchItem(title = R.string.enabled, state = enabled, onCheckedChange = { enabled = it }, padding = false)
|
||||
AnimatedVisibility(enabled) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
@@ -1882,7 +1885,7 @@ fun OverrideApnScreen(onNavigateUp: () -> Unit, onNavigateToAddSetting: (Bundle)
|
||||
settings.addAll(dpm.getOverrideApns(receiver))
|
||||
}
|
||||
LaunchedEffect(Unit) { refresh() }
|
||||
MyScaffold(R.string.override_apn, 0.dp, onNavigateUp, false) {
|
||||
MyScaffold(R.string.override_apn, 0.dp, onNavigateUp) {
|
||||
SwitchItem(
|
||||
R.string.enable, state = enabled,
|
||||
onCheckedChange = {
|
||||
@@ -1966,7 +1969,7 @@ fun AddApnSettingScreen(origin: ApnSetting?, onNavigateUp: () -> Unit) {
|
||||
var persistent by remember { mutableStateOf(if(VERSION.SDK_INT >= 33) origin?.isPersistent == true else false) }
|
||||
var alwaysOn by remember { mutableStateOf(VERSION.SDK_INT >= 35 && origin?.isAlwaysOn == true) }
|
||||
var errorMessage: String? by remember { mutableStateOf(null) }
|
||||
MyScaffold(R.string.apn_setting, 8.dp, onNavigateUp, false) {
|
||||
MySmallTitleScaffold(R.string.apn_setting, 8.dp, onNavigateUp) {
|
||||
val protocolMap = mapOf(
|
||||
ApnSetting.PROTOCOL_IP to "IPv4", ApnSetting.PROTOCOL_IPV6 to "IPv6",
|
||||
ApnSetting.PROTOCOL_IPV4V6 to "IPv4/v6", ApnSetting.PROTOCOL_PPP to "PPP"
|
||||
|
||||
@@ -15,6 +15,7 @@ import android.os.RemoteException
|
||||
import android.os.UserManager
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.annotation.Keep
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
@@ -495,6 +496,7 @@ fun DeviceOwnerScreen(onNavigateUp: () -> Unit) {
|
||||
}
|
||||
}
|
||||
|
||||
@Keep
|
||||
@Suppress("InlinedApi")
|
||||
enum class DelegatedScope(val id: String, @StringRes val string: Int, val requiresApi: Int = 0) {
|
||||
AppRestrictions(DevicePolicyManager.DELEGATION_APP_RESTRICTIONS, R.string.manage_application_restrictions),
|
||||
@@ -585,7 +587,7 @@ fun AddDelegatedAdminScreen(data: AddDelegatedAdmin, onNavigateUp: () -> Unit) {
|
||||
val choosePackage = rememberLauncherForActivityResult(ChoosePackageContract()) { result ->
|
||||
result?.let { input = it }
|
||||
}
|
||||
MyScaffold(if(updateMode) R.string.place_holder else R.string.add_delegated_admin, 0.dp, onNavigateUp, !updateMode) {
|
||||
MySmallTitleScaffold(if(updateMode) R.string.place_holder else R.string.add_delegated_admin, 0.dp, onNavigateUp,) {
|
||||
OutlinedTextField(
|
||||
value = input, onValueChange = { input = it },
|
||||
label = { Text(stringResource(R.string.package_name)) },
|
||||
|
||||
@@ -38,7 +38,7 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.bintianqi.owndroid.IUserService
|
||||
import com.bintianqi.owndroid.R
|
||||
import com.bintianqi.owndroid.ui.MyScaffold
|
||||
import com.bintianqi.owndroid.ui.MySmallTitleScaffold
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.Serializable
|
||||
@@ -66,7 +66,7 @@ fun ShizukuScreen(navArgs: Bundle, onNavigateUp: () -> Unit, onNavigateToAccount
|
||||
null
|
||||
}
|
||||
}
|
||||
MyScaffold(R.string.shizuku, 0.dp, onNavigateUp, false) {
|
||||
MySmallTitleScaffold(R.string.shizuku, 0.dp, onNavigateUp) {
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
@@ -185,7 +185,7 @@ data class Accounts(
|
||||
|
||||
@Composable
|
||||
fun AccountsScreen(accounts: Accounts, onNavigateUp: () -> Unit) {
|
||||
MyScaffold(R.string.accounts, 8.dp, onNavigateUp, false) {
|
||||
MySmallTitleScaffold(R.string.accounts, 8.dp, onNavigateUp) {
|
||||
accounts.list.forEach {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
||||
@@ -99,6 +99,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TimePicker
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberDatePickerState
|
||||
import androidx.compose.material3.rememberTimePickerState
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -138,6 +139,7 @@ import com.bintianqi.owndroid.ui.FunctionItem
|
||||
import com.bintianqi.owndroid.ui.InfoCard
|
||||
import com.bintianqi.owndroid.ui.ListItem
|
||||
import com.bintianqi.owndroid.ui.MyScaffold
|
||||
import com.bintianqi.owndroid.ui.MySmallTitleScaffold
|
||||
import com.bintianqi.owndroid.ui.NavIcon
|
||||
import com.bintianqi.owndroid.ui.RadioButtonItem
|
||||
import com.bintianqi.owndroid.ui.SwitchItem
|
||||
@@ -431,7 +433,7 @@ fun HardwareMonitorScreen(onNavigateUp: () -> Unit) {
|
||||
delay(refreshIntervalMs)
|
||||
}
|
||||
}
|
||||
MyScaffold(R.string.hardware_monitor, 8.dp, onNavigateUp, false) {
|
||||
MySmallTitleScaffold(R.string.hardware_monitor, 8.dp, onNavigateUp) {
|
||||
Text(stringResource(R.string.refresh_interval), style = typography.titleLarge, modifier = Modifier.padding(vertical = 4.dp))
|
||||
Slider(refreshInterval, { refreshInterval = it }, valueRange = 0.5F..2F, steps = 14)
|
||||
Text("${refreshIntervalMs}ms")
|
||||
@@ -943,7 +945,7 @@ fun NearbyStreamingPolicyScreen(onNavigateUp: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
var appPolicy by remember { mutableIntStateOf(dpm.nearbyAppStreamingPolicy) }
|
||||
MyScaffold(R.string.nearby_streaming_policy, 0.dp, onNavigateUp, false) {
|
||||
MyScaffold(R.string.nearby_streaming_policy, 0.dp, onNavigateUp) {
|
||||
Text(
|
||||
stringResource(R.string.nearby_app_streaming),
|
||||
Modifier.padding(start = 8.dp, top = 10.dp, bottom = 4.dp), style = typography.titleLarge
|
||||
@@ -1018,7 +1020,8 @@ fun LockTaskModeScreen(onNavigateUp: () -> Unit) {
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.lock_task_mode)) },
|
||||
navigationIcon = { NavIcon(onNavigateUp) }
|
||||
navigationIcon = { NavIcon(onNavigateUp) },
|
||||
colors = TopAppBarDefaults.topAppBarColors(colorScheme.surfaceContainer)
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
@@ -1108,7 +1111,8 @@ private fun ColumnScope.StartLockTaskMode() {
|
||||
} else {
|
||||
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
},
|
||||
enabled = startLockTaskApp.isNotBlank() && (!specifyActivity || startLockTaskActivity.isNotBlank())
|
||||
) {
|
||||
Text(stringResource(R.string.start))
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ fun UserRestrictionOptionsScreen(
|
||||
data: UserRestrictionOptions, restrictions: Bundle,
|
||||
onNavigateUp: () -> Unit, onRestrictionChange: (String, Boolean) -> Unit
|
||||
) {
|
||||
MyScaffold(data.title, 0.dp, onNavigateUp, false) {
|
||||
MyScaffold(data.title, 0.dp, onNavigateUp) {
|
||||
data.items.filter { Build.VERSION.SDK_INT >= it.requiresApi }.forEach { restriction ->
|
||||
SwitchItem(
|
||||
restriction.name, restriction.id, restriction.icon,
|
||||
|
||||
@@ -171,7 +171,7 @@ fun CreateWorkProfileScreen(onNavigateUp: () -> Unit) {
|
||||
fun OrganizationOwnedProfileScreen(onNavigateUp: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
MyScaffold(R.string.org_owned_work_profile, 8.dp, onNavigateUp, false) {
|
||||
MyScaffold(R.string.org_owned_work_profile, 8.dp, onNavigateUp) {
|
||||
CardItem(R.string.org_owned_work_profile, dpm.isOrganizationOwnedDeviceWithManagedProfile.yesOrNo)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
if(!dpm.isOrganizationOwnedDeviceWithManagedProfile) {
|
||||
|
||||
@@ -23,9 +23,9 @@ import androidx.compose.material3.MaterialTheme.typography
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@@ -292,20 +292,16 @@ fun MyScaffold(
|
||||
@StringRes title: Int,
|
||||
horizonPadding: Dp,
|
||||
onNavIconClicked: () -> Unit,
|
||||
displayTitle: Boolean = true,
|
||||
content: @Composable ColumnScope.() -> Unit
|
||||
) {
|
||||
val scrollState = rememberScrollState()
|
||||
val sb = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
||||
Scaffold(
|
||||
Modifier.nestedScroll(sb.nestedScrollConnection),
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(title),
|
||||
modifier = if(displayTitle) Modifier.alpha((maxOf(scrollState.value-90,0)).toFloat()/50) else Modifier
|
||||
)
|
||||
},
|
||||
navigationIcon = { NavIcon (onNavIconClicked) }
|
||||
LargeTopAppBar(
|
||||
{ Text(stringResource(title)) },
|
||||
navigationIcon = { NavIcon(onNavIconClicked) },
|
||||
scrollBehavior = sb
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
@@ -314,14 +310,39 @@ fun MyScaffold(
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
.padding(horizontal = horizonPadding)
|
||||
.verticalScroll(scrollState)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(bottom = 80.dp)
|
||||
) {
|
||||
if(displayTitle) Text(
|
||||
text = stringResource(title),
|
||||
style = typography.headlineLarge,
|
||||
modifier = Modifier.padding(start = if(horizonPadding == 0.dp) 16.dp else 0.dp,top = 10.dp, bottom = 5.dp)
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MySmallTitleScaffold(
|
||||
@StringRes title: Int,
|
||||
horizonPadding: Dp,
|
||||
onNavIconClicked: () -> Unit,
|
||||
content: @Composable ColumnScope.() -> Unit
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
{ Text(stringResource(title)) },
|
||||
navigationIcon = { NavIcon(onNavIconClicked) },
|
||||
colors = TopAppBarDefaults.topAppBarColors(colorScheme.surfaceContainer)
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
.padding(horizontal = horizonPadding)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(bottom = 80.dp)
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,19 +6,11 @@ import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.bintianqi.owndroid.MyViewModel
|
||||
import com.bintianqi.owndroid.ThemeSettings
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
private val lightScheme = lightColorScheme(
|
||||
primary = primaryLight,
|
||||
@@ -111,8 +103,6 @@ fun OwnDroidTheme(
|
||||
else -> lightScheme
|
||||
}.let {
|
||||
if(darkTheme && theme.blackTheme) it.copy(background = Color.Black) else it
|
||||
}.let {
|
||||
if(!darkTheme) it.copy(background = it.primary.copy(alpha = 0.05f)) else it
|
||||
}
|
||||
val view = LocalView.current
|
||||
SideEffect {
|
||||
|
||||
@@ -410,6 +410,9 @@
|
||||
<string name="silent_uninstall">Тихое удаление</string>
|
||||
<string name="request_uninstall">Запросить удаление</string>
|
||||
<string name="install_app">Установить приложение</string>
|
||||
<!--TODO: 2 strings-->
|
||||
<string name="choose_apk_file">Choose an APK file</string>
|
||||
<string name="install_existing_app">Install existing app</string>
|
||||
<string name="search">Поиск</string>
|
||||
|
||||
<!--Ограничения пользователя-->
|
||||
@@ -701,6 +704,7 @@
|
||||
<string name="info_suspend_app">Приостановленный пакет не сможет запускать активности. Его уведомления будут скрыты, он не будет отображаться в списке последних запущенных приложений, не сможет показывать всплывающие уведомления или диалоговые окна и звонить на устройство.\nНекоторые приложения не могут быть приостановлены, такие как администраторы устройства, активный лаунчер и приложение для набора номера по умолчанию.</string>
|
||||
<string name="info_disable_user_control">Пользователь не сможет очищать данные приложений или принудительно останавливать пакеты.</string>
|
||||
<string name="info_keep_uninstalled_apps">Установить список приложений, которые нужно сохранить в виде APK-файлов, даже если ни у одного пользователя в данный момент они не установлены.</string>
|
||||
<!--TODO--><string name="info_install_existing_app">Install an existing package that has been installed in another user, or has been kept after uninstall.</string>
|
||||
<string name="info_headless_system_user_mode">Режим "безголового" системного пользователя означает, что системный пользователь запускает системные службы и некоторый системный интерфейс, но он не связан с каким-либо реальным человеком, и для связи с реальными людьми должны быть созданы дополнительные пользователи.</string>
|
||||
<string name="info_logout">If the current user is not switched by OwnDroid, this function cannot be used.</string> <!--TODO-->
|
||||
<string name="info_affiliation_id">Когда владелец устройства создает управляемого пользователя, управляемый пользователь не является аффилированным. Чтобы сделать управляемого пользователя аффилированным с владельцем устройства, вам следует установить одинаковые аффилированные идентификаторы в основном и управляемом пользователях.</string>
|
||||
|
||||
@@ -416,7 +416,10 @@
|
||||
<string name="silent_uninstall">Sessiz kaldırma</string>
|
||||
<string name="request_uninstall">Kaldırma isteği</string>
|
||||
<string name="install_app">Uygulamayı yükle</string>
|
||||
<string name="search">Search</string> <!--TODO-->
|
||||
<!--TODO: 3 strings-->
|
||||
<string name="choose_apk_file">Choose an APK file</string>
|
||||
<string name="install_existing_app">Install existing app</string>
|
||||
<string name="search">Search</string>
|
||||
|
||||
<!--UserRestriction-->
|
||||
<string name="user_restriction">Kullanıcı kısıtlaması</string>
|
||||
|
||||
@@ -372,7 +372,7 @@
|
||||
<string name="scope_is_work_profile">作用域: 工作资料</string>
|
||||
<string name="app_info">应用详情</string>
|
||||
<string name="not_installed">未安装</string>
|
||||
<string name="block_uninstall">阻止</string>
|
||||
<string name="block_uninstall">阻止卸载</string>
|
||||
<string name="ucd">禁止用户控制</string>
|
||||
<string name="ucd_desc">用户将无法清除这些应用的存储空间或强制停止这些应用</string>
|
||||
<string name="app_list_is">应用列表:</string>
|
||||
@@ -398,6 +398,8 @@
|
||||
<string name="silent_uninstall">静默卸载</string>
|
||||
<string name="request_uninstall">请求卸载</string>
|
||||
<string name="install_app">安装应用</string>
|
||||
<string name="choose_apk_file">选择一个APK文件</string>
|
||||
<string name="install_existing_app">安装已存在的应用</string>
|
||||
<string name="enable_system_app">启用系统应用</string>
|
||||
<string name="enable_system_app_desc">重新启用一个默认被禁用的系统应用</string>
|
||||
<string name="search">搜索</string>
|
||||
@@ -684,6 +686,7 @@
|
||||
<string name="info_suspend_app">挂起的应用无法被打开,通知会被隐藏,不会在最近任务中显示,不能弹窗,不能发送Toast。\n有些应用无法被挂起,比如Device admin、启动器和默认拨号应用。</string>
|
||||
<string name="info_disable_user_control">用户无法清除这些应用的存储空间,也无法强制停止应用</string>
|
||||
<string name="info_keep_uninstalled_apps">这个列表中的应用的APK将会一直保留,即使没有任何用户安装这个应用</string>
|
||||
<string name="info_install_existing_app">安装一个已经在其他用户中安装或在卸载后保留的app。</string>
|
||||
<string name="info_headless_system_user_mode">无头系统用户模式意味着系统用户运行系统服务和一些系统UI,但它不与任何真实的人相关联,必须创建额外的用户才能与真实的人相关联。</string>
|
||||
<string name="info_logout">如果当前用户不是由OwnDroid切换的,无法使用此功能。</string>
|
||||
<string name="info_affiliation_id">当Device owner创建并管理用户时,新的用户不是附属用户。Device owner设置和受管理用户完全相同的附属用户ID后,受管理用户成为附属于Device owner的用户</string>
|
||||
|
||||
@@ -437,6 +437,8 @@
|
||||
<string name="silent_uninstall">Silent uninstall</string>
|
||||
<string name="request_uninstall">Request uninstall</string>
|
||||
<string name="install_app">Install app</string>
|
||||
<string name="choose_apk_file">Choose an APK file</string>
|
||||
<string name="install_existing_app">Install existing app</string>
|
||||
<string name="search">Search</string>
|
||||
|
||||
<!--UserRestriction-->
|
||||
@@ -724,6 +726,7 @@
|
||||
<string name="info_suspend_app">A suspended package will not be able to start activities. Its notifications will be hidden, it will not show up in recent activities, will not be able to show toasts or dialogs or ring the device.\nSome apps cannot be suspended, such as device admins, the active launcher and the default dialer.</string>
|
||||
<string name="info_disable_user_control">User will not be able to clear app data or force-stop packages.</string>
|
||||
<string name="info_keep_uninstalled_apps">Set a list of apps to keep around as APKs even if no user has currently installed it. </string>
|
||||
<string name="info_install_existing_app">Install an existing package that has been installed in another user, or has been kept after uninstall.</string>
|
||||
<string name="info_headless_system_user_mode">Headless system user mode means the system user runs system services and some system UI, but it is not associated with any real person and additional users must be created to be associated with real persons.</string>
|
||||
<string name="info_logout">If the current user is not switched by OwnDroid, this function cannot be used.</string>
|
||||
<string name="info_affiliation_id">When Device owner create a managed user, the managed user isn\'t affiliated. In order to make the managed user affiliated with the Device owner, you should set same affiliated IDs in main user and managed user</string>
|
||||
|
||||
Reference in New Issue
Block a user