diff --git a/app/src/main/java/com/bintianqi/owndroid/Utils.kt b/app/src/main/java/com/bintianqi/owndroid/Utils.kt index 8d2cf09..6179a8b 100644 --- a/app/src/main/java/com/bintianqi/owndroid/Utils.kt +++ b/app/src/main/java/com/bintianqi/owndroid/Utils.kt @@ -23,9 +23,12 @@ import java.io.File import java.io.FileNotFoundException import java.io.IOException import java.io.InputStream +import java.text.SimpleDateFormat import java.time.Instant import java.time.ZoneId import java.time.format.DateTimeFormatter +import java.time.format.FormatStyle +import java.util.Date import java.util.Locale lateinit var getFile: ActivityResultLauncher @@ -132,3 +135,10 @@ fun parseTimestamp(timestamp: Long): String { val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault()) return formatter.format(instant) } + +val Long.humanReadableDate: String + get() = SimpleDateFormat("yyyy/MM/dd", Locale.getDefault()).format(Date(this)) + +val Long.humanReadableTime: String + get() = SimpleDateFormat("HH:mm:ss", Locale.getDefault()).format(Date(this)) + diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt index 2db2cd2..54e82ab 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/System.kt @@ -45,15 +45,20 @@ import android.widget.Toast import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateContentSize import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.interaction.collectIsPressedAsState import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions @@ -63,14 +68,23 @@ import androidx.compose.material.icons.filled.Add import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.DatePicker +import androidx.compose.material3.DatePickerDialog +import androidx.compose.material3.ExperimentalMaterial3Api 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.SegmentedButton +import androidx.compose.material3.SegmentedButtonDefaults +import androidx.compose.material3.SingleChoiceSegmentedButtonRow import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.TextButton +import androidx.compose.material3.TimePicker +import androidx.compose.material3.rememberDatePickerState +import androidx.compose.material3.rememberTimePickerState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -102,6 +116,7 @@ import com.bintianqi.owndroid.exportFilePath import com.bintianqi.owndroid.fileUriFlow import com.bintianqi.owndroid.formatFileSize import com.bintianqi.owndroid.getFile +import com.bintianqi.owndroid.humanReadableDate import com.bintianqi.owndroid.prepareForNotification import com.bintianqi.owndroid.selectedPackage import com.bintianqi.owndroid.toggle @@ -360,6 +375,7 @@ fun Keyguard(navCtrl: NavHostController) { } } +@OptIn(ExperimentalMaterial3Api::class) @SuppressLint("NewApi") @Composable fun ChangeTime(navCtrl: NavHostController) { @@ -367,26 +383,108 @@ fun ChangeTime(navCtrl: NavHostController) { val dpm = context.getDPM() val receiver = context.getReceiver() val focusMgr = LocalFocusManager.current - var inputTime by remember { mutableStateOf("") } + val coroutine = rememberCoroutineScope() + val pagerState = rememberPagerState { 2 } + var manualInput by remember { mutableStateOf(false) } + var inputTime by remember { mutableStateOf("")} + var picker by remember { mutableIntStateOf(0) } //0:None, 1:DatePicker, 2:TimePicker + val datePickerState = rememberDatePickerState() + val timePickerState = rememberTimePickerState() + val dateInteractionSource = remember { MutableInteractionSource() } + val timeInteractionSource = remember { MutableInteractionSource() } + if(dateInteractionSource.collectIsPressedAsState().value) picker = 1 + if(timeInteractionSource.collectIsPressedAsState().value) picker = 2 + val isInputLegal = (manualInput && (try { inputTime.toLong() } catch(_: Exception) { -1 }) >= 0) || + (!manualInput && datePickerState.selectedDateMillis != null) MyScaffold(R.string.change_time, 8.dp, navCtrl) { - OutlinedTextField( - value = inputTime, - label = { Text(stringResource(R.string.time_unit_ms)) }, - onValueChange = { inputTime = it }, - supportingText = { Text(stringResource(R.string.info_change_time)) }, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done), - keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus() }), - modifier = Modifier.fillMaxWidth() - ) - Spacer(Modifier.padding(vertical = 5.dp)) + SingleChoiceSegmentedButtonRow( + modifier = Modifier.fillMaxWidth().padding(top = 4.dp) + ) { + SegmentedButton( + selected = !manualInput, shape = SegmentedButtonDefaults.itemShape(0, 2), + onClick = { + manualInput = false + coroutine.launch { + pagerState.animateScrollToPage(0) + } + } + ) { + Text(stringResource(R.string.selector)) + } + SegmentedButton( + selected = manualInput, shape = SegmentedButtonDefaults.itemShape(1, 2), + onClick = { + manualInput = true + coroutine.launch { + pagerState.animateScrollToPage(1) + } + } + ) { + Text(stringResource(R.string.manually_input)) + } + } + HorizontalPager( + state = pagerState, modifier = Modifier.height(140.dp).padding(top = 4.dp), + verticalAlignment = Alignment.Top + ) { page -> + if(page == 0) Column { + OutlinedTextField( + value = datePickerState.selectedDateMillis?.humanReadableDate ?: "", + onValueChange = {}, readOnly = true, + label = { Text(stringResource(R.string.date)) }, + interactionSource = dateInteractionSource, + modifier = Modifier.fillMaxWidth() + ) + OutlinedTextField( + value = timePickerState.hour.toString() + ":" + timePickerState.minute.toString(), + onValueChange = {}, readOnly = true, + label = { Text(stringResource(R.string.time)) }, + interactionSource = timeInteractionSource, + modifier = Modifier.fillMaxWidth() + ) + } + if(page == 1) OutlinedTextField( + value = inputTime, + label = { Text(stringResource(R.string.time_unit_ms)) }, + onValueChange = { inputTime = it }, + supportingText = { Text(stringResource(R.string.info_change_time)) }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done), + keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus() }), + modifier = Modifier.fillMaxWidth() + ) + } Button( - onClick = { dpm.setTime(receiver, inputTime.toLong()) }, + onClick = { + val timeMillis = if(manualInput) inputTime.toLong() + else datePickerState.selectedDateMillis!! + timePickerState.hour * 3600000 + timePickerState.minute * 60000 + val result = dpm.setTime(receiver, timeMillis) + Toast.makeText(context, if(result) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show() + }, modifier = Modifier.fillMaxWidth(), - enabled = inputTime != "" + enabled = isInputLegal ) { Text(stringResource(R.string.apply)) } } + if(picker == 1) DatePickerDialog( + confirmButton = { + TextButton(onClick = { picker = 0; focusMgr.clearFocus() } ) { + Text(stringResource(R.string.confirm)) + } + }, + onDismissRequest = { picker = 0; focusMgr.clearFocus() } + ) { + DatePicker(datePickerState) + } + if(picker == 2) AlertDialog( + text = { TimePicker(timePickerState) }, + confirmButton = { + TextButton(onClick = { picker = 0; focusMgr.clearFocus() } ) { + Text(stringResource(R.string.confirm)) + } + }, + onDismissRequest = { picker = 0; focusMgr.clearFocus() } + ) } @SuppressLint("NewApi") diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index eb9b342..523ba40 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -143,8 +143,13 @@ Отчет об ошибке Запросить отчет об ошибке? Перезагрузить - Change time - Change timezone + + Change time + Selector + Manually input + Date + Time + Change timezone Идентификатор часового пояса Автоматический часовой пояс должен быть отключен перед установкой пользовательского часового пояса. Политика разрешений diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 7dd3071..797be1a 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -144,8 +144,13 @@ Hata raporu Hata raporu iste? Yeniden başlat - Change time - Change timezone + + Change time + Selector + Manually input + Date + Time + Change timezone Saat dilimi kimliği Özel bir saat dilimi ayarlamadan önce otomatik saat dilimi devre dışı bırakılmalıdır İzin politikası diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 188e96f..8422449 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -140,6 +140,10 @@ 请求错误报告? 重启 更改时间 + 选择器 + 手动输入 + 日期 + 时间 更改时区 时区ID 在设置时区前需要关闭自动时区 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7f2d672..76c392f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -150,6 +150,10 @@ Request bug report? Reboot Change time + Selector + Manually input + Date + Time Change timezone Timezone ID Auto timezone should be disabled before set a custom timezone.