From fbe68e6656e3f88d2ce15431abd39c54b6d609ec Mon Sep 17 00:00:00 2001 From: tornaco Date: Wed, 25 May 2022 21:57:08 +0800 Subject: [PATCH] [app] Regular interval ui of the DateTimeEngine --- .../thanos/core/profile/IProfileManager.aidl | 2 +- .../thanos/core/profile/IProfileManager.java | 21 ++- .../thanos/core/profile/ProfileManager.java | 7 +- android/internal/Thanox-Internal | 2 +- .../common/widget/DurationPickerDialog.kt | 163 ++++++++++++++++++ .../common/widget/SingleChoiceDialog.kt | 2 +- .../ProfileShortcutEngineActivity.java | 4 +- .../profile/engine/DateTimeEngineScreen.kt | 58 +++---- .../profile/engine/DateTimeEngineViewModel.kt | 48 ++++++ .../profile/engine/work/PeriodicWork.kt | 38 ++++ 10 files changed, 301 insertions(+), 44 deletions(-) create mode 100644 android/modules/module_compose_common/src/main/java/github/tornaco/android/thanos/module/compose/common/widget/DurationPickerDialog.kt create mode 100644 android/modules/module_profile/src/main/java/github/tornaco/thanos/android/module/profile/engine/DateTimeEngineViewModel.kt create mode 100644 android/modules/module_profile/src/main/java/github/tornaco/thanos/android/module/profile/engine/work/PeriodicWork.kt diff --git a/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/profile/IProfileManager.aidl b/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/profile/IProfileManager.aidl index d0a28f3d7..84d6c1ef9 100644 --- a/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/profile/IProfileManager.aidl +++ b/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/profile/IProfileManager.aidl @@ -58,7 +58,7 @@ interface IProfileManager { void addRuleIfNotExists(String ruleJson, in IRuleAddCallback callback, int format); - void publishStringFact(String factValue, long delayMills); + void publishStringFact(int source, String factValue, long delayMills); void updateRule(int ruleId, String ruleJson, in IRuleAddCallback callback, int format); diff --git a/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/profile/IProfileManager.java b/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/profile/IProfileManager.java index 775e2376a..1aa71f812 100644 --- a/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/profile/IProfileManager.java +++ b/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/profile/IProfileManager.java @@ -133,7 +133,7 @@ public static class Default implements github.tornaco.android.thanos.core.profil @Override public void addRuleIfNotExists(java.lang.String ruleJson, github.tornaco.android.thanos.core.profile.IRuleAddCallback callback, int format) throws android.os.RemoteException { } - @Override public void publishStringFact(java.lang.String factValue, long delayMills) throws android.os.RemoteException + @Override public void publishStringFact(int source, java.lang.String factValue, long delayMills) throws android.os.RemoteException { } @Override public void updateRule(int ruleId, java.lang.String ruleJson, github.tornaco.android.thanos.core.profile.IRuleAddCallback callback, int format) throws android.os.RemoteException @@ -564,11 +564,13 @@ public static github.tornaco.android.thanos.core.profile.IProfileManager asInter case TRANSACTION_publishStringFact: { data.enforceInterface(descriptor); - java.lang.String _arg0; - _arg0 = data.readString(); - long _arg1; - _arg1 = data.readLong(); - this.publishStringFact(_arg0, _arg1); + int _arg0; + _arg0 = data.readInt(); + java.lang.String _arg1; + _arg1 = data.readString(); + long _arg2; + _arg2 = data.readLong(); + this.publishStringFact(_arg0, _arg1, _arg2); reply.writeNoException(); return true; } @@ -1415,17 +1417,18 @@ public java.lang.String getInterfaceDescriptor() _data.recycle(); } } - @Override public void publishStringFact(java.lang.String factValue, long delayMills) throws android.os.RemoteException + @Override public void publishStringFact(int source, java.lang.String factValue, long delayMills) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); + _data.writeInt(source); _data.writeString(factValue); _data.writeLong(delayMills); boolean _status = mRemote.transact(Stub.TRANSACTION_publishStringFact, _data, _reply, 0); if (!_status && getDefaultImpl() != null) { - getDefaultImpl().publishStringFact(factValue, delayMills); + getDefaultImpl().publishStringFact(source, factValue, delayMills); return; } _reply.readException(); @@ -1743,7 +1746,7 @@ public static github.tornaco.android.thanos.core.profile.IProfileManager getDefa public java.lang.String getAutoConfigTemplateSelectionId() throws android.os.RemoteException; public boolean applyConfigTemplateForPackage(java.lang.String packageName, github.tornaco.android.thanos.core.profile.ConfigTemplate template) throws android.os.RemoteException; public void addRuleIfNotExists(java.lang.String ruleJson, github.tornaco.android.thanos.core.profile.IRuleAddCallback callback, int format) throws android.os.RemoteException; - public void publishStringFact(java.lang.String factValue, long delayMills) throws android.os.RemoteException; + public void publishStringFact(int source, java.lang.String factValue, long delayMills) throws android.os.RemoteException; public void updateRule(int ruleId, java.lang.String ruleJson, github.tornaco.android.thanos.core.profile.IRuleAddCallback callback, int format) throws android.os.RemoteException; public void registerRuleChangeListener(github.tornaco.android.thanos.core.profile.IRuleChangeListener listener) throws android.os.RemoteException; public void unRegisterRuleChangeListener(github.tornaco.android.thanos.core.profile.IRuleChangeListener listener) throws android.os.RemoteException; diff --git a/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/profile/ProfileManager.java b/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/profile/ProfileManager.java index e6ff78f60..dc13b4093 100644 --- a/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/profile/ProfileManager.java +++ b/android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/profile/ProfileManager.java @@ -11,6 +11,9 @@ public class ProfileManager { public static final int RULE_FORMAT_JSON = 0; public static final int RULE_FORMAT_YAML = 1; + public static final int FACT_SOURCE_SHORTCUT = 1; + public static final int FACT_SOURCE_DATE_TIME = 2; + public static final String PROFILE_AUTO_APPLY_NEW_INSTALLED_APPS_CONFIG_TEMPLATE_PACKAGE_PREFIX = "thanox.config.template."; @@ -201,8 +204,8 @@ public boolean applyConfigTemplateForPackage(String packageName, ConfigTemplate } @SneakyThrows - public void publishStringFact(String factValue, long delayMills) { - server.publishStringFact(factValue, delayMills); + public void publishStringFact(int source, String factValue, long delayMills) { + server.publishStringFact(source, factValue, delayMills); } @SneakyThrows diff --git a/android/internal/Thanox-Internal b/android/internal/Thanox-Internal index b826e8d36..601a3b605 160000 --- a/android/internal/Thanox-Internal +++ b/android/internal/Thanox-Internal @@ -1 +1 @@ -Subproject commit b826e8d36873299c54b79c01ea2ecae193b8de83 +Subproject commit 601a3b6055e87ad797834255332fb26da1326a12 diff --git a/android/modules/module_compose_common/src/main/java/github/tornaco/android/thanos/module/compose/common/widget/DurationPickerDialog.kt b/android/modules/module_compose_common/src/main/java/github/tornaco/android/thanos/module/compose/common/widget/DurationPickerDialog.kt new file mode 100644 index 000000000..1db34f6bc --- /dev/null +++ b/android/modules/module_compose_common/src/main/java/github/tornaco/android/thanos/module/compose/common/widget/DurationPickerDialog.kt @@ -0,0 +1,163 @@ +/* + * (C) Copyright 2022 Thanox + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package github.tornaco.android.thanos.module.compose.common.widget + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Remove +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.window.DialogProperties +import github.tornaco.android.thanos.module.compose.common.LargeSpacer +import github.tornaco.android.thanos.module.compose.common.StandardSpacer +import kotlin.time.Duration + + +class DurationPickerDialogState( + val title: String, + val onDurationPick: (Duration) -> Unit +) { + private var _isShowing: Boolean by mutableStateOf(false) + val isShowing get() = _isShowing + + fun show() { + _isShowing = true + } + + fun dismiss() { + _isShowing = false + } +} + +@Composable +fun rememberDurationPickerDialogState( + title: String, + onDurationPick: (Duration) -> Unit +): DurationPickerDialogState { + return remember { + DurationPickerDialogState(title, onDurationPick) + } +} + +@Composable +fun DurationPickerDialog(state: DurationPickerDialogState) { + if (state.isShowing) { + AlertDialog( + title = { + Text(text = state.title) + }, + text = { + Column( + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.Start + ) { + Hour() + StandardSpacer() + Minute() + StandardSpacer() + Second() + } + }, + confirmButton = {}, + onDismissRequest = { + state.dismiss() + }, + properties = DialogProperties() + ) + } +} + +@Composable +private fun Hour() { + Field( + "Hour", + 0, + 999 + ) +} + +@Composable +private fun Minute() { + Field( + "Minute", + 0, + 59 + ) +} + +@Composable +private fun Second() { + Field( + "Second", + 0, + 59 + ) +} + +@Composable +private fun Field(label: String, min: Int = 0, max: Int) { + var value by remember { + mutableStateOf(min) + } + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + Text(text = label) + LargeSpacer() + + // - + IconButton(onClick = { + if (value > min) { + value -= 1 + } else { + value = max + } + }) { + Icon( + imageVector = Icons.Filled.Remove, + contentDescription = "-" + ) + } + + StandardSpacer() + Text(text = "$value") + StandardSpacer() + + // + + IconButton(onClick = { + if (value < max) { + value += 1 + } else { + value = min + } + }) { + Icon( + imageVector = Icons.Filled.Add, + contentDescription = "+" + ) + } + } +} \ No newline at end of file diff --git a/android/modules/module_compose_common/src/main/java/github/tornaco/android/thanos/module/compose/common/widget/SingleChoiceDialog.kt b/android/modules/module_compose_common/src/main/java/github/tornaco/android/thanos/module/compose/common/widget/SingleChoiceDialog.kt index dcb3aa6cf..e54774e0b 100644 --- a/android/modules/module_compose_common/src/main/java/github/tornaco/android/thanos/module/compose/common/widget/SingleChoiceDialog.kt +++ b/android/modules/module_compose_common/src/main/java/github/tornaco/android/thanos/module/compose/common/widget/SingleChoiceDialog.kt @@ -53,7 +53,7 @@ class SingleChoiceDialogState( } @Composable -fun rememberDialogState( +fun rememberSingleChoiceDialogState( title: String, items: List, onItemClick: (String) -> Unit diff --git a/android/modules/module_profile/src/main/java/github/tornaco/thanos/android/module/profile/ProfileShortcutEngineActivity.java b/android/modules/module_profile/src/main/java/github/tornaco/thanos/android/module/profile/ProfileShortcutEngineActivity.java index 72fa15b90..dac71e8d0 100644 --- a/android/modules/module_profile/src/main/java/github/tornaco/thanos/android/module/profile/ProfileShortcutEngineActivity.java +++ b/android/modules/module_profile/src/main/java/github/tornaco/thanos/android/module/profile/ProfileShortcutEngineActivity.java @@ -1,5 +1,7 @@ package github.tornaco.thanos.android.module.profile; +import static github.tornaco.android.thanos.core.profile.ProfileManager.FACT_SOURCE_SHORTCUT; + import android.app.Activity; import android.content.Context; import android.content.Intent; @@ -39,7 +41,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { String factValue = getIntent().getStringExtra(EXTRA_PROFILE_FACT_VALUE); if (!TextUtils.isEmpty(factValue)) { XLog.i("publish factValue= %s", factValue); - ThanosManager.from(this).ifServiceInstalled(thanosManager -> thanosManager.getProfileManager().publishStringFact(factValue, 0)); + ThanosManager.from(this).ifServiceInstalled(thanosManager -> thanosManager.getProfileManager().publishStringFact(FACT_SOURCE_SHORTCUT, factValue, 0)); } } } finally { diff --git a/android/modules/module_profile/src/main/java/github/tornaco/thanos/android/module/profile/engine/DateTimeEngineScreen.kt b/android/modules/module_profile/src/main/java/github/tornaco/thanos/android/module/profile/engine/DateTimeEngineScreen.kt index 738558a79..f03eb645c 100644 --- a/android/modules/module_profile/src/main/java/github/tornaco/thanos/android/module/profile/engine/DateTimeEngineScreen.kt +++ b/android/modules/module_profile/src/main/java/github/tornaco/thanos/android/module/profile/engine/DateTimeEngineScreen.kt @@ -24,28 +24,43 @@ import androidx.compose.material.icons.filled.Schedule import androidx.compose.material.icons.filled.Timer import androidx.compose.material3.Icon import androidx.compose.material3.Text -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource import github.tornaco.android.thanos.module.compose.common.theme.TypographyDefaults import github.tornaco.android.thanos.module.compose.common.widget.* import github.tornaco.thanos.android.module.profile.R -private class UIState { - var showCreateRegularIntervalDialog by mutableStateOf(false) -} +private const val ID_TIME_OF_A_DAY = "tod" +private const val ID_REGULAR_INTERVAL = "ri" @Composable fun Activity.DateTimeEngineScreen() { - val uiState = rememberUIState() + val durationPickerDialogState = rememberDurationPickerDialogState(title = "Regular interval") { - val dialogState = - rememberDialogState(title = stringResource(id = R.string.module_profile_rule_new), + } + + + val typeSelectDialogState = + rememberSingleChoiceDialogState(title = stringResource(id = R.string.module_profile_rule_new), items = listOf( - SingleChoiceItem(id = "", icon = Icons.Filled.Schedule, label = "Time of a day (coming soon)"), - SingleChoiceItem(id = "", icon = Icons.Filled.Timer, label = "Regular interval (coming soon)"), + SingleChoiceItem( + id = ID_TIME_OF_A_DAY, + icon = Icons.Filled.Schedule, + label = "Time of a day (coming soon)" + ), + SingleChoiceItem( + id = ID_REGULAR_INTERVAL, + icon = Icons.Filled.Timer, + label = "Regular interval" + ), ), onItemClick = { - uiState.showCreateRegularIntervalDialog = true + when (it) { + ID_REGULAR_INTERVAL -> { + durationPickerDialogState.show() + } + ID_TIME_OF_A_DAY -> {} + } }) ThanoxSmallAppBarScaffold(title = { @@ -66,26 +81,11 @@ fun Activity.DateTimeEngineScreen() { contentDescription = stringResource(id = R.string.module_profile_rule_new) ) }) { - dialogState.show() + typeSelectDialogState.show() } }) { contentPadding -> - SingleChoiceDialog(state = dialogState) - } -} - - -@Composable -private fun CreateRegularIntervalDialog(uiState: UIState) { - if (uiState.showCreateRegularIntervalDialog) { - // TODO Impl. - } -} - - -@Composable -private fun rememberUIState(): UIState { - return remember { - UIState() + SingleChoiceDialog(state = typeSelectDialogState) + DurationPickerDialog(state = durationPickerDialogState) } -} +} \ No newline at end of file diff --git a/android/modules/module_profile/src/main/java/github/tornaco/thanos/android/module/profile/engine/DateTimeEngineViewModel.kt b/android/modules/module_profile/src/main/java/github/tornaco/thanos/android/module/profile/engine/DateTimeEngineViewModel.kt new file mode 100644 index 000000000..0f906876a --- /dev/null +++ b/android/modules/module_profile/src/main/java/github/tornaco/thanos/android/module/profile/engine/DateTimeEngineViewModel.kt @@ -0,0 +1,48 @@ +/* + * (C) Copyright 2022 Thanox + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package github.tornaco.thanos.android.module.profile.engine + +import android.annotation.SuppressLint +import android.content.Context +import androidx.lifecycle.ViewModel +import androidx.work.Data +import androidx.work.ExistingPeriodicWorkPolicy +import androidx.work.PeriodicWorkRequest +import androidx.work.WorkManager +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext +import github.tornaco.thanos.android.module.profile.engine.work.PeriodicWork +import java.time.Duration +import javax.inject.Inject + +@SuppressLint("StaticFieldLeak") +@HiltViewModel +class DateTimeEngineViewModel @Inject constructor(@ApplicationContext private val context: Context) : + ViewModel() { + private val workManager get() = WorkManager.getInstance(context) + + fun schedulePeriodicWork(fact: String, duration: Duration) { + workManager.enqueueUniquePeriodicWork( + "Profile-PeriodicWork_$fact", + ExistingPeriodicWorkPolicy.REPLACE, + PeriodicWorkRequest.Builder(PeriodicWork::class.java, duration) + .setInputData(Data.Builder().putString("fact", fact).build()) + .build() + ) + } +} \ No newline at end of file diff --git a/android/modules/module_profile/src/main/java/github/tornaco/thanos/android/module/profile/engine/work/PeriodicWork.kt b/android/modules/module_profile/src/main/java/github/tornaco/thanos/android/module/profile/engine/work/PeriodicWork.kt new file mode 100644 index 000000000..12112a0fb --- /dev/null +++ b/android/modules/module_profile/src/main/java/github/tornaco/thanos/android/module/profile/engine/work/PeriodicWork.kt @@ -0,0 +1,38 @@ +/* + * (C) Copyright 2022 Thanox + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package github.tornaco.thanos.android.module.profile.engine.work + +import android.content.Context +import androidx.work.Worker +import androidx.work.WorkerParameters +import github.tornaco.android.thanos.core.app.ThanosManager +import github.tornaco.android.thanos.core.profile.ProfileManager + +class PeriodicWork(private val context: Context, workerParams: WorkerParameters) : + Worker(context, workerParams) { + private val thanox get() = ThanosManager.from(context) + + override fun doWork(): Result { + thanox.profileManager.publishStringFact( + ProfileManager.FACT_SOURCE_DATE_TIME, + inputData.getString("fact"), + 0L + ) + return Result.success() + } +} \ No newline at end of file