Merge "Bubble bar drag to dismiss" into udc-qpr-dev

This commit is contained in:
Ivan Tkachenko
2023-07-12 17:13:50 +00:00
committed by Android (Google) Code Review
11 changed files with 708 additions and 7 deletions
@@ -90,6 +90,8 @@ import com.android.launcher3.taskbar.bubbles.BubbleBarController;
import com.android.launcher3.taskbar.bubbles.BubbleBarView;
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController;
import com.android.launcher3.taskbar.bubbles.BubbleControllers;
import com.android.launcher3.taskbar.bubbles.BubbleDismissController;
import com.android.launcher3.taskbar.bubbles.BubbleDragController;
import com.android.launcher3.taskbar.bubbles.BubbleStashController;
import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
@@ -216,7 +218,9 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
new BubbleBarController(this, bubbleBarView),
new BubbleBarViewController(this, bubbleBarView),
new BubbleStashController(this),
new BubbleStashedHandleViewController(this, bubbleHandleView)));
new BubbleStashedHandleViewController(this, bubbleHandleView),
new BubbleDragController(this),
new BubbleDismissController(this, mDragLayer)));
}
// Construct controllers.
@@ -95,6 +95,8 @@ public class BubbleBarView extends FrameLayout {
private View.OnClickListener mOnClickListener;
private final Rect mTempRect = new Rect();
private float mRelativePivotX = 1f;
private float mRelativePivotY = 1f;
// An animator that represents the expansion state of the bubble bar, where 0 corresponds to the
// collapsed state and 1 to the fully expanded state.
@@ -109,6 +111,9 @@ public class BubbleBarView extends FrameLayout {
@Nullable
private Consumer<String> mUpdateSelectedBubbleAfterCollapse;
@Nullable
private BubbleView mDraggedBubbleView;
public BubbleBarView(Context context) {
this(context, null);
}
@@ -181,9 +186,10 @@ public class BubbleBarView extends FrameLayout {
mBubbleBarBounds.right = right;
mBubbleBarBounds.bottom = bottom;
// The bubble bar handle is aligned to the bottom edge of the screen so scale towards that.
setPivotX(getWidth());
setPivotY(getHeight());
// The bubble bar handle is aligned according to the relative pivot,
// by default it's aligned to the bottom edge of the screen so scale towards that
setPivotX(mRelativePivotX * getWidth());
setPivotY(mRelativePivotY * getHeight());
// Position the views
updateChildrenRenderNodeProperties();
@@ -198,6 +204,32 @@ public class BubbleBarView extends FrameLayout {
return mBubbleBarBounds;
}
/**
* Set bubble bar relative pivot value for X and Y, applied as a fraction of view width/height
* respectively. If the value is not in range of 0 to 1 it will be normalized.
* @param x relative X pivot value in range 0..1
* @param y relative Y pivot value in range 0..1
*/
public void setRelativePivot(float x, float y) {
mRelativePivotX = Float.max(Float.min(x, 1), 0);
mRelativePivotY = Float.max(Float.min(y, 1), 0);
requestLayout();
}
/**
* Get current relative pivot for X axis
*/
public float getRelativePivotX() {
return mRelativePivotX;
}
/**
* Get current relative pivot for Y axis
*/
public float getRelativePivotY() {
return mRelativePivotY;
}
// TODO: (b/280605790) animate it
@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
@@ -254,9 +286,9 @@ public class BubbleBarView extends FrameLayout {
// where the bubble will end up when the animation ends
final float targetX = currentWidth - expandedWidth + expandedX;
bv.setTranslationX(widthState * (targetX - collapsedX) + collapsedX);
// if we're fully expanded, set the z level to 0
// if we're fully expanded, set the z level to 0 or to bubble elevation if dragged
if (widthState == 1f) {
bv.setZ(0);
bv.setZ(bv == mDraggedBubbleView ? mBubbleElevation : 0);
}
// When we're expanded, we're not stacked so we're not behind the stack
bv.setBehindStack(false, animate);
@@ -328,6 +360,14 @@ public class BubbleBarView extends FrameLayout {
updateArrowForSelected(/* shouldAnimate= */ true);
}
/**
* Sets the dragged bubble view to correctly apply Z order. Dragged view should appear on top
*/
public void setDraggedBubble(@Nullable BubbleView view) {
mDraggedBubbleView = view;
requestLayout();
}
/**
* Update the arrow position to match the selected bubble.
*
@@ -24,6 +24,8 @@ import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.taskbar.TaskbarActivityContext;
@@ -54,6 +56,7 @@ public class BubbleBarViewController {
// Initialized in init.
private BubbleStashController mBubbleStashController;
private BubbleBarController mBubbleBarController;
private BubbleDragController mBubbleDragController;
private TaskbarStashController mTaskbarStashController;
private TaskbarInsetsController mTaskbarInsetsController;
private View.OnClickListener mBubbleClickListener;
@@ -85,6 +88,7 @@ public class BubbleBarViewController {
public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) {
mBubbleStashController = bubbleControllers.bubbleStashController;
mBubbleBarController = bubbleControllers.bubbleBarController;
mBubbleDragController = bubbleControllers.bubbleDragController;
mTaskbarStashController = controllers.taskbarStashController;
mTaskbarInsetsController = controllers.taskbarInsetsController;
@@ -95,6 +99,7 @@ public class BubbleBarViewController {
mBubbleBarScale.updateValue(1f);
mBubbleClickListener = v -> onBubbleClicked(v);
mBubbleBarClickListener = v -> setExpanded(true);
mBubbleDragController.setupBubbleBarView(mBarView);
mBarView.setOnClickListener(mBubbleBarClickListener);
mBarView.addOnLayoutChangeListener((view, i, i1, i2, i3, i4, i5, i6, i7) ->
mTaskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
@@ -256,6 +261,7 @@ public class BubbleBarViewController {
if (b != null) {
mBarView.addView(b.getView(), 0, new FrameLayout.LayoutParams(mIconSize, mIconSize));
b.getView().setOnClickListener(mBubbleClickListener);
mBubbleDragController.setupBubbleView(b.getView());
} else {
Log.w(TAG, "addBubble, bubble was null!");
}
@@ -307,4 +313,41 @@ public class BubbleBarViewController {
mBubbleStashController.showBubbleBar(true /* expand the bubbles */);
}
}
/**
* Updates the dragged bubble view in the bubble bar view, and notifies SystemUI
* to collapse the selected bubble while it's dragged.
* @param bubbleView dragged bubble view
*/
public void onDragStart(@NonNull BubbleView bubbleView) {
mBarView.setDraggedBubble(bubbleView);
if (bubbleView.getBubble() == null) return;
mSystemUiProxy.collapseWhileDragging(bubbleView.getBubble().getKey(), true /* collapse */);
}
/**
* Removes the dragged bubble view in the bubble bar view, and notifies SystemUI
* to expand the selected bubble when dragging finished.
* @param bubbleView dragged bubble view
*/
public void onDragEnd(@NonNull BubbleView bubbleView) {
mBarView.setDraggedBubble(null);
if (bubbleView.getBubble() == null) return;
mSystemUiProxy.collapseWhileDragging(bubbleView.getBubble().getKey(), false /* collapse */);
}
/**
* Called when bubble was dragged into the dismiss target. Notifies System
* @param bubble dismissed bubble item
*/
public void onDismissBubbleWhileDragging(@NonNull BubbleBarItem bubble) {
mSystemUiProxy.removeBubble(bubble.getKey());
}
/**
* Called when bubble stack was dragged into the dismiss target
*/
public void onDismissAllBubblesWhileDragging() {
mSystemUiProxy.removeAllBubbles();
}
}
@@ -27,6 +27,8 @@ public class BubbleControllers {
public final BubbleBarViewController bubbleBarViewController;
public final BubbleStashController bubbleStashController;
public final BubbleStashedHandleViewController bubbleStashedHandleViewController;
public final BubbleDragController bubbleDragController;
public final BubbleDismissController bubbleDismissController;
private final RunnableList mPostInitRunnables = new RunnableList();
@@ -39,11 +41,15 @@ public class BubbleControllers {
BubbleBarController bubbleBarController,
BubbleBarViewController bubbleBarViewController,
BubbleStashController bubbleStashController,
BubbleStashedHandleViewController bubbleStashedHandleViewController) {
BubbleStashedHandleViewController bubbleStashedHandleViewController,
BubbleDragController bubbleDragController,
BubbleDismissController bubbleDismissController) {
this.bubbleBarController = bubbleBarController;
this.bubbleBarViewController = bubbleBarViewController;
this.bubbleStashController = bubbleStashController;
this.bubbleStashedHandleViewController = bubbleStashedHandleViewController;
this.bubbleDragController = bubbleDragController;
this.bubbleDismissController = bubbleDismissController;
}
/**
@@ -56,6 +62,8 @@ public class BubbleControllers {
bubbleBarViewController.init(taskbarControllers, this);
bubbleStashedHandleViewController.init(taskbarControllers, this);
bubbleStashController.init(taskbarControllers, this);
bubbleDragController.init(/* bubbleControllers = */ this);
bubbleDismissController.init(/* bubbleControllers = */ this);
mPostInitRunnables.executeAllAndDestroy();
}
@@ -0,0 +1,276 @@
/*
* 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.ViewGroup.LayoutParams.MATCH_PARENT;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.res.Resources;
import android.os.SystemProperties;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.dynamicanimation.animation.DynamicAnimation;
import com.android.launcher3.R;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarDragLayer;
import com.android.wm.shell.common.bubbles.DismissView;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
/**
* Controls dismiss view presentation for the bubble bar dismiss functionality.
* Provides the dragged view snapping to the target dismiss area and animates it.
* When the dragged bubble/bubble stack is realised inside of the target area, it gets dismissed.
*
* @see BubbleDragController
*/
public class BubbleDismissController {
private static final float FLING_TO_DISMISS_MIN_VELOCITY = 6000f;
public static final boolean ENABLE_FLING_TO_DISMISS_BUBBLE =
SystemProperties.getBoolean("persist.wm.debug.fling_to_dismiss_bubble", true);
private final TaskbarActivityContext mActivity;
private final TaskbarDragLayer mDragLayer;
@Nullable
private BubbleBarViewController mBubbleBarViewController;
// Dismiss view that's attached to drag layer. It consists of the scrim view and the circular
// dismiss view used as a dismiss target.
@Nullable
private DismissView mDismissView;
// The currently magnetized object, which is being dragged and will be attracted to the magnetic
// dismiss target. This is either the stack itself, or an individual bubble.
@Nullable
private MagnetizedObject<View> mMagnetizedObject;
// The MagneticTarget instance for our circular dismiss view. This is added to the
// MagnetizedObject instances for the stack and any dragged-out bubbles.
@Nullable
private MagnetizedObject.MagneticTarget mMagneticTarget;
@Nullable
private ValueAnimator mDismissAnimator;
public BubbleDismissController(TaskbarActivityContext activity, TaskbarDragLayer dragLayer) {
mActivity = activity;
mDragLayer = dragLayer;
}
/**
* Initializes dependencies when bubble controllers are created.
* Should be careful to only access things that were created in constructors for now, as some
* controllers may still be waiting for init().
*/
public void init(@NonNull BubbleControllers bubbleControllers) {
mBubbleBarViewController = bubbleControllers.bubbleBarViewController;
}
/**
* Setup the dismiss view and magnetized object that will be attracted to magnetic target.
* Should be called before handling events or showing/hiding dismiss view.
* @param magnetizedView the view to be pulled into target dismiss area
*/
public void setupDismissView(@NonNull View magnetizedView) {
setupDismissView();
setupMagnetizedObject(magnetizedView);
}
/**
* Handle the touch event and pass it to the magnetized object.
* It should be called after {@code setupDismissView}
*/
public boolean handleTouchEvent(@NonNull MotionEvent event) {
return mMagnetizedObject != null && mMagnetizedObject.maybeConsumeMotionEvent(event);
}
/**
* Show dismiss view with animation
* It should be called after {@code setupDismissView}
*/
public void showDismissView() {
if (mDismissView == null) return;
mDismissView.show();
}
/**
* Hide dismiss view with animation
* It should be called after {@code setupDismissView}
*/
public void hideDismissView() {
if (mDismissView == null) return;
mDismissView.hide();
}
/**
* Dismiss magnetized object when it's released in the dismiss target area
*/
private void dismissMagnetizedObject() {
if (mMagnetizedObject == null || mBubbleBarViewController == null) return;
if (mMagnetizedObject.getUnderlyingObject() instanceof BubbleView) {
BubbleView bubbleView = (BubbleView) mMagnetizedObject.getUnderlyingObject();
if (bubbleView.getBubble() != null) {
mBubbleBarViewController.onDismissBubbleWhileDragging(bubbleView.getBubble());
}
} else if (mMagnetizedObject.getUnderlyingObject() instanceof BubbleBarView) {
mBubbleBarViewController.onDismissAllBubblesWhileDragging();
}
cleanUpAnimatedViews();
}
/**
* Animate dismiss view when magnetized object gets stuck in the magnetic target
* @param view captured view
*/
private void animateDismissCaptured(@NonNull View view) {
cancelAnimations();
mDismissAnimator = createDismissAnimator(view);
mDismissAnimator.start();
}
/**
* Animate dismiss view when magnetized object gets unstuck from the magnetic target
*/
private void animateDismissReleased() {
if (mDismissAnimator == null) return;
mDismissAnimator.removeAllListeners();
mDismissAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationCancel(Animator animation) {
cancelAnimations();
}
@Override
public void onAnimationEnd(Animator animation) {
cancelAnimations();
}
});
mDismissAnimator.reverse();
}
/**
* Cancel and clear dismiss animations and reset view states
*/
private void cancelAnimations() {
if (mDismissAnimator == null) return;
ValueAnimator animator = mDismissAnimator;
mDismissAnimator = null;
animator.cancel();
}
/**
* Clean up views changed during animation
*/
private void cleanUpAnimatedViews() {
// Cancel animations
cancelAnimations();
// Reset dismiss view
if (mDismissView != null) {
mDismissView.getCircle().setScaleX(1f);
mDismissView.getCircle().setScaleY(1f);
}
// Reset magnetized view
if (mMagnetizedObject != null) {
mMagnetizedObject.getUnderlyingObject().setAlpha(1f);
mMagnetizedObject.getUnderlyingObject().setScaleX(1f);
mMagnetizedObject.getUnderlyingObject().setScaleY(1f);
}
}
private void setupDismissView() {
if (mDismissView != null) return;
mDismissView = new DismissView(mActivity.getApplicationContext());
BubbleDismissViewUtils.setup(mDismissView);
mDragLayer.addView(mDismissView, /* index = */ 0,
new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
setupMagneticTarget(mDismissView.getCircle());
}
private void setupMagneticTarget(@NonNull View view) {
int magneticFieldRadius = mActivity.getResources().getDimensionPixelSize(
R.dimen.bubblebar_dismiss_target_size);
mMagneticTarget = new MagnetizedObject.MagneticTarget(view, magneticFieldRadius);
}
private void setupMagnetizedObject(@NonNull View magnetizedView) {
mMagnetizedObject = new MagnetizedObject<>(mActivity.getApplicationContext(),
magnetizedView, DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y) {
@Override
public float getWidth(@NonNull View underlyingObject) {
return underlyingObject.getWidth();
}
@Override
public float getHeight(@NonNull View underlyingObject) {
return underlyingObject.getHeight();
}
@Override
public void getLocationOnScreen(@NonNull View underlyingObject, @NonNull int[] loc) {
underlyingObject.getLocationOnScreen(loc);
}
};
mMagnetizedObject.setHapticsEnabled(true);
mMagnetizedObject.setFlingToTargetEnabled(ENABLE_FLING_TO_DISMISS_BUBBLE);
mMagnetizedObject.setFlingToTargetMinVelocity(FLING_TO_DISMISS_MIN_VELOCITY);
if (mMagneticTarget != null) {
mMagnetizedObject.addTarget(mMagneticTarget);
}
mMagnetizedObject.setMagnetListener(new MagnetizedObject.MagnetListener() {
@Override
public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
animateDismissCaptured(magnetizedView);
}
@Override
public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
float velX, float velY, boolean wasFlungOut) {
animateDismissReleased();
}
@Override
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
dismissMagnetizedObject();
}
});
}
private ValueAnimator createDismissAnimator(@NonNull View magnetizedView) {
Resources resources = mActivity.getResources();
int expandedSize = resources.getDimensionPixelSize(R.dimen.bubblebar_dismiss_target_size);
int collapsedSize = resources.getDimensionPixelSize(
R.dimen.bubblebar_dismiss_target_small_size);
float minScale = (float) collapsedSize / expandedSize;
ValueAnimator animator = ValueAnimator.ofFloat(1f, minScale);
animator.addUpdateListener(animation -> {
if (mDismissView == null) return;
final float animatedValue = (float) animation.getAnimatedValue();
mDismissView.getCircle().setScaleX(animatedValue);
mDismissView.getCircle().setScaleY(animatedValue);
magnetizedView.setAlpha(animatedValue);
if (magnetizedView instanceof BubbleBarView) {
magnetizedView.setScaleX(animatedValue);
magnetizedView.setScaleY(animatedValue);
}
});
return animator;
}
}
@@ -0,0 +1,42 @@
/*
* 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.
*/
@file:JvmName("BubbleDismissViewUtils")
package com.android.launcher3.taskbar.bubbles
import com.android.launcher3.R
import com.android.wm.shell.common.bubbles.DismissView
/**
* Dismiss view is shared from WMShell. It requires setup with local resources.
*
* Usage:
* - Kotlin `dismissView.setup()`
* - Java `BubbleDismissViewUtils.setup(dismissView)`
*/
fun DismissView.setup() {
setup(
DismissView.Config(
targetSizeResId = R.dimen.bubblebar_dismiss_target_size,
iconSizeResId = R.dimen.bubblebar_dismiss_target_icon_size,
bottomMarginResId = R.dimen.bubblebar_dismiss_target_bottom_margin,
floatingGradientHeightResId = R.dimen.bubblebar_dismiss_floating_gradient_height,
floatingGradientColorResId = android.R.color.system_neutral1_900,
backgroundResId = R.drawable.bg_bubble_dismiss_circle,
iconResId = R.drawable.ic_bubble_dismiss_white
)
)
}
@@ -0,0 +1,189 @@
/*
* 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 android.annotation.SuppressLint;
import android.graphics.PointF;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.NonNull;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.wm.shell.common.bubbles.RelativeTouchListener;
/**
* Controls bubble bar drag to dismiss interaction.
* Interacts with {@link BubbleDismissController}, used by {@link BubbleBarViewController}.
* Supported interactions:
* - Drag a single bubble view into dismiss target to remove it.
* - Drag the bubble stack into dismiss target to remove all.
* Restores initial position of dragged view if released outside of the dismiss target.
*/
public class BubbleDragController {
private final TaskbarActivityContext mActivity;
private BubbleBarViewController mBubbleBarViewController;
private BubbleDismissController mBubbleDismissController;
public BubbleDragController(TaskbarActivityContext activity) {
mActivity = activity;
}
/**
* Initializes dependencies when bubble controllers are created.
* Should be careful to only access things that were created in constructors for now, as some
* controllers may still be waiting for init().
*/
public void init(@NonNull BubbleControllers bubbleControllers) {
mBubbleBarViewController = bubbleControllers.bubbleBarViewController;
mBubbleDismissController = bubbleControllers.bubbleDismissController;
}
/**
* Setup the bubble view for dragging and attach touch listener to it
*/
@SuppressLint("ClickableViewAccessibility")
public void setupBubbleView(@NonNull BubbleView bubbleView) {
// Don't setup dragging for overflow bubble view
if (bubbleView.getBubble() == null
|| !(bubbleView.getBubble() instanceof BubbleBarBubble)) return;
bubbleView.setOnTouchListener(new BaseDragListener() {
@Override
protected void onDragStart() {
super.onDragStart();
mBubbleBarViewController.onDragStart(bubbleView);
}
@Override
protected void onDragEnd() {
super.onDragEnd();
mBubbleBarViewController.onDragEnd(bubbleView);
}
});
}
/**
* Setup the bubble bar view for dragging and attach touch listener to it
*/
@SuppressLint("ClickableViewAccessibility")
public void setupBubbleBarView(@NonNull BubbleBarView bubbleBarView) {
PointF initialRelativePivot = new PointF();
bubbleBarView.setOnTouchListener(new BaseDragListener() {
@Override
public boolean onDown(@NonNull View view, @NonNull MotionEvent event) {
if (bubbleBarView.isExpanded()) return false;
// Setup dragging only when bubble bar is collapsed
return super.onDown(view, event);
}
@Override
protected void onDragStart() {
super.onDragStart();
initialRelativePivot.set(bubbleBarView.getRelativePivotX(),
bubbleBarView.getRelativePivotY());
bubbleBarView.setRelativePivot(/* x = */ 0.5f, /* y = */ 0.5f);
}
@Override
protected void onDragEnd() {
super.onDragEnd();
bubbleBarView.setRelativePivot(initialRelativePivot.x, initialRelativePivot.y);
}
});
}
/**
* Base drag listener for handling a single bubble view or bubble bar view dragging.
* Controls dragging interaction and interacts with {@link BubbleDismissController}
* to coordinate dismiss view presentation.
* Lifecycle methods can be overridden do add extra setup/clean up steps
*/
private class BaseDragListener extends RelativeTouchListener {
private boolean mHandling;
private boolean mDragging;
@Override
public boolean onDown(@NonNull View view, @NonNull MotionEvent event) {
mHandling = true;
mActivity.setTaskbarWindowFullscreen(true);
mBubbleDismissController.setupDismissView(view);
mBubbleDismissController.handleTouchEvent(event);
return true;
}
@Override
public void onMove(@NonNull View view, @NonNull MotionEvent event, float viewInitialX,
float viewInitialY, float dx, float dy) {
if (!mHandling) return;
if (!mDragging) {
// Start dragging
mDragging = true;
onDragStart();
}
if (!mBubbleDismissController.handleTouchEvent(event)) {
// Drag the view if not processed by dismiss controller
view.setTranslationX(viewInitialX + dx);
view.setTranslationY(viewInitialY + dy);
}
}
@Override
public void onUp(@NonNull View view, @NonNull MotionEvent event, float viewInitialX,
float viewInitialY, float dx, float dy, float velX, float velY) {
onComplete(view, event, viewInitialX, viewInitialY);
}
@Override
public void onCancel(@NonNull View view, @NonNull MotionEvent event, float viewInitialX,
float viewInitialY, float dx, float dy) {
onComplete(view, event, viewInitialX, viewInitialY);
}
/**
* Prepares dismiss view for dragging.
* This method can be overridden to add extra setup on drag start
*/
protected void onDragStart() {
mBubbleDismissController.showDismissView();
}
/**
* Cleans up dismiss view after dragging.
* This method can be overridden to add extra setup on drag end
*/
protected void onDragEnd() {
mBubbleDismissController.hideDismissView();
}
/**
* Complete drag handling and clean up dependencies
*/
private void onComplete(@NonNull View view, @NonNull MotionEvent event,
float viewInitialX, float viewInitialY) {
if (!mHandling) return;
if (mDragging) {
// Stop dragging
mDragging = false;
view.setTranslationX(viewInitialX);
view.setTranslationY(viewInitialY);
onDragEnd();
}
mBubbleDismissController.handleTouchEvent(event);
mActivity.setTaskbarWindowFullscreen(false);
mHandling = false;
}
}
}
@@ -661,6 +661,31 @@ public class SystemUiProxy implements ISystemUiProxy {
}
}
/**
* Tells SysUI to remove the bubble with the provided key.
* @param key the key of the bubble to show.
*/
public void removeBubble(String key) {
if (mBubbles == null) return;
try {
mBubbles.removeBubble(key);
} catch (RemoteException e) {
Log.w(TAG, "Failed call removeBubble");
}
}
/**
* Tells SysUI to remove all bubbles.
*/
public void removeAllBubbles() {
if (mBubbles == null) return;
try {
mBubbles.removeAllBubbles();
} catch (RemoteException e) {
Log.w(TAG, "Failed call removeAllBubbles");
}
}
/**
* Tells SysUI to collapse the bubbles.
*/
@@ -674,6 +699,21 @@ public class SystemUiProxy implements ISystemUiProxy {
}
}
/**
* Tells SysUI to collapse/expand selected bubble view while it's dragged.
* Should be called only when the bubble bar is expanded.
* @param bubbleKey the key of the bubble to collapse/expand
* @param collapse whether to collapse/expand selected bubble
*/
public void collapseWhileDragging(@Nullable String bubbleKey, boolean collapse) {
if (mBubbles == null) return;
try {
mBubbles.collapseWhileDragging(bubbleKey, collapse);
} catch (RemoteException e) {
Log.w(TAG, "Failed call collapseWhileDragging");
}
}
//
// Splitscreen
//