mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 19:15:58 +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)
|
||||
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,22 +283,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 = {
|
||||
try {
|
||||
dpm.enableSystemApp(receiver, pkgName)
|
||||
context.showOperationResultToast(true)
|
||||
} catch(_: IllegalArgumentException) {
|
||||
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
dialogStatus = 0
|
||||
TextButton({
|
||||
try {
|
||||
dpm.enableSystemApp(receiver, pkgName)
|
||||
context.showOperationResultToast(true)
|
||||
} catch(_: IllegalArgumentException) {
|
||||
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
) {
|
||||
dialogStatus = 0
|
||||
}) {
|
||||
Text(stringResource(R.string.confirm))
|
||||
}
|
||||
},
|
||||
@@ -317,16 +311,15 @@ 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{ "" } +
|
||||
if(pkg != "") { "$pkg\n" } else { "" } +
|
||||
context.getString(R.string.clear_data) +
|
||||
context.getString(if(succeed) R.string.success else R.string.failed )
|
||||
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 = {
|
||||
try{
|
||||
dpm.setDefaultDialerApplication(pkgName)
|
||||
context.showOperationResultToast(true)
|
||||
} catch(_: IllegalArgumentException) {
|
||||
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
dialogStatus = 0
|
||||
TextButton({
|
||||
try {
|
||||
dpm.setDefaultDialerApplication(pkgName)
|
||||
context.showOperationResultToast(true)
|
||||
} catch(_: IllegalArgumentException) {
|
||||
context.showOperationResultToast(false)
|
||||
}
|
||||
) {
|
||||
dialogStatus = 0
|
||||
}) {
|
||||
Text(stringResource(R.string.confirm))
|
||||
}
|
||||
},
|
||||
@@ -379,18 +368,15 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { dialogStatus = 0 },
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(
|
||||
when(appControlAction) {
|
||||
1 -> R.string.suspend
|
||||
2 -> R.string.hide
|
||||
3 -> R.string.block_uninstall
|
||||
4 -> R.string.always_on_vpn
|
||||
else -> R.string.unknown
|
||||
}
|
||||
),
|
||||
style = typography.headlineMedium
|
||||
)
|
||||
Text(stringResource(
|
||||
when(appControlAction) {
|
||||
1 -> R.string.suspend
|
||||
2 -> R.string.hide
|
||||
3 -> R.string.block_uninstall
|
||||
4 -> R.string.always_on_vpn
|
||||
else -> R.string.unknown
|
||||
}
|
||||
))
|
||||
},
|
||||
text = {
|
||||
val enabled = when(appControlAction){
|
||||
@@ -406,27 +392,38 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
appControl(true)
|
||||
dialogStatus = 0
|
||||
}
|
||||
) {
|
||||
TextButton({
|
||||
appControl(true)
|
||||
dialogStatus = 0
|
||||
}) {
|
||||
Text(text = stringResource(R.string.enable))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
appControl(false)
|
||||
dialogStatus = 0
|
||||
}
|
||||
) {
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user