From bd121e47c8612abdfb5245c72b29c8230ed57d06 Mon Sep 17 00:00:00 2001 From: DivyaKhunt07 Date: Tue, 17 Feb 2026 12:49:32 +0000 Subject: [PATCH] Fix bubble desired height calculation. --- .../components/InsetAwareConstraintLayout.kt | 20 +++++++++++++- .../BubbleConversationActivity.kt | 10 ++++++- .../conversation/v2/ConversationFragment.kt | 8 +++++- .../notifications/v2/NotificationBuilder.kt | 3 +-- .../securesms/util/BubbleUtil.java | 27 +++++++++++++++++++ 5 files changed, 63 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/InsetAwareConstraintLayout.kt b/app/src/main/java/org/thoughtcrime/securesms/components/InsetAwareConstraintLayout.kt index 9e7e71314d..d612053eba 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/InsetAwareConstraintLayout.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/InsetAwareConstraintLayout.kt @@ -64,6 +64,7 @@ open class InsetAwareConstraintLayout @JvmOverloads constructor( private var insets: WindowInsetsCompat? = null private var windowTypes: Int = InsetAwareConstraintLayout.windowTypes + private var navigationBarInsetOverride: Int? = null private val windowInsetsListener = androidx.core.view.OnApplyWindowInsetsListener { _, insets -> this.insets = insets @@ -114,6 +115,23 @@ open class InsetAwareConstraintLayout @JvmOverloads constructor( } } + fun setNavigationBarInsetOverride(inset: Int?) { + if (navigationBarInsetOverride == inset) return + navigationBarInsetOverride = inset + if (inset != null) { + // Apply immediately so layout is correct before next inset dispatch (important for + // Android 15 bubble where insets can arrive late or with different values). + navigationBarGuideline?.setGuidelineEnd(inset) + if (!isKeyboardShowing) { + keyboardGuideline?.setGuidelineEnd(inset) + } + requestLayout() + } + if (insets != null) { + applyInsets(insets!!.getInsets(windowTypes), insets!!.getInsets(keyboardType)) + } + } + fun addKeyboardStateListener(listener: KeyboardStateListener) { keyboardStateListeners += listener } @@ -134,7 +152,7 @@ open class InsetAwareConstraintLayout @JvmOverloads constructor( val isLtr = ViewUtil.isLtr(this) val statusBar = windowInsets.top - val navigationBar = if (windowInsets.bottom == 0 && Build.VERSION.SDK_INT <= 29) { + val navigationBar = navigationBarInsetOverride ?: if (windowInsets.bottom == 0 && Build.VERSION.SDK_INT <= 29) { ViewUtil.getNavigationBarHeight(resources) } else { windowInsets.bottom diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/BubbleConversationActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/BubbleConversationActivity.kt index 2075bf034f..1d6aaf4044 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/BubbleConversationActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/BubbleConversationActivity.kt @@ -1,17 +1,25 @@ package org.thoughtcrime.securesms.conversation +import android.os.Bundle +import androidx.core.view.WindowCompat import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.conversation.v2.ConversationActivity import org.thoughtcrime.securesms.util.ViewUtil /** * Activity which encapsulates a conversation for a Bubble window. - * + *8 * This activity exists so that we can override some of its manifest parameters * without clashing with [ConversationActivity] and provide an API-level * independent "is in bubble?" check. */ class BubbleConversationActivity : ConversationActivity() { + + override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) { + WindowCompat.setDecorFitsSystemWindows(window, false) + super.onCreate(savedInstanceState, ready) + } + override fun onPause() { super.onPause() ViewUtil.hideKeyboard(this, findViewById(R.id.fragment_container)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt index ec4a93bcd5..18a0e35210 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt @@ -637,8 +637,14 @@ class ConversationFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { binding.toolbar.isBackInvokedCallbackEnabled = false - binding.root.setUseWindowTypes(args.conversationScreenType == ConversationScreenType.NORMAL && !resources.getWindowSizeClass().isSplitPane()) + if (args.conversationScreenType == ConversationScreenType.BUBBLE) { + binding.root.setNavigationBarInsetOverride(0) + view.post { + ViewCompat.requestApplyInsets(binding.root) + binding.root.requestLayout() + } + } disposables.bindTo(viewLifecycleOwner) diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationBuilder.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationBuilder.kt index 5cb706380b..8674a240d3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationBuilder.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationBuilder.kt @@ -362,14 +362,13 @@ sealed class NotificationBuilder(protected val context: Context) { if (intent != null) { val bubbleMetadata = NotificationCompat.BubbleMetadata.Builder(intent, AvatarUtil.getIconCompat(context, conversation.recipient)) .setAutoExpandBubble(bubbleState === BubbleUtil.BubbleState.SHOWN) - .setDesiredHeight(600) + .setDesiredHeight(BubbleUtil.getDesiredBubbleHeightPx(context)) .setSuppressNotification(bubbleState === BubbleUtil.BubbleState.SHOWN) .build() builder.bubbleMetadata = bubbleMetadata } } - override fun setLights(@ColorInt color: Int, onTime: Int, offTime: Int) { if (NotificationChannels.supported()) { return diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/BubbleUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/BubbleUtil.java index 6170b4e913..a1576cac97 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/BubbleUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/BubbleUtil.java @@ -4,8 +4,10 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.Context; +import android.graphics.Rect; import android.os.Build; import android.service.notification.StatusBarNotification; +import android.view.WindowManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -14,6 +16,7 @@ import androidx.annotation.WorkerThread; import com.annimon.stream.Stream; +import org.signal.core.util.DimensionUnit; import org.signal.core.util.concurrent.SignalExecutors; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.dependencies.AppDependencies; @@ -35,6 +38,8 @@ public final class BubbleUtil { private static final String TAG = Log.tag(BubbleUtil.class); private static String currentState = ""; + private static final float MIN_BUBBLE_HEIGHT_DP = 600f; + private static final float BUBBLE_HEIGHT_SCREEN_FRACTION = 0.9f; private BubbleUtil() { } @@ -134,4 +139,26 @@ public final class BubbleUtil { SHOWN, HIDDEN } + + public static int getDesiredBubbleHeightPx(@NonNull Context context) { + int minHeightPx = (int) DimensionUnit.DP.toPixels(MIN_BUBBLE_HEIGHT_DP); + int screenHeightPx = context.getResources().getDisplayMetrics().heightPixels; + + if (Build.VERSION.SDK_INT >= 30) { + WindowManager wm = context.getSystemService(WindowManager.class); + if (wm != null) { + Rect bounds = wm.getCurrentWindowMetrics().getBounds(); + screenHeightPx = bounds.height(); + } + } + + if (screenHeightPx <= 0) { + return minHeightPx; + } + + int targetPx = (int) (screenHeightPx * BUBBLE_HEIGHT_SCREEN_FRACTION); + int desiredPx = Math.max(minHeightPx, targetPx); + + return Math.min(desiredPx, screenHeightPx); + } }