Merge "Initial commit of taskbar stashing" into sc-v2-dev

This commit is contained in:
Tony Wickham 2021-06-21 15:56:12 +00:00 committed by Android (Google) Code Review
commit 0ac023196d
20 changed files with 654 additions and 38 deletions

View File

@ -15,6 +15,7 @@
-->
<com.android.launcher3.taskbar.TaskbarDragLayer
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/taskbar_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -39,6 +40,7 @@
android:id="@+id/start_nav_buttons"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:paddingLeft="@dimen/taskbar_nav_buttons_spacing"
android:paddingRight="@dimen/taskbar_nav_buttons_spacing"
android:gravity="center_vertical"
@ -54,4 +56,14 @@
android:layout_gravity="end"/>
</FrameLayout>
<View
android:id="@+id/stashed_handle"
tools:comment1="The actual size and shape will be set as a ViewOutlineProvider at runtime"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:comment2="TODO: Tint dynamically"
android:background="?android:attr/textColorPrimary"
android:clipToOutline="true"
android:layout_gravity="bottom"/>
</com.android.launcher3.taskbar.TaskbarDragLayer>

View File

@ -153,4 +153,7 @@
<dimen name="taskbar_folder_margin">16dp</dimen>
<dimen name="taskbar_nav_buttons_spacing">16dp</dimen>
<dimen name="taskbar_nav_buttons_size">48dp</dimen>
<dimen name="taskbar_stashed_size">24dp</dimen>
<dimen name="taskbar_stashed_handle_width">220dp</dimen>
<dimen name="taskbar_stashed_handle_height">6dp</dimen>
</resources>

View File

@ -50,21 +50,23 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
private final TaskbarHotseatController mHotseatController;
private final TaskbarActivityContext mContext;
final TaskbarDragLayer mTaskbarDragLayer;
final TaskbarView mTaskbarView;
private final TaskbarDragLayer mTaskbarDragLayer;
private final TaskbarView mTaskbarView;
private final AnimatedFloat mIconAlignmentForResumedState =
new AnimatedFloat(this::onIconAlignmentRatioChanged);
private final AnimatedFloat mIconAlignmentForGestureState =
new AnimatedFloat(this::onIconAlignmentRatioChanged);
// Initialized in init.
private TaskbarControllers mControllers;
private AnimatedFloat mTaskbarBackgroundAlpha;
private AlphaProperty mIconAlphaForHome;
private boolean mIsAnimatingToLauncher;
private boolean mIsAnimatingToLauncherViaResume;
private boolean mIsAnimatingToLauncherViaGesture;
private TaskbarKeyguardController mKeyguardController;
private LauncherState mTargetStateOverride = null;
private TaskbarControllers mControllers;
public LauncherTaskbarUIController(
BaseQuickstepLauncher launcher, TaskbarActivityContext context) {
@ -80,13 +82,14 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
@Override
protected void init(TaskbarControllers taskbarControllers) {
mTaskbarBackgroundAlpha = taskbarControllers.taskbarDragLayerController
.getTaskbarBackgroundAlpha();
MultiValueAlpha taskbarIconAlpha = taskbarControllers.taskbarViewController
.getTaskbarIconAlpha();
mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME);
mControllers = taskbarControllers;
mTaskbarBackgroundAlpha = mControllers.taskbarDragLayerController
.getTaskbarBackgroundAlpha();
MultiValueAlpha taskbarIconAlpha = mControllers.taskbarViewController.getTaskbarIconAlpha();
mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME);
mHotseatController.init();
mLauncher.setTaskbarUIController(this);
mKeyguardController = taskbarControllers.taskbarKeyguardController;
@ -109,19 +112,17 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
@Override
protected boolean isTaskbarTouchable() {
return !mIsAnimatingToLauncher && mTargetStateOverride == null;
return !isAnimatingToLauncher() && !mControllers.taskbarStashController.isStashed();
}
private boolean isAnimatingToLauncher() {
return mIsAnimatingToLauncherViaResume || mIsAnimatingToLauncherViaGesture;
}
@Override
protected void updateContentInsets(Rect outContentInsets) {
// TaskbarDragLayer provides insets to other apps based on contentInsets. These
// insets should stay consistent even if we expand TaskbarDragLayer's bounds, e.g.
// to show a floating view like Folder. Thus, we set the contentInsets to be where
// mTaskbarView is, since its position never changes and insets rather than overlays.
outContentInsets.left = mTaskbarView.getLeft();
outContentInsets.top = mTaskbarView.getTop();
outContentInsets.right = mTaskbarDragLayer.getWidth() - mTaskbarView.getRight();
outContentInsets.bottom = mTaskbarDragLayer.getHeight() - mTaskbarView.getBottom();
int contentHeight = mControllers.taskbarStashController.getContentHeight();
outContentInsets.top = mTaskbarDragLayer.getHeight() - contentHeight;
}
/**
@ -137,13 +138,20 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
}
}
long duration = QuickstepTransitionManager.CONTENT_ALPHA_DURATION;
ObjectAnimator anim = mIconAlignmentForResumedState.animateToValue(
getCurrentIconAlignmentRatio(), isResumed ? 1 : 0)
.setDuration(QuickstepTransitionManager.CONTENT_ALPHA_DURATION);
.setDuration(duration);
anim.addListener(AnimatorListeners.forEndCallback(() -> mIsAnimatingToLauncher = false));
anim.addListener(AnimatorListeners.forEndCallback(
() -> mIsAnimatingToLauncherViaResume = false));
anim.start();
mIsAnimatingToLauncher = isResumed;
mIsAnimatingToLauncherViaResume = isResumed;
if (!isResumed) {
TaskbarStashController stashController = mControllers.taskbarStashController;
stashController.animateToIsStashed(stashController.isStashedInApp(), duration);
}
}
/**
@ -155,36 +163,48 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
public Animator createAnimToLauncher(@NonNull LauncherState toState,
@NonNull RecentsAnimationCallbacks callbacks,
long duration) {
TaskbarStashController stashController = mControllers.taskbarStashController;
ObjectAnimator animator = mIconAlignmentForGestureState
.animateToValue(mIconAlignmentForGestureState.value, 1)
.animateToValue(1)
.setDuration(duration);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mTargetStateOverride = null;
animator.removeListener(this);
}
@Override
public void onAnimationStart(Animator animation) {
mTargetStateOverride = toState;
mIsAnimatingToLauncherViaGesture = true;
// TODO: base this on launcher state
stashController.animateToIsStashed(false, duration);
}
});
callbacks.addListener(new RecentsAnimationListener() {
@Override
public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
endGestureStateOverride();
endGestureStateOverride(true);
}
@Override
public void onRecentsAnimationFinished(RecentsAnimationController controller) {
endGestureStateOverride();
endGestureStateOverride(!controller.getFinishTargetIsLauncher());
}
private void endGestureStateOverride() {
private void endGestureStateOverride(boolean finishedToApp) {
callbacks.removeListener(this);
mIsAnimatingToLauncherViaGesture = false;
mIconAlignmentForGestureState
.animateToValue(mIconAlignmentForGestureState.value, 0)
.animateToValue(0)
.start();
if (finishedToApp) {
// We only need this for the exiting live tile case.
stashController.animateToIsStashed(stashController.isStashedInApp());
}
}
});
return animator;
@ -215,6 +235,11 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
}
}
@Override
public boolean onLongPressToUnstashTaskbar() {
return mControllers.taskbarStashController.onLongPressToUnstashTaskbar();
}
/**
* Should be called when one or more items in the Hotseat have changed.
*/

View File

@ -0,0 +1,108 @@
/*
* Copyright (C) 2021 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;
import android.animation.Animator;
import android.content.res.Resources;
import android.graphics.Outline;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewOutlineProvider;
import androidx.annotation.Nullable;
import com.android.launcher3.R;
import com.android.launcher3.anim.RevealOutlineAnimation;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.quickstep.AnimatedFloat;
/**
* Handles properties/data collection, then passes the results to our stashed handle View to render.
*/
public class StashedHandleViewController {
private final TaskbarActivityContext mActivity;
private final View mStashedHandleView;
private final int mStashedHandleWidth;
private final int mStashedHandleHeight;
private final AnimatedFloat mTaskbarStashedHandleAlpha = new AnimatedFloat(
this::updateStashedHandleAlpha);
// Initialized in init.
private TaskbarControllers mControllers;
// The bounds we want to clip to in the settled state when showing the stashed handle.
private final Rect mStashedHandleBounds = new Rect();
private float mStashedHandleRadius;
private boolean mIsAtStashedRevealBounds = true;
public StashedHandleViewController(TaskbarActivityContext activity, View stashedHandleView) {
mActivity = activity;
mStashedHandleView = stashedHandleView;
final Resources resources = mActivity.getResources();
mStashedHandleWidth = resources.getDimensionPixelSize(R.dimen.taskbar_stashed_handle_width);
mStashedHandleHeight = resources.getDimensionPixelSize(
R.dimen.taskbar_stashed_handle_height);
}
public void init(TaskbarControllers controllers) {
mControllers = controllers;
mStashedHandleView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarSize;
updateStashedHandleAlpha();
final int stashedTaskbarHeight = mControllers.taskbarStashController.getStashedHeight();
mStashedHandleView.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
final int stashedCenterX = view.getWidth() / 2;
final int stashedCenterY = view.getHeight() - stashedTaskbarHeight / 2;
mStashedHandleBounds.set(
stashedCenterX - mStashedHandleWidth / 2,
stashedCenterY - mStashedHandleHeight / 2,
stashedCenterX + mStashedHandleWidth / 2,
stashedCenterY + mStashedHandleHeight / 2);
mStashedHandleRadius = view.getHeight() / 2f;
outline.setRoundRect(mStashedHandleBounds, mStashedHandleRadius);
}
});
}
public AnimatedFloat getStashedHandleAlpha() {
return mTaskbarStashedHandleAlpha;
}
/**
* Creates and returns a {@link RevealOutlineAnimation} 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 taskbar icons will be.
*/
public @Nullable Animator createRevealAnimToIsStashed(boolean isStashed) {
if (mIsAtStashedRevealBounds == isStashed) {
return null;
}
mIsAtStashedRevealBounds = isStashed;
final RevealOutlineAnimation handleRevealProvider = new RoundedRectRevealOutlineProvider(
mStashedHandleRadius, mStashedHandleRadius,
mControllers.taskbarViewController.getIconLayoutBounds(), mStashedHandleBounds);
return handleRevealProvider.createRevealAnimator(mStashedHandleView, !isStashed);
}
protected void updateStashedHandleAlpha() {
mStashedHandleView.setAlpha(mTaskbarStashedHandleAlpha.value);
}
}

View File

@ -115,6 +115,7 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ
R.layout.taskbar, null, false);
TaskbarView taskbarView = mDragLayer.findViewById(R.id.taskbar_view);
FrameLayout navButtonsView = mDragLayer.findViewById(R.id.navbuttons_view);
View stashedHandleView = mDragLayer.findViewById(R.id.stashed_handle);
// Construct controllers.
mControllers = new TaskbarControllers(this,
@ -125,7 +126,9 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ
R.color.popup_color_primary_light),
new TaskbarDragLayerController(this, mDragLayer),
new TaskbarViewController(this, taskbarView),
new TaskbarKeyguardController(this));
new TaskbarKeyguardController(this),
new StashedHandleViewController(this, stashedHandleView),
new TaskbarStashController(this));
Display display = windowContext.getDisplay();
Context c = display.getDisplayId() == Display.DEFAULT_DISPLAY

View File

@ -32,6 +32,8 @@ public class TaskbarControllers {
public final TaskbarDragLayerController taskbarDragLayerController;
public final TaskbarViewController taskbarViewController;
public final TaskbarKeyguardController taskbarKeyguardController;
public final StashedHandleViewController stashedHandleViewController;
public final TaskbarStashController taskbarStashController;
/** Do not store this controller, as it may change at runtime. */
@NonNull public TaskbarUIController uiController = TaskbarUIController.DEFAULT;
@ -43,7 +45,9 @@ public class TaskbarControllers {
RotationButtonController rotationButtonController,
TaskbarDragLayerController taskbarDragLayerController,
TaskbarViewController taskbarViewController,
TaskbarKeyguardController taskbarKeyguardController) {
TaskbarKeyguardController taskbarKeyguardController,
StashedHandleViewController stashedHandleViewController,
TaskbarStashController taskbarStashController) {
this.taskbarActivityContext = taskbarActivityContext;
this.taskbarDragController = taskbarDragController;
this.navButtonController = navButtonController;
@ -52,6 +56,8 @@ public class TaskbarControllers {
this.taskbarDragLayerController = taskbarDragLayerController;
this.taskbarViewController = taskbarViewController;
this.taskbarKeyguardController = taskbarKeyguardController;
this.stashedHandleViewController = stashedHandleViewController;
this.taskbarStashController = taskbarStashController;
}
/**
@ -67,6 +73,8 @@ public class TaskbarControllers {
taskbarDragLayerController.init(this);
taskbarViewController.init(this);
taskbarKeyguardController.init(navbarButtonsViewController);
stashedHandleViewController.init(this);
taskbarStashController.init(this);
}
/**

View File

@ -40,10 +40,11 @@ import com.android.systemui.shared.system.ViewTreeObserverWrapper.OnComputeInset
public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> {
private final Paint mTaskbarBackgroundPaint;
private final OnComputeInsetsListener mTaskbarInsetsComputer = this::onComputeTaskbarInsets;
private TaskbarDragLayerController.TaskbarDragLayerCallbacks mControllerCallbacks;
private final OnComputeInsetsListener mTaskbarInsetsComputer = this::onComputeTaskbarInsets;
private float mTaskbarBackgroundOffset;
public TaskbarDragLayer(@NonNull Context context) {
this(context, null);
@ -118,8 +119,10 @@ public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> {
@Override
protected void dispatchDraw(Canvas canvas) {
canvas.drawRect(0, canvas.getHeight() - mControllerCallbacks.getTaskbarBackgroundHeight(),
canvas.getWidth(), canvas.getHeight(), mTaskbarBackgroundPaint);
float backgroundHeight = mControllerCallbacks.getTaskbarBackgroundHeight()
* (1f - mTaskbarBackgroundOffset);
canvas.drawRect(0, canvas.getHeight() - backgroundHeight, canvas.getWidth(),
canvas.getHeight(), mTaskbarBackgroundPaint);
super.dispatchDraw(canvas);
}
@ -132,6 +135,15 @@ public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> {
invalidate();
}
/**
* Sets the translation of the background color behind all the Taskbar contents.
* @param offset 0 is fully onscreen, 1 is fully offscreen.
*/
protected void setTaskbarBackgroundOffset(float offset) {
mTaskbarBackgroundOffset = offset;
invalidate();
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);

View File

@ -39,6 +39,8 @@ public class TaskbarDragLayerController {
// Alpha properties for taskbar background.
private final AnimatedFloat mBgTaskbar = new AnimatedFloat(this::updateBackgroundAlpha);
private final AnimatedFloat mBgNavbar = new AnimatedFloat(this::updateBackgroundAlpha);
// Translation property for taskbar background.
private final AnimatedFloat mBgOffset = new AnimatedFloat(this::updateBackgroundOffset);
// Initialized in init.
private TaskbarControllers mControllers;
@ -78,10 +80,18 @@ public class TaskbarDragLayerController {
return mBgNavbar;
}
public AnimatedFloat getTaskbarBackgroundOffset() {
return mBgOffset;
}
private void updateBackgroundAlpha() {
mTaskbarDragLayer.setTaskbarBackgroundAlpha(Math.max(mBgNavbar.value, mBgTaskbar.value));
}
private void updateBackgroundOffset() {
mTaskbarDragLayer.setTaskbarBackgroundOffset(mBgOffset.value);
}
/**
* Callbacks for {@link TaskbarDragLayer} to interact with its controller.
*/

View File

@ -0,0 +1,262 @@
/*
* Copyright (C) 2021 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;
import static android.view.HapticFeedbackConstants.LONG_PRESS;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.annotation.Nullable;
import android.content.SharedPreferences;
import android.content.res.Resources;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.quickstep.AnimatedFloat;
/**
* Coordinates between controllers such as TaskbarViewController and StashedHandleViewController to
* create a cohesive animation between stashed/unstashed states.
*/
public class TaskbarStashController {
/**
* How long to stash/unstash when manually invoked via long press.
*/
private static final long TASKBAR_STASH_DURATION = 300;
/**
* The scale TaskbarView animates to when being stashed.
*/
private static final float STASHED_TASKBAR_SCALE = 0.5f;
/**
* The SharedPreferences key for whether user has manually stashed the taskbar.
*/
private static final String SHARED_PREFS_STASHED_KEY = "taskbar_is_stashed";
/**
* Whether taskbar should be stashed out of the box.
*/
private static final boolean DEFAULT_STASHED_PREF = false;
private final TaskbarActivityContext mActivity;
private final SharedPreferences mPrefs;
private final int mStashedHeight;
private final int mUnstashedHeight;
// Initialized in init.
private TaskbarControllers mControllers;
// Taskbar background properties.
private AnimatedFloat mTaskbarBackgroundOffset;
// TaskbarView icon properties.
private AlphaProperty mIconAlphaForStash;
private AnimatedFloat mIconScaleForStash;
private AnimatedFloat mIconTranslationYForStash;
// Stashed handle properties.
private AnimatedFloat mTaskbarStashedHandleAlpha;
/** Whether the user has manually invoked taskbar stashing, which we persist. */
private boolean mIsStashedInApp;
/** Whether we are currently visually stashed (might change based on launcher state). */
private boolean mIsStashed = false;
private @Nullable AnimatorSet mAnimator;
public TaskbarStashController(TaskbarActivityContext activity) {
mActivity = activity;
mPrefs = Utilities.getPrefs(mActivity);
final Resources resources = mActivity.getResources();
mStashedHeight = resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
mUnstashedHeight = mActivity.getDeviceProfile().taskbarSize;
}
public void init(TaskbarControllers controllers) {
mControllers = controllers;
TaskbarDragLayerController dragLayerController = controllers.taskbarDragLayerController;
mTaskbarBackgroundOffset = dragLayerController.getTaskbarBackgroundOffset();
TaskbarViewController taskbarViewController = controllers.taskbarViewController;
mIconAlphaForStash = taskbarViewController.getTaskbarIconAlpha().getProperty(
TaskbarViewController.ALPHA_INDEX_STASH);
mIconScaleForStash = taskbarViewController.getTaskbarIconScaleForStash();
mIconTranslationYForStash = taskbarViewController.getTaskbarIconTranslationYForStash();
StashedHandleViewController stashedHandleController =
controllers.stashedHandleViewController;
mTaskbarStashedHandleAlpha = stashedHandleController.getStashedHandleAlpha();
mIsStashedInApp = supportsStashing()
&& mPrefs.getBoolean(SHARED_PREFS_STASHED_KEY, DEFAULT_STASHED_PREF);
}
/**
* Returns whether the user can manually stash the taskbar based on the current device state.
*/
private boolean supportsStashing() {
return !mActivity.isThreeButtonNav();
}
/**
* Returns whether the taskbar is currently visually stashed.
*/
public boolean isStashed() {
return mIsStashed;
}
/**
* Returns whether the user has manually stashed the taskbar in apps.
*/
public boolean isStashedInApp() {
return mIsStashedInApp;
}
public int getContentHeight() {
return isStashed() ? mStashedHeight : mUnstashedHeight;
}
public int getStashedHeight() {
return mStashedHeight;
}
/**
* Should be called when long pressing the nav region when taskbar is present.
* @return Whether taskbar was stashed and now is unstashed.
*/
public boolean onLongPressToUnstashTaskbar() {
if (!isStashed()) {
// We only listen for long press on the nav region to unstash the taskbar. To stash the
// taskbar, we use an OnLongClickListener on TaskbarView instead.
return false;
}
if (updateAndAnimateIsStashedInApp(false)) {
mControllers.taskbarActivityContext.getDragLayer().performHapticFeedback(LONG_PRESS);
return true;
}
return false;
}
/**
* Updates whether we should stash the taskbar when in apps, and animates to the changed state.
* @return Whether we started an animation to either be newly stashed or unstashed.
*/
public boolean updateAndAnimateIsStashedInApp(boolean isStashedInApp) {
if (!supportsStashing()) {
return false;
}
if (mIsStashedInApp != isStashedInApp) {
boolean wasStashed = mIsStashedInApp;
mIsStashedInApp = isStashedInApp;
mPrefs.edit().putBoolean(SHARED_PREFS_STASHED_KEY, mIsStashedInApp).apply();
boolean isStashed = mIsStashedInApp;
if (wasStashed != isStashed) {
createAnimToIsStashed(isStashed, TASKBAR_STASH_DURATION).start();
return true;
}
}
return false;
}
/**
* Starts an animation to the new stashed state with a default duration.
*/
public void animateToIsStashed(boolean isStashed) {
animateToIsStashed(isStashed, TASKBAR_STASH_DURATION);
}
/**
* Starts an animation to the new stashed state with the specified duration.
*/
public void animateToIsStashed(boolean isStashed, long duration) {
createAnimToIsStashed(isStashed, duration).start();
}
private Animator createAnimToIsStashed(boolean isStashed, long duration) {
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;
final float stashTranslation = (mUnstashedHeight - mStashedHeight) / 2f;
fullLengthAnimatorSet.playTogether(
mTaskbarBackgroundOffset.animateToValue(1),
mIconTranslationYForStash.animateToValue(stashTranslation)
);
firstHalfAnimatorSet.playTogether(
mIconAlphaForStash.animateToValue(0),
mIconScaleForStash.animateToValue(STASHED_TASKBAR_SCALE)
);
secondHalfAnimatorSet.playTogether(
mTaskbarStashedHandleAlpha.animateToValue(1)
);
} else {
firstHalfDurationScale = 0.5f;
secondHalfDurationScale = 0.75f;
fullLengthAnimatorSet.playTogether(
mTaskbarBackgroundOffset.animateToValue(0),
mIconScaleForStash.animateToValue(1),
mIconTranslationYForStash.animateToValue(0)
);
firstHalfAnimatorSet.playTogether(
mTaskbarStashedHandleAlpha.animateToValue(0)
);
secondHalfAnimatorSet.playTogether(
mIconAlphaForStash.animateToValue(1)
);
}
Animator stashedHandleRevealAnim = mControllers.stashedHandleViewController
.createRevealAnimToIsStashed(isStashed);
if (stashedHandleRevealAnim != null) {
fullLengthAnimatorSet.play(stashedHandleRevealAnim);
}
fullLengthAnimatorSet.setDuration(duration);
firstHalfAnimatorSet.setDuration((long) (duration * firstHalfDurationScale));
secondHalfAnimatorSet.setDuration((long) (duration * secondHalfDurationScale));
secondHalfAnimatorSet.setStartDelay((long) (duration * (1 - secondHalfDurationScale)));
if (mAnimator != null) {
mAnimator.cancel();
}
mAnimator = new AnimatorSet();
mAnimator.playTogether(fullLengthAnimatorSet, firstHalfAnimatorSet,
secondHalfAnimatorSet);
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mIsStashed = isStashed;
}
@Override
public void onAnimationEnd(Animator animation) {
mAnimator = null;
}
});
return mAnimator;
}
}

View File

@ -33,4 +33,8 @@ public class TaskbarUIController {
}
protected void updateContentInsets(Rect outContentInsets) { }
protected boolean onLongPressToUnstashTaskbar() {
return false;
}
}

View File

@ -94,8 +94,10 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
protected void init(TaskbarViewController.TaskbarViewCallbacks callbacks) {
mControllerCallbacks = callbacks;
mIconClickListener = mControllerCallbacks.getOnClickListener();
mIconLongClickListener = mControllerCallbacks.getOnLongClickListener();
mIconClickListener = mControllerCallbacks.getIconOnClickListener();
mIconLongClickListener = mControllerCallbacks.getIconOnLongClickListener();
setOnLongClickListener(mControllerCallbacks.getBackgroundOnLongClickListener());
}
private void removeAndRecycle(View view) {
@ -235,6 +237,10 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
return isShown() && mIconLayoutBounds.contains(xInOurCoordinates, yInOurCoorindates);
}
public Rect getIconLayoutBounds() {
return mIconLayoutBounds;
}
// FolderIconParent implemented methods.
@Override

View File

@ -17,8 +17,8 @@ package com.android.launcher3.taskbar;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.quickstep.AnimatedFloat.VALUE;
import android.graphics.Rect;
import android.view.View;
@ -28,6 +28,7 @@ import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.AnimatedFloat;
/**
* Handles properties/data collection, then passes the results to TaskbarView to render.
@ -38,10 +39,16 @@ public class TaskbarViewController {
public static final int ALPHA_INDEX_HOME = 0;
public static final int ALPHA_INDEX_IME = 1;
public static final int ALPHA_INDEX_KEYGUARD = 2;
public static final int ALPHA_INDEX_STASH = 3;
private final TaskbarActivityContext mActivity;
private final TaskbarView mTaskbarView;
private final MultiValueAlpha mTaskbarIconAlpha;
private final AnimatedFloat mTaskbarIconScaleForStash = new AnimatedFloat(this::updateScale);
private final AnimatedFloat mTaskbarIconTranslationYForHome = new AnimatedFloat(
this::updateTranslationY);
private final AnimatedFloat mTaskbarIconTranslationYForStash = new AnimatedFloat(
this::updateTranslationY);
// Initialized in init.
private TaskbarControllers mControllers;
@ -54,7 +61,7 @@ public class TaskbarViewController {
public TaskbarViewController(TaskbarActivityContext activity, TaskbarView taskbarView) {
mActivity = activity;
mTaskbarView = taskbarView;
mTaskbarIconAlpha = new MultiValueAlpha(mTaskbarView, 3);
mTaskbarIconAlpha = new MultiValueAlpha(mTaskbarView, 4);
mTaskbarIconAlpha.setUpdateVisibility(true);
}
@ -62,6 +69,8 @@ public class TaskbarViewController {
mControllers = controllers;
mTaskbarView.init(new TaskbarViewCallbacks());
mTaskbarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarSize;
mTaskbarIconScaleForStash.updateValue(1f);
}
public boolean areIconsVisible() {
@ -86,6 +95,32 @@ public class TaskbarViewController {
mTaskbarView.setClickAndLongClickListenersForIcon(icon);
}
public Rect getIconLayoutBounds() {
return mTaskbarView.getIconLayoutBounds();
}
public AnimatedFloat getTaskbarIconScaleForStash() {
return mTaskbarIconScaleForStash;
}
public AnimatedFloat getTaskbarIconTranslationYForStash() {
return mTaskbarIconTranslationYForStash;
}
/**
* Applies scale properties for the entire TaskbarView (rather than individual icons).
*/
private void updateScale() {
float scale = mTaskbarIconScaleForStash.value;
mTaskbarView.setScaleX(scale);
mTaskbarView.setScaleY(scale);
}
private void updateTranslationY() {
mTaskbarView.setTranslationY(mTaskbarIconTranslationYForHome.value
+ mTaskbarIconTranslationYForStash.value);
}
/**
* Sets the taskbar icon alignment relative to Launcher hotseat icons
* @param alignmentRatio [0, 1]
@ -116,7 +151,7 @@ public class TaskbarViewController {
/ launcherDp.numShownHotseatIcons;
int offsetY = launcherDp.getTaskbarOffsetY();
setter.setFloat(mTaskbarView, VIEW_TRANSLATE_Y, -offsetY, LINEAR);
setter.setFloat(mTaskbarIconTranslationYForHome, VALUE, -offsetY, LINEAR);
int collapsedHeight = mActivity.getDeviceProfile().taskbarSize;
int expandedHeight = collapsedHeight + offsetY;
@ -144,12 +179,16 @@ public class TaskbarViewController {
* Callbacks for {@link TaskbarView} to interact with its controller.
*/
public class TaskbarViewCallbacks {
public View.OnClickListener getOnClickListener() {
public View.OnClickListener getIconOnClickListener() {
return mActivity::onTaskbarIconClicked;
}
public View.OnLongClickListener getOnLongClickListener() {
public View.OnLongClickListener getIconOnLongClickListener() {
return mControllers.taskbarDragController::startDragOnLongClick;
}
public View.OnLongClickListener getBackgroundOnLongClickListener() {
return view -> mControllers.taskbarStashController.updateAndAnimateIsStashedInApp(true);
}
}
}

View File

@ -53,6 +53,16 @@ public class AnimatedFloat {
mUpdateCallback = updateCallback;
}
/**
* Returns an animation from the current value to the given value.
*/
public ObjectAnimator animateToValue(float end) {
return animateToValue(value, end);
}
/**
* Returns an animation from the given start value to the given end value.
*/
public ObjectAnimator animateToValue(float start, float end) {
cancelAnimation();
mValueAnimator = ObjectAnimator.ofFloat(this, VALUE, start, end);

View File

@ -363,6 +363,14 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
return null;
}
/**
* Called when we detect a long press in the nav region before passing the gesture slop.
* @return Whether taskbar handled the long press, and thus should cancel the gesture.
*/
public boolean onLongPressToUnstashTaskbar() {
return false;
}
/**
* Returns the color of the scrim behind overview when at rest in this state.
* Return {@link Color#TRANSPARENT} for no scrim.

View File

@ -39,6 +39,7 @@ public interface InputConsumer {
int TYPE_OVERSCROLL = 1 << 9;
int TYPE_SYSUI_OVERLAY = 1 << 10;
int TYPE_ONE_HANDED = 1 << 11;
int TYPE_TASKBAR_STASH = 1 << 12;
String[] NAMES = new String[] {
"TYPE_NO_OP", // 0
@ -53,6 +54,7 @@ public interface InputConsumer {
"TYPE_OVERSCROLL", // 9
"TYPE_SYSUI_OVERLAY", // 10
"TYPE_ONE_HANDED", // 11
"TYPE_TASKBAR_STASH", // 12
};
InputConsumer NO_OP = () -> TYPE_NO_OP;

View File

@ -302,6 +302,15 @@ public final class LauncherActivityInterface extends
}
}
@Override
public boolean onLongPressToUnstashTaskbar() {
LauncherTaskbarUIController taskbarController = getTaskbarController();
if (taskbarController == null) {
return super.onLongPressToUnstashTaskbar();
}
return taskbarController.onLongPressToUnstashTaskbar();
}
@Override
protected int getOverviewScrimColorForState(BaseQuickstepLauncher launcher,
LauncherState state) {

View File

@ -46,6 +46,8 @@ public class RecentsAnimationController {
private boolean mUseLauncherSysBarFlags = false;
private boolean mSplitScreenMinimized = false;
private boolean mFinishRequested = false;
// Only valid when mFinishRequested == true.
private boolean mFinishTargetIsLauncher;
private RunnableList mPendingFinishCallbacks = new RunnableList();
public RecentsAnimationController(RecentsAnimationControllerCompat controller,
@ -145,6 +147,7 @@ public class RecentsAnimationController {
// Finish not yet requested
mFinishRequested = true;
mFinishTargetIsLauncher = toRecents;
mOnFinishedListener.accept(this);
mPendingFinishCallbacks.add(callback);
UI_HELPER_EXECUTOR.execute(() -> {
@ -201,4 +204,12 @@ public class RecentsAnimationController {
public RecentsAnimationControllerCompat getController() {
return mController;
}
/**
* RecentsAnimationListeners can check this in onRecentsAnimationFinished() to determine whether
* the animation was finished to launcher vs an app.
*/
public boolean getFinishTargetIsLauncher() {
return mFinishTargetIsLauncher;
}
}

View File

@ -95,6 +95,7 @@ import com.android.quickstep.inputconsumers.OverviewWithoutFocusInputConsumer;
import com.android.quickstep.inputconsumers.ResetGestureInputConsumer;
import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer;
import com.android.quickstep.inputconsumers.SysUiOverlayInputConsumer;
import com.android.quickstep.inputconsumers.TaskbarStashInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.AssistantUtilities;
import com.android.quickstep.util.ProtoTracer;
@ -673,6 +674,14 @@ public class TouchInteractionService extends Service implements PluginListener<O
mDeviceState, event);
}
// If Taskbar is present, we listen for long press to unstash it.
BaseActivityInterface activityInterface = newGestureState.getActivityInterface();
StatefulActivity activity = activityInterface.getCreatedActivity();
if (activity != null && activity.getDeviceProfile().isTaskbarPresent) {
base = new TaskbarStashInputConsumer(this, base, mInputMonitorCompat,
activityInterface);
}
if (FeatureFlags.ENABLE_QUICK_CAPTURE_GESTURE.get()) {
OverscrollPlugin plugin = null;
if (FeatureFlags.FORCE_LOCAL_OVERSCROLL_PLUGIN.get()) {

View File

@ -0,0 +1,66 @@
/*
* Copyright (C) 2021 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.quickstep.inputconsumers;
import android.content.Context;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.InputConsumer;
import com.android.systemui.shared.system.InputMonitorCompat;
/**
* Listens for a long press, and cancels the current gesture if that causes Taskbar to be unstashed.
*/
public class TaskbarStashInputConsumer extends DelegateInputConsumer {
private final BaseActivityInterface mActivityInterface;
private final GestureDetector mLongPressDetector;
public TaskbarStashInputConsumer(Context context, InputConsumer delegate,
InputMonitorCompat inputMonitor, BaseActivityInterface activityInterface) {
super(delegate, inputMonitor);
mActivityInterface = activityInterface;
mLongPressDetector = new GestureDetector(context, new SimpleOnGestureListener() {
@Override
public void onLongPress(MotionEvent motionEvent) {
onLongPressDetected(motionEvent);
}
});
}
@Override
public int getType() {
return TYPE_TASKBAR_STASH | mDelegate.getType();
}
@Override
public void onMotionEvent(MotionEvent ev) {
mLongPressDetector.onTouchEvent(ev);
if (mState != STATE_ACTIVE) {
mDelegate.onMotionEvent(ev);
}
}
private void onLongPressDetected(MotionEvent motionEvent) {
if (mActivityInterface.onLongPressToUnstashTaskbar()) {
setActive(motionEvent);
}
}
}

View File

@ -16,6 +16,8 @@
package com.android.launcher3.util;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.util.FloatProperty;
import android.view.View;
@ -121,5 +123,12 @@ public class MultiValueAlpha {
public String toString() {
return Float.toString(mValue);
}
/**
* Creates and returns an Animator from the current value to the given value.
*/
public Animator animateToValue(float value) {
return ObjectAnimator.ofFloat(this, VALUE, value);
}
}
}