• 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.launcher3.taskbar.bubbles;
17 
18 import static android.view.View.INVISIBLE;
19 import static android.view.View.VISIBLE;
20 
21 import android.graphics.Rect;
22 import android.util.Log;
23 import android.view.MotionEvent;
24 import android.view.View;
25 import android.widget.FrameLayout;
26 
27 import androidx.annotation.NonNull;
28 
29 import com.android.launcher3.R;
30 import com.android.launcher3.anim.AnimatedFloat;
31 import com.android.launcher3.taskbar.TaskbarActivityContext;
32 import com.android.launcher3.taskbar.TaskbarControllers;
33 import com.android.launcher3.taskbar.TaskbarInsetsController;
34 import com.android.launcher3.taskbar.TaskbarStashController;
35 import com.android.launcher3.util.MultiPropertyFactory;
36 import com.android.launcher3.util.MultiValueAlpha;
37 import com.android.quickstep.SystemUiProxy;
38 
39 import java.util.List;
40 import java.util.Objects;
41 import java.util.function.Consumer;
42 
43 /**
44  * Controller for {@link BubbleBarView}. Manages the visibility of the bubble bar as well as
45  * responding to changes in bubble state provided by BubbleBarController.
46  */
47 public class BubbleBarViewController {
48 
49     private static final String TAG = BubbleBarViewController.class.getSimpleName();
50 
51     private final SystemUiProxy mSystemUiProxy;
52     private final TaskbarActivityContext mActivity;
53     private final BubbleBarView mBarView;
54     private final int mIconSize;
55 
56     // Initialized in init.
57     private BubbleStashController mBubbleStashController;
58     private BubbleBarController mBubbleBarController;
59     private BubbleDragController mBubbleDragController;
60     private TaskbarStashController mTaskbarStashController;
61     private TaskbarInsetsController mTaskbarInsetsController;
62     private View.OnClickListener mBubbleClickListener;
63     private View.OnClickListener mBubbleBarClickListener;
64 
65     // These are exposed to {@link BubbleStashController} to animate for stashing/un-stashing
66     private final MultiValueAlpha mBubbleBarAlpha;
67     private final AnimatedFloat mBubbleBarScale = new AnimatedFloat(this::updateScale);
68     private final AnimatedFloat mBubbleBarTranslationY = new AnimatedFloat(
69             this::updateTranslationY);
70 
71     // Modified when swipe up is happening on the bubble bar or task bar.
72     private float mBubbleBarSwipeUpTranslationY;
73 
74     // Whether the bar is hidden for a sysui state.
75     private boolean mHiddenForSysui;
76     // Whether the bar is hidden because there are no bubbles.
77     private boolean mHiddenForNoBubbles;
78 
BubbleBarViewController(TaskbarActivityContext activity, BubbleBarView barView)79     public BubbleBarViewController(TaskbarActivityContext activity, BubbleBarView barView) {
80         mActivity = activity;
81         mBarView = barView;
82         mSystemUiProxy = SystemUiProxy.INSTANCE.get(mActivity);
83         mBubbleBarAlpha = new MultiValueAlpha(mBarView, 1 /* num alpha channels */);
84         mBubbleBarAlpha.setUpdateVisibility(true);
85         mIconSize = activity.getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_size);
86     }
87 
init(TaskbarControllers controllers, BubbleControllers bubbleControllers)88     public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) {
89         mBubbleStashController = bubbleControllers.bubbleStashController;
90         mBubbleBarController = bubbleControllers.bubbleBarController;
91         mBubbleDragController = bubbleControllers.bubbleDragController;
92         mTaskbarStashController = controllers.taskbarStashController;
93         mTaskbarInsetsController = controllers.taskbarInsetsController;
94 
95         mActivity.addOnDeviceProfileChangeListener(dp ->
96                 mBarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarHeight
97         );
98         mBarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarHeight;
99         mBubbleBarScale.updateValue(1f);
100         mBubbleClickListener = v -> onBubbleClicked(v);
101         mBubbleBarClickListener = v -> setExpanded(true);
102         mBubbleDragController.setupBubbleBarView(mBarView);
103         mBarView.setOnClickListener(mBubbleBarClickListener);
104         mBarView.addOnLayoutChangeListener((view, i, i1, i2, i3, i4, i5, i6, i7) ->
105                 mTaskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
106         );
107     }
108 
onBubbleClicked(View v)109     private void onBubbleClicked(View v) {
110         BubbleBarItem bubble = ((BubbleView) v).getBubble();
111         if (bubble == null) {
112             Log.e(TAG, "bubble click listener, bubble was null");
113         }
114         final String currentlySelected = mBubbleBarController.getSelectedBubbleKey();
115         if (mBarView.isExpanded() && Objects.equals(bubble.getKey(), currentlySelected)) {
116             // Tapping the currently selected bubble while expanded collapses the view.
117             setExpanded(false);
118             mBubbleStashController.stashBubbleBar();
119         } else {
120             mBubbleBarController.showAndSelectBubble(bubble);
121         }
122     }
123 
124     //
125     // The below animators are exposed to BubbleStashController so it can manage the stashing
126     // animation.
127     //
128 
getBubbleBarAlpha()129     public MultiPropertyFactory<View> getBubbleBarAlpha() {
130         return mBubbleBarAlpha;
131     }
132 
getBubbleBarScale()133     public AnimatedFloat getBubbleBarScale() {
134         return mBubbleBarScale;
135     }
136 
getBubbleBarTranslationY()137     public AnimatedFloat getBubbleBarTranslationY() {
138         return mBubbleBarTranslationY;
139     }
140 
141     /**
142      * Whether the bubble bar is visible or not.
143      */
isBubbleBarVisible()144     public boolean isBubbleBarVisible() {
145         return mBarView.getVisibility() == VISIBLE;
146     }
147 
148     /** Whether the bubble bar has bubbles. */
hasBubbles()149     public boolean hasBubbles() {
150         return mBubbleBarController.getSelectedBubbleKey() != null;
151     }
152 
153     /**
154      * The bounds of the bubble bar.
155      */
getBubbleBarBounds()156     public Rect getBubbleBarBounds() {
157         return mBarView.getBubbleBarBounds();
158     }
159 
160     /** The horizontal margin of the bubble bar from the edge of the screen. */
getHorizontalMargin()161     public int getHorizontalMargin() {
162         return mBarView.getHorizontalMargin();
163     }
164 
165     /**
166      * When the bubble bar is not stashed, it can be collapsed (the icons are in a stack) or
167      * expanded (the icons are in a row). This indicates whether the bubble bar is expanded.
168      */
isExpanded()169     public boolean isExpanded() {
170         return mBarView.isExpanded();
171     }
172 
173     /**
174      * Whether the motion event is within the bounds of the bubble bar.
175      */
isEventOverAnyItem(MotionEvent ev)176     public boolean isEventOverAnyItem(MotionEvent ev) {
177         return mBarView.isEventOverAnyItem(ev);
178     }
179 
180     //
181     // Visibility of the bubble bar
182     //
183 
184     /**
185      * Returns whether the bubble bar is hidden because there are no bubbles.
186      */
isHiddenForNoBubbles()187     public boolean isHiddenForNoBubbles() {
188         return mHiddenForNoBubbles;
189     }
190 
191     /**
192      * Sets whether the bubble bar should be hidden because there are no bubbles.
193      */
setHiddenForBubbles(boolean hidden)194     public void setHiddenForBubbles(boolean hidden) {
195         if (mHiddenForNoBubbles != hidden) {
196             mHiddenForNoBubbles = hidden;
197             updateVisibilityForStateChange();
198         }
199     }
200 
201     /** Sets a callback that updates the selected bubble after the bubble bar collapses. */
setUpdateSelectedBubbleAfterCollapse( Consumer<String> updateSelectedBubbleAfterCollapse)202     public void setUpdateSelectedBubbleAfterCollapse(
203             Consumer<String> updateSelectedBubbleAfterCollapse) {
204         mBarView.setUpdateSelectedBubbleAfterCollapse(updateSelectedBubbleAfterCollapse);
205     }
206 
207     /**
208      * Sets whether the bubble bar should be hidden due to SysUI state (e.g. on lockscreen).
209      */
setHiddenForSysui(boolean hidden)210     public void setHiddenForSysui(boolean hidden) {
211         if (mHiddenForSysui != hidden) {
212             mHiddenForSysui = hidden;
213             updateVisibilityForStateChange();
214         }
215     }
216 
217     // TODO: (b/273592694) animate it
updateVisibilityForStateChange()218     private void updateVisibilityForStateChange() {
219         if (!mHiddenForSysui && !mHiddenForNoBubbles) {
220             mBarView.setVisibility(VISIBLE);
221         } else {
222             mBarView.setVisibility(INVISIBLE);
223             mBarView.setAlpha(0);
224             mBarView.setExpanded(false);
225         }
226     }
227 
228     //
229     // Modifying view related properties.
230     //
231 
232     /**
233      * Sets the translation of the bubble bar during the swipe up gesture.
234      */
setTranslationYForSwipe(float transY)235     public void setTranslationYForSwipe(float transY) {
236         mBubbleBarSwipeUpTranslationY = transY;
237         updateTranslationY();
238     }
239 
updateTranslationY()240     private void updateTranslationY() {
241         mBarView.setTranslationY(mBubbleBarTranslationY.value
242                 + mBubbleBarSwipeUpTranslationY);
243     }
244 
245     /**
246      * Applies scale properties for the entire bubble bar.
247      */
updateScale()248     private void updateScale() {
249         float scale = mBubbleBarScale.value;
250         mBarView.setScaleX(scale);
251         mBarView.setScaleY(scale);
252     }
253 
254     //
255     // Manipulating the specific bubble views in the bar
256     //
257 
258     /**
259      * Removes the provided bubble from the bubble bar.
260      */
removeBubble(BubbleBarItem b)261     public void removeBubble(BubbleBarItem b) {
262         if (b != null) {
263             mBarView.removeView(b.getView());
264         } else {
265             Log.w(TAG, "removeBubble, bubble was null!");
266         }
267     }
268 
269     /**
270      * Adds the provided bubble to the bubble bar.
271      */
addBubble(BubbleBarItem b)272     public void addBubble(BubbleBarItem b) {
273         if (b != null) {
274             mBarView.addView(b.getView(), 0, new FrameLayout.LayoutParams(mIconSize, mIconSize));
275             b.getView().setOnClickListener(mBubbleClickListener);
276             mBubbleDragController.setupBubbleView(b.getView());
277         } else {
278             Log.w(TAG, "addBubble, bubble was null!");
279         }
280     }
281 
282     /**
283      * Reorders the bubbles based on the provided list.
284      */
reorderBubbles(List<BubbleBarBubble> newOrder)285     public void reorderBubbles(List<BubbleBarBubble> newOrder) {
286         List<BubbleView> viewList = newOrder.stream().filter(Objects::nonNull)
287                 .map(BubbleBarBubble::getView).toList();
288         mBarView.reorder(viewList);
289     }
290 
291     /**
292      * Updates the selected bubble.
293      */
updateSelectedBubble(BubbleBarItem newlySelected)294     public void updateSelectedBubble(BubbleBarItem newlySelected) {
295         mBarView.setSelectedBubble(newlySelected.getView());
296     }
297 
298     /**
299      * Sets whether the bubble bar should be expanded (not unstashed, but have the contents
300      * within it expanded). This method notifies SystemUI that the bubble bar is expanded and
301      * showing a selected bubble. This method should ONLY be called from UI events originating
302      * from Launcher.
303      */
setExpanded(boolean isExpanded)304     public void setExpanded(boolean isExpanded) {
305         if (isExpanded != mBarView.isExpanded()) {
306             mBarView.setExpanded(isExpanded);
307             if (!isExpanded) {
308                 mSystemUiProxy.collapseBubbles();
309             } else {
310                 mBubbleBarController.showSelectedBubble();
311                 mTaskbarStashController.updateAndAnimateTransientTaskbar(true /* stash */,
312                         false /* shouldBubblesFollow */);
313             }
314         }
315     }
316 
317     /**
318      * Sets whether the bubble bar should be expanded. This method is used in response to UI events
319      * from SystemUI.
320      */
setExpandedFromSysui(boolean isExpanded)321     public void setExpandedFromSysui(boolean isExpanded) {
322         if (!isExpanded) {
323             mBubbleStashController.stashBubbleBar();
324         } else {
325             mBubbleStashController.showBubbleBar(true /* expand the bubbles */);
326         }
327     }
328 
329     /**
330      * Updates the dragged bubble view in the bubble bar view, and notifies SystemUI
331      * that a bubble is being dragged to dismiss.
332      * @param bubbleView dragged bubble view
333      */
onDragStart(@onNull BubbleView bubbleView)334     public void onDragStart(@NonNull BubbleView bubbleView) {
335         if (bubbleView.getBubble() == null) return;
336         mSystemUiProxy.onBubbleDrag(bubbleView.getBubble().getKey(), /* isBeingDragged = */ true);
337         mBarView.setDraggedBubble(bubbleView);
338     }
339 
340     /**
341      * Notifies SystemUI to expand the selected bubble when the bubble is released.
342      * @param bubbleView dragged bubble view
343      */
onDragRelease(@onNull BubbleView bubbleView)344     public void onDragRelease(@NonNull BubbleView bubbleView) {
345         if (bubbleView.getBubble() == null) return;
346         mSystemUiProxy.onBubbleDrag(bubbleView.getBubble().getKey(), /* isBeingDragged = */ false);
347     }
348 
349     /**
350      * Removes the dragged bubble view in the bubble bar view
351      */
onDragEnd()352     public void onDragEnd() {
353         mBarView.setDraggedBubble(null);
354     }
355 
356     /**
357      * Called when bubble was dragged into the dismiss target. Notifies System
358      * @param bubble dismissed bubble item
359      */
onDismissBubbleWhileDragging(@onNull BubbleBarItem bubble)360     public void onDismissBubbleWhileDragging(@NonNull BubbleBarItem bubble) {
361         mSystemUiProxy.removeBubble(bubble.getKey());
362     }
363 
364     /**
365      * Called when bubble stack was dragged into the dismiss target
366      */
onDismissAllBubblesWhileDragging()367     public void onDismissAllBubblesWhileDragging() {
368         mSystemUiProxy.removeAllBubbles();
369     }
370 }
371