Add keboard focus outline for Chooser targets

This change applies the same focus outline as Launcher but the end
result still needs more polishing. Some noticeable issues are:
* the outline may overlap with a long label;
* targets with a one-line labels look adjusted to the top and not
  centered;
* with the light system ui theme, the outer online frame is barely
  visible compare to the inner outline (this is also true for Launcher).

Bug: 295175912
Test: visual effect testing
Flag: com.android.intentresolver.target_hover_and_keyboard_focus_states
Change-Id: I1d22b187e0cc4b95c385d4f5b956effa31fd4505
diff --git a/java/res/layout/chooser_grid_item_hover.xml b/java/res/layout/chooser_grid_item_hover.xml
index f4396ec..0520606 100644
--- a/java/res/layout/chooser_grid_item_hover.xml
+++ b/java/res/layout/chooser_grid_item_hover.xml
@@ -19,6 +19,7 @@
 <com.android.intentresolver.widget.ChooserTargetItemView
               xmlns:android="http://schemas.android.com/apk/res/android"
               xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+              xmlns:app="http://schemas.android.com/apk/res-auto"
               android:id="@androidprv:id/item"
               android:orientation="vertical"
               android:layout_width="match_parent"
@@ -28,7 +29,11 @@
               android:paddingVertical="1dp"
               android:paddingHorizontal="4dp"
               android:focusable="true"
-              android:background="?android:attr/selectableItemBackgroundBorderless">
+              android:defaultFocusHighlightEnabled="false"
+              app:focusOutlineWidth="@dimen/chooser_item_focus_outline_width"
+              app:focusOutlineCornerRadius="@dimen/chooser_item_focus_outline_corner_radius"
+              app:focusOutlineColor="?androidprv:attr/materialColorSecondaryFixed"
+              app:focusInnerOutlineColor="?androidprv:attr/materialColorOnSecondaryFixedVariant">
 
     <ImageView android:id="@android:id/icon"
                android:layout_width="@dimen/chooser_icon_width_with_padding"
diff --git a/java/res/values-sw600dp/dimens.xml b/java/res/values-sw600dp/dimens.xml
index 240ee06..e152ba0 100644
--- a/java/res/values-sw600dp/dimens.xml
+++ b/java/res/values-sw600dp/dimens.xml
@@ -20,4 +20,5 @@
 <resources>
     <dimen name="chooser_width">624dp</dimen>
     <dimen name="modify_share_text_toggle_max_width">250dp</dimen>
+    <dimen name="chooser_item_focus_outline_corner_radius">16dp</dimen>
 </resources>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index c9f2c30..8c3ff7e 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -56,4 +56,11 @@
         <attr name="itemOuterSpacing" format="dimension" />
         <attr name="maxWidthHint" format="dimension" />
     </declare-styleable>
+
+    <declare-styleable name="ChooserTargetItemView">
+        <attr name="focusOutlineColor" format="color" />
+        <attr name="focusInnerOutlineColor" format="color" />
+        <attr name="focusOutlineWidth" format="dimension" />
+        <attr name="focusOutlineCornerRadius" format="dimension" />
+    </declare-styleable>
 </resources>
diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml
index f85ad06..515343b 100644
--- a/java/res/values/dimens.xml
+++ b/java/res/values/dimens.xml
@@ -41,6 +41,8 @@
     <dimen name="chooser_headline_text_size">18sp</dimen>
     <dimen name="chooser_grid_target_name_text_size">12sp</dimen>
     <dimen name="chooser_grid_activity_name_text_size">12sp</dimen>
+    <dimen name="chooser_item_focus_outline_corner_radius">11dp</dimen>
+    <dimen name="chooser_item_focus_outline_width">2dp</dimen>
     <dimen name="resolver_icon_size">32dp</dimen>
     <dimen name="resolver_button_bar_spacing">0dp</dimen>
     <dimen name="resolver_badge_size">18dp</dimen>
diff --git a/java/src/com/android/intentresolver/widget/ChooserTargetItemView.kt b/java/src/com/android/intentresolver/widget/ChooserTargetItemView.kt
index 2893449..b5a4d61 100644
--- a/java/src/com/android/intentresolver/widget/ChooserTargetItemView.kt
+++ b/java/src/com/android/intentresolver/widget/ChooserTargetItemView.kt
@@ -17,11 +17,16 @@
 package com.android.intentresolver.widget
 
 import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
 import android.util.AttributeSet
+import android.util.TypedValue
 import android.view.MotionEvent
 import android.view.View
 import android.widget.ImageView
 import android.widget.LinearLayout
+import com.android.intentresolver.R
 
 class ChooserTargetItemView(
     context: Context,
@@ -29,6 +34,14 @@
     defStyleAttr: Int,
     defStyleRes: Int,
 ) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
+    private val outlineRadius: Float
+    private val outlineWidth: Float
+    private val outlinePaint: Paint =
+        Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.STROKE }
+    private val outlineInnerPaint: Paint =
+        Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.STROKE }
+    private var iconView: ImageView? = null
+
     constructor(context: Context) : this(context, null)
 
     constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
@@ -39,7 +52,28 @@
         defStyleAttr: Int,
     ) : this(context, attrs, defStyleAttr, 0)
 
-    private var iconView: ImageView? = null
+    init {
+        val a = context.obtainStyledAttributes(attrs, R.styleable.ChooserTargetItemView)
+        val defaultWidth =
+            TypedValue.applyDimension(
+                TypedValue.COMPLEX_UNIT_DIP,
+                2f,
+                context.resources.displayMetrics,
+            )
+        outlineRadius =
+            a.getDimension(R.styleable.ChooserTargetItemView_focusOutlineCornerRadius, 0f)
+        outlineWidth =
+            a.getDimension(R.styleable.ChooserTargetItemView_focusOutlineWidth, defaultWidth)
+
+        outlinePaint.strokeWidth = outlineWidth
+        outlinePaint.color =
+            a.getColor(R.styleable.ChooserTargetItemView_focusOutlineColor, Color.TRANSPARENT)
+
+        outlineInnerPaint.strokeWidth = outlineWidth
+        outlineInnerPaint.color =
+            a.getColor(R.styleable.ChooserTargetItemView_focusInnerOutlineColor, Color.TRANSPARENT)
+        a.recycle()
+    }
 
     override fun onViewAdded(child: View) {
         super.onViewAdded(child)
@@ -70,4 +104,38 @@
     }
 
     override fun onInterceptHoverEvent(event: MotionEvent?) = true
+
+    override fun dispatchDraw(canvas: Canvas) {
+        super.dispatchDraw(canvas)
+        if (isFocused) {
+            drawFocusInnerOutline(canvas)
+            drawFocusOutline(canvas)
+        }
+    }
+
+    private fun drawFocusInnerOutline(canvas: Canvas) {
+        val outlineOffset = outlineWidth + outlineWidth / 2
+        canvas.drawRoundRect(
+            outlineOffset,
+            outlineOffset,
+            maxOf(0f, width - outlineOffset),
+            maxOf(0f, height - outlineOffset),
+            outlineRadius - outlineWidth,
+            outlineRadius - outlineWidth,
+            outlineInnerPaint,
+        )
+    }
+
+    private fun drawFocusOutline(canvas: Canvas) {
+        val outlineOffset = outlineWidth / 2
+        canvas.drawRoundRect(
+            outlineOffset,
+            outlineOffset,
+            maxOf(0f, width - outlineOffset),
+            maxOf(0f, height - outlineOffset),
+            outlineRadius,
+            outlineRadius,
+            outlinePaint,
+        )
+    }
 }