From 39c1c1e3715ebb50f595082155a54b99a23a3ad9 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Thu, 17 Aug 2023 15:02:16 -0400 Subject: [PATCH] Fix ANR-like bug when resuming MainActivity. --- .../thoughtcrime/securesms/MainActivity.java | 43 ++++++++---- .../PromptBatterySaverDialogFragment.kt | 3 +- .../NotificationsSettingsFragment.kt | 2 +- .../SlowNotificationsViewModel.kt | 68 +++++++++++++++++++ gradle.properties | 2 +- 5 files changed, 101 insertions(+), 17 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/notifications/SlowNotificationsViewModel.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java index 6bdd3323ac..fe2461bd14 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MainActivity.java @@ -1,10 +1,10 @@ package org.thoughtcrime.securesms; +import android.annotation.SuppressLint; 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,6 +16,7 @@ import androidx.lifecycle.ViewModelProvider; import com.google.android.material.dialog.MaterialAlertDialogBuilder; +import org.signal.core.util.concurrent.LifecycleDisposable; import org.thoughtcrime.securesms.components.DebugLogsPromptDialogFragment; import org.thoughtcrime.securesms.components.PromptBatterySaverDialogFragment; import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController; @@ -24,8 +25,7 @@ import org.thoughtcrime.securesms.conversationlist.RelinkDevicesReminderBottomSh 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.notifications.SlowNotificationsViewModel; import org.thoughtcrime.securesms.stories.tabs.ConversationListTabRepository; import org.thoughtcrime.securesms.stories.tabs.ConversationListTabsViewModel; import org.thoughtcrime.securesms.util.AppStartup; @@ -33,7 +33,6 @@ 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; @@ -46,6 +45,9 @@ public class MainActivity extends PassphraseRequiredActivity implements VoiceNot private VoiceNoteMediaController mediaController; private ConversationListTabsViewModel conversationListTabsViewModel; + private SlowNotificationsViewModel slowNotificationsViewModel; + + private final LifecycleDisposable lifecycleDisposable = new LifecycleDisposable(); private boolean onFirstRender = false; @@ -80,6 +82,7 @@ public class MainActivity extends PassphraseRequiredActivity implements VoiceNot } }); + lifecycleDisposable.bindTo(this); mediaController = new VoiceNoteMediaController(this, true); @@ -95,6 +98,28 @@ public class MainActivity extends PassphraseRequiredActivity implements VoiceNot conversationListTabsViewModel = new ViewModelProvider(this, factory).get(ConversationListTabsViewModel.class); updateTabVisibility(); + + slowNotificationsViewModel = new ViewModelProvider(this).get(SlowNotificationsViewModel.class); + + lifecycleDisposable.add( + slowNotificationsViewModel + .getSlowNotificationState() + .subscribe(this::presentSlowNotificationState) + ); + } + + @SuppressLint("NewApi") + private void presentSlowNotificationState(SlowNotificationsViewModel.State slowNotificationState) { + switch (slowNotificationState) { + case NONE: + break; + case PROMPT_BATTERY_SAVER_DIALOG: + PromptBatterySaverDialogFragment.show(getSupportFragmentManager()); + break; + case PROMPT_DEBUGLOGS: + DebugLogsPromptDialogFragment.show(this, getSupportFragmentManager()); + break; + } } @Override @@ -143,15 +168,7 @@ public class MainActivity extends PassphraseRequiredActivity implements VoiceNot updateTabVisibility(); - 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()); - } - } + slowNotificationsViewModel.checkSlowNotificationHeuristics(); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/PromptBatterySaverDialogFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/PromptBatterySaverDialogFragment.kt index 0a343fe296..cabf4c752d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/PromptBatterySaverDialogFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/PromptBatterySaverDialogFragment.kt @@ -5,7 +5,6 @@ package org.thoughtcrime.securesms.components -import android.content.Context import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -27,7 +26,7 @@ class PromptBatterySaverDialogFragment : FixedRoundedCornerBottomSheetDialogFrag companion object { @JvmStatic - fun show(context: Context, fragmentManager: FragmentManager) { + fun show(fragmentManager: FragmentManager) { if (fragmentManager.findFragmentByTag(BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG) == null) { PromptBatterySaverDialogFragment().apply { arguments = bundleOf() 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 f3148a6132..0e0df65181 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 @@ -190,7 +190,7 @@ class NotificationsSettingsFragment : DSLSettingsFragment(R.string.preferences__ title = DSLSettingsText.from(R.string.preferences_notifications__troubleshoot), isEnabled = true, onClick = { - PromptBatterySaverDialogFragment.show(requireContext(), childFragmentManager) + PromptBatterySaverDialogFragment.show(childFragmentManager) } ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/SlowNotificationsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/SlowNotificationsViewModel.kt new file mode 100644 index 0000000000..37f931928a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/SlowNotificationsViewModel.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.notifications + +import android.os.Build +import androidx.lifecycle.ViewModel +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.core.Single +import io.reactivex.rxjava3.schedulers.Schedulers +import io.reactivex.rxjava3.subjects.BehaviorSubject +import org.thoughtcrime.securesms.notifications.SlowNotificationHeuristics.isHavingDelayedNotifications +import org.thoughtcrime.securesms.notifications.SlowNotificationHeuristics.isPotentiallyCausedByBatteryOptimizations +import org.thoughtcrime.securesms.notifications.SlowNotificationHeuristics.shouldPromptBatterySaver +import org.thoughtcrime.securesms.notifications.SlowNotificationHeuristics.shouldPromptUserForLogs +import java.util.concurrent.TimeUnit + +/** + * View model for checking for slow notifications and if we should prompt the user with help or for information. + */ +class SlowNotificationsViewModel : ViewModel() { + + private val checkSubject = BehaviorSubject.create() + + val slowNotificationState: Observable + + init { + slowNotificationState = checkSubject + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.io()) + .throttleFirst(1, TimeUnit.MINUTES) + .switchMapSingle { + checkHeuristics() + } + .distinctUntilChanged() + .observeOn(AndroidSchedulers.mainThread()) + } + + fun checkSlowNotificationHeuristics() { + checkSubject.onNext(Unit) + } + + private fun checkHeuristics(): Single { + return Single.fromCallable { + var state = State.NONE + if (isHavingDelayedNotifications()) { + if (isPotentiallyCausedByBatteryOptimizations() && Build.VERSION.SDK_INT >= 23) { + if (shouldPromptBatterySaver()) { + state = State.PROMPT_BATTERY_SAVER_DIALOG + } + } else if (shouldPromptUserForLogs()) { + state = State.PROMPT_DEBUGLOGS + } + } + + return@fromCallable state + }.subscribeOn(Schedulers.io()) + } + + enum class State { + NONE, + PROMPT_BATTERY_SAVER_DIALOG, + PROMPT_DEBUGLOGS + } +} diff --git a/gradle.properties b/gradle.properties index dfee94a2f4..d7cb0fccbd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -org.gradle.jvmargs=-Xmx4g -Xms256m -XX:MaxMetaspaceSize=1g +org.gradle.jvmargs=-Xmx6g -Xms256m -XX:MaxMetaspaceSize=1g android.useAndroidX=true android.enableJetifier=true kapt.incremental.apt=false