Creates BubbleStashController & BubbleStashedHandleViewController
Adds two controllers to manage and animate the stash / unstash state
of the bubble bar. Adds them to BubbleControllers.
Bug: 253318833
Test: manual, with other CLs, see go/bubble-bar-tests
Flag: WM_BUBBLE_BAR
Change-Id: I1a67da888384b1ae0fe3f79a25ea6a2c1b7eef87
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java
index 5eec6a4..83e4571 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java
@@ -30,6 +30,10 @@
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.R;
+/**
+ * View to render a handle that changes color based on the background to ensure contrast. Used for
+ * the taskbar when stashed as well as the bubble bar when stashed.
+ */
public class StashedHandleView extends View {
private static final long COLOR_CHANGE_DURATION = 120;
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index deac42f..4145ac6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -47,10 +47,11 @@
private final int mIconSize;
// Initialized in init.
+ private BubbleStashController mBubbleStashController;
private View.OnClickListener mBubbleClickListener;
private View.OnClickListener mBubbleBarClickListener;
- // These are exposed to BubbleStashController to animate for stashing/un-stashing
+ // These are exposed to {@link BubbleStashController} to animate for stashing/un-stashing
private final MultiValueAlpha mBubbleBarAlpha;
private final AnimatedFloat mBubbleBarScale = new AnimatedFloat(this::updateScale);
private final AnimatedFloat mBubbleBarTranslationY = new AnimatedFloat(
@@ -73,6 +74,8 @@
}
public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) {
+ mBubbleStashController = bubbleControllers.bubbleStashController;
+
mActivity.addOnDeviceProfileChangeListener(dp ->
mBarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarHeight
);
@@ -171,8 +174,7 @@
// TODO: (b/273592694) animate it
private void updateVisibilityForStateChange() {
- // TODO: check if it's stashed
- if (!mHiddenForSysui && !mHiddenForNoBubbles) {
+ if (!mHiddenForSysui && !mBubbleStashController.isStashed() && !mHiddenForNoBubbles) {
mBarView.setVisibility(VISIBLE);
} else {
mBarView.setVisibility(INVISIBLE);
@@ -271,6 +273,10 @@
* from SystemUI.
*/
public void setExpandedFromSysui(boolean isExpanded) {
- // TODO: Tell bubble bar stash controller to stash or unstash the bubble bar
+ if (!isExpanded) {
+ mBubbleStashController.stashBubbleBar();
+ } else {
+ mBubbleStashController.showBubbleBar(true /* expand the bubbles */);
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
index e92d4fb..8b07062 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
@@ -24,6 +24,8 @@
public class BubbleControllers {
public final BubbleBarViewController bubbleBarViewController;
+ public final BubbleStashController bubbleStashController;
+ public final BubbleStashedHandleViewController bubbleStashedHandleViewController;
private final RunnableList mPostInitRunnables = new RunnableList();
@@ -32,8 +34,12 @@
* * Call init
* * Call onDestroy
*/
- public BubbleControllers(BubbleBarViewController bubbleBarViewController) {
+ public BubbleControllers(BubbleBarViewController bubbleBarViewController,
+ BubbleStashController bubbleStashController,
+ BubbleStashedHandleViewController bubbleStashedHandleViewController) {
this.bubbleBarViewController = bubbleBarViewController;
+ this.bubbleStashController = bubbleStashController;
+ this.bubbleStashedHandleViewController = bubbleStashedHandleViewController;
}
/**
@@ -43,6 +49,8 @@
*/
public void init(TaskbarControllers taskbarControllers) {
bubbleBarViewController.init(taskbarControllers, this);
+ bubbleStashedHandleViewController.init(taskbarControllers, this);
+ bubbleStashController.init(taskbarControllers, this);
mPostInitRunnables.executeAllAndDestroy();
}
@@ -61,6 +69,6 @@
* Cleans up all controllers.
*/
public void onDestroy() {
- // TODO
+ bubbleStashedHandleViewController.onDestroy();
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
new file mode 100644
index 0000000..0ab53b0
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
@@ -0,0 +1,277 @@
+/*
+ * 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.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.annotation.Nullable;
+import android.view.InsetsController;
+
+import com.android.launcher3.anim.AnimatedFloat;
+import com.android.launcher3.taskbar.StashedHandleViewController;
+import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.launcher3.taskbar.TaskbarControllers;
+import com.android.launcher3.taskbar.TaskbarStashController;
+import com.android.launcher3.util.MultiPropertyFactory;
+
+/**
+ * Coordinates between controllers such as BubbleBarView and BubbleHandleViewController to
+ * create a cohesive animation between stashed/unstashed states.
+ */
+public class BubbleStashController {
+
+ private static final String TAG = BubbleStashController.class.getSimpleName();
+
+ /**
+ * How long to stash/unstash.
+ */
+ public static final long BAR_STASH_DURATION = InsetsController.ANIMATION_DURATION_RESIZE;
+
+ /**
+ * The scale bubble bar animates to when being stashed.
+ */
+ private static final float STASHED_BAR_SCALE = 0.5f;
+
+ protected final TaskbarActivityContext mActivity;
+
+ // Initialized in init.
+ private TaskbarControllers mControllers;
+ private BubbleBarViewController mBarViewController;
+ private BubbleStashedHandleViewController mHandleViewController;
+ private TaskbarStashController mTaskbarStashController;
+
+ private MultiPropertyFactory.MultiProperty mIconAlphaForStash;
+ private AnimatedFloat mIconScaleForStash;
+ private AnimatedFloat mIconTranslationYForStash;
+ private MultiPropertyFactory.MultiProperty mBubbleStashedHandleAlpha;
+
+ private boolean mRequestedStashState;
+ private boolean mRequestedExpandedState;
+
+ private boolean mIsStashed;
+ private int mStashedHeight;
+ private int mUnstashedHeight;
+ private boolean mBubblesShowingOnHome;
+ private boolean mBubblesShowingOnOverview;
+
+ @Nullable
+ private AnimatorSet mAnimator;
+
+ public BubbleStashController(TaskbarActivityContext activity) {
+ mActivity = activity;
+ }
+
+ public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) {
+ mControllers = controllers;
+ mBarViewController = bubbleControllers.bubbleBarViewController;
+ mHandleViewController = bubbleControllers.bubbleStashedHandleViewController;
+ mTaskbarStashController = controllers.taskbarStashController;
+
+ mIconAlphaForStash = mBarViewController.getBubbleBarAlpha().get(0);
+ mIconScaleForStash = mBarViewController.getBubbleBarScale();
+ mIconTranslationYForStash = mBarViewController.getBubbleBarTranslationY();
+
+ mBubbleStashedHandleAlpha = mHandleViewController.getStashedHandleAlpha().get(
+ StashedHandleViewController.ALPHA_INDEX_STASHED);
+
+ mStashedHeight = mHandleViewController.getStashedHeight();
+ mUnstashedHeight = mHandleViewController.getUnstashedHeight();
+
+ bubbleControllers.runAfterInit(() -> {
+ if (mTaskbarStashController.isStashed()) {
+ stashBubbleBar();
+ } else {
+ showBubbleBar(false /* expandBubbles */);
+ }
+ });
+ }
+
+ /**
+ * Returns the touchable height of the bubble bar based on it's stashed state.
+ */
+ public int getTouchableHeight() {
+ return mIsStashed ? mStashedHeight : mUnstashedHeight;
+ }
+
+ /**
+ * Returns whether the bubble bar is currently stashed.
+ */
+ public boolean isStashed() {
+ return mIsStashed;
+ }
+
+ /**
+ * Called when launcher enters or exits the home page. Bubbles are unstashed on home.
+ */
+ public void setBubblesShowingOnHome(boolean onHome) {
+ if (mBubblesShowingOnHome != onHome) {
+ mBubblesShowingOnHome = onHome;
+ if (mBubblesShowingOnHome) {
+ showBubbleBar(/* expanded= */ false);
+ } else if (!mBarViewController.isExpanded()) {
+ stashBubbleBar();
+ }
+ }
+ }
+
+ /** Whether bubbles are showing on the launcher home page. */
+ public boolean isBubblesShowingOnHome() {
+ return mBubblesShowingOnHome;
+ }
+
+ // TODO: when tapping on an app in overview, this is a bit delayed compared to taskbar stashing
+ /** Called when launcher enters or exits overview. Bubbles are unstashed on overview. */
+ public void setBubblesShowingOnOverview(boolean onOverview) {
+ if (mBubblesShowingOnOverview != onOverview) {
+ mBubblesShowingOnOverview = onOverview;
+ if (!mBubblesShowingOnOverview && !mBarViewController.isExpanded()) {
+ stashBubbleBar();
+ }
+ }
+ }
+
+ /** Called when sysui locked state changes, when locked, bubble bar is stashed. */
+ public void onSysuiLockedStateChange(boolean isSysuiLocked) {
+ if (isSysuiLocked) {
+ // TODO: should the normal path flip mBubblesOnHome / check if this is needed
+ // If we're locked, we're no longer showing on home.
+ mBubblesShowingOnHome = false;
+ mBubblesShowingOnOverview = false;
+ stashBubbleBar();
+ }
+ }
+
+ /**
+ * Stashes the bubble bar if allowed based on other state (e.g. on home and overview the
+ * bar does not stash).
+ */
+ public void stashBubbleBar() {
+ mRequestedStashState = true;
+ mRequestedExpandedState = false;
+ updateStashedAndExpandedState();
+ }
+
+ /**
+ * Shows the bubble bar, and expands bubbles depending on {@param expandBubbles}.
+ */
+ public void showBubbleBar(boolean expandBubbles) {
+ mRequestedStashState = false;
+ mRequestedExpandedState = expandBubbles;
+ updateStashedAndExpandedState();
+ }
+
+ private void updateStashedAndExpandedState() {
+ if (mBarViewController.isHiddenForNoBubbles()) {
+ // If there are no bubbles the bar and handle are invisible, nothing to do here.
+ return;
+ }
+ boolean isStashed = mRequestedStashState
+ && !mBubblesShowingOnHome
+ && !mBubblesShowingOnOverview;
+ if (mIsStashed != isStashed) {
+ mIsStashed = isStashed;
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ }
+ mAnimator = createStashAnimator(mIsStashed, BAR_STASH_DURATION);
+ mAnimator.start();
+ onIsStashedChanged();
+ }
+ if (mBarViewController.isExpanded() != mRequestedExpandedState) {
+ mBarViewController.setExpanded(mRequestedExpandedState);
+ }
+ }
+
+ /**
+ * Create a stash animation.
+ *
+ * @param isStashed whether it's a stash animation or an unstash animation
+ * @param duration duration of the animation
+ * @return the animation
+ */
+ private AnimatorSet createStashAnimator(boolean isStashed, long duration) {
+ AnimatorSet animatorSet = new AnimatorSet();
+ final float stashTranslation = (mUnstashedHeight - mStashedHeight) / 2f;
+
+ AnimatorSet fullLengthAnimatorSet = new AnimatorSet();
+ // Not exactly half and may overlap. See [first|second]HalfDurationScale below.
+ AnimatorSet firstHalfAnimatorSet = new AnimatorSet();
+ AnimatorSet secondHalfAnimatorSet = new AnimatorSet();
+
+ final float firstHalfDurationScale;
+ final float secondHalfDurationScale;
+
+ if (isStashed) {
+ firstHalfDurationScale = 0.75f;
+ secondHalfDurationScale = 0.5f;
+
+ fullLengthAnimatorSet.play(mIconTranslationYForStash.animateToValue(stashTranslation));
+
+ firstHalfAnimatorSet.playTogether(
+ mIconAlphaForStash.animateToValue(0),
+ mIconScaleForStash.animateToValue(STASHED_BAR_SCALE));
+ secondHalfAnimatorSet.playTogether(
+ mBubbleStashedHandleAlpha.animateToValue(1));
+ } else {
+ firstHalfDurationScale = 0.5f;
+ secondHalfDurationScale = 0.75f;
+
+ // If we're on home, adjust the translation so the bubble bar aligns with hotseat.
+ final float hotseatTransY = mActivity.getDeviceProfile().getTaskbarOffsetY();
+ final float translationY = mBubblesShowingOnHome ? hotseatTransY : 0;
+ fullLengthAnimatorSet.playTogether(
+ mIconScaleForStash.animateToValue(1),
+ mIconTranslationYForStash.animateToValue(translationY));
+
+ firstHalfAnimatorSet.playTogether(
+ mBubbleStashedHandleAlpha.animateToValue(0)
+ );
+ secondHalfAnimatorSet.playTogether(
+ mIconAlphaForStash.animateToValue(1)
+ );
+ }
+
+ fullLengthAnimatorSet.play(mHandleViewController.createRevealAnimToIsStashed(isStashed));
+
+ fullLengthAnimatorSet.setDuration(duration);
+ firstHalfAnimatorSet.setDuration((long) (duration * firstHalfDurationScale));
+ secondHalfAnimatorSet.setDuration((long) (duration * secondHalfDurationScale));
+ secondHalfAnimatorSet.setStartDelay((long) (duration * (1 - secondHalfDurationScale)));
+
+ animatorSet.playTogether(fullLengthAnimatorSet, firstHalfAnimatorSet,
+ secondHalfAnimatorSet);
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimator = null;
+ mControllers.runAfterInit(() -> {
+ if (isStashed) {
+ mBarViewController.setExpanded(false);
+ }
+ });
+ }
+ });
+ return animatorSet;
+ }
+
+ private void onIsStashedChanged() {
+ mControllers.runAfterInit(() -> {
+ mHandleViewController.onIsStashedChanged();
+ // TODO: when stash changes tell taskbarInsetsController the insets have changed.
+ });
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
new file mode 100644
index 0000000..2170a5d
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
@@ -0,0 +1,262 @@
+/*
+ * 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.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.res.Resources;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+
+import com.android.launcher3.R;
+import com.android.launcher3.anim.RevealOutlineAnimation;
+import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
+import com.android.launcher3.taskbar.StashedHandleView;
+import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.launcher3.taskbar.TaskbarControllers;
+import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.MultiPropertyFactory;
+import com.android.launcher3.util.MultiValueAlpha;
+import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
+
+/**
+ * Handles properties/data collection, then passes the results to our stashed handle View to render.
+ */
+public class BubbleStashedHandleViewController {
+
+ private final TaskbarActivityContext mActivity;
+ private final StashedHandleView mStashedHandleView;
+ private final MultiValueAlpha mTaskbarStashedHandleAlpha;
+
+ // Initialized in init.
+ private BubbleBarViewController mBarViewController;
+ private BubbleStashController mBubbleStashController;
+ private RegionSamplingHelper mRegionSamplingHelper;
+ private int mBarSize;
+ private int mStashedHandleWidth;
+ private int mStashedHandleHeight;
+
+ // The bounds we want to clip to in the settled state when showing the stashed handle.
+ private final Rect mStashedHandleBounds = new Rect();
+
+ // When the reveal animation is cancelled, we can assume it's about to create a new animation,
+ // which should start off at the same point the cancelled one left off.
+ private float mStartProgressForNextRevealAnim;
+ private boolean mWasLastRevealAnimReversed;
+
+ // XXX: if there are more of these maybe do state flags instead
+ private boolean mHiddenForSysui;
+ private boolean mHiddenForNoBubbles;
+ private boolean mHiddenForHomeButtonDisabled;
+
+ public BubbleStashedHandleViewController(TaskbarActivityContext activity,
+ StashedHandleView stashedHandleView) {
+ mActivity = activity;
+ mStashedHandleView = stashedHandleView;
+ mTaskbarStashedHandleAlpha = new MultiValueAlpha(mStashedHandleView, 1);
+ }
+
+ public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) {
+ mBarViewController = bubbleControllers.bubbleBarViewController;
+ mBubbleStashController = bubbleControllers.bubbleStashController;
+
+ Resources resources = mActivity.getResources();
+ mStashedHandleHeight = resources.getDimensionPixelSize(
+ R.dimen.bubblebar_stashed_handle_height);
+ mStashedHandleWidth = resources.getDimensionPixelSize(
+ R.dimen.bubblebar_stashed_handle_width);
+ mBarSize = resources.getDimensionPixelSize(R.dimen.bubblebar_size);
+
+ final int bottomMargin = resources.getDimensionPixelSize(
+ R.dimen.transient_taskbar_bottom_margin);
+ mStashedHandleView.getLayoutParams().height = mBarSize + bottomMargin;
+
+ mTaskbarStashedHandleAlpha.get(0).setValue(0);
+
+ final int stashedTaskbarHeight = resources.getDimensionPixelSize(
+ R.dimen.bubblebar_stashed_size);
+ mStashedHandleView.setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ float stashedHandleRadius = view.getHeight() / 2f;
+ outline.setRoundRect(mStashedHandleBounds, stashedHandleRadius);
+ }
+ });
+
+ mRegionSamplingHelper = new RegionSamplingHelper(mStashedHandleView,
+ new RegionSamplingHelper.SamplingCallback() {
+ @Override
+ public void onRegionDarknessChanged(boolean isRegionDark) {
+ mStashedHandleView.updateHandleColor(isRegionDark, true /* animate */);
+ }
+
+ @Override
+ public Rect getSampledRegion(View sampledView) {
+ return mStashedHandleView.getSampledRegion();
+ }
+ }, Executors.UI_HELPER_EXECUTOR);
+
+ mStashedHandleView.addOnLayoutChangeListener((view, i, i1, i2, i3, i4, i5, i6, i7) -> {
+ // As more bubbles get added, the icon bounds become larger. To ensure a consistent
+ // handle bar position, we pin it to the edge of the screen.
+ Rect bubblebarRect = mBarViewController.getBubbleBarBounds();
+ final int stashedCenterY = view.getHeight() - stashedTaskbarHeight / 2;
+
+ mStashedHandleBounds.set(
+ bubblebarRect.right - mStashedHandleWidth,
+ stashedCenterY - mStashedHandleHeight / 2,
+ bubblebarRect.right,
+ stashedCenterY + mStashedHandleHeight / 2);
+ mStashedHandleView.updateSampledRegion(mStashedHandleBounds);
+
+ view.setPivotX(view.getWidth());
+ view.setPivotY(view.getHeight() - stashedTaskbarHeight / 2f);
+ });
+ }
+
+ public void onDestroy() {
+ mRegionSamplingHelper.stopAndDestroy();
+ mRegionSamplingHelper = null;
+ }
+
+ /**
+ * Returns the height of the stashed handle.
+ */
+ public int getStashedHeight() {
+ return mStashedHandleHeight;
+ }
+
+ /**
+ * Returns the height when the bubble bar is unstashed (so the height of the bubble bar).
+ */
+ public int getUnstashedHeight() {
+ return mBarSize;
+ }
+
+ /**
+ * Called when system ui state changes. Bubbles don't show when the device is locked.
+ */
+ public void setHiddenForSysui(boolean hidden) {
+ if (mHiddenForSysui != hidden) {
+ mHiddenForSysui = hidden;
+ updateVisibilityForStateChange();
+ }
+ }
+
+ /**
+ * Called when the handle should be hidden (or shown) because there are no bubbles
+ * (or 1+ bubbles).
+ */
+ public void setHiddenForBubbles(boolean hidden) {
+ if (mHiddenForNoBubbles != hidden) {
+ mHiddenForNoBubbles = hidden;
+ updateVisibilityForStateChange();
+ }
+ }
+
+ /**
+ * Called when the home button is enabled / disabled. Bubbles don't show if home is disabled.
+ */
+ // TODO: is this needed for bubbles?
+ public void setIsHomeButtonDisabled(boolean homeDisabled) {
+ mHiddenForHomeButtonDisabled = homeDisabled;
+ updateVisibilityForStateChange();
+ }
+
+ // TODO: (b/273592694) animate it?
+ private void updateVisibilityForStateChange() {
+ if (!mHiddenForSysui && !mHiddenForHomeButtonDisabled && !mHiddenForNoBubbles) {
+ mStashedHandleView.setVisibility(VISIBLE);
+ } else {
+ mStashedHandleView.setVisibility(INVISIBLE);
+ }
+ updateRegionSampling();
+ }
+
+ /**
+ * Called when bubble bar is stash state changes so that updates to the stashed handle color
+ * can be started or stopped.
+ */
+ public void onIsStashedChanged() {
+ updateRegionSampling();
+ }
+
+ private void updateRegionSampling() {
+ boolean handleVisible = mStashedHandleView.getVisibility() == VISIBLE
+ && mBubbleStashController.isStashed();
+ mRegionSamplingHelper.setWindowVisible(handleVisible);
+ if (handleVisible) {
+ mStashedHandleView.updateSampledRegion(mStashedHandleBounds);
+ mRegionSamplingHelper.start(mStashedHandleView.getSampledRegion());
+ } else {
+ mRegionSamplingHelper.stop();
+ }
+ }
+
+ /**
+ * Sets the translation of the stashed handle during the swipe up gesture.
+ */
+ public void setTranslationYForSwipe(float transY) {
+ mStashedHandleView.setTranslationY(transY);
+ }
+
+ /**
+ * Used by {@link BubbleStashController} to animate the handle when stashing or un stashing.
+ */
+ public MultiPropertyFactory<View> getStashedHandleAlpha() {
+ return mTaskbarStashedHandleAlpha;
+ }
+
+ /**
+ * Creates and returns an Animator that updates the stashed handle shape and size.
+ * When stashed, the shape is a thin rounded pill. When unstashed, the shape morphs into
+ * the size of where the bubble bar icons will be.
+ */
+ public Animator createRevealAnimToIsStashed(boolean isStashed) {
+ Rect bubbleBarBounds = new Rect(mBarViewController.getBubbleBarBounds());
+
+ // Account for the full visual height of the bubble bar
+ int heightDiff = (mBarSize - bubbleBarBounds.height()) / 2;
+ bubbleBarBounds.top -= heightDiff;
+ bubbleBarBounds.bottom += heightDiff;
+ float stashedHandleRadius = mStashedHandleView.getHeight() / 2f;
+ final RevealOutlineAnimation handleRevealProvider = new RoundedRectRevealOutlineProvider(
+ stashedHandleRadius, stashedHandleRadius, bubbleBarBounds, mStashedHandleBounds);
+
+ boolean isReversed = !isStashed;
+ boolean changingDirection = mWasLastRevealAnimReversed != isReversed;
+ mWasLastRevealAnimReversed = isReversed;
+ if (changingDirection) {
+ mStartProgressForNextRevealAnim = 1f - mStartProgressForNextRevealAnim;
+ }
+
+ ValueAnimator revealAnim = handleRevealProvider.createRevealAnimator(mStashedHandleView,
+ isReversed, mStartProgressForNextRevealAnim);
+ revealAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mStartProgressForNextRevealAnim = ((ValueAnimator) animation).getAnimatedFraction();
+ }
+ });
+ return revealAnim;
+ }
+}