• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 package com.android.wm.shell.bubbles.bar;
17 
18 import android.animation.Animator;
19 import android.animation.AnimatorListenerAdapter;
20 import android.animation.ArgbEvaluator;
21 import android.animation.ObjectAnimator;
22 import android.annotation.Nullable;
23 import android.content.Context;
24 import android.graphics.Canvas;
25 import android.graphics.Paint;
26 import android.util.AttributeSet;
27 import android.view.View;
28 
29 import androidx.annotation.ColorInt;
30 import androidx.annotation.VisibleForTesting;
31 import androidx.core.animation.IntProperty;
32 import androidx.core.content.ContextCompat;
33 
34 import com.android.wm.shell.R;
35 
36 /**
37  * Handle view to show at the top of a bubble bar expanded view.
38  */
39 public class BubbleBarHandleView extends View {
40     private static final long COLOR_CHANGE_DURATION = 120;
41 
42     /** Custom property to set handle color. */
43     private static final IntProperty<BubbleBarHandleView> HANDLE_COLOR = new IntProperty<>(
44             "handleColor") {
45         @Override
46         public void setValue(BubbleBarHandleView bubbleBarHandleView, int color) {
47             bubbleBarHandleView.setHandleColor(color);
48         }
49 
50         @Override
51         public Integer get(BubbleBarHandleView bubbleBarHandleView) {
52             return bubbleBarHandleView.getHandleColor();
53         }
54     };
55 
56     @VisibleForTesting
57     final Paint mHandlePaint = new Paint();
58     private final @ColorInt int mHandleLightColor;
59     private final @ColorInt int mHandleDarkColor;
60     private final ArgbEvaluator mArgbEvaluator = ArgbEvaluator.getInstance();
61     private final float mHandleHeight;
62     private final float mHandleWidth;
63     private float mCurrentHandleHeight;
64     private float mCurrentHandleWidth;
65     @Nullable
66     private ObjectAnimator mColorChangeAnim;
67     private @ColorInt int mRegionSamplerColor;
68     private boolean mHasSampledColor;
69 
BubbleBarHandleView(Context context)70     public BubbleBarHandleView(Context context) {
71         this(context, null /* attrs */);
72     }
73 
BubbleBarHandleView(Context context, AttributeSet attrs)74     public BubbleBarHandleView(Context context, AttributeSet attrs) {
75         this(context, attrs, 0 /* defStyleAttr */);
76     }
77 
BubbleBarHandleView(Context context, AttributeSet attrs, int defStyleAttr)78     public BubbleBarHandleView(Context context, AttributeSet attrs, int defStyleAttr) {
79         this(context, attrs, defStyleAttr, 0 /* defStyleRes */);
80     }
81 
BubbleBarHandleView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)82     public BubbleBarHandleView(Context context, AttributeSet attrs, int defStyleAttr,
83             int defStyleRes) {
84         super(context, attrs, defStyleAttr, defStyleRes);
85         mHandlePaint.setFlags(Paint.ANTI_ALIAS_FLAG);
86         mHandlePaint.setStyle(Paint.Style.FILL);
87         mHandlePaint.setColor(0);
88         mHandleHeight = getResources().getDimensionPixelSize(
89                 R.dimen.bubble_bar_expanded_view_handle_height);
90         mHandleWidth = getResources().getDimensionPixelSize(
91                 R.dimen.bubble_bar_expanded_view_caption_width);
92         mHandleLightColor = ContextCompat.getColor(getContext(),
93                 R.color.bubble_bar_expanded_view_handle_light);
94         mHandleDarkColor = ContextCompat.getColor(getContext(),
95                 R.color.bubble_bar_expanded_view_handle_dark);
96         mCurrentHandleHeight = mHandleHeight;
97         mCurrentHandleWidth = mHandleWidth;
98         setContentDescription(getResources().getString(R.string.handle_text));
99     }
100 
setHandleColor(int color)101     private void setHandleColor(int color) {
102         mHandlePaint.setColor(color);
103         invalidate();
104     }
105 
106     /**
107      * Get current color value for the handle
108      */
109     @ColorInt
getHandleColor()110     public int getHandleColor() {
111         return mHandlePaint.getColor();
112     }
113 
114     @Override
onDraw(Canvas canvas)115     protected void onDraw(Canvas canvas) {
116         super.onDraw(canvas);
117         float handleLeft = (getWidth() - mCurrentHandleWidth) / 2;
118         float handleRight = handleLeft + mCurrentHandleWidth;
119         float handleCenterY = (float) getHeight() / 2;
120         float handleTop = (int) (handleCenterY - mCurrentHandleHeight / 2);
121         float handleBottom = handleTop + mCurrentHandleHeight;
122         float cornerRadius = mCurrentHandleHeight / 2;
123         canvas.drawRoundRect(handleLeft, handleTop, handleRight, handleBottom, cornerRadius,
124                 cornerRadius, mHandlePaint);
125     }
126 
127     /** Sets handle width, height and color. Does not change the layout properties */
setHandleProperties(float width, float height, int color)128     private void setHandleProperties(float width, float height, int color) {
129         mCurrentHandleHeight = height;
130         mCurrentHandleWidth = width;
131         mHandlePaint.setColor(color);
132         invalidate();
133     }
134 
135     /**
136      * Set initial color for the handle. Takes effect if the
137      * {@link #updateHandleColor(boolean, boolean)} has not been called.
138      */
setHandleInitialColor(@olorInt int color)139     public void setHandleInitialColor(@ColorInt int color) {
140         if (!mHasSampledColor) {
141             setHandleColor(color);
142         }
143     }
144 
145     /**
146      * Updates the handle color.
147      *
148      * @param isRegionDark Whether the background behind the handle is dark, and thus the handle
149      *                     should be light (and vice versa).
150      * @param animated     Whether to animate the change, or apply it immediately.
151      */
updateHandleColor(boolean isRegionDark, boolean animated)152     public void updateHandleColor(boolean isRegionDark, boolean animated) {
153         int newColor = isRegionDark ? mHandleLightColor : mHandleDarkColor;
154         if (newColor == mRegionSamplerColor) {
155             return;
156         }
157         mHasSampledColor = true;
158         mRegionSamplerColor = newColor;
159         if (mColorChangeAnim != null) {
160             mColorChangeAnim.cancel();
161         }
162         if (animated) {
163             mColorChangeAnim = ObjectAnimator.ofArgb(this, HANDLE_COLOR, newColor);
164             mColorChangeAnim.addListener(new AnimatorListenerAdapter() {
165                 @Override
166                 public void onAnimationEnd(Animator animation) {
167                     mColorChangeAnim = null;
168                 }
169             });
170             mColorChangeAnim.setDuration(COLOR_CHANGE_DURATION);
171             mColorChangeAnim.start();
172         } else {
173             setHandleColor(newColor);
174         }
175     }
176 
177     /** Returns handle padding top. */
getHandlePaddingTop()178     public int getHandlePaddingTop() {
179         return (getHeight() - getResources().getDimensionPixelSize(
180                 R.dimen.bubble_bar_expanded_view_handle_height)) / 2;
181     }
182 
183     /** Animates handle for the bubble menu. */
animateHandleForMenu(float progress, float widthDelta, float heightDelta, int menuColor)184     public void animateHandleForMenu(float progress, float widthDelta, float heightDelta,
185             int menuColor) {
186         float currentWidth = mHandleWidth + widthDelta * progress;
187         float currentHeight = mHandleHeight + heightDelta * progress;
188         int color = (int) mArgbEvaluator.evaluate(progress, mRegionSamplerColor, menuColor);
189         setHandleProperties(currentWidth, currentHeight, color);
190         setTranslationY(heightDelta * progress / 2);
191     }
192 
193     /** Restores all the properties that were animated to the default values. */
restoreAnimationDefaults()194     public void restoreAnimationDefaults() {
195         setHandleProperties(mHandleWidth, mHandleHeight, mRegionSamplerColor);
196         setTranslationY(0);
197     }
198 
199     /** Returns the handle height. */
getHandleHeight()200     public int getHandleHeight() {
201         return (int) mHandleHeight;
202     }
203 
204     /** Returns the handle width. */
getHandleWidth()205     public int getHandleWidth() {
206         return (int) mHandleWidth;
207     }
208 }
209