diff --git a/app/src/main/java/one/mixin/android/extension/ContextExtension.kt b/app/src/main/java/one/mixin/android/extension/ContextExtension.kt index 8da191c7dc..a6fa8530ce 100644 --- a/app/src/main/java/one/mixin/android/extension/ContextExtension.kt +++ b/app/src/main/java/one/mixin/android/extension/ContextExtension.kt @@ -34,6 +34,7 @@ import android.os.Build import android.os.Bundle import android.os.Handler import android.os.Looper +import android.os.PowerManager import android.os.VibrationEffect import android.os.VibrationEffect.EFFECT_CLICK import android.os.VibrationEffect.EFFECT_DOUBLE_CLICK @@ -91,6 +92,8 @@ import one.mixin.android.util.RomPermissionUtil import one.mixin.android.util.XiaomiUtilities import one.mixin.android.util.blurhash.BlurHashEncoder import one.mixin.android.util.getChainName +import one.mixin.android.util.isPowerManagerBatteryOptimizationRestricted +import one.mixin.android.util.shouldPrioritizeBatteryOptimizationRequest import one.mixin.android.util.video.MediaController import one.mixin.android.util.video.VideoEditedInfo import one.mixin.android.vo.ChatHistoryMessageItem @@ -1359,40 +1362,62 @@ fun Context.getStringDeviceId(): String { return getStringDeviceId(contentResolver) } -fun Context.handleIgnoreBatteryOptimization(newTask: Boolean = false) { - if (Build.MANUFACTURER.equalsIgnoreCase("google") || Build.MANUFACTURER.equalsIgnoreCase("samsung")) { - requestIgnoreBatteryOptimization(newTask) +fun Context.isBatteryOptimizationRestricted(): Boolean { + return isSystemBatteryOptimizationRestricted() +} + +private fun Context.isSystemBatteryOptimizationRestricted(): Boolean { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !RomUtil.isEmui) { + getSystemService()?.isBackgroundRestricted == true } else { - openIgnoreBatteryOptimizationSetting(newTask) + isPowerManagerBatteryOptimizationRestricted( + getSystemService()?.isIgnoringBatteryOptimizations(packageName), + ) } } -fun Context.requestIgnoreBatteryOptimization(newTask: Boolean = false) { - Intent().apply { - action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS - data = Uri.parse("package:$packageName") - if (newTask) { - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) +@SuppressLint("BatteryLife") +fun Context.openBatteryOptimizationSetting() { + val appDetailsIntent = + Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { + data = Uri.parse("package:$packageName") } - try { - startActivity(this) - } catch (e: ActivityNotFoundException) { - Timber.w("Battery optimization activity not found") + val requestIntent = + Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply { + data = Uri.parse("package:$packageName") } + val romType = RomPermissionUtil.getCurrentRomType() + val intents = + if (romType == RomPermissionUtil.RomType.SAMSUNG) { + listOf(appDetailsIntent, requestIntent) + } else if (shouldPrioritizeBatteryOptimizationRequest(romType)) { + listOf(requestIntent, appDetailsIntent) + } else { + listOf(appDetailsIntent, requestIntent) + } + + if (!intents.any(::tryStartActivity)) { + Timber.w("Battery optimization page activity not found") } } -fun Context.openIgnoreBatteryOptimizationSetting(newTask: Boolean = false) { - Intent().apply { - action = Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS - if (newTask) { - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - } - try { - startActivity(this) - } catch (e: ActivityNotFoundException) { - Timber.w("Power setting activity not found") +private fun Context.tryStartActivity(intent: Intent): Boolean { + val launchIntent = + Intent(intent).apply { + if (this@tryStartActivity !is Activity) { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } } + if (launchIntent.resolveActivity(packageManager) == null) { + return false + } + return try { + startActivity(launchIntent) + true + } catch (e: ActivityNotFoundException) { + false + } catch (e: SecurityException) { + false } } diff --git a/app/src/main/java/one/mixin/android/job/BlazeMessageService.kt b/app/src/main/java/one/mixin/android/job/BlazeMessageService.kt index 5ca31ef1c6..0e02f3df9a 100644 --- a/app/src/main/java/one/mixin/android/job/BlazeMessageService.kt +++ b/app/src/main/java/one/mixin/android/job/BlazeMessageService.kt @@ -1,18 +1,14 @@ package one.mixin.android.job import android.annotation.SuppressLint -import android.app.ActivityManager import android.app.PendingIntent import android.content.Context import android.content.Intent import android.content.pm.ServiceInfo -import android.os.Build import android.os.IBinder -import android.os.PowerManager import androidx.core.app.NotificationCompat import androidx.core.app.ServiceCompat import androidx.core.content.ContextCompat -import androidx.core.content.getSystemService import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleService import androidx.lifecycle.lifecycleScope @@ -47,6 +43,7 @@ import one.mixin.android.db.pending.PendingDatabase import one.mixin.android.event.ExpiredEvent import one.mixin.android.extension.base64Encode import one.mixin.android.extension.currentTimeSeconds +import one.mixin.android.extension.isBatteryOptimizationRestricted import one.mixin.android.extension.networkConnected import one.mixin.android.extension.notificationManager import one.mixin.android.extension.supportsOreo @@ -62,7 +59,6 @@ import one.mixin.android.ui.common.BatteryOptimizationDialogActivity import one.mixin.android.ui.home.MainActivity import one.mixin.android.util.ChannelManager.Companion.createNodeChannel import one.mixin.android.util.GsonHelper -import one.mixin.android.util.RomUtil import one.mixin.android.util.reportException import one.mixin.android.vo.CallStateLiveData import one.mixin.android.vo.MessageStatus @@ -164,8 +160,6 @@ class BlazeMessageService : LifecycleService(), NetworkEventProvider.Listener, C private val gson = GsonHelper.customGson - private val powerManager by lazy { getSystemService() } - private val activityManager by lazy { getSystemService() } private var isIgnoringBatteryOptimizations = false private var disposable: Disposable? = null private val destroyScope = scope(Lifecycle.Event.ON_DESTROY) @@ -293,11 +287,7 @@ class BlazeMessageService : LifecycleService(), NetworkEventProvider.Listener, C } private fun updateIgnoringBatteryOptimizations() { - isIgnoringBatteryOptimizations = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !RomUtil.isEmui) { - activityManager?.isBackgroundRestricted?.not() - } else { - powerManager?.isIgnoringBatteryOptimizations(packageName) - } ?: false + isIgnoringBatteryOptimizations = !applicationContext.isBatteryOptimizationRestricted() } @SuppressLint("NewApi") diff --git a/app/src/main/java/one/mixin/android/ui/common/BatteryOptimizationDialogActivity.kt b/app/src/main/java/one/mixin/android/ui/common/BatteryOptimizationDialogActivity.kt index 103388159a..b775d3978f 100644 --- a/app/src/main/java/one/mixin/android/ui/common/BatteryOptimizationDialogActivity.kt +++ b/app/src/main/java/one/mixin/android/ui/common/BatteryOptimizationDialogActivity.kt @@ -11,22 +11,19 @@ import android.text.SpannableStringBuilder import android.text.style.StyleSpan import one.mixin.android.R import one.mixin.android.extension.alertDialogBuilder -import one.mixin.android.extension.handleIgnoreBatteryOptimization +import one.mixin.android.extension.openBatteryOptimizationSetting import one.mixin.android.session.Session import one.mixin.android.util.RomUtil import timber.log.Timber class BatteryOptimizationDialogActivity : BaseActivity() { companion object { - const val ARGS_NEW_TASK = "args_new_task" - fun show( context: Context, newTask: Boolean = false, ) { if (!Session.hasSafe()) return Intent(context, BatteryOptimizationDialogActivity::class.java).apply { - putExtra(ARGS_NEW_TASK, newTask) if (newTask) { addFlags(FLAG_ACTIVITY_NEW_TASK) } @@ -37,36 +34,33 @@ class BatteryOptimizationDialogActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val newTask = intent.getBooleanExtra(ARGS_NEW_TASK, false) alertDialogBuilder() - .setMessage( - replaceTags( - getString( - if (RomUtil.isOneUi) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - R.string.setting_battery_optimize_title_one_ui_above_s - } else { - R.string.setting_battery_optimize_title_one_ui_below_s - } - } else { - R.string.setting_battery_optimize_title - }, - ), - ), - ) + .setMessage(replaceTags(getString(getBatteryOptimizationContentResId()))) .setCancelable(false) .setNegativeButton(R.string.Cancel) { dialog, _ -> dialog.dismiss() finish() } .setPositiveButton(R.string.Go_settings) { dialog, _ -> - handleIgnoreBatteryOptimization(newTask) + openBatteryOptimizationSetting() dialog.dismiss() finish() } .show() } + private fun getBatteryOptimizationContentResId(): Int { + return if (RomUtil.isOneUi) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + R.string.setting_battery_optimize_title_one_ui_above_s + } else { + R.string.setting_battery_optimize_title_one_ui_below_s + } + } else { + R.string.setting_battery_optimize_title + } + } + private fun replaceTags(str: String): SpannableStringBuilder { try { var start: Int @@ -95,7 +89,12 @@ class BatteryOptimizationDialogActivity : BaseActivity() { val spannableStringBuilder = SpannableStringBuilder(stringBuilder) for (a in 0 until bolds.count() / 2) { - spannableStringBuilder.setSpan(StyleSpan(Typeface.BOLD), bolds[a * 2], bolds[a * 2 + 1], Spannable.SPAN_INCLUSIVE_EXCLUSIVE) + spannableStringBuilder.setSpan( + StyleSpan(Typeface.BOLD), + bolds[a * 2], + bolds[a * 2 + 1], + Spannable.SPAN_INCLUSIVE_EXCLUSIVE, + ) } return spannableStringBuilder } catch (e: Exception) { diff --git a/app/src/main/java/one/mixin/android/ui/home/MainActivity.kt b/app/src/main/java/one/mixin/android/ui/home/MainActivity.kt index fec197f6ed..bfbcdcba1c 100644 --- a/app/src/main/java/one/mixin/android/ui/home/MainActivity.kt +++ b/app/src/main/java/one/mixin/android/ui/home/MainActivity.kt @@ -3,7 +3,6 @@ package one.mixin.android.ui.home import android.Manifest import android.annotation.SuppressLint import android.app.Activity -import android.app.ActivityManager import android.app.Dialog import android.app.NotificationManager import android.content.Context @@ -11,7 +10,6 @@ import android.content.Intent import android.content.IntentSender import android.os.Build import android.os.Bundle -import android.os.PowerManager import androidx.activity.result.contract.ActivityResultContracts import androidx.core.content.getSystemService import androidx.fragment.app.DialogFragment @@ -50,7 +48,6 @@ import one.mixin.android.Constants import one.mixin.android.Constants.APP_VERSION import one.mixin.android.Constants.Account import one.mixin.android.Constants.Account.PREF_BACKUP -import one.mixin.android.Constants.Account.PREF_BATTERY_OPTIMIZE import one.mixin.android.Constants.Account.PREF_CHECK_STORAGE import one.mixin.android.Constants.Account.PREF_DEVICE_SDK import one.mixin.android.Constants.Account.PREF_LOGIN_OR_SIGN_UP @@ -130,7 +127,6 @@ import one.mixin.android.tip.wc.WCEvent import one.mixin.android.tip.wc.WalletConnect import one.mixin.android.tip.wc.WalletConnectV2 import one.mixin.android.ui.common.BaseFragment -import one.mixin.android.ui.common.BatteryOptimizationDialogActivity import one.mixin.android.ui.common.BlazeBaseActivity import one.mixin.android.ui.common.LoginVerifyBottomSheetDialogFragment import one.mixin.android.ui.common.NavigationController @@ -172,7 +168,6 @@ import one.mixin.android.util.BiometricUtil import one.mixin.android.util.ErrorHandler import one.mixin.android.util.ErrorHandler.Companion.SERVER import one.mixin.android.util.GsonHelper -import one.mixin.android.util.RomUtil import one.mixin.android.util.RootUtil import one.mixin.android.util.analytics.AnalyticsTracker import one.mixin.android.util.database.databaseFile @@ -494,7 +489,6 @@ class MainActivity : BlazeBaseActivity(), WalletMissingBtcAddressFragment.Callba refreshStickerAlbum() refreshExternalSchemes() cleanCache() - checkBatteryOptimization() if (!defaultSharedPreferences.getBoolean(PREF_SYNC_CIRCLE, false)) { jobManager.addJobInBackground(RefreshCircleJob()) @@ -645,28 +639,6 @@ class MainActivity : BlazeBaseActivity(), WalletMissingBtcAddressFragment.Callba return currentVersion > MINI_VERSION && CURRENT_VERSION != currentVersion } - @SuppressLint("BatteryLife") - private fun checkBatteryOptimization() { - val batteryOptimize = defaultSharedPreferences.getLong(PREF_BATTERY_OPTIMIZE, 0) - val cur = System.currentTimeMillis() - if (cur - batteryOptimize > INTERVAL_24_HOURS) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !RomUtil.isEmui) { - getSystemService()?.let { am -> - if (am.isBackgroundRestricted) { - BatteryOptimizationDialogActivity.show(this) - } - } - } else { - getSystemService()?.let { pm -> - if (!pm.isIgnoringBatteryOptimizations(packageName)) { - BatteryOptimizationDialogActivity.show(this) - } - } - } - defaultSharedPreferences.putLong(PREF_BATTERY_OPTIMIZE, cur) - } - } - private fun delayShowModifyMobile() = lifecycleScope.launch { delay(2000) diff --git a/app/src/main/java/one/mixin/android/ui/home/reminder/ReminderBottomSheetDialogFragment.kt b/app/src/main/java/one/mixin/android/ui/home/reminder/ReminderBottomSheetDialogFragment.kt index a325f75235..e82dc6f450 100644 --- a/app/src/main/java/one/mixin/android/ui/home/reminder/ReminderBottomSheetDialogFragment.kt +++ b/app/src/main/java/one/mixin/android/ui/home/reminder/ReminderBottomSheetDialogFragment.kt @@ -3,17 +3,25 @@ package one.mixin.android.ui.home.reminder import android.annotation.SuppressLint import android.app.Dialog import android.content.Context +import android.os.Build import android.view.Gravity import android.view.View import android.view.ViewGroup +import androidx.annotation.StringRes import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.core.app.NotificationManagerCompat import dagger.hilt.android.AndroidEntryPoint import one.mixin.android.BuildConfig +import one.mixin.android.Constants.Account.PREF_BATTERY_OPTIMIZE import one.mixin.android.Constants.INTERVAL_24_HOURS import one.mixin.android.Constants.INTERVAL_48_HOURS import one.mixin.android.R @@ -21,13 +29,16 @@ import one.mixin.android.compose.theme.MixinAppTheme import one.mixin.android.compose.theme.languageBasedImage import one.mixin.android.extension.booleanFromAttribute import one.mixin.android.extension.defaultSharedPreferences +import one.mixin.android.extension.isBatteryOptimizationRestricted import one.mixin.android.extension.isNightMode +import one.mixin.android.extension.openBatteryOptimizationSetting import one.mixin.android.extension.openNotificationSetting import one.mixin.android.extension.putLong import one.mixin.android.extension.withArgs import one.mixin.android.session.Session import one.mixin.android.ui.common.MixinComposeBottomSheetDialogFragment import one.mixin.android.ui.home.MainActivity +import one.mixin.android.util.RomUtil import one.mixin.android.util.SystemUIManager @AndroidEntryPoint @@ -70,6 +81,13 @@ class ReminderBottomSheetDialogFragment : MixinComposeBottomSheetDialogFragment( return PopupType.NotificationPermissionReminder } + val lastBatteryOptimizationReminderTime = sharedPreferences.getLong(PREF_BATTERY_OPTIMIZE, 0) + if (System.currentTimeMillis() - lastBatteryOptimizationReminderTime > INTERVAL_24_HOURS && + context.isBatteryOptimizationRestricted() + ) { + return PopupType.BatteryOptimizationReminder + } + return null } @@ -99,6 +117,19 @@ class ReminderBottomSheetDialogFragment : MixinComposeBottomSheetDialogFragment( } return 0 } + + @StringRes + private fun getBatteryOptimizationContentResId(): Int { + return if (RomUtil.isOneUi) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + R.string.setting_battery_optimize_title_one_ui_above_s + } else { + R.string.setting_battery_optimize_title_one_ui_below_s + } + } else { + R.string.setting_battery_optimize_title + } + } } private val popupType by lazy { @@ -106,6 +137,7 @@ class ReminderBottomSheetDialogFragment : MixinComposeBottomSheetDialogFragment( when (typeName) { PopupType.NewVersionReminder::class.java.simpleName -> PopupType.NewVersionReminder PopupType.NotificationPermissionReminder::class.java.simpleName -> PopupType.NotificationPermissionReminder + PopupType.BatteryOptimizationReminder::class.java.simpleName -> PopupType.BatteryOptimizationReminder else -> throw IllegalArgumentException("Unknown PopupType") } } @@ -150,7 +182,9 @@ class ReminderBottomSheetDialogFragment : MixinComposeBottomSheetDialogFragment( title = R.string.New_Update_Available, actionStr = R.string.Update_Now, action = { - Session.getAccount()?.system?.messenger?.let { it -> (requireActivity() as? MainActivity)?.showUpdate(it.releaseUrl) } + Session.getAccount()?.system?.messenger?.let { it -> + (requireActivity() as? MainActivity)?.showUpdate(it.releaseUrl) + } dismissAllowingStateLoss() }, dismiss = { @@ -165,7 +199,7 @@ class ReminderBottomSheetDialogFragment : MixinComposeBottomSheetDialogFragment( text = stringResource(R.string.New_Update_Available_desc), color = MixinAppTheme.colors.textAssist, modifier = Modifier.fillMaxWidth(), - textAlign = androidx.compose.ui.text.style.TextAlign.Center + textAlign = TextAlign.Center, ) }, stickyFooter = true, @@ -176,7 +210,7 @@ class ReminderBottomSheetDialogFragment : MixinComposeBottomSheetDialogFragment( ReminderPage( contentImage = languageBasedImage( R.drawable.bg_reminder_notifaction, - R.drawable.bg_reminder_notifaction_cn + R.drawable.bg_reminder_notifaction_cn, ), title = R.string.Turn_On_Notifications, actionStr = R.string.Enable_Notifications, @@ -196,13 +230,43 @@ class ReminderBottomSheetDialogFragment : MixinComposeBottomSheetDialogFragment( text = stringResource(R.string.notification_content), color = MixinAppTheme.colors.textAssist, modifier = Modifier.fillMaxWidth(), - textAlign = androidx.compose.ui.text.style.TextAlign.Center + textAlign = TextAlign.Center, ) }, stickyFooter = true, ) } + is PopupType.BatteryOptimizationReminder -> { + ReminderPage( + contentImage = R.drawable.bg_reminder_battery_optimization, + title = R.string.Battery_Optimization, + actionStr = R.string.Go_settings, + action = { + requireContext().defaultSharedPreferences.putLong( + PREF_BATTERY_OPTIMIZE, + System.currentTimeMillis(), + ) + requireContext().openBatteryOptimizationSetting() + dismissAllowingStateLoss() + }, + dismiss = { + requireContext().defaultSharedPreferences.putLong( + PREF_BATTERY_OPTIMIZE, + System.currentTimeMillis(), + ) + dismissAllowingStateLoss() + }, + contentSlot = { + Text( + text = batteryOptimizationContent(), + color = MixinAppTheme.colors.textAssist, + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center, + ) + }, + ) + } } } } @@ -214,8 +278,53 @@ class ReminderBottomSheetDialogFragment : MixinComposeBottomSheetDialogFragment( override fun showError(error: String) { } + private fun batteryOptimizationContent(): AnnotatedString { + return batteryOptimizationAnnotatedContent(getString(getBatteryOptimizationContentResId())) + } + sealed class PopupType { object NewVersionReminder : PopupType() object NotificationPermissionReminder : PopupType() + object BatteryOptimizationReminder : PopupType() + } +} + +internal fun batteryOptimizationAnnotatedContent(content: String): AnnotatedString { + return buildAnnotatedString { + fun appendBold(text: String) { + val start = length + append(text) + addStyle(SpanStyle(fontWeight = FontWeight.Bold), start, length) + } + + var index = 0 + while (index < content.length) { + when { + content.startsWith("", index) -> { + val end = content.indexOf("", index + 3) + if (end >= 0) { + appendBold(content.substring(index + 3, end)) + index = end + 4 + } else { + append("") + index += 3 + } + } + content.startsWith("**", index) -> { + val end = content.indexOf("**", index + 2) + if (end >= 0) { + appendBold(content.substring(index + 2, end)) + index = end + 2 + } else { + append("**") + index += 2 + } + } + else -> { + append(content[index].toString()) + index++ + } + } + } } } diff --git a/app/src/main/java/one/mixin/android/util/BatteryOptimizationUtil.kt b/app/src/main/java/one/mixin/android/util/BatteryOptimizationUtil.kt new file mode 100644 index 0000000000..c8141a0393 --- /dev/null +++ b/app/src/main/java/one/mixin/android/util/BatteryOptimizationUtil.kt @@ -0,0 +1,15 @@ +package one.mixin.android.util + +internal fun isPowerManagerBatteryOptimizationRestricted(isIgnoringBatteryOptimizations: Boolean?): Boolean { + return isIgnoringBatteryOptimizations != true +} + +internal fun shouldPrioritizeBatteryOptimizationRequest(romType: RomPermissionUtil.RomType): Boolean { + return when (romType) { + RomPermissionUtil.RomType.NATIVE, + RomPermissionUtil.RomType.HUAWEI, + RomPermissionUtil.RomType.HONOR + -> true + else -> false + } +} diff --git a/app/src/main/res/drawable-xxhdpi/bg_reminder_battery_optimization.png b/app/src/main/res/drawable-xxhdpi/bg_reminder_battery_optimization.png new file mode 100644 index 0000000000..511aecc65f Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/bg_reminder_battery_optimization.png differ diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 30d58128ec..a0f089a6f8 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -834,6 +834,7 @@ Se ha establecido el PIN con éxito Aprobado: %1$s, último acceso: %2$s ID de Mixin, nombre + Optimización de batería Esto mejorará la confiabilidad de las notificaciones entrantes y las llamadas de Mixin. Esto mejorará la confiabilidad de las notificaciones entrantes y las llamadas de Mixin. Selecciona **Optimizado** o **Sin restricciones** en Configuración > Batería. Esto mejorará la confiabilidad de las notificaciones entrantes y las llamadas de Mixin. Pulsa Configuración -> Batería y activa **Permitir actividad en segundo plano**. diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 45679256d8..59f858c5a1 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -783,6 +783,7 @@ PINコードの設定が完了しました。 許可: %1$s, 最終アクセス: %2$s Mixin ID, 名前 + バッテリー最適化 これにより、通知の受信や Mixinの呼び出しに対する確実性が向上します これにより、通知の受信やMixinの呼び出しに対する確実性が向上します。「設定」→「バッテリー」をタップしてください。そして**「最適化」** もしくは **「制限なし」**を選択してください。 これにより、通知の受信やMixinの呼び出しに対する確実性が向上します。「設定」→「バッテリー」をタップしてください。そして**「バックグラウンドでの活動を許可」**をオンにしてください。 diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index ebe4849b53..312fdfa0d0 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -804,6 +804,7 @@ ПИН-код успешно установлен Одобрено: %1$s, последнее обращение: %2$s Mixin ID, имя + Оптимизация батареи Это повысит надежность входящих уведомлений и вызовов Mixin. Это повысит надежность входящих уведомлений и вызовов Mixin. Выберите **Оптимизированный** или **Неограниченный** в меню «Настройки» > «Аккумулятор». Это повысит надежность входящих уведомлений и вызовов Mixin. Нажмите «Настройки» -> «Аккумулятор» и включите **Разрешить фоновую активность**. diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 8e1121adfc..74de7bc485 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -985,6 +985,7 @@ PIN 设置成功 授权时间:%1$s, 上次访问:%2$s Mixin ID, 昵称 + 电池优化 这将提高 Mixin 通知和呼叫的可靠性。 这将提高 Mixin 通知和呼叫的可靠性. 请在设置 > 电池中选择 **已优化** 或者 **不受限制**。 这将提高 Mixin 通知和呼叫的可靠性。点击设置 -> 电池,开启**允许后台活动**。 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 17517bd35d..e4ab866d2c 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -794,6 +794,7 @@ PIN 設定成功 授權時間:%1$s, 上次訪問:%2$s Mixin ID, 暱稱 + 電池最佳化 這將提高 Mixin 通知和呼叫的可靠性。 這將提高 Mixin 通知和呼叫的可靠性. 請在設定 > 電池中選擇 **已最佳化** 或者 **不受限制**。 這將提高 Mixin 通知和呼叫的可靠性。點選設定 -> 電池,開啟**允許後臺活動**。 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d3ad92690c..838613b861 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1007,6 +1007,7 @@ Set PIN successfully Approved: %1$s, Last Accessed: %2$s Mixin ID, Name + Battery Optimization This will improve reliability for incoming notifications and Mixin calls This will improve reliability for incoming notifications and Mixin calls. Please select **Optimised** or **Unrestricted** in Settings > Battery. This will improve reliability for incoming notifications and Mixin calls. Tap Settings -> Battery and turn **Allow background activity** on. diff --git a/app/src/test/java/one/mixin/android/ui/home/reminder/BatteryOptimizationContentTest.kt b/app/src/test/java/one/mixin/android/ui/home/reminder/BatteryOptimizationContentTest.kt new file mode 100644 index 0000000000..b234b91904 --- /dev/null +++ b/app/src/test/java/one/mixin/android/ui/home/reminder/BatteryOptimizationContentTest.kt @@ -0,0 +1,28 @@ +package one.mixin.android.ui.home.reminder + +import androidx.compose.ui.text.font.FontWeight +import kotlin.test.Test +import kotlin.test.assertEquals + +class BatteryOptimizationContentTest { + @Test + fun markdownBoldMarkersBecomeSpanStyles() { + val content = batteryOptimizationAnnotatedContent("Select **Optimised** or **Unrestricted**.") + + assertEquals("Select Optimised or Unrestricted.", content.text) + val boldRanges = content.spanStyles.filter { it.item.fontWeight == FontWeight.Bold } + assertEquals(2, boldRanges.size) + assertEquals("Optimised", content.text.substring(boldRanges[0].start, boldRanges[0].end)) + assertEquals("Unrestricted", content.text.substring(boldRanges[1].start, boldRanges[1].end)) + } + + @Test + fun htmlBoldMarkersBecomeSpanStyles() { + val content = batteryOptimizationAnnotatedContent("Turn Allow background activity on.") + + assertEquals("Turn Allow background activity on.", content.text) + val boldRanges = content.spanStyles.filter { it.item.fontWeight == FontWeight.Bold } + assertEquals(1, boldRanges.size) + assertEquals("Allow background activity", content.text.substring(boldRanges[0].start, boldRanges[0].end)) + } +} diff --git a/app/src/test/java/one/mixin/android/util/BatteryOptimizationUtilTest.kt b/app/src/test/java/one/mixin/android/util/BatteryOptimizationUtilTest.kt new file mode 100644 index 0000000000..bc2a2dddb6 --- /dev/null +++ b/app/src/test/java/one/mixin/android/util/BatteryOptimizationUtilTest.kt @@ -0,0 +1,23 @@ +package one.mixin.android.util + +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class BatteryOptimizationUtilTest { + @Test + fun powerManagerNullIsRestricted() { + assertTrue(isPowerManagerBatteryOptimizationRestricted(null)) + assertTrue(isPowerManagerBatteryOptimizationRestricted(false)) + assertFalse(isPowerManagerBatteryOptimizationRestricted(true)) + } + + @Test + fun requestIntentIsPrioritizedForNativeHuaweiAndHonor() { + assertTrue(shouldPrioritizeBatteryOptimizationRequest(RomPermissionUtil.RomType.NATIVE)) + assertTrue(shouldPrioritizeBatteryOptimizationRequest(RomPermissionUtil.RomType.HUAWEI)) + assertTrue(shouldPrioritizeBatteryOptimizationRequest(RomPermissionUtil.RomType.HONOR)) + assertFalse(shouldPrioritizeBatteryOptimizationRequest(RomPermissionUtil.RomType.SAMSUNG)) + assertFalse(shouldPrioritizeBatteryOptimizationRequest(RomPermissionUtil.RomType.UNKNOWN)) + } +}