Merging from ub-launcher3-qt-future-dev @ build 6048032
Test: manual, presubmit on the source branch http://x20/teams/android-launcher/merge/ub-launcher3-qt-future-dev_6048032.html Change-Id: I74059dbc75a8530884f8b4f67917b83c75f32d14 Merged-In: Ieee38cc301364f96cf252b2387142b0aa1b75317
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.util;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
|
||||
/** Empty class, only exists so that l3goWithQuickstepIconRecentsDebug compiles. */
|
||||
public class ShelfPeekAnim {
|
||||
public ShelfPeekAnim(Launcher launcher) {
|
||||
}
|
||||
|
||||
public enum ShelfAnimState {
|
||||
}
|
||||
|
||||
public boolean isPeeking() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+26
-1
@@ -21,8 +21,15 @@ import static com.android.launcher3.LauncherState.BACKGROUND_APP;
|
||||
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.LauncherStateManager.ANIM_ALL;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_SCALE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_TRANSLATE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
|
||||
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
|
||||
import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
|
||||
import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator;
|
||||
|
||||
@@ -40,6 +47,7 @@ import androidx.annotation.Nullable;
|
||||
import com.android.launcher3.LauncherState.ScaleAndTranslation;
|
||||
import com.android.launcher3.allapps.AllAppsTransitionController;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.anim.SpringAnimationBuilder;
|
||||
import com.android.quickstep.util.ClipAnimationHelper;
|
||||
@@ -56,6 +64,9 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti
|
||||
public static final int INDEX_SHELF_ANIM = 0;
|
||||
public static final int INDEX_RECENTS_FADE_ANIM = 1;
|
||||
public static final int INDEX_RECENTS_TRANSLATE_X_ANIM = 2;
|
||||
public static final int INDEX_PAUSE_TO_OVERVIEW_ANIM = 3;
|
||||
|
||||
public static final long ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW = 300;
|
||||
|
||||
public LauncherAppTransitionManagerImpl(Context context) {
|
||||
super(context);
|
||||
@@ -144,7 +155,7 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti
|
||||
|
||||
@Override
|
||||
public int getStateElementAnimationsCount() {
|
||||
return 3;
|
||||
return 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -190,6 +201,20 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti
|
||||
.setStiffness(250)
|
||||
.setValues(values)
|
||||
.build(mLauncher);
|
||||
case INDEX_PAUSE_TO_OVERVIEW_ANIM: {
|
||||
AnimatorSetBuilder builder = new AnimatorSetBuilder();
|
||||
builder.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2);
|
||||
builder.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_3);
|
||||
if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
|
||||
builder.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2);
|
||||
builder.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2);
|
||||
}
|
||||
LauncherStateManager stateManager = mLauncher.getStateManager();
|
||||
return stateManager.createAtomicAnimation(
|
||||
stateManager.getCurrentStableState(), OVERVIEW, builder,
|
||||
ANIM_ALL, ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW);
|
||||
}
|
||||
|
||||
default:
|
||||
return super.createStateElementAnimation(index, values);
|
||||
}
|
||||
|
||||
+1
-1
@@ -276,7 +276,7 @@ public class PredictionRowView extends LinearLayout implements
|
||||
boolean predictionsEnabled = predictionCount > 0;
|
||||
if (predictionsEnabled != mPredictionsEnabled) {
|
||||
mPredictionsEnabled = predictionsEnabled;
|
||||
mLauncher.reapplyUi();
|
||||
mLauncher.reapplyUi(false /* cancelCurrentAnimation */);
|
||||
updateVisibility();
|
||||
}
|
||||
mParent.onHeightUpdated();
|
||||
|
||||
+3
-3
@@ -28,17 +28,17 @@ import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.LauncherStateManager.StateHandler;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.graphics.RotationMode;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.NoButtonQuickSwitchTouchController;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.OverviewToAllAppsTouchController;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchController;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
|
||||
import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
|
||||
import com.android.launcher3.util.TouchController;
|
||||
@@ -145,7 +145,7 @@ public abstract class RecentsUiFactory {
|
||||
ArrayList<TouchController> list = new ArrayList<>();
|
||||
list.add(launcher.getDragController());
|
||||
if (mode == NO_BUTTON) {
|
||||
list.add(new QuickSwitchTouchController(launcher));
|
||||
list.add(new NoButtonQuickSwitchTouchController(launcher));
|
||||
list.add(new NavBarToHomeTouchController(launcher));
|
||||
list.add(new FlingAndHoldTouchController(launcher));
|
||||
} else {
|
||||
|
||||
+2
@@ -22,6 +22,7 @@ import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_Y;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
|
||||
@@ -205,6 +206,7 @@ public class OverviewState extends LauncherState {
|
||||
builder.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
|
||||
builder.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
|
||||
builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
|
||||
builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, OVERSHOOT_1_7);
|
||||
builder.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-16
@@ -16,25 +16,20 @@
|
||||
|
||||
package com.android.launcher3.uioverrides.touchcontrollers;
|
||||
|
||||
import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_PAUSE_TO_OVERVIEW_ANIM;
|
||||
import static com.android.launcher3.LauncherState.ALL_APPS;
|
||||
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
|
||||
import static com.android.launcher3.LauncherStateManager.ANIM_ALL;
|
||||
import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_PEEK_COMPONENT;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_HEADER_FADE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_SCALE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_TRANSLATE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
|
||||
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
|
||||
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
|
||||
|
||||
@@ -46,6 +41,7 @@ import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAppTransitionManagerImpl;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
@@ -79,7 +75,7 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
|
||||
|
||||
@Override
|
||||
protected long getAtomicDuration() {
|
||||
return 300;
|
||||
return LauncherAppTransitionManagerImpl.ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -179,15 +175,8 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
|
||||
mPeekAnim.cancel();
|
||||
}
|
||||
|
||||
AnimatorSetBuilder builder = new AnimatorSetBuilder();
|
||||
builder.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2);
|
||||
builder.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_3);
|
||||
if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
|
||||
builder.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2);
|
||||
builder.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2);
|
||||
}
|
||||
AnimatorSet overviewAnim = mLauncher.getStateManager().createAtomicAnimation(
|
||||
NORMAL, OVERVIEW, builder, ANIM_ALL, ATOMIC_DURATION);
|
||||
Animator overviewAnim = mLauncher.getAppTransitionManager().createStateElementAnimation(
|
||||
INDEX_PAUSE_TO_OVERVIEW_ANIM);
|
||||
overviewAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
|
||||
+473
@@ -0,0 +1,473 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.uioverrides.touchcontrollers;
|
||||
|
||||
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
|
||||
import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_PAUSE_TO_OVERVIEW_ANIM;
|
||||
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.LauncherState.QUICK_SWITCH;
|
||||
import static com.android.launcher3.LauncherStateManager.ANIM_ALL;
|
||||
import static com.android.launcher3.anim.AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
|
||||
import static com.android.launcher3.anim.AnimatorSetBuilder.FLAG_DONT_ANIMATE_OVERVIEW;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL_5;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
|
||||
import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT;
|
||||
import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP;
|
||||
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
|
||||
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
|
||||
import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.CANCEL;
|
||||
import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE;
|
||||
import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK;
|
||||
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.graphics.PointF;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.LauncherStateManager;
|
||||
import com.android.launcher3.LauncherStateManager.AnimationConfig;
|
||||
import com.android.launcher3.QuickstepAppTransitionManagerImpl;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.allapps.AllAppsTransitionController;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.graphics.OverviewScrim;
|
||||
import com.android.launcher3.touch.BaseSwipeDetector;
|
||||
import com.android.launcher3.touch.BothAxesSwipeDetector;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
|
||||
import com.android.launcher3.util.TouchController;
|
||||
import com.android.launcher3.util.VibratorWrapper;
|
||||
import com.android.quickstep.OverviewInteractionState;
|
||||
import com.android.quickstep.util.LayoutUtils;
|
||||
import com.android.quickstep.util.MotionPauseDetector;
|
||||
import com.android.quickstep.util.ShelfPeekAnim;
|
||||
import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
|
||||
import com.android.quickstep.util.StaggeredWorkspaceAnim;
|
||||
import com.android.quickstep.views.LauncherRecentsView;
|
||||
|
||||
/**
|
||||
* Handles quick switching to a recent task from the home screen. To give as much flexibility to
|
||||
* the user as possible, also handles swipe up and hold to go to overview and swiping back home.
|
||||
*/
|
||||
public class NoButtonQuickSwitchTouchController implements TouchController,
|
||||
BothAxesSwipeDetector.Listener, MotionPauseDetector.OnMotionPauseListener {
|
||||
|
||||
/** The minimum progress of the scale/translationY animation until drag end. */
|
||||
private static final float Y_ANIM_MIN_PROGRESS = 0.15f;
|
||||
private static final Interpolator FADE_OUT_INTERPOLATOR = DEACCEL_5;
|
||||
private static final Interpolator TRANSLATE_OUT_INTERPOLATOR = ACCEL_0_75;
|
||||
private static final Interpolator SCALE_DOWN_INTERPOLATOR = DEACCEL;
|
||||
|
||||
private final Launcher mLauncher;
|
||||
private final BothAxesSwipeDetector mSwipeDetector;
|
||||
private final float mXRange;
|
||||
private final float mYRange;
|
||||
private final MotionPauseDetector mMotionPauseDetector;
|
||||
private final float mMotionPauseMinDisplacement;
|
||||
|
||||
private boolean mNoIntercept;
|
||||
private LauncherState mStartState;
|
||||
|
||||
private ShelfPeekAnim mShelfPeekAnim;
|
||||
private boolean mIsHomeScreenVisible = true;
|
||||
|
||||
// As we drag, we control 3 animations: one to get non-overview components out of the way,
|
||||
// and the other two to set overview properties based on x and y progress.
|
||||
private AnimatorPlaybackController mNonOverviewAnim;
|
||||
private AnimatorPlaybackController mXOverviewAnim;
|
||||
private AnimatorPlaybackController mYOverviewAnim;
|
||||
|
||||
public NoButtonQuickSwitchTouchController(Launcher launcher) {
|
||||
mLauncher = launcher;
|
||||
mSwipeDetector = new BothAxesSwipeDetector(mLauncher, this);
|
||||
mXRange = mLauncher.getDeviceProfile().widthPx / 2f;
|
||||
mYRange = LayoutUtils.getShelfTrackingDistance(mLauncher, mLauncher.getDeviceProfile());
|
||||
mMotionPauseDetector = new MotionPauseDetector(mLauncher);
|
||||
mMotionPauseMinDisplacement = mLauncher.getResources().getDimension(
|
||||
R.dimen.motion_pause_detector_min_displacement_from_app);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
|
||||
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
mNoIntercept = !canInterceptTouch(ev);
|
||||
if (mNoIntercept) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only detect horizontal swipe for intercept, then we will allow swipe up as well.
|
||||
mSwipeDetector.setDetectableScrollConditions(DIRECTION_RIGHT,
|
||||
false /* ignoreSlopWhenSettling */);
|
||||
}
|
||||
|
||||
if (mNoIntercept) {
|
||||
return false;
|
||||
}
|
||||
|
||||
onControllerTouchEvent(ev);
|
||||
return mSwipeDetector.isDraggingOrSettling();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onControllerTouchEvent(MotionEvent ev) {
|
||||
return mSwipeDetector.onTouchEvent(ev);
|
||||
}
|
||||
|
||||
private boolean canInterceptTouch(MotionEvent ev) {
|
||||
if (!mLauncher.isInState(LauncherState.NORMAL)) {
|
||||
return false;
|
||||
}
|
||||
if ((ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) == 0) {
|
||||
return false;
|
||||
}
|
||||
int stateFlags = OverviewInteractionState.INSTANCE.get(mLauncher).getSystemUiStateFlags();
|
||||
if ((stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDragStart(boolean start) {
|
||||
mMotionPauseDetector.clear();
|
||||
if (start) {
|
||||
mShelfPeekAnim = ((QuickstepAppTransitionManagerImpl) mLauncher
|
||||
.getAppTransitionManager()).getShelfPeekAnim();
|
||||
|
||||
mStartState = mLauncher.getStateManager().getState();
|
||||
|
||||
mMotionPauseDetector.setOnMotionPauseListener(this);
|
||||
|
||||
// We have detected horizontal drag start, now allow swipe up as well.
|
||||
mSwipeDetector.setDetectableScrollConditions(DIRECTION_RIGHT | DIRECTION_UP,
|
||||
false /* ignoreSlopWhenSettling */);
|
||||
|
||||
setupAnimators();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMotionPauseChanged(boolean isPaused) {
|
||||
ShelfAnimState shelfState = isPaused ? PEEK : HIDE;
|
||||
if (shelfState == PEEK) {
|
||||
// Some shelf elements (e.g. qsb) were hidden, but we need them visible when peeking.
|
||||
AnimatorSetBuilder builder = new AnimatorSetBuilder();
|
||||
AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
|
||||
allAppsController.setAlphas(NORMAL.getVisibleElements(mLauncher),
|
||||
new AnimationConfig(), builder);
|
||||
builder.build().setDuration(0).start();
|
||||
|
||||
if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
|
||||
// Hotseat was hidden, but we need it visible when peeking.
|
||||
mLauncher.getHotseat().setAlpha(1);
|
||||
}
|
||||
}
|
||||
mShelfPeekAnim.setShelfState(shelfState, ShelfPeekAnim.INTERPOLATOR,
|
||||
ShelfPeekAnim.DURATION);
|
||||
VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
|
||||
}
|
||||
|
||||
private void setupAnimators() {
|
||||
// Animate the non-overview components (e.g. workspace, shelf) out of the way.
|
||||
AnimatorSetBuilder nonOverviewBuilder = new AnimatorSetBuilder();
|
||||
nonOverviewBuilder.setInterpolator(ANIM_WORKSPACE_FADE, FADE_OUT_INTERPOLATOR);
|
||||
nonOverviewBuilder.setInterpolator(ANIM_ALL_APPS_FADE, FADE_OUT_INTERPOLATOR);
|
||||
nonOverviewBuilder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, TRANSLATE_OUT_INTERPOLATOR);
|
||||
nonOverviewBuilder.setInterpolator(ANIM_VERTICAL_PROGRESS, TRANSLATE_OUT_INTERPOLATOR);
|
||||
updateNonOverviewAnim(QUICK_SWITCH, nonOverviewBuilder, ANIM_ALL);
|
||||
mNonOverviewAnim.dispatchOnStart();
|
||||
|
||||
setupOverviewAnimators();
|
||||
}
|
||||
|
||||
/** Create state animation to control non-overview components. */
|
||||
private void updateNonOverviewAnim(LauncherState toState, AnimatorSetBuilder builder,
|
||||
@LauncherStateManager.AnimationComponents int animComponents) {
|
||||
builder.addFlag(FLAG_DONT_ANIMATE_OVERVIEW);
|
||||
long accuracy = (long) (Math.max(mXRange, mYRange) * 2);
|
||||
mNonOverviewAnim = mLauncher.getStateManager().createAnimationToNewWorkspace(toState,
|
||||
builder, accuracy, this::clearState, animComponents);
|
||||
}
|
||||
|
||||
private void setupOverviewAnimators() {
|
||||
final LauncherState fromState = QUICK_SWITCH;
|
||||
final LauncherState toState = OVERVIEW;
|
||||
LauncherState.ScaleAndTranslation fromScaleAndTranslation = fromState
|
||||
.getOverviewScaleAndTranslation(mLauncher);
|
||||
LauncherState.ScaleAndTranslation toScaleAndTranslation = toState
|
||||
.getOverviewScaleAndTranslation(mLauncher);
|
||||
// Update RecentView's translationX to have it start offscreen.
|
||||
LauncherRecentsView recentsView = mLauncher.getOverviewPanel();
|
||||
float startScale = Utilities.mapRange(
|
||||
SCALE_DOWN_INTERPOLATOR.getInterpolation(Y_ANIM_MIN_PROGRESS),
|
||||
fromScaleAndTranslation.scale,
|
||||
toScaleAndTranslation.scale);
|
||||
fromScaleAndTranslation.translationX = recentsView.getOffscreenTranslationX(startScale);
|
||||
|
||||
// Set RecentView's initial properties.
|
||||
recentsView.setScaleX(fromScaleAndTranslation.scale);
|
||||
recentsView.setScaleY(fromScaleAndTranslation.scale);
|
||||
recentsView.setTranslationX(fromScaleAndTranslation.translationX);
|
||||
recentsView.setTranslationY(fromScaleAndTranslation.translationY);
|
||||
recentsView.setContentAlpha(1);
|
||||
|
||||
// As we drag right, animate the following properties:
|
||||
// - RecentsView translationX
|
||||
// - OverviewScrim
|
||||
AnimatorSet xOverviewAnim = new AnimatorSet();
|
||||
xOverviewAnim.play(ObjectAnimator.ofFloat(recentsView, View.TRANSLATION_X,
|
||||
toScaleAndTranslation.translationX));
|
||||
xOverviewAnim.play(ObjectAnimator.ofFloat(
|
||||
mLauncher.getDragLayer().getOverviewScrim(), OverviewScrim.SCRIM_PROGRESS,
|
||||
toState.getOverviewScrimAlpha(mLauncher)));
|
||||
long xAccuracy = (long) (mXRange * 2);
|
||||
xOverviewAnim.setDuration(xAccuracy);
|
||||
mXOverviewAnim = AnimatorPlaybackController.wrap(xOverviewAnim, xAccuracy);
|
||||
mXOverviewAnim.dispatchOnStart();
|
||||
|
||||
// As we drag up, animate the following properties:
|
||||
// - RecentsView translationY
|
||||
// - RecentsView scale
|
||||
// - RecentsView fullscreenProgress
|
||||
AnimatorSet yAnimation = new AnimatorSet();
|
||||
Animator translateYAnim = ObjectAnimator.ofFloat(recentsView, View.TRANSLATION_Y,
|
||||
toScaleAndTranslation.translationY);
|
||||
Animator scaleAnim = ObjectAnimator.ofFloat(recentsView, SCALE_PROPERTY,
|
||||
toScaleAndTranslation.scale);
|
||||
Animator fullscreenProgressAnim = ObjectAnimator.ofFloat(recentsView, FULLSCREEN_PROGRESS,
|
||||
fromState.getOverviewFullscreenProgress(), toState.getOverviewFullscreenProgress());
|
||||
scaleAnim.setInterpolator(SCALE_DOWN_INTERPOLATOR);
|
||||
fullscreenProgressAnim.setInterpolator(SCALE_DOWN_INTERPOLATOR);
|
||||
yAnimation.play(translateYAnim);
|
||||
yAnimation.play(scaleAnim);
|
||||
yAnimation.play(fullscreenProgressAnim);
|
||||
long yAccuracy = (long) (mYRange * 2);
|
||||
yAnimation.setDuration(yAccuracy);
|
||||
mYOverviewAnim = AnimatorPlaybackController.wrap(yAnimation, yAccuracy);
|
||||
mYOverviewAnim.dispatchOnStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDrag(PointF displacement, MotionEvent ev) {
|
||||
float xProgress = Math.max(0, displacement.x) / mXRange;
|
||||
float yProgress = Math.max(0, -displacement.y) / mYRange;
|
||||
yProgress = Utilities.mapRange(yProgress, Y_ANIM_MIN_PROGRESS, 1f);
|
||||
|
||||
boolean wasHomeScreenVisible = mIsHomeScreenVisible;
|
||||
if (wasHomeScreenVisible && mNonOverviewAnim != null) {
|
||||
mNonOverviewAnim.setPlayFraction(xProgress);
|
||||
}
|
||||
mIsHomeScreenVisible = FADE_OUT_INTERPOLATOR.getInterpolation(xProgress)
|
||||
<= 1 - ALPHA_CUTOFF_THRESHOLD;
|
||||
|
||||
if (wasHomeScreenVisible && !mIsHomeScreenVisible) {
|
||||
// Get the shelf all the way offscreen so it pops up when we decide to peek it.
|
||||
mShelfPeekAnim.setShelfState(HIDE, LINEAR, 0);
|
||||
}
|
||||
|
||||
// Only allow motion pause if the home screen is invisible, since some
|
||||
// home screen elements will appear in the shelf on motion pause.
|
||||
mMotionPauseDetector.setDisallowPause(mIsHomeScreenVisible
|
||||
|| -displacement.y < mMotionPauseMinDisplacement);
|
||||
mMotionPauseDetector.addPosition(displacement.y, ev.getEventTime());
|
||||
|
||||
if (mIsHomeScreenVisible) {
|
||||
// Cancel the shelf anim so it doesn't clobber mNonOverviewAnim.
|
||||
mShelfPeekAnim.setShelfState(CANCEL, LINEAR, 0);
|
||||
}
|
||||
|
||||
if (mXOverviewAnim != null) {
|
||||
mXOverviewAnim.setPlayFraction(xProgress);
|
||||
}
|
||||
if (mYOverviewAnim != null) {
|
||||
mYOverviewAnim.setPlayFraction(yProgress);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDragEnd(PointF velocity) {
|
||||
boolean horizontalFling = mSwipeDetector.isFling(velocity.x);
|
||||
boolean verticalFling = mSwipeDetector.isFling(velocity.y);
|
||||
boolean noFling = !horizontalFling && !verticalFling;
|
||||
int logAction = noFling ? Touch.SWIPE : Touch.FLING;
|
||||
if (mMotionPauseDetector.isPaused() && noFling) {
|
||||
cancelAnimations();
|
||||
|
||||
Animator overviewAnim = mLauncher.getAppTransitionManager().createStateElementAnimation(
|
||||
INDEX_PAUSE_TO_OVERVIEW_ANIM);
|
||||
overviewAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
onAnimationToStateCompleted(OVERVIEW, logAction);
|
||||
}
|
||||
});
|
||||
overviewAnim.start();
|
||||
return;
|
||||
}
|
||||
|
||||
final LauncherState targetState;
|
||||
if (horizontalFling && verticalFling) {
|
||||
if (velocity.x < 0) {
|
||||
// Flinging left and up or down both go back home.
|
||||
targetState = NORMAL;
|
||||
} else {
|
||||
if (velocity.y > 0) {
|
||||
// Flinging right and down goes to quick switch.
|
||||
targetState = QUICK_SWITCH;
|
||||
} else {
|
||||
// Flinging up and right could go either home or to quick switch.
|
||||
// Determine the target based on the higher velocity.
|
||||
targetState = Math.abs(velocity.x) > Math.abs(velocity.y)
|
||||
? QUICK_SWITCH : NORMAL;
|
||||
}
|
||||
}
|
||||
} else if (horizontalFling) {
|
||||
targetState = velocity.x > 0 ? QUICK_SWITCH : NORMAL;
|
||||
} else if (verticalFling) {
|
||||
targetState = velocity.y > 0 ? QUICK_SWITCH : NORMAL;
|
||||
} else {
|
||||
// If user isn't flinging, just snap to the closest state based on x progress.
|
||||
boolean passedHorizontalThreshold = mXOverviewAnim.getInterpolatedProgress() > 0.5f;
|
||||
targetState = passedHorizontalThreshold ? QUICK_SWITCH : NORMAL;
|
||||
}
|
||||
|
||||
// Animate the various components to the target state.
|
||||
|
||||
float xProgress = mXOverviewAnim.getProgressFraction();
|
||||
float startXProgress = Utilities.boundToRange(xProgress
|
||||
+ velocity.x * getSingleFrameMs(mLauncher) / mXRange, 0f, 1f);
|
||||
final float endXProgress = targetState == NORMAL ? 0 : 1;
|
||||
long xDuration = BaseSwipeDetector.calculateDuration(velocity.x,
|
||||
Math.abs(endXProgress - startXProgress));
|
||||
ValueAnimator xOverviewAnim = mXOverviewAnim.getAnimationPlayer();
|
||||
xOverviewAnim.setFloatValues(startXProgress, endXProgress);
|
||||
xOverviewAnim.setDuration(xDuration)
|
||||
.setInterpolator(scrollInterpolatorForVelocity(velocity.x));
|
||||
mXOverviewAnim.dispatchOnStartWithVelocity(endXProgress, velocity.x);
|
||||
|
||||
boolean flingUpToNormal = verticalFling && velocity.y < 0 && targetState == NORMAL;
|
||||
|
||||
float yProgress = mYOverviewAnim.getProgressFraction();
|
||||
float startYProgress = Utilities.boundToRange(yProgress
|
||||
- velocity.y * getSingleFrameMs(mLauncher) / mYRange, 0f, 1f);
|
||||
final float endYProgress;
|
||||
if (flingUpToNormal) {
|
||||
endYProgress = 1;
|
||||
} else if (targetState == NORMAL) {
|
||||
// Keep overview at its current scale/translationY as it slides off the screen.
|
||||
endYProgress = startYProgress;
|
||||
} else {
|
||||
endYProgress = 0;
|
||||
}
|
||||
long yDuration = BaseSwipeDetector.calculateDuration(velocity.y,
|
||||
Math.abs(endYProgress - startYProgress));
|
||||
ValueAnimator yOverviewAnim = mYOverviewAnim.getAnimationPlayer();
|
||||
yOverviewAnim.setFloatValues(startYProgress, endYProgress);
|
||||
yOverviewAnim.setDuration(yDuration);
|
||||
mYOverviewAnim.dispatchOnStartWithVelocity(endYProgress, velocity.y);
|
||||
|
||||
ValueAnimator nonOverviewAnim = mNonOverviewAnim.getAnimationPlayer();
|
||||
if (flingUpToNormal && !mIsHomeScreenVisible) {
|
||||
// We are flinging to home while workspace is invisible, run the same staggered
|
||||
// animation as from an app.
|
||||
// Update mNonOverviewAnim to do nothing so it doesn't interfere.
|
||||
updateNonOverviewAnim(targetState, new AnimatorSetBuilder(), 0 /* animComponents */);
|
||||
nonOverviewAnim = mNonOverviewAnim.getAnimationPlayer();
|
||||
|
||||
new StaggeredWorkspaceAnim(mLauncher, velocity.y, false /* animateOverviewScrim */)
|
||||
.start();
|
||||
} else {
|
||||
boolean canceled = targetState == NORMAL;
|
||||
if (canceled) {
|
||||
// Let the state manager know that the animation didn't go to the target state,
|
||||
// but don't clean up yet (we already clean up when the animation completes).
|
||||
mNonOverviewAnim.dispatchOnCancelWithoutCancelRunnable();
|
||||
}
|
||||
float startProgress = mNonOverviewAnim.getProgressFraction();
|
||||
float endProgress = canceled ? 0 : 1;
|
||||
nonOverviewAnim.setFloatValues(startProgress, endProgress);
|
||||
mNonOverviewAnim.dispatchOnStartWithVelocity(endProgress,
|
||||
horizontalFling ? velocity.x : velocity.y);
|
||||
}
|
||||
|
||||
nonOverviewAnim.setDuration(Math.max(xDuration, yDuration));
|
||||
mNonOverviewAnim.setEndAction(() -> onAnimationToStateCompleted(targetState, logAction));
|
||||
|
||||
cancelAnimations();
|
||||
xOverviewAnim.start();
|
||||
yOverviewAnim.start();
|
||||
nonOverviewAnim.start();
|
||||
}
|
||||
|
||||
private void onAnimationToStateCompleted(LauncherState targetState, int logAction) {
|
||||
mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
|
||||
getDirectionForLog(), mSwipeDetector.getDownX(), mSwipeDetector.getDownY(),
|
||||
LauncherLogProto.ContainerType.NAVBAR,
|
||||
mStartState.containerType,
|
||||
targetState.containerType,
|
||||
mLauncher.getWorkspace().getCurrentPage());
|
||||
mLauncher.getStateManager().goToState(targetState, false, this::clearState);
|
||||
}
|
||||
|
||||
private int getDirectionForLog() {
|
||||
return Utilities.isRtl(mLauncher.getResources()) ? Direction.LEFT : Direction.RIGHT;
|
||||
}
|
||||
|
||||
private void cancelAnimations() {
|
||||
if (mNonOverviewAnim != null) {
|
||||
mNonOverviewAnim.getAnimationPlayer().cancel();
|
||||
}
|
||||
if (mXOverviewAnim != null) {
|
||||
mXOverviewAnim.getAnimationPlayer().cancel();
|
||||
}
|
||||
if (mYOverviewAnim != null) {
|
||||
mYOverviewAnim.getAnimationPlayer().cancel();
|
||||
}
|
||||
mShelfPeekAnim.setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
|
||||
mMotionPauseDetector.clear();
|
||||
}
|
||||
|
||||
private void clearState() {
|
||||
cancelAnimations();
|
||||
mNonOverviewAnim = null;
|
||||
mXOverviewAnim = null;
|
||||
mYOverviewAnim = null;
|
||||
mIsHomeScreenVisible = true;
|
||||
mSwipeDetector.finishedScrolling();
|
||||
}
|
||||
}
|
||||
+9
-41
@@ -24,7 +24,6 @@ import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_SHELF
|
||||
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.LauncherStateManager.ANIM_ALL;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.INSTANT;
|
||||
@@ -53,15 +52,15 @@ import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.LauncherInitListenerEx;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.LauncherStateManager;
|
||||
import com.android.launcher3.QuickstepAppTransitionManagerImpl;
|
||||
import com.android.launcher3.allapps.DiscoveryBounce;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.uioverrides.states.OverviewState;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.views.FloatingIconView;
|
||||
import com.android.quickstep.SysUINavigationMode.Mode;
|
||||
import com.android.quickstep.util.LayoutUtils;
|
||||
import com.android.quickstep.util.ShelfPeekAnim;
|
||||
import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
|
||||
import com.android.quickstep.util.StaggeredWorkspaceAnim;
|
||||
import com.android.quickstep.views.LauncherRecentsView;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
@@ -167,18 +166,8 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
|
||||
|
||||
@Override
|
||||
public void playAtomicAnimation(float velocity) {
|
||||
// Setup workspace with 0 duration to prepare for our staggered animation.
|
||||
LauncherStateManager stateManager = activity.getStateManager();
|
||||
AnimatorSetBuilder builder = new AnimatorSetBuilder();
|
||||
// setRecentsAttachedToAppWindow() will animate recents out.
|
||||
builder.addFlag(AnimatorSetBuilder.FLAG_DONT_ANIMATE_OVERVIEW);
|
||||
stateManager.createAtomicAnimation(BACKGROUND_APP, NORMAL, builder, ANIM_ALL, 0);
|
||||
builder.build().start();
|
||||
|
||||
// Stop scrolling so that it doesn't interfere with the translation offscreen.
|
||||
recentsView.getScroller().forceFinished(true);
|
||||
|
||||
new StaggeredWorkspaceAnim(activity, workspaceView, velocity).start();
|
||||
new StaggeredWorkspaceAnim(activity, velocity, true /* animateOverviewScrim */)
|
||||
.start();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -201,7 +190,9 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
|
||||
activity.getAppsView().reset(false /* animate */);
|
||||
|
||||
return new AnimationFactory() {
|
||||
private ShelfAnimState mShelfState;
|
||||
private final ShelfPeekAnim mShelfAnim =
|
||||
((QuickstepAppTransitionManagerImpl) activity.getAppTransitionManager())
|
||||
.getShelfPeekAnim();
|
||||
private boolean mIsAttachedToWindow;
|
||||
|
||||
@Override
|
||||
@@ -230,30 +221,7 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
|
||||
@Override
|
||||
public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator,
|
||||
long duration) {
|
||||
if (mShelfState == shelfState) {
|
||||
return;
|
||||
}
|
||||
mShelfState = shelfState;
|
||||
activity.getStateManager().cancelStateElementAnimation(INDEX_SHELF_ANIM);
|
||||
if (mShelfState == ShelfAnimState.CANCEL) {
|
||||
return;
|
||||
}
|
||||
float shelfHiddenProgress = BACKGROUND_APP.getVerticalProgress(activity);
|
||||
float shelfOverviewProgress = OVERVIEW.getVerticalProgress(activity);
|
||||
// Peek based on default overview progress so we can see hotseat if we're showing
|
||||
// that instead of predictions in overview.
|
||||
float defaultOverviewProgress = OverviewState.getDefaultVerticalProgress(activity);
|
||||
float shelfPeekingProgress = shelfHiddenProgress
|
||||
- (shelfHiddenProgress - defaultOverviewProgress) * 0.25f;
|
||||
float toProgress = mShelfState == ShelfAnimState.HIDE
|
||||
? shelfHiddenProgress
|
||||
: mShelfState == ShelfAnimState.PEEK
|
||||
? shelfPeekingProgress
|
||||
: shelfOverviewProgress;
|
||||
Animator shelfAnim = activity.getStateManager()
|
||||
.createStateElementAnimation(INDEX_SHELF_ANIM, toProgress);
|
||||
shelfAnim.setInterpolator(interpolator);
|
||||
shelfAnim.setDuration(duration).start();
|
||||
mShelfAnim.setShelfState(shelfState, interpolator, duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+6
-11
@@ -26,14 +26,14 @@ import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
|
||||
import static com.android.launcher3.util.RaceConditionTracker.ENTER;
|
||||
import static com.android.launcher3.util.RaceConditionTracker.EXIT;
|
||||
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
|
||||
import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.HIDE;
|
||||
import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.PEEK;
|
||||
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
|
||||
import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
|
||||
import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.HOME;
|
||||
import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.LAST_TASK;
|
||||
import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.NEW_TASK;
|
||||
import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.RECENTS;
|
||||
import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE;
|
||||
import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK;
|
||||
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
|
||||
|
||||
@@ -74,13 +74,14 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
|
||||
import com.android.launcher3.util.RaceConditionTracker;
|
||||
import com.android.launcher3.util.TraceHelper;
|
||||
import com.android.quickstep.ActivityControlHelper.AnimationFactory;
|
||||
import com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState;
|
||||
import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory;
|
||||
import com.android.quickstep.SysUINavigationMode.Mode;
|
||||
import com.android.quickstep.inputconsumers.InputConsumer;
|
||||
import com.android.quickstep.inputconsumers.OverviewInputConsumer;
|
||||
import com.android.quickstep.util.ClipAnimationHelper.TargetAlphaProvider;
|
||||
import com.android.quickstep.util.RectFSpringAnim;
|
||||
import com.android.quickstep.util.ShelfPeekAnim;
|
||||
import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
|
||||
import com.android.quickstep.util.SwipeAnimationTargetSet;
|
||||
import com.android.quickstep.views.LiveTileOverlay;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
@@ -192,7 +193,6 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
Math.min(1 / MIN_PROGRESS_FOR_OVERVIEW, 1 / (1 - MIN_PROGRESS_FOR_OVERVIEW));
|
||||
private static final String SCREENSHOT_CAPTURED_EVT = "ScreenshotCaptured";
|
||||
|
||||
private static final long SHELF_ANIM_DURATION = 240;
|
||||
public static final long RECENTS_ATTACH_DURATION = 300;
|
||||
|
||||
/**
|
||||
@@ -206,8 +206,6 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
private boolean mIsShelfPeeking;
|
||||
|
||||
private boolean mContinuingLastGesture;
|
||||
// To avoid UI jump when gesture is started, we offset the animation by the threshold.
|
||||
private float mShiftAtGestureStart = 0;
|
||||
|
||||
private ThumbnailData mTaskSnapshot;
|
||||
|
||||
@@ -442,7 +440,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
|
||||
@Override
|
||||
public void onMotionPauseChanged(boolean isPaused) {
|
||||
setShelfState(isPaused ? PEEK : HIDE, OVERSHOOT_1_2, SHELF_ANIM_DURATION);
|
||||
setShelfState(isPaused ? PEEK : HIDE, ShelfPeekAnim.INTERPOLATOR, ShelfPeekAnim.DURATION);
|
||||
}
|
||||
|
||||
public void maybeUpdateRecentsAttachedState() {
|
||||
@@ -580,9 +578,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
// Normalize the progress to 0 to 1, as the animation controller will clamp it to that
|
||||
// anyway. The controller mimics the drag length factor by applying it to its interpolators.
|
||||
float progress = mCurrentShift.value / mDragLengthFactor;
|
||||
mLauncherTransitionController.setPlayFraction(
|
||||
progress <= mShiftAtGestureStart || mShiftAtGestureStart >= 1
|
||||
? 0 : (progress - mShiftAtGestureStart) / (1 - mShiftAtGestureStart));
|
||||
mLauncherTransitionController.setPlayFraction(progress);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -622,7 +618,6 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
@Override
|
||||
public void onGestureStarted() {
|
||||
notifyGestureStartedAsync();
|
||||
mShiftAtGestureStart = mCurrentShift.value;
|
||||
setStateOnUiThread(STATE_GESTURE_STARTED);
|
||||
mGestureStarted = true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.util;
|
||||
|
||||
import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_SHELF_ANIM;
|
||||
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.uioverrides.states.OverviewState;
|
||||
|
||||
/**
|
||||
* Animates the shelf between states HIDE, PEEK, and OVERVIEW.
|
||||
*/
|
||||
|
||||
public class ShelfPeekAnim {
|
||||
|
||||
public static final Interpolator INTERPOLATOR = OVERSHOOT_1_2;
|
||||
public static final long DURATION = 240;
|
||||
|
||||
private final Launcher mLauncher;
|
||||
|
||||
private ShelfAnimState mShelfState;
|
||||
private boolean mIsPeeking;
|
||||
|
||||
public ShelfPeekAnim(Launcher launcher) {
|
||||
mLauncher = launcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Animates to the given state, canceling the previous animation if it was still running.
|
||||
*/
|
||||
public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator, long duration) {
|
||||
if (mShelfState == shelfState) {
|
||||
return;
|
||||
}
|
||||
mLauncher.getStateManager().cancelStateElementAnimation(INDEX_SHELF_ANIM);
|
||||
mShelfState = shelfState;
|
||||
mIsPeeking = mShelfState == ShelfAnimState.PEEK || mShelfState == ShelfAnimState.HIDE;
|
||||
if (mShelfState == ShelfAnimState.CANCEL) {
|
||||
return;
|
||||
}
|
||||
float shelfHiddenProgress = BACKGROUND_APP.getVerticalProgress(mLauncher);
|
||||
float shelfOverviewProgress = OVERVIEW.getVerticalProgress(mLauncher);
|
||||
// Peek based on default overview progress so we can see hotseat if we're showing
|
||||
// that instead of predictions in overview.
|
||||
float defaultOverviewProgress = OverviewState.getDefaultVerticalProgress(mLauncher);
|
||||
float shelfPeekingProgress = shelfHiddenProgress
|
||||
- (shelfHiddenProgress - defaultOverviewProgress) * 0.25f;
|
||||
float toProgress = mShelfState == ShelfAnimState.HIDE
|
||||
? shelfHiddenProgress
|
||||
: mShelfState == ShelfAnimState.PEEK
|
||||
? shelfPeekingProgress
|
||||
: shelfOverviewProgress;
|
||||
Animator shelfAnim = mLauncher.getStateManager()
|
||||
.createStateElementAnimation(INDEX_SHELF_ANIM, toProgress);
|
||||
shelfAnim.setInterpolator(interpolator);
|
||||
shelfAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
mShelfState = ShelfAnimState.CANCEL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
mIsPeeking = mShelfState == ShelfAnimState.PEEK;
|
||||
}
|
||||
});
|
||||
shelfAnim.setDuration(duration).start();
|
||||
}
|
||||
|
||||
/** @return Whether the shelf is currently peeking or animating to or from peeking. */
|
||||
public boolean isPeeking() {
|
||||
return mIsPeeking;
|
||||
}
|
||||
|
||||
/** The various shelf states we can animate to. */
|
||||
public enum ShelfAnimState {
|
||||
HIDE(true), PEEK(true), OVERVIEW(false), CANCEL(false);
|
||||
|
||||
ShelfAnimState(boolean shouldPreformHaptic) {
|
||||
this.shouldPreformHaptic = shouldPreformHaptic;
|
||||
}
|
||||
|
||||
public final boolean shouldPreformHaptic;
|
||||
}
|
||||
}
|
||||
+26
-40
@@ -18,6 +18,7 @@ package com.android.quickstep.util;
|
||||
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
|
||||
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.LauncherStateManager.ANIM_ALL;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
|
||||
import android.animation.Animator;
|
||||
@@ -27,13 +28,11 @@ import android.animation.ObjectAnimator;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.BubbleTextView;
|
||||
import com.android.launcher3.CellLayout;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.LauncherStateManager;
|
||||
import com.android.launcher3.LauncherStateManager.AnimationConfig;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.ShortcutAndWidgetContainer;
|
||||
@@ -41,9 +40,8 @@ import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.anim.PropertySetter;
|
||||
import com.android.launcher3.anim.SpringObjectAnimator;
|
||||
import com.android.launcher3.folder.FolderIcon;
|
||||
import com.android.launcher3.graphics.OverviewScrim;
|
||||
import com.android.launcher3.views.IconLabelDotView;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -66,18 +64,12 @@ public class StaggeredWorkspaceAnim {
|
||||
private final float mVelocity;
|
||||
private final float mSpringTransY;
|
||||
|
||||
// The original view of the {@link FloatingIconView}.
|
||||
private final View mOriginalView;
|
||||
|
||||
private final List<Animator> mAnimators = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* @param floatingViewOriginalView The FloatingIconView's original view.
|
||||
*/
|
||||
public StaggeredWorkspaceAnim(Launcher launcher, @Nullable View floatingViewOriginalView,
|
||||
float velocity) {
|
||||
public StaggeredWorkspaceAnim(Launcher launcher, float velocity, boolean animateOverviewScrim) {
|
||||
prepareToAnimate(launcher);
|
||||
|
||||
mVelocity = velocity;
|
||||
mOriginalView = floatingViewOriginalView;
|
||||
|
||||
// Scale the translationY based on the initial velocity to better sync the workspace items
|
||||
// with the floating view.
|
||||
@@ -133,8 +125,10 @@ public class StaggeredWorkspaceAnim {
|
||||
addStaggeredAnimationForView(qsb, grid.inv.numRows + 2, totalRows);
|
||||
}
|
||||
|
||||
addScrimAnimationForState(launcher, BACKGROUND_APP, 0);
|
||||
addScrimAnimationForState(launcher, NORMAL, ALPHA_DURATION_MS);
|
||||
if (animateOverviewScrim) {
|
||||
addScrimAnimationForState(launcher, BACKGROUND_APP, 0);
|
||||
addScrimAnimationForState(launcher, NORMAL, ALPHA_DURATION_MS);
|
||||
}
|
||||
|
||||
AnimatorListener resetClipListener = new AnimatorListenerAdapter() {
|
||||
int numAnimations = mAnimators.size();
|
||||
@@ -160,6 +154,21 @@ public class StaggeredWorkspaceAnim {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup workspace with 0 duration to prepare for our staggered animation.
|
||||
*/
|
||||
private void prepareToAnimate(Launcher launcher) {
|
||||
LauncherStateManager stateManager = launcher.getStateManager();
|
||||
AnimatorSetBuilder builder = new AnimatorSetBuilder();
|
||||
// setRecentsAttachedToAppWindow() will animate recents out.
|
||||
builder.addFlag(AnimatorSetBuilder.FLAG_DONT_ANIMATE_OVERVIEW);
|
||||
stateManager.createAtomicAnimation(BACKGROUND_APP, NORMAL, builder, ANIM_ALL, 0);
|
||||
builder.build().start();
|
||||
|
||||
// Stop scrolling so that it doesn't interfere with the translation offscreen.
|
||||
launcher.<RecentsView>getOverviewPanel().getScroller().forceFinished(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the animation.
|
||||
*/
|
||||
@@ -192,35 +201,12 @@ public class StaggeredWorkspaceAnim {
|
||||
springTransY.setStartDelay(startDelay);
|
||||
mAnimators.add(springTransY);
|
||||
|
||||
ObjectAnimator alpha = getAlphaAnimator(v, startDelay);
|
||||
if (v == mOriginalView) {
|
||||
// For IconLabelDotViews, we just want the label to fade in.
|
||||
// Icon, badge, and dots will animate in separately (controlled via FloatingIconView)
|
||||
if (v instanceof IconLabelDotView) {
|
||||
alpha.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
IconLabelDotView view = (IconLabelDotView) v;
|
||||
view.setIconVisible(false);
|
||||
view.setForceHideDot(true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
v.setAlpha(0);
|
||||
mAnimators.add(alpha);
|
||||
}
|
||||
|
||||
private ObjectAnimator getAlphaAnimator(View v, long startDelay) {
|
||||
ObjectAnimator alpha = ObjectAnimator.ofFloat(v, View.ALPHA, 0f, 1f);
|
||||
alpha.setInterpolator(LINEAR);
|
||||
alpha.setDuration(ALPHA_DURATION_MS);
|
||||
alpha.setStartDelay(startDelay);
|
||||
return alpha;
|
||||
|
||||
mAnimators.add(alpha);
|
||||
}
|
||||
|
||||
private void addScrimAnimationForState(Launcher launcher, LauncherState state, long duration) {
|
||||
|
||||
@@ -57,6 +57,9 @@ import android.os.Looper;
|
||||
import android.util.Pair;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
|
||||
import com.android.launcher3.allapps.AllAppsTransitionController;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
@@ -69,6 +72,7 @@ import com.android.launcher3.views.FloatingIconView;
|
||||
import com.android.quickstep.util.MultiValueUpdateListener;
|
||||
import com.android.quickstep.util.RemoteAnimationProvider;
|
||||
import com.android.quickstep.util.RemoteAnimationTargetSet;
|
||||
import com.android.quickstep.util.ShelfPeekAnim;
|
||||
import com.android.systemui.shared.system.ActivityCompat;
|
||||
import com.android.systemui.shared.system.ActivityOptionsCompat;
|
||||
import com.android.systemui.shared.system.QuickStepContract;
|
||||
@@ -80,9 +84,6 @@ import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
|
||||
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
|
||||
import com.android.systemui.shared.system.WindowManagerWrapper;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* {@link LauncherAppTransitionManager} with Quickstep-specific app transitions for launching from
|
||||
* home and/or all-apps.
|
||||
@@ -150,6 +151,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||
|
||||
private RemoteAnimationProvider mRemoteAnimationProvider;
|
||||
|
||||
private final ShelfPeekAnim mShelfPeekAnim;
|
||||
|
||||
private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
@@ -177,6 +180,12 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||
|
||||
mLauncher.addOnDeviceProfileChangeListener(this);
|
||||
registerRemoteAnimations();
|
||||
|
||||
mShelfPeekAnim = new ShelfPeekAnim(mLauncher);
|
||||
}
|
||||
|
||||
public ShelfPeekAnim getShelfPeekAnim() {
|
||||
return mShelfPeekAnim;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -36,6 +36,7 @@ import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.quickstep.util.RemoteAnimationProvider;
|
||||
import com.android.quickstep.util.RemoteAnimationTargetSet;
|
||||
import com.android.quickstep.util.ShelfPeekAnim;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
|
||||
import java.util.function.BiPredicate;
|
||||
@@ -109,16 +110,6 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> {
|
||||
|
||||
interface AnimationFactory {
|
||||
|
||||
enum ShelfAnimState {
|
||||
HIDE(true), PEEK(true), OVERVIEW(false), CANCEL(false);
|
||||
|
||||
ShelfAnimState(boolean shouldPreformHaptic) {
|
||||
this.shouldPreformHaptic = shouldPreformHaptic;
|
||||
}
|
||||
|
||||
public final boolean shouldPreformHaptic;
|
||||
}
|
||||
|
||||
default void onRemoteAnimationReceived(RemoteAnimationTargetSet targets) { }
|
||||
|
||||
void createActivityController(long transitionLength);
|
||||
@@ -127,8 +118,8 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> {
|
||||
|
||||
default void onTransitionCancelled() { }
|
||||
|
||||
default void setShelfState(ShelfAnimState animState, Interpolator interpolator,
|
||||
long duration) { }
|
||||
default void setShelfState(ShelfPeekAnim.ShelfAnimState animState,
|
||||
Interpolator interpolator, long duration) { }
|
||||
|
||||
/**
|
||||
* @param attached Whether to show RecentsView alongside the app window. If false, recents
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.quickstep.views;
|
||||
import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
|
||||
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.LauncherState.QUICK_SWITCH;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
@@ -35,6 +36,8 @@ import android.util.AttributeSet;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.QuickstepAppTransitionManagerImpl;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
@@ -44,6 +47,7 @@ import com.android.quickstep.SysUINavigationMode;
|
||||
import com.android.quickstep.SysUINavigationMode.Mode;
|
||||
import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
|
||||
import com.android.quickstep.util.LayoutUtils;
|
||||
import com.android.quickstep.util.ShelfPeekAnim;
|
||||
|
||||
/**
|
||||
* Scrim used for all-apps and shelf in Overview
|
||||
@@ -193,8 +197,12 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis
|
||||
if (mProgress >= 1) {
|
||||
mRemainingScreenColor = 0;
|
||||
mShelfColor = 0;
|
||||
ShelfPeekAnim shelfPeekAnim = ((QuickstepAppTransitionManagerImpl)
|
||||
mLauncher.getAppTransitionManager()).getShelfPeekAnim();
|
||||
LauncherState state = mLauncher.getStateManager().getState();
|
||||
if (mSysUINavigationMode == Mode.NO_BUTTON
|
||||
&& mLauncher.getStateManager().getState() == BACKGROUND_APP) {
|
||||
&& (state == BACKGROUND_APP || state == QUICK_SWITCH)
|
||||
&& shelfPeekAnim.isPeeking()) {
|
||||
// Show the shelf background when peeking during swipe up.
|
||||
mShelfColor = setColorAlphaBound(mEndScrim, mMidAlpha);
|
||||
}
|
||||
|
||||
@@ -446,12 +446,16 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
|
||||
|
||||
@Override
|
||||
public void reapplyUi() {
|
||||
reapplyUi(true /* cancelCurrentAnimation */);
|
||||
}
|
||||
|
||||
public void reapplyUi(boolean cancelCurrentAnimation) {
|
||||
if (supportsFakeLandscapeUI()) {
|
||||
mRotationMode = mStableDeviceProfile == null
|
||||
? RotationMode.NORMAL : UiFactory.getRotationMode(mDeviceProfile);
|
||||
}
|
||||
getRootView().dispatchInsets();
|
||||
getStateManager().reapplyState(true /* cancelCurrentAnimation */);
|
||||
getStateManager().reapplyState(cancelCurrentAnimation);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,7 +24,8 @@ import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
|
||||
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
@@ -32,7 +33,6 @@ import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.anim.PropertySetter;
|
||||
import com.android.launcher3.anim.PropertySetter.AnimatedPropertySetter;
|
||||
import com.android.launcher3.compat.AccessibilityManagerCompat;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.uioverrides.UiFactory;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
@@ -40,8 +40,6 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
|
||||
/**
|
||||
* TODO: figure out what kind of tests we can write for this
|
||||
*
|
||||
|
||||
@@ -27,7 +27,7 @@ import android.view.ViewGroup;
|
||||
*/
|
||||
public class AlphaUpdateListener extends AnimationSuccessListener
|
||||
implements AnimatorUpdateListener {
|
||||
private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f;
|
||||
public static final float ALPHA_CUTOFF_THRESHOLD = 0.01f;
|
||||
|
||||
private View mView;
|
||||
|
||||
|
||||
@@ -26,15 +26,15 @@ import android.animation.TimeInterpolator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.dynamicanimation.animation.DynamicAnimation;
|
||||
import androidx.dynamicanimation.animation.SpringAnimation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import androidx.dynamicanimation.animation.DynamicAnimation;
|
||||
import androidx.dynamicanimation.animation.SpringAnimation;
|
||||
|
||||
/**
|
||||
* Helper class to control the playback of an {@link AnimatorSet}, with custom interpolators
|
||||
* and durations.
|
||||
@@ -250,6 +250,17 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets mOnCancelRunnable = null before dispatching the cancel and restoring the runnable. This
|
||||
* is intended to be used only if you need to cancel but want to defer cleaning up yourself.
|
||||
*/
|
||||
public void dispatchOnCancelWithoutCancelRunnable() {
|
||||
Runnable onCancel = mOnCancelRunnable;
|
||||
setOnCancelRunnable(null);
|
||||
dispatchOnCancel();
|
||||
setOnCancelRunnable(onCancel);
|
||||
}
|
||||
|
||||
public void dispatchOnCancel() {
|
||||
dispatchOnCancelRecursively(mAnim);
|
||||
}
|
||||
@@ -283,10 +294,6 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat
|
||||
mOnCancelRunnable = runnable;
|
||||
}
|
||||
|
||||
public Runnable getOnCancelRunnable() {
|
||||
return mOnCancelRunnable;
|
||||
}
|
||||
|
||||
public void skipToEnd() {
|
||||
mSkipToEnd = true;
|
||||
for (SpringAnimation spring : mSprings) {
|
||||
|
||||
@@ -39,6 +39,7 @@ public class Interpolators {
|
||||
public static final Interpolator LINEAR = new LinearInterpolator();
|
||||
|
||||
public static final Interpolator ACCEL = new AccelerateInterpolator();
|
||||
public static final Interpolator ACCEL_0_75 = new AccelerateInterpolator(0.75f);
|
||||
public static final Interpolator ACCEL_1_5 = new AccelerateInterpolator(1.5f);
|
||||
public static final Interpolator ACCEL_2 = new AccelerateInterpolator(2);
|
||||
|
||||
@@ -48,6 +49,7 @@ public class Interpolators {
|
||||
public static final Interpolator DEACCEL_2 = new DecelerateInterpolator(2);
|
||||
public static final Interpolator DEACCEL_2_5 = new DecelerateInterpolator(2.5f);
|
||||
public static final Interpolator DEACCEL_3 = new DecelerateInterpolator(3f);
|
||||
public static final Interpolator DEACCEL_5 = new DecelerateInterpolator(5f);
|
||||
|
||||
public static final Interpolator ACCEL_DEACCEL = new AccelerateDecelerateInterpolator();
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
public ExtendedEditText mFolderName;
|
||||
private PageIndicatorDots mPageIndicator;
|
||||
|
||||
private View mFooter;
|
||||
protected View mFooter;
|
||||
private int mFooterHeight;
|
||||
|
||||
// Cell ranks used for drag and drop
|
||||
@@ -991,7 +991,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
lp.y = top;
|
||||
}
|
||||
|
||||
private int getContentAreaHeight() {
|
||||
protected int getContentAreaHeight() {
|
||||
DeviceProfile grid = mLauncher.getDeviceProfile();
|
||||
int maxContentAreaHeight = grid.availableHeightPx - grid.getTotalWorkspacePadding().y
|
||||
- mFooterHeight;
|
||||
|
||||
@@ -59,6 +59,8 @@ import java.util.List;
|
||||
*/
|
||||
public class FolderAnimationManager {
|
||||
|
||||
private static final int FOLDER_NAME_ALPHA_DURATION = 32;
|
||||
|
||||
private Folder mFolder;
|
||||
private FolderPagedView mContent;
|
||||
private GradientDrawable mFolderBackground;
|
||||
@@ -130,11 +132,19 @@ public class FolderAnimationManager {
|
||||
* scaleRelativeToDragLayer;
|
||||
final float finalScale = 1f;
|
||||
float scale = mIsOpening ? initialScale : finalScale;
|
||||
mFolder.setScaleX(scale);
|
||||
mFolder.setScaleY(scale);
|
||||
mFolder.setPivotX(0);
|
||||
mFolder.setPivotY(0);
|
||||
|
||||
// Scale the contents of the folder.
|
||||
mFolder.mContent.setScaleX(scale);
|
||||
mFolder.mContent.setScaleY(scale);
|
||||
mFolder.mContent.setPivotX(0);
|
||||
mFolder.mContent.setPivotY(0);
|
||||
mFolder.mFooter.setScaleX(scale);
|
||||
mFolder.mFooter.setScaleY(scale);
|
||||
mFolder.mFooter.setPivotX(0);
|
||||
mFolder.mFooter.setPivotY(0);
|
||||
|
||||
// We want to create a small X offset for the preview items, so that they follow their
|
||||
// expected path to their final locations. ie. an icon should not move right, if it's final
|
||||
// location is to its left. This value is arbitrarily defined.
|
||||
@@ -143,14 +153,13 @@ public class FolderAnimationManager {
|
||||
previewItemOffsetX = (int) (lp.width * initialScale - initialSize - previewItemOffsetX);
|
||||
}
|
||||
|
||||
final int paddingOffsetX = (int) ((mFolder.getPaddingLeft() + mContent.getPaddingLeft())
|
||||
* initialScale);
|
||||
final int paddingOffsetY = (int) ((mFolder.getPaddingTop() + mContent.getPaddingTop())
|
||||
* initialScale);
|
||||
final int paddingOffsetX = (int) (mContent.getPaddingLeft() * initialScale);
|
||||
final int paddingOffsetY = (int) (mContent.getPaddingTop() * initialScale);
|
||||
|
||||
int initialX = folderIconPos.left + mPreviewBackground.getOffsetX() - paddingOffsetX
|
||||
- previewItemOffsetX;
|
||||
int initialY = folderIconPos.top + mPreviewBackground.getOffsetY() - paddingOffsetY;
|
||||
int initialX = folderIconPos.left + mFolder.getPaddingLeft()
|
||||
+ mPreviewBackground.getOffsetX() - paddingOffsetX - previewItemOffsetX;
|
||||
int initialY = folderIconPos.top + mFolder.getPaddingTop()
|
||||
+ mPreviewBackground.getOffsetY() - paddingOffsetY;
|
||||
final float xDistance = initialX - lp.x;
|
||||
final float yDistance = initialY - lp.y;
|
||||
|
||||
@@ -164,11 +173,10 @@ public class FolderAnimationManager {
|
||||
|
||||
// Set up the reveal animation that clips the Folder.
|
||||
int totalOffsetX = paddingOffsetX + previewItemOffsetX;
|
||||
Rect startRect = new Rect(
|
||||
Math.round(totalOffsetX / initialScale),
|
||||
Math.round(paddingOffsetY / initialScale),
|
||||
Math.round((totalOffsetX + initialSize) / initialScale),
|
||||
Math.round((paddingOffsetY + initialSize) / initialScale));
|
||||
Rect startRect = new Rect(totalOffsetX,
|
||||
paddingOffsetY,
|
||||
Math.round((totalOffsetX + initialSize)),
|
||||
Math.round((paddingOffsetY + initialSize)));
|
||||
Rect endRect = new Rect(0, 0, lp.width, lp.height);
|
||||
float finalRadius = ResourceUtils.pxFromDp(2, mContext.getResources().getDisplayMetrics());
|
||||
|
||||
@@ -189,17 +197,46 @@ public class FolderAnimationManager {
|
||||
|
||||
play(a, getAnimator(mFolder, View.TRANSLATION_X, xDistance, 0f));
|
||||
play(a, getAnimator(mFolder, View.TRANSLATION_Y, yDistance, 0f));
|
||||
play(a, getAnimator(mFolder, SCALE_PROPERTY, initialScale, finalScale));
|
||||
play(a, getAnimator(mFolder.mContent, SCALE_PROPERTY, initialScale, finalScale));
|
||||
play(a, getAnimator(mFolder.mFooter, SCALE_PROPERTY, initialScale, finalScale));
|
||||
play(a, getAnimator(mFolderBackground, "color", initialColor, finalColor));
|
||||
play(a, mFolderIcon.mFolderName.createTextAlphaAnimator(!mIsOpening));
|
||||
play(a, getShape().createRevealAnimator(
|
||||
mFolder, startRect, endRect, finalRadius, !mIsOpening));
|
||||
// Fade in the folder name, as the text can overlap the icons when grid size is small.
|
||||
mFolder.mFolderName.setAlpha(mIsOpening ? 0f : 1f);
|
||||
play(a, getAnimator(mFolder.mFolderName, View.ALPHA, 0, 1),
|
||||
mIsOpening ? FOLDER_NAME_ALPHA_DURATION : 0,
|
||||
mIsOpening ? mDuration - FOLDER_NAME_ALPHA_DURATION : FOLDER_NAME_ALPHA_DURATION);
|
||||
|
||||
// Translate the footer so that it tracks the bottom of the content.
|
||||
float normalHeight = mFolder.getContentAreaHeight();
|
||||
float scaledHeight = normalHeight * initialScale;
|
||||
float diff = normalHeight - scaledHeight;
|
||||
play(a, getAnimator(mFolder.mFooter, View.TRANSLATION_Y, -diff, 0f));
|
||||
|
||||
// Animate the elevation midway so that the shadow is not noticeable in the background.
|
||||
int midDuration = mDuration / 2;
|
||||
Animator z = getAnimator(mFolder, View.TRANSLATION_Z, -mFolder.getElevation(), 0);
|
||||
play(a, z, mIsOpening ? midDuration : 0, midDuration);
|
||||
|
||||
|
||||
// Store clip variables
|
||||
CellLayout cellLayout = mContent.getCurrentCellLayout();
|
||||
boolean folderClipChildren = mFolder.getClipChildren();
|
||||
boolean folderClipToPadding = mFolder.getClipToPadding();
|
||||
boolean contentClipChildren = mContent.getClipChildren();
|
||||
boolean contentClipToPadding = mContent.getClipToPadding();
|
||||
boolean cellLayoutClipChildren = cellLayout.getClipChildren();
|
||||
boolean cellLayoutClipPadding = cellLayout.getClipToPadding();
|
||||
|
||||
mFolder.setClipChildren(false);
|
||||
mFolder.setClipToPadding(false);
|
||||
mContent.setClipChildren(false);
|
||||
mContent.setClipToPadding(false);
|
||||
cellLayout.setClipChildren(false);
|
||||
cellLayout.setClipToPadding(false);
|
||||
|
||||
a.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
@@ -207,8 +244,20 @@ public class FolderAnimationManager {
|
||||
mFolder.setTranslationX(0.0f);
|
||||
mFolder.setTranslationY(0.0f);
|
||||
mFolder.setTranslationZ(0.0f);
|
||||
mFolder.setScaleX(1f);
|
||||
mFolder.setScaleY(1f);
|
||||
mFolder.mContent.setScaleX(1f);
|
||||
mFolder.mContent.setScaleY(1f);
|
||||
mFolder.mFooter.setScaleX(1f);
|
||||
mFolder.mFooter.setScaleY(1f);
|
||||
mFolder.mFooter.setTranslationX(0f);
|
||||
mFolder.mFolderName.setAlpha(1f);
|
||||
|
||||
mFolder.setClipChildren(folderClipChildren);
|
||||
mFolder.setClipToPadding(folderClipToPadding);
|
||||
mContent.setClipChildren(contentClipChildren);
|
||||
mContent.setClipToPadding(contentClipToPadding);
|
||||
cellLayout.setClipChildren(cellLayoutClipChildren);
|
||||
cellLayout.setClipToPadding(cellLayoutClipPadding);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -413,10 +413,7 @@ public abstract class AbstractStateChangeTouchController
|
||||
} else {
|
||||
// Let the state manager know that the animation didn't go to the target state,
|
||||
// but don't cancel ourselves (we already clean up when the animation completes).
|
||||
Runnable onCancel = mCurrentAnimation.getOnCancelRunnable();
|
||||
mCurrentAnimation.setOnCancelRunnable(null);
|
||||
mCurrentAnimation.dispatchOnCancel();
|
||||
mCurrentAnimation.setOnCancelRunnable(onCancel);
|
||||
mCurrentAnimation.dispatchOnCancelWithoutCancelRunnable();
|
||||
|
||||
endProgress = 0;
|
||||
if (progress <= 0) {
|
||||
|
||||
@@ -560,7 +560,7 @@ public class FloatingIconView extends View implements
|
||||
* Checks if the icon result is loaded. If true, we set the icon immediately. Else, we add a
|
||||
* callback to set the icon once the icon result is loaded.
|
||||
*/
|
||||
private void checkIconResult(View originalView, boolean isOpening) {
|
||||
private void checkIconResult(View originalView) {
|
||||
CancellationSignal cancellationSignal = new CancellationSignal();
|
||||
|
||||
if (mIconLoadResult == null) {
|
||||
@@ -572,9 +572,7 @@ public class FloatingIconView extends View implements
|
||||
if (mIconLoadResult.isIconLoaded) {
|
||||
setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge,
|
||||
mIconLoadResult.iconOffset);
|
||||
if (isOpening) {
|
||||
hideOriginalView(originalView);
|
||||
}
|
||||
hideOriginalView(originalView);
|
||||
} else {
|
||||
mIconLoadResult.onIconLoaded = () -> {
|
||||
if (cancellationSignal.isCanceled()) {
|
||||
@@ -583,12 +581,8 @@ public class FloatingIconView extends View implements
|
||||
|
||||
setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge,
|
||||
mIconLoadResult.iconOffset);
|
||||
|
||||
setVisibility(VISIBLE);
|
||||
if (isOpening) {
|
||||
// Delay swapping views until the icon is loaded to prevent a flash.
|
||||
hideOriginalView(originalView);
|
||||
}
|
||||
hideOriginalView(originalView);
|
||||
};
|
||||
mLoadIconSignal = cancellationSignal;
|
||||
}
|
||||
@@ -596,9 +590,9 @@ public class FloatingIconView extends View implements
|
||||
}
|
||||
|
||||
private void hideOriginalView(View originalView) {
|
||||
if (originalView instanceof BubbleTextView) {
|
||||
((BubbleTextView) originalView).setIconVisible(false);
|
||||
((BubbleTextView) originalView).setForceHideDot(true);
|
||||
if (originalView instanceof IconLabelDotView) {
|
||||
((IconLabelDotView) originalView).setIconVisible(false);
|
||||
((IconLabelDotView) originalView).setForceHideDot(true);
|
||||
} else {
|
||||
originalView.setVisibility(INVISIBLE);
|
||||
}
|
||||
@@ -674,6 +668,9 @@ public class FloatingIconView extends View implements
|
||||
}
|
||||
|
||||
public void fastFinish() {
|
||||
if (mLoadIconSignal != null) {
|
||||
mLoadIconSignal.cancel();
|
||||
}
|
||||
if (mEndRunnable != null) {
|
||||
mEndRunnable.run();
|
||||
mEndRunnable = null;
|
||||
@@ -689,6 +686,10 @@ public class FloatingIconView extends View implements
|
||||
if (mIconLoadResult != null && mIconLoadResult.isIconLoaded) {
|
||||
setVisibility(View.VISIBLE);
|
||||
}
|
||||
if (!mIsOpening) {
|
||||
// When closing an app, we want the item on the workspace to be invisible immediately
|
||||
hideOriginalView(mOriginalIcon);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -798,7 +799,7 @@ public class FloatingIconView extends View implements
|
||||
// Must be called after the fastFinish listener and end runnable is created so that
|
||||
// the icon is not left in a hidden state.
|
||||
if (shouldLoadIcon) {
|
||||
view.checkIconResult(originalView, isOpening);
|
||||
view.checkIconResult(originalView);
|
||||
}
|
||||
|
||||
return view;
|
||||
@@ -842,6 +843,7 @@ public class FloatingIconView extends View implements
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
btv.setIconVisible(true);
|
||||
btv.setForceHideDot(true);
|
||||
}
|
||||
});
|
||||
fade.play(ObjectAnimator.ofInt(btv.getIcon(), DRAWABLE_ALPHA, 0, 255));
|
||||
|
||||
Reference in New Issue
Block a user