commit fe3ae15a80a7a2d4ffb387294802b5e15722234a parent fa7d7f5d1670205ba97e19879f0df7718e18f7e4 Author: Mugurell <Mugurell@users.noreply.github.com> Date: Wed, 17 Dec 2025 16:06:37 +0000 Bug 1988730 - part 1 - Improve and upstream the keyboardAsState API to A-C r=android-reviewers,moyin,Roger Saw in my testing that the current version of keyboardAsState is not entirely reliable and sometimes returning the wrong value. Checking Chrome sources I saw there the insets are derived from current Activity's root View, approach I also borrowed. Added also the `isImeVisible` convenience method already available in the jetpack Compose framework as the source of the data on Android 13+ while on lower Android versions we'll use the more expensive but more reliable GlobalLayoutListener approach to know when to recheck and report the keyboard state. Differential Revision: https://phabricator.services.mozilla.com/D275988 Diffstat:
19 files changed, 198 insertions(+), 186 deletions(-)
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml @@ -119,6 +119,7 @@ kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx- # AndroidX androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" } +androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity" } androidx-activity-ktx = { group = "androidx.activity", name = "activity-ktx", version.ref = "activity" } androidx-annotation = { group = "androidx.annotation", name = "annotation", version.ref = "annotation" } androidx-annotation-experimental = { group = "androidx.annotation", name = "annotation-experimental", version.ref = "annotation-experimental" } diff --git a/mobile/android/android-components/components/support/utils/build.gradle b/mobile/android/android-components/components/support/utils/build.gradle @@ -2,10 +2,17 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +plugins { + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.compose) +} + apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' android { + buildFeatures { + compose = true + } namespace = 'mozilla.components.support.utils' } @@ -16,8 +23,10 @@ dependencies { implementation project(':components:support-base') implementation libs.androidx.activity + implementation libs.androidx.activity.compose implementation libs.androidx.annotation implementation platform(libs.androidx.compose.bom) + implementation libs.androidx.compose.foundation implementation libs.androidx.compose.ui implementation libs.androidx.core implementation libs.androidx.core.ktx diff --git a/mobile/android/android-components/components/support/utils/src/main/java/mozilla/components/support/utils/KeyboardState.kt b/mobile/android/android-components/components/support/utils/src/main/java/mozilla/components/support/utils/KeyboardState.kt @@ -0,0 +1,54 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.support.utils + +import android.view.View +import android.view.ViewTreeObserver +import androidx.activity.ComponentActivity +import androidx.activity.compose.LocalActivity +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import mozilla.components.support.utils.ext.isKeyboardVisible + +/** + * Detects if the keyboard is opened or closed and returns as a [KeyboardState]. + */ +@OptIn(ExperimentalLayoutApi::class) +@Composable +fun keyboardAsState(): State<KeyboardState> { + val keyboardState = remember { mutableStateOf(KeyboardState.Closed) } + + val currentActivity = (LocalActivity.current as? ComponentActivity) + DisposableEffect(currentActivity) { + var layoutListener: ViewTreeObserver.OnGlobalLayoutListener? + val contentView: View? = currentActivity?.findViewById(android.R.id.content) + + layoutListener = ViewTreeObserver.OnGlobalLayoutListener { + keyboardState.value = if (contentView?.isKeyboardVisible() == true) { + KeyboardState.Opened + } else { + KeyboardState.Closed + } + } + contentView?.viewTreeObserver?.addOnGlobalLayoutListener(layoutListener) + + onDispose { + contentView?.viewTreeObserver?.removeOnGlobalLayoutListener(layoutListener) + } + } + + return keyboardState +} + +/** + * Represents the current state of the keyboard, opened or closed. + */ +enum class KeyboardState { + Opened, Closed +} diff --git a/mobile/android/android-components/components/support/utils/src/main/java/mozilla/components/support/utils/ext/View.kt b/mobile/android/android-components/components/support/utils/src/main/java/mozilla/components/support/utils/ext/View.kt @@ -6,6 +6,7 @@ package mozilla.components.support.utils.ext import android.content.Context import android.content.ContextWrapper +import android.graphics.Rect import android.os.Build.VERSION.SDK_INT import android.os.Build.VERSION_CODES.TIRAMISU import android.view.View @@ -15,6 +16,10 @@ import androidx.activity.OnBackPressedDispatcher import androidx.activity.OnBackPressedDispatcherOwner import androidx.activity.addCallback import androidx.activity.findViewTreeOnBackPressedDispatcherOwner +import androidx.annotation.VisibleForTesting +import androidx.core.view.ViewCompat +import androidx.core.view.ViewCompat.getRootWindowInsets +import androidx.core.view.WindowInsetsCompat import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.findViewTreeLifecycleOwner @@ -76,3 +81,42 @@ private fun findBackPressedDispatcherOwner(context: Context): OnBackPressedDispa } return null } + +/** + * Checks if the keyboard is visible + * + * Inspired by https://stackoverflow.com/questions/2150078/how-to-check-visibility-of-software-keyboard-in-android + * API 30 adds a native method for this. We should use it (and a compat method if one + * is added) when it becomes available + */ +fun View.isKeyboardVisible(): Boolean { + return getKeyboardHeight() > 0 +} + +/** + * Calculates the height of the onscreen keyboard. + */ +fun View.getKeyboardHeight( + rootViewHeight: Int = rootView.height, + windowVisibleDisplayFrame: Rect = getWindowVisibleDisplayFrame(), + bottomInset: Int = getWindowInsets()?.bottom() ?: 0, +): Int { + val statusBarHeight = windowVisibleDisplayFrame.top + return rootViewHeight - (windowVisibleDisplayFrame.height() + statusBarHeight) - bottomInset +} + +/** + * A safer version of [ViewCompat.getRootWindowInsets] that does not throw a NullPointerException + * if the view is not attached. + */ +fun View.getWindowInsets(): WindowInsetsCompat? { + return rootWindowInsets?.let { + WindowInsetsCompat.toWindowInsetsCompat(it) + } +} + +@VisibleForTesting +internal fun View.getWindowVisibleDisplayFrame(): Rect = with(Rect()) { + getWindowVisibleDisplayFrame(this) + this +} diff --git a/mobile/android/android-components/components/support/utils/src/test/java/mozilla/components/support/utils/ext/ViewTest.kt b/mobile/android/android-components/components/support/utils/src/test/java/mozilla/components/support/utils/ext/ViewTest.kt @@ -0,0 +1,70 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package mozilla.components.support.utils.ext + +import android.graphics.Rect +import android.view.View +import android.view.WindowInsets +import androidx.core.view.WindowInsetsCompat +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.support.test.mock +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.doReturn + +@RunWith(AndroidJUnit4::class) +class ViewTest { + val view: View = mock() + + @Test + fun `getKeyboardHeight returns the keyboard height when keyboard is considered open`() { + // Test the pure calculation logic directly + val result = view.getKeyboardHeight( + rootViewHeight = 1500, + windowVisibleDisplayFrame = Rect(0, 0, 500, 1000), + bottomInset = 0, + ) + assertEquals(500, result) + } + + @Test + fun `getKeyboardHeight returns zero when keyboard is considered closed`() { + // Test the pure calculation logic directly + val result = view.getKeyboardHeight( + rootViewHeight = 1000, + windowVisibleDisplayFrame = Rect(0, 0, 500, 1000), + bottomInset = 0, + ) + assertEquals(0, result) + } + + @Test + fun `getWindowInsets returns null when the system insets don't exist`() { + doReturn(null).`when`(view).rootWindowInsets + assertEquals(null, view.getWindowInsets()) + } + + @Test + fun `getWindowInsets returns the compat insets when the system insets exist`() { + val rootInsets: WindowInsets = mock() + doReturn(rootInsets).`when`(view).rootWindowInsets + + // Construct the expected object directly instead of mocking the static method + val expectedInsets = WindowInsetsCompat.toWindowInsetsCompat(rootInsets) + assertEquals(expectedInsets, view.getWindowInsets()) + } + + @Test + fun `getKeyboardHeight accounts for status bar and navigation bar`() { + val result = view.getKeyboardHeight( + rootViewHeight = 1000, + windowVisibleDisplayFrame = Rect(0, 50, 1000, 500), + bottomInset = 50, + ) + + assertEquals(450, result) + } +} diff --git a/mobile/android/android-components/docs/changelog.md b/mobile/android/android-components/docs/changelog.md @@ -5,6 +5,8 @@ permalink: /changelog/ --- # 148.0 (In Development) +* **support-utils** + * 🆕 New `keyboardAsState` available to use in Jetpack Compose code to know when the IME is shown or hidden. This works more reliably on older Android versions than the frameworks `isImeVisible` API. [Bug 1988730](https://bugzilla.mozilla.org/show_bug.cgi?id=1988730). * **browser-engine-gecko** and **concept-engine** * Add optional link text support to HitResult.UNKNOWN to allow getting the text associated with a link in response to a long click * **feature-contextmenu** diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/browser/ToolbarGestureHandler.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/browser/ToolbarGestureHandler.kt @@ -25,13 +25,13 @@ import mozilla.components.browser.state.store.BrowserStore import mozilla.components.feature.tabs.TabsUseCases import mozilla.components.support.ktx.android.view.getRectWithViewLocation import mozilla.components.support.utils.ext.bottom +import mozilla.components.support.utils.ext.getWindowInsets +import mozilla.components.support.utils.ext.isKeyboardVisible import mozilla.components.support.utils.ext.mandatorySystemGestureInsets import mozilla.telemetry.glean.private.NoExtras import org.mozilla.fenix.GleanMetrics.Events import org.mozilla.fenix.R import org.mozilla.fenix.ext.getRectWithScreenLocation -import org.mozilla.fenix.ext.getWindowInsets -import org.mozilla.fenix.ext.isKeyboardVisible import org.mozilla.fenix.ext.maxActiveTime import org.mozilla.fenix.ext.pixelSizeFor import org.mozilla.fenix.ext.settings diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/browser/TranslationsBannerIntegration.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/browser/TranslationsBannerIntegration.kt @@ -24,11 +24,11 @@ import mozilla.components.browser.state.store.BrowserStore import mozilla.components.lib.state.ext.flowScoped import mozilla.components.lib.state.ext.observeAsComposableState import mozilla.components.lib.state.helpers.AbstractBinding +import mozilla.components.support.utils.KeyboardState +import mozilla.components.support.utils.keyboardAsState import org.mozilla.fenix.R import org.mozilla.fenix.browser.store.BrowserScreenState import org.mozilla.fenix.browser.store.BrowserScreenStore -import org.mozilla.fenix.compose.utils.KeyboardState -import org.mozilla.fenix.compose.utils.keyboardAsState import org.mozilla.fenix.databinding.FragmentBrowserBinding import org.mozilla.fenix.ext.settings import org.mozilla.fenix.theme.FirefoxTheme diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/MenuDialogFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/menu/MenuDialogFragment.kt @@ -60,6 +60,7 @@ import mozilla.components.lib.state.ext.observeAsState import mozilla.components.service.fxa.manager.AccountState.NotAuthenticated import mozilla.components.support.base.feature.ViewBoundFeatureWrapper import mozilla.components.support.ktx.android.util.dpToPx +import mozilla.components.support.utils.ext.getWindowInsets import mozilla.components.support.utils.ext.isLandscape import mozilla.components.support.utils.ext.top import mozilla.telemetry.glean.private.NoExtras @@ -88,7 +89,6 @@ import org.mozilla.fenix.components.menu.store.MenuStore import org.mozilla.fenix.components.menu.store.TranslationInfo import org.mozilla.fenix.components.menu.store.WebExtensionMenuItem import org.mozilla.fenix.ext.components -import org.mozilla.fenix.ext.getWindowInsets import org.mozilla.fenix.ext.openSetDefaultBrowserOption import org.mozilla.fenix.ext.pixelSizeFor import org.mozilla.fenix.ext.requireComponents diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserNavigationBar.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserNavigationBar.kt @@ -22,9 +22,9 @@ import mozilla.components.compose.browser.toolbar.store.ToolbarGravity.Bottom import mozilla.components.compose.browser.toolbar.store.ToolbarGravity.Top import mozilla.components.feature.toolbar.ToolbarBehaviorController import mozilla.components.lib.state.ext.observeAsState +import mozilla.components.support.utils.KeyboardState +import mozilla.components.support.utils.keyboardAsState import org.mozilla.fenix.R -import org.mozilla.fenix.compose.utils.KeyboardState -import org.mozilla.fenix.compose.utils.keyboardAsState import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.utils.Settings diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/toolbar/FenixBrowserToolbarView.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/toolbar/FenixBrowserToolbarView.kt @@ -17,12 +17,12 @@ import mozilla.components.browser.state.state.ExternalAppType import mozilla.components.concept.engine.EngineView import mozilla.components.concept.toolbar.ScrollableToolbar import mozilla.components.support.ktx.android.view.findViewInHierarchy +import mozilla.components.support.utils.KeyboardState +import mozilla.components.support.utils.keyboardAsState import mozilla.components.ui.widgets.behavior.DependencyGravity.Bottom import mozilla.components.ui.widgets.behavior.DependencyGravity.Top import mozilla.components.ui.widgets.behavior.EngineViewScrollingBehavior import mozilla.components.ui.widgets.behavior.EngineViewScrollingBehaviorFactory -import org.mozilla.fenix.compose.utils.KeyboardState -import org.mozilla.fenix.compose.utils.keyboardAsState import org.mozilla.fenix.utils.Settings /** diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/utils/KeyboardState.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/compose/utils/KeyboardState.kt @@ -1,47 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.fenix.compose.utils - -import android.view.ViewTreeObserver -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.State -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.platform.LocalView -import org.mozilla.fenix.ext.isKeyboardVisible -import java.lang.ref.WeakReference - -/** - * Detects if the keyboard is opened or closed and returns as a [KeyboardState]. - */ -@Composable -fun keyboardAsState(): State<KeyboardState> { - val keyboardState = remember { mutableStateOf(KeyboardState.Closed) } - val view = WeakReference(LocalView.current) - DisposableEffect(view) { - val onGlobalListener = ViewTreeObserver.OnGlobalLayoutListener { - keyboardState.value = if (view.get()?.isKeyboardVisible() == true) { - KeyboardState.Opened - } else { - KeyboardState.Closed - } - } - view.get()?.viewTreeObserver?.addOnGlobalLayoutListener(onGlobalListener) - - onDispose { - view.get()?.viewTreeObserver?.removeOnGlobalLayoutListener(onGlobalListener) - } - } - - return keyboardState -} - -/** - * Represents the current state of the keyboard, opened or closed. - */ -enum class KeyboardState { - Opened, Closed -} diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/ext/Bitmap.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/ext/Bitmap.kt @@ -9,6 +9,7 @@ import android.graphics.Matrix import android.view.View import android.widget.ImageView import androidx.annotation.VisibleForTesting +import mozilla.components.support.utils.ext.getKeyboardHeight /** * This will scale the received [Bitmap] to the size of the [view]. It retains the bitmap's diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/ext/View.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/ext/View.kt @@ -12,10 +12,7 @@ import androidx.annotation.DimenRes import androidx.annotation.Dimension import androidx.annotation.Dimension.Companion.DP import androidx.annotation.VisibleForTesting -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat import mozilla.components.support.ktx.android.util.dpToPx -import mozilla.components.support.utils.ext.bottom import org.mozilla.fenix.components.Components /** @@ -85,61 +82,6 @@ fun View.getRectWithScreenLocation(): Rect { } /** - * A safer version of [ViewCompat.getRootWindowInsets] that does not throw a NullPointerException - * if the view is not attached. - */ -fun View.getWindowInsets(): WindowInsetsCompat? { - return rootWindowInsets?.let { - WindowInsetsCompat.toWindowInsetsCompat(it) - } -} - -/** - * Checks if the keyboard is visible - * - * Inspired by https://stackoverflow.com/questions/2150078/how-to-check-visibility-of-software-keyboard-in-android - * API 30 adds a native method for this. We should use it (and a compat method if one - * is added) when it becomes available - */ -fun View.isKeyboardVisible(): Boolean { - // Since we have insets, we don't need to guess what the keyboard height is. - return isKeyboardVisible(getKeyboardHeight()) -} - -@VisibleForTesting -internal fun isKeyboardVisible(keyboardHeight: Int): Boolean { - val minimumKeyboardHeight = 0 - return keyboardHeight > minimumKeyboardHeight -} - -@VisibleForTesting -internal fun View.getWindowVisibleDisplayFrame(): Rect = with(Rect()) { - getWindowVisibleDisplayFrame(this) - this -} - -/** - * Calculates the height of the onscreen keyboard. - */ -fun View.getKeyboardHeight(): Int { - return getKeyboardHeight( - rootViewHeight = rootView.height, - windowVisibleDisplayFrame = getWindowVisibleDisplayFrame(), - bottomInset = getWindowInsets()?.bottom() ?: 0, - ) -} - -@VisibleForTesting -internal fun getKeyboardHeight( - rootViewHeight: Int, - windowVisibleDisplayFrame: Rect, - bottomInset: Int, -): Int { - val statusBarHeight = windowVisibleDisplayFrame.top - return rootViewHeight - (windowVisibleDisplayFrame.height() + statusBarHeight) - bottomInset -} - -/** * Returns the pixel size for the given dimension resource ID. * * This is a wrapper around [Resources.getDimensionPixelSize], reducing verbosity when accessing diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -66,6 +66,8 @@ import mozilla.components.lib.state.ext.consumeFlow import mozilla.components.lib.state.ext.consumeFrom import mozilla.components.lib.state.ext.flow import mozilla.components.support.base.feature.ViewBoundFeatureWrapper +import mozilla.components.support.utils.KeyboardState +import mozilla.components.support.utils.keyboardAsState import mozilla.telemetry.glean.private.NoExtras import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.GleanMetrics.HomeScreen @@ -93,8 +95,6 @@ import org.mozilla.fenix.components.components import org.mozilla.fenix.components.toolbar.BottomToolbarContainerView import org.mozilla.fenix.compose.snackbar.Snackbar import org.mozilla.fenix.compose.snackbar.SnackbarState -import org.mozilla.fenix.compose.utils.KeyboardState -import org.mozilla.fenix.compose.utils.keyboardAsState import org.mozilla.fenix.databinding.FragmentHomeBinding import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.getBottomToolbarHeight diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/toolbar/HomeNavigationBar.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/home/toolbar/HomeNavigationBar.kt @@ -19,9 +19,9 @@ import mozilla.components.compose.browser.toolbar.store.BrowserToolbarStore import mozilla.components.compose.browser.toolbar.store.ToolbarGravity.Bottom import mozilla.components.compose.browser.toolbar.store.ToolbarGravity.Top import mozilla.components.lib.state.ext.observeAsState +import mozilla.components.support.utils.KeyboardState +import mozilla.components.support.utils.keyboardAsState import org.mozilla.fenix.R -import org.mozilla.fenix.compose.utils.KeyboardState -import org.mozilla.fenix.compose.utils.keyboardAsState import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.utils.Settings diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/microsurvey/ui/MicrosurveyRequestPrompt.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/microsurvey/ui/MicrosurveyRequestPrompt.kt @@ -33,10 +33,10 @@ import androidx.compose.ui.unit.dp import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview import mozilla.components.compose.base.button.FilledButton import mozilla.components.compose.base.button.IconButton +import mozilla.components.support.utils.KeyboardState +import mozilla.components.support.utils.keyboardAsState import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R -import org.mozilla.fenix.compose.utils.KeyboardState -import org.mozilla.fenix.compose.utils.keyboardAsState import org.mozilla.fenix.microsurvey.ui.ext.MicrosurveyUIData import org.mozilla.fenix.theme.FirefoxTheme import org.mozilla.fenix.theme.Theme diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/ext/BitmapTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/ext/BitmapTest.kt @@ -7,6 +7,7 @@ package org.mozilla.fenix.ext import android.view.View import io.mockk.every import io.mockk.mockk +import mozilla.components.support.utils.ext.getKeyboardHeight import org.junit.Assert.assertEquals import org.junit.Ignore import org.junit.Test diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/ext/ViewTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/ext/ViewTest.kt @@ -6,21 +6,16 @@ package org.mozilla.fenix.ext import android.graphics.Rect import android.view.View -import android.view.WindowInsets import android.widget.FrameLayout -import androidx.core.view.WindowInsetsCompat import io.mockk.MockKAnnotations import io.mockk.Runs import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.just -import io.mockk.mockk import io.mockk.slot import io.mockk.verify import mozilla.components.support.test.robolectric.testContext import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -75,44 +70,6 @@ class ViewTest { } @Test - fun `getWindowInsets returns null when the system insets don't exist`() { - every { view.rootWindowInsets } returns null - assertEquals(null, view.getWindowInsets()) - } - - @Test - fun `getWindowInsets returns the compat insets when the system insets exist`() { - val rootInsets: WindowInsets = mockk(relaxed = true) - every { view.rootWindowInsets } returns rootInsets - - // Construct the expected object directly instead of mocking the static method - val expectedInsets = WindowInsetsCompat.toWindowInsetsCompat(rootInsets) - assertEquals(expectedInsets, view.getWindowInsets()) - } - - @Test - fun `getKeyboardHeight accounts for status bar and navigation bar`() { - val result = getKeyboardHeight( - rootViewHeight = 1000, - windowVisibleDisplayFrame = Rect(0, 50, 1000, 500), - bottomInset = 50, - ) - - assertEquals(450, result) - } - - @Test - fun `isKeyboardVisible returns false when the keyboard height is 0`() { - assertFalse(isKeyboardVisible(keyboardHeight = 0)) - } - - @Test - fun `isKeyboardVisible returns true when the keyboard height is greater than 0`() { - // Test the pure logic directly - assertTrue(isKeyboardVisible(keyboardHeight = 100)) - } - - @Test fun `getRectWithScreenLocation should transform getLocationInScreen method values`() { val locationOnScreen = slot<IntArray>() every { view.getLocationOnScreen(capture(locationOnScreen)) } answers { @@ -129,26 +86,4 @@ class ViewTest { assertEquals(250, outRect.right) assertEquals(450, outRect.bottom) } - - @Test - fun `getKeyboardHeight returns the keyboard height when keyboard is considered open`() { - // Test the pure calculation logic directly - val result = getKeyboardHeight( - rootViewHeight = 1500, - windowVisibleDisplayFrame = Rect(0, 0, 500, 1000), - bottomInset = 0, - ) - assertEquals(500, result) - } - - @Test - fun `getKeyboardHeight returns zero when keyboard is considered closed`() { - // Test the pure calculation logic directly - val result = getKeyboardHeight( - rootViewHeight = 1000, - windowVisibleDisplayFrame = Rect(0, 0, 500, 1000), - bottomInset = 0, - ) - assertEquals(0, result) - } }