From 40c4633d411d587747b46c8c93263b69af6c6e32 Mon Sep 17 00:00:00 2001 From: jeffrey-signal Date: Tue, 28 Apr 2026 14:46:00 -0400 Subject: [PATCH] Add utility method for resolving a FragmentActivity from Context. --- .../securesms/audio/SignalBluetoothManager.kt | 2 +- .../backup/v2/ArchiveRestoreProgress.kt | 3 +- .../components/emoji/MediaKeyboard.java | 34 +++++++------------ .../securesms/util/ContextExtensions.kt | 15 -------- .../webrtc/audio/SignalAudioManager.kt | 3 +- .../org/signal/core/util/ContextExtensions.kt | 30 ++++++++++++++++ 6 files changed, 48 insertions(+), 39 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/util/ContextExtensions.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/audio/SignalBluetoothManager.kt b/app/src/main/java/org/thoughtcrime/securesms/audio/SignalBluetoothManager.kt index aeda6a6f03..fbed5fac2c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/audio/SignalBluetoothManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/audio/SignalBluetoothManager.kt @@ -16,8 +16,8 @@ import android.content.Intent import android.content.IntentFilter import android.media.AudioManager import org.signal.core.util.logging.Log +import org.signal.core.util.safeUnregisterReceiver import org.thoughtcrime.securesms.dependencies.AppDependencies -import org.thoughtcrime.securesms.util.safeUnregisterReceiver import org.thoughtcrime.securesms.webrtc.audio.SignalAudioHandler import java.util.concurrent.TimeUnit diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ArchiveRestoreProgress.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ArchiveRestoreProgress.kt index 5000dc057c..73358e074d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ArchiveRestoreProgress.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/ArchiveRestoreProgress.kt @@ -19,6 +19,7 @@ import kotlinx.coroutines.flow.update import org.signal.core.util.bytes import org.signal.core.util.concurrent.SignalExecutors import org.signal.core.util.logging.Log +import org.signal.core.util.safeUnregisterReceiver import org.signal.core.util.throttleLatest import org.thoughtcrime.securesms.BuildConfig import org.thoughtcrime.securesms.attachments.AttachmentId @@ -31,7 +32,6 @@ import org.thoughtcrime.securesms.jobmanager.impl.DiskSpaceNotLowConstraint import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint import org.thoughtcrime.securesms.jobmanager.impl.WifiConstraint import org.thoughtcrime.securesms.keyvalue.SignalStore -import org.thoughtcrime.securesms.util.safeUnregisterReceiver import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicLong @@ -203,6 +203,7 @@ object ArchiveRestoreProgress { state.hasActivelyRestoredThisRun -> ArchiveRestoreProgressState.RestoreStatus.FINISHED else -> ArchiveRestoreProgressState.RestoreStatus.NONE } + else -> { val availableBytes = SignalStore.backup.spaceAvailableOnDiskBytes diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboard.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboard.java index 93dca90b79..c777a82193 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboard.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/MediaKeyboard.java @@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.components.emoji; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; -import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.ViewGroup; import android.widget.FrameLayout; @@ -15,6 +14,7 @@ import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; +import org.signal.core.util.ContextExtensionsKt; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.components.InputAwareLayout.InputView; @@ -29,12 +29,12 @@ public class MediaKeyboard extends FrameLayout implements InputView { private static final String EMOJI_SEARCH = "emoji_search_fragment"; @Nullable private MediaKeyboardListener keyboardListener; - private boolean isInitialised; - private int latestKeyboardHeight; - private State keyboardState; - private KeyboardPagerFragment keyboardPagerFragment; - private FragmentManager fragmentManager; - private int mediaKeyboardTheme; + private boolean isInitialised; + private int latestKeyboardHeight; + private State keyboardState; + private KeyboardPagerFragment keyboardPagerFragment; + private FragmentManager fragmentManager; + private int mediaKeyboardTheme; public MediaKeyboard(Context context) { this(context, null); @@ -175,7 +175,7 @@ public class MediaKeyboard extends FrameLayout implements InputView { LayoutInflater.from(getContext()).inflate(R.layout.media_keyboard, this, true); if (fragmentManager == null) { - FragmentActivity activity = resolveActivity(getContext()); + FragmentActivity activity = ContextExtensionsKt.requireFragmentActivity(getContext()); fragmentManager = activity.getSupportFragmentManager(); } @@ -188,25 +188,17 @@ public class MediaKeyboard extends FrameLayout implements InputView { .replace(R.id.media_keyboard_fragment_container, keyboardPagerFragment, TAG) .commitNowAllowingStateLoss(); - keyboardState = State.NORMAL; - latestKeyboardHeight = -1; - isInitialised = true; - } - } - - private static FragmentActivity resolveActivity(@Nullable Context context) { - if (context instanceof FragmentActivity) { - return (FragmentActivity) context; - } else if (context instanceof ContextThemeWrapper) { - return resolveActivity(((ContextThemeWrapper) context).getBaseContext()); - } else { - throw new IllegalStateException("Could not locate FragmentActivity"); + keyboardState = State.NORMAL; + latestKeyboardHeight = -1; + isInitialised = true; } } public interface MediaKeyboardListener { void onShown(); + void onHidden(); + void onKeyboardChanged(@NonNull KeyboardPage page); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ContextExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/util/ContextExtensions.kt deleted file mode 100644 index 410804f49d..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/util/ContextExtensions.kt +++ /dev/null @@ -1,15 +0,0 @@ -package org.thoughtcrime.securesms.util - -import android.content.BroadcastReceiver -import android.content.Context - -fun Context.safeUnregisterReceiver(receiver: BroadcastReceiver?) { - if (receiver == null) { - return - } - - try { - unregisterReceiver(receiver) - } catch (e: IllegalArgumentException) { - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.kt index 7f3b7e2435..1f6207f7ca 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.kt @@ -17,13 +17,13 @@ import android.os.Build import org.signal.core.util.ThreadUtil import org.signal.core.util.concurrent.SignalExecutors import org.signal.core.util.logging.Log +import org.signal.core.util.safeUnregisterReceiver import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.audio.AudioDeviceUpdatedListener import org.thoughtcrime.securesms.audio.SignalBluetoothManager import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.service.webrtc.AndroidTelecomUtil -import org.thoughtcrime.securesms.util.safeUnregisterReceiver import org.whispersystems.signalservice.api.util.Preconditions private val TAG = Log.tag(SignalAudioManager::class.java) @@ -397,6 +397,7 @@ class FullSignalAudioManager(context: Context, eventListener: EventListener?) : AudioDevice.SPEAKER_PHONE } } + else -> throw AssertionError("Invalid default audio device selection") } diff --git a/core/util/src/main/java/org/signal/core/util/ContextExtensions.kt b/core/util/src/main/java/org/signal/core/util/ContextExtensions.kt index 99fa3e5de4..6a62bf8703 100644 --- a/core/util/src/main/java/org/signal/core/util/ContextExtensions.kt +++ b/core/util/src/main/java/org/signal/core/util/ContextExtensions.kt @@ -6,8 +6,38 @@ package org.signal.core.util import android.app.DownloadManager +import android.content.BroadcastReceiver import android.content.Context +import android.content.ContextWrapper +import androidx.fragment.app.FragmentActivity fun Context.getDownloadManager(): DownloadManager { return this.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager } + +fun Context.safeUnregisterReceiver(receiver: BroadcastReceiver?) { + if (receiver == null) return + + try { + unregisterReceiver(receiver) + } catch (_: IllegalArgumentException) { + } +} + +/** + * Attempts to resolve a [FragmentActivity] from this context. Throws an [IllegalStateException] if none is found. + */ +fun Context.requireFragmentActivity(): FragmentActivity { + return resolveFragmentActivity() ?: throw IllegalStateException("Required FragmentActivity context, but found: ${javaClass.name} instead.") +} + +/** + * Attempts to resolve a [FragmentActivity] from this context. Returns null for non-activity contexts. + */ +fun Context.resolveFragmentActivity(): FragmentActivity? { + return when (val context = this) { + is FragmentActivity -> context + is ContextWrapper -> context.baseContext.takeUnless { it === this }?.resolveFragmentActivity() + else -> null + } +}