Merging from ub-launcher3-rvc-qpr-dev @ build 6770831

Bug:150504032
Test: manual, presubmit on the source branch
x20/teams/android-launcher/merge/ub-launcher3-rvc-qpr-dev_rvc-qpr-dev_6770831.html

Change-Id: Ic07dd8bd495cd0e013a00a29d9d9f11e246dc568
Merged-In: I5e9c73a7ac3033fe82006c4bd72824f56b8988f8
This commit is contained in:
Tony Wickham
2020-08-17 19:18:22 -07:00
50 changed files with 1090 additions and 217 deletions
@@ -17,6 +17,7 @@ package com.android.launcher3.uioverrides;
import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
@@ -57,7 +58,7 @@ public final class RecentsViewStateController extends
mRecentsView.updateEmptyMessage();
mRecentsView.resetTaskVisuals();
}
setAlphas(PropertySetter.NO_ANIM_PROPERTY_SETTER, state);
setAlphas(PropertySetter.NO_ANIM_PROPERTY_SETTER, new StateAnimationConfig(), state);
mRecentsView.setFullscreenProgress(state.getOverviewFullscreenProgress());
}
@@ -75,17 +76,19 @@ public final class RecentsViewStateController extends
AnimationSuccessListener.forRunnable(mRecentsView::resetTaskVisuals));
}
setAlphas(builder, toState);
setAlphas(builder, config, toState);
builder.setFloat(mRecentsView, FULLSCREEN_PROGRESS,
toState.getOverviewFullscreenProgress(), LINEAR);
}
private void setAlphas(PropertySetter propertySetter, LauncherState state) {
private void setAlphas(PropertySetter propertySetter, StateAnimationConfig config,
LauncherState state) {
float buttonAlpha = (state.getVisibleElements(mLauncher) & OVERVIEW_BUTTONS) != 0 ? 1 : 0;
propertySetter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
buttonAlpha, LINEAR);
propertySetter.setFloat(mLauncher.getActionsView().getVisibilityAlpha(),
MultiValueAlpha.VALUE, buttonAlpha, LINEAR);
MultiValueAlpha.VALUE, buttonAlpha, config.getInterpolator(
ANIM_OVERVIEW_ACTIONS_FADE, LINEAR));
}
@Override
@@ -25,6 +25,7 @@ import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
import static com.android.launcher3.WorkspaceStateTransitionAnimation.getSpringScaleAnimator;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
@@ -163,10 +164,15 @@ public class QuickstepAtomicAnimationFactory extends
config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
config.setInterpolator(ANIM_ALL_APPS_FADE, ACCEL);
config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL);
config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
Workspace workspace = mActivity.getWorkspace();
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL_DEACCEL);
if (SysUINavigationMode.getMode(mActivity) == NO_BUTTON) {
config.setInterpolator(ANIM_OVERVIEW_FADE, FINAL_FRAME);
} else {
config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
}
Workspace workspace = mActivity.getWorkspace();
// Start from a higher workspace scale, but only if we're invisible so we don't jump.
boolean isWorkspaceVisible = workspace.getVisibility() == VISIBLE;
if (isWorkspaceVisible) {
@@ -206,8 +212,10 @@ public class QuickstepAtomicAnimationFactory extends
config.setInterpolator(ANIM_WORKSPACE_SCALE,
fromState == NORMAL ? ACCEL : OVERSHOOT_1_2);
config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
config.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
} else {
config.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
config.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
// Scale up the recents, if it is not coming from the side
RecentsView overview = mActivity.getOverviewPanel();
@@ -225,7 +233,6 @@ public class QuickstepAtomicAnimationFactory extends
: OVERSHOOT_1_7;
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, translationInterpolator);
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, translationInterpolator);
config.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
} else if (fromState == HINT_STATE && toState == NORMAL) {
config.setInterpolator(ANIM_DEPTH, DEACCEL_3);
if (mHintToNormalDuration == -1) {
@@ -19,13 +19,13 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_EDU;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
import static com.android.launcher3.touch.AbstractStateChangeTouchController.SUCCESS_TRANSITION_PROGRESS;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import android.animation.ValueAnimator;
@@ -45,6 +45,7 @@ import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.OverviewScrim;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.testing.TestProtocol;
@@ -52,7 +53,9 @@ import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.AssistantUtilities;
import com.android.quickstep.util.OverviewToHomeAnim;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -63,6 +66,8 @@ public class NavBarToHomeTouchController implements TouchController,
SingleAxisSwipeDetector.Listener {
private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL_3;
// How much of the overview scrim we can remove during the transition.
private static final float OVERVIEW_TO_HOME_SCRIM_PROGRESS = 0.5f;
private final Launcher mLauncher;
private final SingleAxisSwipeDetector mSwipeDetector;
@@ -156,8 +161,13 @@ public class NavBarToHomeTouchController implements TouchController,
final PendingAnimation builder = new PendingAnimation(accuracy);
if (mStartState.overviewUi) {
RecentsView recentsView = mLauncher.getOverviewPanel();
builder.setFloat(recentsView, ADJACENT_PAGE_OFFSET,
-mPullbackDistance / recentsView.getPageOffsetScale(), PULLBACK_INTERPOLATOR);
AnimatorControllerWithResistance.createRecentsResistanceFromOverviewAnim(mLauncher,
builder);
float endScrimAlpha = Utilities.mapRange(OVERVIEW_TO_HOME_SCRIM_PROGRESS,
mStartState.getOverviewScrimAlpha(mLauncher),
mEndState.getOverviewScrimAlpha(mLauncher));
builder.setFloat(mLauncher.getDragLayer().getOverviewScrim(),
OverviewScrim.SCRIM_PROGRESS, endScrimAlpha, PULLBACK_INTERPOLATOR);
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
builder.addOnFrameCallback(
() -> recentsView.redrawLiveTile(false /* mightNeedToRefill */));
@@ -212,8 +222,13 @@ public class NavBarToHomeTouchController implements TouchController,
recentsView.switchToScreenshot(null,
() -> recentsView.finishRecentsAnimation(true /* toRecents */, null));
}
mLauncher.getStateManager().goToState(mEndState, true,
() -> onSwipeInteractionCompleted(mEndState));
if (mStartState == OVERVIEW) {
new OverviewToHomeAnim(mLauncher, () -> onSwipeInteractionCompleted(mEndState))
.animateWithVelocity(velocity);
} else {
mLauncher.getStateManager().goToState(mEndState, true,
() -> onSwipeInteractionCompleted(mEndState));
}
if (mStartState != mEndState) {
logStateChange(mStartState.containerType, logAction);
}
@@ -21,11 +21,8 @@ import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.graphics.PointF;
@@ -35,14 +32,15 @@ import android.view.MotionEvent;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.graphics.OverviewScrim;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.VibratorWrapper;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.OverviewToHomeAnim;
import com.android.quickstep.views.RecentsView;
/**
@@ -62,10 +60,10 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
private boolean mDidTouchStartInNavBar;
private boolean mReachedOverview;
private boolean mIsOverviewRehidden;
private boolean mIsHomeStaggeredAnimFinished;
// The last recorded displacement before we reached overview.
private PointF mStartDisplacement = new PointF();
private float mStartY;
private AnimatorPlaybackController mOverviewResistYAnim;
// Normal to Hint animation has flag SKIP_OVERVIEW, so we update this scrim with this animator.
private ObjectAnimator mNormalToHintOverviewScrimAnimator;
@@ -123,6 +121,7 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
mToState.getOverviewScrimAlpha(mLauncher));
}
mReachedOverview = false;
mOverviewResistYAnim = null;
}
@Override
@@ -137,6 +136,11 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
public void onDragEnd(float velocity) {
super.onDragEnd(velocity);
mNormalToHintOverviewScrimAnimator = null;
if (mLauncher.isInState(OVERVIEW)) {
// Normally we would cleanup the state based on mCurrentAnimation, but since we stop
// using that when we pause to go to Overview, we need to clean up ourselves.
clearState();
}
}
@Override
@@ -160,6 +164,9 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
mNormalToHintOverviewScrimAnimator = null;
mCurrentAnimation.dispatchOnCancelWithoutCancelRunnable(() -> {
mLauncher.getStateManager().goToState(OVERVIEW, true, () -> {
mOverviewResistYAnim = AnimatorControllerWithResistance
.createRecentsResistanceFromOverviewAnim(mLauncher, null)
.createPlaybackController();
mReachedOverview = true;
maybeSwipeInteractionToOverviewComplete();
});
@@ -173,13 +180,6 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
}
}
// Used if flinging back to home after reaching overview
private void maybeSwipeInteractionToHomeComplete() {
if (mIsHomeStaggeredAnimFinished && mIsOverviewRehidden) {
onSwipeInteractionCompleted(NORMAL, Touch.FLING);
}
}
@Override
protected boolean handlingOverviewAnim() {
return mDidTouchStartInNavBar && super.handlingOverviewAnim();
@@ -193,11 +193,17 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
if (mMotionPauseDetector.isPaused()) {
if (!mReachedOverview) {
mStartDisplacement.set(xDisplacement, yDisplacement);
mStartY = event.getY();
} else {
mRecentsView.setTranslationX((xDisplacement - mStartDisplacement.x)
* OVERVIEW_MOVEMENT_FACTOR);
mRecentsView.setTranslationY((yDisplacement - mStartDisplacement.y)
* OVERVIEW_MOVEMENT_FACTOR);
float yProgress = (mStartDisplacement.y - yDisplacement) / mStartY;
if (yProgress > 0 && mOverviewResistYAnim != null) {
mOverviewResistYAnim.setPlayFraction(yProgress);
} else {
mRecentsView.setTranslationY((yDisplacement - mStartDisplacement.y)
* OVERVIEW_MOVEMENT_FACTOR);
}
}
// Stay in Overview.
return true;
@@ -212,35 +218,8 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
StateManager<LauncherState> stateManager = mLauncher.getStateManager();
boolean goToHomeInsteadOfOverview = isFling;
if (goToHomeInsteadOfOverview) {
if (velocity > 0) {
stateManager.goToState(NORMAL, true,
() -> onSwipeInteractionCompleted(NORMAL, Touch.FLING));
} else {
mIsHomeStaggeredAnimFinished = mIsOverviewRehidden = false;
StaggeredWorkspaceAnim staggeredWorkspaceAnim = new StaggeredWorkspaceAnim(
mLauncher, velocity, false /* animateOverviewScrim */);
staggeredWorkspaceAnim.addAnimatorListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
mIsHomeStaggeredAnimFinished = true;
maybeSwipeInteractionToHomeComplete();
}
}).start();
// StaggeredWorkspaceAnim doesn't animate overview, so we handle it here.
stateManager.cancelAnimation();
StateAnimationConfig config = new StateAnimationConfig();
config.duration = OVERVIEW.getTransitionDuration(mLauncher);
config.animFlags = PLAY_ATOMIC_OVERVIEW_PEEK;
AnimatorSet anim = stateManager.createAtomicAnimation(
stateManager.getState(), NORMAL, config);
anim.addListener(AnimationSuccessListener.forRunnable(() -> {
mIsOverviewRehidden = true;
maybeSwipeInteractionToHomeComplete();
}));
anim.start();
}
new OverviewToHomeAnim(mLauncher, ()-> onSwipeInteractionCompleted(NORMAL, Touch.FLING))
.animateWithVelocity(velocity);
}
if (mReachedOverview) {
float distanceDp = dpiFromPx(Math.max(
@@ -256,6 +235,13 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
.withEndAction(goToHomeInsteadOfOverview
? null
: this::maybeSwipeInteractionToOverviewComplete);
if (!goToHomeInsteadOfOverview) {
// Return to normal properties for the overview state.
StateAnimationConfig config = new StateAnimationConfig();
config.duration = duration;
LauncherState state = mLauncher.getStateManager().getState();
mLauncher.getStateManager().createAtomicAnimation(state, state, config).start();
}
}
}
@@ -22,7 +22,6 @@ import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS;
import static com.android.launcher3.LauncherState.QUICK_SWITCH;
import static com.android.launcher3.anim.AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD;
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;
@@ -47,6 +46,7 @@ import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import android.animation.Animator;
@@ -74,7 +74,9 @@ 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.AnimatedFloat;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.ShelfPeekAnim;
@@ -90,16 +92,17 @@ 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 float Y_ANIM_MIN_PROGRESS = 0.25f;
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 static final Interpolator SCALE_DOWN_INTERPOLATOR = LINEAR;
private final BaseQuickstepLauncher mLauncher;
private final BothAxesSwipeDetector mSwipeDetector;
private final ShelfPeekAnim mShelfPeekAnim;
private final float mXRange;
private final float mYRange;
private final float mMaxYProgress;
private final MotionPauseDetector mMotionPauseDetector;
private final float mMotionPauseMinDisplacement;
private final LauncherRecentsView mRecentsView;
@@ -113,7 +116,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
// and the other two to set overview properties based on x and y progress.
private AnimatorPlaybackController mNonOverviewAnim;
private AnimatorPlaybackController mXOverviewAnim;
private AnimatorPlaybackController mYOverviewAnim;
private AnimatedFloat mYOverviewAnim;
public NoButtonQuickSwitchTouchController(BaseQuickstepLauncher launcher) {
mLauncher = launcher;
@@ -123,6 +126,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
mXRange = mLauncher.getDeviceProfile().widthPx / 2f;
mYRange = LayoutUtils.getShelfTrackingDistance(
mLauncher, mLauncher.getDeviceProfile(), mRecentsView.getPagedOrientationHandler());
mMaxYProgress = mLauncher.getDeviceProfile().heightPx / mYRange;
mMotionPauseDetector = new MotionPauseDetector(mLauncher);
mMotionPauseMinDisplacement = mLauncher.getResources().getDimension(
R.dimen.motion_pause_detector_min_displacement_from_app);
@@ -270,8 +274,18 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
SCALE_DOWN_INTERPOLATOR);
yAnim.setFloat(mRecentsView, FULLSCREEN_PROGRESS,
toState.getOverviewFullscreenProgress(), SCALE_DOWN_INTERPOLATOR);
mYOverviewAnim = yAnim.createPlaybackController();
mYOverviewAnim.dispatchOnStart();
AnimatorPlaybackController yNormalController = yAnim.createPlaybackController();
AnimatorControllerWithResistance yAnimWithResistance = AnimatorControllerWithResistance
.createForRecents(yNormalController, mLauncher,
mRecentsView.getPagedViewOrientedState(), mLauncher.getDeviceProfile(),
mRecentsView, RECENTS_SCALE_PROPERTY, mRecentsView,
TASK_SECONDARY_TRANSLATION);
mYOverviewAnim = new AnimatedFloat(() -> {
if (mYOverviewAnim != null) {
yAnimWithResistance.setProgress(mYOverviewAnim.value, mMaxYProgress);
}
});
yNormalController.dispatchOnStart();
}
@Override
@@ -307,7 +321,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
mXOverviewAnim.setPlayFraction(xProgress);
}
if (mYOverviewAnim != null) {
mYOverviewAnim.setPlayFraction(yProgress);
mYOverviewAnim.updateValue(yProgress);
}
return true;
}
@@ -354,9 +368,11 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
} 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.
// If user isn't flinging, just snap to the closest state.
boolean passedHorizontalThreshold = mXOverviewAnim.getInterpolatedProgress() > 0.5f;
targetState = passedHorizontalThreshold ? QUICK_SWITCH : NORMAL;
boolean passedVerticalThreshold = mYOverviewAnim.value > 1f;
targetState = passedHorizontalThreshold && !passedVerticalThreshold
? QUICK_SWITCH : NORMAL;
}
// Animate the various components to the target state.
@@ -375,9 +391,9 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
boolean flingUpToNormal = verticalFling && velocity.y < 0 && targetState == NORMAL;
float yProgress = mYOverviewAnim.getProgressFraction();
float yProgress = mYOverviewAnim.value;
float startYProgress = Utilities.boundToRange(yProgress
- velocity.y * getSingleFrameMs(mLauncher) / mYRange, 0f, 1f);
- velocity.y * getSingleFrameMs(mLauncher) / mYRange, 0f, mMaxYProgress);
final float endYProgress;
if (flingUpToNormal) {
endYProgress = 1;
@@ -387,12 +403,11 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
} else {
endYProgress = 0;
}
long yDuration = BaseSwipeDetector.calculateDuration(velocity.y,
Math.abs(endYProgress - startYProgress));
ValueAnimator yOverviewAnim = mYOverviewAnim.getAnimationPlayer();
yOverviewAnim.setFloatValues(startYProgress, endYProgress);
float yDistanceToCover = Math.abs(endYProgress - startYProgress) * mYRange;
long yDuration = (long) (yDistanceToCover / Math.max(1f, Math.abs(velocity.y)));
ValueAnimator yOverviewAnim = mYOverviewAnim.animateToValue(startYProgress, endYProgress);
yOverviewAnim.setDuration(yDuration);
mYOverviewAnim.dispatchOnStart();
mYOverviewAnim.updateValue(startYProgress);
ValueAnimator nonOverviewAnim = mNonOverviewAnim.getAnimationPlayer();
if (flingUpToNormal && !mIsHomeScreenVisible) {
@@ -457,7 +472,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
mXOverviewAnim.getAnimationPlayer().cancel();
}
if (mYOverviewAnim != null) {
mYOverviewAnim.getAnimationPlayer().cancel();
mYOverviewAnim.cancelAnimation();
}
mShelfPeekAnim.setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
mMotionPauseDetector.clear();
@@ -25,6 +25,7 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
@@ -206,14 +207,19 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
long maxDuration = 2 * secondaryLayerDimension;
int verticalFactor = orientationHandler.getTaskDragDisplacementFactor(mIsRtl);
int secondaryTaskDimension = orientationHandler.getSecondaryDimension(mTaskBeingDragged);
// The interpolator controlling the most prominent visual movement. We use this to determine
// whether we passed SUCCESS_TRANSITION_PROGRESS.
final Interpolator currentInterpolator;
if (goingUp) {
currentInterpolator = Interpolators.LINEAR;
mPendingAnimation = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged,
true /* animateTaskView */, true /* removeTask */, maxDuration);
mEndDisplacement = -secondaryTaskDimension;
} else {
currentInterpolator = Interpolators.ZOOM_IN;
mPendingAnimation = mRecentsView.createTaskLaunchAnimation(
mTaskBeingDragged, maxDuration, Interpolators.ZOOM_IN);
mTaskBeingDragged, maxDuration, currentInterpolator);
// Since the thumbnail is what is filling the screen, based the end displacement on it.
View thumbnailView = mTaskBeingDragged.getThumbnail();
@@ -228,6 +234,9 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
}
mCurrentAnimation = mPendingAnimation.createPlaybackController()
.setOnCancelRunnable(this::clearState);
// Setting this interpolator doesn't affect the visual motion, but is used to determine
// whether we successfully reached the target state in onDragEnd().
mCurrentAnimation.getTarget().setInterpolator(currentInterpolator);
onUserControlledAnimationCreated(mCurrentAnimation);
mCurrentAnimation.getTarget().addListener(this);
mCurrentAnimation.dispatchOnStart();
@@ -79,8 +79,8 @@ final class AppToOverviewAnimationProvider<T extends StatefulActivity<?>> extend
BaseActivityInterface.AnimationFactory factory = mActivityInterface.prepareRecentsUI(
mDeviceState,
wasVisible, (controller) -> {
controller.dispatchOnStart();
controller.getAnimationPlayer().end();
controller.getNormalController().dispatchOnStart();
controller.getNormalController().getAnimationPlayer().end();
});
factory.createActivityInterface(RECENTS_LAUNCH_DURATION);
factory.setRecentsAttachedToAppWindow(true, false);
@@ -365,8 +365,7 @@ public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extend
*/
protected void applyWindowTransform() {
if (mWindowTransitionController != null) {
float progress = mCurrentShift.value / mDragLengthFactor;
mWindowTransitionController.setPlayFraction(progress);
mWindowTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor);
}
if (mRecentsAnimationTargets != null) {
if (mRecentsViewScrollLinked) {
@@ -21,7 +21,6 @@ import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
@@ -45,7 +44,6 @@ import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHO
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
import android.animation.Animator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.app.ActivityManager;
@@ -67,7 +65,6 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.UserEventDispatcher;
@@ -80,6 +77,7 @@ import com.android.quickstep.BaseActivityInterface.AnimationFactory;
import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.inputconsumers.OverviewInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.ShelfPeekAnim;
import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
@@ -181,8 +179,7 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
private ThumbnailData mTaskSnapshot;
// Used to control launcher components throughout the swipe gesture.
private AnimatorPlaybackController mLauncherTransitionController;
private boolean mHasLauncherTransitionControllerStarted;
private AnimatorControllerWithResistance mLauncherTransitionController;
private AnimationFactory mAnimationFactory = (t) -> { };
@@ -491,6 +488,20 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
recentsAttachedToAppWindow = mIsShelfPeeking || mIsLikelyToStartNewTask;
}
mAnimationFactory.setRecentsAttachedToAppWindow(recentsAttachedToAppWindow, animate);
// Reapply window transform throughout the attach animation, as the animation affects how
// much the window is bound by overscroll (vs moving freely).
if (animate) {
ValueAnimator reapplyWindowTransformAnim = ValueAnimator.ofFloat(0, 1);
reapplyWindowTransformAnim.addUpdateListener(anim -> {
if (mRunningWindowAnim == null) {
applyWindowTransform();
}
});
reapplyWindowTransformAnim.setDuration(RECENTS_ATTACH_DURATION).start();
} else {
applyWindowTransform();
}
}
@Override
@@ -528,11 +539,11 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
/**
* We don't want to change mLauncherTransitionController if mGestureState.getEndTarget() == HOME
* (it has its own animation) or if we're already animating the current controller.
* (it has its own animation).
* @return Whether we can create the launcher controller or update its progress.
*/
private boolean canCreateNewOrUpdateExistingLauncherTransitionController() {
return mGestureState.getEndTarget() != HOME && !mHasLauncherTransitionControllerStarted;
return mGestureState.getEndTarget() != HOME;
}
@Override
@@ -542,10 +553,9 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
return result;
}
private void onAnimatorPlaybackControllerCreated(AnimatorPlaybackController anim) {
private void onAnimatorPlaybackControllerCreated(AnimatorControllerWithResistance anim) {
mLauncherTransitionController = anim;
mLauncherTransitionController.dispatchSetInterpolator(t -> t * mDragLengthFactor);
mLauncherTransitionController.dispatchOnStart();
mLauncherTransitionController.getNormalController().dispatchOnStart();
updateLauncherTransitionProgress();
}
@@ -582,10 +592,7 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
|| !canCreateNewOrUpdateExistingLauncherTransitionController()) {
return;
}
// 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);
mLauncherTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor);
}
/**
@@ -1028,31 +1035,6 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
windowAnim.start();
mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
}
// Always play the entire launcher animation when going home, since it is separate from
// the animation that has been controlled thus far.
if (mGestureState.getEndTarget() == HOME) {
start = 0;
}
// We want to use the same interpolator as the window, but need to adjust it to
// interpolate over the remaining progress (end - start).
TimeInterpolator adjustedInterpolator = Interpolators.mapToProgress(
interpolator, start, end);
if (mLauncherTransitionController == null) {
return;
}
if (start == end || duration <= 0) {
mLauncherTransitionController.dispatchSetInterpolator(t -> end);
} else {
mLauncherTransitionController.dispatchSetInterpolator(adjustedInterpolator);
}
mLauncherTransitionController.getAnimationPlayer().setDuration(Math.max(0, duration));
if (UNSTABLE_SPRINGS.get()) {
mLauncherTransitionController.dispatchOnStart();
}
mLauncherTransitionController.getAnimationPlayer().start();
mHasLauncherTransitionControllerStarted = true;
}
private void computeRecentsScrollIfInvisible() {
@@ -1173,10 +1155,6 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
private void cancelCurrentAnimation() {
mCanceled = true;
mCurrentShift.cancelAnimation();
if (mLauncherTransitionController != null && mLauncherTransitionController
.getAnimationPlayer().isStarted()) {
mLauncherTransitionController.getAnimationPlayer().cancel();
}
}
private void invalidateHandler() {
@@ -1202,7 +1180,10 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
private void endLauncherTransitionController() {
setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
if (mLauncherTransitionController != null) {
mLauncherTransitionController.getAnimationPlayer().end();
// End the animation, but stay at the same visual progress.
mLauncherTransitionController.getNormalController().dispatchSetInterpolator(
t -> Utilities.boundToRange(mCurrentShift.value, 0, 1));
mLauncherTransitionController.getNormalController().getAnimationPlayer().end();
mLauncherTransitionController = null;
}
}
@@ -27,11 +27,11 @@ import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.fallback.RecentsState;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -84,7 +84,7 @@ public final class FallbackActivityInterface extends
/** 6 */
@Override
public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
boolean activityVisible, Consumer<AnimatorPlaybackController> callback) {
boolean activityVisible, Consumer<AnimatorControllerWithResistance> callback) {
DefaultAnimationFactory factory = new DefaultAnimationFactory(callback);
factory.initUI();
return factory;
@@ -39,7 +39,6 @@ import com.android.launcher3.LauncherInitListener;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.statehandlers.DepthController;
@@ -50,6 +49,7 @@ import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
import com.android.quickstep.views.RecentsView;
@@ -119,7 +119,7 @@ public final class LauncherActivityInterface extends
@Override
public AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
boolean activityVisible, Consumer<AnimatorPlaybackController> callback) {
boolean activityVisible, Consumer<AnimatorControllerWithResistance> callback) {
notifyRecentsOfOrientation(deviceState.getRotationTouchHelper());
DefaultAnimationFactory factory = new DefaultAnimationFactory(callback) {
@Override
@@ -0,0 +1,40 @@
/*
* Copyright (C) 2020 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;
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import com.android.launcher3.R;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.systemui.plugins.OverscrollPlugin;
/**
* Resource overrideable factory for forcing a local overscroll plugin.
* Override {@link R.string#overscroll_plugin_factory_class} to set a different class.
*/
public class OverscrollPluginFactory implements ResourceBasedOverride {
public static final MainThreadInitializedObject<OverscrollPluginFactory> INSTANCE = forOverride(
OverscrollPluginFactory.class,
R.string.overscroll_plugin_factory_class);
/**
* Get the plugin that is defined locally in launcher, as opposed to a dynamic side loaded one.
*/
public OverscrollPlugin getLocalOverscrollPlugin() {
return null;
}
}
@@ -58,6 +58,12 @@ public class QuickstepTestInformationHandler extends TestInformationHandler {
FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get());
return response;
}
case TestProtocol.REQUEST_OVERVIEW_SHARE_ENABLED: {
response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
FeatureFlags.ENABLE_OVERVIEW_SHARE.get());
return response;
}
}
return super.call(method);
@@ -16,7 +16,7 @@
package com.android.quickstep;
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import android.animation.Animator;
import android.content.Context;
@@ -24,7 +24,6 @@ import android.graphics.Matrix;
import android.graphics.Matrix.ScaleToFit;
import android.graphics.Rect;
import android.graphics.RectF;
import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
@@ -35,6 +34,7 @@ import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
@@ -45,7 +45,6 @@ import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.
public abstract class SwipeUpAnimationLogic {
protected static final Rect TEMP_RECT = new Rect();
private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL;
protected DeviceProfile mDp;
@@ -66,12 +65,8 @@ public abstract class SwipeUpAnimationLogic {
protected int mTransitionDragLength;
// How much further we can drag past recents, as a factor of mTransitionDragLength.
protected float mDragLengthFactor = 1;
// Start resisting when swiping past this factor of mTransitionDragLength.
private float mDragLengthFactorStartPullback = 1f;
// This is how far down we can scale down, where 0f is full screen and 1f is recents.
private float mDragLengthFactorMaxPullback = 1f;
protected AnimatorPlaybackController mWindowTransitionController;
protected AnimatorControllerWithResistance mWindowTransitionController;
public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState,
GestureState gestureState, TransformParams transformParams) {
@@ -97,19 +92,17 @@ public abstract class SwipeUpAnimationLogic {
if (mDeviceState.isFullyGesturalNavMode()) {
// We can drag all the way to the top of the screen.
mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
float startScale = mTaskViewSimulator.getFullScreenScale();
// Start pulling back when RecentsView scale is 0.75f, and let it go down to 0.5f.
mDragLengthFactorStartPullback = (0.75f - startScale) / (1 - startScale);
mDragLengthFactorMaxPullback = (0.5f - startScale) / (1 - startScale);
} else {
mDragLengthFactor = 1;
mDragLengthFactorStartPullback = mDragLengthFactorMaxPullback = 1;
mDragLengthFactor = 1 + AnimatorControllerWithResistance.TWO_BUTTON_EXTRA_DRAG_FACTOR;
}
PendingAnimation pa = new PendingAnimation(mTransitionDragLength * 2);
mTaskViewSimulator.addAppToOverviewAnim(pa, t -> t * mDragLengthFactor);
mWindowTransitionController = pa.createPlaybackController();
mTaskViewSimulator.addAppToOverviewAnim(pa, LINEAR);
AnimatorPlaybackController normalController = pa.createPlaybackController();
mWindowTransitionController = AnimatorControllerWithResistance.createForRecents(
normalController, mContext, mTaskViewSimulator.getOrientationState(),
mDp, mTaskViewSimulator.recentsViewScale, AnimatedFloat.VALUE,
mTaskViewSimulator.recentsViewSecondaryTranslation, AnimatedFloat.VALUE);
}
@UiThread
@@ -122,13 +115,6 @@ public abstract class SwipeUpAnimationLogic {
} else {
float translation = Math.max(displacement, 0);
shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength;
if (shift > mDragLengthFactorStartPullback) {
float pullbackProgress = Utilities.getProgress(shift,
mDragLengthFactorStartPullback, mDragLengthFactor);
pullbackProgress = PULLBACK_INTERPOLATOR.getInterpolation(pullbackProgress);
shift = mDragLengthFactorStartPullback + pullbackProgress
* (mDragLengthFactorMaxPullback - mDragLengthFactorStartPullback);
}
}
mCurrentShift.updateValue(shift);
@@ -183,7 +169,7 @@ public abstract class SwipeUpAnimationLogic {
HomeAnimationFactory homeAnimationFactory) {
final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
mWindowTransitionController.setPlayFraction(startProgress / mDragLengthFactor);
mCurrentShift.updateValue(startProgress);
mTaskViewSimulator.apply(mTransformParams.setProgress(startProgress));
RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());
@@ -18,7 +18,6 @@ package com.android.quickstep;
import static android.view.Surface.ROTATION_0;
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import static com.android.quickstep.views.OverviewActionsView.DISABLED_NO_THUMBNAIL;
import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED;
@@ -39,13 +38,11 @@ import com.android.launcher3.R;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.TaskThumbnailView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.plugins.OverscrollPlugin;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -91,20 +88,22 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
return shortcuts;
}
public static final MainThreadInitializedObject<TaskOverlayFactory> INSTANCE =
forOverride(TaskOverlayFactory.class, R.string.task_overlay_factory_class);
/**
* @return a launcher-provided OverscrollPlugin if available, otherwise null
*/
public OverscrollPlugin getLocalOverscrollPlugin() {
return null;
}
public TaskOverlay createOverlay(TaskThumbnailView thumbnailView) {
return new TaskOverlay(thumbnailView);
}
/**
* Subclasses can attach any system listeners in this method, must be paired with
* {@link #removeListeners()}
*/
public void initListeners() { }
/**
* Subclasses should remove any system listeners in this method, must be paired with
* {@link #initListeners()}
*/
public void removeListeners() { }
/** Note that these will be shown in order from top to bottom, if available for the task. */
private static final TaskShortcutFactory[] MENU_OPTIONS = new TaskShortcutFactory[]{
TaskShortcutFactory.APP_INFO,
@@ -597,9 +597,8 @@ public class TouchInteractionService extends Service implements PluginListener<O
if (FeatureFlags.ENABLE_QUICK_CAPTURE_GESTURE.get()) {
OverscrollPlugin plugin = null;
if (FeatureFlags.FORCE_LOCAL_OVERSCROLL_PLUGIN.get()) {
TaskOverlayFactory factory =
TaskOverlayFactory.INSTANCE.get(getApplicationContext());
plugin = factory.getLocalOverscrollPlugin(); // may be null
plugin = OverscrollPluginFactory.INSTANCE.get(
getApplicationContext()).getLocalOverscrollPlugin();
}
// If not local plugin was forced, use the actual overscroll plugin if available.
@@ -19,6 +19,7 @@ import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
@@ -26,6 +27,7 @@ import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
@@ -86,6 +88,8 @@ public class FallbackRecentsStateController implements StateHandler<RecentsState
config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR));
setter.setFloat(mRecentsView, ADJACENT_PAGE_OFFSET, scaleAndOffset[1],
config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR));
setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f,
config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
setter.setFloat(mRecentsView, TASK_MODALNESS, state.getOverviewModalness(),
config.getInterpolator(ANIM_OVERVIEW_MODAL, LINEAR));
@@ -90,7 +90,6 @@ public class RecentsState implements BaseState<RecentsState> {
return new float[] { NO_SCALE, NO_OFFSET };
}
private static class ModalState extends RecentsState {
public ModalState(int id, int flags) {
@@ -0,0 +1,146 @@
/*
* Copyright (C) 2020 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.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
import static com.android.launcher3.anim.Interpolators.INSTANT;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.util.Log;
import android.view.animation.Interpolator;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.quickstep.views.RecentsView;
/**
* Runs an animation from overview to home. Currently, this animation is just a wrapper around the
* normal state transition, in order to keep RecentsView at the same scale and translationY that
* it started out at as it translates offscreen. It also scrolls RecentsView to page 0 and may play
* a {@link StaggeredWorkspaceAnim} if we're starting from an upward fling.
*/
public class OverviewToHomeAnim {
private static final String TAG = "OverviewToHomeAnim";
// Constants to specify how to scroll RecentsView to the default page if it's not already there.
private static final int DEFAULT_PAGE = 0;
private static final int PER_PAGE_SCROLL_DURATION = 150;
private static final int MAX_PAGE_SCROLL_DURATION = 750;
private final Launcher mLauncher;
private final Runnable mOnReachedHome;
// Only run mOnReachedHome when both of these are true.
private boolean mIsHomeStaggeredAnimFinished;
private boolean mIsOverviewHidden;
public OverviewToHomeAnim(Launcher launcher, Runnable onReachedHome) {
mLauncher = launcher;
mOnReachedHome = onReachedHome;
}
/**
* Starts the animation. If velocity < 0 (i.e. upwards), also plays a
* {@link StaggeredWorkspaceAnim}.
*/
public void animateWithVelocity(float velocity) {
StateManager<LauncherState> stateManager = mLauncher.getStateManager();
LauncherState startState = stateManager.getState();
if (startState != OVERVIEW) {
Log.e(TAG, "animateFromOverviewToHome: unexpected start state " + startState);
}
AnimatorSet anim = new AnimatorSet();
boolean playStaggeredWorkspaceAnim = velocity < 0;
if (playStaggeredWorkspaceAnim) {
StaggeredWorkspaceAnim staggeredWorkspaceAnim = new StaggeredWorkspaceAnim(
mLauncher, velocity, false /* animateOverviewScrim */);
staggeredWorkspaceAnim.addAnimatorListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
mIsHomeStaggeredAnimFinished = true;
maybeOverviewToHomeAnimComplete();
}
});
anim.play(staggeredWorkspaceAnim.getAnimators());
} else {
mIsHomeStaggeredAnimFinished = true;
}
RecentsView recentsView = mLauncher.getOverviewPanel();
int numPagesToScroll = recentsView.getNextPage() - DEFAULT_PAGE;
int scrollDuration = Math.min(MAX_PAGE_SCROLL_DURATION,
numPagesToScroll * PER_PAGE_SCROLL_DURATION);
int duration = Math.max(scrollDuration, startState.getTransitionDuration(mLauncher));
StateAnimationConfig config = new UseFirstInterpolatorStateAnimConfig();
config.duration = duration;
config.animFlags = playStaggeredWorkspaceAnim
// StaggeredWorkspaceAnim doesn't animate overview, so we handle it here.
? PLAY_ATOMIC_OVERVIEW_PEEK
: ANIM_ALL_COMPONENTS;
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, DEACCEL);
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, FINAL_FRAME);
config.setInterpolator(ANIM_OVERVIEW_SCALE, FINAL_FRAME);
config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, INSTANT);
AnimatorSet stateAnim = stateManager.createAtomicAnimation(
startState, NORMAL, config);
stateAnim.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
mIsOverviewHidden = true;
maybeOverviewToHomeAnimComplete();
}
});
anim.play(stateAnim);
stateManager.setCurrentAnimation(anim, NORMAL);
anim.start();
recentsView.snapToPage(DEFAULT_PAGE, duration);
}
private void maybeOverviewToHomeAnimComplete() {
if (mIsHomeStaggeredAnimFinished && mIsOverviewHidden) {
mOnReachedHome.run();
}
}
/**
* Wrapper around StateAnimationConfig that doesn't allow interpolators to be set if they are
* already set. This ensures they aren't overridden before being used.
*/
private static class UseFirstInterpolatorStateAnimConfig extends StateAnimationConfig {
@Override
public void setInterpolator(int animId, Interpolator interpolator) {
if (mInterpolators[animId] == null || interpolator == null) {
super.setInterpolator(animId, interpolator);
}
}
}
}
@@ -90,6 +90,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
// RecentsView properties
public final AnimatedFloat recentsViewScale = new AnimatedFloat();
public final AnimatedFloat fullScreenProgress = new AnimatedFloat();
public final AnimatedFloat recentsViewSecondaryTranslation = new AnimatedFloat();
private final ScrollState mScrollState = new ScrollState();
// Cached calculations
@@ -274,8 +275,10 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
mOrientationState.getOrientationHandler().set(
mMatrix, MATRIX_POST_TRANSLATE, mScrollState.scroll);
// Apply recensView matrix
// Apply RecentsView matrix
mMatrix.postScale(recentsViewScale.value, recentsViewScale.value, mPivot.x, mPivot.y);
mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
recentsViewSecondaryTranslation.value);
applyWindowToHomeRotation(mMatrix);
// Crop rect is the inverse of thumbnail matrix
@@ -51,6 +51,7 @@ import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.util.OverviewToHomeAnim;
import com.android.quickstep.util.TransformParams;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.RecentsExtraCard;
@@ -105,12 +106,14 @@ public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher>
@Override
public void startHome() {
Runnable onReachedHome = () -> mActivity.getStateManager().goToState(NORMAL, false);
OverviewToHomeAnim overviewToHomeAnim = new OverviewToHomeAnim(mActivity, onReachedHome);
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
switchToScreenshot(null,
() -> finishRecentsAnimation(true /* toRecents */,
() -> mActivity.getStateManager().goToState(NORMAL)));
() -> overviewToHomeAnim.animateWithVelocity(0)));
} else {
mActivity.getStateManager().goToState(NORMAL);
overviewToHomeAnim.animateWithVelocity(0);
}
}
@@ -105,6 +105,7 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
public OverviewActionsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr, 0);
mMultiValueAlpha = new MultiValueAlpha(this, 4);
mMultiValueAlpha.setUpdateVisibility(true);
}
@Override
@@ -168,7 +169,6 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
}
boolean isHidden = mHiddenFlags != 0;
mMultiValueAlpha.getProperty(INDEX_HIDDEN_FLAGS_ALPHA).setValue(isHidden ? 0 : 1);
setVisibility(isHidden ? INVISIBLE : VISIBLE);
}
/**
@@ -24,6 +24,7 @@ import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAG
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.Utilities.mapToRange;
import static com.android.launcher3.Utilities.squaredHypot;
@@ -93,6 +94,7 @@ import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherState;
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -115,6 +117,7 @@ import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.DynamicResource;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.OverScroller;
import com.android.launcher3.util.ResourceBasedOverride.Overrides;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.ViewPool;
import com.android.quickstep.BaseActivityInterface;
@@ -123,6 +126,7 @@ import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.RecentsModel.TaskVisualsChangeListener;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.ViewUtils;
@@ -210,6 +214,19 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
}
};
public static final FloatProperty<RecentsView> TASK_SECONDARY_TRANSLATION =
new FloatProperty<RecentsView>("taskSecondaryTranslation") {
@Override
public void setValue(RecentsView recentsView, float v) {
recentsView.setTaskViewsSecondaryTranslation(v);
}
@Override
public Float get(RecentsView recentsView) {
return recentsView.mTaskViewsSecondaryTranslation;
}
};
/** Same as normal SCALE_PROPERTY, but also updates page offsets that depend on this scale. */
public static final FloatProperty<RecentsView> RECENTS_SCALE_PROPERTY =
new FloatProperty<RecentsView>("recentsScale") {
@@ -219,6 +236,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
view.setScaleY(scale);
view.mLastComputedTaskPushOutDistance = null;
view.updatePageOffsets();
view.setTaskViewsSecondaryTranslation(view.mTaskViewsSecondaryTranslation);
}
@Override
@@ -263,12 +281,15 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
private final ViewPool<TaskView> mTaskViewPool;
private final TaskOverlayFactory mTaskOverlayFactory;
private boolean mDwbToastShown;
protected boolean mDisallowScrollToClearAll;
private boolean mOverlayEnabled;
protected boolean mFreezeViewVisibility;
private float mAdjacentPageOffset = 0;
private float mTaskViewsSecondaryTranslation = 0;
/**
* TODO: Call reloadIdNeeded in onTaskStackChanged.
@@ -449,6 +470,11 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
updateEmptyMessage();
mOrientationHandler = mOrientationState.getOrientationHandler();
mTaskOverlayFactory = Overrides.getObject(
TaskOverlayFactory.class,
context.getApplicationContext(),
R.string.task_overlay_factory_class);
// Initialize quickstep specific cache params here, as this is constructed only once
mActivity.getViewCache().setCacheSize(R.layout.digital_wellbeing_toast, 5);
}
@@ -542,6 +568,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
mIPinnedStackAnimationListener);
mOrientationState.initListeners();
SplitScreenBounds.INSTANCE.addOnChangeListener(this);
mTaskOverlayFactory.initListeners();
}
@Override
@@ -558,6 +585,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
SplitScreenBounds.INSTANCE.removeOnChangeListener(this);
mIPinnedStackAnimationListener.setActivity(null);
mOrientationState.destroyListeners();
mTaskOverlayFactory.removeListeners();
}
@Override
@@ -1391,7 +1419,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
FloatProperty<View> secondaryViewTranslate =
mOrientationHandler.getSecondaryViewTranslate();
int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView);
int verticalFactor = mOrientationHandler.getTaskDismissDirectionFactor();
int verticalFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor();
ResourceProvider rp = DynamicResource.provider(mActivity);
SpringProperty sp = new SpringProperty(SpringProperty.FLAG_CAN_SPRING_ON_START)
@@ -1697,6 +1725,12 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
if (mOrientationState.setRecentsRotation(mActivity.getDisplay().getRotation())) {
updateOrientationHandler();
}
// If overview is in modal state when rotate, reset it to overview state without running
// animation.
if (mActivity.isInState(OVERVIEW_MODAL_TASK)) {
mActivity.getStateManager().goToState(LauncherState.OVERVIEW, false);
resetModalVisuals();
}
}
public void setLayoutRotation(int touchRotation, int displayRotation) {
@@ -1853,7 +1887,9 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
: i < modalMidpoint
? modalLeftOffsetSize
: modalRightOffsetSize;
getChildAt(i).setTranslationX(translation + modalTranslation);
float totalTranslation = translation + modalTranslation;
mOrientationHandler.getPrimaryViewTranslate().set(getChildAt(i),
totalTranslation * mOrientationHandler.getPrimaryTranslationDirectionFactor());
}
updateCurveProperties();
}
@@ -1906,6 +1942,14 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
return distanceToOffscreen * offsetProgress;
}
private void setTaskViewsSecondaryTranslation(float translation) {
mTaskViewsSecondaryTranslation = translation;
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView task = getTaskViewAt(i);
mOrientationHandler.getSecondaryViewTranslate().set(task, translation / getScaleY());
}
}
/**
* TODO: Do not assume motion across X axis for adjacent page
*/
@@ -2290,7 +2334,14 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
if (pageIndex == -1) {
return 0;
}
return getScrollForPage(pageIndex) - mOrientationHandler.getPrimaryScroll(this);
// Unbound the scroll (due to overscroll) if the adjacent tasks are offset away from it.
// This allows the page to move freely, given there's no visual indication why it shouldn't.
int boundedScroll = mOrientationHandler.getPrimaryScroll(this);
int unboundedScroll = getUnboundedScroll();
float unboundedProgress = mAdjacentPageOffset;
int scroll = Math.round(unboundedScroll * unboundedProgress
+ boundedScroll * (1 - unboundedProgress));
return getScrollForPage(pageIndex) - scroll;
}
public Consumer<MotionEvent> getEventDispatcher(float navbarRotation) {
@@ -2392,6 +2443,10 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
*/
public void setModalStateEnabled(boolean isModalState) { }
public TaskOverlayFactory getTaskOverlayFactory() {
return mTaskOverlayFactory;
}
public BaseActivityInterface getSizeStrategy() {
return mSizeStrategy;
}
@@ -52,7 +52,6 @@ import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
import com.android.quickstep.views.TaskView.FullscreenDrawParams;
import com.android.systemui.plugins.OverviewScreenshotActions;
@@ -85,7 +84,7 @@ public class TaskThumbnailView extends View implements PluginListener<OverviewSc
};
private final BaseActivity mActivity;
private final TaskOverlay mOverlay;
private TaskOverlay mOverlay;
private final boolean mIsDarkTextTheme;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -118,7 +117,6 @@ public class TaskThumbnailView extends View implements PluginListener<OverviewSc
public TaskThumbnailView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mOverlay = TaskOverlayFactory.INSTANCE.get(context).createOverlay(this);
mPaint.setFilterBitmap(true);
mBackgroundPaint.setColor(Color.WHITE);
mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
@@ -134,7 +132,7 @@ public class TaskThumbnailView extends View implements PluginListener<OverviewSc
* @param task
*/
public void bind(Task task) {
mOverlay.reset();
getTaskOverlay().reset();
mTask = task;
int color = task == null ? Color.BLACK : task.colorBackground | 0xFF000000;
mPaint.setColor(color);
@@ -176,7 +174,7 @@ public class TaskThumbnailView extends View implements PluginListener<OverviewSc
mBitmapShader = null;
mThumbnailData = null;
mPaint.setShader(null);
mOverlay.reset();
getTaskOverlay().reset();
}
if (mOverviewScreenshotActionsPlugin != null) {
mOverviewScreenshotActionsPlugin.setupActions(getTaskView(), getThumbnail(), mActivity);
@@ -200,6 +198,9 @@ public class TaskThumbnailView extends View implements PluginListener<OverviewSc
}
public TaskOverlay getTaskOverlay() {
if (mOverlay == null) {
mOverlay = getTaskView().getRecentsView().getTaskOverlayFactory().createOverlay(this);
}
return mOverlay;
}
@@ -357,10 +358,10 @@ public class TaskThumbnailView extends View implements PluginListener<OverviewSc
private void updateOverlay() {
if (mOverlayEnabled) {
mOverlay.initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.mMatrix,
getTaskOverlay().initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.mMatrix,
mPreviewPositionHelper.mIsOrientationChanged);
} else {
mOverlay.reset();
getTaskOverlay().reset();
}
}
@@ -479,17 +480,18 @@ public class TaskThumbnailView extends View implements PluginListener<OverviewSc
boolean isOrientationDifferent;
mClipBottom = -1;
int thumbnailRotation = thumbnailData.rotation;
int deltaRotate = getRotationDelta(currentRotation, thumbnailRotation);
Rect thumbnailInsets = getBoundedInsets(
dp.getInsets(), thumbnailData.insets, deltaRotate);
float scale = thumbnailData.scale;
Rect activityInsets = dp.getInsets();
Rect thumbnailInsets = getBoundedInsets(activityInsets, thumbnailData.insets);
final float thumbnailWidth = thumbnailPosition.width()
- (thumbnailInsets.left + thumbnailInsets.right) * scale;
final float thumbnailHeight = thumbnailPosition.height()
- (thumbnailInsets.top + thumbnailInsets.bottom) * scale;
final float thumbnailScale;
int thumbnailRotation = thumbnailData.rotation;
int deltaRotate = getRotationDelta(currentRotation, thumbnailRotation);
// Landscape vs portrait change
boolean windowingModeSupportsRotation = !dp.isMultiWindowMode
@@ -558,7 +560,10 @@ public class TaskThumbnailView extends View implements PluginListener<OverviewSc
mIsOrientationChanged = isOrientationDifferent;
}
private Rect getBoundedInsets(Rect activityInsets, Rect insets) {
private Rect getBoundedInsets(Rect activityInsets, Rect insets, int deltaRotation) {
if (deltaRotation != 0) {
return insets;
}
return new Rect(Math.min(insets.left, activityInsets.left),
Math.min(insets.top, activityInsets.top),
Math.min(insets.right, activityInsets.right),
+2
View File
@@ -16,6 +16,8 @@
<resources>
<string name="task_overlay_factory_class" translatable="false"/>
<string name="overscroll_plugin_factory_class" translatable="false" />
<!-- Activities which block home gesture -->
<string-array name="gesture_blocking_activities" translatable="false">
<item>com.android.launcher3/com.android.quickstep.interaction.GestureSandboxActivity</item>
@@ -43,9 +43,11 @@ import android.util.ArrayMap;
import android.util.Log;
import androidx.annotation.MainThread;
import androidx.annotation.WorkerThread;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.popup.RemoteActionShortcut;
import com.android.launcher3.popup.SystemShortcut;
@@ -57,6 +59,7 @@ import com.android.launcher3.util.SimpleBroadcastReceiver;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
/**
* Data model for digital wellbeing status of apps.
@@ -72,6 +75,9 @@ public final class WellbeingModel {
private static final int MSG_FULL_REFRESH = 3;
// Welbeing contract
private static final String PATH_ACTIONS = "actions";
private static final String PATH_MINIMAL_DEVICE = "minimal_device";
private static final String METHOD_GET_MINIMAL_DEVICE_CONFIG = "get_minimal_device_config";
private static final String METHOD_GET_ACTIONS = "get_actions";
private static final String EXTRA_ACTIONS = "actions";
private static final String EXTRA_ACTION = "action";
@@ -104,15 +110,22 @@ public final class WellbeingModel {
mContentObserver = new ContentObserver(MAIN_EXECUTOR.getHandler()) {
@Override
public void onChange(boolean selfChange, Uri uri) {
// Wellbeing reports that app actions have changed.
if (DEBUG || mIsInTest) {
Log.d(TAG, "ContentObserver.onChange() called with: selfChange = [" + selfChange
+ "], uri = [" + uri + "]");
Log.d(TAG, "ContentObserver.onChange() called with: selfChange = ["
+ selfChange + "], uri = [" + uri + "]");
}
Preconditions.assertUIThread();
updateWellbeingData();
if (uri.getPath().contains(PATH_ACTIONS)) {
// Wellbeing reports that app actions have changed.
updateWellbeingData();
} else if (uri.getPath().contains(PATH_MINIMAL_DEVICE)) {
// Wellbeing reports that minimal device state or config is changed.
updateLauncherModel();
}
}
};
FeatureFlags.ENABLE_MINIMAL_DEVICE.addChangeListener(mContext, this::updateLauncherModel);
if (!TextUtils.isEmpty(mWellbeingProviderPkg)) {
context.registerReceiver(
@@ -146,14 +159,18 @@ public final class WellbeingModel {
private void restartObserver() {
final ContentResolver resolver = mContext.getContentResolver();
resolver.unregisterContentObserver(mContentObserver);
Uri actionsUri = apiBuilder().path("actions").build();
Uri actionsUri = apiBuilder().path(PATH_ACTIONS).build();
Uri minimalDeviceUri = apiBuilder().path(PATH_MINIMAL_DEVICE).build();
try {
resolver.registerContentObserver(
actionsUri, true /* notifyForDescendants */, mContentObserver);
resolver.registerContentObserver(
minimalDeviceUri, true /* notifyForDescendants */, mContentObserver);
} catch (Exception e) {
Log.e(TAG, "Failed to register content observer for " + actionsUri + ": " + e);
if (mIsInTest) throw new RuntimeException(e);
}
updateWellbeingData();
}
@@ -191,12 +208,42 @@ public final class WellbeingModel {
mWorkerHandler.sendEmptyMessage(MSG_FULL_REFRESH);
}
private void updateLauncherModel() {
if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) return;
// TODO: init Launcher in minimal device / normal mode
}
private Uri.Builder apiBuilder() {
return new Uri.Builder()
.scheme(SCHEME_CONTENT)
.authority(mWellbeingProviderPkg + ".api");
}
/**
* Fetch most up-to-date minimal device config.
*/
@WorkerThread
private void runWithMinimalDeviceConfigs(Consumer<Bundle> consumer) {
if (DEBUG || mIsInTest) {
Log.d(TAG, "runWithMinimalDeviceConfigs() called");
}
Preconditions.assertNonUiThread();
final Uri contentUri = apiBuilder().build();
final Bundle remoteBundle;
try (ContentProviderClient client = mContext.getContentResolver()
.acquireUnstableContentProviderClient(contentUri)) {
remoteBundle = client.call(
METHOD_GET_MINIMAL_DEVICE_CONFIG, null /* args */, null /* extras */);
consumer.accept(remoteBundle);
} catch (Exception e) {
Log.e(TAG, "Failed to retrieve data from " + contentUri + ": " + e);
if (mIsInTest) throw new RuntimeException(e);
}
if (DEBUG || mIsInTest) Log.i(TAG, "runWithMinimalDeviceConfigs(): finished");
}
private boolean updateActions(String... packageNames) {
if (packageNames.length == 0) {
return true;
@@ -24,11 +24,13 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MO
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCRIM_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
import android.util.FloatProperty;
@@ -63,6 +65,7 @@ public abstract class BaseRecentsViewStateController<T extends RecentsView>
float[] scaleAndOffset = state.getOverviewScaleAndOffset(mLauncher);
RECENTS_SCALE_PROPERTY.set(mRecentsView, scaleAndOffset[0]);
ADJACENT_PAGE_OFFSET.set(mRecentsView, scaleAndOffset[1]);
TASK_SECONDARY_TRANSLATION.set(mRecentsView, 0f);
getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim();
@@ -97,6 +100,8 @@ public abstract class BaseRecentsViewStateController<T extends RecentsView>
config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR));
setter.setFloat(mRecentsView, ADJACENT_PAGE_OFFSET, scaleAndOffset[1],
config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR));
setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f,
config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
@@ -18,6 +18,7 @@ package com.android.launcher3.uioverrides.states;
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
import android.content.Context;
@@ -92,7 +93,8 @@ public class AllAppsState extends LauncherState {
@Override
public float[] getOverviewScaleAndOffset(Launcher launcher) {
return new float[] {0.9f, 0};
float offset = ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(launcher) ? 1 : 0;
return new float[] {0.9f, offset};
}
@Override
@@ -142,6 +142,10 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS,
"PortraitStatesTouchController.getTargetState 1");
}
if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(mLauncher)) {
// Don't allow swiping down to overview.
return NORMAL;
}
return TouchInteractionService.isConnected() ?
mLauncher.getStateManager().getLastState() : NORMAL;
} else if (fromState == OVERVIEW) {
@@ -28,6 +28,7 @@ import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_REC
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
import android.animation.Animator;
import android.annotation.TargetApi;
@@ -52,6 +53,7 @@ import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.WindowBounds;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.ShelfPeekAnim;
import com.android.quickstep.util.SplitScreenBounds;
import com.android.quickstep.views.RecentsView;
@@ -106,7 +108,7 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
public abstract void onAssistantVisibilityChanged(float visibility);
public abstract AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
boolean activityVisible, Consumer<AnimatorPlaybackController> callback);
boolean activityVisible, Consumer<AnimatorControllerWithResistance> callback);
public abstract ActivityInitListener createActivityInitListener(
Predicate<Boolean> onInitListener);
@@ -319,11 +321,11 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
protected final ACTIVITY_TYPE mActivity;
private final STATE_TYPE mStartState;
private final Consumer<AnimatorPlaybackController> mCallback;
private final Consumer<AnimatorControllerWithResistance> mCallback;
private boolean mIsAttachedToWindow;
DefaultAnimationFactory(Consumer<AnimatorPlaybackController> callback) {
DefaultAnimationFactory(Consumer<AnimatorControllerWithResistance> callback) {
mCallback = callback;
mActivity = getCreatedActivity();
@@ -351,7 +353,14 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
controller.setEndAction(() -> mActivity.getStateManager().goToState(
controller.getInterpolatedProgress() > 0.5 ? mOverviewState : mBackgroundState,
false));
mCallback.accept(controller);
RecentsView recentsView = mActivity.getOverviewPanel();
AnimatorControllerWithResistance controllerWithResistance =
AnimatorControllerWithResistance.createForRecents(controller, mActivity,
recentsView.getPagedViewOrientedState(), mActivity.getDeviceProfile(),
recentsView, RECENTS_SCALE_PROPERTY, recentsView,
TASK_SECONDARY_TRANSLATION);
mCallback.accept(controllerWithResistance);
// Creating the activity controller animation sometimes reapplies the launcher state
// (because we set the animation as the current state animation), so we reapply the
@@ -184,8 +184,7 @@ abstract class SwipeUpGestureTutorialController extends TutorialController {
@Override
public void updateFinalShift() {
float progress = mCurrentShift.value / mDragLengthFactor;
mWindowTransitionController.setPlayFraction(progress);
mWindowTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor);
mTaskViewSimulator.apply(mTransformParams);
}
@@ -0,0 +1,230 @@
/*
* Copyright (C) 2020 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.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
import android.animation.TimeInterpolator;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.FloatProperty;
import androidx.annotation.Nullable;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.views.RecentsView;
/**
* Controls an animation that can go beyond progress = 1, at which point resistance should be
* applied. Internally, this is a wrapper around 2 {@link AnimatorPlaybackController}s, one that
* runs from progress 0 to 1 like normal, then one that seamlessly continues that animation but
* starts applying resistance as well.
*/
public class AnimatorControllerWithResistance {
/**
* How much farther we can drag past overview in 2-button mode, as a factor of the distance
* it takes to drag from an app to overview.
*/
public static final float TWO_BUTTON_EXTRA_DRAG_FACTOR = 0.25f;
private enum RecentsParams {
FROM_APP(0.75f, 0.5f, 1f),
FROM_OVERVIEW(1f, 0.75f, 0.5f);
RecentsParams(float scaleStartResist, float scaleMaxResist, float translationFactor) {
this.scaleStartResist = scaleStartResist;
this.scaleMaxResist = scaleMaxResist;
this.translationFactor = translationFactor;
}
/**
* Start slowing down the rate of scaling down when recents view is smaller than this scale.
*/
public final float scaleStartResist;
/**
* Recents view will reach this scale at the very end of the drag.
*/
public final float scaleMaxResist;
/**
* How much translation to apply to RecentsView when the drag reaches the top of the screen,
* where 0 will keep it centered and 1 will have it barely touch the top of the screen.
*/
public final float translationFactor;
}
private static final TimeInterpolator RECENTS_SCALE_RESIST_INTERPOLATOR = DEACCEL;
private static final TimeInterpolator RECENTS_TRANSLATE_RESIST_INTERPOLATOR = LINEAR;
private final AnimatorPlaybackController mNormalController;
private final AnimatorPlaybackController mResistanceController;
// Initialize to -1 so the first 0 gets applied.
private float mLastNormalProgress = -1;
private float mLastResistProgress;
public AnimatorControllerWithResistance(AnimatorPlaybackController normalController,
AnimatorPlaybackController resistanceController) {
mNormalController = normalController;
mResistanceController = resistanceController;
}
public AnimatorPlaybackController getNormalController() {
return mNormalController;
}
/**
* Applies the current progress of the animation.
* @param progress From 0 to maxProgress, where 1 is the target we are animating towards.
* @param maxProgress > 1, this is where the resistance will be applied.
*/
public void setProgress(float progress, float maxProgress) {
float normalProgress = Utilities.boundToRange(progress, 0, 1);
if (normalProgress != mLastNormalProgress) {
mLastNormalProgress = normalProgress;
mNormalController.setPlayFraction(normalProgress);
}
if (maxProgress <= 1) {
return;
}
float resistProgress = progress <= 1 ? 0 : Utilities.getProgress(progress, 1, maxProgress);
if (resistProgress != mLastResistProgress) {
mLastResistProgress = resistProgress;
mResistanceController.setPlayFraction(resistProgress);
}
}
/**
* Applies resistance to recents when swiping up past its target position.
* @param normalController The controller to run from 0 to 1 before this resistance applies.
* @param context Used to compute start and end values.
* @param recentsOrientedState Used to compute start and end values.
* @param dp Used to compute start and end values.
* @param scaleTarget The target for the scaleProperty.
* @param scaleProperty Animate the value to change the scale of the window/recents view.
* @param translationTarget The target for the translationProperty.
* @param translationProperty Animate the value to change the translation of the recents view.
*/
public static <SCALE, TRANSLATION> AnimatorControllerWithResistance createForRecents(
AnimatorPlaybackController normalController, Context context,
RecentsOrientedState recentsOrientedState, DeviceProfile dp, SCALE scaleTarget,
FloatProperty<SCALE> scaleProperty, TRANSLATION translationTarget,
FloatProperty<TRANSLATION> translationProperty) {
PendingAnimation resistAnim = createRecentsResistanceAnim(null, context,
recentsOrientedState, dp, scaleTarget, scaleProperty, translationTarget,
translationProperty, RecentsParams.FROM_APP);
AnimatorPlaybackController resistanceController = resistAnim.createPlaybackController();
return new AnimatorControllerWithResistance(normalController, resistanceController);
}
/**
* Creates the resistance animation for {@link #createForRecents}, or can be used separately
* when starting from recents, i.e. {@link #createRecentsResistanceFromOverviewAnim}.
*/
public static <SCALE, TRANSLATION> PendingAnimation createRecentsResistanceAnim(
@Nullable PendingAnimation resistAnim, Context context,
RecentsOrientedState recentsOrientedState, DeviceProfile dp, SCALE scaleTarget,
FloatProperty<SCALE> scaleProperty, TRANSLATION translationTarget,
FloatProperty<TRANSLATION> translationProperty, RecentsParams params) {
Rect startRect = new Rect();
LauncherActivityInterface.INSTANCE.calculateTaskSize(context, dp, startRect,
recentsOrientedState.getOrientationHandler());
long distanceToCover = startRect.bottom;
boolean isTwoButtonMode = SysUINavigationMode.getMode(context) == TWO_BUTTONS;
if (isTwoButtonMode) {
// We can only drag a small distance past overview, not to the top of the screen.
distanceToCover = (long)
((dp.heightPx - startRect.bottom) * TWO_BUTTON_EXTRA_DRAG_FACTOR);
}
if (resistAnim == null) {
resistAnim = new PendingAnimation(distanceToCover * 2);
}
PointF pivot = new PointF();
float fullscreenScale = recentsOrientedState.getFullScreenScaleAndPivot(
startRect, dp, pivot);
float startScale = 1;
float prevScaleRate = (fullscreenScale - startScale) / (dp.heightPx - startRect.bottom);
// This is what the scale would be at the end of the drag if we didn't apply resistance.
float endScale = startScale - prevScaleRate * distanceToCover;
final TimeInterpolator scaleInterpolator;
if (isTwoButtonMode) {
// We are bounded by the distance of the drag, so we don't need to apply resistance.
scaleInterpolator = LINEAR;
} else {
// Create an interpolator that resists the scale so the scale doesn't get smaller than
// RECENTS_SCALE_MAX_RESIST.
float startResist = Utilities.getProgress(params.scaleStartResist , startScale,
endScale);
float maxResist = Utilities.getProgress(params.scaleMaxResist, startScale, endScale);
scaleInterpolator = t -> {
if (t < startResist) {
return t;
}
float resistProgress = Utilities.getProgress(t, startResist, 1);
resistProgress = RECENTS_SCALE_RESIST_INTERPOLATOR.getInterpolation(resistProgress);
return startResist + resistProgress * (maxResist - startResist);
};
}
resistAnim.addFloat(scaleTarget, scaleProperty, startScale, endScale,
scaleInterpolator);
if (!isTwoButtonMode) {
// Compute where the task view would be based on the end scale, if we didn't translate.
RectF endRectF = new RectF(startRect);
Matrix temp = new Matrix();
temp.setScale(params.scaleMaxResist, params.scaleMaxResist, pivot.x, pivot.y);
temp.mapRect(endRectF);
// Translate such that the task view touches the top of the screen when drag does.
float endTranslation = endRectF.top * recentsOrientedState.getOrientationHandler()
.getSecondaryTranslationDirectionFactor() * params.translationFactor;
resistAnim.addFloat(translationTarget, translationProperty, 0, endTranslation,
RECENTS_TRANSLATE_RESIST_INTERPOLATOR);
}
return resistAnim;
}
/**
* Helper method to update or create a PendingAnimation suitable for animating
* a RecentsView interaction that started from the overview state.
*/
public static PendingAnimation createRecentsResistanceFromOverviewAnim(
BaseDraggingActivity activity, @Nullable PendingAnimation resistanceAnim) {
RecentsView recentsView = activity.getOverviewPanel();
return createRecentsResistanceAnim(resistanceAnim, activity,
recentsView.getPagedViewOrientedState(), activity.getDeviceProfile(),
recentsView, RECENTS_SCALE_PROPERTY, recentsView, TASK_SECONDARY_TRANSLATION,
RecentsParams.FROM_OVERVIEW);
}
}
@@ -36,6 +36,7 @@ import com.android.launcher3.tapl.AllAppsFromOverview;
import com.android.launcher3.tapl.Background;
import com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel;
import com.android.launcher3.tapl.Overview;
import com.android.launcher3.tapl.OverviewActions;
import com.android.launcher3.tapl.OverviewTask;
import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.ui.TaplTestsLauncher3;
@@ -68,11 +69,14 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
});
}
private void startTestApps() throws Exception {
public static void startTestApps() throws Exception {
startAppFast(getAppPackageName());
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
startTestActivity(2);
}
private void startTestAppsWithCheck() throws Exception {
startTestApps();
executeOnLauncher(launcher -> assertTrue(
"Launcher activity is the top activity; expecting another activity to be the top "
+ "one",
@@ -105,7 +109,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
@Test
@PortraitLandscape
public void testOverview() throws Exception {
startTestApps();
startTestAppsWithCheck();
// mLauncher.pressHome() also tests an important case of pressing home while in background.
Overview overview = mLauncher.pressHome().switchToOverview();
assertTrue("Launcher internal state didn't switch to Overview",
@@ -189,6 +193,22 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
0, getTaskCount(launcher)));
}
/**
* Smoke test for action buttons: Presses all the buttons and makes sure no crashes occur.
*/
@Test
@NavigationModeSwitch
@PortraitLandscape
public void testOverviewActions() throws Exception {
if (mLauncher.getNavigationModel() != NavigationModel.TWO_BUTTON) {
startTestAppsWithCheck();
OverviewActions actionsView =
mLauncher.pressHome().switchToOverview().getOverviewActions();
actionsView.clickAndDismissScreenshot();
actionsView.clickAndDismissShare();
}
}
private int getCurrentOverviewPage(Launcher launcher) {
return launcher.<RecentsView>getOverviewPanel().getCurrentPage();
}
@@ -42,6 +42,7 @@ import android.widget.Toast;
import androidx.annotation.Nullable;
import com.android.launcher3.Launcher.OnResumeCallback;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.InstanceIdSequence;
@@ -108,10 +109,20 @@ public abstract class BaseDraggingActivity extends BaseActivity
private void updateTheme() {
if (mThemeRes != Themes.getActivityThemeRes(this)) {
recreate();
// Workaround (b/162812884): The system currently doesn't allow recreating an activity
// when it is not resumed, in such a case defer recreation until it is possible
if (hasBeenResumed()) {
recreate();
} else {
addOnResumeCallback(this::recreate);
}
}
}
protected void addOnResumeCallback(OnResumeCallback callback) {
// To be overridden
}
@Override
public void onActionModeStarted(ActionMode mode) {
super.onActionModeStarted(mode);
+1
View File
@@ -1943,6 +1943,7 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
return result;
}
@Override
public void addOnResumeCallback(OnResumeCallback callback) {
mOnResumeCallbacks.add(callback);
}
@@ -198,6 +198,7 @@ public class Interpolators {
public OvershootParams(float startProgress, float overshootPastProgress,
float endProgress, float velocityPxPerMs, int totalDistancePx, Context context) {
velocityPxPerMs = Math.abs(velocityPxPerMs);
overshootPastProgress = Math.max(overshootPastProgress, startProgress);
start = startProgress;
int startPx = (int) (start * totalDistancePx);
// Overshoot by about half a frame.
@@ -92,7 +92,7 @@ public final class FeatureFlags {
// Keep as DeviceFlag to allow remote disable in emergency.
public static final BooleanFlag ENABLE_SUGGESTED_ACTIONS_OVERVIEW = new DeviceFlag(
"ENABLE_SUGGESTED_ACTIONS_OVERVIEW", false, "Show chip hints on the overview screen");
"ENABLE_SUGGESTED_ACTIONS_OVERVIEW", true, "Show chip hints on the overview screen");
public static final BooleanFlag FOLDER_NAME_SUGGEST = new DeviceFlag(
"FOLDER_NAME_SUGGEST", true,
@@ -316,7 +316,10 @@ public class StatsLogManager implements ResourceBasedOverride {
LAUNCHER_NAVIGATION_MODE_2_BUTTON(624),
@UiEvent(doc = "System navigation mode is 0 button mode/gesture navigation mode .")
LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON(625);
LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON(625),
@UiEvent(doc = "User tapped on image content in Overview Select mode.")
LAUNCHER_SELECT_MODE_IMAGE(627);
// ADD MORE
@@ -311,7 +311,13 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> {
handler.setStateWithAnimation(state, mConfig, builder);
}
}
builder.addListener(new AnimationSuccessListener() {
builder.addListener(createStateAnimationListener(state));
mConfig.setAnimation(builder.buildAnim(), state);
return builder;
}
private AnimatorListener createStateAnimationListener(STATE_TYPE state) {
return new AnimationSuccessListener() {
@Override
public void onAnimationStart(Animator animation) {
@@ -326,9 +332,7 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> {
}
onStateTransitionEnd(state);
}
});
mConfig.setAnimation(builder.buildAnim(), state);
return builder;
};
}
private void onStateTransitionStart(STATE_TYPE state) {
@@ -395,6 +399,19 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> {
mConfig.playbackController = controller;
}
/**
* @see #setCurrentAnimation(AnimatorSet, Animator...). Using this method tells the StateManager
* that this is a custom animation to the given state, and thus the StateManager will add an
* animation listener to call {@link #onStateTransitionStart} and {@link #onStateTransitionEnd}.
* @param anim The custom animation to the given state.
* @param toState The state we are animating towards.
*/
public void setCurrentAnimation(AnimatorSet anim, STATE_TYPE toState) {
cancelAnimation();
setCurrentAnimation(anim);
anim.addListener(createStateAnimationListener(toState));
}
/**
* Sets the animation as the current state animation, i.e., canceled when
* starting another animation and may block some launcher interactions while running.
@@ -71,6 +71,7 @@ public class StateAnimationConfig {
ANIM_ALL_APPS_HEADER_FADE,
ANIM_OVERVIEW_MODAL,
ANIM_DEPTH,
ANIM_OVERVIEW_ACTIONS_FADE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface AnimType {}
@@ -89,10 +90,11 @@ public class StateAnimationConfig {
public static final int ANIM_ALL_APPS_HEADER_FADE = 12; // e.g. predictions
public static final int ANIM_OVERVIEW_MODAL = 13;
public static final int ANIM_DEPTH = 14;
public static final int ANIM_OVERVIEW_ACTIONS_FADE = 15;
private static final int ANIM_TYPES_COUNT = 15;
private static final int ANIM_TYPES_COUNT = 16;
private final Interpolator[] mInterpolators = new Interpolator[ANIM_TYPES_COUNT];
protected final Interpolator[] mInterpolators = new Interpolator[ANIM_TYPES_COUNT];
public StateAnimationConfig() { }
@@ -98,6 +98,7 @@ public final class TestProtocol {
public static final String REQUEST_DISABLE_DEBUG_TRACING = "disable-debug-tracing";
public static final String REQUEST_OVERVIEW_ACTIONS_ENABLED = "overview-actions-enabled";
public static final String REQUEST_OVERVIEW_SHARE_ENABLED = "overview-share-enabled";
public static boolean sDisableSensorRotation;
public static final String REQUEST_MOCK_SENSOR_ROTATION = "mock-sensor-rotation";
@@ -107,6 +107,11 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler {
action.call(target, 0, param);
}
@Override
public <T> void setSecondary(T target, Float2DAction<T> action, float param) {
action.call(target, param, 0);
}
@Override
public float getPrimaryDirection(MotionEvent event, int pointerIndex) {
return event.getY(pointerIndex);
@@ -215,7 +220,11 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler {
}
@Override
public int getTaskDismissDirectionFactor() {
public int getPrimaryTranslationDirectionFactor() {
return -1;
}
public int getSecondaryTranslationDirectionFactor() {
return 1;
}
@@ -57,6 +57,7 @@ public interface PagedOrientationHandler {
<T> void set(T target, Int2DAction<T> action, int param);
<T> void set(T target, Float2DAction<T> action, float param);
<T> void setSecondary(T target, Float2DAction<T> action, float param);
float getPrimaryDirection(MotionEvent event, int pointerIndex);
float getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId);
int getMeasuredSize(View view);
@@ -74,7 +75,8 @@ public interface PagedOrientationHandler {
int getScrollOffsetStart(View view, Rect insets);
int getScrollOffsetEnd(View view, Rect insets);
SingleAxisSwipeDetector.Direction getOppositeSwipeDirection();
int getTaskDismissDirectionFactor();
int getPrimaryTranslationDirectionFactor();
int getSecondaryTranslationDirectionFactor();
int getTaskDragDisplacementFactor(boolean isRtl);
ChildBounds getChildBounds(View child, int childStart, int pageCenter, boolean layoutChild);
void setMaxScroll(AccessibilityEvent event, int maxScroll);
@@ -104,6 +104,11 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler {
action.call(target, param, 0);
}
@Override
public <T> void setSecondary(T target, Float2DAction<T> action, float param) {
action.call(target, 0, param);
}
@Override
public float getPrimaryDirection(MotionEvent event, int pointerIndex) {
return event.getX(pointerIndex);
@@ -212,7 +217,11 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler {
}
@Override
public int getTaskDismissDirectionFactor() {
public int getPrimaryTranslationDirectionFactor() {
return 1;
}
public int getSecondaryTranslationDirectionFactor() {
return -1;
}
@@ -28,7 +28,7 @@ import com.android.launcher3.Utilities;
public class SeascapePagedViewHandler extends LandscapePagedViewHandler {
@Override
public int getTaskDismissDirectionFactor() {
public int getSecondaryTranslationDirectionFactor() {
return -1;
}
@@ -19,6 +19,8 @@ package com.android.launcher3.util;
import android.util.FloatProperty;
import android.view.View;
import com.android.launcher3.anim.AlphaUpdateListener;
import java.util.Arrays;
/**
@@ -44,6 +46,8 @@ public class MultiValueAlpha {
private final AlphaProperty[] mMyProperties;
private int mValidMask;
// Whether we should change from INVISIBLE to VISIBLE and vice versa at low alpha values.
private boolean mUpdateVisibility;
public MultiValueAlpha(View view, int size) {
mView = view;
@@ -66,6 +70,11 @@ public class MultiValueAlpha {
return mMyProperties[index];
}
/** Sets whether we should update between INVISIBLE and VISIBLE based on alpha. */
public void setUpdateVisibility(boolean updateVisibility) {
mUpdateVisibility = updateVisibility;
}
public class AlphaProperty {
private final int mMyMask;
@@ -99,6 +108,9 @@ public class MultiValueAlpha {
mValue = value;
mView.setAlpha(mOthers * mValue);
if (mUpdateVisibility) {
AlphaUpdateListener.updateVisibility(mView);
}
}
public float getValue() {
@@ -27,7 +27,7 @@ import java.util.Collections;
import java.util.List;
/**
* Common overview pane for both Launcher and fallback recents
* Common overview panel for both Launcher and fallback recents
*/
public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
private static final int FLINGS_FOR_DISMISS_LIMIT = 40;
@@ -135,4 +135,19 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
public boolean hasTasks() {
return getTasks().size() > 0;
}
/**
* Gets Overview Actions.
*
* @return The Overview Actions
*/
@NonNull
public OverviewActions getOverviewActions() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to get overview actions")) {
verifyActiveContainer();
UiObject2 overviewActions = mLauncher.waitForLauncherObject("action_buttons");
return new OverviewActions(overviewActions, mLauncher);
}
}
}
@@ -154,6 +154,7 @@ public final class LauncherInstrumentation {
private static final String CONTEXT_MENU_RES_ID = "deep_shortcuts_container";
public static final int WAIT_TIME_MS = 10000;
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
private static final String ANDROID_PACKAGE = "android";
private static WeakReference<VisibleContainer> sActiveContainer = new WeakReference<>(null);
@@ -926,6 +927,14 @@ public final class LauncherInstrumentation {
return waitForObjectBySelector(getOverviewObjectSelector(resName));
}
@NonNull
UiObject2 waitForAndroidObject(String resId) {
final UiObject2 object = mDevice.wait(
Until.findObject(By.res(ANDROID_PACKAGE, resId)), WAIT_TIME_MS);
assertNotNull("Can't find a android object with id: " + resId, object);
return object;
}
private UiObject2 waitForObjectBySelector(BySelector selector) {
final UiObject2 object = mDevice.wait(Until.findObject(selector), WAIT_TIME_MS);
assertNotNull("Can't find a view in Launcher, selector: " + selector, object);
@@ -1302,6 +1311,11 @@ public final class LauncherInstrumentation {
TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
boolean overviewShareEnabled() {
return getTestInfo(TestProtocol.REQUEST_OVERVIEW_SHARE_ENABLED).getBoolean(
TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
private void disableSensorRotation() {
getTestInfo(TestProtocol.REQUEST_MOCK_SENSOR_ROTATION);
}
@@ -0,0 +1,130 @@
/*
* Copyright (C) 2020 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.tapl;
import androidx.annotation.NonNull;
import androidx.test.uiautomator.UiObject2;
import com.android.launcher3.testing.TestProtocol;
/**
* View containing overview actions
*/
public class OverviewActions {
private final UiObject2 mOverviewActions;
private final LauncherInstrumentation mLauncher;
OverviewActions(UiObject2 overviewActions, LauncherInstrumentation launcherInstrumentation) {
this.mOverviewActions = overviewActions;
this.mLauncher = launcherInstrumentation;
}
/**
* Clicks screenshot button and closes screenshot ui.
*/
@NonNull
public Overview clickAndDismissScreenshot() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to click screenshot button and exit screenshot ui")) {
UiObject2 screenshot = mLauncher.waitForObjectInContainer(mOverviewActions,
"action_screenshot");
mLauncher.clickLauncherObject(screenshot);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"clicked screenshot button")) {
UiObject2 closeScreenshot = mLauncher.waitForSystemUiObject(
"global_screenshot_dismiss_image");
if (mLauncher.getNavigationModel()
!= LauncherInstrumentation.NavigationModel.THREE_BUTTON) {
mLauncher.expectEvent(TestProtocol.SEQUENCE_TIS,
LauncherInstrumentation.EVENT_TOUCH_DOWN_TIS);
mLauncher.expectEvent(TestProtocol.SEQUENCE_TIS,
LauncherInstrumentation.EVENT_TOUCH_UP_TIS);
}
closeScreenshot.click();
try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
"dismissed screenshot")) {
return new Overview(mLauncher);
}
}
}
}
/**
* Click share button, then drags sharesheet down to remove it.
*
* Share is currently hidden behind flag, test is kept in case share becomes a default feature.
* If share is completely removed then remove this test as well.
*/
@NonNull
public Overview clickAndDismissShare() {
if (mLauncher.overviewShareEnabled()) {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to click share button and dismiss sharesheet")) {
UiObject2 share = mLauncher.waitForObjectInContainer(mOverviewActions,
"action_share");
mLauncher.clickLauncherObject(share);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"clicked share button")) {
mLauncher.waitForAndroidObject("contentPanel");
mLauncher.getDevice().pressBack();
try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
"dismissed sharesheet")) {
return new Overview(mLauncher);
}
}
}
}
return new Overview(mLauncher);
}
/**
* Click select button
*
* @return The select mode buttons that are now shown instead of action buttons.
*/
@NonNull
public SelectModeButtons clickSelect() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c =
mLauncher.addContextLayer("want to click select button")) {
UiObject2 select = mLauncher.waitForObjectInContainer(mOverviewActions,
"action_select");
mLauncher.clickLauncherObject(select);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"clicked select button")) {
return getSelectModeButtons();
}
}
}
/**
* Gets the Select Mode Buttons.
*
* @return The Select Mode Buttons.
*/
@NonNull
private SelectModeButtons getSelectModeButtons() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to get select mode buttons")) {
UiObject2 selectModeButtons = mLauncher.waitForLauncherObject("select_mode_buttons");
return new SelectModeButtons(selectModeButtons, mLauncher);
}
}
}
@@ -0,0 +1,68 @@
/*
* Copyright (C) 2020 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.tapl;
import androidx.annotation.NonNull;
import androidx.test.uiautomator.UiObject2;
/**
* View containing select mode buttons
*/
public class SelectModeButtons {
private final UiObject2 mSelectModeButtons;
private final LauncherInstrumentation mLauncher;
SelectModeButtons(UiObject2 selectModeButtons,
LauncherInstrumentation launcherInstrumentation) {
mSelectModeButtons = selectModeButtons;
mLauncher = launcherInstrumentation;
}
/**
* Click close button.
*/
@NonNull
public Overview clickClose() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c =
mLauncher.addContextLayer("want to click close button")) {
UiObject2 close = mLauncher.waitForObjectInContainer(mSelectModeButtons, "close");
mLauncher.clickLauncherObject(close);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"clicked close button")) {
return new Overview(mLauncher);
}
}
}
/**
* Click feedback button.
*/
@NonNull
public Background clickFeedback() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c =
mLauncher.addContextLayer("want to click feedback button")) {
UiObject2 feedback = mLauncher.waitForObjectInContainer(mSelectModeButtons, "feedback");
mLauncher.clickLauncherObject(feedback);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"clicked feedback button")) {
return new Background(mLauncher);
}
}
}
}