Merge changes I6fe1139e,I581dddf2 into main
* changes: Handle new bubble notification while drag is in progress. Made the bubble bar react to drag events from the Shell.
This commit is contained in:
committed by
Android (Google) Code Review
commit
a593d2a7da
@@ -52,6 +52,7 @@ import android.view.ViewRootImpl;
|
||||
import android.window.SurfaceSyncGroup;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.app.animation.Interpolators;
|
||||
import com.android.internal.logging.InstanceId;
|
||||
@@ -75,6 +76,7 @@ import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.popup.PopupContainerWithArrow;
|
||||
import com.android.launcher3.shortcuts.DeepShortcutView;
|
||||
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
|
||||
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController;
|
||||
import com.android.launcher3.testing.TestLogging;
|
||||
import com.android.launcher3.testing.shared.TestProtocol;
|
||||
import com.android.launcher3.util.DisplayController;
|
||||
@@ -517,6 +519,7 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
|
||||
return mIsSystemDragInProgress;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
private void maybeOnDragEnd() {
|
||||
if (!isDragging()) {
|
||||
((BubbleTextView) mDragObject.originalView).setIconDisabled(false);
|
||||
@@ -524,17 +527,38 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
|
||||
TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING, false);
|
||||
mActivity.onDragEnd();
|
||||
if (mReturnAnimator == null) {
|
||||
// If an item is dropped on the bubble bar, the bubble bar handles the drop,
|
||||
// so it should not collapse along with the taskbar.
|
||||
boolean droppedOnBubbleBar = notifyBubbleBarItemDropped();
|
||||
// Upon successful drag, immediately stash taskbar.
|
||||
// Note, this must be done last to ensure no AutohideSuspendFlags are active, as
|
||||
// that will prevent us from stashing until the timeout.
|
||||
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
|
||||
|
||||
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(
|
||||
/* stash = */ true,
|
||||
/* shouldBubblesFollow = */ !droppedOnBubbleBar
|
||||
);
|
||||
mActivity.getStatsLogManager().logger().withItemInfo(mDragObject.dragInfo)
|
||||
.log(LAUNCHER_APP_LAUNCH_DRAGDROP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exits the Bubble Bar drop target mode if applicable.
|
||||
*
|
||||
* @return {@code true} if drop target mode was active.
|
||||
*/
|
||||
private boolean notifyBubbleBarItemDropped() {
|
||||
return mControllers.bubbleControllers.map(bc -> {
|
||||
BubbleBarViewController bubbleBarViewController = bc.bubbleBarViewController;
|
||||
boolean showingDropTarget = bubbleBarViewController.isShowingDropTarget();
|
||||
if (showingDropTarget) {
|
||||
bubbleBarViewController.onItemDroppedInBubbleBarDragZone();
|
||||
}
|
||||
return showingDropTarget;
|
||||
}).orElse(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void endDrag() {
|
||||
if (mDisallowGlobalDrag) {
|
||||
|
||||
@@ -44,10 +44,13 @@ class BubbleBarBackground(context: Context, private var backgroundHeight: Float)
|
||||
private val arrowVisibleHeight: Float
|
||||
|
||||
private val strokeAlpha: Int
|
||||
private val strokeColor: Int
|
||||
private val strokeColorDropTarget: Int
|
||||
private val shadowAlpha: Int
|
||||
private val shadowBlur: Float
|
||||
private val keyShadowDistance: Float
|
||||
private var arrowHeightFraction = 1f
|
||||
private var isShowingDropTarget: Boolean = false
|
||||
|
||||
var arrowPositionX: Float = 0f
|
||||
private set
|
||||
@@ -100,7 +103,9 @@ class BubbleBarBackground(context: Context, private var backgroundHeight: Float)
|
||||
fillPaint.flags = Paint.ANTI_ALIAS_FLAG
|
||||
fillPaint.style = Paint.Style.FILL
|
||||
// configure stroke paint
|
||||
strokePaint.color = context.getColor(R.color.taskbar_stroke)
|
||||
strokeColor = context.getColor(R.color.taskbar_stroke)
|
||||
strokeColorDropTarget = context.getColor(com.android.internal.R.color.system_primary_fixed)
|
||||
strokePaint.color = strokeColor
|
||||
strokePaint.flags = Paint.ANTI_ALIAS_FLAG
|
||||
strokePaint.style = Paint.Style.STROKE
|
||||
strokePaint.strokeWidth = res.getDimension(R.dimen.transient_taskbar_stroke_width)
|
||||
@@ -235,9 +240,25 @@ class BubbleBarBackground(context: Context, private var backgroundHeight: Float)
|
||||
return max(0f, getScaledArrowHeight() - (arrowHeight - arrowVisibleHeight))
|
||||
}
|
||||
|
||||
/** Set whether the background should show the drop target */
|
||||
fun showDropTarget(isDropTarget: Boolean) {
|
||||
if (isShowingDropTarget == isDropTarget) {
|
||||
return
|
||||
}
|
||||
isShowingDropTarget = isDropTarget
|
||||
val strokeColor = if (isDropTarget) strokeColorDropTarget else strokeColor
|
||||
val alpha = if (isDropTarget) DRAG_STROKE_ALPHA else strokeAlpha
|
||||
strokePaint.color = strokeColor
|
||||
strokePaint.alpha = alpha
|
||||
invalidateSelf()
|
||||
}
|
||||
|
||||
fun isShowingDropTarget() = isShowingDropTarget
|
||||
|
||||
companion object {
|
||||
private const val DARK_THEME_STROKE_ALPHA = 51
|
||||
private const val LIGHT_THEME_STROKE_ALPHA = 41
|
||||
private const val DRAG_STROKE_ALPHA = 255
|
||||
private const val DARK_THEME_SHADOW_ALPHA = 51
|
||||
private const val LIGHT_THEME_SHADOW_ALPHA = 25
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@ import android.os.Bundle;
|
||||
import android.os.SystemProperties;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.launcher3.taskbar.TaskbarSharedState;
|
||||
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController;
|
||||
@@ -589,15 +590,24 @@ public class BubbleBarController extends IBubblesListener.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDragItemOverBubbleBarDragZone(BubbleBarLocation location) {
|
||||
//TODO(b/388894910): add meaningful implementation
|
||||
MAIN_EXECUTOR.execute(() ->
|
||||
Toast.makeText(mContext, "onDragItemOver " + location, Toast.LENGTH_SHORT).show());
|
||||
public void onDragItemOverBubbleBarDragZone(@NonNull BubbleBarLocation bubbleBarLocation) {
|
||||
MAIN_EXECUTOR.execute(() -> {
|
||||
mBubbleBarViewController.onDragItemOverBubbleBarDragZone(bubbleBarLocation);
|
||||
if (mBubbleBarViewController.isLocationUpdatedForDropTarget()) {
|
||||
mBubbleBarLocationListener.onBubbleBarLocationAnimated(bubbleBarLocation);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemDraggedOutsideBubbleBarDropZone() {
|
||||
|
||||
MAIN_EXECUTOR.execute(() -> {
|
||||
if (mBubbleBarViewController.isLocationUpdatedForDropTarget()) {
|
||||
BubbleBarLocation original = mBubbleBarViewController.getBubbleBarLocation();
|
||||
mBubbleBarLocationListener.onBubbleBarLocationAnimated(original);
|
||||
}
|
||||
mBubbleBarViewController.onItemDraggedOutsideBubbleBarDropZone();
|
||||
});
|
||||
}
|
||||
|
||||
/** Notifies WMShell to show the expanded view. */
|
||||
|
||||
@@ -536,6 +536,16 @@ public class BubbleBarView extends FrameLayout {
|
||||
return (float) (displayWidth - getWidth() - margin);
|
||||
}
|
||||
|
||||
/** Set whether the background should show the drop target */
|
||||
public void showDropTarget(boolean isDropTarget) {
|
||||
mBubbleBarBackground.showDropTarget(isDropTarget);
|
||||
}
|
||||
|
||||
/** Returns whether the Bubble Bar is currently displaying a drop target. */
|
||||
public boolean isShowingDropTarget() {
|
||||
return mBubbleBarBackground.isShowingDropTarget();
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate bubble bar to the given location transiently. Does not modify the layout or the value
|
||||
* returned by {@link #getBubbleBarLocation()}.
|
||||
|
||||
@@ -131,12 +131,13 @@ public class BubbleBarViewController {
|
||||
// Whether the bar is hidden when stashed
|
||||
private boolean mHiddenForStashed;
|
||||
private boolean mShouldShowEducation;
|
||||
|
||||
public boolean mOverflowAdded;
|
||||
private boolean mIsLocationUpdatedForDropTarget = false;
|
||||
|
||||
private BubbleBarViewAnimator mBubbleBarViewAnimator;
|
||||
private final FrameLayout mBubbleBarContainer;
|
||||
private BubbleBarFlyoutController mBubbleBarFlyoutController;
|
||||
private BubbleBarPinController mBubbleBarPinController;
|
||||
private TaskbarSharedState mTaskbarSharedState;
|
||||
private final TimeSource mTimeSource = System::currentTimeMillis;
|
||||
private final int mTaskbarTranslationDelta;
|
||||
@@ -166,6 +167,7 @@ public class BubbleBarViewController {
|
||||
mBubbleStashController = bubbleControllers.bubbleStashController;
|
||||
mBubbleBarController = bubbleControllers.bubbleBarController;
|
||||
mBubbleDragController = bubbleControllers.bubbleDragController;
|
||||
mBubbleBarPinController = bubbleControllers.bubbleBarPinController;
|
||||
mTaskbarStashController = controllers.taskbarStashController;
|
||||
mTaskbarInsetsController = controllers.taskbarInsetsController;
|
||||
mBubbleBarFlyoutController = new BubbleBarFlyoutController(
|
||||
@@ -274,7 +276,10 @@ public class BubbleBarViewController {
|
||||
|
||||
@Override
|
||||
public boolean isOnLeft() {
|
||||
return mBarView.getBubbleBarLocation().isOnLeft(mBarView.isLayoutRtl());
|
||||
boolean shouldRevertLocation =
|
||||
mBarView.isShowingDropTarget() && mIsLocationUpdatedForDropTarget;
|
||||
boolean isOnLeft = mBarView.getBubbleBarLocation().isOnLeft(mBarView.isLayoutRtl());
|
||||
return shouldRevertLocation != isOnLeft;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -524,6 +529,61 @@ public class BubbleBarViewController {
|
||||
mBarView.animateToBubbleBarLocation(bubbleBarLocation);
|
||||
}
|
||||
|
||||
/** Returns whether the Bubble Bar is currently displaying a drop target. */
|
||||
public boolean isShowingDropTarget() {
|
||||
return mBarView.isShowingDropTarget();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the controller that a drag event is over the Bubble Bar drop zone. The controller
|
||||
* will display the appropriate drop target and enter drop target mode. The controller will also
|
||||
* update the return value of {@link #isLocationUpdatedForDropTarget()} to true if location was
|
||||
* updated.
|
||||
*/
|
||||
public void onDragItemOverBubbleBarDragZone(@NonNull BubbleBarLocation bubbleBarLocation) {
|
||||
mBarView.showDropTarget(/* isDropTarget = */ true);
|
||||
mIsLocationUpdatedForDropTarget = getBubbleBarLocation() != bubbleBarLocation;
|
||||
if (mIsLocationUpdatedForDropTarget) {
|
||||
animateBubbleBarLocation(bubbleBarLocation);
|
||||
}
|
||||
if (!hasBubbles()) {
|
||||
mBubbleBarPinController.showDropTarget(bubbleBarLocation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if location was updated after most recent
|
||||
* {@link #onDragItemOverBubbleBarDragZone}}.
|
||||
*/
|
||||
public boolean isLocationUpdatedForDropTarget() {
|
||||
return mIsLocationUpdatedForDropTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the controller that the drag event is outside the Bubble Bar drop zone.
|
||||
* This will hide the drop target zone if there are no bubbles or return the
|
||||
* Bubble Bar to its original location. The controller will also exit drop target
|
||||
* mode and reset the value returned from {@link #isLocationUpdatedForDropTarget()} to false.
|
||||
*/
|
||||
public void onItemDraggedOutsideBubbleBarDropZone() {
|
||||
mBarView.showDropTarget(/* isDropTarget = */ false);
|
||||
if (mIsLocationUpdatedForDropTarget) {
|
||||
animateBubbleBarLocation(getBubbleBarLocation());
|
||||
}
|
||||
mBubbleBarPinController.hideDropTarget();
|
||||
mIsLocationUpdatedForDropTarget = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the controller that the drag has completed over the Bubble Bar drop zone.
|
||||
* The controller will hide the drop target if there are no bubbles and exit drop target mode.
|
||||
*/
|
||||
public void onItemDroppedInBubbleBarDragZone() {
|
||||
mBarView.showDropTarget(/* isDropTarget = */ false);
|
||||
mBubbleBarPinController.hideDropTarget();
|
||||
mIsLocationUpdatedForDropTarget = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The bounds of the bubble bar.
|
||||
*/
|
||||
@@ -996,7 +1056,12 @@ public class BubbleBarViewController {
|
||||
boolean isInApp = mTaskbarStashController.isInApp();
|
||||
// if this is the first bubble, animate to the initial state.
|
||||
if (mBarView.getBubbleChildCount() == 1 && !isUpdate) {
|
||||
mBubbleBarViewAnimator.animateToInitialState(bubble, isInApp, isExpanding);
|
||||
// If a drop target is visible and the first bubble is added, hide the empty drop target
|
||||
if (mBarView.isShowingDropTarget()) {
|
||||
mBubbleBarPinController.hideDropTarget();
|
||||
}
|
||||
mBubbleBarViewAnimator.animateToInitialState(bubble, isInApp, isExpanding,
|
||||
mBarView.isShowingDropTarget());
|
||||
return;
|
||||
}
|
||||
// if we're not stashed or we're in persistent taskbar, animate for collapsed state.
|
||||
|
||||
+18
-12
@@ -365,7 +365,12 @@ constructor(
|
||||
}
|
||||
|
||||
/** Animates to the initial state of the bubble bar, when there are no previous bubbles. */
|
||||
fun animateToInitialState(b: BubbleBarBubble, isInApp: Boolean, isExpanding: Boolean) {
|
||||
fun animateToInitialState(
|
||||
b: BubbleBarBubble,
|
||||
isInApp: Boolean,
|
||||
isExpanding: Boolean,
|
||||
isDragging: Boolean = false,
|
||||
) {
|
||||
val bubbleView = b.view
|
||||
val animator = PhysicsAnimator.getInstance(bubbleView)
|
||||
if (animator.isRunning()) animator.cancel()
|
||||
@@ -374,15 +379,12 @@ constructor(
|
||||
// bubble bar to the handle if we're in an app.
|
||||
val showAnimation = buildBubbleBarSpringInAnimation()
|
||||
val hideAnimation =
|
||||
if (isInApp && !isExpanding) {
|
||||
if (isInApp && !isExpanding && !isDragging) {
|
||||
buildBubbleBarToHandleAnimation()
|
||||
} else {
|
||||
Runnable {
|
||||
moveToState(AnimatingBubble.State.ANIMATING_OUT)
|
||||
bubbleBarFlyoutController.collapseFlyout {
|
||||
onFlyoutRemoved()
|
||||
clearAnimatingBubble()
|
||||
}
|
||||
collapseFlyoutAndUpdateState()
|
||||
if (isDragging) return@Runnable
|
||||
bubbleStashController.showBubbleBarImmediate()
|
||||
bubbleStashController.updateTaskbarTouchRegion()
|
||||
}
|
||||
@@ -440,11 +442,7 @@ constructor(
|
||||
// first bounce the bubble bar and show the flyout. Then hide the flyout.
|
||||
val showAnimation = buildBubbleBarBounceAnimation()
|
||||
val hideAnimation = Runnable {
|
||||
moveToState(AnimatingBubble.State.ANIMATING_OUT)
|
||||
bubbleBarFlyoutController.collapseFlyout {
|
||||
onFlyoutRemoved()
|
||||
clearAnimatingBubble()
|
||||
}
|
||||
collapseFlyoutAndUpdateState()
|
||||
bubbleStashController.showBubbleBarImmediate()
|
||||
bubbleStashController.updateTaskbarTouchRegion()
|
||||
}
|
||||
@@ -454,6 +452,14 @@ constructor(
|
||||
scheduler.postDelayed(FLYOUT_DELAY_MS, hideAnimation)
|
||||
}
|
||||
|
||||
private fun collapseFlyoutAndUpdateState() {
|
||||
moveToState(AnimatingBubble.State.ANIMATING_OUT)
|
||||
bubbleBarFlyoutController.collapseFlyout {
|
||||
onFlyoutRemoved()
|
||||
clearAnimatingBubble()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The bubble bar animation when it is collapsed is divided into 2 chained animations. The first
|
||||
* animation is a regular accelerate animation that moves the bubble bar upwards. When it ends
|
||||
|
||||
+66
@@ -60,6 +60,7 @@ import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.atLeastOnce
|
||||
import org.mockito.kotlin.eq
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.never
|
||||
import org.mockito.kotlin.verify
|
||||
import org.mockito.kotlin.whenever
|
||||
|
||||
@@ -572,6 +573,71 @@ class BubbleBarViewAnimatorTest {
|
||||
verify(bubbleStashController).stashBubbleBarImmediate()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun animateToInitialState_whileDragging_inApp() {
|
||||
setUpBubbleBar()
|
||||
setUpBubbleStashController()
|
||||
whenever(bubbleStashController.bubbleBarTranslationY)
|
||||
.thenReturn(BAR_TRANSLATION_Y_FOR_TASKBAR)
|
||||
|
||||
val handle = View(context)
|
||||
val handleAnimator = PhysicsAnimator.getInstance(handle)
|
||||
whenever(bubbleStashController.getStashedHandlePhysicsAnimator()).thenReturn(handleAnimator)
|
||||
|
||||
val barAnimator = PhysicsAnimator.getInstance(bubbleBarView)
|
||||
|
||||
var notifiedBubbleBarVisible = false
|
||||
val onBubbleBarVisible = Runnable { notifiedBubbleBarVisible = true }
|
||||
val animator =
|
||||
BubbleBarViewAnimator(
|
||||
bubbleBarView,
|
||||
bubbleStashController,
|
||||
flyoutController,
|
||||
bubbleBarParentViewController,
|
||||
onExpanded = emptyRunnable,
|
||||
onBubbleBarVisible = onBubbleBarVisible,
|
||||
animatorScheduler,
|
||||
)
|
||||
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync {
|
||||
bubbleBarView.visibility = INVISIBLE
|
||||
animator.animateToInitialState(
|
||||
bubble,
|
||||
isInApp = true,
|
||||
isExpanding = false,
|
||||
isDragging = true,
|
||||
)
|
||||
}
|
||||
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync {}
|
||||
PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(DynamicAnimation.TRANSLATION_Y)
|
||||
|
||||
barAnimator.assertIsNotRunning()
|
||||
assertThat(animator.isAnimating).isTrue()
|
||||
assertThat(bubbleBarView.alpha).isEqualTo(1)
|
||||
assertThat(bubbleBarView.translationY).isEqualTo(BAR_TRANSLATION_Y_FOR_TASKBAR)
|
||||
assertThat(bubbleBarParentViewController.timesInvoked).isEqualTo(1)
|
||||
waitForFlyoutToShow()
|
||||
|
||||
assertThat(animatorScheduler.delayedBlock).isNotNull()
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync(animatorScheduler.delayedBlock!!)
|
||||
|
||||
waitForFlyoutToHide()
|
||||
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync {}
|
||||
PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(DynamicAnimation.TRANSLATION_Y)
|
||||
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
|
||||
assertThat(bubbleBarParentViewController.timesInvoked).isEqualTo(2)
|
||||
assertThat(animator.isAnimating).isFalse()
|
||||
assertThat(bubbleBarView.alpha).isEqualTo(1)
|
||||
assertThat(handle.translationY).isEqualTo(0)
|
||||
assertThat(bubbleBarView.visibility).isEqualTo(VISIBLE)
|
||||
assertThat(notifiedBubbleBarVisible).isTrue()
|
||||
|
||||
verify(bubbleStashController, never()).stashBubbleBarImmediate()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun animateToInitialState_inApp_autoExpanding() {
|
||||
setUpBubbleBar()
|
||||
|
||||
Reference in New Issue
Block a user