Add falsing for edit mode button

Add a viewmodel for the button. Also, the action to start editing is not
needed anymore as part of TileGrid.

Test: manual, enter edit mode
Test: atest EditModeButtonViewModelTest
Flag: com.android.systemui.qs_ui_refactor_compose_fragment
Fixes: 379311646
Change-Id: I6e7a1c864ed2eba1202cee0473816d11f4df96a3
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
index 26c827a..f84865f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
@@ -168,7 +168,6 @@
                 modifier =
                     Modifier.fillMaxWidth()
                         .heightIn(max = QuickSettingsShade.Dimensions.GridMaxHeight),
-                viewModel.editModeViewModel::startEditing,
             )
         }
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelTest.kt
new file mode 100644
index 0000000..f2bfd72
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelTest.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.fakeFalsingManager
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class EditModeButtonViewModelTest : SysuiTestCase() {
+    val kosmos = testKosmos()
+
+    val underTest = kosmos.editModeButtonViewModelFactory.create()
+
+    @Test
+    fun falsingFalseTap_editModeDoesntStart() =
+        kosmos.runTest {
+            val isEditing by collectLastValue(editModeViewModel.isEditing)
+
+            fakeFalsingManager.setFalseTap(true)
+
+            underTest.onButtonClick()
+            runCurrent()
+
+            assertThat(isEditing).isFalse()
+        }
+
+    @Test
+    fun falsingNotFalseTap_editModeStarted() =
+        kosmos.runTest {
+            val isEditing by collectLastValue(editModeViewModel.isEditing)
+
+            fakeFalsingManager.setFalseTap(false)
+
+            underTest.onButtonClick()
+            runCurrent()
+
+            assertThat(isEditing).isTrue()
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt b/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt
index 57e9ade..074b64e 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.classifier.FalsingCollectorActual
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.FalsingManager.Penalty
 import javax.inject.Inject
 
 /**
@@ -63,12 +64,13 @@
      * Inserts the given [result] into the falsing system, affecting future runs of the classifier
      * as if this was a result that had organically happened before.
      */
-    fun updateFalseConfidence(
-        result: FalsingClassifier.Result,
-    ) = collector.updateFalseConfidence(result)
+    fun updateFalseConfidence(result: FalsingClassifier.Result) =
+        collector.updateFalseConfidence(result)
 
     /** Returns `true` if the gesture should be rejected. */
-    fun isFalseTouch(
-        @Classifier.InteractionType interactionType: Int,
-    ): Boolean = manager.isFalseTouch(interactionType)
+    fun isFalseTouch(@Classifier.InteractionType interactionType: Int): Boolean =
+        manager.isFalseTouch(interactionType)
+
+    /** Returns `true` if the tap gesture should be rejected */
+    fun isFalseTap(@Penalty penalty: Int): Boolean = manager.isFalseTap(penalty)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index 32790be..5167d17 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -719,7 +719,6 @@
                                                     max =
                                                         QuickSettingsShade.Dimensions.GridMaxHeight
                                                 ),
-                                        containerViewModel.editModeViewModel::startEditing,
                                     )
                                 }
                             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditModeButton.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditModeButton.kt
new file mode 100644
index 0000000..c2764f9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditModeButton.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.compose
+
+import androidx.compose.foundation.shape.CornerSize
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Edit
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.EditModeButtonViewModel
+import com.android.systemui.qs.ui.compose.borderOnFocus
+import com.android.systemui.res.R
+
+@Composable
+fun EditModeButton(
+    viewModelFactory: EditModeButtonViewModel.Factory,
+    modifier: Modifier = Modifier,
+) {
+    val viewModel = rememberViewModel(traceName = "EditModeButton") { viewModelFactory.create() }
+    CompositionLocalProvider(
+        value = LocalContentColor provides MaterialTheme.colorScheme.onSurface
+    ) {
+        IconButton(
+            onClick = viewModel::onButtonClick,
+            shape = RoundedCornerShape(CornerSize(28.dp)),
+            modifier =
+                modifier.borderOnFocus(
+                    color = MaterialTheme.colorScheme.secondary,
+                    cornerSize = CornerSize(24.dp),
+                ),
+        ) {
+            Icon(
+                imageVector = Icons.Default.Edit,
+                contentDescription = stringResource(id = R.string.qs_edit),
+            )
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt
index 0d37581..a22eb3a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt
@@ -27,12 +27,7 @@
 
 /** A layout of tiles, indicating how they should be composed when showing in QS or in edit mode. */
 interface GridLayout {
-    @Composable
-    fun SceneScope.TileGrid(
-        tiles: List<TileViewModel>,
-        modifier: Modifier,
-        editModeStart: () -> Unit,
-    )
+    @Composable fun SceneScope.TileGrid(tiles: List<TileViewModel>, modifier: Modifier)
 
     @Composable
     fun EditTileGrid(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
index 32eaba2..b6dbf4d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PaginatedGridLayout.kt
@@ -30,15 +30,8 @@
 import androidx.compose.foundation.pager.PagerState
 import androidx.compose.foundation.pager.rememberPagerState
 import androidx.compose.foundation.shape.CornerSize
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Edit
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
-import androidx.compose.material3.LocalContentColor
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.remember
@@ -46,7 +39,6 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.input.pointer.pointerInteropFilter
-import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.padding
@@ -57,10 +49,10 @@
 import com.android.systemui.qs.panels.dagger.PaginatedBaseLayoutType
 import com.android.systemui.qs.panels.ui.compose.Dimensions.FooterHeight
 import com.android.systemui.qs.panels.ui.compose.Dimensions.InterPageSpacing
+import com.android.systemui.qs.panels.ui.viewmodel.EditModeButtonViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.PaginatedGridViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
 import com.android.systemui.qs.ui.compose.borderOnFocus
-import com.android.systemui.res.R
 import javax.inject.Inject
 
 class PaginatedGridLayout
@@ -70,11 +62,7 @@
     @PaginatedBaseLayoutType private val delegateGridLayout: PaginatableGridLayout,
 ) : GridLayout by delegateGridLayout {
     @Composable
-    override fun SceneScope.TileGrid(
-        tiles: List<TileViewModel>,
-        modifier: Modifier,
-        editModeStart: () -> Unit,
-    ) {
+    override fun SceneScope.TileGrid(tiles: List<TileViewModel>, modifier: Modifier) {
         val viewModel =
             rememberViewModel(traceName = "PaginatedGridLayout-TileGrid") {
                 viewModelFactory.create()
@@ -131,14 +119,12 @@
             ) {
                 val page = pages[it]
 
-                with(delegateGridLayout) {
-                    TileGrid(tiles = page, modifier = Modifier, editModeStart = {})
-                }
+                with(delegateGridLayout) { TileGrid(tiles = page, modifier = Modifier) }
             }
             FooterBar(
                 buildNumberViewModelFactory = viewModel.buildNumberViewModelFactory,
                 pagerState = pagerState,
-                editModeStart = editModeStart,
+                editButtonViewModelFactory = viewModel.editModeButtonViewModelFactory,
             )
         }
     }
@@ -153,7 +139,7 @@
 private fun FooterBar(
     buildNumberViewModelFactory: BuildNumberViewModel.Factory,
     pagerState: PagerState,
-    editModeStart: () -> Unit,
+    editButtonViewModelFactory: EditModeButtonViewModel.Factory,
 ) {
     // Use requiredHeight so it won't be squished if the view doesn't quite fit. As this is
     // expected to be inside a scrollable container, this should not be an issue.
@@ -189,24 +175,7 @@
         )
         Row(Modifier.weight(1f)) {
             Spacer(modifier = Modifier.weight(1f))
-            CompositionLocalProvider(
-                value = LocalContentColor provides MaterialTheme.colorScheme.onSurface
-            ) {
-                IconButton(
-                    onClick = editModeStart,
-                    shape = RoundedCornerShape(CornerSize(28.dp)),
-                    modifier =
-                        Modifier.borderOnFocus(
-                            color = MaterialTheme.colorScheme.secondary,
-                            cornerSize = CornerSize(FooterHeight / 2),
-                        ),
-                ) {
-                    Icon(
-                        imageVector = Icons.Default.Edit,
-                        contentDescription = stringResource(id = R.string.qs_edit),
-                    )
-                }
-            }
+            EditModeButton(viewModelFactory = editButtonViewModelFactory)
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt
index 1a5297b..6c1906b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt
@@ -24,13 +24,9 @@
 import com.android.systemui.qs.panels.ui.viewmodel.TileGridViewModel
 
 @Composable
-fun SceneScope.TileGrid(
-    viewModel: TileGridViewModel,
-    modifier: Modifier = Modifier,
-    editModeStart: () -> Unit,
-) {
+fun SceneScope.TileGrid(viewModel: TileGridViewModel, modifier: Modifier = Modifier) {
     val gridLayout by viewModel.gridLayout.collectAsStateWithLifecycle()
     val tiles by viewModel.tileViewModels.collectAsStateWithLifecycle(emptyList())
 
-    with(gridLayout) { TileGrid(tiles, modifier, editModeStart) }
+    with(gridLayout) { TileGrid(tiles, modifier) }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
index 29ff171..cb857ae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
@@ -55,11 +55,7 @@
 ) : PaginatableGridLayout {
 
     @Composable
-    override fun SceneScope.TileGrid(
-        tiles: List<TileViewModel>,
-        modifier: Modifier,
-        editModeStart: () -> Unit,
-    ) {
+    override fun SceneScope.TileGrid(tiles: List<TileViewModel>, modifier: Modifier) {
         DisposableEffect(tiles) {
             val token = Any()
             tiles.forEach { it.startListening(token) }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModel.kt
new file mode 100644
index 0000000..b033473
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModel.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import com.android.systemui.classifier.domain.interactor.FalsingInteractor
+import com.android.systemui.plugins.FalsingManager
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+class EditModeButtonViewModel
+@AssistedInject
+constructor(
+    private val editModeViewModel: EditModeViewModel,
+    private val falsingInteractor: FalsingInteractor,
+) {
+
+    fun onButtonClick() {
+        if (!falsingInteractor.isFalseTap(FalsingManager.LOW_PENALTY)) {
+            editModeViewModel.startEditing()
+        }
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): EditModeButtonViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
index b5873be..4a18872 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModel.kt
@@ -38,6 +38,7 @@
     paginatedGridInteractor: PaginatedGridInteractor,
     inFirstPageViewModel: InFirstPageViewModel,
     val buildNumberViewModelFactory: BuildNumberViewModel.Factory,
+    val editModeButtonViewModelFactory: EditModeButtonViewModel.Factory,
     private val falsingInteractor: FalsingInteractor,
 ) : IconTilesViewModel by iconTilesViewModel, ExclusiveActivatable() {
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelKosmos.kt
new file mode 100644
index 0000000..b8d3ff4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeButtonViewModelKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import com.android.systemui.classifier.domain.interactor.falsingInteractor
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.editModeButtonViewModelFactory by
+    Kosmos.Fixture {
+        object : EditModeButtonViewModel.Factory {
+            override fun create(): EditModeButtonViewModel {
+                return EditModeButtonViewModel(editModeViewModel, falsingInteractor)
+            }
+        }
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
index d515ad0..5c71ba2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/PaginatedGridViewModelKosmos.kt
@@ -29,6 +29,7 @@
             paginatedGridInteractor,
             inFirstPageViewModel,
             buildNumberViewModelFactory,
+            editModeButtonViewModelFactory,
             falsingInteractor,
         )
     }