From 4cbcee85d69f4e89924b4406eb32f3f4b74d0a54 Mon Sep 17 00:00:00 2001 From: Clark Date: Tue, 15 Aug 2023 12:31:54 -0400 Subject: [PATCH] Add prompt to help troubleshoot slow notifications. --- .../securesms/ApplicationContext.java | 2 + .../thoughtcrime/securesms/MainActivity.java | 14 ++- .../PromptBatterySaverDialogFragment.kt | 65 ++++++++++++++ .../NotificationsSettingsFragment.kt | 11 +++ .../NotificationsSettingsState.kt | 3 +- .../NotificationsSettingsViewModel.kt | 4 +- .../securesms/keyvalue/UiHints.java | 18 ++++ .../logsubmit/LogSectionSystemInfo.java | 4 + .../securesms/megaphone/Megaphones.java | 1 - .../SlowNotificationHeuristics.kt | 57 +++++++++++- .../securesms/util/FeatureFlags.java | 12 ++- .../securesms/util/LocaleFeatureFlags.java | 5 ++ .../securesms/util/PowerManagerCompat.java | 7 ++ .../drawable/ic_troubleshoot_notification.xml | 36 ++++++++ .../prompt_battery_saver_bottom_sheet.xml | 88 +++++++++++++++++++ app/src/main/res/values/strings.xml | 12 +++ 16 files changed, 329 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/components/PromptBatterySaverDialogFragment.kt create mode 100644 app/src/main/res/drawable/ic_troubleshoot_notification.xml create mode 100644 app/src/main/res/layout/prompt_battery_saver_bottom_sheet.xml diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 676711c4c9..7b69285b05 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -90,6 +90,8 @@ import org.thoughtcrime.securesms.util.AppForegroundObserver; import org.thoughtcrime.securesms.util.AppStartup; import org.thoughtcrime.securesms.util.DynamicTheme; import org.thoughtcrime.securesms.util.FeatureFlags; +import org.thoughtcrime.securesms.util.PowerManagerCompat; +import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.SignalLocalMetrics; import org.thoughtcrime.securesms.util.SignalUncaughtExceptionHandler; import org.thoughtcrime.securesms.util.TextSecurePreferences; diff --git a/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java index 96f4c7cc2a..6bdd3323ac 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java @@ -4,6 +4,7 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.view.View; import android.view.ViewTreeObserver; @@ -16,12 +17,14 @@ import androidx.lifecycle.ViewModelProvider; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import org.thoughtcrime.securesms.components.DebugLogsPromptDialogFragment; +import org.thoughtcrime.securesms.components.PromptBatterySaverDialogFragment; import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController; import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner; import org.thoughtcrime.securesms.conversationlist.RelinkDevicesReminderBottomSheetFragment; import org.thoughtcrime.securesms.devicetransfer.olddevice.OldDeviceExitActivity; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.net.DeviceTransferBlockingInterceptor; +import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.notifications.SlowNotificationHeuristics; import org.thoughtcrime.securesms.stories.tabs.ConversationListTabRepository; import org.thoughtcrime.securesms.stories.tabs.ConversationListTabsViewModel; @@ -30,6 +33,7 @@ import org.thoughtcrime.securesms.util.CachedInflater; import org.thoughtcrime.securesms.util.CommunicationActions; import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme; import org.thoughtcrime.securesms.util.DynamicTheme; +import org.thoughtcrime.securesms.util.PowerManagerCompat; import org.thoughtcrime.securesms.util.SplashScreenUtil; import org.thoughtcrime.securesms.util.WindowUtil; @@ -139,8 +143,14 @@ public class MainActivity extends PassphraseRequiredActivity implements VoiceNot updateTabVisibility(); - if (SlowNotificationHeuristics.shouldPromptUserForLogs()) { - DebugLogsPromptDialogFragment.show(this, getSupportFragmentManager()); + if (SlowNotificationHeuristics.isHavingDelayedNotifications()) { + if (SlowNotificationHeuristics.isPotentiallyCausedByBatteryOptimizations() && Build.VERSION.SDK_INT >= 23) { + if (SlowNotificationHeuristics.shouldPromptBatterySaver()) { + PromptBatterySaverDialogFragment.show(this, getSupportFragmentManager()); + } + } else if (SlowNotificationHeuristics.shouldPromptUserForLogs()) { + DebugLogsPromptDialogFragment.show(this, getSupportFragmentManager()); + } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/PromptBatterySaverDialogFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/PromptBatterySaverDialogFragment.kt new file mode 100644 index 0000000000..0a343fe296 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/components/PromptBatterySaverDialogFragment.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.components + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.annotation.RequiresApi +import androidx.core.os.bundleOf +import androidx.fragment.app.FragmentManager +import androidx.lifecycle.ViewModelProvider +import org.signal.core.util.concurrent.LifecycleDisposable +import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.databinding.PromptBatterySaverBottomSheetBinding +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.util.BottomSheetUtil +import org.thoughtcrime.securesms.util.PowerManagerCompat + +@RequiresApi(23) +class PromptBatterySaverDialogFragment : FixedRoundedCornerBottomSheetDialogFragment() { + + companion object { + + @JvmStatic + fun show(context: Context, fragmentManager: FragmentManager) { + if (fragmentManager.findFragmentByTag(BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG) == null) { + PromptBatterySaverDialogFragment().apply { + arguments = bundleOf() + }.show(fragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG) + SignalStore.uiHints().lastBatterySaverPrompt = System.currentTimeMillis() + } + } + } + + override val peekHeightPercentage: Float = 0.66f + override val themeResId: Int = R.style.Widget_Signal_FixedRoundedCorners_Messages + + private val binding by ViewBinderDelegate(PromptBatterySaverBottomSheetBinding::bind) + + private lateinit var viewModel: PromptLogsViewModel + + private val disposables: LifecycleDisposable = LifecycleDisposable() + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + return inflater.inflate(R.layout.prompt_battery_saver_bottom_sheet, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + disposables.bindTo(viewLifecycleOwner) + + viewModel = ViewModelProvider(this)[PromptLogsViewModel::class.java] + binding.continueButton.setOnClickListener { + PowerManagerCompat.requestIgnoreBatteryOptimizations(requireContext()) + } + binding.dismissButton.setOnClickListener { + SignalStore.uiHints().markDismissedBatterySaverPrompt() + dismiss() + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsFragment.kt index 355a4b909b..f3148a6132 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsFragment.kt @@ -22,6 +22,7 @@ import androidx.preference.PreferenceManager import org.signal.core.util.getParcelableExtraCompat import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.R +import org.thoughtcrime.securesms.components.PromptBatterySaverDialogFragment import org.thoughtcrime.securesms.components.settings.DSLConfiguration import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment import org.thoughtcrime.securesms.components.settings.DSLSettingsText @@ -184,6 +185,16 @@ class NotificationsSettingsFragment : DSLSettingsFragment(R.string.preferences__ } ) + if (Build.VERSION.SDK_INT >= 23 && state.messageNotificationsState.troubleshootNotifications) { + clickPref( + title = DSLSettingsText.from(R.string.preferences_notifications__troubleshoot), + isEnabled = true, + onClick = { + PromptBatterySaverDialogFragment.show(requireContext(), childFragmentManager) + } + ) + } + if (Build.VERSION.SDK_INT < 30) { if (NotificationChannels.supported()) { clickPref( diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsState.kt index 02d52f24c3..5ae47346bf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsState.kt @@ -17,7 +17,8 @@ data class MessageNotificationsState( val inChatSoundsEnabled: Boolean, val repeatAlerts: Int, val messagePrivacy: String, - val priority: Int + val priority: Int, + val troubleshootNotifications: Boolean ) data class CallNotificationsState( diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsViewModel.kt index 744a55ca81..ae66c7b8bd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/notifications/NotificationsSettingsViewModel.kt @@ -8,6 +8,7 @@ import androidx.lifecycle.ViewModelProvider import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.notifications.NotificationChannels +import org.thoughtcrime.securesms.notifications.SlowNotificationHeuristics import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPreference import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.livedata.Store @@ -104,7 +105,8 @@ class NotificationsSettingsViewModel(private val sharedPreferences: SharedPrefer inChatSoundsEnabled = SignalStore.settings().isMessageNotificationsInChatSoundsEnabled, repeatAlerts = SignalStore.settings().messageNotificationsRepeatAlerts, messagePrivacy = SignalStore.settings().messageNotificationsPrivacy.toString(), - priority = TextSecurePreferences.getNotificationPriority(ApplicationDependencies.getApplication()) + priority = TextSecurePreferences.getNotificationPriority(ApplicationDependencies.getApplication()), + troubleshootNotifications = SlowNotificationHeuristics.isPotentiallyCausedByBatteryOptimizations() && SlowNotificationHeuristics.isHavingDelayedNotifications() ), callNotificationsState = CallNotificationsState( notificationsEnabled = SignalStore.settings().isCallNotificationsEnabled, diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHints.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHints.java index 24ffb06192..b5c2ce8db2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHints.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHints.java @@ -20,6 +20,8 @@ public class UiHints extends SignalStoreValues { private static final String HAS_SEEN_SAFETY_NUMBER_NUX = "uihints.has_seen_safety_number_nux"; private static final String DECLINED_NOTIFICATION_LOGS_PROMPT = "uihints.declined_notification_logs"; private static final String LAST_NOTIFICATION_LOGS_PROMPT_TIME = "uihints.last_notification_logs_prompt"; + private static final String DISMISSED_BATTERY_SAVER_PROMPT = "uihints.declined_battery_saver_prompt"; + private static final String LAST_BATTERY_SAVER_PROMPT = "uihints.last_battery_saver_prompt"; UiHints(@NonNull KeyValueStore store) { super(store); @@ -136,4 +138,20 @@ public class UiHints extends SignalStoreValues { public boolean hasDeclinedToShareNotificationLogs() { return getBoolean(DECLINED_NOTIFICATION_LOGS_PROMPT, false); } + + public void markDismissedBatterySaverPrompt() { + putBoolean(DISMISSED_BATTERY_SAVER_PROMPT, true); + } + + public boolean hasDismissedBatterySaverPrompt() { + return getBoolean(DISMISSED_BATTERY_SAVER_PROMPT, false); + } + + public long getLastBatterySaverPrompt() { + return getLong(LAST_BATTERY_SAVER_PROMPT, 0); + } + + public void setLastBatterySaverPrompt(long time) { + putLong(LAST_BATTERY_SAVER_PROMPT, time); + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionSystemInfo.java b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionSystemInfo.java index ad024c7f45..d5c12cb11b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionSystemInfo.java +++ b/app/src/main/java/org/thoughtcrime/securesms/logsubmit/LogSectionSystemInfo.java @@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.emoji.EmojiFiles; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.net.StandardUserAgentInterceptor; +import org.thoughtcrime.securesms.notifications.SlowNotificationHeuristics; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.service.webrtc.AndroidTelecomUtil; import org.thoughtcrime.securesms.util.AppSignatureUtil; @@ -88,7 +89,10 @@ public class LogSectionSystemInfo implements LogSection { builder.append("Server Time Offset: ").append(SignalStore.misc().getLastKnownServerTimeOffset()).append(" ms (last updated: ").append(SignalStore.misc().getLastKnownServerTimeOffsetUpdateTime()).append(")").append("\n"); builder.append("Telecom : ").append(AndroidTelecomUtil.getTelecomSupported()).append("\n"); builder.append("User-Agent : ").append(StandardUserAgentInterceptor.USER_AGENT).append("\n"); + builder.append("SlowNotifications : ").append(SlowNotificationHeuristics.isHavingDelayedNotifications()).append("\n"); + builder.append("PotentiallyBattery: ").append(SlowNotificationHeuristics.isPotentiallyCausedByBatteryOptimizations()).append("\n"); builder.append("App : "); + try { builder.append(pm.getApplicationLabel(pm.getApplicationInfo(context.getPackageName(), 0))) .append(" ") diff --git a/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphones.java b/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphones.java index 4bf6816fca..07a91fb5a9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphones.java +++ b/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphones.java @@ -23,7 +23,6 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.InAppDona import org.thoughtcrime.securesms.database.model.MegaphoneRecord; import org.thoughtcrime.securesms.database.model.RemoteMegaphoneRecord; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; -import org.thoughtcrime.securesms.exporter.flow.SmsExportActivity; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues; import org.thoughtcrime.securesms.keyvalue.SignalStore; diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/SlowNotificationHeuristics.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/SlowNotificationHeuristics.kt index 464507bf17..6e92f516b3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/SlowNotificationHeuristics.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/SlowNotificationHeuristics.kt @@ -5,14 +5,17 @@ package org.thoughtcrime.securesms.notifications +import android.os.Build import android.text.TextUtils import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.database.LocalMetricsDatabase import org.thoughtcrime.securesms.dependencies.ApplicationDependencies import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.util.DeviceProperties import org.thoughtcrime.securesms.util.FeatureFlags import org.thoughtcrime.securesms.util.JsonUtils import org.thoughtcrime.securesms.util.LocaleFeatureFlags +import org.thoughtcrime.securesms.util.PowerManagerCompat import org.thoughtcrime.securesms.util.SignalLocalMetrics import java.util.concurrent.TimeUnit import kotlin.time.Duration.Companion.days @@ -63,12 +66,34 @@ object SlowNotificationHeuristics { if (System.currentTimeMillis() - SignalStore.uiHints().lastNotificationLogsPrompt < TimeUnit.DAYS.toMillis(7)) { return false } - val configuration = getConfiguration() - return isHavingDelayedNotifications(configuration) + return true } - fun isHavingDelayedNotifications(configuration: Configuration): Boolean { + @JvmStatic + fun shouldPromptBatterySaver(): Boolean { + if (Build.VERSION.SDK_INT < 23) { + return false + } + if (!LocaleFeatureFlags.isBatterySaverPromptEnabled() || SignalStore.uiHints().hasDismissedBatterySaverPrompt()) { + return false + } + if (System.currentTimeMillis() - SignalStore.uiHints().lastBatterySaverPrompt < TimeUnit.DAYS.toMillis(7)) { + return false + } + + return true + } + + @JvmStatic + fun isHavingDelayedNotifications(): Boolean { + if (!SignalStore.settings().isMessageNotificationsEnabled || + !NotificationChannels.getInstance().areNotificationsEnabled() + ) { + // If user does not have notifications enabled, we shouldn't bother them about delayed notifications + return false + } + val configuration = getConfiguration() val db = LocalMetricsDatabase.getInstance(ApplicationDependencies.getApplication()) val metrics = db.getMetrics() @@ -84,6 +109,32 @@ object SlowNotificationHeuristics { return false } + /** + * Returns whether or not the delayed notifications may be due to battery saver optimizations. + * + * Some OEMs over-optimize this battery restrictions and remove network even after receiving a + * high priority push. + * + * We consider a scenario where removing battery optimizations can fix delayed notifications: + * - Data saver must be off (or white listed), otherwise it can be causing delayed notifications + * - App must not already be exempted from battery optimizations + * + * We do not need to check if {ActivityManager#isBackgroundRestricted} is true, because if the app + * is set to "Optimized" this will be false (and can be culprit to delayed notifications) or if + * true can most definitely be at fault. + */ + @JvmStatic + fun isPotentiallyCausedByBatteryOptimizations(): Boolean { + val applicationContext = ApplicationDependencies.getApplication() + if (DeviceProperties.getDataSaverState(applicationContext) == DeviceProperties.DataSaverState.ENABLED) { + return false + } + if (PowerManagerCompat.isIgnoringBatteryOptimizations(applicationContext)) { + return false + } + return true + } + private fun hasRepeatedFailedServiceStarts(metrics: List, minimumEventAgeMs: Long, minimumEventCount: Int, failurePercentage: Float): Boolean { if (!haveEnoughData(SignalLocalMetrics.FcmServiceStartSuccess.NAME, minimumEventAgeMs) && !haveEnoughData(SignalLocalMetrics.FcmServiceStartFailure.NAME, minimumEventAgeMs)) { Log.d(TAG, "insufficient data for service starts") diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java index 64d590dda7..f6e8e2cda7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java @@ -111,6 +111,7 @@ public final class FeatureFlags { private static final String SAFETY_NUMBER_ACI = "global.safetyNumberAci"; public static final String PROMPT_FOR_NOTIFICATION_LOGS = "android.logs.promptNotifications"; private static final String PROMPT_FOR_NOTIFICATION_CONFIG = "android.logs.promptNotificationsConfig"; + public static final String PROMPT_BATTERY_SAVER = "android.promptBatterySaver"; /** * We will only store remote values for flags in this set. If you want a flag to be controllable @@ -173,7 +174,8 @@ public final class FeatureFlags { SAFETY_NUMBER_ACI, FCM_MAY_HAVE_MESSAGES_KILL_SWITCH, PROMPT_FOR_NOTIFICATION_LOGS, - PROMPT_FOR_NOTIFICATION_CONFIG + PROMPT_FOR_NOTIFICATION_CONFIG, + PROMPT_BATTERY_SAVER ); @VisibleForTesting @@ -242,7 +244,8 @@ public final class FeatureFlags { SAFETY_NUMBER_ACI, FCM_MAY_HAVE_MESSAGES_KILL_SWITCH, PROMPT_FOR_NOTIFICATION_LOGS, - PROMPT_FOR_NOTIFICATION_CONFIG + PROMPT_FOR_NOTIFICATION_CONFIG, + PROMPT_BATTERY_SAVER ); /** @@ -631,6 +634,11 @@ public final class FeatureFlags { public static String delayedNotificationsPromptConfig() { return getString(PROMPT_FOR_NOTIFICATION_CONFIG, ""); } + + public static String promptBatterySaver() { + return getString(PROMPT_BATTERY_SAVER, "*"); + } + /** Only for rendering debug info. */ public static synchronized @NonNull Map getMemoryValues() { return new TreeMap<>(REMOTE_VALUES); diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/LocaleFeatureFlags.java b/app/src/main/java/org/thoughtcrime/securesms/util/LocaleFeatureFlags.java index 8f790694aa..c121b0dd18 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/LocaleFeatureFlags.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/LocaleFeatureFlags.java @@ -68,6 +68,11 @@ public final class LocaleFeatureFlags { public static boolean isDelayedNotificationPromptEnabled() { return isEnabled(FeatureFlags.PROMPT_FOR_NOTIFICATION_LOGS, FeatureFlags.promptForDelayedNotificationLogs()); } + + public static boolean isBatterySaverPromptEnabled() { + return isEnabled(FeatureFlags.PROMPT_BATTERY_SAVER, FeatureFlags.PROMPT_BATTERY_SAVER); + } + /** * Parses a comma-separated list of country codes colon-separated from how many buckets out of 1 million * should be enabled to see this megaphone in that country code. At the end of the list, an optional diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/PowerManagerCompat.java b/app/src/main/java/org/thoughtcrime/securesms/util/PowerManagerCompat.java index e5f8273f3d..8c202e3a40 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/PowerManagerCompat.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/PowerManagerCompat.java @@ -19,6 +19,13 @@ public class PowerManagerCompat { return false; } + public static boolean isIgnoringBatteryOptimizations(@NonNull Context context) { + if (Build.VERSION.SDK_INT < 23) { + return true; + } + return ServiceUtil.getPowerManager(context).isIgnoringBatteryOptimizations(context.getPackageName()); + } + @RequiresApi(api = 23) public static void requestIgnoreBatteryOptimizations(@NonNull Context context) { Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, diff --git a/app/src/main/res/drawable/ic_troubleshoot_notification.xml b/app/src/main/res/drawable/ic_troubleshoot_notification.xml new file mode 100644 index 0000000000..1bf4cf0590 --- /dev/null +++ b/app/src/main/res/drawable/ic_troubleshoot_notification.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + diff --git a/app/src/main/res/layout/prompt_battery_saver_bottom_sheet.xml b/app/src/main/res/layout/prompt_battery_saver_bottom_sheet.xml new file mode 100644 index 0000000000..17fd1d195d --- /dev/null +++ b/app/src/main/res/layout/prompt_battery_saver_bottom_sheet.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4ffe0946aa..fbf6905a19 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -910,6 +910,16 @@ Debug logs helps us diagnose and fix the issue, and do not contain identifying information. + + Notifications may be delayed due to battery optimizations + + You can disable battery optimizations for Signal to ensure that message notifications will not be delayed. + + + Continue + + Dismiss + Pending group invites Requests @@ -3105,6 +3115,8 @@ Ringtone Message font size Priority + + Troubleshoot notifications Censorship circumvention