1 /* 2 * Copyright (C) 2020 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 * http://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.wm.shell.bubbles 18 19 import android.app.ActivityTaskManager.INVALID_TASK_ID 20 import android.content.Context 21 import android.graphics.Bitmap 22 import android.graphics.Matrix 23 import android.graphics.Path 24 import android.graphics.drawable.AdaptiveIconDrawable 25 import android.graphics.drawable.ColorDrawable 26 import android.graphics.drawable.InsetDrawable 27 import android.util.PathParser 28 import android.util.TypedValue 29 import android.view.LayoutInflater 30 import android.widget.FrameLayout 31 import com.android.wm.shell.R 32 33 class BubbleOverflow( 34 private val context: Context, 35 private val positioner: BubblePositioner 36 ) : BubbleViewProvider { 37 38 private lateinit var bitmap: Bitmap 39 private lateinit var dotPath: Path 40 41 private var dotColor = 0 42 private var showDot = false 43 private var overflowIconInset = 0 44 45 private val inflater: LayoutInflater = LayoutInflater.from(context) 46 private var expandedView: BubbleExpandedView? 47 private var overflowBtn: BadgedImageView? 48 49 init { 50 updateResources() 51 expandedView = null 52 overflowBtn = null 53 } 54 55 /** Call before use and again if cleanUpExpandedState was called. */ initializenull56 fun initialize(controller: BubbleController) { 57 createExpandedView() 58 getExpandedView()?.initialize(controller, controller.stackView, true /* isOverflow */) 59 } 60 cleanUpExpandedStatenull61 fun cleanUpExpandedState() { 62 expandedView?.cleanUpExpandedState() 63 expandedView = null 64 } 65 updatenull66 fun update() { 67 updateResources() 68 getExpandedView()?.applyThemeAttrs() 69 // Apply inset and new style to fresh icon drawable. 70 getIconView()?.setIconImageResource(R.drawable.bubble_ic_overflow_button) 71 updateBtnTheme() 72 } 73 updateResourcesnull74 fun updateResources() { 75 overflowIconInset = context.resources.getDimensionPixelSize( 76 R.dimen.bubble_overflow_icon_inset) 77 overflowBtn?.layoutParams = FrameLayout.LayoutParams(positioner.bubbleSize, 78 positioner.bubbleSize) 79 expandedView?.updateDimensions() 80 } 81 updateBtnThemenull82 private fun updateBtnTheme() { 83 val res = context.resources 84 85 // Set overflow button accent color, dot color 86 val typedValue = TypedValue() 87 context.theme.resolveAttribute(com.android.internal.R.attr.colorAccentPrimary, 88 typedValue, true) 89 val colorAccent = res.getColor(typedValue.resourceId, null) 90 dotColor = colorAccent 91 92 val shapeColor = res.getColor(android.R.color.system_accent1_1000) 93 overflowBtn?.iconDrawable?.setTint(shapeColor) 94 95 val iconFactory = BubbleIconFactory(context) 96 97 // Update bitmap 98 val fg = InsetDrawable(overflowBtn?.iconDrawable, overflowIconInset) 99 bitmap = iconFactory.createBadgedIconBitmap( 100 AdaptiveIconDrawable(ColorDrawable(colorAccent), fg)).icon 101 102 // Update dot path 103 dotPath = PathParser.createPathFromPathData( 104 res.getString(com.android.internal.R.string.config_icon_mask)) 105 val scale = iconFactory.normalizer.getScale(iconView!!.iconDrawable, 106 null /* outBounds */, null /* path */, null /* outMaskShape */) 107 val radius = BadgedImageView.DEFAULT_PATH_SIZE / 2f 108 val matrix = Matrix() 109 matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */, 110 radius /* pivot y */) 111 dotPath.transform(matrix) 112 113 // Attach BubbleOverflow to BadgedImageView 114 overflowBtn?.setRenderedBubble(this) 115 overflowBtn?.removeDotSuppressionFlag(BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE) 116 } 117 setVisiblenull118 fun setVisible(visible: Int) { 119 overflowBtn?.visibility = visible 120 } 121 setShowDotnull122 fun setShowDot(show: Boolean) { 123 showDot = show 124 overflowBtn?.updateDotVisibility(true /* animate */) 125 } 126 createExpandedViewnull127 fun createExpandedView(): BubbleExpandedView? { 128 expandedView = inflater.inflate(R.layout.bubble_expanded_view, 129 null /* root */, false /* attachToRoot */) as BubbleExpandedView 130 expandedView?.applyThemeAttrs() 131 updateResources() 132 return expandedView 133 } 134 getExpandedViewnull135 override fun getExpandedView(): BubbleExpandedView? { 136 return expandedView 137 } 138 getDotColornull139 override fun getDotColor(): Int { 140 return dotColor 141 } 142 getAppBadgenull143 override fun getAppBadge(): Bitmap? { 144 return null 145 } 146 getRawAppBadgenull147 override fun getRawAppBadge(): Bitmap? { 148 return null 149 } 150 getBubbleIconnull151 override fun getBubbleIcon(): Bitmap { 152 return bitmap 153 } 154 showDotnull155 override fun showDot(): Boolean { 156 return showDot 157 } 158 getDotPathnull159 override fun getDotPath(): Path? { 160 return dotPath 161 } 162 setTaskViewVisibilitynull163 override fun setTaskViewVisibility(visible: Boolean) { 164 // Overflow does not have a TaskView. 165 } 166 getIconViewnull167 override fun getIconView(): BadgedImageView? { 168 if (overflowBtn == null) { 169 overflowBtn = inflater.inflate(R.layout.bubble_overflow_button, 170 null /* root */, false /* attachToRoot */) as BadgedImageView 171 overflowBtn?.initialize(positioner) 172 overflowBtn?.contentDescription = context.resources.getString( 173 R.string.bubble_overflow_button_content_description) 174 val bubbleSize = positioner.bubbleSize 175 overflowBtn?.layoutParams = FrameLayout.LayoutParams(bubbleSize, bubbleSize) 176 updateBtnTheme() 177 } 178 return overflowBtn 179 } 180 getKeynull181 override fun getKey(): String { 182 return KEY 183 } 184 getTaskIdnull185 override fun getTaskId(): Int { 186 return if (expandedView != null) expandedView!!.taskId else INVALID_TASK_ID 187 } 188 189 companion object { 190 const val KEY = "Overflow" 191 } 192 }