Merging from ub-launcher3-rvc-qpr-dev @ build 6770831 am: fa9675a32e
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/12398196 Change-Id: I4f0a01aeff214a31f5dec23f1a1aff41e220b758
This commit is contained in:
+7
-4
@@ -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
|
||||
|
||||
+11
-4
@@ -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) {
|
||||
|
||||
+20
-5
@@ -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);
|
||||
}
|
||||
|
||||
+31
-45
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+32
-17
@@ -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();
|
||||
|
||||
+10
-1
@@ -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();
|
||||
|
||||
+2
-2
@@ -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) {
|
||||
|
||||
+25
-44
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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;
|
||||
|
||||
+2
-2
@@ -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;
|
||||
}
|
||||
}
|
||||
+6
@@ -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);
|
||||
|
||||
+11
-25
@@ -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,
|
||||
|
||||
+2
-3
@@ -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.
|
||||
|
||||
+4
@@ -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) {
|
||||
|
||||
+146
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -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
|
||||
|
||||
+5
-2
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
|
||||
+17
-12
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
+4
@@ -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
|
||||
|
||||
+1
-2
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user