Merge "Support predictive back from all apps to home" into tm-qpr-dev am: 944348a522

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/20789107

Change-Id: Ib53aa807f447aeba483f6a4a10f078a5b39bf660
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
TreeHugger Robot
2023-01-12 17:50:42 +00:00
committed by Automerger Merge Worker
9 changed files with 171 additions and 12 deletions

View File

@@ -69,9 +69,13 @@ import android.view.HapticFeedbackConstants;
import android.view.RemoteAnimationTarget;
import android.view.View;
import android.view.WindowManagerGlobal;
import android.window.BackEvent;
import android.window.OnBackAnimationCallback;
import android.window.OnBackInvokedDispatcher;
import android.window.SplashScreen;
import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.app.viewcapture.ViewCapture;
@@ -105,6 +109,8 @@ import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.taskbar.TaskbarManager;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.uioverrides.QuickstepWidgetHolder.QuickstepHolderFactory;
import com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory;
import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
@@ -623,6 +629,29 @@ public class QuickstepLauncher extends Launcher {
}
}
@Override
protected void registerBackDispatcher() {
getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
OnBackInvokedDispatcher.PRIORITY_DEFAULT,
new OnBackAnimationCallback() {
@Override
public void onBackInvoked() {
onBackPressed();
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked");
}
@Override
public void onBackProgressed(@NonNull BackEvent backEvent) {
QuickstepLauncher.this.onBackProgressed(backEvent.getProgress());
}
@Override
public void onBackCancelled() {
QuickstepLauncher.this.onBackCancelled();
}
});
}
private void onTaskbarInAppDisplayProgressUpdate(float progress, int flag) {
if (mTaskbarManager == null
|| mTaskbarManager.getCurrentActivityContext() == null

View File

@@ -176,14 +176,7 @@ public abstract class BaseActivity extends Activity implements ActivityContext {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Utilities.ATLEAST_T) {
getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
OnBackInvokedDispatcher.PRIORITY_DEFAULT,
() -> {
onBackPressed();
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked");
});
}
registerBackDispatcher();
}
@Override
@@ -246,6 +239,17 @@ public abstract class BaseActivity extends Activity implements ActivityContext {
}
protected void registerBackDispatcher() {
if (Utilities.ATLEAST_T) {
getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
OnBackInvokedDispatcher.PRIORITY_DEFAULT,
() -> {
onBackPressed();
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked");
});
}
}
public boolean isStarted() {
return (mActivityFlags & ACTIVITY_STATE_STARTED) != 0;
}

View File

@@ -120,6 +120,7 @@ import android.widget.ImageView;
import android.widget.Toast;
import androidx.annotation.CallSuper;
import androidx.annotation.FloatRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
@@ -2065,6 +2066,14 @@ public class Launcher extends StatefulActivity<LauncherState>
mStateManager.getState().onBackPressed(this);
}
protected void onBackProgressed(@FloatRange(from = 0.0, to = 1.0) float backProgress) {
mStateManager.getState().onBackProgressed(this, backProgress);
}
protected void onBackCancelled() {
mStateManager.getState().onBackCancelled(this);
}
protected void onScreenOff() {
// Reset AllApps to its initial state only if we are not in the middle of
// processing a multi-step drop

View File

@@ -34,6 +34,8 @@ import android.content.Context;
import android.graphics.Color;
import android.view.animation.Interpolator;
import androidx.annotation.FloatRange;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.states.HintState;
@@ -342,6 +344,27 @@ public abstract class LauncherState implements BaseState<LauncherState> {
}
}
/**
* Find {@link StateManager} and target {@link LauncherState} to handle back progress in
* predictive back gesture.
*/
public void onBackProgressed(
Launcher launcher, @FloatRange(from = 0.0, to = 1.0) float backProgress) {
StateManager<LauncherState> lsm = launcher.getStateManager();
LauncherState toState = lsm.getLastState();
lsm.onBackProgressed(toState, backProgress);
}
/**
* Find {@link StateManager} and target {@link LauncherState} to handle backProgress in
* predictive back gesture.
*/
public void onBackCancelled(Launcher launcher) {
StateManager<LauncherState> lsm = launcher.getStateManager();
LauncherState toState = lsm.getLastState();
lsm.onBackCancelled(toState);
}
public static abstract class PageAlphaProvider {
public final Interpolator interpolator;

View File

@@ -15,6 +15,7 @@
*/
package com.android.launcher3.allapps;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
@@ -33,11 +34,15 @@ import android.view.HapticFeedbackConstants;
import android.view.View;
import android.view.animation.Interpolator;
import androidx.annotation.FloatRange;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.statemanager.StateManager.StateHandler;
@@ -61,6 +66,8 @@ public class AllAppsTransitionController
implements StateHandler<LauncherState>, OnDeviceProfileChangeListener {
// This constant should match the second derivative of the animator interpolator.
public static final float INTERP_COEFF = 1.7f;
private static final float SWIPE_ALL_APPS_TO_HOME_MIN_SCALE = 0.9f;
private static final int REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS = 200;
public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
new FloatProperty<AllAppsTransitionController>("allAppsProgress") {
@@ -139,6 +146,7 @@ public class AllAppsTransitionController
private ActivityAllAppsContainerView<Launcher> mAppsView;
private final Launcher mLauncher;
private final AnimatedFloat mAllAppScale = new AnimatedFloat(this::onScaleProgressChanged);
private boolean mIsVerticalLayout;
// Whether this class should take care of closing the keyboard.
@@ -232,6 +240,52 @@ public class AllAppsTransitionController
onProgressAnimationEnd();
}
@Override
public void onBackProgressed(
LauncherState toState, @FloatRange(from = 0.0, to = 1.0) float backProgress) {
if (!mLauncher.isInState(ALL_APPS) || !NORMAL.equals(toState)) {
return;
}
float deceleratedProgress =
Interpolators.PREDICTIVE_BACK_DECELERATED_EASE.getInterpolation(backProgress);
float scaleProgress = SWIPE_ALL_APPS_TO_HOME_MIN_SCALE
+ (1 - SWIPE_ALL_APPS_TO_HOME_MIN_SCALE) * (1 - deceleratedProgress);
mAllAppScale.updateValue(scaleProgress);
}
@Override
public void onBackCancelled(LauncherState toState) {
if (!mLauncher.isInState(ALL_APPS) || !NORMAL.equals(toState)) {
return;
}
// TODO: once ag/20649618 is picked into tm-qpr, we don't need to animate back on cancel
// swipe because framework will do that for us in {@link #onBackProgressed}.
animateAllAppsToNoScale();
}
private void onScaleProgressChanged() {
final float scaleProgress = mAllAppScale.value;
SCALE_PROPERTY.set(mLauncher.getAppsView(), scaleProgress);
mLauncher.getScrimView().setScrimHeaderScale(scaleProgress);
AllAppsRecyclerView rv = mLauncher.getAppsView().getActiveRecyclerView();
if (rv != null && rv.getScrollbar() != null) {
rv.getScrollbar().setVisibility(scaleProgress < 1f ? View.INVISIBLE : View.VISIBLE);
}
// TODO(b/264906511): We need to disable view clipping on all apps' parent views so
// that the extra roll of app icons are displayed.
}
private void animateAllAppsToNoScale() {
mAllAppScale.animateToValue(1f)
.setDuration(REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS)
.start();
}
/**
* Creates an animation which updates the vertical transition progress and updates all the
* dependent UI using various animation events
@@ -258,6 +312,8 @@ public class AllAppsTransitionController
if (config.userControlled && success && mShouldControlKeyboard) {
mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard();
}
mAllAppScale.updateValue(1f);
});
}

View File

@@ -808,7 +808,7 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte
}
@Override
public void drawOnScrim(Canvas canvas) {
public void drawOnScrimWithScale(Canvas canvas, float scale) {
boolean isTablet = mActivityContext.getDeviceProfile().isTablet;
// Draw full background panel for tablets.
@@ -833,7 +833,9 @@ public abstract class BaseAllAppsContainerView<T extends Context & ActivityConte
if (mHeaderPaint.getColor() == mScrimColor || mHeaderPaint.getColor() == 0) {
return;
}
int bottom = getHeaderBottom() + getVisibleContainerView().getPaddingTop();
final float offset = (getVisibleContainerView().getHeight() * (1 - scale) / 2);
final float bottom =
scale * (getHeaderBottom() + getVisibleContainerView().getPaddingTop()) + offset;
FloatingHeaderView headerView = getFloatingHeaderView();
if (isTablet) {
// Start adding header protection if search bar or tabs will attach to the top.

View File

@@ -56,6 +56,8 @@ public class Interpolators {
public static final Interpolator DECELERATED_EASE = new PathInterpolator(0, 0, .2f, 1f);
public static final Interpolator ACCELERATED_EASE = new PathInterpolator(0.4f, 0, 1f, 1f);
public static final Interpolator PREDICTIVE_BACK_DECELERATED_EASE =
new PathInterpolator(0, 0, 0, 1f);
/**
* The default emphasized interpolator. Used for hero / emphasized movement of content.

View File

@@ -28,6 +28,8 @@ import android.animation.AnimatorSet;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.FloatRange;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
@@ -195,6 +197,21 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> {
}
}
/** Handles backProgress in predictive back gesture by passing it to state handlers. */
public void onBackProgressed(
STATE_TYPE toState, @FloatRange(from = 0.0, to = 1.0) float backProgress) {
for (StateHandler handler : getStateHandlers()) {
handler.onBackProgressed(toState, backProgress);
}
}
/** Handles back cancelled event in predictive back gesture by passing it to state handlers. */
public void onBackCancelled(STATE_TYPE toState) {
for (StateHandler handler : getStateHandlers()) {
handler.onBackCancelled(toState);
}
}
private void goToState(
STATE_TYPE state, boolean animated, long delay, AnimatorListener listener) {
animated &= areAnimatorsEnabled();
@@ -586,6 +603,13 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> {
*/
void setStateWithAnimation(
STATE_TYPE toState, StateAnimationConfig config, PendingAnimation animation);
/** Handles backProgress in predictive back gesture for target state. */
default void onBackProgressed(
STATE_TYPE toState, @FloatRange(from = 0.0, to = 1.0) float backProgress) {};
/** Handles back cancelled event in predictive back gesture for target state. */
default void onBackCancelled(STATE_TYPE toState) {};
}
public interface StateListener<STATE_TYPE> {

View File

@@ -46,6 +46,7 @@ public class ScrimView extends View implements Insettable {
private int mBackgroundColor;
private boolean mIsVisible = true;
private boolean mLastDispatchedOpaqueness;
private float mHeaderScale = 1f;
public ScrimView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -91,7 +92,16 @@ public class ScrimView extends View implements Insettable {
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mDrawingController != null) {
mDrawingController.drawOnScrim(canvas);
mDrawingController.drawOnScrimWithScale(canvas, mHeaderScale);
}
}
/** Set scrim header's scale and bottom offset. */
public void setScrimHeaderScale(float scale) {
boolean hasChanged = mHeaderScale != scale;
mHeaderScale = scale;
if (hasChanged) {
invalidate();
}
}
@@ -176,6 +186,6 @@ public class ScrimView extends View implements Insettable {
/**
* Called inside ScrimView#OnDraw
*/
void drawOnScrim(Canvas canvas);
void drawOnScrimWithScale(Canvas canvas, float scale);
}
}