Added buttons for entering and exiting customize mode

+ added buttons for adding shortcut.

Fix: 373632868
Fix: 373619545
Test: Manual - ensure UI corresponds with UI mocks.
Flag: com.android.systemui.keyboard_shortcut_helper_shortcut_customizer
Change-Id: Ia8a695049cbc2dfc2a638f31d06b2708aef20313
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index d124c02..d50a92b 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1667,3 +1667,12 @@
   description: "Expands the shade on long press of any status bar"
   bug: "371224114"
 }
+
+
+flag {
+    name: "keyboard_shortcut_helper_shortcut_customizer"
+    namespace: "systemui"
+    description: "An implementation of shortcut customizations through shortcut helper."
+    bug: "365064144"
+}
+
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index fe720b9..412d1d9 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3732,6 +3732,10 @@
     <!-- Title at the top of the keyboard shortcut helper UI. The helper is a component
          that shows the user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] -->
     <string name="shortcut_helper_title">Keyboard shortcuts</string>
+    <!-- Title at the top of the keyboard shortcut helper UI when in customize mode. The helper
+         is a component that shows the user which keyboard shortcuts they can use.
+         [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_customize_mode_title">Customize keyboard shortcuts</string>
     <!-- Placeholder text shown in the search box of the keyboard shortcut helper, when the user
          hasn't typed in anything in the search box yet. The helper is a  component that shows the
          user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] -->
@@ -3743,6 +3747,14 @@
          use. The helper shows shortcuts in categories, which can be collapsed or expanded.
          [CHAR LIMIT=NONE] -->
     <string name="shortcut_helper_content_description_collapse_icon">Collapse icon</string>
+    <!-- Description text of the button that allows user to customize shortcuts in keyboard
+         shortcut helper The helper is a  component that shows the  user which keyboard shortcuts
+         they can use. [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_customize_button_text">Customize</string>
+    <!-- Description text of the button that allows user to exit shortcut customization mode in
+         keyboard shortcut helper The helper is a  component that shows the  user which keyboard
+         shortcuts they can use. [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_done_button_text">Done</string>
     <!-- Content description of the icon that allows to expand a keyboard shortcut helper category
          panel. The helper is a  component that shows the  user which keyboard shortcuts they can
          use. The helper shows shortcuts in categories, which can be collapsed or expanded.
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
index 5cade68..d537056 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
@@ -52,8 +52,10 @@
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.automirrored.filled.OpenInNew
+import androidx.compose.material.icons.filled.Add
 import androidx.compose.material.icons.filled.ExpandMore
 import androidx.compose.material.icons.filled.Search
+import androidx.compose.material.icons.filled.Tune
 import androidx.compose.material3.CenterAlignedTopAppBar
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.HorizontalDivider
@@ -69,6 +71,7 @@
 import androidx.compose.material3.TopAppBarDefaults
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -169,6 +172,7 @@
             selectedCategoryType,
             onCategorySelected = { selectedCategoryType = it },
             onKeyboardSettingsClicked,
+            shortcutsUiState.isShortcutCustomizerFlagEnabled,
         )
     }
 }
@@ -357,10 +361,29 @@
     selectedCategoryType: ShortcutCategoryType?,
     onCategorySelected: (ShortcutCategoryType?) -> Unit,
     onKeyboardSettingsClicked: () -> Unit,
+    isShortcutCustomizerFlagEnabled: Boolean,
 ) {
     val selectedCategory = categories.fastFirstOrNull { it.type == selectedCategoryType }
+    var isCustomizeModeEntered by remember { mutableStateOf(false) }
+    val isCustomizing by
+        remember(isCustomizeModeEntered, isShortcutCustomizerFlagEnabled) {
+            derivedStateOf { isCustomizeModeEntered && isCustomizeModeEntered }
+        }
+
     Column(modifier = modifier.fillMaxSize().padding(horizontal = 24.dp)) {
-        TitleBar()
+        Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
+            Box(modifier = Modifier.padding(start = 202.dp).width(412.dp)) {
+                TitleBar(isCustomizing)
+            }
+            Spacer(modifier = Modifier.weight(1f))
+            if (isShortcutCustomizerFlagEnabled) {
+                if (isCustomizeModeEntered) {
+                    DoneButton(onClick = { isCustomizeModeEntered = false })
+                } else {
+                    CustomizeButton(onClick = { isCustomizeModeEntered = true })
+                }
+            }
+        }
         Spacer(modifier = Modifier.height(12.dp))
         Row(Modifier.fillMaxWidth()) {
             StartSidePanel(
@@ -372,13 +395,46 @@
                 onCategoryClicked = { onCategorySelected(it.type) },
             )
             Spacer(modifier = Modifier.width(24.dp))
-            EndSidePanel(searchQuery, Modifier.fillMaxSize().padding(top = 8.dp), selectedCategory)
+            EndSidePanel(
+                searchQuery,
+                Modifier.fillMaxSize().padding(top = 8.dp),
+                selectedCategory,
+                isCustomizing = isCustomizing,
+            )
         }
     }
 }
 
 @Composable
-private fun EndSidePanel(searchQuery: String, modifier: Modifier, category: ShortcutCategoryUi?) {
+private fun CustomizeButton(onClick: () -> Unit) {
+    ShortcutHelperButton(
+        onClick = onClick,
+        color = MaterialTheme.colorScheme.secondaryContainer,
+        width = 133.dp,
+        iconSource = IconSource(imageVector = Icons.Default.Tune),
+        text = stringResource(id = R.string.shortcut_helper_customize_button_text),
+        contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
+    )
+}
+
+@Composable
+private fun DoneButton(onClick: () -> Unit) {
+    ShortcutHelperButton(
+        onClick = onClick,
+        color = MaterialTheme.colorScheme.primary,
+        width = 69.dp,
+        text = stringResource(R.string.shortcut_helper_done_button_text),
+        contentColor = MaterialTheme.colorScheme.onPrimary,
+    )
+}
+
+@Composable
+private fun EndSidePanel(
+    searchQuery: String,
+    modifier: Modifier,
+    category: ShortcutCategoryUi?,
+    isCustomizing: Boolean,
+) {
     val listState = rememberLazyListState()
     LaunchedEffect(key1 = category) { if (category != null) listState.animateScrollToItem(0) }
     if (category == null) {
@@ -387,7 +443,11 @@
     }
     LazyColumn(modifier = modifier, state = listState) {
         items(category.subCategories) { subcategory ->
-            SubCategoryContainerDualPane(searchQuery = searchQuery, subCategory = subcategory)
+            SubCategoryContainerDualPane(
+                searchQuery = searchQuery,
+                subCategory = subcategory,
+                isCustomizing = isCustomizing,
+            )
             Spacer(modifier = Modifier.height(8.dp))
         }
     }
@@ -412,7 +472,11 @@
 }
 
 @Composable
-private fun SubCategoryContainerDualPane(searchQuery: String, subCategory: ShortcutSubCategory) {
+private fun SubCategoryContainerDualPane(
+    searchQuery: String,
+    subCategory: ShortcutSubCategory,
+    isCustomizing: Boolean,
+) {
     Surface(
         modifier = Modifier.fillMaxWidth(),
         shape = RoundedCornerShape(28.dp),
@@ -432,6 +496,7 @@
                     modifier = Modifier.padding(vertical = 8.dp),
                     searchQuery = searchQuery,
                     shortcut = shortcut,
+                    isCustomizing = isCustomizing,
                 )
             }
         }
@@ -448,7 +513,12 @@
 }
 
 @Composable
-private fun Shortcut(modifier: Modifier, searchQuery: String, shortcut: ShortcutModel) {
+private fun Shortcut(
+    modifier: Modifier,
+    searchQuery: String,
+    shortcut: ShortcutModel,
+    isCustomizing: Boolean = false,
+) {
     val interactionSource = remember { MutableInteractionSource() }
     val isFocused by interactionSource.collectIsFocusedAsState()
     val focusColor = MaterialTheme.colorScheme.secondary
@@ -471,7 +541,7 @@
             ShortcutDescriptionText(searchQuery = searchQuery, shortcut = shortcut)
         }
         Spacer(modifier = Modifier.width(24.dp))
-        ShortcutKeyCombinations(modifier = Modifier.weight(1f), shortcut = shortcut)
+        ShortcutKeyCombinations(modifier = Modifier.weight(1f), shortcut = shortcut, isCustomizing)
     }
 }
 
@@ -495,7 +565,11 @@
 
 @OptIn(ExperimentalLayoutApi::class)
 @Composable
-private fun ShortcutKeyCombinations(modifier: Modifier = Modifier, shortcut: ShortcutModel) {
+private fun ShortcutKeyCombinations(
+    modifier: Modifier = Modifier,
+    shortcut: ShortcutModel,
+    isCustomizing: Boolean = false,
+) {
     FlowRow(
         modifier = modifier,
         verticalArrangement = Arrangement.spacedBy(8.dp),
@@ -507,6 +581,25 @@
             }
             ShortcutCommand(command)
         }
+        if (isCustomizing) {
+            Spacer(modifier = Modifier.width(16.dp))
+            ShortcutHelperButton(
+                modifier =
+                    Modifier.border(
+                        width = 1.dp,
+                        color = MaterialTheme.colorScheme.outline,
+                        shape = CircleShape,
+                    ),
+                onClick = {},
+                color = Color.Transparent,
+                width = 32.dp,
+                height = 32.dp,
+                iconSource = IconSource(imageVector = Icons.Default.Add),
+                contentColor = MaterialTheme.colorScheme.primary,
+                contentPaddingVertical = 0.dp,
+                contentPaddingHorizontal = 0.dp,
+            )
+        }
     }
 }
 
@@ -700,12 +793,18 @@
 
 @Composable
 @OptIn(ExperimentalMaterial3Api::class)
-private fun TitleBar() {
+private fun TitleBar(isCustomizing: Boolean = false) {
+    val text =
+        if (isCustomizing) {
+            stringResource(R.string.shortcut_helper_customize_mode_title)
+        } else {
+            stringResource(R.string.shortcut_helper_title)
+        }
     CenterAlignedTopAppBar(
         colors = TopAppBarDefaults.centerAlignedTopAppBarColors(containerColor = Color.Transparent),
         title = {
             Text(
-                text = stringResource(R.string.shortcut_helper_title),
+                text = text,
                 color = MaterialTheme.colorScheme.onSurface,
                 style = MaterialTheme.typography.headlineSmall,
             )
@@ -753,14 +852,12 @@
 
 @Composable
 private fun KeyboardSettings(horizontalPadding: Dp, verticalPadding: Dp, onClick: () -> Unit) {
-    val interactionSource = remember { MutableInteractionSource() }
     ClickableShortcutSurface(
         onClick = onClick,
         shape = RoundedCornerShape(24.dp),
         color = Color.Transparent,
         modifier =
             Modifier.semantics { role = Role.Button }.fillMaxWidth().padding(horizontal = 12.dp),
-        interactionSource = interactionSource,
         interactionsConfig =
             InteractionsConfig(
                 hoverOverlayColor = MaterialTheme.colorScheme.onSurface,
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
index f64d59a..435968e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
@@ -27,13 +27,24 @@
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.interaction.PressInteraction
 import androidx.compose.foundation.interaction.collectIsFocusedAsState
+import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.selection.selectable
+import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material3.ColorScheme
+import androidx.compose.material3.Icon
 import androidx.compose.material3.LocalAbsoluteTonalElevation
 import androidx.compose.material3.LocalContentColor
 import androidx.compose.material3.LocalTonalElevationEnabled
 import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
 import androidx.compose.material3.contentColorFor
 import androidx.compose.material3.minimumInteractiveComponentSize
 import androidx.compose.material3.surfaceColorAtElevation
@@ -43,6 +54,7 @@
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.CornerRadius
 import androidx.compose.ui.geometry.Offset
@@ -57,11 +69,16 @@
 import androidx.compose.ui.node.DelegatableNode
 import androidx.compose.ui.node.DrawModifierNode
 import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
 import androidx.compose.ui.zIndex
-import com.android.compose.modifiers.thenIf
 import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.compose.modifiers.thenIf
+import com.android.systemui.keyboard.shortcut.ui.model.IconSource
 
 /**
  * A selectable surface with no default focus/hover indications.
@@ -175,6 +192,96 @@
     }
 }
 
+/**
+ * A composable that provides a button with a customizable icon and text, designed to be re-used
+ * across shortcut helper/customizer. Supports defaults hover/focus/pressed states used across
+ * shortcut helper.
+ *
+ * This button utilizes [ClickableShortcutSurface] to provide a clickable surface with hover and
+ * pressed states, and a focus outline.
+ *
+ * The content of the button can be an icon (from [IconSource]) and/or text.
+ *
+ * @param modifier The modifier to be applied to the button.
+ * @param onClick The callback function that will be invoked when the button is clicked.
+ * @param shape The shape of the button. Defaults to a rounded corner shape used across shortcut
+ *   helper.
+ * @param color The background color of the button.
+ * @param width The width of the button.
+ * @param height The height of the button. Defaults to 40.dp as often used in shortcut helper
+ * @param iconSource The source of the icon to be displayed. Defaults to an empty [IconSource].
+ * @param text The text to be displayed. Defaults to null.
+ * @param contentColor The color of the icon and text.
+ * @param contentPaddingHorizontal The horizontal padding of the content. Defaults to 16.dp.
+ * @param contentPaddingVertical The vertical padding of the content. Defaults to 10.dp.
+ */
+@Composable
+fun ShortcutHelperButton(
+    modifier: Modifier = Modifier,
+    onClick: () -> Unit,
+    shape: Shape = RoundedCornerShape(360.dp),
+    color: Color,
+    width: Dp,
+    height: Dp = 40.dp,
+    iconSource: IconSource = IconSource(),
+    text: String? = null,
+    contentColor: Color,
+    contentPaddingHorizontal: Dp = 16.dp,
+    contentPaddingVertical: Dp = 10.dp,
+) {
+    ClickableShortcutSurface(
+        onClick = onClick,
+        shape = shape,
+        color = color,
+        modifier = modifier.semantics { role = Role.Button }.width(width).height(height),
+        interactionsConfig =
+            InteractionsConfig(
+                hoverOverlayColor = MaterialTheme.colorScheme.onSurface,
+                hoverOverlayAlpha = 0.11f,
+                pressedOverlayColor = MaterialTheme.colorScheme.onSurface,
+                pressedOverlayAlpha = 0.15f,
+                focusOutlineColor = MaterialTheme.colorScheme.secondary,
+                focusOutlineStrokeWidth = 3.dp,
+                focusOutlinePadding = 2.dp,
+                surfaceCornerRadius = 28.dp,
+                focusOutlineCornerRadius = 33.dp,
+            ),
+    ) {
+        Row(
+            modifier =
+                Modifier.padding(
+                    horizontal = contentPaddingHorizontal,
+                    vertical = contentPaddingVertical,
+                ),
+            verticalAlignment = Alignment.CenterVertically,
+            horizontalArrangement = Arrangement.Center,
+        ) {
+            if (iconSource.imageVector != null) {
+                Icon(
+                    tint = contentColor,
+                    imageVector = iconSource.imageVector,
+                    contentDescription = null,
+                    modifier = Modifier.size(20.dp).wrapContentSize(Alignment.Center),
+                )
+            }
+
+            if (iconSource.imageVector != null && text != null) {
+                Spacer(modifier = Modifier.weight(1f))
+            }
+
+            if (text != null) {
+                Text(
+                    text,
+                    color = contentColor,
+                    fontSize = 14.sp,
+                    style = MaterialTheme.typography.labelLarge,
+                    modifier = Modifier.wrapContentSize(Alignment.Center),
+                )
+            }
+        }
+    }
+}
+
 @Composable
 private fun surfaceColorAtElevation(color: Color, elevation: Dp): Color {
     return MaterialTheme.colorScheme.applyTonalElevation(color, elevation)
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt
index 8f23261..02b0b43 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt
@@ -24,6 +24,7 @@
         val searchQuery: String,
         val shortcutCategories: List<ShortcutCategoryUi>,
         val defaultSelectedCategory: ShortcutCategoryType?,
+        val isShortcutCustomizerFlagEnabled: Boolean = false,
     ) : ShortcutsUiState
 
     data object Inactive : ShortcutsUiState
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
index 20d09ed..912bfe9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
@@ -26,6 +26,7 @@
 import androidx.compose.material.icons.filled.Tv
 import androidx.compose.material.icons.filled.VerticalSplit
 import com.android.compose.ui.graphics.painter.DrawablePainter
+import com.android.systemui.Flags.keyboardShortcutHelperShortcutCustomizer
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperCategoriesInteractor
 import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperStateInteractor
@@ -86,6 +87,7 @@
                         searchQuery = query,
                         shortcutCategories = shortcutCategoriesUi,
                         defaultSelectedCategory = getDefaultSelectedCategory(filteredCategories),
+                        isShortcutCustomizerFlagEnabled = keyboardShortcutHelperShortcutCustomizer(),
                     )
                 }
             }