mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 11:05:59 +00:00
Improve UI
This commit is contained in:
@@ -38,10 +38,6 @@ android {
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
signingConfig = signingConfigs.getByName("defaultSignature")
|
||||
composeCompiler {
|
||||
includeSourceInformation = false
|
||||
includeTraceMarkers = false
|
||||
}
|
||||
}
|
||||
debug {
|
||||
signingConfig = signingConfigs.getByName("defaultSignature")
|
||||
@@ -64,6 +60,10 @@ android {
|
||||
dependenciesInfo {
|
||||
includeInApk = false
|
||||
}
|
||||
composeCompiler {
|
||||
includeSourceInformation = false
|
||||
includeTraceMarkers = false
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.bintianqi.owndroid
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Build.VERSION
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
@@ -237,13 +236,11 @@ import com.bintianqi.owndroid.ui.Animations
|
||||
import com.bintianqi.owndroid.ui.theme.OwnDroidTheme
|
||||
import com.rosan.dhizuku.api.Dhizuku
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.lsposed.hiddenapibypass.HiddenApiBypass
|
||||
import java.util.Locale
|
||||
|
||||
val backToHomeStateFlow = MutableStateFlow(false)
|
||||
@ExperimentalMaterial3Api
|
||||
class MainActivity : FragmentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -291,12 +288,7 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val receiver = context.getReceiver()
|
||||
val focusMgr = LocalFocusManager.current
|
||||
val backToHome by backToHomeStateFlow.collectAsState()
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
LaunchedEffect(backToHome) {
|
||||
if(backToHome) { navController.navigateUp(); backToHomeStateFlow.value = false }
|
||||
}
|
||||
val userRestrictions by vm.userRestrictions.collectAsStateWithLifecycle()
|
||||
fun navigateUp() { navController.navigateUp() }
|
||||
fun navigate(destination: Any) { navController.navigate(destination) }
|
||||
LaunchedEffect(Unit) {
|
||||
@@ -320,7 +312,7 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
|
||||
popEnterTransition = Animations.navHostPopEnterTransition,
|
||||
popExitTransition = Animations.navHostPopExitTransition
|
||||
) {
|
||||
composable<Home> { HomeScreen { navController.navigate(it) } }
|
||||
composable<Home> { HomeScreen(::navigate) }
|
||||
composable<WorkModes> {
|
||||
WorkModesScreen(it.toRoute(), ::navigateUp, {
|
||||
navController.navigate(Home) {
|
||||
@@ -330,9 +322,7 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
|
||||
navController.navigate(WorkModes(false)) {
|
||||
popUpTo<Home> { inclusive = true }
|
||||
}
|
||||
}, {
|
||||
navController.navigate(it)
|
||||
})
|
||||
}, ::navigate)
|
||||
}
|
||||
|
||||
composable<DelegatedAdmins> { DelegatedAdminsScreen(::navigateUp, ::navigate) }
|
||||
@@ -369,14 +359,14 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
|
||||
composable<WipeData> { WipeDataScreen(::navigateUp) }
|
||||
|
||||
composable<Network> { NetworkScreen(::navigateUp, ::navigate) }
|
||||
composable<WiFi> { WifiScreen(::navigateUp, { navController.navigate(it) }) { navController.navigate(AddNetwork, it)} }
|
||||
composable<WiFi> { WifiScreen(::navigateUp, ::navigate) { navController.navigate(AddNetwork, it)} }
|
||||
composable<NetworkOptions> { NetworkOptionsScreen(::navigateUp) }
|
||||
composable<AddNetwork> { AddNetworkScreen(it.arguments!!, ::navigateUp) }
|
||||
composable<WifiSecurityLevel> { WifiSecurityLevelScreen(::navigateUp) }
|
||||
composable<WifiSsidPolicyScreen> { WifiSsidPolicyScreen(::navigateUp) }
|
||||
composable<QueryNetworkStats> { NetworkStatsScreen(::navigateUp, ::navigate) }
|
||||
composable<NetworkStatsViewer>(mapOf(serializableNavTypePair<List<NetworkStatsViewer.Data>>())) {
|
||||
NetworkStatsViewerScreen(it.toRoute()) { navController.navigateUp() }
|
||||
NetworkStatsViewerScreen(it.toRoute(), ::navigateUp)
|
||||
}
|
||||
composable<PrivateDns> { PrivateDnsScreen(::navigateUp) }
|
||||
composable<AlwaysOnVpnPackage> { AlwaysOnVpnPackageScreen(::navigateUp) }
|
||||
@@ -433,25 +423,12 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
|
||||
composable<SetDefaultDialer> { SetDefaultDialerScreen(::navigateUp) }
|
||||
|
||||
composable<UserRestriction> {
|
||||
LaunchedEffect(Unit) {
|
||||
vm.userRestrictions.value = context.getDPM().getUserRestrictions(receiver)
|
||||
}
|
||||
UserRestrictionScreen(::navigateUp) { title, items ->
|
||||
navController.navigate(UserRestrictionOptions(title, items))
|
||||
navigate(UserRestrictionOptions(title, items))
|
||||
}
|
||||
}
|
||||
composable<UserRestrictionOptions>(mapOf(serializableNavTypePair<List<Restriction>>())) {
|
||||
UserRestrictionOptionsScreen(it.toRoute(), userRestrictions, ::navigateUp) { id, status ->
|
||||
try {
|
||||
val dpm = context.getDPM()
|
||||
if(status) dpm.addUserRestriction(receiver, id)
|
||||
else dpm.clearUserRestriction(receiver, id)
|
||||
@SuppressLint("NewApi")
|
||||
vm.userRestrictions.value = dpm.getUserRestrictions(receiver)
|
||||
} catch(_: Exception) {
|
||||
context.showOperationResultToast(false)
|
||||
}
|
||||
}
|
||||
UserRestrictionOptionsScreen(it.toRoute(), ::navigateUp)
|
||||
}
|
||||
|
||||
composable<Users> { UsersScreen(::navigateUp, ::navigate) }
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.bintianqi.owndroid
|
||||
|
||||
import android.app.Application
|
||||
import android.os.Bundle
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -9,7 +8,6 @@ import kotlinx.coroutines.launch
|
||||
|
||||
class MyViewModel(application: Application): AndroidViewModel(application) {
|
||||
val theme = MutableStateFlow(ThemeSettings())
|
||||
val userRestrictions = MutableStateFlow(Bundle())
|
||||
|
||||
init {
|
||||
val sp = SharedPrefs(application)
|
||||
|
||||
@@ -12,20 +12,29 @@ import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LargeTopAppBar
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -35,6 +44,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.painterResource
|
||||
@@ -49,6 +59,7 @@ import androidx.core.net.toUri
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.bintianqi.owndroid.ui.FunctionItem
|
||||
import com.bintianqi.owndroid.ui.MyScaffold
|
||||
import com.bintianqi.owndroid.ui.NavIcon
|
||||
import com.bintianqi.owndroid.ui.Notes
|
||||
import com.bintianqi.owndroid.ui.SwitchItem
|
||||
import kotlinx.serialization.Serializable
|
||||
@@ -59,6 +70,7 @@ import java.util.Locale
|
||||
|
||||
@Serializable object Settings
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SettingsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
|
||||
val context = LocalContext.current
|
||||
@@ -66,7 +78,46 @@ fun SettingsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
|
||||
val exportLogsLauncher = rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("text/plain")) {
|
||||
if(it != null) exportLogs(context, it)
|
||||
}
|
||||
MyScaffold(R.string.settings, onNavigateUp, 0.dp) {
|
||||
val sb = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
||||
var dropdown by remember { mutableStateOf(false) }
|
||||
Scaffold(
|
||||
Modifier.nestedScroll(sb.nestedScrollConnection),
|
||||
topBar = {
|
||||
LargeTopAppBar(
|
||||
{ Text(stringResource(R.string.settings)) },
|
||||
navigationIcon = { NavIcon(onNavigateUp) },
|
||||
scrollBehavior = sb,
|
||||
actions = {
|
||||
Box {
|
||||
IconButton({ dropdown = true }) {
|
||||
Icon(Icons.Default.MoreVert, null)
|
||||
}
|
||||
DropdownMenu(dropdown, { dropdown = false }) {
|
||||
DropdownMenuItem(
|
||||
{ Text(stringResource(R.string.export_logs)) },
|
||||
{
|
||||
dropdown = false
|
||||
val time = SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault())
|
||||
.format(Date(System.currentTimeMillis()))
|
||||
exportLogsLauncher.launch("owndroid_log_$time")
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(painterResource(R.drawable.description_fill0), null)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(bottom = 80.dp)
|
||||
) {
|
||||
FunctionItem(title = R.string.options, icon = R.drawable.tune_fill0) { onNavigate(SettingsOptions) }
|
||||
FunctionItem(title = R.string.appearance, icon = R.drawable.format_paint_fill0) { onNavigate(Appearance) }
|
||||
FunctionItem(R.string.app_lock, icon = R.drawable.lock_fill0) { onNavigate(AppLockSettings) }
|
||||
@@ -74,12 +125,9 @@ fun SettingsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
|
||||
FunctionItem(title = R.string.api, icon = R.drawable.code_fill0) { onNavigate(ApiSettings) }
|
||||
if (privilege.device && !privilege.dhizuku)
|
||||
FunctionItem(R.string.notifications, icon = R.drawable.notifications_fill0) { onNavigate(Notifications) }
|
||||
FunctionItem(title = R.string.export_logs, icon = R.drawable.description_fill0) {
|
||||
val time = SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).format(Date(System.currentTimeMillis()))
|
||||
exportLogsLauncher.launch("owndroid_log_$time")
|
||||
}
|
||||
FunctionItem(title = R.string.about, icon = R.drawable.info_fill0) { onNavigate(About) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable object SettingsOptions
|
||||
@@ -111,6 +159,10 @@ fun SettingsOptionsScreen(onNavigateUp: () -> Unit) {
|
||||
fun AppearanceScreen(onNavigateUp: () -> Unit, currentTheme: ThemeSettings, onThemeChange: (ThemeSettings) -> Unit) {
|
||||
var darkThemeMenu by remember { mutableStateOf(false) }
|
||||
var theme by remember { mutableStateOf(currentTheme) }
|
||||
fun update(it: ThemeSettings) {
|
||||
theme = it
|
||||
onThemeChange(it)
|
||||
}
|
||||
val darkThemeTextID = when(theme.darkTheme) {
|
||||
1 -> R.string.on
|
||||
0 -> R.string.off
|
||||
@@ -121,7 +173,7 @@ fun AppearanceScreen(onNavigateUp: () -> Unit, currentTheme: ThemeSettings, onTh
|
||||
SwitchItem(
|
||||
R.string.material_you_color,
|
||||
state = theme.materialYou,
|
||||
onCheckedChange = { theme = theme.copy(materialYou = it) }
|
||||
onCheckedChange = { update(theme.copy(materialYou = it)) }
|
||||
)
|
||||
}
|
||||
Box {
|
||||
@@ -133,13 +185,14 @@ fun AppearanceScreen(onNavigateUp: () -> Unit, currentTheme: ThemeSettings, onTh
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.follow_system)) },
|
||||
onClick = {
|
||||
theme = theme.copy(darkTheme = -1)
|
||||
update(theme.copy(darkTheme = -1))
|
||||
darkThemeMenu = false
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.on)) },
|
||||
onClick = {
|
||||
update(theme.copy(darkTheme = 1))
|
||||
theme = theme.copy(darkTheme = 1)
|
||||
darkThemeMenu = false
|
||||
}
|
||||
@@ -147,19 +200,17 @@ fun AppearanceScreen(onNavigateUp: () -> Unit, currentTheme: ThemeSettings, onTh
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.off)) },
|
||||
onClick = {
|
||||
theme = theme.copy(darkTheme = 0)
|
||||
update(theme.copy(darkTheme = 0))
|
||||
darkThemeMenu = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
AnimatedVisibility(theme.darkTheme == 1 || (theme.darkTheme == -1 && isSystemInDarkTheme())) {
|
||||
SwitchItem(R.string.black_theme, state = theme.blackTheme, onCheckedChange = { theme = theme.copy(blackTheme = it) })
|
||||
}
|
||||
AnimatedVisibility(theme != currentTheme, Modifier.fillMaxWidth().padding(8.dp)) {
|
||||
Button({onThemeChange(theme)}) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
SwitchItem(
|
||||
R.string.black_theme, state = theme.blackTheme,
|
||||
onCheckedChange = { update(theme.copy(blackTheme = it)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import com.bintianqi.owndroid.backToHomeStateFlow
|
||||
import com.bintianqi.owndroid.createShortcuts
|
||||
import com.bintianqi.owndroid.myPrivilege
|
||||
import com.rosan.dhizuku.api.Dhizuku
|
||||
import com.rosan.dhizuku.api.Dhizuku.binderWrapper
|
||||
import com.rosan.dhizuku.api.DhizukuBinderWrapper
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.serialization.encodeToString
|
||||
@@ -73,7 +72,7 @@ fun binderWrapperDevicePolicyManager(appContext: Context): DevicePolicyManager?
|
||||
val oldInterface = field[manager] as IDevicePolicyManager
|
||||
if (oldInterface is DhizukuBinderWrapper) return manager
|
||||
val oldBinder = oldInterface.asBinder()
|
||||
val newBinder = binderWrapper(oldBinder)
|
||||
val newBinder = Dhizuku.binderWrapper(oldBinder)
|
||||
val newInterface = IDevicePolicyManager.Stub.asInterface(newBinder)
|
||||
field[manager] = newInterface
|
||||
return manager
|
||||
@@ -93,7 +92,7 @@ private fun binderWrapperPackageInstaller(appContext: Context): PackageInstaller
|
||||
val oldInterface = field[installer] as IPackageInstaller
|
||||
if (oldInterface is DhizukuBinderWrapper) return installer
|
||||
val oldBinder = oldInterface.asBinder()
|
||||
val newBinder = binderWrapper(oldBinder)
|
||||
val newBinder = Dhizuku.binderWrapper(oldBinder)
|
||||
val newInterface = IPackageInstaller.Stub.asInterface(newBinder)
|
||||
field[installer] = newInterface
|
||||
return installer
|
||||
@@ -107,7 +106,6 @@ fun Context.getPackageInstaller(): PackageInstaller {
|
||||
if(SharedPrefs(this).dhizuku) {
|
||||
if (!dhizukuPermissionGranted()) {
|
||||
dhizukuErrorStatus.value = 2
|
||||
backToHomeStateFlow.value = true
|
||||
return this.packageManager.packageInstaller
|
||||
}
|
||||
return binderWrapperPackageInstaller(this) ?: this.packageManager.packageInstaller
|
||||
@@ -120,7 +118,6 @@ fun Context.getDPM(): DevicePolicyManager {
|
||||
if(SharedPrefs(this).dhizuku) {
|
||||
if (!dhizukuPermissionGranted()) {
|
||||
dhizukuErrorStatus.value = 2
|
||||
backToHomeStateFlow.value = true
|
||||
return this.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
}
|
||||
return binderWrapperDevicePolicyManager(this) ?: this.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||
|
||||
@@ -295,7 +295,7 @@ fun WorkModesScreen(
|
||||
Spacer(Modifier.padding(horizontal = 2.dp))
|
||||
Button({ dialog = 5 }) { Text(stringResource(R.string.adb_command)) }
|
||||
Spacer(Modifier.padding(horizontal = 2.dp))
|
||||
if (VERSION.SDK_INT == 35) Button({
|
||||
if (VERSION.SDK_INT >= 33) Button({
|
||||
dialog = 6
|
||||
}) {
|
||||
Text(stringResource(R.string.root_force_activate))
|
||||
@@ -575,6 +575,7 @@ fun LockScreenInfoScreen(onNavigateUp: () -> Unit) {
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
dpm.setDeviceOwnerLockScreenInfo(receiver, null)
|
||||
infoText = ""
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
|
||||
@@ -10,8 +10,12 @@ import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateMapOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
@@ -73,14 +77,30 @@ data class UserRestrictionOptions(
|
||||
@RequiresApi(24)
|
||||
@Composable
|
||||
fun UserRestrictionOptionsScreen(
|
||||
data: UserRestrictionOptions, restrictions: Bundle,
|
||||
onNavigateUp: () -> Unit, onRestrictionChange: (String, Boolean) -> Unit
|
||||
data: UserRestrictionOptions, onNavigateUp: () -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
val receiver = context.getReceiver()
|
||||
val status = remember { mutableStateMapOf<String, Boolean>() }
|
||||
LaunchedEffect(Unit) {
|
||||
val restrictions = dpm.getUserRestrictions(receiver)
|
||||
data.items.forEach {
|
||||
status.put(it.id, restrictions.getBoolean(it.id))
|
||||
}
|
||||
}
|
||||
MyScaffold(data.title, onNavigateUp, 0.dp) {
|
||||
data.items.filter { Build.VERSION.SDK_INT >= it.requiresApi }.forEach { restriction ->
|
||||
SwitchItem(
|
||||
restriction.name, restriction.id, restriction.icon,
|
||||
restrictions.getBoolean(restriction.id), { onRestrictionChange(restriction.id, it) }, padding = true
|
||||
restriction.name, restriction.id, restriction.icon, status[restriction.id] == true,
|
||||
{
|
||||
if (it) {
|
||||
dpm.addUserRestriction(receiver, restriction.id)
|
||||
} else {
|
||||
dpm.clearUserRestriction(receiver, restriction.id)
|
||||
}
|
||||
status[restriction.id] = dpm.getUserRestrictions(receiver).getBoolean(restriction.id)
|
||||
}, padding = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ fun OwnDroidTheme(
|
||||
darkTheme -> darkScheme
|
||||
else -> lightScheme
|
||||
}.let {
|
||||
if(darkTheme && theme.blackTheme) it.copy(background = Color.Black) else it
|
||||
if(darkTheme && theme.blackTheme) it.copy(background = Color.Black, surface = Color.Black) else it
|
||||
}
|
||||
val view = LocalView.current
|
||||
SideEffect {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[versions]
|
||||
agp = "8.9.2"
|
||||
agp = "8.10.0"
|
||||
kotlin = "2.1.20"
|
||||
|
||||
navigation-compose = "2.9.0"
|
||||
|
||||
Reference in New Issue
Block a user