Merge "Shape screen communication with the Launcher's app (1/2)" into main
diff --git a/res/layout/color_option2.xml b/res/layout/color_option2.xml
new file mode 100644
index 0000000..2605da9
--- /dev/null
+++ b/res/layout/color_option2.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+     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.
+-->
+<!-- Content description is set programmatically on the parent FrameLayout -->
+<com.android.customization.picker.color.ui.view.ColorOptionIconView2
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@id/background"
+    android:layout_width="@dimen/floating_sheet_color_option_size"
+    android:layout_height="@dimen/floating_sheet_color_option_size"/>
+
diff --git a/res/layout/floating_sheet_colors.xml b/res/layout/floating_sheet_colors.xml
index 5834caa..f8cfc98 100644
--- a/res/layout/floating_sheet_colors.xml
+++ b/res/layout/floating_sheet_colors.xml
@@ -25,7 +25,8 @@
         android:layout_height="wrap_content"
         android:background="@drawable/floating_sheet_content_background"
         android:paddingVertical="@dimen/floating_sheet_content_vertical_padding"
-        android:orientation="vertical">
+        android:orientation="vertical"
+        android:clipChildren="false">
 
         <TextView
             android:id="@+id/color_type_tab_subhead"
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index fe06735..c242fe9 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -191,5 +191,7 @@
     <dimen name="floating_sheet_clock_edit_icon_size">48dp</dimen>
     <dimen name="floating_sheet_clock_style_thumbnail_margin">12dp</dimen>
     <dimen name="floating_sheet_clock_style_clock_size_text_margin_end">16dp</dimen>
+    <dimen name="floating_sheet_color_option_size">54dp</dimen>
+    <dimen name="floating_sheet_color_option_stroke_width">3dp</dimen>
     <dimen name="customization_option_entry_shortcut_icon_size">20dp</dimen>
 </resources>
diff --git a/src/com/android/customization/picker/color/ui/binder/ColorOptionIconBinder2.kt b/src/com/android/customization/picker/color/ui/binder/ColorOptionIconBinder2.kt
new file mode 100644
index 0000000..2c197ad
--- /dev/null
+++ b/src/com/android/customization/picker/color/ui/binder/ColorOptionIconBinder2.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.customization.picker.color.ui.binder
+
+import com.android.customization.picker.color.ui.view.ColorOptionIconView2
+import com.android.customization.picker.color.ui.viewmodel.ColorOptionIconViewModel
+
+object ColorOptionIconBinder2 {
+    fun bind(view: ColorOptionIconView2, viewModel: ColorOptionIconViewModel, darkTheme: Boolean) {
+        if (darkTheme) {
+            view.bindColor(
+                view.resources.getColor(android.R.color.system_primary_dark, view.context.theme),
+                viewModel.darkThemeColor0,
+                viewModel.darkThemeColor1,
+                viewModel.darkThemeColor2,
+                viewModel.darkThemeColor3,
+            )
+        } else {
+            view.bindColor(
+                view.resources.getColor(android.R.color.system_primary_light, view.context.theme),
+                viewModel.lightThemeColor0,
+                viewModel.lightThemeColor1,
+                viewModel.lightThemeColor2,
+                viewModel.lightThemeColor3,
+            )
+        }
+    }
+}
diff --git a/src/com/android/customization/picker/color/ui/view/ColorOptionIconView2.kt b/src/com/android/customization/picker/color/ui/view/ColorOptionIconView2.kt
new file mode 100644
index 0000000..3fc6324
--- /dev/null
+++ b/src/com/android/customization/picker/color/ui/view/ColorOptionIconView2.kt
@@ -0,0 +1,139 @@
+/*
+ * 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.customization.picker.color.ui.view
+
+import android.annotation.ColorInt
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.Path
+import android.util.AttributeSet
+import com.android.themepicker.R
+import com.android.wallpaper.picker.option.ui.view.OptionItemBackground
+
+/**
+ * Draw a color option icon, which is a quadrant circle that can show at most 4 different colors.
+ */
+class ColorOptionIconView2(context: Context, attrs: AttributeSet) :
+    OptionItemBackground(context, attrs) {
+
+    private val paint = Paint().apply { style = Paint.Style.FILL }
+
+    private val path = Path()
+
+    private var color0 = DEFAULT_PLACEHOLDER_COLOR
+    private var color1 = DEFAULT_PLACEHOLDER_COLOR
+    private var color2 = DEFAULT_PLACEHOLDER_COLOR
+    private var color3 = DEFAULT_PLACEHOLDER_COLOR
+    private var strokeColor = DEFAULT_PLACEHOLDER_COLOR
+    private val strokeWidth =
+        context.resources
+            .getDimensionPixelSize(R.dimen.floating_sheet_color_option_stroke_width)
+            .toFloat()
+
+    private var w = 0
+    private var h = 0
+
+    /**
+     * @param color0 the color in the top left quadrant
+     * @param color1 the color in the top right quadrant
+     * @param color2 the color in the bottom left quadrant
+     * @param color3 the color in the bottom right quadrant
+     */
+    fun bindColor(
+        @ColorInt strokeColor: Int,
+        @ColorInt color0: Int,
+        @ColorInt color1: Int,
+        @ColorInt color2: Int,
+        @ColorInt color3: Int,
+    ) {
+        this.strokeColor = strokeColor
+        this.color0 = color0
+        this.color1 = color1
+        this.color2 = color2
+        this.color3 = color3
+        invalidate()
+    }
+
+    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+        this.w = w
+        this.h = h
+        super.onSizeChanged(w, h, oldw, oldh)
+    }
+
+    override fun onDraw(canvas: Canvas) {
+        // The w and h need to be an even number to avoid tiny pixel-level gaps between the pies
+        w = w.roundDownToEven()
+        h = h.roundDownToEven()
+
+        val width = w.toFloat()
+        val height = h.toFloat()
+
+        val left = 2 * strokeWidth
+        val right = width - 2 * strokeWidth
+        val top = 2 * strokeWidth
+        val bottom = height - 2 * strokeWidth
+        val cornerRadius = ((right - left) / 2) * (1f - 0.25f * progress)
+        val save = canvas.save()
+        path.reset()
+        path.addRoundRect(left, top, right, bottom, cornerRadius, cornerRadius, Path.Direction.CW)
+        path.close()
+        canvas.clipPath(path)
+
+        canvas.apply {
+            paint.style = Paint.Style.FILL
+            // top left
+            paint.color = color0
+            drawRect(0f, 0f, width / 2, height / 2, paint)
+            // top right
+            paint.color = color1
+            drawRect(width / 2, 0f, width, height / 2, paint)
+            // bottom left
+            paint.color = color2
+            drawRect(0f, height / 2, width / 2, height, paint)
+            // bottom right
+            paint.color = color3
+            drawRect(width / 2, height / 2, width, height, paint)
+        }
+
+        canvas.restoreToCount(save)
+        paint.style = Paint.Style.STROKE
+        paint.color = strokeColor
+        paint.alpha = (255 * progress).toInt()
+        paint.strokeWidth = this.strokeWidth
+        val strokeCornerRadius = ((width - strokeWidth) / 2) * (1f - 0.25f * progress)
+        val halfStrokeWidth = 0.5f * strokeWidth
+        // Stroke is centered along the path, so account for half strokeWidth to stay within View
+        canvas.drawRoundRect(
+            halfStrokeWidth,
+            halfStrokeWidth,
+            width - halfStrokeWidth,
+            height - halfStrokeWidth,
+            strokeCornerRadius,
+            strokeCornerRadius,
+            paint,
+        )
+    }
+
+    companion object {
+        const val DEFAULT_PLACEHOLDER_COLOR = Color.BLACK
+
+        fun Int.roundDownToEven(): Int {
+            return if (this % 2 == 0) this else this - 1
+        }
+    }
+}
diff --git a/src/com/android/wallpaper/customization/ui/binder/ColorsFloatingSheetBinder.kt b/src/com/android/wallpaper/customization/ui/binder/ColorsFloatingSheetBinder.kt
index 10bedb3..58a1fd9 100644
--- a/src/com/android/wallpaper/customization/ui/binder/ColorsFloatingSheetBinder.kt
+++ b/src/com/android/wallpaper/customization/ui/binder/ColorsFloatingSheetBinder.kt
@@ -25,13 +25,12 @@
 import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
-import androidx.recyclerview.widget.GridLayoutManager
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
-import com.android.customization.picker.color.ui.binder.ColorOptionIconBinder
-import com.android.customization.picker.color.ui.view.ColorOptionIconView
+import com.android.customization.picker.color.ui.binder.ColorOptionIconBinder2
+import com.android.customization.picker.color.ui.view.ColorOptionIconView2
 import com.android.customization.picker.color.ui.viewmodel.ColorOptionIconViewModel
-import com.android.customization.picker.common.ui.view.DoubleRowListItemSpacing
+import com.android.customization.picker.common.ui.view.SingleRowListItemSpacing
 import com.android.customization.picker.mode.ui.binder.DarkModeBinder
 import com.android.themepicker.R
 import com.android.wallpaper.customization.ui.util.ThemePickerCustomizationOptionUtil.ThemePickerHomeCustomizationOption.COLORS
@@ -39,7 +38,7 @@
 import com.android.wallpaper.picker.customization.ui.view.FloatingToolbar
 import com.android.wallpaper.picker.customization.ui.view.adapter.FloatingToolbarTabAdapter
 import com.android.wallpaper.picker.customization.ui.viewmodel.ColorUpdateViewModel
-import com.android.wallpaper.picker.option.ui.adapter.OptionItemAdapter
+import com.android.wallpaper.picker.option.ui.adapter.OptionItemAdapter2
 import java.lang.ref.WeakReference
 import kotlinx.coroutines.launch
 
@@ -110,35 +109,33 @@
     private fun createOptionItemAdapter(
         uiMode: Int,
         lifecycleOwner: LifecycleOwner,
-    ): OptionItemAdapter<ColorOptionIconViewModel> =
-        OptionItemAdapter(
-            layoutResourceId = R.layout.color_option,
+    ): OptionItemAdapter2<ColorOptionIconViewModel> =
+        OptionItemAdapter2(
+            layoutResourceId = R.layout.color_option2,
             lifecycleOwner = lifecycleOwner,
-            bindIcon = { foregroundView: View, colorIcon: ColorOptionIconViewModel ->
-                val colorOptionIconView = foregroundView as? ColorOptionIconView
+            bindPayload = { itemView: View, colorIcon: ColorOptionIconViewModel ->
+                val colorOptionIconView =
+                    itemView.requireViewById<ColorOptionIconView2>(
+                        com.android.wallpaper.R.id.background
+                    )
                 val night = uiMode and UI_MODE_NIGHT_MASK == UI_MODE_NIGHT_YES
-                colorOptionIconView?.let { ColorOptionIconBinder.bind(it, colorIcon, night) }
+                ColorOptionIconBinder2.bind(colorOptionIconView, colorIcon, night)
             },
         )
 
     private fun RecyclerView.initColorsList(
         context: Context,
-        adapter: OptionItemAdapter<ColorOptionIconViewModel>,
+        adapter: OptionItemAdapter2<ColorOptionIconViewModel>,
     ) {
         apply {
             this.adapter = adapter
-            layoutManager = GridLayoutManager(context, 2, GridLayoutManager.HORIZONTAL, false)
+            layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
             addItemDecoration(
-                DoubleRowListItemSpacing(
+                SingleRowListItemSpacing(
                     context.resources.getDimensionPixelSize(
                         R.dimen.floating_sheet_content_horizontal_padding
                     ),
-                    context.resources.getDimensionPixelSize(
-                        R.dimen.floating_sheet_list_item_horizontal_space
-                    ),
-                    context.resources.getDimensionPixelSize(
-                        R.dimen.floating_sheet_list_item_vertical_space
-                    ),
+                    0,
                 )
             )
         }
diff --git a/src/com/android/wallpaper/customization/ui/binder/ShortcutFloatingSheetBinder.kt b/src/com/android/wallpaper/customization/ui/binder/ShortcutFloatingSheetBinder.kt
index 0fdf931..4c72412 100644
--- a/src/com/android/wallpaper/customization/ui/binder/ShortcutFloatingSheetBinder.kt
+++ b/src/com/android/wallpaper/customization/ui/binder/ShortcutFloatingSheetBinder.kt
@@ -152,9 +152,10 @@
         OptionItemAdapter2(
             layoutResourceId = R.layout.quick_affordance_list_item2,
             lifecycleOwner = lifecycleOwner,
-            bindIcon = { foregroundView: View, gridIcon: Icon ->
-                val imageView = foregroundView as? ImageView
-                imageView?.let { IconViewBinder.bind(imageView, gridIcon) }
+            bindPayload = { itemView: View, gridIcon: Icon ->
+                val imageView =
+                    itemView.requireViewById<ImageView>(com.android.wallpaper.R.id.foreground)
+                IconViewBinder.bind(imageView, gridIcon)
             },
         )