2022-11-14 10:41:21 -08:00
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2023 The Android Open Source Project
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
package com.android.launcher3.taskbar.bubbles;
|
|
|
|
|
|
|
|
|
|
import static android.view.View.INVISIBLE;
|
|
|
|
|
import static android.view.View.VISIBLE;
|
|
|
|
|
|
|
|
|
|
import android.graphics.Rect;
|
|
|
|
|
import android.util.Log;
|
|
|
|
|
import android.view.MotionEvent;
|
|
|
|
|
import android.view.View;
|
|
|
|
|
import android.widget.FrameLayout;
|
|
|
|
|
|
|
|
|
|
import com.android.launcher3.R;
|
|
|
|
|
import com.android.launcher3.anim.AnimatedFloat;
|
|
|
|
|
import com.android.launcher3.taskbar.TaskbarActivityContext;
|
|
|
|
|
import com.android.launcher3.taskbar.TaskbarControllers;
|
|
|
|
|
import com.android.launcher3.util.MultiPropertyFactory;
|
|
|
|
|
import com.android.launcher3.util.MultiValueAlpha;
|
|
|
|
|
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Objects;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Controller for {@link BubbleBarView}. Manages the visibility of the bubble bar as well as
|
|
|
|
|
* responding to changes in bubble state provided by BubbleBarController.
|
|
|
|
|
*/
|
|
|
|
|
public class BubbleBarViewController {
|
|
|
|
|
|
|
|
|
|
private static final String TAG = BubbleBarViewController.class.getSimpleName();
|
|
|
|
|
|
|
|
|
|
private final TaskbarActivityContext mActivity;
|
|
|
|
|
private final BubbleBarView mBarView;
|
|
|
|
|
private final int mIconSize;
|
|
|
|
|
|
|
|
|
|
// Initialized in init.
|
2023-04-14 11:36:30 -07:00
|
|
|
private BubbleStashController mBubbleStashController;
|
2023-04-14 11:36:51 -07:00
|
|
|
private BubbleBarController mBubbleBarController;
|
2022-11-14 10:41:21 -08:00
|
|
|
private View.OnClickListener mBubbleClickListener;
|
|
|
|
|
private View.OnClickListener mBubbleBarClickListener;
|
|
|
|
|
|
2023-04-14 11:36:30 -07:00
|
|
|
// These are exposed to {@link BubbleStashController} to animate for stashing/un-stashing
|
2022-11-14 10:41:21 -08:00
|
|
|
private final MultiValueAlpha mBubbleBarAlpha;
|
|
|
|
|
private final AnimatedFloat mBubbleBarScale = new AnimatedFloat(this::updateScale);
|
|
|
|
|
private final AnimatedFloat mBubbleBarTranslationY = new AnimatedFloat(
|
|
|
|
|
this::updateTranslationY);
|
|
|
|
|
|
|
|
|
|
// Modified when swipe up is happening on the bubble bar or task bar.
|
|
|
|
|
private float mBubbleBarSwipeUpTranslationY;
|
|
|
|
|
|
|
|
|
|
// Whether the bar is hidden for a sysui state.
|
|
|
|
|
private boolean mHiddenForSysui;
|
|
|
|
|
// Whether the bar is hidden because there are no bubbles.
|
|
|
|
|
private boolean mHiddenForNoBubbles;
|
|
|
|
|
|
|
|
|
|
public BubbleBarViewController(TaskbarActivityContext activity, BubbleBarView barView) {
|
|
|
|
|
mActivity = activity;
|
|
|
|
|
mBarView = barView;
|
|
|
|
|
mBubbleBarAlpha = new MultiValueAlpha(mBarView, 1 /* num alpha channels */);
|
|
|
|
|
mBubbleBarAlpha.setUpdateVisibility(true);
|
|
|
|
|
mIconSize = activity.getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) {
|
2023-04-14 11:36:30 -07:00
|
|
|
mBubbleStashController = bubbleControllers.bubbleStashController;
|
2023-04-14 11:36:51 -07:00
|
|
|
mBubbleBarController = bubbleControllers.bubbleBarController;
|
2023-04-14 11:36:30 -07:00
|
|
|
|
2022-11-14 10:41:21 -08:00
|
|
|
mActivity.addOnDeviceProfileChangeListener(dp ->
|
|
|
|
|
mBarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarHeight
|
|
|
|
|
);
|
|
|
|
|
mBarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarHeight;
|
|
|
|
|
mBubbleBarScale.updateValue(1f);
|
|
|
|
|
mBubbleClickListener = v -> onBubbleClicked(v);
|
|
|
|
|
mBubbleBarClickListener = v -> setExpanded(true);
|
|
|
|
|
mBarView.setOnClickListener(mBubbleBarClickListener);
|
|
|
|
|
// TODO: when barView layout changes tell taskbarInsetsController the insets have changed.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void onBubbleClicked(View v) {
|
|
|
|
|
BubbleBarBubble bubble = ((BubbleView) v).getBubble();
|
|
|
|
|
if (bubble == null) {
|
|
|
|
|
Log.e(TAG, "bubble click listener, bubble was null");
|
|
|
|
|
}
|
2023-04-14 11:36:51 -07:00
|
|
|
final String currentlySelected = mBubbleBarController.getSelectedBubbleKey();
|
|
|
|
|
if (mBarView.isExpanded() && Objects.equals(bubble.getKey(), currentlySelected)) {
|
|
|
|
|
// Tapping the currently selected bubble while expanded collapses the view.
|
|
|
|
|
setExpanded(false);
|
|
|
|
|
mBubbleStashController.stashBubbleBar();
|
|
|
|
|
} else {
|
|
|
|
|
mBubbleBarController.setSelectedBubble(bubble);
|
|
|
|
|
// TODO: Tell SysUi to show the expanded view for this bubble.
|
|
|
|
|
}
|
2022-11-14 10:41:21 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// The below animators are exposed to BubbleStashController so it can manage the stashing
|
|
|
|
|
// animation.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
public MultiPropertyFactory<View> getBubbleBarAlpha() {
|
|
|
|
|
return mBubbleBarAlpha;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public AnimatedFloat getBubbleBarScale() {
|
|
|
|
|
return mBubbleBarScale;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public AnimatedFloat getBubbleBarTranslationY() {
|
|
|
|
|
return mBubbleBarTranslationY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Whether the bubble bar is visible or not.
|
|
|
|
|
*/
|
|
|
|
|
public boolean isBubbleBarVisible() {
|
|
|
|
|
return mBarView.getVisibility() == VISIBLE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The bounds of the bubble bar.
|
|
|
|
|
*/
|
|
|
|
|
public Rect getBubbleBarBounds() {
|
|
|
|
|
return mBarView.getBubbleBarBounds();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* When the bubble bar is not stashed, it can be collapsed (the icons are in a stack) or
|
|
|
|
|
* expanded (the icons are in a row). This indicates whether the bubble bar is expanded.
|
|
|
|
|
*/
|
|
|
|
|
public boolean isExpanded() {
|
|
|
|
|
return mBarView.isExpanded();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Whether the motion event is within the bounds of the bubble bar.
|
|
|
|
|
*/
|
|
|
|
|
public boolean isEventOverAnyItem(MotionEvent ev) {
|
|
|
|
|
return mBarView.isEventOverAnyItem(ev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Visibility of the bubble bar
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns whether the bubble bar is hidden because there are no bubbles.
|
|
|
|
|
*/
|
|
|
|
|
public boolean isHiddenForNoBubbles() {
|
|
|
|
|
return mHiddenForNoBubbles;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sets whether the bubble bar should be hidden because there are no bubbles.
|
|
|
|
|
*/
|
|
|
|
|
public void setHiddenForBubbles(boolean hidden) {
|
|
|
|
|
if (mHiddenForNoBubbles != hidden) {
|
|
|
|
|
mHiddenForNoBubbles = hidden;
|
|
|
|
|
updateVisibilityForStateChange();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sets whether the bubble bar should be hidden due to SysUI state (e.g. on lockscreen).
|
|
|
|
|
*/
|
|
|
|
|
public void setHiddenForSysui(boolean hidden) {
|
|
|
|
|
if (mHiddenForSysui != hidden) {
|
|
|
|
|
mHiddenForSysui = hidden;
|
|
|
|
|
updateVisibilityForStateChange();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: (b/273592694) animate it
|
|
|
|
|
private void updateVisibilityForStateChange() {
|
2023-04-14 11:36:30 -07:00
|
|
|
if (!mHiddenForSysui && !mBubbleStashController.isStashed() && !mHiddenForNoBubbles) {
|
2022-11-14 10:41:21 -08:00
|
|
|
mBarView.setVisibility(VISIBLE);
|
|
|
|
|
} else {
|
|
|
|
|
mBarView.setVisibility(INVISIBLE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Modifying view related properties.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sets the translation of the bubble bar during the swipe up gesture.
|
|
|
|
|
*/
|
|
|
|
|
public void setTranslationYForSwipe(float transY) {
|
|
|
|
|
mBubbleBarSwipeUpTranslationY = transY;
|
|
|
|
|
updateTranslationY();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void updateTranslationY() {
|
|
|
|
|
mBarView.setTranslationY(mBubbleBarTranslationY.value
|
|
|
|
|
+ mBubbleBarSwipeUpTranslationY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Applies scale properties for the entire bubble bar.
|
|
|
|
|
*/
|
|
|
|
|
private void updateScale() {
|
|
|
|
|
float scale = mBubbleBarScale.value;
|
|
|
|
|
mBarView.setScaleX(scale);
|
|
|
|
|
mBarView.setScaleY(scale);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Manipulating the specific bubble views in the bar
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Removes the provided bubble from the bubble bar.
|
|
|
|
|
*/
|
|
|
|
|
public void removeBubble(BubbleBarBubble b) {
|
|
|
|
|
if (b != null) {
|
|
|
|
|
mBarView.removeView(b.getView());
|
|
|
|
|
} else {
|
|
|
|
|
Log.w(TAG, "removeBubble, bubble was null!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Adds the provided bubble to the bubble bar.
|
|
|
|
|
*/
|
|
|
|
|
public void addBubble(BubbleBarBubble b) {
|
|
|
|
|
if (b != null) {
|
|
|
|
|
mBarView.addView(b.getView(), 0, new FrameLayout.LayoutParams(mIconSize, mIconSize));
|
|
|
|
|
b.getView().setOnClickListener(mBubbleClickListener);
|
|
|
|
|
} else {
|
|
|
|
|
Log.w(TAG, "addBubble, bubble was null!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Reorders the bubbles based on the provided list.
|
|
|
|
|
*/
|
|
|
|
|
public void reorderBubbles(List<BubbleBarBubble> newOrder) {
|
|
|
|
|
List<BubbleView> viewList = newOrder.stream().filter(Objects::nonNull)
|
|
|
|
|
.map(BubbleBarBubble::getView).toList();
|
|
|
|
|
mBarView.reorder(viewList);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Updates the selected bubble.
|
|
|
|
|
*/
|
|
|
|
|
public void updateSelectedBubble(BubbleBarBubble newlySelected) {
|
|
|
|
|
mBarView.setSelectedBubble(newlySelected.getView());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sets whether the bubble bar should be expanded (not unstashed, but have the contents
|
|
|
|
|
* within it expanded). This method notifies SystemUI that the bubble bar is expanded and
|
|
|
|
|
* showing a selected bubble. This method should ONLY be called from UI events originating
|
|
|
|
|
* from Launcher.
|
|
|
|
|
*/
|
|
|
|
|
public void setExpanded(boolean isExpanded) {
|
|
|
|
|
if (isExpanded != mBarView.isExpanded()) {
|
|
|
|
|
mBarView.setExpanded(isExpanded);
|
|
|
|
|
if (!isExpanded) {
|
|
|
|
|
// TODO: Tell SysUi to collapse the bubble
|
|
|
|
|
} else {
|
2023-04-14 11:36:51 -07:00
|
|
|
final String selectedKey = mBubbleBarController.getSelectedBubbleKey();
|
|
|
|
|
if (selectedKey != null) {
|
|
|
|
|
// TODO: Tell SysUi to show the bubble
|
|
|
|
|
} else {
|
|
|
|
|
Log.w(TAG, "trying to expand bubbles when there isn't one selected");
|
|
|
|
|
}
|
2022-11-14 10:41:21 -08:00
|
|
|
// TODO: Tell taskbar stash controller to stash without bubbles following
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sets whether the bubble bar should be expanded. This method is used in response to UI events
|
|
|
|
|
* from SystemUI.
|
|
|
|
|
*/
|
|
|
|
|
public void setExpandedFromSysui(boolean isExpanded) {
|
2023-04-14 11:36:30 -07:00
|
|
|
if (!isExpanded) {
|
|
|
|
|
mBubbleStashController.stashBubbleBar();
|
|
|
|
|
} else {
|
|
|
|
|
mBubbleStashController.showBubbleBar(true /* expand the bubbles */);
|
|
|
|
|
}
|
2022-11-14 10:41:21 -08:00
|
|
|
}
|
|
|
|
|
}
|