diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4312c28ecb..503df5ef24 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,7 +13,6 @@ SPDX-License-Identifier: GPL-3.0-or-later - diff --git a/app/src/main/assets/shared/trime.yaml b/app/src/main/assets/shared/trime.yaml index 595d6cf432..823c62e280 100644 --- a/app/src/main/assets/shared/trime.yaml +++ b/app/src/main/assets/shared/trime.yaml @@ -984,8 +984,8 @@ preset_keys: WebSearch: {label: 搜索網頁, command: web_search, option: "%4$s"} #搜索,其他view、dial、edit、search等intent,參考安卓的intent文檔:https://developer.android.com/reference/android/content/Intent.html Search: {label: 搜索, command: search, option: "%1$s"} #搜索短信、字典等 Share: {label: 分享, command: send, option: "%s"} #分享指定文本: %s或者%1$s爲當前字符 - Deploy: {label: 部署, command: broadcast, option: "com.osfans.trime.deploy"} - Sync: {label: 同步, command: broadcast, option: "com.osfans.trime.sync"} + Deploy: {label: 部署, command: broadcast, option: "com.osfans.trime.DEPLOY"} + Sync: {label: 同步, command: broadcast, option: "com.osfans.trime.SYNC"} RepeatCommit: { label: 重复, command: commit, option: "%1$s" } #重复输入刚上屏的内容 preset_keyboards: diff --git a/app/src/main/java/com/osfans/trime/TrimeApplication.kt b/app/src/main/java/com/osfans/trime/TrimeApplication.kt index 41d6954a14..23e022c479 100644 --- a/app/src/main/java/com/osfans/trime/TrimeApplication.kt +++ b/app/src/main/java/com/osfans/trime/TrimeApplication.kt @@ -9,13 +9,17 @@ import android.content.Intent import android.os.Process import android.util.Log import androidx.preference.PreferenceManager +import androidx.work.Configuration +import androidx.work.WorkManager import com.osfans.trime.data.db.ClipboardHelper import com.osfans.trime.data.db.CollectionHelper import com.osfans.trime.data.db.DraftHelper import com.osfans.trime.data.prefs.AppPrefs import com.osfans.trime.ui.main.LogActivity +import com.osfans.trime.worker.BackgroundSyncWork import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.MainScope +import kotlinx.coroutines.launch import kotlinx.coroutines.plus import timber.log.Timber import kotlin.system.exitProcess @@ -116,9 +120,29 @@ class TrimeApplication : Application() { ClipboardHelper.init(applicationContext) CollectionHelper.init(applicationContext) DraftHelper.init(applicationContext) + + initializeWorkManagerIfAndroidForgotIt() + startWorkManager() } catch (e: Exception) { e.fillInStackTrace() return } } + + private fun initializeWorkManagerIfAndroidForgotIt() { + if (!WorkManager.isInitialized()) { + WorkManager.initialize( + applicationContext, + Configuration + .Builder() + .build(), + ) + } + } + + private fun startWorkManager() { + coroutineScope.launch { + BackgroundSyncWork.start(applicationContext) + } + } } diff --git a/app/src/main/java/com/osfans/trime/core/Rime.kt b/app/src/main/java/com/osfans/trime/core/Rime.kt index 080c752e5c..58654a67f1 100644 --- a/app/src/main/java/com/osfans/trime/core/Rime.kt +++ b/app/src/main/java/com/osfans/trime/core/Rime.kt @@ -148,6 +148,11 @@ class Rime : getRimeCandidates(startIndex, limit) ?: emptyArray() } + override suspend fun syncUserData(): Boolean = + withRimeContext { + syncRimeUserData() + } + private fun handleRimeNotification(notif: RimeNotification<*>) { when (notif) { is RimeNotification.SchemaNotification -> schemaItemCached = notif.value diff --git a/app/src/main/java/com/osfans/trime/core/RimeApi.kt b/app/src/main/java/com/osfans/trime/core/RimeApi.kt index dbb493ea96..d55a8dbc91 100644 --- a/app/src/main/java/com/osfans/trime/core/RimeApi.kt +++ b/app/src/main/java/com/osfans/trime/core/RimeApi.kt @@ -64,4 +64,6 @@ interface RimeApi { startIndex: Int, limit: Int, ): Array + + suspend fun syncUserData(): Boolean } diff --git a/app/src/main/java/com/osfans/trime/daemon/RimeDaemon.kt b/app/src/main/java/com/osfans/trime/daemon/RimeDaemon.kt index 27cd32973c..8d1993edc0 100644 --- a/app/src/main/java/com/osfans/trime/daemon/RimeDaemon.kt +++ b/app/src/main/java/com/osfans/trime/daemon/RimeDaemon.kt @@ -147,4 +147,6 @@ object RimeDaemon { } } } + + suspend fun syncUserData() = realRime.syncUserData() } diff --git a/app/src/main/java/com/osfans/trime/ime/broadcast/IntentReceiver.kt b/app/src/main/java/com/osfans/trime/ime/broadcast/IntentReceiver.kt index a4413bf979..0324386066 100644 --- a/app/src/main/java/com/osfans/trime/ime/broadcast/IntentReceiver.kt +++ b/app/src/main/java/com/osfans/trime/ime/broadcast/IntentReceiver.kt @@ -4,101 +4,43 @@ package com.osfans.trime.ime.broadcast -import android.app.AlarmManager -import android.app.PendingIntent import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter -import android.os.Build.VERSION -import android.os.Build.VERSION_CODES -import android.os.PowerManager.PARTIAL_WAKE_LOCK import android.widget.Toast import androidx.core.content.ContextCompat +import androidx.lifecycle.lifecycleScope import com.osfans.trime.R -import com.osfans.trime.core.Rime import com.osfans.trime.daemon.RimeDaemon -import com.osfans.trime.data.prefs.AppPrefs +import com.osfans.trime.ime.core.TrimeInputMethodService import com.osfans.trime.util.toast -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import splitties.systemservices.alarmManager -import splitties.systemservices.powerManager import timber.log.Timber -import java.util.Calendar -import java.util.concurrent.TimeUnit /** 接收 Intent 廣播事件 */ -class IntentReceiver : - BroadcastReceiver(), - CoroutineScope by MainScope() { +class IntentReceiver( + private val service: TrimeInputMethodService, +) : BroadcastReceiver() { override fun onReceive( context: Context, intent: Intent, ) { val command = intent.action ?: return - Timber.d("Received Command = %s", command) + Timber.d("Received command: $command") when (command) { COMMAND_DEPLOY -> - launch { + service.lifecycleScope.launch { withContext(Dispatchers.IO) { RimeDaemon.restartRime(true) } context.toast(R.string.deploy_finish, Toast.LENGTH_LONG) } COMMAND_SYNC -> - launch { - withContext(Dispatchers.IO) { - Rime.syncRimeUserData() - RimeDaemon.restartRime(true) - } - } - COMMAND_TIMING_SYNC -> - launch { - withContext(Dispatchers.IO) { - // 获取唤醒锁 - val wakeLock = - powerManager.newWakeLock(PARTIAL_WAKE_LOCK, "com.osfans.trime:WakeLock") - wakeLock.acquire(600000) // 10分钟超时 - val cal = Calendar.getInstance() - val triggerTime = cal.timeInMillis + TimeUnit.DAYS.toMillis(1) // 下次同步时间 - AppPrefs.defaultInstance().profile.timingBackgroundSyncSetTime = - triggerTime // 更新定时同步偏好值 - // 设置待发送的同步事件 - val pendingIntent = - PendingIntent.getBroadcast( - context, - 0, - Intent(COMMAND_TIMING_SYNC), - if (VERSION.SDK_INT >= VERSION_CODES.M) { - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE - } else { - PendingIntent.FLAG_UPDATE_CURRENT - }, - ) - if (VERSION.SDK_INT < VERSION_CODES.S || alarmManager.canScheduleExactAlarms()) { - if (VERSION.SDK_INT >= VERSION_CODES.M) { // 根据SDK设置alarm任务 - alarmManager.setExactAndAllowWhileIdle( - AlarmManager.RTC_WAKEUP, - triggerTime, - pendingIntent, - ) - } else { - alarmManager.setExact( - AlarmManager.RTC_WAKEUP, - triggerTime, - pendingIntent, - ) - } - } - - Rime.syncRimeUserData() - RimeDaemon.restartRime(true) - wakeLock.release() // 释放唤醒锁 - } + service.lifecycleScope.launch(Dispatchers.IO) { + RimeDaemon.syncUserData() } else -> return } @@ -117,12 +59,6 @@ class IntentReceiver : IntentFilter(COMMAND_SYNC), ContextCompat.RECEIVER_NOT_EXPORTED, ) - ContextCompat.registerReceiver( - context, - this, - IntentFilter(COMMAND_TIMING_SYNC), - ContextCompat.RECEIVER_NOT_EXPORTED, - ) context.registerReceiver(this, IntentFilter(Intent.ACTION_SHUTDOWN)) } @@ -131,8 +67,7 @@ class IntentReceiver : } companion object { - private const val COMMAND_DEPLOY = "com.osfans.trime.deploy" - private const val COMMAND_SYNC = "com.osfans.trime.sync" - private const val COMMAND_TIMING_SYNC = "com.osfans.trime.timing.sync" + private const val COMMAND_DEPLOY = "com.osfans.trime.DEPLOY" + private const val COMMAND_SYNC = "com.osfans.trime.SYNC" } } 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 9b9407af0e..0d6dbf136f 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 @@ -226,7 +226,7 @@ open class TrimeInputMethodService : LifecycleInputMethodService() { // and lead to a crash loop Timber.d("onCreate") mIntentReceiver = - IntentReceiver().also { + IntentReceiver(this).also { it.registerReceiver(this) } postRimeJob { 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 d953a8a291..b147030c1d 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 @@ -20,7 +14,6 @@ import androidx.preference.Preference import androidx.preference.Preference.SummaryProvider 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.data.base.DataManager import com.osfans.trime.data.prefs.AppPrefs @@ -38,11 +31,8 @@ import com.osfans.trime.util.withLoadingDialog 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() @@ -71,9 +61,8 @@ class ProfileFragment : } get("profile_sync_user_data")?.setOnPreferenceClickListener { lifecycleScope.launch { - this@ProfileFragment.context?.rimeActionWithResultDialog("rime.trime", "W", 1) { - Rime.syncRimeUserData() - RimeDaemon.restartRime(true) + requireContext().rimeActionWithResultDialog("rime.trime", "W", 1) { + RimeDaemon.syncUserData() true } } @@ -134,54 +123,9 @@ 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.setToolbarTitle(getString(R.string.pref_profile)) viewModel.disableTopOptionsMenu() - preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this) - } - - override fun onPause() { - super.onPause() - preferenceScreen.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this) } } 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 939e72bf32..9f7a081ddd 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 @@ -6,9 +6,7 @@ package com.osfans.trime.ui.main import android.app.AlertDialog 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 @@ -40,7 +38,6 @@ import com.osfans.trime.util.isStorageAvailable import com.osfans.trime.util.progressBarDialogIndeterminate import com.osfans.trime.util.rimeActionWithResultDialog import kotlinx.coroutines.launch -import splitties.systemservices.alarmManager import splitties.views.topPadding class PrefMainActivity : AppCompatActivity() { @@ -111,7 +108,6 @@ class PrefMainActivity : AppCompatActivity() { startActivity(Intent(this, SetupActivity::class.java)) } - checkScheduleExactAlarmPermission() checkNotificationPermission() lifecycleScope.launch { @@ -176,20 +172,6 @@ class PrefMainActivity : AppCompatActivity() { loadingDialog = null } - 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/util/ShortcutUtils.kt b/app/src/main/java/com/osfans/trime/util/ShortcutUtils.kt index 6002671b1c..2b8c522459 100644 --- a/app/src/main/java/com/osfans/trime/util/ShortcutUtils.kt +++ b/app/src/main/java/com/osfans/trime/util/ShortcutUtils.kt @@ -16,17 +16,11 @@ import android.os.Build import android.text.TextUtils import android.util.SparseArray import android.view.KeyEvent -import com.osfans.trime.core.Rime -import com.osfans.trime.daemon.RimeDaemon -import com.osfans.trime.data.prefs.AppPrefs import com.osfans.trime.ime.core.TrimeInputMethodService import com.osfans.trime.ime.symbol.SymbolBoardType import com.osfans.trime.ui.main.LiquidKeyboardEditActivity import com.osfans.trime.ui.main.LogActivity import com.osfans.trime.ui.main.PrefMainActivity -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import splitties.systemservices.clipboardManager import timber.log.Timber import java.text.FieldPosition @@ -141,14 +135,6 @@ object ShortcutUtils { @JvmStatic fun pasteFromClipboard(context: Context): CharSequence? = clipboardManager.primaryClip?.getItemAt(0)?.coerceToText(context) - fun syncInBackground() { - val prefs = AppPrefs.defaultInstance() - prefs.profile.lastBackgroundSyncTime = Date().time - CoroutineScope(Dispatchers.IO).launch { - prefs.profile.lastSyncStatus = Rime.syncRimeUserData().also { RimeDaemon.restartRime() } - } - } - fun Context.openCategory(keyCode: Int): Boolean { val category = applicationLaunchKeyCategories[keyCode] return if (!category.isNullOrEmpty()) { diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 719f7f5c59..b057ea3b32 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -239,8 +239,6 @@ SPDX-License-Identifier: GPL-3.0-or-later 当前同文需要存储权限以访问配置、使得用户容易更改。 请求存储权限 授予权限 - 没有闹钟和提醒权限 - 没有闹钟和提醒权限,我们无法在启用定时同步时及时为您同步数据配置。 没有通知权限 没有发送通知的权限,我们无法在一些耗时操作完成时通知您。 Rime 守护程序 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index d47591fb8d..71564f7d74 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -240,8 +240,6 @@ SPDX-License-Identifier: GPL-3.0-or-later 當前同文須要存儲權限以訪問配置檔案、使得使用者容易修改。 请求存储权限 授予權限 - 沒有鬧鐘和提醒權限 - 尚未賦予鬧鐘和提醒權限,因此無法在啟用定時同步時及時為您同步資料配置。 沒有通知權限 尚未賦予通知權限,因此無法在耗時較長的操作完成後給您發送通知。 Rime 常駐程式 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d0d257f387..b147b93669 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -240,8 +240,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 diff --git a/app/src/test/assets/trime.yaml b/app/src/test/assets/trime.yaml index fc6e492b79..f6cb86814e 100644 --- a/app/src/test/assets/trime.yaml +++ b/app/src/test/assets/trime.yaml @@ -994,8 +994,8 @@ preset_keys: WebSearch: {label: 搜索網頁, command: web_search, option: "%4$s"} #搜索,其他view、dial、edit、search等intent,參考安卓的intent文檔:https://developer.android.com/reference/android/content/Intent.html Search: {label: 搜索, command: search, option: "%1$s"} #搜索短信、字典等 Share: {label: 分享, command: send, option: "%s"} #分享指定文本: %s或者%1$s爲當前字符 - Deploy: {label: 部署, command: broadcast, option: "com.osfans.trime.deploy"} - Sync: {label: 同步, command: broadcast, option: "com.osfans.trime.sync"} + Deploy: {label: 部署, command: broadcast, option: "com.osfans.trime.DEPLOY"} + Sync: {label: 同步, command: broadcast, option: "com.osfans.trime.SYNC"} RepeatCommit: { label: 重复, command: commit, option: "%1$s" } #重复输入刚上屏的内容 preset_keyboards: