Merge changes from topic "state-supplier" into ub-launcher3-master

* changes:
  Translate recents slightly while dragging after pausing
  Two-zone model: swipe up from nav bar vs above it
  Change LauncherState to Supplier<LauncherState> in tests
This commit is contained in:
TreeHugger Robot
2020-01-29 03:06:04 +00:00
committed by Android (Google) Code Review
28 changed files with 513 additions and 110 deletions
@@ -37,6 +37,7 @@ import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.NoButtonNavbarToOverviewTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.NoButtonQuickSwitchTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.OverviewToAllAppsTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
@@ -224,7 +225,11 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
if (mode == NO_BUTTON) {
list.add(new NoButtonQuickSwitchTouchController(this));
list.add(new NavBarToHomeTouchController(this));
list.add(new FlingAndHoldTouchController(this));
if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
list.add(new NoButtonNavbarToOverviewTouchController(this));
} else {
list.add(new FlingAndHoldTouchController(this));
}
} else {
if (getDeviceProfile().isVerticalBarLayout()) {
list.add(new OverviewToAllAppsTouchController(this));
@@ -30,11 +30,13 @@ import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
import android.graphics.Rect;
import android.view.View;
import android.view.animation.Interpolator;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
@@ -44,7 +46,6 @@ import com.android.launcher3.R;
import com.android.launcher3.Workspace;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.quickstep.SysUINavigationMode;
@@ -115,6 +116,15 @@ public class OverviewState extends LauncherState {
return new ScaleAndTranslation(1f, 0f, 0f);
}
@Override
public ScaleAndTranslation getQsbScaleAndTranslation(Launcher launcher) {
if (this == OVERVIEW && ENABLE_OVERVIEW_ACTIONS.get()) {
// Treat the QSB as part of the hotseat so they move together.
return getHotseatScaleAndTranslation(launcher);
}
return super.getQsbScaleAndTranslation(launcher);
}
@Override
public void onStateEnabled(Launcher launcher) {
AbstractFloatingView.closeAllOpenViews(launcher);
@@ -141,7 +151,7 @@ public class OverviewState extends LauncherState {
if (launcher.getDeviceProfile().isVerticalBarLayout()) {
return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON;
} else {
if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
if (ENABLE_OVERVIEW_ACTIONS.get()) {
return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON;
}
@@ -195,9 +205,10 @@ public class OverviewState extends LauncherState {
@Override
public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState,
AnimatorSetBuilder builder) {
if (fromState == NORMAL && this == OVERVIEW) {
if ((fromState == NORMAL || fromState == HINT_STATE) && this == OVERVIEW) {
if (SysUINavigationMode.getMode(launcher) == SysUINavigationMode.Mode.NO_BUTTON) {
builder.setInterpolator(ANIM_WORKSPACE_SCALE, ACCEL);
builder.setInterpolator(ANIM_WORKSPACE_SCALE,
fromState == NORMAL ? ACCEL : OVERSHOOT_1_2);
builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
} else {
builder.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
@@ -210,8 +221,11 @@ public class OverviewState extends LauncherState {
}
builder.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
builder.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, OVERSHOOT_1_7);
Interpolator translationInterpolator = ENABLE_OVERVIEW_ACTIONS.get()
? OVERSHOOT_1_2
: OVERSHOOT_1_7;
builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, translationInterpolator);
builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, translationInterpolator);
builder.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
}
}
@@ -60,7 +60,7 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
private static final long PEEK_OUT_ANIM_DURATION = 100;
private static final float MAX_DISPLACEMENT_PERCENT = 0.75f;
private final MotionPauseDetector mMotionPauseDetector;
protected final MotionPauseDetector mMotionPauseDetector;
private final float mMotionPauseMinDisplacement;
private final float mMotionPauseMaxDisplacement;
@@ -85,37 +85,39 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
super.onDragStart(start);
if (handlingOverviewAnim()) {
mMotionPauseDetector.setOnMotionPauseListener(isPaused -> {
RecentsView recentsView = mLauncher.getOverviewPanel();
recentsView.setOverviewStateEnabled(isPaused);
if (mPeekAnim != null) {
mPeekAnim.cancel();
}
LauncherState fromState = isPaused ? NORMAL : OVERVIEW_PEEK;
LauncherState toState = isPaused ? OVERVIEW_PEEK : NORMAL;
long peekDuration = isPaused ? PEEK_IN_ANIM_DURATION : PEEK_OUT_ANIM_DURATION;
mPeekAnim = mLauncher.getStateManager().createAtomicAnimation(fromState, toState,
new AnimatorSetBuilder(), ATOMIC_OVERVIEW_PEEK_COMPONENT, peekDuration);
mPeekAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mPeekAnim = null;
}
});
mPeekAnim.start();
VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
mLauncher.getDragLayer().getScrim().animateToSysuiMultiplier(isPaused ? 0 : 1,
peekDuration, 0);
});
mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseChanged);
}
}
protected void onMotionPauseChanged(boolean isPaused) {
RecentsView recentsView = mLauncher.getOverviewPanel();
recentsView.setOverviewStateEnabled(isPaused);
if (mPeekAnim != null) {
mPeekAnim.cancel();
}
LauncherState fromState = isPaused ? NORMAL : OVERVIEW_PEEK;
LauncherState toState = isPaused ? OVERVIEW_PEEK : NORMAL;
long peekDuration = isPaused ? PEEK_IN_ANIM_DURATION : PEEK_OUT_ANIM_DURATION;
mPeekAnim = mLauncher.getStateManager().createAtomicAnimation(fromState, toState,
new AnimatorSetBuilder(), ATOMIC_OVERVIEW_PEEK_COMPONENT, peekDuration);
mPeekAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mPeekAnim = null;
}
});
mPeekAnim.start();
VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
mLauncher.getDragLayer().getScrim().animateToSysuiMultiplier(isPaused ? 0 : 1,
peekDuration, 0);
}
/**
* @return Whether we are handling the overview animation, rather than
* having it as part of the existing animation to the target state.
*/
private boolean handlingOverviewAnim() {
protected boolean handlingOverviewAnim() {
int stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags();
return mStartState == NORMAL && (stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0;
}
@@ -162,7 +164,8 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
@Override
public boolean onDrag(float displacement, MotionEvent event) {
float upDisplacement = -displacement;
mMotionPauseDetector.setDisallowPause(upDisplacement < mMotionPauseMinDisplacement
mMotionPauseDetector.setDisallowPause(!handlingOverviewAnim()
|| upDisplacement < mMotionPauseMinDisplacement
|| upDisplacement > mMotionPauseMaxDisplacement);
mMotionPauseDetector.addPosition(displacement, event.getEventTime());
return super.onDrag(displacement, event);
@@ -171,19 +174,7 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
@Override
public void onDragEnd(float velocity) {
if (mMotionPauseDetector.isPaused() && handlingOverviewAnim()) {
if (mPeekAnim != null) {
mPeekAnim.cancel();
}
Animator overviewAnim = mLauncher.getAppTransitionManager().createStateElementAnimation(
INDEX_PAUSE_TO_OVERVIEW_ANIM);
overviewAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
onSwipeInteractionCompleted(OVERVIEW, Touch.SWIPE);
}
});
overviewAnim.start();
goToOverviewOnDragEnd(velocity);
} else {
super.onDragEnd(velocity);
}
@@ -195,6 +186,22 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
mMotionPauseDetector.clear();
}
protected void goToOverviewOnDragEnd(float velocity) {
if (mPeekAnim != null) {
mPeekAnim.cancel();
}
Animator overviewAnim = mLauncher.getAppTransitionManager().createStateElementAnimation(
INDEX_PAUSE_TO_OVERVIEW_ANIM);
overviewAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
onSwipeInteractionCompleted(OVERVIEW, Touch.SWIPE);
}
});
overviewAnim.start();
}
@Override
protected void goToTargetState(LauncherState targetState, int logAction) {
if (mPeekAnim != null && mPeekAnim.isStarted()) {
@@ -0,0 +1,208 @@
/*
* 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.uioverrides.touchcontrollers;
import static com.android.launcher3.LauncherState.HINT_STATE;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_PEEK_COMPONENT;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.graphics.PointF;
import android.view.MotionEvent;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorSetBuilder;
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.views.RecentsView;
/**
* Touch controller which handles swipe and hold from the nav bar to go to Overview. Swiping above
* the nav bar falls back to go to All Apps. Swiping from the nav bar without holding goes to the
* first home screen instead of to Overview.
*/
public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchController {
// How much of the movement to use for translating overview after swipe and hold.
private static final float OVERVIEW_MOVEMENT_FACTOR = 0.25f;
private static final long TRANSLATION_ANIM_MIN_DURATION_MS = 80;
private static final float TRANSLATION_ANIM_VELOCITY_DP_PER_MS = 0.8f;
private final RecentsView mRecentsView;
private boolean mDidTouchStartInNavBar;
private boolean mReachedOverview;
// The last recorded displacement before we reached overview.
private PointF mStartDisplacement = new PointF();
public NoButtonNavbarToOverviewTouchController(Launcher l) {
super(l);
mRecentsView = l.getOverviewPanel();
}
@Override
protected boolean canInterceptTouch(MotionEvent ev) {
mDidTouchStartInNavBar = (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
return super.canInterceptTouch(ev);
}
@Override
protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
if (fromState == NORMAL && mDidTouchStartInNavBar) {
return HINT_STATE;
} else if (fromState == OVERVIEW && isDragTowardPositive) {
// Don't allow swiping up to all apps.
return OVERVIEW;
}
return super.getTargetState(fromState, isDragTowardPositive);
}
@Override
protected float initCurrentAnimation(int animComponents) {
float progressMultiplier = super.initCurrentAnimation(animComponents);
if (mToState == HINT_STATE) {
// Track the drag across the entire height of the screen.
progressMultiplier = -1 / getShiftRange();
}
return progressMultiplier;
}
@Override
public void onDragStart(boolean start) {
super.onDragStart(start);
mReachedOverview = false;
}
@Override
protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
LauncherState targetState, float velocity, boolean isFling) {
super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState, velocity,
isFling);
if (targetState == HINT_STATE) {
// Normally we compute the duration based on the velocity and distance to the given
// state, but since the hint state tracks the entire screen without a clear endpoint, we
// need to manually set the duration to a reasonable value.
animator.setDuration(HINT_STATE.transitionDuration);
}
}
@Override
protected void onMotionPauseChanged(boolean isPaused) {
if (mCurrentAnimation == null) {
return;
}
mCurrentAnimation.dispatchOnCancelWithoutCancelRunnable(() -> {
mLauncher.getStateManager().goToState(OVERVIEW, true, () -> {
mReachedOverview = true;
maybeSwipeInteractionToOverviewComplete();
});
});
VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
}
private void maybeSwipeInteractionToOverviewComplete() {
if (mReachedOverview && mDetector.isSettlingState()) {
onSwipeInteractionCompleted(OVERVIEW, Touch.SWIPE);
}
}
@Override
protected boolean handlingOverviewAnim() {
return mDidTouchStartInNavBar && super.handlingOverviewAnim();
}
@Override
public boolean onDrag(float yDisplacement, float xDisplacement, MotionEvent event) {
if (mMotionPauseDetector.isPaused()) {
if (!mReachedOverview) {
mStartDisplacement.set(xDisplacement, yDisplacement);
} else {
mRecentsView.setTranslationX((xDisplacement - mStartDisplacement.x)
* OVERVIEW_MOVEMENT_FACTOR);
mRecentsView.setTranslationY((yDisplacement - mStartDisplacement.y)
* OVERVIEW_MOVEMENT_FACTOR);
}
// Stay in Overview.
return true;
}
return super.onDrag(yDisplacement, xDisplacement, event);
}
@Override
protected void goToOverviewOnDragEnd(float velocity) {
float velocityDp = dpiFromPx(velocity);
boolean isFling = Math.abs(velocityDp) > 1;
LauncherStateManager stateManager = mLauncher.getStateManager();
if (isFling) {
// When flinging, go back to home instead of overview.
if (velocity > 0) {
stateManager.goToState(NORMAL, true,
() -> onSwipeInteractionCompleted(NORMAL, Touch.FLING));
} else {
StaggeredWorkspaceAnim staggeredWorkspaceAnim = new StaggeredWorkspaceAnim(
mLauncher, velocity, false /* animateOverviewScrim */);
staggeredWorkspaceAnim.start();
// StaggeredWorkspaceAnim doesn't animate overview, so we handle it here.
stateManager.cancelAnimation();
AnimatorSetBuilder builder = new AnimatorSetBuilder();
long duration = OVERVIEW.transitionDuration;
AnimatorSet anim = stateManager.createAtomicAnimation(
stateManager.getState(), NORMAL, builder,
ATOMIC_OVERVIEW_PEEK_COMPONENT, duration);
anim.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
onSwipeInteractionCompleted(NORMAL, Touch.SWIPE);
}
});
anim.start();
}
} else {
if (mReachedOverview) {
float distanceDp = dpiFromPx(Math.max(
Math.abs(mRecentsView.getTranslationX()),
Math.abs(mRecentsView.getTranslationY())));
long duration = (long) Math.max(TRANSLATION_ANIM_MIN_DURATION_MS,
distanceDp / TRANSLATION_ANIM_VELOCITY_DP_PER_MS);
mRecentsView.animate()
.translationX(0)
.translationY(0)
.setInterpolator(ACCEL_DEACCEL)
.setDuration(duration)
.withEndAction(this::maybeSwipeInteractionToOverviewComplete);
}
}
}
private float dpiFromPx(float pixels) {
return Utilities.dpiFromPx(pixels, mLauncher.getResources().getDisplayMetrics());
}
}
@@ -62,6 +62,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.OverviewScrim;
import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.BothAxesSwipeDetector;
@@ -181,6 +182,12 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
@Override
public void onMotionPauseChanged(boolean isPaused) {
VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
return;
}
ShelfAnimState shelfState = isPaused ? PEEK : HIDE;
if (shelfState == PEEK) {
// Some shelf elements (e.g. qsb) were hidden, but we need them visible when peeking.
@@ -197,7 +204,6 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
}
mShelfPeekAnim.setShelfState(shelfState, ShelfPeekAnim.INTERPOLATOR,
ShelfPeekAnim.DURATION);
VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
}
private void setupAnimators() {
@@ -25,6 +25,7 @@ import android.animation.AnimatorListenerAdapter;
import android.view.animation.Interpolator;
import com.android.launcher3.Launcher;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.uioverrides.states.OverviewState;
/**
@@ -48,7 +49,7 @@ public class ShelfPeekAnim {
* Animates to the given state, canceling the previous animation if it was still running.
*/
public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator, long duration) {
if (mShelfState == shelfState) {
if (mShelfState == shelfState || FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
return;
}
mLauncher.getStateManager().cancelStateElementAnimation(INDEX_SHELF_ANIM);
@@ -15,6 +15,11 @@
*/
package com.android.quickstep;
import static android.content.Context.MODE_PRIVATE;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.config.FeatureFlags.FLAGS_PREF_NAME;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.UserManager;
@@ -22,6 +27,7 @@ import android.util.Log;
import com.android.launcher3.BuildConfig;
import com.android.launcher3.MainProcessInitializer;
import com.android.launcher3.config.FeatureFlags;
import com.android.systemui.shared.system.ThreadedRendererCompat;
@SuppressWarnings("unused")
@@ -50,5 +56,21 @@ public class QuickstepProcessInitializer extends MainProcessInitializer {
// Elevate GPU priority for Quickstep and Remote animations.
ThreadedRendererCompat.setContextPriority(ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
// Force disable some feature flags based on the system ui navigation mode.
SysUINavigationMode.Mode currMode = SysUINavigationMode.INSTANCE.get(context)
.addModeChangeListener(mode -> disableFeatureFlagsForSysuiNavMode(context, mode));
disableFeatureFlagsForSysuiNavMode(context, currMode);
}
private void disableFeatureFlagsForSysuiNavMode(Context ctx, SysUINavigationMode.Mode mode) {
if (mode == SysUINavigationMode.Mode.TWO_BUTTONS) {
ctx.getSharedPreferences(FLAGS_PREF_NAME, MODE_PRIVATE)
.edit()
.putBoolean(ENABLE_OVERVIEW_ACTIONS.key, false)
.apply();
FeatureFlags.initialize(ctx);
}
}
}
@@ -41,6 +41,8 @@ import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.uioverrides.states.OverviewState;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.SysUINavigationMode;
@@ -155,12 +157,18 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
mRemainingScreenPathValid = false;
mShiftRange = mLauncher.getAllAppsController().getShiftRange();
Context context = getContext();
if ((OVERVIEW.getVisibleElements(mLauncher) & ALL_APPS_HEADER_EXTRA) == 0) {
mMidProgress = 1;
mDragHandleProgress = 1;
mMidAlpha = 0;
if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
// Fade in all apps background quickly to distinguish from swiping from nav bar.
mMidAlpha = Themes.getAttrInteger(context, R.attr.allAppsInterimScrimAlpha);
mMidProgress = OverviewState.getDefaultVerticalProgress(mLauncher);
} else {
mMidAlpha = 0;
mMidProgress = 1;
}
} else {
Context context = getContext();
mMidAlpha = Themes.getAttrInteger(context, R.attr.allAppsInterimScrimAlpha);
mMidProgress = OVERVIEW.getVerticalProgress(mLauncher);
Rect hotseatPadding = dp.getHotseatLayoutPadding();
@@ -68,7 +68,7 @@ public class DigitalWellBeingToastTest extends AbstractQuickStepTest {
private DigitalWellBeingToast getToast() {
executeOnLauncher(launcher -> launcher.getStateManager().goToState(OVERVIEW));
waitForState("Launcher internal state didn't switch to Overview", OVERVIEW);
waitForState("Launcher internal state didn't switch to Overview", () -> OVERVIEW);
final TaskView task = getOnceNotNull("No latest task", launcher -> getLatestTask(launcher));
return getFromLauncher(launcher -> {
@@ -85,7 +85,8 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
@Ignore // Enable after b/131115533
public void testPressRecentAppsLauncherAndGetOverview() throws RemoteException {
mDevice.pressRecentApps();
waitForState("Launcher internal state didn't switch to Overview", LauncherState.OVERVIEW);
waitForState("Launcher internal state didn't switch to Overview",
() -> LauncherState.OVERVIEW);
assertNotNull("getOverview() returned null", mLauncher.getOverview());
}
@@ -96,7 +97,8 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
public void testWorkspaceSwitchToAllApps() {
assertNotNull("switchToAllApps() returned null",
mLauncher.getWorkspace().switchToAllApps());
assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
assertTrue("Launcher internal state is not All Apps",
isInState(() -> LauncherState.ALL_APPS));
}
@Test
@@ -115,7 +117,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
// 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",
isInState(LauncherState.OVERVIEW));
isInState(() -> LauncherState.OVERVIEW));
executeOnLauncher(
launcher -> assertTrue("Don't have at least 3 tasks", getTaskCount(launcher) >= 3));
@@ -124,14 +126,16 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
0, getCurrentOverviewPage(launcher)));
overview.flingForward();
assertTrue("Launcher internal state is not Overview", isInState(LauncherState.OVERVIEW));
assertTrue("Launcher internal state is not Overview",
isInState(() -> LauncherState.OVERVIEW));
final Integer currentTaskAfterFlingForward = getFromLauncher(
launcher -> getCurrentOverviewPage(launcher));
executeOnLauncher(launcher -> assertTrue("Current task in Overview is still 0",
currentTaskAfterFlingForward > 0));
overview.flingBackward();
assertTrue("Launcher internal state is not Overview", isInState(LauncherState.OVERVIEW));
assertTrue("Launcher internal state is not Overview",
isInState(() -> LauncherState.OVERVIEW));
executeOnLauncher(launcher -> assertTrue("Flinging back in Overview did nothing",
getCurrentOverviewPage(launcher) < currentTaskAfterFlingForward));
@@ -150,7 +154,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
// Test dismissing a task.
overview = mLauncher.pressHome().switchToOverview();
assertTrue("Launcher internal state didn't switch to Overview",
isInState(LauncherState.OVERVIEW));
isInState(() -> LauncherState.OVERVIEW));
final Integer numTasks = getFromLauncher(launcher -> getTaskCount(launcher));
task = overview.getCurrentTask();
assertNotNull("overview.getCurrentTask() returned null (2)", task);
@@ -165,29 +169,29 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
final AllAppsFromOverview allApps = overview.switchToAllApps();
assertNotNull("overview.switchToAllApps() returned null (1)", allApps);
assertTrue("Launcher internal state is not All Apps (1)",
isInState(LauncherState.ALL_APPS));
isInState(() -> LauncherState.ALL_APPS));
overview = allApps.switchBackToOverview();
assertNotNull("allApps.switchBackToOverview() returned null", overview);
assertTrue("Launcher internal state didn't switch to Overview",
isInState(LauncherState.OVERVIEW));
isInState(() -> LauncherState.OVERVIEW));
// Test UIDevice.pressBack()
overview.switchToAllApps();
assertNotNull("overview.switchToAllApps() returned null (2)", allApps);
assertTrue("Launcher internal state is not All Apps (2)",
isInState(LauncherState.ALL_APPS));
isInState(() -> LauncherState.ALL_APPS));
mDevice.pressBack();
mLauncher.getOverview();
}
// Test UIDevice.pressHome, once we are in AllApps.
mDevice.pressHome();
waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
// Test dismissing all tasks.
mLauncher.getWorkspace().switchToOverview().dismissAllTasks();
waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
executeOnLauncher(
launcher -> assertEquals("Still have tasks after dismissing all",
0, getTaskCount(launcher)));
@@ -205,7 +209,8 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
public void testAppIconLaunchFromAllAppsFromOverview() throws Exception {
final AllApps allApps =
mLauncher.getWorkspace().switchToOverview().switchToAllApps();
assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
assertTrue("Launcher internal state is not All Apps",
isInState(() -> LauncherState.ALL_APPS));
TaplTestsLauncher3.runIconLaunchFromAllAppsTest(this, allApps);
}
@@ -217,7 +222,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
assertNotNull("Workspace.switchToOverview() returned null",
mLauncher.pressHome().switchToOverview());
assertTrue("Launcher internal state didn't switch to Overview",
isInState(LauncherState.OVERVIEW));
isInState(() -> LauncherState.OVERVIEW));
}
@Test
@@ -229,7 +234,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
assertNotNull("Background.switchToOverview() returned null", background.switchToOverview());
assertTrue("Launcher internal state didn't switch to Overview",
isInState(LauncherState.OVERVIEW));
isInState(() -> LauncherState.OVERVIEW));
}
private Background getAndAssertBackground() {
@@ -252,9 +257,11 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
TaplTestsLauncher3.runAllAppsTest(this, mLauncher.getAllApps());
// Testing pressHome.
assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
assertTrue("Launcher internal state is not All Apps",
isInState(() -> LauncherState.ALL_APPS));
assertNotNull("pressHome returned null", mLauncher.pressHome());
assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
assertTrue("Launcher internal state is not Home",
isInState(() -> LauncherState.NORMAL));
assertNotNull("getHome returned null", mLauncher.getWorkspace());
}
+5
View File
@@ -1180,6 +1180,11 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
mDropTargetBar.setup(mDragController);
mAllAppsController.setupViews(mAppsView);
if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
// Overview is above all other launcher elements, including qsb, so move it to the top.
mOverviewPanel.bringToFront();
}
}
/**
@@ -16,6 +16,8 @@
package com.android.launcher3;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import android.graphics.drawable.Drawable;
import android.util.FloatProperty;
import android.util.Property;
@@ -28,9 +30,10 @@ public class LauncherAnimUtils {
* easier access from static classes and enums
*/
public static final int ALL_APPS_TRANSITION_MS = 320;
public static final int OVERVIEW_TRANSITION_MS = 250;
public static final int OVERVIEW_TRANSITION_MS = ENABLE_OVERVIEW_ACTIONS.get() ? 380 : 250;
public static final int SPRING_LOADED_TRANSITION_MS = 150;
public static final int SPRING_LOADED_EXIT_DELAY = 500;
public static final int HINT_TRANSITION_MS = 80;
// The progress of an animation to all apps must be at least this far along to snap to all apps.
public static final float MIN_PROGRESS_TO_ALL_APPS = 0.5f;
+13 -1
View File
@@ -30,9 +30,11 @@ import static com.android.launcher3.anim.Interpolators.ACCEL_2;
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.clampToProgress;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.OVERVIEW_PEEK_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
@@ -42,6 +44,7 @@ import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORD
import android.view.animation.Interpolator;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.states.HintState;
import com.android.launcher3.states.SpringLoadedState;
import com.android.launcher3.uioverrides.states.AllAppsState;
import com.android.launcher3.uioverrides.states.OverviewState;
@@ -88,7 +91,7 @@ public class LauncherState {
}
};
private static final LauncherState[] sAllStates = new LauncherState[7];
private static final LauncherState[] sAllStates = new LauncherState[8];
/**
* TODO: Create a separate class for NORMAL state.
@@ -104,6 +107,7 @@ public class LauncherState {
public static final LauncherState SPRING_LOADED = new SpringLoadedState(
SPRING_LOADED_STATE_ORDINAL);
public static final LauncherState ALL_APPS = new AllAppsState(ALL_APPS_STATE_ORDINAL);
public static final LauncherState HINT_STATE = new HintState(HINT_STATE_ORDINAL);
public static final LauncherState OVERVIEW = new OverviewState(OVERVIEW_STATE_ORDINAL);
public static final LauncherState OVERVIEW_PEEK =
@@ -212,6 +216,10 @@ public class LauncherState {
return launcher.getOverviewScaleAndTranslationForNormalState();
}
public ScaleAndTranslation getQsbScaleAndTranslation(Launcher launcher) {
return new ScaleAndTranslation(1, 0, 0);
}
public float getOverviewFullscreenProgress() {
return 0;
}
@@ -319,6 +327,10 @@ public class LauncherState {
if (!isHotseatVisible) {
hotseat.setScaleX(0.92f);
hotseat.setScaleY(0.92f);
if (ENABLE_OVERVIEW_ACTIONS.get()) {
launcher.getAppsView().setScaleX(0.92f);
launcher.getAppsView().setScaleY(0.92f);
}
}
} else if (this == NORMAL && fromState == OVERVIEW_PEEK) {
// Keep fully visible until the very end (when overview is offscreen) to make invisible.
+1 -1
View File
@@ -381,7 +381,7 @@ public final class Utilities {
return res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
}
public static float dpiFromPx(int size, DisplayMetrics metrics){
public static float dpiFromPx(float size, DisplayMetrics metrics) {
float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
return (size / densityRatio);
}
+2 -1
View File
@@ -3255,7 +3255,8 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
return mOverlayShown;
}
void moveToDefaultScreen() {
/** Calls {@link #snapToPage(int)} on the {@link #DEFAULT_PAGE}, then requests focus on it. */
public void moveToDefaultScreen() {
int page = DEFAULT_PAGE;
if (!workspaceInModalState() && getNextPage() != page) {
snapToPage(page);
@@ -36,6 +36,7 @@ import android.view.animation.Interpolator;
import com.android.launcher3.LauncherState.PageAlphaProvider;
import com.android.launcher3.LauncherState.ScaleAndTranslation;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
@@ -77,6 +78,7 @@ public class WorkspaceStateTransitionAnimation {
ScaleAndTranslation scaleAndTranslation = state.getWorkspaceScaleAndTranslation(mLauncher);
ScaleAndTranslation hotseatScaleAndTranslation = state.getHotseatScaleAndTranslation(
mLauncher);
ScaleAndTranslation qsbScaleAndTranslation = state.getQsbScaleAndTranslation(mLauncher);
mNewScale = scaleAndTranslation.scale;
PageAlphaProvider pageAlphaProvider = state.getWorkspacePageAlphaProvider(mLauncher);
final int childCount = mWorkspace.getChildCount();
@@ -90,24 +92,24 @@ public class WorkspaceStateTransitionAnimation {
pageAlphaProvider.interpolator);
boolean playAtomicComponent = config.playAtomicOverviewScaleComponent();
Hotseat hotseat = mWorkspace.getHotseat();
// Since we set the pivot relative to mWorkspace, we need to scale a sibling of Workspace.
AllAppsContainerView qsbScaleView = mLauncher.getAppsView();
View qsbView = qsbScaleView.getSearchView();
if (playAtomicComponent) {
Interpolator scaleInterpolator = builder.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
if (!hotseat.getRotationMode().isTransposed) {
// Set the hotseat's pivot point to match the workspace's, so that it scales
// together. Since both hotseat and workspace can move, transform the point
// manually instead of using dragLayer.getDescendantCoordRelativeToSelf and
// related methods.
hotseat.setPivotY(mWorkspace.getPivotY() + mWorkspace.getTop() - hotseat.getTop());
hotseat.setPivotX(mWorkspace.getPivotX()
+ mWorkspace.getLeft() - hotseat.getLeft());
setPivotToScaleWithWorkspace(hotseat);
setPivotToScaleWithWorkspace(qsbScaleView);
}
float hotseatScale = hotseatScaleAndTranslation.scale;
Interpolator hotseatScaleInterpolator = builder.getInterpolator(ANIM_HOTSEAT_SCALE,
scaleInterpolator);
propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale,
hotseatScaleInterpolator);
propertySetter.setFloat(qsbScaleView, SCALE_PROPERTY, qsbScaleAndTranslation.scale,
hotseatScaleInterpolator);
float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha, fadeInterpolator);
@@ -134,10 +136,24 @@ public class WorkspaceStateTransitionAnimation {
hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator);
propertySetter.setFloat(mWorkspace.getPageIndicator(), View.TRANSLATION_Y,
hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator);
propertySetter.setFloat(qsbView, View.TRANSLATION_Y,
qsbScaleAndTranslation.translationY, hotseatTranslationInterpolator);
setScrim(propertySetter, state);
}
/**
* Set the given view's pivot point to match the workspace's, so that it scales together. Since
* both this view and workspace can move, transform the point manually instead of using
* dragLayer.getDescendantCoordRelativeToSelf and related methods.
*/
private void setPivotToScaleWithWorkspace(View sibling) {
sibling.setPivotY(mWorkspace.getPivotY() + mWorkspace.getTop()
- sibling.getTop() - sibling.getTranslationY());
sibling.setPivotX(mWorkspace.getPivotX() + mWorkspace.getLeft()
- sibling.getLeft() - sibling.getTranslationX());
}
public void setScrim(PropertySetter propertySetter, LauncherState state) {
WorkspaceAndHotseatScrim scrim = mLauncher.getDragLayer().getScrim();
propertySetter.setFloat(scrim, SCRIM_PROGRESS, state.getWorkspaceScrimAlpha(mLauncher),
@@ -26,6 +26,7 @@ import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringAnimation;
@@ -250,14 +251,24 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat
}
}
/** @see #dispatchOnCancelWithoutCancelRunnable(Runnable) */
public void dispatchOnCancelWithoutCancelRunnable() {
dispatchOnCancelWithoutCancelRunnable(null);
}
/**
* Sets mOnCancelRunnable = null before dispatching the cancel and restoring the runnable. This
* is intended to be used only if you need to cancel but want to defer cleaning up yourself.
* @param callback An optional callback to run after dispatching the cancel but before resetting
* the onCancelRunnable.
*/
public void dispatchOnCancelWithoutCancelRunnable() {
public void dispatchOnCancelWithoutCancelRunnable(@Nullable Runnable callback) {
Runnable onCancel = mOnCancelRunnable;
setOnCancelRunnable(null);
dispatchOnCancel();
if (callback != null) {
callback.run();
}
setOnCancelRunnable(onCancel);
}
@@ -117,7 +117,8 @@ public final class FeatureFlags {
"Show launcher preview in grid picker");
public static final BooleanFlag ENABLE_OVERVIEW_ACTIONS = getDebugFlag(
"ENABLE_OVERVIEW_ACTIONS", false, "Show app actions in Overview");
"ENABLE_OVERVIEW_ACTIONS", false, "Show app actions instead of the shelf in Overview."
+ " As part of this decoupling, also distinguish swipe up from nav bar vs above it.");
public static final BooleanFlag ENABLE_DATABASE_RESTORE = getDebugFlag(
"ENABLE_DATABASE_RESTORE", true,
@@ -536,6 +536,9 @@ public class DragLayer extends BaseDragLayer<Launcher> {
mOverviewScrim.updateCurrentScrimmedView(this);
mFocusIndicatorHelper.draw(canvas);
super.dispatchDraw(canvas);
if (mOverviewScrim.getScrimmedView() == null) {
mOverviewScrim.draw(canvas);
}
}
@Override
@@ -55,16 +55,17 @@ public class OverviewScrim extends Scrim {
mCurrentScrimmedView = mStableScrimmedView;
int currentIndex = root.indexOfChild(mCurrentScrimmedView);
final int childCount = root.getChildCount();
while (mCurrentScrimmedView.getVisibility() != VISIBLE && currentIndex < childCount) {
while (mCurrentScrimmedView != null && mCurrentScrimmedView.getVisibility() != VISIBLE
&& currentIndex < childCount) {
currentIndex++;
mCurrentScrimmedView = root.getChildAt(currentIndex);
}
}
/**
* @return The view to draw the scrim behind.
* @return The view to draw the scrim behind, or null if all visible views should be scrimmed.
*/
public View getScrimmedView() {
public @Nullable View getScrimmedView() {
return mCurrentScrimmedView;
}
}
@@ -0,0 +1,54 @@
/*
* 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.states;
import static com.android.launcher3.LauncherAnimUtils.HINT_TRANSITION_MS;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Workspace;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
/**
* Scale down workspace/hotseat to hint at going to either overview (on pause) or first home screen.
*/
public class HintState extends LauncherState {
private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY | FLAG_DISABLE_RESTORE
| FLAG_HAS_SYS_UI_SCRIM;
public HintState(int id) {
super(id, ContainerType.DEFAULT_CONTAINERTYPE, HINT_TRANSITION_MS, STATE_FLAGS);
}
@Override
public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
return new ScaleAndTranslation(0.9f, 0, 0);
}
@Override
public ScaleAndTranslation getQsbScaleAndTranslation(Launcher launcher) {
// Treat the QSB as part of the hotseat so they move together.
return getHotseatScaleAndTranslation(launcher);
}
@Override
public void onStateTransitionEnd(Launcher launcher) {
launcher.getStateManager().goToState(NORMAL);
Workspace workspace = launcher.getWorkspace();
workspace.post(workspace::moveToDefaultScreen);
}
}
@@ -31,6 +31,7 @@ public final class TestProtocol {
public static final int QUICK_SWITCH_STATE_ORDINAL = 4;
public static final int ALL_APPS_STATE_ORDINAL = 5;
public static final int BACKGROUND_APP_STATE_ORDINAL = 6;
public static final int HINT_STATE_ORDINAL = 7;
public static final String TAPL_EVENTS_TAG = "TaplEvents";
public static String stateOrdinalToString(int ordinal) {
@@ -49,6 +50,8 @@ public final class TestProtocol {
return "AllApps";
case BACKGROUND_APP_STATE_ORDINAL:
return "Background";
case HINT_STATE_ORDINAL:
return "Hint";
default:
return null;
}
@@ -525,7 +525,11 @@ public abstract class AbstractStateChangeTouchController
if (targetState != mStartState) {
logReachedState(logAction, targetState);
}
mLauncher.getStateManager().goToState(targetState, false /* animated */);
if (!mLauncher.isInState(targetState)) {
// If we're already in the target state, don't jump to it at the end of the animation in
// case the user started interacting with it before the animation finished.
mLauncher.getStateManager().goToState(targetState, false /* animated */);
}
mLauncher.getDragLayer().getScrim().animateToSysuiMultiplier(1, 0, 0);
}
@@ -54,8 +54,8 @@ public class SingleAxisSwipeDetector extends BaseSwipeDetector {
}
@Override
boolean canScrollStart(PointF displacement, float touchSlop) {
return Math.abs(displacement.y) >= Math.max(Math.abs(displacement.x), touchSlop);
float extractOrthogonalDirection(PointF direction) {
return direction.x;
}
};
@@ -80,9 +80,10 @@ public class SingleAxisSwipeDetector extends BaseSwipeDetector {
}
@Override
boolean canScrollStart(PointF displacement, float touchSlop) {
return Math.abs(displacement.x) >= Math.max(Math.abs(displacement.y), touchSlop);
float extractOrthogonalDirection(PointF direction) {
return direction.y;
}
};
private final Direction mDir;
@@ -126,7 +127,9 @@ public class SingleAxisSwipeDetector extends BaseSwipeDetector {
@Override
protected boolean shouldScrollStart(PointF displacement) {
// Reject cases where the angle or slop condition is not met.
if (!mDir.canScrollStart(displacement, mTouchSlop)) {
float minDisplacement = Math.max(mTouchSlop,
Math.abs(mDir.extractOrthogonalDirection(displacement)));
if (Math.abs(mDir.extractDirection(displacement)) < minDisplacement) {
return false;
}
@@ -150,7 +153,8 @@ public class SingleAxisSwipeDetector extends BaseSwipeDetector {
@Override
protected void reportDraggingInternal(PointF displacement, MotionEvent event) {
mListener.onDrag(mDir.extractDirection(displacement), event);
mListener.onDrag(mDir.extractDirection(displacement),
mDir.extractOrthogonalDirection(displacement), event);
}
@Override
@@ -164,13 +168,16 @@ public class SingleAxisSwipeDetector extends BaseSwipeDetector {
/** @param start whether this was the original drag start, as opposed to a recatch. */
void onDragStart(boolean start);
// TODO remove
boolean onDrag(float displacement);
default boolean onDrag(float displacement, MotionEvent event) {
return onDrag(displacement);
}
default boolean onDrag(float displacement, float orthogonalDisplacement, MotionEvent ev) {
return onDrag(displacement, ev);
}
void onDragEnd(float velocity);
}
@@ -183,8 +190,7 @@ public class SingleAxisSwipeDetector extends BaseSwipeDetector {
/** Returns the part of the given {@link PointF} that is relevant to this direction. */
abstract float extractDirection(PointF point);
/** Reject cases where the angle or slop condition is not met. */
abstract boolean canScrollStart(PointF displacement, float touchSlop);
abstract float extractOrthogonalDirection(PointF point);
}
}
@@ -49,7 +49,7 @@ public class PromiseIconUiTest extends AbstractLauncherUiTest {
super.setUp();
mDevice.pressHome();
waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null);
waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
mSessionId = -1;
}
@@ -22,15 +22,16 @@ import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyFloat;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import androidx.test.InstrumentationRegistry;
@@ -158,7 +159,7 @@ public class SingleAxisSwipeDetectorTest {
mGenerator.put(0, 100, 100);
mGenerator.move(0, 100, 100 + mTouchSlop);
// TODO: actually calculate the following parameters and do exact value checks.
verify(mMockListener).onDrag(anyFloat(), anyObject());
verify(mMockListener).onDrag(anyFloat(), anyFloat(), any(MotionEvent.class));
}
@Test
@@ -84,6 +84,7 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Base class for all instrumentation tests providing various utility methods.
@@ -281,9 +282,9 @@ public abstract class AbstractLauncherUiTest {
// Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call expecting
// the results of that gesture because the wait can hide flakeness.
protected void waitForState(String message, LauncherState state) {
protected void waitForState(String message, Supplier<LauncherState> state) {
waitForLauncherCondition(message,
launcher -> launcher.getStateManager().getCurrentStableState() == state);
launcher -> launcher.getStateManager().getCurrentStableState() == state.get());
}
protected void waitForResumed(String message) {
@@ -430,9 +431,9 @@ public abstract class AbstractLauncherUiTest {
return !launcher.hasBeenResumed();
}
protected boolean isInState(LauncherState state) {
protected boolean isInState(Supplier<LauncherState> state) {
if (!TestHelpers.isInLauncherProcess()) return true;
return getFromLauncher(launcher -> launcher.getStateManager().getState() == state);
return getFromLauncher(launcher -> launcher.getStateManager().getState() == state.get());
}
protected int getAllAppsScroll(Launcher launcher) {
@@ -76,7 +76,8 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
test.clearLauncherData();
test.mDevice.pressHome();
test.waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null);
test.waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
test.waitForState("Launcher internal state didn't switch to Home",
() -> LauncherState.NORMAL);
test.waitForResumed("Launcher internal state is still Background");
// Check that we switched to home.
test.mLauncher.getWorkspace();
@@ -146,7 +147,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
assertTrue(
"Launcher internal state is not All Apps",
test.isInState(LauncherState.ALL_APPS));
test.isInState(() -> LauncherState.ALL_APPS));
// Test flinging forward and backward.
test.executeOnLauncher(launcher -> assertEquals(
@@ -155,7 +156,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
allApps.flingForward();
assertTrue("Launcher internal state is not All Apps",
test.isInState(LauncherState.ALL_APPS));
test.isInState(() -> LauncherState.ALL_APPS));
final Integer flingForwardY = test.getFromLauncher(
launcher -> test.getAllAppsScroll(launcher));
test.executeOnLauncher(
@@ -165,7 +166,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
allApps.flingBackward();
assertTrue(
"Launcher internal state is not All Apps",
test.isInState(LauncherState.ALL_APPS));
test.isInState(() -> LauncherState.ALL_APPS));
final Integer flingBackwardY = test.getFromLauncher(
launcher -> test.getAllAppsScroll(launcher));
test.executeOnLauncher(launcher -> assertTrue("flingBackward() didn't scroll App Apps",
@@ -182,7 +183,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
assertTrue(
"Launcher internal state is not All Apps",
test.isInState(LauncherState.ALL_APPS));
test.isInState(() -> LauncherState.ALL_APPS));
} finally {
allApps.unfreeze();
}
@@ -193,7 +194,8 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
public void testWorkspaceSwitchToAllApps() {
assertNotNull("switchToAllApps() returned null",
mLauncher.getWorkspace().switchToAllApps());
assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
assertTrue("Launcher internal state is not All Apps",
isInState(() -> LauncherState.ALL_APPS));
}
@Test
@@ -219,7 +221,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
// Test flinging workspace.
workspace.flingBackward();
assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
assertTrue("Launcher internal state is not Home", isInState(() -> LauncherState.NORMAL));
executeOnLauncher(
launcher -> assertEquals("Flinging back didn't switch workspace to page #0",
0, getCurrentWorkspacePage(launcher)));
@@ -228,7 +230,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
executeOnLauncher(
launcher -> assertEquals("Flinging forward didn't switch workspace to page #1",
1, getCurrentWorkspacePage(launcher)));
assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
assertTrue("Launcher internal state is not Home", isInState(() -> LauncherState.NORMAL));
// Test starting a workspace app.
final AppIcon app = workspace.getWorkspaceAppIcon("Chrome");
@@ -254,7 +256,8 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
@PortraitLandscape
public void testAppIconLaunchFromAllAppsFromHome() throws Exception {
final AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
assertTrue("Launcher internal state is not All Apps",
isInState(() -> LauncherState.ALL_APPS));
runIconLaunchFromAllAppsTest(this, allApps);
}