1 /* 2 * Copyright 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * https://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.intentresolver.widget 18 19 import android.content.Context 20 import android.graphics.Canvas 21 import android.graphics.Color 22 import android.graphics.Paint 23 import android.util.AttributeSet 24 import android.util.TypedValue 25 import android.view.MotionEvent 26 import android.view.View 27 import android.widget.ImageView 28 import android.widget.LinearLayout 29 import com.android.intentresolver.R 30 31 class ChooserTargetItemView( 32 context: Context, 33 attrs: AttributeSet?, 34 defStyleAttr: Int, 35 defStyleRes: Int, 36 ) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) { 37 private val outlineRadius: Float 38 private val outlineWidth: Float 39 private val outlinePaint: Paint = <lambda>null40 Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.STROKE } 41 private val outlineInnerPaint: Paint = <lambda>null42 Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.STROKE } 43 private var iconView: ImageView? = null 44 45 constructor(context: Context) : this(context, null) 46 47 constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) 48 49 constructor( 50 context: Context, 51 attrs: AttributeSet?, 52 defStyleAttr: Int, 53 ) : this(context, attrs, defStyleAttr, 0) 54 55 init { 56 val a = context.obtainStyledAttributes(attrs, R.styleable.ChooserTargetItemView) 57 val defaultWidth = 58 TypedValue.applyDimension( 59 TypedValue.COMPLEX_UNIT_DIP, 60 2f, 61 context.resources.displayMetrics, 62 ) 63 outlineRadius = 64 a.getDimension(R.styleable.ChooserTargetItemView_focusOutlineCornerRadius, 0f) 65 outlineWidth = 66 a.getDimension(R.styleable.ChooserTargetItemView_focusOutlineWidth, defaultWidth) 67 68 outlinePaint.strokeWidth = outlineWidth 69 outlinePaint.color = 70 a.getColor(R.styleable.ChooserTargetItemView_focusOutlineColor, Color.TRANSPARENT) 71 72 outlineInnerPaint.strokeWidth = outlineWidth 73 outlineInnerPaint.color = 74 a.getColor(R.styleable.ChooserTargetItemView_focusInnerOutlineColor, Color.TRANSPARENT) 75 a.recycle() 76 } 77 onViewAddednull78 override fun onViewAdded(child: View) { 79 super.onViewAdded(child) 80 if (child is ImageView) { 81 iconView = child 82 } 83 } 84 onViewRemovednull85 override fun onViewRemoved(child: View?) { 86 super.onViewRemoved(child) 87 if (child === iconView) { 88 iconView = null 89 } 90 } 91 onHoverEventnull92 override fun onHoverEvent(event: MotionEvent): Boolean { 93 val iconView = iconView ?: return false 94 if (!isEnabled) return true 95 when (event.action) { 96 MotionEvent.ACTION_HOVER_ENTER -> { 97 iconView.isHovered = true 98 } 99 MotionEvent.ACTION_HOVER_EXIT -> { 100 iconView.isHovered = false 101 } 102 } 103 return true 104 } 105 onInterceptHoverEventnull106 override fun onInterceptHoverEvent(event: MotionEvent?) = true 107 108 override fun dispatchDraw(canvas: Canvas) { 109 super.dispatchDraw(canvas) 110 if (isFocused) { 111 drawFocusInnerOutline(canvas) 112 drawFocusOutline(canvas) 113 } 114 } 115 drawFocusInnerOutlinenull116 private fun drawFocusInnerOutline(canvas: Canvas) { 117 val outlineOffset = outlineWidth + outlineWidth / 2 118 canvas.drawRoundRect( 119 outlineOffset, 120 outlineOffset, 121 maxOf(0f, width - outlineOffset), 122 maxOf(0f, height - outlineOffset), 123 outlineRadius - outlineWidth, 124 outlineRadius - outlineWidth, 125 outlineInnerPaint, 126 ) 127 } 128 drawFocusOutlinenull129 private fun drawFocusOutline(canvas: Canvas) { 130 val outlineOffset = outlineWidth / 2 131 canvas.drawRoundRect( 132 outlineOffset, 133 outlineOffset, 134 maxOf(0f, width - outlineOffset), 135 maxOf(0f, height - outlineOffset), 136 outlineRadius, 137 outlineRadius, 138 outlinePaint, 139 ) 140 } 141 } 142