• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.view.LayoutInflater
29 import android.view.View.VISIBLE
30 import android.widget.FrameLayout
31 import androidx.core.content.ContextCompat
32 import com.android.launcher3.icons.BubbleIconFactory
33 import com.android.wm.shell.R
34 import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView
35 
36 class BubbleOverflow(private val context: Context, private val positioner: BubblePositioner) :
37     BubbleViewProvider {
38 
39     private lateinit var bitmap: Bitmap
40     private lateinit var dotPath: Path
41 
42     private var dotColor = 0
43     private var showDot = false
44     private var overflowIconInset = 0
45 
46     private val inflater: LayoutInflater = LayoutInflater.from(context)
47     private var expandedView: BubbleExpandedView?
48     private var bubbleBarExpandedView: BubbleBarExpandedView? = null
49     private var overflowBtn: BadgedImageView?
50 
51     init {
52         updateResources()
53         expandedView = null
54         overflowBtn = null
55     }
56 
57     /** Call before use and again if cleanUpExpandedState was called. */
initializenull58     fun initialize(
59         expandedViewManager: BubbleExpandedViewManager,
60         stackView: BubbleStackView,
61         positioner: BubblePositioner
62     ) {
63         createExpandedView()
64                 .initialize(
65                         expandedViewManager,
66                     stackView,
67                     positioner,
68                     /* isOverflow= */ true,
69                     /* bubbleTaskView= */ null
70                 )
71     }
72 
initializeForBubbleBarnull73     fun initializeForBubbleBar(
74         expandedViewManager: BubbleExpandedViewManager,
75         positioner: BubblePositioner,
76     ) {
77         createBubbleBarExpandedView()
78             .initialize(
79                 expandedViewManager,
80                 positioner,
81                 /* isOverflow= */ true,
82                 /* bubbleTaskView= */ null,
83                 /* mainExecutor= */ null,
84                 /* backgroundExecutor= */ null,
85                 /* regionSamplingProvider= */ null,
86             )
87     }
88 
cleanUpExpandedStatenull89     fun cleanUpExpandedState() {
90         expandedView?.cleanUpExpandedState()
91         expandedView = null
92         bubbleBarExpandedView?.cleanUpExpandedState()
93         bubbleBarExpandedView = null
94     }
95 
updatenull96     fun update() {
97         updateResources()
98         getExpandedView()?.applyThemeAttrs()
99         getBubbleBarExpandedView()?.applyThemeAttrs()
100         // Apply inset and new style to fresh icon drawable.
101         getIconView()?.setIconImageResource(R.drawable.bubble_ic_overflow_button)
102         updateBtnTheme()
103     }
104 
updateResourcesnull105     fun updateResources() {
106         overflowIconInset =
107             context.resources.getDimensionPixelSize(R.dimen.bubble_overflow_icon_inset)
108         overflowBtn?.layoutParams =
109             FrameLayout.LayoutParams(positioner.bubbleSize, positioner.bubbleSize)
110         expandedView?.updateDimensions()
111     }
112 
updateBtnThemenull113     private fun updateBtnTheme() {
114         val res = context.resources
115 
116         // Set overflow button accent color, dot color
117         val colorAccent = context.getColor(com.android.internal.R.color.materialColorPrimaryFixed)
118         val shapeColor = context.getColor(com.android.internal.R.color.materialColorOnPrimaryFixed)
119 
120         dotColor = colorAccent
121         overflowBtn?.iconDrawable?.setTint(shapeColor)
122 
123         val iconFactory =
124             BubbleIconFactory(
125                 context,
126                 res.getDimensionPixelSize(R.dimen.bubble_size),
127                 res.getDimensionPixelSize(R.dimen.bubble_badge_size),
128                 ContextCompat.getColor(
129                     context,
130                     com.android.launcher3.icons.R.color.important_conversation
131                 ),
132                 res.getDimensionPixelSize(com.android.internal.R.dimen.importance_ring_stroke_width)
133             )
134 
135         // Update bitmap
136         val fg = InsetDrawable(overflowBtn?.iconDrawable, overflowIconInset)
137         val drawable = AdaptiveIconDrawable(ColorDrawable(colorAccent), fg)
138         val bubbleBitmapScale = FloatArray(1)
139         bitmap = iconFactory.getBubbleBitmap(drawable, bubbleBitmapScale)
140 
141         // Update dot path
142         dotPath =
143             PathParser.createPathFromPathData(
144                 res.getString(com.android.internal.R.string.config_icon_mask)
145             )
146         val scale = bubbleBitmapScale[0]
147         val radius = BadgedImageView.DEFAULT_PATH_SIZE / 2f
148         val matrix = Matrix()
149         matrix.setScale(
150             scale /* x scale */,
151             scale /* y scale */,
152             radius /* pivot x */,
153             radius /* pivot y */
154         )
155         dotPath.transform(matrix)
156 
157         // Attach BubbleOverflow to BadgedImageView
158         overflowBtn?.setRenderedBubble(this)
159         overflowBtn?.removeDotSuppressionFlag(BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE)
160     }
161 
setVisiblenull162     fun setVisible(visible: Int) {
163         overflowBtn?.visibility = visible
164     }
165 
setShowDotnull166     fun setShowDot(show: Boolean) {
167         showDot = show
168         if (overflowBtn?.visibility == VISIBLE) {
169             overflowBtn?.updateDotVisibility(true /* animate */)
170         }
171     }
172 
173     /** Creates the expanded view for bubbles showing in the stack view. */
createExpandedViewnull174     private fun createExpandedView(): BubbleExpandedView {
175         val view =
176             inflater.inflate(
177                 R.layout.bubble_expanded_view,
178                 null /* root */,
179                 false /* attachToRoot */
180             ) as BubbleExpandedView
181         view.applyThemeAttrs()
182         expandedView = view
183         updateResources()
184         return view
185     }
186 
getExpandedViewnull187     override fun getExpandedView(): BubbleExpandedView? {
188         return expandedView
189     }
190 
191     /** Creates the expanded view for bubbles showing in the bubble bar. */
createBubbleBarExpandedViewnull192     private fun createBubbleBarExpandedView(): BubbleBarExpandedView {
193         val view =
194             inflater.inflate(
195                 R.layout.bubble_bar_expanded_view,
196                 null, /* root */
197                 false /* attachToRoot*/
198             ) as BubbleBarExpandedView
199         view.applyThemeAttrs()
200         bubbleBarExpandedView = view
201         return view
202     }
203 
getBubbleBarExpandedViewnull204     override fun getBubbleBarExpandedView(): BubbleBarExpandedView? = bubbleBarExpandedView
205 
206     override fun getDotColor(): Int {
207         return dotColor
208     }
209 
getAppBadgenull210     override fun getAppBadge(): Bitmap? {
211         return null
212     }
213 
getRawAppBadgenull214     override fun getRawAppBadge(): Bitmap? {
215         return null
216     }
217 
getBubbleIconnull218     override fun getBubbleIcon(): Bitmap {
219         return bitmap
220     }
221 
showDotnull222     override fun showDot(): Boolean {
223         return showDot
224     }
225 
getDotPathnull226     override fun getDotPath(): Path? {
227         return dotPath
228     }
229 
setTaskViewVisibilitynull230     override fun setTaskViewVisibility(visible: Boolean) {
231         // Overflow does not have a TaskView.
232     }
233 
getIconViewnull234     override fun getIconView(): BadgedImageView? {
235         if (overflowBtn == null) {
236             overflowBtn =
237                 inflater.inflate(
238                     R.layout.bubble_overflow_button,
239                     null /* root */,
240                     false /* attachToRoot */
241                 ) as BadgedImageView
242             overflowBtn?.initialize(positioner)
243             overflowBtn?.contentDescription =
244                 context.resources.getString(R.string.bubble_overflow_button_content_description)
245             val bubbleSize = positioner.bubbleSize
246             overflowBtn?.layoutParams = FrameLayout.LayoutParams(bubbleSize, bubbleSize)
247             updateBtnTheme()
248         }
249         return overflowBtn
250     }
251 
getKeynull252     override fun getKey(): String {
253         return KEY
254     }
255 
getTaskIdnull256     override fun getTaskId(): Int {
257         return if (expandedView != null) expandedView!!.taskId else INVALID_TASK_ID
258     }
259 
260     companion object {
261         const val KEY = "Overflow"
262     }
263 }
264