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: