Repository: woocommerce/woocommerce-android
Pull request #13953: Fix issues related to edge-to-edge and IME handling
Author: login: hichamboushaba
Labels: name: type: task
description: An internally driven task.
Created: 2025-04-19T23:19:02Z | Merged: 2025-04-23T18:54:38Z

<!-- Remember about a good descriptive title. -->

### Description
This PR fixes some issues related to the IME insets handling in the app:
- In `MainActivity`, we were duplicating the handling of the IME insets in the app, we apply the padding on the root then on some internal fragments, which created some issues like in the Jetpack connection email screen. Normally this shouldn't cause issues as we consume the insets at the root level, but this seems like a Compose bug where the IME animation causes the insets to be re-applied, I created a bug report about this [here](https://issuetracker.google.com/issues/411868840), and I removed the duplicated padding to fix our issues.
- In some DialogFragments, we aren't handling the IME insets, I fixed this (check the Product Selector screenshot for an example).
- `AppSettingsActivity` wasn't handling the IME inset, I added this. 

### Steps to reproduce
I think the flows are clear, if you need exact steps for any test, please ping me.

### Testing information
- Confirm the insets are applied correctly.

### The tests that have been performed
<!-- To give the reviewer an idea of what could be missed in terms of testing -->

### Images/gif
##### Jetpack and Coupons

##### Other fixes
| Before | After |
| --- | --- |
| ![Screenshot_20250421_225746](https://github.com/user-attachments/assets/1c22326e-cf8f-430d-9db6-015278052b9e) | <img src=https://github.com/user-attachments/assets/ca28ad83-3b15-44b0-a1eb-87b8145b7004 /> |
| ![Screenshot_20250421_230017](https://github.com/user-attachments/assets/02ca0f9a-f2bf-4f47-87c2-9bafc1a20306) | <img src=https://github.com/user-attachments/assets/f8aa7e22-3546-4073-84ea-74f54c7c3c9d />
| ![Screenshot_20250420_000226](https://github.com/user-attachments/assets/5f596191-798a-4eb7-ab1a-aed4a7e8b8d9) | ![Screenshot_20250421_102043](https://github.com/user-attachments/assets/d0acd032-a18f-4482-8341-a0ab90eec5cd) |
| ![Screenshot_20250421_230145](https://github.com/user-attachments/assets/878263c3-354e-40ca-8ef5-aab39ea8e579) | ![Screenshot_20250421_230238](https://github.com/user-attachments/assets/02d1b85c-6ffb-4de0-8114-f1a415c9b4fe) |


- [x] I have considered if this change warrants release notes and have added them to `RELEASE-NOTES.txt` if necessary. Use the "[Internal]" label for non-user-facing changes.

## Reviewer (or Author, in the case of optional code reviews):

Please make sure these conditions are met before approving the PR, or request changes if the PR needs improvement:

- [x] The PR is small and has a clear, single focus, or a valid explanation is provided in the description. If needed, please request to split it into smaller PRs.
- [x] Ensure Adequate Unit Test Coverage: The changes are reasonably covered by unit tests or an explanation is provided in the PR description.
- [x] Manual Testing: The author listed all the tests they ran, including smoke tests when needed (e.g., for refactorings). The reviewer confirmed that the PR works as expected on big (tablet) and small (phone) in case of UI changes, and no regressions are added.

<!-- Pull request guidelines: https://github.com/woocommerce/woocommerce-android/blob/develop/docs/pull-request-guidelines.md -->


Diff:
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/compose/component/Toolbar.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/compose/component/Toolbar.kt
index d41e02526fe3..7d4827749087 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/compose/component/Toolbar.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/compose/component/Toolbar.kt
@@ -1,6 +1,7 @@
 package com.woocommerce.android.ui.compose.component
 
 import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.WindowInsets
 import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.Text
@@ -27,6 +28,7 @@ fun ToolbarWithHelpButton(
     onNavigationButtonClick: (() -> Unit)? = null,
     navigationIcon: ImageVector? = Icons.AutoMirrored.Filled.ArrowBack,
     navigationIconContentDescription: String = stringResource(id = string.back),
+    windowInsets: WindowInsets = WindowInsets(0),
     onHelpButtonClick: (() -> Unit)
 ) {
     Toolbar(
@@ -37,7 +39,8 @@ fun ToolbarWithHelpButton(
         navigationIconContentDescription = navigationIconContentDescription,
         actionButtonIcon = ImageVector.vectorResource(id = drawable.ic_help_24dp),
         onActionButtonClick = onHelpButtonClick,
-        actionIconContentDescription = stringResource(id = string.help)
+        actionIconContentDescription = stringResource(id = string.help),
+        windowInsets = windowInsets
     )
 }
 
@@ -47,7 +50,8 @@ fun Toolbar(
     title: String = "",
     onNavigationButtonClick: (() -> Unit),
     navigationIcon: ImageVector = Icons.AutoMirrored.Filled.ArrowBack,
-    navigationIconContentDescription: String = stringResource(id = string.back)
+    navigationIconContentDescription: String = stringResource(id = string.back),
+    windowInsets: WindowInsets = WindowInsets(0),
 ) {
     Toolbar(
         modifier = modifier,
@@ -55,6 +59,7 @@ fun Toolbar(
         onNavigationButtonClick = onNavigationButtonClick,
         navigationIcon = navigationIcon,
         navigationIconContentDescription = navigationIconContentDescription,
+        windowInsets = windowInsets,
     )
 }
 
@@ -65,6 +70,7 @@ fun Toolbar(
     onNavigationButtonClick: (() -> Unit)? = null,
     navigationIcon: ImageVector? = Icons.AutoMirrored.Filled.ArrowBack,
     navigationIconContentDescription: String = stringResource(id = string.back),
+    windowInsets: WindowInsets = WindowInsets(0),
     actionButtonIcon: ImageVector,
     onActionButtonClick: (() -> Unit),
     actionIconContentDescription: String
@@ -75,6 +81,7 @@ fun Toolbar(
         onNavigationButtonClick = onNavigationButtonClick,
         navigationIcon = navigationIcon,
         navigationIconContentDescription = navigationIconContentDescription,
+        windowInsets = windowInsets,
         actions = {
             IconButton(onClick = onActionButtonClick) {
                 Icon(
@@ -93,6 +100,7 @@ fun Toolbar(
     onNavigationButtonClick: (() -> Unit)? = null,
     navigationIcon: ImageVector? = Icons.AutoMirrored.Filled.ArrowBack,
     navigationIconContentDescription: String = stringResource(id = string.back),
+    windowInsets: WindowInsets = WindowInsets(0),
     actions: @Composable RowScope.() -> Unit = {}
 ) {
     Toolbar(
@@ -101,6 +109,7 @@ fun Toolbar(
         onNavigationButtonClick = onNavigationButtonClick,
         navigationIcon = navigationIcon,
         navigationIconContentDescription = navigationIconContentDescription,
+        windowInsets = windowInsets,
         actions = actions
     )
 }
@@ -112,6 +121,7 @@ fun Toolbar(
     onNavigationButtonClick: (() -> Unit)? = null,
     navigationIcon: ImageVector? = Icons.AutoMirrored.Filled.ArrowBack,
     navigationIconContentDescription: String = stringResource(id = string.back),
+    windowInsets: WindowInsets = WindowInsets(0),
     onActionButtonClick: (() -> Unit),
     actionButtonText: String
 ) {
@@ -121,6 +131,7 @@ fun Toolbar(
         onNavigationButtonClick = onNavigationButtonClick,
         navigationIcon = navigationIcon,
         navigationIconContentDescription = navigationIconContentDescription,
+        windowInsets = windowInsets,
         actions = {
             TextButton(onClick = onActionButtonClick) {
                 Text(text = actionButtonText)
@@ -136,9 +147,11 @@ fun Toolbar(
     onNavigationButtonClick: (() -> Unit)? = null,
     navigationIcon: ImageVector? = null,
     navigationIconContentDescription: String = stringResource(id = string.back),
+    windowInsets: WindowInsets = WindowInsets(0),
     actions: @Composable RowScope.() -> Unit = {}
 ) {
     TopAppBar(
+        windowInsets = windowInsets,
         backgroundColor = colorResource(id = R.color.color_toolbar),
         title = title,
         navigationIcon = {
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/coupons/edit/EditCouponScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/coupons/edit/EditCouponScreen.kt
index 3ecac927f82f..6d7f6a786107 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/coupons/edit/EditCouponScreen.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/coupons/edit/EditCouponScreen.kt
@@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.imePadding
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.rememberScrollState
@@ -102,7 +101,6 @@ fun EditCouponScreen(
             .verticalScroll(scrollState)
             .padding(vertical = dimensionResource(id = R.dimen.major_100))
             .fillMaxSize()
-            .imePadding()
     ) {
         DetailsSection(
             viewState = viewState,
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/login/jetpack/wpcom/JetpackActivationWPComEmailScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/login/jetpack/wpcom/JetpackActivationWPComEmailScreen.kt
index aab668b1d86d..ee6ebfb75f5f 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/login/jetpack/wpcom/JetpackActivationWPComEmailScreen.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/login/jetpack/wpcom/JetpackActivationWPComEmailScreen.kt
@@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.imePadding
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.text.KeyboardActions
@@ -68,7 +67,6 @@ fun JetpackActivationWPComEmailScreen(
             modifier = Modifier
                 .background(MaterialTheme.colors.surface)
                 .padding(paddingValues)
-                .imePadding()
                 .fillMaxSize()
                 .verticalScroll(rememberScrollState())
         ) {
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/creation/customerlist/CustomerListDialogFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/creation/customerlist/CustomerListDialogFragment.kt
index 2da19ff6d20d..eb8e219e08a3 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/creation/customerlist/CustomerListDialogFragment.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/creation/customerlist/CustomerListDialogFragment.kt
@@ -6,6 +6,7 @@ import android.view.View
 import android.view.ViewGroup
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.platform.ViewCompositionStrategy
+import androidx.core.view.WindowCompat
 import androidx.fragment.app.DialogFragment
 import androidx.fragment.app.viewModels
 import androidx.navigation.fragment.findNavController
@@ -50,7 +51,7 @@ class CustomerListDialogFragment : DialogFragment() {
         setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
         setContent {
             WooThemeWithBackground {
-                OrderCustomerListScreen(viewModel)
+                OrderCustomerListScreen(viewModel = viewModel, handleInsets = true)
             }
         }
     }
@@ -94,6 +95,9 @@ class CustomerListDialogFragment : DialogFragment() {
                 (DisplayUtils.getWindowPixelHeight(requireContext()) * TABLET_LANDSCAPE_HEIGHT_RATIO).toInt()
             )
         }
+        dialog?.window?.let {
+            WindowCompat.setDecorFitsSystemWindows(it, false)
+        }
     }
 
     override fun onResume() {
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/creation/customerlist/OrderCustomerListFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/creation/customerlist/OrderCustomerListFragment.kt
index fcf07bba6e7e..55dbbf4a42c3 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/creation/customerlist/OrderCustomerListFragment.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/creation/customerlist/OrderCustomerListFragment.kt
@@ -35,7 +35,7 @@ class OrderCustomerListFragment : BaseFragment() {
         setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
         setContent {
             WooThemeWithBackground {
-                OrderCustomerListScreen(viewModel)
+                OrderCustomerListScreen(viewModel = viewModel, handleInsets = false)
             }
         }
     }
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/creation/customerlist/OrderCustomerListScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/creation/customerlist/OrderCustomerListScreen.kt
index 4ca5c9094fd4..130926d75e79 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/creation/customerlist/OrderCustomerListScreen.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/creation/customerlist/OrderCustomerListScreen.kt
@@ -2,12 +2,14 @@
 
 package com.woocommerce.android.ui.orders.creation.customerlist
 
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.imePadding
+import androidx.compose.foundation.layout.navigationBarsPadding
 import androidx.compose.foundation.layout.padding
+import androidx.compose.material.AppBarDefaults
 import androidx.compose.material.FloatingActionButton
 import androidx.compose.material.Icon
-import androidx.compose.material.IconButton
 import androidx.compose.material.Scaffold
-import androidx.compose.material.Text
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.automirrored.filled.ArrowBack
 import androidx.compose.material.icons.filled.Add
@@ -18,19 +20,30 @@ import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.colorResource
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
 import com.woocommerce.android.R
-import com.woocommerce.android.ui.compose.component.TopAppBarEdgeToEdge
+import com.woocommerce.android.ui.compose.component.Toolbar
 import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground
 import com.woocommerce.android.ui.customer.CustomerListScreen
 import org.wordpress.android.fluxc.model.customer.WCCustomerModel
 
+/**
+ * @param handleInsets true if the screen should handle insets manually.
+ *  This is needed when the screen is used in a DialogFragment, otherwise the handling of insets at the root
+ *  layout will be enough.
+ *
+ *  Note: normally this shouldn't be needed, as we consume the insets in the root layout, but due to this
+ *  bug https://issuetracker.google.com/issues/411868840 the insets are re-applied when the keyboard is shown.
+ */
 @Composable
-fun OrderCustomerListScreen(viewModel: CustomerListSelectionViewModel) {
+fun OrderCustomerListScreen(
+    viewModel: CustomerListSelectionViewModel,
+    handleInsets: Boolean
+) {
     val state by viewModel.viewState.observeAsState()
     state?.let {
         OrderCustomerListScreen(
             state = it,
+            handleInsets = handleInsets,
             onNavigateBack = viewModel::onNavigateBack,
             onAddCustomerClicked = viewModel::onAddCustomerClicked,
             onCustomerSelected = viewModel::onCustomerSelected,
@@ -44,6 +57,7 @@ fun OrderCustomerListScreen(viewModel: CustomerListSelectionViewModel) {
 @Composable
 fun OrderCustomerListScreen(
     state: CustomerListViewState,
+    handleInsets: Boolean,
     onNavigateBack: () -> Unit,
     onAddCustomerClicked: () -> Unit,
     onCustomerSelected: (WCCustomerModel) -> Unit,
@@ -54,18 +68,11 @@ fun OrderCustomerListScreen(
 ) {
     Scaffold(
         topBar = {
-            TopAppBarEdgeToEdge(
-                title = { Text(stringResource(id = R.string.order_creation_add_customer)) },
-                navigationIcon = {
-                    IconButton(onNavigateBack) {
-                        Icon(
-                            imageVector = Icons.AutoMirrored.Filled.ArrowBack,
-                            contentDescription = stringResource(id = R.string.back)
-                        )
-                    }
-                },
-                backgroundColor = colorResource(id = R.color.color_toolbar),
-                elevation = 0.dp,
+            Toolbar(
+                title = stringResource(id = R.string.order_creation_add_customer),
+                navigationIcon = Icons.AutoMirrored.Filled.ArrowBack,
+                onNavigationButtonClick = onNavigateBack,
+                windowInsets = if (handleInsets) AppBarDefaults.topAppBarWindowInsets else WindowInsets(0),
             )
         },
         floatingActionButton = {
@@ -74,7 +81,9 @@ fun OrderCustomerListScreen(
         modifier = modifier
     ) { padding ->
         CustomerListScreen(
-            modifier = Modifier.padding(padding),
+            modifier = Modifier
+                .padding(padding)
+                .then(if (handleInsets) Modifier.navigationBarsPadding().imePadding() else Modifier),
             state = state,
             onCustomerSelected = onCustomerSelected,
             onSearchQueryChanged = onSearchQueryChanged,
@@ -161,6 +170,7 @@ fun OrderCustomerListScreenPreview() {
                     shouldResetScrollPosition = true
                 ),
             ),
+            handleInsets = false,
             onNavigateBack = {},
             onAddCustomerClicked = {},
             onCustomerSelected = {},
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/AppSettingsActivity.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/AppSettingsActivity.kt
index 2e00457f7a69..08b013fb5067 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/AppSettingsActivity.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/prefs/AppSettingsActivity.kt
@@ -5,6 +5,7 @@ import android.os.Bundle
 import android.view.View
 import androidx.appcompat.app.AppCompatActivity
 import androidx.appcompat.widget.Toolbar
+import androidx.core.view.WindowInsetsCompat
 import androidx.core.view.isVisible
 import androidx.core.view.updatePadding
 import androidx.fragment.app.DialogFragment
@@ -70,7 +71,12 @@ class AppSettingsActivity :
         binding = ActivityAppSettingsBinding.inflate(layoutInflater)
         setContentView(binding.root)
 
-        binding.root.doOnApplyWindowInsets(consumeInsets = true) {
+        binding.root.doOnApplyWindowInsets(
+            insetsMask = WindowInsetsCompat.Type.systemBars() or
+                WindowInsetsCompat.Type.displayCutout() or
+                WindowInsetsCompat.Type.ime(),
+            consumeInsets = true
+        ) {
             binding.root.updatePadding(
                 left = it.left,
                 right = it.right,
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ai/productinfo/AiProductPromptScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ai/productinfo/AiProductPromptScreen.kt
index 2551a7b968df..1ee568b81837 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ai/productinfo/AiProductPromptScreen.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/ai/productinfo/AiProductPromptScreen.kt
@@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.defaultMinSize
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.imePadding
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.wrapContentWidth
@@ -148,7 +147,6 @@ fun AiProductPromptScreen(
                 .background(MaterialTheme.colors.surface)
                 .padding(padding)
                 .fillMaxSize()
-                .imePadding()
         ) {
             Column(
                 modifier = Modifier
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/selector/ProductSelectorDialogFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/selector/ProductSelectorDialogFragment.kt
index dbaf72fcac40..a25368a35d80 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/selector/ProductSelectorDialogFragment.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/selector/ProductSelectorDialogFragment.kt
@@ -33,7 +33,8 @@ class ProductSelectorDialogFragment : DialogFragment() {
         private const val TABLET_LANDSCAPE_HEIGHT_RATIO = 0.6f
     }
 
-    @Inject lateinit var navigator: ProductNavigator
+    @Inject
+    lateinit var navigator: ProductNavigator
 
     private val viewModel: ProductSelectorViewModel by viewModels()
 
@@ -64,7 +65,7 @@ class ProductSelectorDialogFragment : DialogFragment() {
 
             setContent {
                 WooThemeWithBackground {
-                    ProductSelectorScreen(viewModel)
+                    ProductSelectorScreen(viewModel = viewModel, handleInsets = true)
                 }
             }
         }
@@ -99,6 +100,7 @@ class ProductSelectorDialogFragment : DialogFragment() {
                         event.data as Collection<SelectedItem>
                     )
                 }
+
                 is ProductNavigationTarget -> navigator.navigate(this, event)
                 is Exit -> findNavController().navigateUp()
             }
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/selector/ProductSelectorFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/selector/ProductSelectorFragment.kt
index fc6bc8968863..c36169539cf3 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/selector/ProductSelectorFragment.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/selector/ProductSelectorFragment.kt
@@ -54,7 +54,7 @@ class ProductSelectorFragment : BaseFragment() {
             setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
             setContent {
                 WooThemeWithBackground {
-                    ProductSelectorScreen(viewModel)
+                    ProductSelectorScreen(viewModel = viewModel, handleInsets = false)
                 }
             }
         }
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/selector/ProductSelectorScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/selector/ProductSelectorScreen.kt
index 3b83807ea6ea..85918662b048 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/selector/ProductSelectorScreen.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/selector/ProductSelectorScreen.kt
@@ -9,9 +9,11 @@ import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.WindowInsets
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.imePadding
 import androidx.compose.foundation.layout.navigationBarsPadding
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.padding
@@ -23,10 +25,9 @@ import androidx.compose.foundation.lazy.itemsIndexed
 import androidx.compose.foundation.lazy.rememberLazyListState
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.AppBarDefaults
 import androidx.compose.material.CircularProgressIndicator
 import androidx.compose.material.Divider
-import androidx.compose.material.Icon
-import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.Scaffold
 import androidx.compose.material.Text
@@ -44,7 +45,6 @@ import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
 import com.woocommerce.android.R
 import com.woocommerce.android.R.color
 import com.woocommerce.android.R.dimen
@@ -53,7 +53,7 @@ import com.woocommerce.android.ui.compose.animations.SkeletonView
 import com.woocommerce.android.ui.compose.component.InfiniteListHandler
 import com.woocommerce.android.ui.compose.component.SearchLayoutWithParams
 import com.woocommerce.android.ui.compose.component.SearchLayoutWithParamsState
-import com.woocommerce.android.ui.compose.component.TopAppBarEdgeToEdge
+import com.woocommerce.android.ui.compose.component.Toolbar
 import com.woocommerce.android.ui.compose.component.WCColoredButton
 import com.woocommerce.android.ui.compose.component.WCTextButton
 import com.woocommerce.android.ui.products.ProductType.BUNDLE
@@ -74,45 +74,50 @@ import com.woocommerce.android.ui.products.selector.SelectionState.SELECTED
 import com.woocommerce.android.ui.products.selector.components.SelectorListItem
 import com.woocommerce.android.util.StringUtils
 
+/**
+ * @param handleInsets true if the screen should handle insets manually.
+ *  This is needed when the screen is used in a DialogFragment, otherwise the handling of insets at the root
+ *  layout will be enough.
+ *
+ *  Note: normally this shouldn't be needed, as we consume the insets in the root layout, but due to this
+ *  bug https://issuetracker.google.com/issues/411868840 the insets are re-applied when the keyboard is shown.
+ */
 @Composable
-fun ProductSelectorScreen(viewModel: ProductSelectorViewModel) {
+fun ProductSelectorScreen(
+    viewModel: ProductSelectorViewModel,
+    handleInsets: Boolean,
+) {
     val viewState by viewModel.viewState.observeAsState()
     BackHandler(onBack = viewModel::onNavigateBack)
     viewState?.let { state ->
         val showToolbar = state.selectionMode != SelectionMode.LIVE
         Scaffold(topBar = {
             if (showToolbar) {
-                TopAppBarEdgeToEdge(
-                    title = {
-                        Text(
-                            text = state.screenTitleOverride
-                                ?: stringResource(id = string.coupon_conditions_products_select_products_title)
-                        )
+                Toolbar(
+                    title = state.screenTitleOverride
+                        ?: stringResource(id = string.coupon_conditions_products_select_products_title),
+                    navigationIcon = if (state.searchState.isActive) {
+                        Icons.AutoMirrored.Filled.ArrowBack
+                    } else {
+                        Icons.Filled.Close
                     },
-                    navigationIcon = {
-                        IconButton(viewModel::onNavigateBack) {
-                            Icon(
-                                imageVector = if (state.searchState.isActive) {
-                                    Icons.AutoMirrored.Filled.ArrowBack
-                                } else {
-                                    Icons.Filled.Close
-                                },
-                                contentDescription = stringResource(id = string.back)
-                            )
-                        }
-                    },
-                    backgroundColor = colorResource(id = R.color.color_toolbar),
-                    elevation = 0.dp,
+                    onNavigationButtonClick = viewModel::onNavigateBack,
+                    windowInsets = if (handleInsets) AppBarDefaults.topAppBarWindowInsets else WindowInsets(0),
                 )
             }
         }) { padding ->
-            val modifier = if (showToolbar) {
-                Modifier.padding(padding)
-            } else {
-                Modifier
-                    .padding(padding)
-                    .statusBarsPadding()
-            }.navigationBarsPadding()
+            val modifier = Modifier
+                .padding(padding)
+                .then(
+                    if (handleInsets) {
+                        Modifier
+                            .navigationBarsPadding()
+                            .imePadding()
+                            .then(if (!showToolbar) Modifier.statusBarsPadding() else Modifier)
+                    } else {
+                        Modifier
+                    }
+                )
             ProductSelectorScreen(
                 modifier = modifier,
                 state = state,


Reviews:
user: login: hichamboushaba
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-21T22:13:29Z
user: login: kidinov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T13:24:16Z
user: login: kidinov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T14:30:22Z
user: login: kidinov
body: Thanks @hichamboushaba! I tested all the changes - all look good

Please take a look into the comments I left
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T14:53:38Z
user: login: hichamboushaba
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T14:07:30Z
user: login: hichamboushaba
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T14:10:04Z
user: login: kidinov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T14:23:05Z
user: login: hichamboushaba
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T14:23:33Z
user: login: hichamboushaba
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T14:28:59Z
user: login: github-advanced-security[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-23T14:41:53Z
user: login: kidinov
body: Thanks @hichamboushaba 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T18:54:32Z

Review comments:
diff_hunk: @@ -26,11 +30,15 @@ import com.woocommerce.android.ui.customer.CustomerListScreen
 import org.wordpress.android.fluxc.model.customer.WCCustomerModel
 
 @Composable
-fun OrderCustomerListScreen(viewModel: CustomerListSelectionViewModel) {
+fun OrderCustomerListScreen(
+    viewModel: CustomerListSelectionViewModel,
+    handleImeInsets: Boolean = true,
path: WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/creation/customerlist/OrderCustomerListScreen.kt
user: login: hichamboushaba
body: This `Composable` is used both in a `Fragment` and in `DialogFragment`, this parameter is to allow not applying the IME inset when it's not needed.

Even with this, there is still an issue, the bug I mentioned in the PR description still causes issues when the keyboard is shown, `Compose` re-applies the consumed insets, which adds additional padding on top and below, like the video below:

https://github.com/user-attachments/assets/75e69e3d-8475-4cc7-81af-d94422ece42c

###### (You can reproduce this in the Edit coupon screen, by tapping on "All Products" button to select some products)

One way to fix this is to apply insets at the View level (to the `ComposeView` itself), but this would mean the `TopBar` won't expand to the top edge. Maybe that's an acceptable solution until the bug is fixed 🤔, WDYT? If you have any other ideas, please share them.
created_at: 2025-04-21T22:13:29Z
author_association: MEMBER
diff_hunk: @@ -26,11 +28,15 @@ import com.woocommerce.android.ui.customer.CustomerListScreen
 import org.wordpress.android.fluxc.model.customer.WCCustomerModel
 
 @Composable
-fun OrderCustomerListScreen(viewModel: CustomerListSelectionViewModel) {
+fun OrderCustomerListScreen(
+    viewModel: CustomerListSelectionViewModel,
+    handleImeInsets: Boolean = true,
path: WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/creation/customerlist/OrderCustomerListScreen.kt
user: login: kidinov
body: It's usually not the best approach to pass a boolean to a method, and having a default value for it can lead to confusion and bugs. And It seems like in this case, you ended up with "true" for both scenarios, right?

<img width="606" alt="image" src="https://github.com/user-attachments/assets/c96b55e5-add2-4734-9a17-be59a686e320" />

Maybe to have 2 functions `OrderCustomerListScreenWithImePadding` and simply `OrderCustomerListScreen`?
created_at: 2025-04-22T13:24:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -26,11 +30,15 @@ import com.woocommerce.android.ui.customer.CustomerListScreen
 import org.wordpress.android.fluxc.model.customer.WCCustomerModel
 
 @Composable
-fun OrderCustomerListScreen(viewModel: CustomerListSelectionViewModel) {
+fun OrderCustomerListScreen(
+    viewModel: CustomerListSelectionViewModel,
+    handleImeInsets: Boolean = true,
path: WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/creation/customerlist/OrderCustomerListScreen.kt
user: login: kidinov
body: I don't understand where the first inset comes from and why it applied only when the keyboard is shown, but having two Functions here, one with an inset and another without all of them, fixes the issue for the case when it's used from the coupons

<img width="391" alt="image" src="https://github.com/user-attachments/assets/0c069f44-c682-4336-863e-46632bd5c998" />

(example of what I changed to to achieve that)
[Showcase_for_the_removed_insets1.patch](https://github.com/user-attachments/files/19851582/Showcase_for_the_removed_insets1.patch)

created_at: 2025-04-22T14:30:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -26,11 +30,15 @@ import com.woocommerce.android.ui.customer.CustomerListScreen
 import org.wordpress.android.fluxc.model.customer.WCCustomerModel
 
 @Composable
-fun OrderCustomerListScreen(viewModel: CustomerListSelectionViewModel) {
+fun OrderCustomerListScreen(
+    viewModel: CustomerListSelectionViewModel,
+    handleImeInsets: Boolean = true,
path: WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/creation/customerlist/OrderCustomerListScreen.kt
user: login: hichamboushaba
body: > I don't understand where the first inset comes from and why it applied only when the keyboard is shown

This is caused by the [bug](https://issuetracker.google.com/issues/411868840) I mentioned in the description, when the keyboard is shown, the insets that were marked as "consumed" gets re-applied, leading to the additional padding.

> but having two Functions here, one with an inset and another without all of them, fixes the issue for the case when it's used from the coupons

Yes, this is a little bit similar to what I suggested as well above by applying the insets at the view level (meaning only in the DialogFragment).
I applied what you suggested in the last comments, but I kept the boolean instead of using two functions 😅, more on this below in the other thread.
created_at: 2025-04-23T14:07:30Z
author_association: MEMBER
diff_hunk: @@ -26,11 +28,15 @@ import com.woocommerce.android.ui.customer.CustomerListScreen
 import org.wordpress.android.fluxc.model.customer.WCCustomerModel
 
 @Composable
-fun OrderCustomerListScreen(viewModel: CustomerListSelectionViewModel) {
+fun OrderCustomerListScreen(
+    viewModel: CustomerListSelectionViewModel,
+    handleImeInsets: Boolean = true,
path: WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/creation/customerlist/OrderCustomerListScreen.kt
user: login: hichamboushaba
body: > It's usually not the best approach to pass a boolean to a method

Can I learn more about this 🙂? This is a very common pattern in `Compose`, so I'm not sure I agree with this as a rule. For the bug that happened here, it's an issue of relying on AI's autocompletion 🤦.

I removed the default value here, but I kept the Boolean instead of using two functions, using two functions won't be straightforward as we need to handle the insets at different levels (the toolbar and also to the content).
created_at: 2025-04-23T14:10:04Z
author_association: MEMBER
diff_hunk: @@ -26,11 +28,15 @@ import com.woocommerce.android.ui.customer.CustomerListScreen
 import org.wordpress.android.fluxc.model.customer.WCCustomerModel
 
 @Composable
-fun OrderCustomerListScreen(viewModel: CustomerListSelectionViewModel) {
+fun OrderCustomerListScreen(
+    viewModel: CustomerListSelectionViewModel,
+    handleImeInsets: Boolean = true,
path: WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/creation/customerlist/OrderCustomerListScreen.kt
user: login: kidinov
body: > Can I learn more about this 🙂?

Absolutely, it's a widely known idea. Just Google "Why passing a boolean to a method is a code smell". There are thousands of articles on that.

Copying the AI-generated summary from Google to save your time:


_It makes your code harder to read. It is also a sign that your function has multiple responsibilities, violating the Single Responsibility Principle. On top of that, a boolean has only two options, so it hurts extensibility. It is better to split the logic, and provide two distinct behaviors for the caller._
created_at: 2025-04-23T14:23:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -27,6 +28,7 @@ fun ToolbarWithHelpButton(
     onNavigationButtonClick: (() -> Unit)? = null,
     navigationIcon: ImageVector? = Icons.AutoMirrored.Filled.ArrowBack,
     navigationIconContentDescription: String = stringResource(id = string.back),
+    windowInsets: WindowInsets = WindowInsets(0),
path: WooCommerce/src/main/kotlin/com/woocommerce/android/ui/compose/component/Toolbar.kt
user: login: hichamboushaba
body: This `argument` windowInsets is related to what we discussed in a previous PR when I suggested that this would be more flexible than an additional `Composable` Toolbar implementation 🙂. This argument makes the logic easier, and I believe that's why the Compose team used the same approach:
```kotlin
     windowInsets = if (condition) something else somethingElse
```

Instead of:

```kotlin
   if (condition) {
       TopAppBar1(
        ....
        )
    } else {
        TopAppBar2(
         .... // Repeats the same logic as TopAppBar1
    }
```
created_at: 2025-04-23T14:23:33Z
author_association: MEMBER
diff_hunk: @@ -26,11 +28,15 @@ import com.woocommerce.android.ui.customer.CustomerListScreen
 import org.wordpress.android.fluxc.model.customer.WCCustomerModel
 
 @Composable
-fun OrderCustomerListScreen(viewModel: CustomerListSelectionViewModel) {
+fun OrderCustomerListScreen(
+    viewModel: CustomerListSelectionViewModel,
+    handleImeInsets: Boolean = true,
path: WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/creation/customerlist/OrderCustomerListScreen.kt
user: login: hichamboushaba
body: Thanks @kidinov, but TBH I don't think this is a rule that's valid everywhere 🙂. Most of the time, the simpler solution is the best, and in this case the Boolean is the simpler solution. Also, in Compose, if you check the API, you'll see that it's a very common pattern (`enabled` argument of most composables, `checked` argument...).
created_at: 2025-04-23T14:28:59Z
author_association: MEMBER
diff_hunk: @@ -2,12 +2,14 @@
 
 package com.woocommerce.android.ui.orders.creation.customerlist
 
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.imePadding
+import androidx.compose.foundation.layout.navigationBarsPadding
 import androidx.compose.foundation.layout.padding
+import androidx.compose.material.AppBarDefaults
path: WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/creation/customerlist/OrderCustomerListScreen.kt
user: login: github-advanced-security[bot]
body: ## material and material3 are separate, incompatible design system libraries

Using a material import while also using the material3 library

[Show more details](https://github.com/woocommerce/woocommerce-android/security/code-scanning/76896)
created_at: 2025-04-23T14:41:53Z
author_association: NONE
diff_hunk: @@ -23,10 +25,9 @@
 import androidx.compose.foundation.lazy.rememberLazyListState
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.AppBarDefaults
path: WooCommerce/src/main/kotlin/com/woocommerce/android/ui/products/selector/ProductSelectorScreen.kt
user: login: github-advanced-security[bot]
body: ## material and material3 are separate, incompatible design system libraries

Using a material import while also using the material3 library

[Show more details](https://github.com/woocommerce/woocommerce-android/security/code-scanning/76897)
created_at: 2025-04-23T14:41:53Z
author_association: NONE

PR comments:
user: login: wpmobilebot
created_at: 2025-04-19T23:24:46Z
body: <!-- REUSE_ID: WooCommerce-Wear-prototype-build-link -->

<details><summary>📲 You can test the changes from this Pull Request in <b>WooCommerce-Wear Android</b> by scanning the QR code below to install the corresponding build.</summary>
<table>
<tr>
  <td rowspan='6' width='260px'><img src='https://api.qrserver.com/v1/create-qr-code/?size=500x500&qzone=4&data=https%3A%2F%2Fcdn.a8c-ci.services%2F70ad533e752cbd4dcd515ed6cfb9f823d6f9f819%2Fwoocommerce-wear-prototype-build-pr13953-4227178.apk' width='250' height='250' /></td>
  <td><b>App Name</b></td><td> WooCommerce-Wear Android</td>
</tr>
<tr><td><b>Platform</b></td><td>⌚️ Wear OS</td></tr>
<tr><td><b>Flavor</b></td><td>Jalapeno</td></tr>
<tr><td><b>Build Type</b></td><td>Debug</td></tr>
<tr><td><b>Commit</b></td><td>4227178fd8dc43f5ac38d0831eb434ba82543645</td></tr>
<tr><td><b>Direct Download</b></td><td><a href='https://cdn.a8c-ci.services/70ad533e752cbd4dcd515ed6cfb9f823d6f9f819/woocommerce-wear-prototype-build-pr13953-4227178.apk'><code>woocommerce-wear-prototype-build-pr13953-4227178.apk</code></a></td></tr>
</table>

</details>

author_association: COLLABORATOR
user: login: wpmobilebot
created_at: 2025-04-19T23:37:22Z
body: <!-- REUSE_ID: WooCommerce-prototype-build-link -->

<p>📲 You can test the changes from this Pull Request in <b>WooCommerce Android</b> by scanning the QR code below to install the corresponding build.</p>
<table>
<tr>
  <td rowspan='6' width='260px'><img src='https://api.qrserver.com/v1/create-qr-code/?size=500x500&qzone=4&data=https%3A%2F%2Fcdn.a8c-ci.services%2Fa8b6e90d9d969f1b573b3c0dd090796761ee106e%2Fwoocommerce-prototype-build-pr13953-4227178.apk' width='250' height='250' /></td>
  <td><b>App Name</b></td><td> WooCommerce Android</td>
</tr>
<tr><td><b>Platform</b></td><td>📱 Mobile</td></tr>
<tr><td><b>Flavor</b></td><td>Jalapeno</td></tr>
<tr><td><b>Build Type</b></td><td>Debug</td></tr>
<tr><td><b>Commit</b></td><td>4227178fd8dc43f5ac38d0831eb434ba82543645</td></tr>
<tr><td><b>Direct Download</b></td><td><a href='https://cdn.a8c-ci.services/a8b6e90d9d969f1b573b3c0dd090796761ee106e/woocommerce-prototype-build-pr13953-4227178.apk'><code>woocommerce-prototype-build-pr13953-4227178.apk</code></a></td></tr>
</table>


author_association: COLLABORATOR
user: login: hichamboushaba
created_at: 2025-04-23T14:24:45Z
body: Thanks so much @kidinov for the review, ready for another round.
author_association: MEMBER
user: login: codecov-commenter
created_at: 2025-04-23T14:42:22Z
body: ## [Codecov](https://app.codecov.io/gh/woocommerce/woocommerce-android/pull/13953?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=woocommerce) Report
Attention: Patch coverage is `0%` with `14 lines` in your changes missing coverage. Please review.
> Project coverage is 38.24%. Comparing base [(`228f70b`)](https://app.codecov.io/gh/woocommerce/woocommerce-android/commit/228f70b9f2cd0724ebcef6b18e2fa00b226e974c?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=woocommerce) to head [(`4227178`)](https://app.codecov.io/gh/woocommerce/woocommerce-android/commit/4227178fd8dc43f5ac38d0831eb434ba82543645?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=woocommerce).
> Report is 46 commits behind head on 13270-android-sdk-update-target-sdk-to-35.

| [Files with missing lines](https://app.codecov.io/gh/woocommerce/woocommerce-android/pull/13953?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=woocommerce) | Patch % | Lines |
|---|---|---|
| [...oocommerce/android/ui/compose/component/Toolbar.kt](https://app.codecov.io/gh/woocommerce/woocommerce-android/pull/13953?src=pr&el=tree&filepath=WooCommerce%2Fsrc%2Fmain%2Fkotlin%2Fcom%2Fwoocommerce%2Fandroid%2Fui%2Fcompose%2Fcomponent%2FToolbar.kt&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=woocommerce#diff-V29vQ29tbWVyY2Uvc3JjL21haW4va290bGluL2NvbS93b29jb21tZXJjZS9hbmRyb2lkL3VpL2NvbXBvc2UvY29tcG9uZW50L1Rvb2xiYXIua3Q=) | 0.00% | [14 Missing :warning: ](https://app.codecov.io/gh/woocommerce/woocommerce-android/pull/13953?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=woocommerce) |

<details><summary>Additional details and impacted files</summary>


```diff
@@                               Coverage Diff                               @@
##             13270-android-sdk-update-target-sdk-to-35   #13953      +/-   ##
===============================================================================
- Coverage                                        38.25%   38.24%   -0.01%     
+ Complexity                                        9413     9412       -1     
===============================================================================
  Files                                             2103     2103              
  Lines                                           116294   116306      +12     
  Branches                                         14883    14889       +6     
===============================================================================
- Hits                                             44488    44485       -3     
- Misses                                           67761    67775      +14     
- Partials                                          4045     4046       +1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/woocommerce/woocommerce-android/pull/13953?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=woocommerce).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=woocommerce).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE