From b9c1793e34d1f817e4c6b0e2e6f831b1c8bd7f92 Mon Sep 17 00:00:00 2001 From: WhiredPlanck Date: Mon, 3 Feb 2025 16:21:51 +0800 Subject: [PATCH] feat: execute background sync work via WorkManager --- app/src/main/AndroidManifest.xml | 2 - .../java/com/osfans/trime/TrimeApplication.kt | 9 ++ .../com/osfans/trime/daemon/RimeDaemon.kt | 14 ++- .../com/osfans/trime/data/prefs/AppPrefs.kt | 18 +-- .../data/prefs/PreferenceDelegateOwner.kt | 6 +- .../trime/ime/core/TrimeInputMethodService.kt | 31 ----- .../ui/components/TimePickerPreference.kt | 97 --------------- .../trime/ui/fragments/ProfileFragment.kt | 114 +++++------------- .../com/osfans/trime/ui/main/MainViewModel.kt | 2 + .../osfans/trime/ui/main/PrefMainActivity.kt | 27 ++--- .../ui/main/settings/EditTextIntPreference.kt | 92 ++++++++++++++ .../java/com/osfans/trime/util/DateTime.kt | 5 + .../osfans/trime/worker/BackgroundSyncWork.kt | 96 +++++++++++++++ app/src/main/res/values-zh-rCN/strings.xml | 12 +- app/src/main/res/values-zh-rTW/strings.xml | 12 +- app/src/main/res/values/strings.xml | 12 +- app/src/main/res/xml/profile_preference.xml | 18 +-- 17 files changed, 287 insertions(+), 280 deletions(-) delete mode 100644 app/src/main/java/com/osfans/trime/ui/components/TimePickerPreference.kt create mode 100644 app/src/main/java/com/osfans/trime/ui/main/settings/EditTextIntPreference.kt create mode 100644 app/src/main/java/com/osfans/trime/worker/BackgroundSyncWork.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 291e903c75..119feb5031 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,8 +12,6 @@ SPDX-License-Identifier: GPL-3.0-or-later - - = PreferenceDelegate(sharedPreferences, key, defaultValue) + ): PreferenceDelegate = PreferenceDelegate(sharedPreferences, key, defaultValue).apply { register() } protected fun string( key: String, diff --git a/app/src/main/java/com/osfans/trime/ime/core/TrimeInputMethodService.kt b/app/src/main/java/com/osfans/trime/ime/core/TrimeInputMethodService.kt index 10791b8fdc..56a2250db5 100644 --- a/app/src/main/java/com/osfans/trime/ime/core/TrimeInputMethodService.kt +++ b/app/src/main/java/com/osfans/trime/ime/core/TrimeInputMethodService.kt @@ -6,10 +6,7 @@ package com.osfans.trime.ime.core import android.annotation.SuppressLint import android.annotation.TargetApi -import android.app.AlarmManager import android.app.Dialog -import android.app.PendingIntent -import android.content.Intent import android.content.IntentFilter import android.content.res.Configuration import android.graphics.RectF @@ -161,33 +158,6 @@ open class TrimeInputMethodService : LifecycleInputMethodService() { } } - /** 防止重启系统 强行停止应用时alarm任务丢失 */ - @SuppressLint("ScheduleExactAlarm") - fun restartSystemStartTimingSync() { - if (prefs.profile.timingBackgroundSyncEnabled) { - val triggerTime = prefs.profile.timingBackgroundSyncSetTime - val alarmManager = getSystemService(ALARM_SERVICE) as AlarmManager - - /** 设置待发送的同步事件 */ - val pendingIntent = - PendingIntent.getBroadcast( - this, - 0, - Intent("com.osfans.trime.timing.sync"), - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE - } else { - PendingIntent.FLAG_UPDATE_CURRENT - }, - ) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // 根据SDK设置alarm任务 - alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent) - } else { - alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent) - } - } - } - private fun registerReceiver() { val intentFilter = IntentFilter().apply { @@ -231,7 +201,6 @@ open class TrimeInputMethodService : LifecycleInputMethodService() { ColorManager.init(resources.configuration) ThemeManager.init() InputFeedbackManager.init() - restartSystemStartTimingSync() val theme = ThemeManager.activeTheme val defaultLocale = theme.generalStyle.locale.split(DELIMITER_SPLITTER) locales[0] = diff --git a/app/src/main/java/com/osfans/trime/ui/components/TimePickerPreference.kt b/app/src/main/java/com/osfans/trime/ui/components/TimePickerPreference.kt deleted file mode 100644 index c93938d14d..0000000000 --- a/app/src/main/java/com/osfans/trime/ui/components/TimePickerPreference.kt +++ /dev/null @@ -1,97 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015 - 2024 Rime community - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -package com.osfans.trime.ui.components - -import android.app.TimePickerDialog -import android.content.Context -import android.util.AttributeSet -import androidx.core.content.edit -import androidx.preference.DialogPreference -import java.util.Calendar -import java.util.concurrent.TimeUnit - -class TimePickerPreference - @JvmOverloads - constructor( - context: Context, - attrs: AttributeSet? = null, - ) : DialogPreference(context, attrs) { - var default: Long = System.currentTimeMillis() - - var value = System.currentTimeMillis() - private set - - override fun onSetInitialValue(defaultValue: Any?) { - super.onSetInitialValue(defaultValue) - preferenceDataStore?.apply { - value = getLong(key, default) - } ?: sharedPreferences?.apply { - value = getLong(key, default) - } - } - - override fun setDefaultValue(defaultValue: Any?) { - val time = defaultValue as? Long ?: return - value = time as? Long ?: System.currentTimeMillis() - } - - private fun persistValues(setTime: Long) { - if (!shouldPersist()) return - value = setTime - preferenceDataStore?.apply { - putLong(key, setTime) - } ?: sharedPreferences?.edit { - putLong(key, setTime) - } - } - - override fun onClick() { - showTimePickerDialog() - } - - private fun showTimePickerDialog() { - val cal = Calendar.getInstance() - val timeSetListener = // 监听时间选择器设置 - TimePickerDialog.OnTimeSetListener { _, hour, minute -> - cal.set(Calendar.HOUR_OF_DAY, hour) - cal.set(Calendar.MINUTE, minute) - val timeInMillis = cal.timeInMillis // 设置的时间 - val triggerAtMillis = - if (timeInMillis > System.currentTimeMillis() + PERIOD) { - timeInMillis - } else { - // 设置的时间小于当前时间 20 分钟时将同步推迟到明天 - timeInMillis + TimeUnit.DAYS.toMillis(1) - } - setValue(triggerAtMillis) - } - // 时间选择器设置 - TimePickerDialog( - context, - timeSetListener, - cal.get(Calendar.HOUR_OF_DAY), - cal.get(Calendar.MINUTE), - true, - ).apply { - setOnCancelListener { - // 当取消时间选择器时重置偏好 - setValue(0L) - } - show() - } - } - - private fun setValue(setTime: Long) { - if (callChangeListener(setTime)) { - persistValues(setTime) - notifyChanged() - } - } - - companion object { - private const val PERIOD = 1_200_000L // 20 分钟 - } - } diff --git a/app/src/main/java/com/osfans/trime/ui/fragments/ProfileFragment.kt b/app/src/main/java/com/osfans/trime/ui/fragments/ProfileFragment.kt index 826a0b4f22..c970b66a39 100644 --- a/app/src/main/java/com/osfans/trime/ui/fragments/ProfileFragment.kt +++ b/app/src/main/java/com/osfans/trime/ui/fragments/ProfileFragment.kt @@ -4,12 +4,6 @@ package com.osfans.trime.ui.fragments -import android.app.AlarmManager -import android.app.PendingIntent -import android.content.Intent -import android.content.SharedPreferences -import android.os.Build.VERSION -import android.os.Build.VERSION_CODES import android.os.Bundle import android.provider.DocumentsContract import androidx.activity.result.contract.ActivityResultContracts @@ -18,31 +12,32 @@ import androidx.fragment.app.activityViewModels import androidx.lifecycle.lifecycleScope import androidx.preference.Preference import androidx.preference.Preference.SummaryProvider +import androidx.preference.SwitchPreferenceCompat import androidx.preference.get import com.osfans.trime.R -import com.osfans.trime.core.Rime -import com.osfans.trime.daemon.RimeDaemon +import com.osfans.trime.daemon.launchOnReady import com.osfans.trime.data.base.DataManager import com.osfans.trime.data.prefs.AppPrefs +import com.osfans.trime.data.prefs.PreferenceDelegate import com.osfans.trime.ui.components.FolderPickerPreference import com.osfans.trime.ui.components.PaddingPreferenceFragment -import com.osfans.trime.ui.components.TimePickerPreference import com.osfans.trime.ui.components.withLoadingDialog import com.osfans.trime.ui.main.MainViewModel +import com.osfans.trime.ui.main.settings.EditTextIntPreference import com.osfans.trime.util.ResourceUtils import com.osfans.trime.util.appContext -import com.osfans.trime.util.formatDateTime +import com.osfans.trime.util.customFormatTimeInDefault import com.osfans.trime.util.toast import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import splitties.systemservices.alarmManager -class ProfileFragment : - PaddingPreferenceFragment(), - SharedPreferences.OnSharedPreferenceChangeListener { +class ProfileFragment : PaddingPreferenceFragment() { private val viewModel: MainViewModel by activityViewModels() - private val prefs get() = AppPrefs.defaultInstance() + private val prefs = AppPrefs.defaultInstance().profile + private val backgroundSyncEnable by prefs.periodicBackgroundSync + private val lastSyncTime by prefs.lastBackgroundSyncTime + private val lastSyncStatus by prefs.lastBackgroundSyncStatus private fun FolderPickerPreference.registerDocumentTreeLauncher() { documentTreeLauncher = @@ -57,46 +52,48 @@ class ProfileFragment : } } + private val onSyncIntervalChange = + PreferenceDelegate.OnChangeListener { _, _ -> + if (backgroundSyncEnable) { + viewModel.restartBackgroundSyncWork.value = true + } + } + override fun onCreatePreferences( savedInstanceState: Bundle?, rootKey: String?, ) { addPreferencesFromResource(R.xml.profile_preference) + prefs.periodicBackgroundSyncInterval.registerOnChangeListener(onSyncIntervalChange) with(preferenceScreen) { get("profile_user_data_dir")?.apply { setDefaultValue(DataManager.defaultDataDirectory.path) registerDocumentTreeLauncher() } - get("profile_sync_user_data")?.setOnPreferenceClickListener { + get("sync_user_data")?.setOnPreferenceClickListener { lifecycleScope.launch { - Rime.syncRimeUserData() - RimeDaemon.restartRime(true) + viewModel.rime.launchOnReady { it.syncUserData() } } true } - get("profile_timing_background_sync_set_time")?.apply { - setDefaultValue(false to System.currentTimeMillis()) + get("periodic_background_sync")?.apply { summaryProvider = - SummaryProvider { - if (prefs.profile.timingBackgroundSyncEnabled) { - val (lastTime, lastStatus) = - if (prefs.profile.lastBackgroundSyncTime != 0L) { - formatDateTime(prefs.profile.lastBackgroundSyncTime) to - context.getString(if (prefs.profile.lastSyncStatus) R.string.success else R.string.failure) - } else { - "N/A" to "N/A" - } - context.getString( - R.string.profile_timing_background_sync_status, - lastTime, - lastStatus, - formatDateTime(prefs.profile.timingBackgroundSyncSetTime), + SummaryProvider { + if (backgroundSyncEnable) { + getString( + R.string.periodic_background_sync_status, + customFormatTimeInDefault("yyyy-MM-dd HH:mm", lastSyncTime), + if (lastSyncStatus) getString(R.string.success) else getString(R.string.failure), ) } else { - context.getString(R.string.profile_timing_background_sync_hint) + "" } } } + get("periodic_background_sync_interval")?.apply { + min = 15 + summaryProvider = EditTextIntPreference.SimpleSummaryProvider + } get("profile_reset")?.setOnPreferenceClickListener { val base = "shared" val items = appContext.assets.list(base)!! @@ -129,53 +126,8 @@ class ProfileFragment : } } - override fun onSharedPreferenceChanged( - sharedPreferences: SharedPreferences?, - key: String?, - ) { - // 监听定时同步偏好设置 - // 设置待发送的同步事件 - val pendingIntent = - PendingIntent.getBroadcast( - context, - 0, - Intent("com.osfans.trime.timing.sync"), - if (VERSION.SDK_INT >= VERSION_CODES.M) { - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE - } else { - PendingIntent.FLAG_UPDATE_CURRENT - }, - ) - if (prefs.profile.timingBackgroundSyncEnabled) { - val timeAtMillis = prefs.profile.timingBackgroundSyncSetTime - if (VERSION.SDK_INT < VERSION_CODES.S || alarmManager.canScheduleExactAlarms()) { - if (VERSION.SDK_INT >= VERSION_CODES.M) { // 根据 API Level 设置 alarm 任务 - alarmManager.setExactAndAllowWhileIdle( - AlarmManager.RTC_WAKEUP, - timeAtMillis, - pendingIntent, - ) - } else { - alarmManager.setExact( - AlarmManager.RTC_WAKEUP, - timeAtMillis, - pendingIntent, - ) - } - } - } else { - alarmManager.cancel(pendingIntent) - } - } - - override fun onResume() { - super.onResume() - viewModel.disableTopOptionsMenu() - preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this) - } - override fun onPause() { super.onPause() - preferenceScreen.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this) + prefs.periodicBackgroundSyncInterval.unregisterOnChangeListener(onSyncIntervalChange) } } diff --git a/app/src/main/java/com/osfans/trime/ui/main/MainViewModel.kt b/app/src/main/java/com/osfans/trime/ui/main/MainViewModel.kt index 15ebb4ec57..ad47b5f1c0 100644 --- a/app/src/main/java/com/osfans/trime/ui/main/MainViewModel.kt +++ b/app/src/main/java/com/osfans/trime/ui/main/MainViewModel.kt @@ -16,6 +16,8 @@ class MainViewModel : ViewModel() { val rime: RimeSession = RimeDaemon.createSession(javaClass.name) + val restartBackgroundSyncWork = MutableLiveData(false) + fun setToolbarTitle(title: String) { toolbarTitle.value = title } diff --git a/app/src/main/java/com/osfans/trime/ui/main/PrefMainActivity.kt b/app/src/main/java/com/osfans/trime/ui/main/PrefMainActivity.kt index 4b4593b7ef..f05fb92b32 100644 --- a/app/src/main/java/com/osfans/trime/ui/main/PrefMainActivity.kt +++ b/app/src/main/java/com/osfans/trime/ui/main/PrefMainActivity.kt @@ -5,9 +5,7 @@ package com.osfans.trime.ui.main import android.content.Intent -import android.os.Build import android.os.Bundle -import android.provider.Settings import android.view.Menu import android.view.MenuItem import android.view.ViewGroup @@ -37,8 +35,8 @@ import com.osfans.trime.databinding.ActivityPrefBinding import com.osfans.trime.ui.components.ProgressBarDialogIndeterminate import com.osfans.trime.ui.setup.SetupActivity import com.osfans.trime.util.isStorageAvailable +import com.osfans.trime.worker.BackgroundSyncWork import kotlinx.coroutines.launch -import splitties.systemservices.alarmManager import splitties.views.topPadding class PrefMainActivity : AppCompatActivity() { @@ -109,7 +107,6 @@ class PrefMainActivity : AppCompatActivity() { startActivity(Intent(this, SetupActivity::class.java)) } - checkScheduleExactAlarmPermission() checkNotificationPermission() lifecycleScope.launch { @@ -153,6 +150,14 @@ class PrefMainActivity : AppCompatActivity() { else -> super.onOptionsItemSelected(item) } + override fun onPause() { + super.onPause() + if (viewModel.restartBackgroundSyncWork.value == true) { + viewModel.restartBackgroundSyncWork.value = false + BackgroundSyncWork.forceStart(this) + } + } + override fun onResume() { super.onResume() if (isStorageAvailable()) { @@ -160,20 +165,6 @@ class PrefMainActivity : AppCompatActivity() { } } - private fun checkScheduleExactAlarmPermission() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !alarmManager.canScheduleExactAlarms()) { - AlertDialog - .Builder(this) - .setIconAttribute(android.R.attr.alertDialogIcon) - .setTitle(R.string.schedule_exact_alarm_permission_title) - .setMessage(R.string.schedule_exact_alarm_permission_message) - .setPositiveButton(R.string.grant_permission) { _, _ -> - startActivity(Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM)) - }.setNegativeButton(android.R.string.cancel, null) - .show() - } - } - private fun checkNotificationPermission() { if (XXPermissions.isGranted(this, Permission.POST_NOTIFICATIONS)) { return diff --git a/app/src/main/java/com/osfans/trime/ui/main/settings/EditTextIntPreference.kt b/app/src/main/java/com/osfans/trime/ui/main/settings/EditTextIntPreference.kt new file mode 100644 index 0000000000..36c00b791c --- /dev/null +++ b/app/src/main/java/com/osfans/trime/ui/main/settings/EditTextIntPreference.kt @@ -0,0 +1,92 @@ +/* + * SPDX-FileCopyrightText: 2015 - 2025 Rime community + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package com.osfans.trime.ui.main.settings + +import android.content.Context +import android.content.res.TypedArray +import android.os.Build +import android.text.InputType +import android.text.method.DigitsKeyListener +import android.util.AttributeSet +import androidx.preference.EditTextPreference +import java.util.Locale + +class EditTextIntPreference + @JvmOverloads + constructor( + context: Context, + attrs: AttributeSet?, + defStyleAttr: Int = androidx.preference.R.attr.editTextPreferenceStyle, + ) : EditTextPreference(context, attrs, defStyleAttr) { + private var value = 0 + var min: Int = Int.MIN_VALUE + var max: Int = Int.MAX_VALUE + var unit: String = "" + + private var default: Int = 0 + + override fun persistInt(value: Int): Boolean = + super.persistInt(value).also { + if (it) this@EditTextIntPreference.value = value + } + + // it appears as an "Int" Preference to the user, we want to accept Int for defaultValue + override fun setDefaultValue(defaultValue: Any?) { + val value = defaultValue as? Int ?: return + default = value + // the underlying Preference is an "EditText", we must use String for it's defaultValue + super.setDefaultValue(value.toString()) + } + + override fun onGetDefaultValue( + a: TypedArray, + index: Int, + ): Any = a.getInteger(index, default) + + override fun onSetInitialValue(defaultValue: Any?) { + value = getPersistedInt(default) + } + + private fun textForValue(): String = getPersistedInt(value).toString() + + init { + setOnBindEditTextListener { + it.setText(textForValue()) + it.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL + it.keyListener = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + DigitsKeyListener.getInstance(Locale.ROOT, min < 0, false) + } else { + @Suppress("DEPRECATION") + DigitsKeyListener.getInstance(min < 0, false) + } + } + } + + private fun fitValue(v: Int) = + if (v < min) { + min + } else if (v > max) { + max + } else { + v + } + + override fun setText(text: String?) { + val value = text?.toIntOrNull(10) ?: return + persistInt(fitValue(value)) + notifyChanged() + } + + override fun callChangeListener(newValue: Any?): Boolean { + if (newValue !is String) return false + val value = newValue.toIntOrNull(10) ?: return false + return super.callChangeListener(fitValue(value)) + } + + object SimpleSummaryProvider : SummaryProvider { + override fun provideSummary(preference: EditTextIntPreference): CharSequence = preference.run { "${textForValue()} $unit" } + } + } diff --git a/app/src/main/java/com/osfans/trime/util/DateTime.kt b/app/src/main/java/com/osfans/trime/util/DateTime.kt index b5af80fc25..99cfd2dacc 100644 --- a/app/src/main/java/com/osfans/trime/util/DateTime.kt +++ b/app/src/main/java/com/osfans/trime/util/DateTime.kt @@ -24,6 +24,11 @@ private val iso8601DateFormat by lazy { fun iso8601UTCDateTime(timeMillis: Long? = null): String = iso8601DateFormat.format(timeMillis?.let { Date(it) } ?: Date()) +fun customFormatTimeInDefault( + pattern: String, + timeMillis: Long? = null, +): String = SimpleDateFormat(pattern, Locale.getDefault()).format(timeMillis?.let { Date(it) } ?: Date()) + fun customFormatDateTime( pattern: String, timeMillis: Long? = null, diff --git a/app/src/main/java/com/osfans/trime/worker/BackgroundSyncWork.kt b/app/src/main/java/com/osfans/trime/worker/BackgroundSyncWork.kt new file mode 100644 index 0000000000..8b42dc3d98 --- /dev/null +++ b/app/src/main/java/com/osfans/trime/worker/BackgroundSyncWork.kt @@ -0,0 +1,96 @@ +/* + * SPDX-FileCopyrightText: 2015 - 2025 Rime community + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.osfans.trime.worker + +import android.content.Context +import androidx.work.Constraints +import androidx.work.CoroutineWorker +import androidx.work.ExistingPeriodicWorkPolicy +import androidx.work.PeriodicWorkRequestBuilder +import androidx.work.WorkManager +import androidx.work.WorkerParameters +import com.osfans.trime.daemon.RimeDaemon +import com.osfans.trime.data.prefs.AppPrefs +import timber.log.Timber +import java.util.concurrent.TimeUnit + +class BackgroundSyncWork( + context: Context, + workerParams: WorkerParameters, +) : CoroutineWorker(context, workerParams) { + override suspend fun doWork(): Result { + try { + Timber.i("Starting background sync ...") + return doBackgroundSync() + } catch (e: Exception) { + Timber.e(e, "Background sync job failed.") + return Result.retry() + } + } + + private suspend fun doBackgroundSync(): Result { + if (!enable) { + return Result.failure() + } + val rime = RimeDaemon.getFirstSessionOrNull() ?: return Result.retry() + val success = rime.runOnReady { syncUserData() } + lastSyncTime = System.currentTimeMillis() + lastSyncStatus = success + + return if (success) Result.success() else Result.retry() + } + + companion object { + private const val PERIODIC_BACKGROUND_SYNC_KEY = "periodic_background_sync" + + private val prefs = AppPrefs.defaultInstance().profile + private val enable by prefs.periodicBackgroundSync + private val interval by prefs.periodicBackgroundSyncInterval + private var lastSyncStatus by prefs.lastBackgroundSyncStatus + private var lastSyncTime by prefs.lastBackgroundSyncTime + + fun start(context: Context) { + Timber.i("BackgroundSyncWork scheduled!") + internalStart(context, ExistingPeriodicWorkPolicy.UPDATE) + } + + fun forceStart(context: Context) { + internalStart(context, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE) + } + + private fun internalStart( + context: Context, + policy: ExistingPeriodicWorkPolicy, + ) { + val instance = WorkManager.getInstance(context.applicationContext) + if (!enable) { + instance.cancelUniqueWork(PERIODIC_BACKGROUND_SYNC_KEY) + Timber.i("BackgroundSyncWork canceled!") + return + } + val constraints = + Constraints + .Builder() + .setRequiresBatteryNotLow(true) + .setRequiresStorageNotLow(true) + .build() + + val workRequest = + PeriodicWorkRequestBuilder( + interval.toLong(), + TimeUnit.MINUTES, + 5, + TimeUnit.MINUTES, + ).setConstraints(constraints) + .build() + instance.enqueueUniquePeriodicWork( + PERIODIC_BACKGROUND_SYNC_KEY, + policy, + workRequest, + ) + } + } +} diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 1543e29736..16da138d82 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -57,7 +57,7 @@ SPDX-License-Identifier: GPL-3.0-or-later 悬浮窗编码区使用插入符号(^) 部署 - 同步用户数据 + 立即同步用户数据 重置成功 重置失败 正在部署… @@ -68,9 +68,8 @@ SPDX-License-Identifier: GPL-3.0-or-later 显示通知栏图标 退出时清除缓存 用户文件夹 - 后台定时同步 - 上次同步时间:%1$s(%2$s)\n下次同步时间: %3$s - 后台定时同步已禁用 + 后台定时同步 + 上次同步时间(状态):%1$s(%2$s) 应用市场 用户社区 按键效果 @@ -160,7 +159,6 @@ SPDX-License-Identifier: GPL-3.0-or-later 失败 设定存储位置和修改同步设置等 用默认的内置配置覆盖共享目录中的相同文件 - 备份配置文件和同步用户词典 删除 编辑 收藏 @@ -189,8 +187,6 @@ SPDX-License-Identifier: GPL-3.0-or-later 当前同文需要存储权限以访问配置、使得用户容易更改。 请求存储权限 授予权限 - 没有闹钟和提醒权限 - 没有闹钟和提醒权限,我们无法在启用定时同步时及时为您同步数据配置。 没有通知权限 没有发送通知的权限,我们无法在一些耗时操作完成时通知您。 Rime 守护程序 @@ -215,7 +211,7 @@ SPDX-License-Identifier: GPL-3.0-or-later 设置向导 忘记该词 同步 - 设置后台同步时间 + 后台同步时间间隔(以分钟为单位) 候选窗口 显示候选词窗口 常规 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 58138bc0be..1101d8e4b2 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -60,8 +60,7 @@ SPDX-License-Identifier: GPL-3.0-or-later 懸浮窗編碼區使用插入符號(^) 部署 - 同步使用者資料 - 備份配置檔案和同步使用者詞典 + 立即同步使用者資料 回廠成功 回廠失敗 正在部署… @@ -72,9 +71,8 @@ SPDX-License-Identifier: GPL-3.0-or-later 顯示通知欄圖標 離開時清理記憶體 使用者資料夾 - 後台定時同步 - 上次同步時間:%1$s(%2$s)\n下次同步時間: %3$s - 後台定時同步已禁用 + 後台定時同步 + 上次同步時間(狀態):%1$s(%2$s) 應用市場 使用者社群 檢視 @@ -189,8 +187,6 @@ SPDX-License-Identifier: GPL-3.0-or-later 當前同文須要存儲權限以訪問配置檔案、使得使用者容易修改。 请求存储权限 授予權限 - 沒有鬧鐘和提醒權限 - 尚未賦予鬧鐘和提醒權限,因此無法在啟用定時同步時及時為您同步資料配置。 沒有通知權限 尚未賦予通知權限,因此無法在耗時較長的操作完成後給您發送通知。 Rime 常駐程式 @@ -215,7 +211,7 @@ SPDX-License-Identifier: GPL-3.0-or-later 設定精靈 忘記該詞 同步 - 設定後台同步時間 + 後台同步時間間隔(以分鐘為單位) 候选視窗 顯示候選詞視窗 常規 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9806e9ba7a..d4b708db04 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -60,8 +60,7 @@ SPDX-License-Identifier: GPL-3.0-or-later Use soft cursor(^) in composition Deploy - Sync user data - Backup config files and user dict sync + Sync user data immediately Reset Succeeded! Reset Failed! Deploying… @@ -72,9 +71,8 @@ SPDX-License-Identifier: GPL-3.0-or-later Show icon in notification bar Free memory on quit User directory - Timing background sync - Last: %1$s (%2$s)\nNext: %3$s - Timing background sync is disabled + Periodic background sync + Last: %1$s (%2$s) Marketplace User Community View @@ -189,8 +187,6 @@ SPDX-License-Identifier: GPL-3.0-or-later Currently Trime requests storage permmissions to access its configuration\nand allow users to modify easily. Request storage permmision Grant permission - No Schedule Exact Alarm Permmision - Without the permmission to schedule exact alarm, we are not able to sync your profile in time when you enable syncing on schedule. No Notification Permission Without the permission to post notifications, we are not able to notify you when some time-consuming operations are done. Rime Daemon @@ -215,7 +211,7 @@ SPDX-License-Identifier: GPL-3.0-or-later Setup Wizard Forget this word Synchronization - Set background sync time + Interval of background syncing (in minutes) Candidates Window Show candidates window General diff --git a/app/src/main/res/xml/profile_preference.xml b/app/src/main/res/xml/profile_preference.xml index ec219b563f..6fbf144164 100644 --- a/app/src/main/res/xml/profile_preference.xml +++ b/app/src/main/res/xml/profile_preference.xml @@ -22,21 +22,21 @@ SPDX-License-Identifier: GPL-3.0-or-later - + android:title="@string/sync_user_data_immediately" /> + android:title="@string/periodic_background_sync" /> - + android:title="@string/periodic_background_sync_interval" + android:defaultValue="30" + app:dependency="periodic_background_sync" />