Fix UI issues (#185, #188)

This commit is contained in:
BinTianqi
2025-10-26 14:57:12 +08:00
parent 13a69d05e8
commit de43df7f59
4 changed files with 45 additions and 32 deletions

View File

@@ -384,7 +384,7 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
vm:: getLockTaskFeatures, vm::setLockTaskFeatures, ::navigateUp) vm:: getLockTaskFeatures, vm::setLockTaskFeatures, ::navigateUp)
} }
composable<CaCert> { composable<CaCert> {
CaCertScreen(vm.installedCaCerts, vm::getCaCerts, vm::installCaCert, vm::parseCaCert, CaCertScreen(vm.installedCaCerts, vm::getCaCerts, vm.selectedCaCert, vm::selectCaCert, vm::installCaCert, vm::parseCaCert,
vm::exportCaCert, vm::uninstallCaCert, vm::uninstallAllCaCerts, ::navigateUp) vm::exportCaCert, vm::uninstallCaCert, vm::uninstallAllCaCerts, ::navigateUp)
} }
composable<SecurityLogging> { composable<SecurityLogging> {

View File

@@ -797,17 +797,20 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
} }
} }
val installedCaCerts = MutableStateFlow(emptyList<CaCertInfo>()) val installedCaCerts = MutableStateFlow(emptyList<CaCertInfo>())
val selectedCaCert = MutableStateFlow<CaCertInfo?>(null)
fun getCaCerts() { fun getCaCerts() {
installedCaCerts.value = DPM.getInstalledCaCerts(DAR).mapNotNull { parseCaCert(it) } installedCaCerts.value = DPM.getInstalledCaCerts(DAR).mapNotNull { parseCaCert(it) }
} }
fun parseCaCert(uri: Uri): CaCertInfo? { fun selectCaCert(cert: CaCertInfo) {
return try { selectedCaCert.value = cert
}
fun parseCaCert(uri: Uri) {
try {
application.contentResolver.openInputStream(uri)?.use { application.contentResolver.openInputStream(uri)?.use {
parseCaCert(it.readBytes()) selectedCaCert.value = parseCaCert(it.readBytes())
} }
} catch(e: Exception) { } catch(e: Exception) {
e.printStackTrace() e.printStackTrace()
null
} }
} }
fun parseCaCert(bytes: ByteArray): CaCertInfo? { fun parseCaCert(bytes: ByteArray): CaCertInfo? {
@@ -825,22 +828,22 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
null null
} }
} }
fun installCaCert(cert: CaCertInfo): Boolean { fun installCaCert(): Boolean {
val result = DPM.installCaCert(DAR, cert.bytes) val result = DPM.installCaCert(DAR, selectedCaCert.value!!.bytes)
if (result) getCaCerts() if (result) getCaCerts()
return result return result
} }
fun uninstallCaCert(cert: CaCertInfo) { fun uninstallCaCert() {
DPM.uninstallCaCert(DAR, cert.bytes) DPM.uninstallCaCert(DAR, selectedCaCert.value!!.bytes)
getCaCerts() getCaCerts()
} }
fun uninstallAllCaCerts() { fun uninstallAllCaCerts() {
DPM.uninstallAllUserCaCerts(DAR) DPM.uninstallAllUserCaCerts(DAR)
getCaCerts() getCaCerts()
} }
fun exportCaCert(uri: Uri, cert: CaCertInfo) { fun exportCaCert(uri: Uri) {
application.contentResolver.openOutputStream(uri)?.use { application.contentResolver.openOutputStream(uri)?.use {
it.write(cert.bytes) it.write(selectedCaCert.value!!.bytes)
} }
} }
val mdAccountTypes = MutableStateFlow(emptyList<String>()) val mdAccountTypes = MutableStateFlow(emptyList<String>())

View File

@@ -244,7 +244,7 @@ fun ResetPasswordTokenScreen(
) { ) {
val context = LocalContext.current val context = LocalContext.current
var token by rememberSaveable { mutableStateOf("") } var token by rememberSaveable { mutableStateOf("") }
var state by rememberSaveable { mutableStateOf(getState()) } var state by remember { mutableStateOf(getState()) }
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { val launcher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == Activity.RESULT_OK) { if (it.resultCode == Activity.RESULT_OK) {
context.popToast(R.string.token_activated) context.popToast(R.string.token_activated)

View File

@@ -68,6 +68,7 @@ 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.OutlinedTextField
import androidx.compose.material3.PrimaryTabRow
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Slider import androidx.compose.material3.Slider
import androidx.compose.material3.Tab import androidx.compose.material3.Tab
@@ -75,6 +76,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.TimePicker import androidx.compose.material3.TimePicker
import androidx.compose.material3.TimePickerDialog
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberDatePickerState import androidx.compose.material3.rememberDatePickerState
@@ -127,6 +129,7 @@ import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.yesOrNo import com.bintianqi.owndroid.yesOrNo
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@@ -562,7 +565,7 @@ fun ChangeTimeScreen(setTime: (Long, Boolean) -> Boolean, onNavigateUp: () -> Un
.fillMaxSize() .fillMaxSize()
.padding(paddingValues) .padding(paddingValues)
) { ) {
TabRow(tab) { PrimaryTabRow(tab) {
Tab( Tab(
tab == 0, { coroutine.launch { pagerState.animateScrollToPage(0) } }, tab == 0, { coroutine.launch { pagerState.animateScrollToPage(0) } },
text = { Text(stringResource(R.string.selector)) } text = { Text(stringResource(R.string.selector)) }
@@ -579,6 +582,7 @@ fun ChangeTimeScreen(setTime: (Long, Boolean) -> Boolean, onNavigateUp: () -> Un
Column( Column(
Modifier Modifier
.fillMaxSize() .fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(top = 8.dp) .padding(top = 8.dp)
.padding(horizontal = HorizontalPadding) .padding(horizontal = HorizontalPadding)
) { ) {
@@ -634,6 +638,7 @@ fun ChangeTimeScreen(setTime: (Long, Boolean) -> Boolean, onNavigateUp: () -> Un
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))
} }
} }
Spacer(Modifier.height(BottomPadding))
} }
} }
} }
@@ -646,17 +651,21 @@ fun ChangeTimeScreen(setTime: (Long, Boolean) -> Boolean, onNavigateUp: () -> Un
}, },
onDismissRequest = { picker = 0; focusMgr.clearFocus() } onDismissRequest = { picker = 0; focusMgr.clearFocus() }
) { ) {
DatePicker(datePickerState) Column(Modifier.verticalScroll(rememberScrollState())) {
DatePicker(datePickerState)
}
} }
if(picker == 2) AlertDialog( if (picker == 2) TimePickerDialog(
text = { TimePicker(timePickerState) }, title = {},
confirmButton = { confirmButton = {
TextButton(onClick = { picker = 0; focusMgr.clearFocus() } ) { TextButton({ picker = 0 }) {
Text(stringResource(R.string.confirm)) Text(stringResource(R.string.confirm))
} }
}, },
onDismissRequest = { picker = 0; focusMgr.clearFocus() } onDismissRequest = { picker = 0 }
) ) {
TimePicker(timePickerState)
}
} }
@Serializable object ChangeTimeZone @Serializable object ChangeTimeZone
@@ -1353,25 +1362,26 @@ data class CaCertInfo(
@Composable @Composable
fun CaCertScreen( fun CaCertScreen(
caCertificates: StateFlow<List<CaCertInfo>>, getCerts: () -> Unit, caCertificates: StateFlow<List<CaCertInfo>>, getCerts: () -> Unit,
installCert: (CaCertInfo) -> Boolean, parseCert: (Uri) -> CaCertInfo?, selectedCaCert: MutableStateFlow<CaCertInfo?>, selectCaCert: (CaCertInfo) -> Unit,
exportCert: (Uri, CaCertInfo) -> Unit, uninstallCert: (CaCertInfo) -> Unit, installCert: () -> Boolean, parseCert: (Uri) -> Unit,
exportCert: (Uri) -> Unit, uninstallCert: () -> Unit,
uninstallAllCerts: () -> Unit, onNavigateUp: () -> Unit uninstallAllCerts: () -> Unit, onNavigateUp: () -> Unit
) { ) {
val context = LocalContext.current val context = LocalContext.current
/** 0:none, 1:install, 2:info, 3:uninstall all */ /** 0:none, 1:install, 2:info, 3:uninstall all */
var dialog by rememberSaveable { mutableIntStateOf(0) } var dialog by rememberSaveable { mutableIntStateOf(0) }
val caCerts by caCertificates.collectAsStateWithLifecycle() val caCerts by caCertificates.collectAsStateWithLifecycle()
var selectedCaCert by rememberSaveable { mutableStateOf<CaCertInfo?>(null) } val selectedCert by selectedCaCert.collectAsStateWithLifecycle()
val getCertLauncher = rememberLauncherForActivityResult( val getCertLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.OpenDocument()) { uri -> ActivityResultContracts.OpenDocument()) { uri ->
if(uri != null) { if (uri != null) {
selectedCaCert = parseCert(uri) parseCert(uri)
dialog = 1 dialog = 1
} }
} }
val exportCertLauncher = rememberLauncherForActivityResult( val exportCertLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.CreateDocument()) { uri -> ActivityResultContracts.CreateDocument()) { uri ->
if(uri != null) exportCert(uri, selectedCaCert!!) if (uri != null) exportCert(uri)
} }
LaunchedEffect(Unit) { getCerts() } LaunchedEffect(Unit) { getCerts() }
Scaffold( Scaffold(
@@ -1407,7 +1417,7 @@ fun CaCertScreen(
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { .clickable {
selectedCaCert = cert selectCaCert(cert)
dialog = 2 dialog = 2
} }
.animateItem() .animateItem()
@@ -1421,11 +1431,11 @@ fun CaCertScreen(
Spacer(Modifier.height(BottomPadding)) Spacer(Modifier.height(BottomPadding))
} }
} }
if (selectedCaCert != null && (dialog == 1 || dialog == 2)) { if (selectedCert != null && (dialog == 1 || dialog == 2)) {
val cert = selectedCaCert!! val cert = selectedCert!!
AlertDialog( AlertDialog(
text = { text = {
Column { Column(Modifier.verticalScroll(rememberScrollState())) {
Text("Serial number", style = typography.labelLarge) Text("Serial number", style = typography.labelLarge)
SelectionContainer { Text(cert.serialNumber) } SelectionContainer { Text(cert.serialNumber) }
Text("Subject", style = typography.labelLarge) Text("Subject", style = typography.labelLarge)
@@ -1445,7 +1455,7 @@ fun CaCertScreen(
) { ) {
TextButton( TextButton(
onClick = { onClick = {
uninstallCert(cert) uninstallCert()
dialog = 0 dialog = 0
}, },
modifier = Modifier.fillMaxWidth(0.49F), modifier = Modifier.fillMaxWidth(0.49F),
@@ -1467,7 +1477,7 @@ fun CaCertScreen(
confirmButton = { confirmButton = {
if (dialog == 1) { if (dialog == 1) {
TextButton({ TextButton({
context.showOperationResultToast(installCert(cert)) context.showOperationResultToast(installCert())
dialog = 0 dialog = 0
}) { }) {
Text(stringResource(R.string.install)) Text(stringResource(R.string.install))
@@ -1489,7 +1499,7 @@ fun CaCertScreen(
} }
} }
}, },
onDismissRequest = {} onDismissRequest = { dialog = 0 }
) )
} }
if (dialog == 3) { if (dialog == 3) {