Fix crash caused by DelegatedScope

Use LargeTopAppBar in most screens
Optimize Package Chooser
Install existing app
This commit is contained in:
BinTianqi
2025-02-23 11:50:24 +08:00
parent fa81a2f30e
commit b734522171
15 changed files with 182 additions and 157 deletions

View File

@@ -1,6 +1,5 @@
package com.bintianqi.owndroid package com.bintianqi.owndroid
import android.app.Application
import android.content.Intent import android.content.Intent
import android.content.pm.ApplicationInfo import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager import android.content.pm.PackageManager
@@ -32,7 +31,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.LinearProgressIndicator 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.MaterialTheme.typography
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold 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.res.stringResource
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewModelScope import androidx.lifecycle.lifecycleScope
import com.bintianqi.owndroid.ui.theme.OwnDroidTheme import com.bintianqi.owndroid.ui.theme.OwnDroidTheme
import com.google.accompanist.drawablepainter.rememberDrawablePainter import com.google.accompanist.drawablepainter.rememberDrawablePainter
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -70,46 +68,41 @@ import kotlinx.coroutines.withContext
class PackageChooserActivity: ComponentActivity() { class PackageChooserActivity: ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val myVm by viewModels<MyViewModel>() val vm by viewModels<MyViewModel>()
val vm by viewModels<PackageChooserViewModel>() if(getPackagesProgress.value < 1F) getPackages()
vm.initialize()
setContent { setContent {
val theme by myVm.theme.collectAsStateWithLifecycle() val theme by vm.theme.collectAsStateWithLifecycle()
OwnDroidTheme(theme) { OwnDroidTheme(theme) {
val packages by vm.packages.collectAsStateWithLifecycle() val packages by installedPackages.collectAsStateWithLifecycle()
val progress by vm.progress.collectAsStateWithLifecycle() val progress by getPackagesProgress.collectAsStateWithLifecycle()
PackageChooserScreen(packages, progress, vm::getPackages) { PackageChooserScreen(packages, progress, ::getPackages) {
setResult(0, Intent().putExtra("package", it)) setResult(0, Intent().putExtra("package", it))
finish() 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 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() { fun getPackages() {
packages.value = emptyList() installedPackages.value = emptyList()
viewModelScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
val pm = getApplication<Application>().packageManager val pm = packageManager
val apps = pm.getInstalledApplications(flags) val apps = pm.getInstalledApplications(flags)
for(pkg in apps) { for(pkg in apps) {
packages.update { installedPackages.update {
it + PackageInfo( it + PackageInfo(
pkg.packageName, pkg.loadLabel(pm).toString(), pkg.loadIcon(pm), pkg.packageName, pkg.loadLabel(pm).toString(), pkg.loadIcon(pm),
(pkg.flags and ApplicationInfo.FLAG_SYSTEM) != 0 (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( data class PackageInfo(
@@ -186,7 +179,7 @@ private fun PackageChooserScreen(
Icon(Icons.AutoMirrored.Default.ArrowBack, null) Icon(Icons.AutoMirrored.Default.ArrowBack, null)
} }
}, },
colors = TopAppBarDefaults.topAppBarColors(containerColor = colorScheme.background) colors = TopAppBarDefaults.topAppBarColors(MaterialTheme.colorScheme.surfaceContainer)
) )
} }
) { paddingValues-> ) { paddingValues->

View File

@@ -10,7 +10,6 @@ import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Process
import android.widget.Toast import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.result.contract.ActivityResultContract import androidx.activity.result.contract.ActivityResultContract
@@ -97,6 +96,9 @@ fun parseDate(date: Date)
val Long.humanReadableDate: String val Long.humanReadableDate: String
get() = SimpleDateFormat("yyyy/MM/dd", Locale.getDefault()).format(Date(this)) 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) { fun Context.showOperationResultToast(success: Boolean) {
Toast.makeText(this, if(success) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show() Toast.makeText(this, if(success) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
} }

View File

@@ -2,7 +2,6 @@ package com.bintianqi.owndroid.dpm
import android.app.AlertDialog import android.app.AlertDialog
import android.app.PendingIntent 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_DEFAULT
import android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED import android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED
import android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED import android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED
@@ -15,7 +14,6 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.content.pm.PackageInstaller import android.content.pm.PackageInstaller
import android.content.pm.PackageManager.NameNotFoundException
import android.net.Uri import android.net.Uri
import android.os.Build.VERSION import android.os.Build.VERSION
import android.os.Looper import android.os.Looper
@@ -50,11 +48,11 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton 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.OutlinedTextField
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Switch import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.material3.TextField
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -88,8 +86,8 @@ import androidx.navigation.compose.rememberNavController
import com.bintianqi.owndroid.APK_MIME import com.bintianqi.owndroid.APK_MIME
import com.bintianqi.owndroid.AppInstallerActivity import com.bintianqi.owndroid.AppInstallerActivity
import com.bintianqi.owndroid.AppInstallerViewModel import com.bintianqi.owndroid.AppInstallerViewModel
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.ChoosePackageContract import com.bintianqi.owndroid.ChoosePackageContract
import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.showOperationResultToast import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.Animations
import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.FunctionItem
@@ -117,13 +115,13 @@ fun ApplicationsScreen(onNavigateUp: () -> Unit) {
topBar = { topBar = {
TopAppBar( TopAppBar(
title = { title = {
TextField( OutlinedTextField(
value = pkgName, value = pkgName,
onValueChange = { pkgName = it }, onValueChange = { pkgName = it },
label = { Text(stringResource(R.string.package_name)) }, label = { Text(stringResource(R.string.package_name)) },
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), keyboardActions = KeyboardActions { focusMgr.clearFocus() },
trailingIcon = { trailingIcon = {
IconButton({ IconButton({
focusMgr.clearFocus() focusMgr.clearFocus()
@@ -137,7 +135,7 @@ fun ApplicationsScreen(onNavigateUp: () -> Unit) {
) )
}, },
navigationIcon = { NavIcon(onNavigateUp) }, navigationIcon = { NavIcon(onNavigateUp) },
colors = TopAppBarDefaults.topAppBarColors(containerColor = colorScheme.background) colors = TopAppBarDefaults.topAppBarColors(colorScheme.surfaceContainer)
) )
} }
) { paddingValues-> ) { paddingValues->
@@ -167,6 +165,7 @@ fun ApplicationsScreen(onNavigateUp: () -> Unit) {
@Composable @Composable
private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) { 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) } var dialogStatus by remember { mutableIntStateOf(0) }
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
@@ -176,31 +175,26 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
var suspend by remember { mutableStateOf(false) } var suspend by remember { mutableStateOf(false) }
var hide by remember { mutableStateOf(false) } var hide by remember { mutableStateOf(false) }
var blockUninstall 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 focusMgr = LocalFocusManager.current
val appControl: (Boolean) -> Unit = { fun refresh() {
when(appControlAction) { if(VERSION.SDK_INT >= 24) {
1 -> if(VERSION.SDK_INT >= 24) dpm.setPackagesSuspended(receiver, arrayOf(pkgName), it) try {
2 -> dpm.setApplicationHidden(receiver, pkgName, it) suspend = dpm.isPackageSuspended(receiver, pkgName)
3 -> dpm.setUninstallBlocked(receiver, pkgName, it) } 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) hide = dpm.isApplicationHidden(receiver, pkgName)
blockUninstall = dpm.isUninstallBlocked(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( Column(
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()) modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())
) { ) {
@@ -222,7 +216,7 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
) )
} }
SwitchItem( 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, state = hide,
onCheckedChange = { appControlAction = 2; appControl(it) }, onCheckedChange = { appControlAction = 2; appControl(it) },
onClickBlank = { appControlAction = 2; dialogStatus = 4 } onClickBlank = { appControlAction = 2; dialogStatus = 4 }
@@ -270,8 +264,10 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
startActivity(context, intent, null) startActivity(context, intent, null)
} }
FunctionItem(title = R.string.install_app, icon = R.drawable.install_mobile_fill0) { 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) 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) } FunctionItem(title = R.string.uninstall_app, icon = R.drawable.delete_fill0) { onNavigate(UninstallPackage) }
if(VERSION.SDK_INT >= 34 && (deviceOwner || dpm.isOrgProfile(receiver))) { if(VERSION.SDK_INT >= 34 && (deviceOwner || dpm.isOrgProfile(receiver))) {
FunctionItem(title = R.string.set_default_dialer, icon = R.drawable.call_fill0) { FunctionItem(title = R.string.set_default_dialer, icon = R.drawable.call_fill0) {
@@ -287,22 +283,20 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
}, },
onDismissRequest = { dialogStatus = 0 }, onDismissRequest = { dialogStatus = 0 },
dismissButton = { dismissButton = {
TextButton(onClick = { dialogStatus = 0 }) { TextButton({ dialogStatus = 0 }) {
Text(stringResource(R.string.cancel)) Text(stringResource(R.string.cancel))
} }
}, },
confirmButton = { confirmButton = {
TextButton( TextButton({
onClick = { try {
try { dpm.enableSystemApp(receiver, pkgName)
dpm.enableSystemApp(receiver, pkgName) context.showOperationResultToast(true)
context.showOperationResultToast(true) } catch(_: IllegalArgumentException) {
} catch(_: IllegalArgumentException) { Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
}
dialogStatus = 0
} }
) { dialogStatus = 0
}) {
Text(stringResource(R.string.confirm)) Text(stringResource(R.string.confirm))
} }
}, },
@@ -317,16 +311,15 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
TextButton( TextButton(
onClick = { onClick = {
val executor = Executors.newCachedThreadPool() val executor = Executors.newCachedThreadPool()
val onClear = DevicePolicyManager.OnClearApplicationUserDataListener { pkg: String, succeed: Boolean -> dpm.clearApplicationUserData(receiver, pkgName, executor) { pkg: String, succeed: Boolean ->
Looper.prepare() Looper.prepare()
val toastText = val toastText =
if(pkg!="") { "$pkg\n" }else{ "" } + if(pkg != "") { "$pkg\n" } else { "" } +
context.getString(R.string.clear_data) + context.getString(R.string.clear_data) +
context.getString(if(succeed) R.string.success else R.string.failed ) context.getString(if(succeed) R.string.success else R.string.failed )
Toast.makeText(context, toastText, Toast.LENGTH_SHORT).show() Toast.makeText(context, toastText, Toast.LENGTH_SHORT).show()
Looper.loop() Looper.loop()
} }
dpm.clearApplicationUserData(receiver, pkgName, executor, onClear)
dialogStatus = 0 dialogStatus = 0
}, },
colors = ButtonDefaults.textButtonColors(contentColor = colorScheme.error) colors = ButtonDefaults.textButtonColors(contentColor = colorScheme.error)
@@ -335,9 +328,7 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
} }
}, },
dismissButton = { dismissButton = {
TextButton( TextButton({ dialogStatus = 0 }) {
onClick = { dialogStatus = 0 }
) {
Text(text = stringResource(R.string.cancel)) Text(text = stringResource(R.string.cancel))
} }
}, },
@@ -351,22 +342,20 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
}, },
onDismissRequest = { dialogStatus = 0 }, onDismissRequest = { dialogStatus = 0 },
dismissButton = { dismissButton = {
TextButton(onClick = { dialogStatus = 0 }) { TextButton({ dialogStatus = 0 }) {
Text(stringResource(R.string.cancel)) Text(stringResource(R.string.cancel))
} }
}, },
confirmButton = { confirmButton = {
TextButton( TextButton({
onClick = { try {
try{ dpm.setDefaultDialerApplication(pkgName)
dpm.setDefaultDialerApplication(pkgName) context.showOperationResultToast(true)
context.showOperationResultToast(true) } catch(_: IllegalArgumentException) {
} catch(_: IllegalArgumentException) { context.showOperationResultToast(false)
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
}
dialogStatus = 0
} }
) { dialogStatus = 0
}) {
Text(stringResource(R.string.confirm)) Text(stringResource(R.string.confirm))
} }
}, },
@@ -379,18 +368,15 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
AlertDialog( AlertDialog(
onDismissRequest = { dialogStatus = 0 }, onDismissRequest = { dialogStatus = 0 },
title = { title = {
Text( Text(stringResource(
text = stringResource( when(appControlAction) {
when(appControlAction) { 1 -> R.string.suspend
1 -> R.string.suspend 2 -> R.string.hide
2 -> R.string.hide 3 -> R.string.block_uninstall
3 -> R.string.block_uninstall 4 -> R.string.always_on_vpn
4 -> R.string.always_on_vpn else -> R.string.unknown
else -> R.string.unknown }
} ))
),
style = typography.headlineMedium
)
}, },
text = { text = {
val enabled = when(appControlAction){ val enabled = when(appControlAction){
@@ -406,27 +392,38 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
} }
}, },
confirmButton = { confirmButton = {
TextButton( TextButton({
onClick = { appControl(true)
appControl(true) dialogStatus = 0
dialogStatus = 0 }) {
}
) {
Text(text = stringResource(R.string.enable)) Text(text = stringResource(R.string.enable))
} }
}, },
dismissButton = { dismissButton = {
TextButton( TextButton({
onClick = { appControl(false)
appControl(false) dialogStatus = 0
dialogStatus = 0 }) {
}
) {
Text(text = stringResource(R.string.disable)) 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() } LaunchedEffect(dialogStatus) { focusMgr.clearFocus() }
} }

View File

@@ -98,6 +98,7 @@ import androidx.compose.material3.TabRow
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.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberDatePickerState import androidx.compose.material3.rememberDatePickerState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@@ -129,6 +130,7 @@ import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.SharedPrefs import com.bintianqi.owndroid.SharedPrefs
import com.bintianqi.owndroid.formatFileSize import com.bintianqi.owndroid.formatFileSize
import com.bintianqi.owndroid.humanReadableDate import com.bintianqi.owndroid.humanReadableDate
import com.bintianqi.owndroid.humanReadableDateTime
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.ExpandExposedTextFieldIcon 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.InfoCard
import com.bintianqi.owndroid.ui.ListItem import com.bintianqi.owndroid.ui.ListItem
import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.MySmallTitleScaffold
import com.bintianqi.owndroid.ui.NavIcon import com.bintianqi.owndroid.ui.NavIcon
import com.bintianqi.owndroid.ui.RadioButtonItem import com.bintianqi.owndroid.ui.RadioButtonItem
import com.bintianqi.owndroid.ui.SwitchItem import com.bintianqi.owndroid.ui.SwitchItem
@@ -231,7 +234,8 @@ fun WifiScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit, onNavigateTo
topBar = { topBar = {
TopAppBar( TopAppBar(
title = { Text(stringResource(R.string.wifi)) }, title = { Text(stringResource(R.string.wifi)) },
navigationIcon = { NavIcon(onNavigateUp) } navigationIcon = { NavIcon(onNavigateUp) },
colors = TopAppBarDefaults.topAppBarColors(MaterialTheme.colorScheme.surfaceContainer)
) )
} }
) { paddingValues -> ) { paddingValues ->
@@ -466,7 +470,7 @@ object AddNetwork
@Composable @Composable
fun AddNetworkScreen(data: Bundle, onNavigateUp: () -> Unit) { 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) AddNetworkScreen(data.getParcelable("wifi_configuration"), onNavigateUp)
} }
} }
@@ -1213,8 +1217,6 @@ fun NetworkStatsScreen(onNavigateUp: () -> Unit, onNavigateToViewer: (NetworkSta
context.showOperationResultToast(false) context.showOperationResultToast(false)
} }
} else { } else {
val bundle = Bundle()
bundle.putInt("size", buckets.size)
val stats = buckets.map { val stats = buckets.map {
NetworkStatsViewer.Data( NetworkStatsViewer.Data(
it.rxBytes, it.rxPackets, it.txBytes, it.txPackets, it.rxBytes, it.rxPackets, it.txBytes, it.txPackets,
@@ -1289,7 +1291,7 @@ data class NetworkStatsViewer(
fun NetworkStatsViewerScreen(nsv: NetworkStatsViewer, onNavigateUp: () -> Unit) { fun NetworkStatsViewerScreen(nsv: NetworkStatsViewer, onNavigateUp: () -> Unit) {
var index by remember { mutableIntStateOf(0) } var index by remember { mutableIntStateOf(0) }
val size = nsv.stats.size 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( if(size > 1) Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.align(Alignment.CenterHorizontally).padding(bottom = 8.dp) modifier = Modifier.align(Alignment.CenterHorizontally).padding(bottom = 8.dp)
@@ -1310,7 +1312,7 @@ fun NetworkStatsViewerScreen(nsv: NetworkStatsViewer, onNavigateUp: () -> Unit)
} }
val data = nsv.stats[index] val data = nsv.stats[index]
Text( Text(
data.startTime.humanReadableDate + " ~ " + data.endTime.humanReadableDate, data.startTime.humanReadableDateTime + " ~ " + data.endTime.humanReadableDateTime,
modifier = Modifier.align(Alignment.CenterHorizontally).padding(bottom = 8.dp) modifier = Modifier.align(Alignment.CenterHorizontally).padding(bottom = 8.dp)
) )
val txBytes = data.txBytes val txBytes = data.txBytes
@@ -1556,10 +1558,11 @@ fun RecommendedGlobalProxyScreen(onNavigateUp: () -> Unit) {
label = { Text(stringResource(R.string.excluded_hosts)) }, label = { Text(stringResource(R.string.excluded_hosts)) },
maxLines = 5, maxLines = 5,
minLines = 2, minLines = 2,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions { focusMgr.clearFocus() },
modifier = Modifier.fillMaxWidth().padding(vertical = 2.dp) modifier = Modifier.fillMaxWidth().padding(vertical = 2.dp)
) )
} }
Spacer(Modifier.padding(vertical = 4.dp))
Button( Button(
onClick = { onClick = {
if(proxyType == 0) { if(proxyType == 0) {
@@ -1597,7 +1600,7 @@ fun RecommendedGlobalProxyScreen(onNavigateUp: () -> Unit) {
dpm.setRecommendedGlobalProxy(receiver, proxyInfo) dpm.setRecommendedGlobalProxy(receiver, proxyInfo)
context.showOperationResultToast(true) context.showOperationResultToast(true)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp)
) { ) {
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))
} }
@@ -1716,7 +1719,7 @@ fun PreferentialNetworkServiceScreen(onNavigateUp: () -> Unit, onNavigate: (AddP
configs.addAll(dpm.preferentialNetworkServiceConfigs) configs.addAll(dpm.preferentialNetworkServiceConfigs)
} }
LaunchedEffect(Unit) { refresh() } 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 = { SwitchItem(R.string.enabled, state = masterEnabled, onCheckedChange = {
dpm.isPreferentialNetworkServiceEnabled = it dpm.isPreferentialNetworkServiceEnabled = it
refresh() refresh()
@@ -1780,7 +1783,7 @@ fun AddPreferentialNetworkServiceConfigScreen(route: AddPreferentialNetworkServi
var blockNonMatching by remember { mutableStateOf(route.blockNonMatching) } var blockNonMatching by remember { mutableStateOf(route.blockNonMatching) }
var excludedUids by remember { mutableStateOf(route.excludedUids.joinToString("\n")) } var excludedUids by remember { mutableStateOf(route.excludedUids.joinToString("\n")) }
var includedUids by remember { mutableStateOf(route.includedUids.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) SwitchItem(title = R.string.enabled, state = enabled, onCheckedChange = { enabled = it }, padding = false)
AnimatedVisibility(enabled) { AnimatedVisibility(enabled) {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
@@ -1882,7 +1885,7 @@ fun OverrideApnScreen(onNavigateUp: () -> Unit, onNavigateToAddSetting: (Bundle)
settings.addAll(dpm.getOverrideApns(receiver)) settings.addAll(dpm.getOverrideApns(receiver))
} }
LaunchedEffect(Unit) { refresh() } LaunchedEffect(Unit) { refresh() }
MyScaffold(R.string.override_apn, 0.dp, onNavigateUp, false) { MyScaffold(R.string.override_apn, 0.dp, onNavigateUp) {
SwitchItem( SwitchItem(
R.string.enable, state = enabled, R.string.enable, state = enabled,
onCheckedChange = { 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 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 alwaysOn by remember { mutableStateOf(VERSION.SDK_INT >= 35 && origin?.isAlwaysOn == true) }
var errorMessage: String? by remember { mutableStateOf(null) } 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( val protocolMap = mapOf(
ApnSetting.PROTOCOL_IP to "IPv4", ApnSetting.PROTOCOL_IPV6 to "IPv6", ApnSetting.PROTOCOL_IP to "IPv4", ApnSetting.PROTOCOL_IPV6 to "IPv6",
ApnSetting.PROTOCOL_IPV4V6 to "IPv4/v6", ApnSetting.PROTOCOL_PPP to "PPP" ApnSetting.PROTOCOL_IPV4V6 to "IPv4/v6", ApnSetting.PROTOCOL_PPP to "PPP"

View File

@@ -15,6 +15,7 @@ import android.os.RemoteException
import android.os.UserManager import android.os.UserManager
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.annotation.Keep
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
@@ -495,6 +496,7 @@ fun DeviceOwnerScreen(onNavigateUp: () -> Unit) {
} }
} }
@Keep
@Suppress("InlinedApi") @Suppress("InlinedApi")
enum class DelegatedScope(val id: String, @StringRes val string: Int, val requiresApi: Int = 0) { enum class DelegatedScope(val id: String, @StringRes val string: Int, val requiresApi: Int = 0) {
AppRestrictions(DevicePolicyManager.DELEGATION_APP_RESTRICTIONS, R.string.manage_application_restrictions), 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 -> val choosePackage = rememberLauncherForActivityResult(ChoosePackageContract()) { result ->
result?.let { input = it } 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( OutlinedTextField(
value = input, onValueChange = { input = it }, value = input, onValueChange = { input = it },
label = { Text(stringResource(R.string.package_name)) }, label = { Text(stringResource(R.string.package_name)) },

View File

@@ -38,7 +38,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.bintianqi.owndroid.IUserService import com.bintianqi.owndroid.IUserService
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.MySmallTitleScaffold
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@@ -66,7 +66,7 @@ fun ShizukuScreen(navArgs: Bundle, onNavigateUp: () -> Unit, onNavigateToAccount
null null
} }
} }
MyScaffold(R.string.shizuku, 0.dp, onNavigateUp, false) { MySmallTitleScaffold(R.string.shizuku, 0.dp, onNavigateUp) {
Button( Button(
onClick = { onClick = {
@@ -185,7 +185,7 @@ data class Accounts(
@Composable @Composable
fun AccountsScreen(accounts: Accounts, onNavigateUp: () -> Unit) { fun AccountsScreen(accounts: Accounts, onNavigateUp: () -> Unit) {
MyScaffold(R.string.accounts, 8.dp, onNavigateUp, false) { MySmallTitleScaffold(R.string.accounts, 8.dp, onNavigateUp) {
accounts.list.forEach { accounts.list.forEach {
Column( Column(
modifier = Modifier modifier = Modifier

View File

@@ -99,6 +99,7 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.material3.TimePicker import androidx.compose.material3.TimePicker
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberDatePickerState import androidx.compose.material3.rememberDatePickerState
import androidx.compose.material3.rememberTimePickerState import androidx.compose.material3.rememberTimePickerState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -138,6 +139,7 @@ import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.InfoCard
import com.bintianqi.owndroid.ui.ListItem import com.bintianqi.owndroid.ui.ListItem
import com.bintianqi.owndroid.ui.MyScaffold import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.MySmallTitleScaffold
import com.bintianqi.owndroid.ui.NavIcon import com.bintianqi.owndroid.ui.NavIcon
import com.bintianqi.owndroid.ui.RadioButtonItem import com.bintianqi.owndroid.ui.RadioButtonItem
import com.bintianqi.owndroid.ui.SwitchItem import com.bintianqi.owndroid.ui.SwitchItem
@@ -431,7 +433,7 @@ fun HardwareMonitorScreen(onNavigateUp: () -> Unit) {
delay(refreshIntervalMs) 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)) 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) Slider(refreshInterval, { refreshInterval = it }, valueRange = 0.5F..2F, steps = 14)
Text("${refreshIntervalMs}ms") Text("${refreshIntervalMs}ms")
@@ -943,7 +945,7 @@ fun NearbyStreamingPolicyScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
var appPolicy by remember { mutableIntStateOf(dpm.nearbyAppStreamingPolicy) } 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( Text(
stringResource(R.string.nearby_app_streaming), stringResource(R.string.nearby_app_streaming),
Modifier.padding(start = 8.dp, top = 10.dp, bottom = 4.dp), style = typography.titleLarge Modifier.padding(start = 8.dp, top = 10.dp, bottom = 4.dp), style = typography.titleLarge
@@ -1018,7 +1020,8 @@ fun LockTaskModeScreen(onNavigateUp: () -> Unit) {
topBar = { topBar = {
TopAppBar( TopAppBar(
title = { Text(stringResource(R.string.lock_task_mode)) }, title = { Text(stringResource(R.string.lock_task_mode)) },
navigationIcon = { NavIcon(onNavigateUp) } navigationIcon = { NavIcon(onNavigateUp) },
colors = TopAppBarDefaults.topAppBarColors(colorScheme.surfaceContainer)
) )
} }
) { paddingValues -> ) { paddingValues ->
@@ -1108,7 +1111,8 @@ private fun ColumnScope.StartLockTaskMode() {
} else { } else {
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
} }
} },
enabled = startLockTaskApp.isNotBlank() && (!specifyActivity || startLockTaskActivity.isNotBlank())
) { ) {
Text(stringResource(R.string.start)) Text(stringResource(R.string.start))
} }

View File

@@ -75,7 +75,7 @@ fun UserRestrictionOptionsScreen(
data: UserRestrictionOptions, restrictions: Bundle, data: UserRestrictionOptions, restrictions: Bundle,
onNavigateUp: () -> Unit, onRestrictionChange: (String, Boolean) -> Unit 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 -> data.items.filter { Build.VERSION.SDK_INT >= it.requiresApi }.forEach { restriction ->
SwitchItem( SwitchItem(
restriction.name, restriction.id, restriction.icon, restriction.name, restriction.id, restriction.icon,

View File

@@ -171,7 +171,7 @@ fun CreateWorkProfileScreen(onNavigateUp: () -> Unit) {
fun OrganizationOwnedProfileScreen(onNavigateUp: () -> Unit) { fun OrganizationOwnedProfileScreen(onNavigateUp: () -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() 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) CardItem(R.string.org_owned_work_profile, dpm.isOrganizationOwnedDeviceWithManagedProfile.yesOrNo)
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
if(!dpm.isOrganizationOwnedDeviceWithManagedProfile) { if(!dpm.isOrganizationOwnedDeviceWithManagedProfile) {

View File

@@ -23,9 +23,9 @@ import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.runtime.* import androidx.compose.runtime.*
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.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate import androidx.compose.ui.draw.rotate
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@@ -292,20 +292,16 @@ fun MyScaffold(
@StringRes title: Int, @StringRes title: Int,
horizonPadding: Dp, horizonPadding: Dp,
onNavIconClicked: () -> Unit, onNavIconClicked: () -> Unit,
displayTitle: Boolean = true,
content: @Composable ColumnScope.() -> Unit content: @Composable ColumnScope.() -> Unit
) { ) {
val scrollState = rememberScrollState() val sb = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
Scaffold( Scaffold(
Modifier.nestedScroll(sb.nestedScrollConnection),
topBar = { topBar = {
TopAppBar( LargeTopAppBar(
title = { { Text(stringResource(title)) },
Text( navigationIcon = { NavIcon(onNavIconClicked) },
text = stringResource(title), scrollBehavior = sb
modifier = if(displayTitle) Modifier.alpha((maxOf(scrollState.value-90,0)).toFloat()/50) else Modifier
)
},
navigationIcon = { NavIcon (onNavIconClicked) }
) )
} }
) { paddingValues -> ) { paddingValues ->
@@ -314,14 +310,39 @@ fun MyScaffold(
.fillMaxSize() .fillMaxSize()
.padding(paddingValues) .padding(paddingValues)
.padding(horizontal = horizonPadding) .padding(horizontal = horizonPadding)
.verticalScroll(scrollState) .verticalScroll(rememberScrollState())
.padding(bottom = 80.dp) .padding(bottom = 80.dp)
) { ) {
if(displayTitle) Text( content()
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) }
@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() content()
} }
} }

View File

@@ -6,19 +6,11 @@ import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.ui.graphics.Color 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.LocalContext
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.MyViewModel
import com.bintianqi.owndroid.ThemeSettings import com.bintianqi.owndroid.ThemeSettings
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
private val lightScheme = lightColorScheme( private val lightScheme = lightColorScheme(
primary = primaryLight, primary = primaryLight,
@@ -111,8 +103,6 @@ fun OwnDroidTheme(
else -> lightScheme else -> lightScheme
}.let { }.let {
if(darkTheme && theme.blackTheme) it.copy(background = Color.Black) else it 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 val view = LocalView.current
SideEffect { SideEffect {

View File

@@ -410,6 +410,9 @@
<string name="silent_uninstall">Тихое удаление</string> <string name="silent_uninstall">Тихое удаление</string>
<string name="request_uninstall">Запросить удаление</string> <string name="request_uninstall">Запросить удаление</string>
<string name="install_app">Установить приложение</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> <string name="search">Поиск</string>
<!--Ограничения пользователя--> <!--Ограничения пользователя-->
@@ -701,6 +704,7 @@
<string name="info_suspend_app">Приостановленный пакет не сможет запускать активности. Его уведомления будут скрыты, он не будет отображаться в списке последних запущенных приложений, не сможет показывать всплывающие уведомления или диалоговые окна и звонить на устройство.\nНекоторые приложения не могут быть приостановлены, такие как администраторы устройства, активный лаунчер и приложение для набора номера по умолчанию.</string> <string name="info_suspend_app">Приостановленный пакет не сможет запускать активности. Его уведомления будут скрыты, он не будет отображаться в списке последних запущенных приложений, не сможет показывать всплывающие уведомления или диалоговые окна и звонить на устройство.\nНекоторые приложения не могут быть приостановлены, такие как администраторы устройства, активный лаунчер и приложение для набора номера по умолчанию.</string>
<string name="info_disable_user_control">Пользователь не сможет очищать данные приложений или принудительно останавливать пакеты.</string> <string name="info_disable_user_control">Пользователь не сможет очищать данные приложений или принудительно останавливать пакеты.</string>
<string name="info_keep_uninstalled_apps">Установить список приложений, которые нужно сохранить в виде APK-файлов, даже если ни у одного пользователя в данный момент они не установлены.</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_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_logout">If the current user is not switched by OwnDroid, this function cannot be used.</string> <!--TODO-->
<string name="info_affiliation_id">Когда владелец устройства создает управляемого пользователя, управляемый пользователь не является аффилированным. Чтобы сделать управляемого пользователя аффилированным с владельцем устройства, вам следует установить одинаковые аффилированные идентификаторы в основном и управляемом пользователях.</string> <string name="info_affiliation_id">Когда владелец устройства создает управляемого пользователя, управляемый пользователь не является аффилированным. Чтобы сделать управляемого пользователя аффилированным с владельцем устройства, вам следует установить одинаковые аффилированные идентификаторы в основном и управляемом пользователях.</string>

View File

@@ -416,7 +416,10 @@
<string name="silent_uninstall">Sessiz kaldırma</string> <string name="silent_uninstall">Sessiz kaldırma</string>
<string name="request_uninstall">Kaldırma isteği</string> <string name="request_uninstall">Kaldırma isteği</string>
<string name="install_app">Uygulamayı yükle</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--> <!--UserRestriction-->
<string name="user_restriction">Kullanıcı kısıtlaması</string> <string name="user_restriction">Kullanıcı kısıtlaması</string>

View File

@@ -372,7 +372,7 @@
<string name="scope_is_work_profile">作用域: 工作资料</string> <string name="scope_is_work_profile">作用域: 工作资料</string>
<string name="app_info">应用详情</string> <string name="app_info">应用详情</string>
<string name="not_installed">未安装</string> <string name="not_installed">未安装</string>
<string name="block_uninstall">阻止</string> <string name="block_uninstall">阻止卸载</string>
<string name="ucd">禁止用户控制</string> <string name="ucd">禁止用户控制</string>
<string name="ucd_desc">用户将无法清除这些应用的存储空间或强制停止这些应用</string> <string name="ucd_desc">用户将无法清除这些应用的存储空间或强制停止这些应用</string>
<string name="app_list_is">应用列表:</string> <string name="app_list_is">应用列表:</string>
@@ -398,6 +398,8 @@
<string name="silent_uninstall">静默卸载</string> <string name="silent_uninstall">静默卸载</string>
<string name="request_uninstall">请求卸载</string> <string name="request_uninstall">请求卸载</string>
<string name="install_app">安装应用</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">启用系统应用</string>
<string name="enable_system_app_desc">重新启用一个默认被禁用的系统应用</string> <string name="enable_system_app_desc">重新启用一个默认被禁用的系统应用</string>
<string name="search">搜索</string> <string name="search">搜索</string>
@@ -684,6 +686,7 @@
<string name="info_suspend_app">挂起的应用无法被打开通知会被隐藏不会在最近任务中显示不能弹窗不能发送Toast。\n有些应用无法被挂起比如Device admin、启动器和默认拨号应用。</string> <string name="info_suspend_app">挂起的应用无法被打开通知会被隐藏不会在最近任务中显示不能弹窗不能发送Toast。\n有些应用无法被挂起比如Device admin、启动器和默认拨号应用。</string>
<string name="info_disable_user_control">用户无法清除这些应用的存储空间,也无法强制停止应用</string> <string name="info_disable_user_control">用户无法清除这些应用的存储空间,也无法强制停止应用</string>
<string name="info_keep_uninstalled_apps">这个列表中的应用的APK将会一直保留即使没有任何用户安装这个应用</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_headless_system_user_mode">无头系统用户模式意味着系统用户运行系统服务和一些系统UI但它不与任何真实的人相关联必须创建额外的用户才能与真实的人相关联。</string>
<string name="info_logout">如果当前用户不是由OwnDroid切换的无法使用此功能。</string> <string name="info_logout">如果当前用户不是由OwnDroid切换的无法使用此功能。</string>
<string name="info_affiliation_id">当Device owner创建并管理用户时新的用户不是附属用户。Device owner设置和受管理用户完全相同的附属用户ID后受管理用户成为附属于Device owner的用户</string> <string name="info_affiliation_id">当Device owner创建并管理用户时新的用户不是附属用户。Device owner设置和受管理用户完全相同的附属用户ID后受管理用户成为附属于Device owner的用户</string>

View File

@@ -437,6 +437,8 @@
<string name="silent_uninstall">Silent uninstall</string> <string name="silent_uninstall">Silent uninstall</string>
<string name="request_uninstall">Request uninstall</string> <string name="request_uninstall">Request uninstall</string>
<string name="install_app">Install app</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> <string name="search">Search</string>
<!--UserRestriction--> <!--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_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_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_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_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_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> <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>