Merge "Cancel gestures on launcher destroy" into tm-qpr-dev

This commit is contained in:
TreeHugger Robot
2022-12-05 20:25:47 +00:00
committed by Android (Google) Code Review
11 changed files with 243 additions and 118 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 The Android Open Source Project
* Copyright (C) 2022 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.
@@ -16,19 +16,23 @@
package com.android.launcher3;
import android.animation.AnimatorSet;
import android.annotation.Nullable;
import android.annotation.TargetApi;
import android.os.Build;
import android.os.CancellationSignal;
import android.view.RemoteAnimationTarget;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.ActivityLifecycleListener;
import com.android.quickstep.util.RemoteAnimationProvider;
import java.util.function.BiPredicate;
/**
* {@link ActivityLifecycleListener} for the in-launcher recents.
*/
@TargetApi(Build.VERSION_CODES.P)
public class LauncherInitListener extends ActivityInitListener<Launcher> {
public class LauncherLifecycleListener extends ActivityLifecycleListener<Launcher> {
private RemoteAnimationProvider mRemoteAnimationProvider;
@@ -36,13 +40,16 @@ public class LauncherInitListener extends ActivityInitListener<Launcher> {
* @param onInitListener a callback made when the activity is initialized. The callback should
* return true to continue receiving callbacks (ie. for if the activity is
* recreated).
* @param onDestroyListener a callback made when the activity is destroyed.
*/
public LauncherInitListener(BiPredicate<Launcher, Boolean> onInitListener) {
super(onInitListener, Launcher.ACTIVITY_TRACKER);
public LauncherLifecycleListener(
@Nullable BiPredicate<Launcher, Boolean> onInitListener,
@Nullable Runnable onDestroyListener) {
super(onInitListener, onDestroyListener, Launcher.ACTIVITY_TRACKER);
}
@Override
public boolean handleInit(Launcher launcher, boolean alreadyOnHome) {
public boolean handleActivityReady(Launcher launcher, boolean alreadyOnHome) {
if (mRemoteAnimationProvider != null) {
QuickstepTransitionManager appTransitionManager =
((QuickstepLauncher) launcher).getAppTransitionManager();
@@ -68,7 +75,7 @@ public class LauncherInitListener extends ActivityInitListener<Launcher> {
}, cancellationSignal);
}
launcher.deferOverlayCallbacksUntilNextResumeOrStop();
return super.handleInit(launcher, alreadyOnHome);
return super.handleActivityReady(launcher, alreadyOnHome);
}
@Override

View File

@@ -50,6 +50,7 @@ import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHE
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.CANCEL_RECENTS_ANIMATION;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.EXPECTING_TASK_APPEARED;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.LAUNCHER_DESTROYED;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_SETTLED_ON_END_TARGET;
import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
@@ -113,7 +114,7 @@ import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.ActivityLifecycleListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.InputConsumerProxy;
import com.android.quickstep.util.InputProxyHandlerFactory;
@@ -159,7 +160,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
protected final BaseActivityInterface<S, T> mActivityInterface;
protected final InputConsumerProxy mInputConsumerProxy;
protected final ActivityInitListener mActivityInitListener;
protected final ActivityLifecycleListener mActivityInitListener;
// Callbacks to be made once the recents animation starts
private final ArrayList<Runnable> mRecentsAnimationStartCallbacks = new ArrayList<>();
private final OnScrollChangedListener mOnRecentsScrollListener = this::onRecentsViewScroll;
@@ -329,7 +330,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
InputConsumerController inputConsumer) {
super(context, deviceState, gestureState);
mActivityInterface = gestureState.getActivityInterface();
mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);
mActivityInitListener = mActivityInterface.createActivityLifecycleListener(
this::onActivityInit, this::onActivityDestroy);
mInputConsumerProxy =
new InputConsumerProxy(context, /* rotationSupplier = */ () -> {
if (mRecentsView == null) {
@@ -520,6 +522,11 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
return true;
}
private void onActivityDestroy() {
ActiveGestureLog.INSTANCE.addLog("Launcher activity destroyed", LAUNCHER_DESTROYED);
onGestureCancelled();
}
/**
* Return true if the window should be translated horizontally if the recents view scrolls
*/

View File

@@ -60,7 +60,7 @@ import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.ActivityLifecycleListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -123,8 +123,17 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
public abstract AnimationFactory prepareRecentsUI(RecentsAnimationDeviceState deviceState,
boolean activityVisible, Consumer<AnimatorControllerWithResistance> callback);
public abstract ActivityInitListener createActivityInitListener(
Predicate<Boolean> onInitListener);
/**
* Creates a activity listener for activity initialized and/or destroyed. One or both of these
* listeners must be provided.
*
* @param onInitListener a callback made when the activity is initialized. The callback should
* return true to continue receiving callbacks (ie. for if the activity is
* recreated).
* @param onDestroyListener a callback made when the activity is destroyed.
*/
public abstract ActivityLifecycleListener createActivityLifecycleListener(
@Nullable Predicate<Boolean> onInitListener, @Nullable Runnable onDestroyListener);
/**
* Sets a callback to be run when an activity launch happens while launcher is not yet resumed.

View File

@@ -36,7 +36,7 @@ import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.fallback.RecentsState;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.ActivityLifecycleListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.views.RecentsView;
@@ -88,10 +88,12 @@ public final class FallbackActivityInterface extends
}
@Override
public ActivityInitListener createActivityInitListener(
Predicate<Boolean> onInitListener) {
return new ActivityInitListener<>((activity, alreadyOnHome) ->
onInitListener.test(alreadyOnHome), RecentsActivity.ACTIVITY_TRACKER);
public ActivityLifecycleListener createActivityLifecycleListener(
@Nullable Predicate<Boolean> onInitListener, @Nullable Runnable onDestroyListener) {
return new ActivityLifecycleListener<>(
(activity, alreadyOnHome) -> onInitListener.test(alreadyOnHome),
onDestroyListener,
RecentsActivity.ACTIVITY_TRACKER);
}
@Nullable

View File

@@ -37,7 +37,7 @@ import androidx.annotation.UiThread;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherInitListener;
import com.android.launcher3.LauncherLifecycleListener;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statehandlers.DepthController;
@@ -49,7 +49,7 @@ import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.NavigationMode;
import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.ActivityLifecycleListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
@@ -136,9 +136,10 @@ public final class LauncherActivityInterface extends
}
@Override
public ActivityInitListener createActivityInitListener(Predicate<Boolean> onInitListener) {
return new LauncherInitListener((activity, alreadyOnHome) ->
onInitListener.test(alreadyOnHome));
public ActivityLifecycleListener createActivityLifecycleListener(
@Nullable Predicate<Boolean> onInitListener, @Nullable Runnable onDestroyListener) {
return new LauncherLifecycleListener((activity, alreadyOnHome) ->
onInitListener.test(alreadyOnHome), onDestroyListener);
}
@Override

View File

@@ -37,7 +37,7 @@ public class ActiveGestureErrorDetector {
ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION, FINISH_RECENTS_ANIMATION,
CANCEL_RECENTS_ANIMATION, SET_ON_PAGE_TRANSITION_END_CALLBACK, CANCEL_CURRENT_ANIMATION,
CLEANUP_SCREENSHOT, SCROLLER_ANIMATION_ABORTED, TASK_APPEARED, EXPECTING_TASK_APPEARED,
FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER,
FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER, LAUNCHER_DESTROYED,
/**
* These GestureEvents are specifically associated to state flags that get set in
@@ -162,6 +162,13 @@ public class ActiveGestureErrorDetector {
+ "before/without setting end target to new task",
writer);
break;
case LAUNCHER_DESTROYED:
errorDetected |= printErrorIfTrue(
true,
prefix,
/* errorMessage= */ "Launcher destroyed mid-gesture",
writer);
break;
case STATE_GESTURE_COMPLETED:
errorDetected |= printErrorIfTrue(
!encounteredEvents.contains(GestureEvent.MOTION_UP),

View File

@@ -1,73 +0,0 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.quickstep.util;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.util.ActivityTracker;
import com.android.launcher3.util.ActivityTracker.SchedulerCallback;
import java.util.function.BiPredicate;
public class ActivityInitListener<T extends BaseActivity> implements
SchedulerCallback<T> {
private BiPredicate<T, Boolean> mOnInitListener;
private final ActivityTracker<T> mActivityTracker;
private boolean mIsRegistered = false;
/**
* @param onInitListener a callback made when the activity is initialized. The callback should
* return true to continue receiving callbacks (ie. for if the activity is
* recreated).
*/
public ActivityInitListener(BiPredicate<T, Boolean> onInitListener,
ActivityTracker<T> tracker) {
mOnInitListener = onInitListener;
mActivityTracker = tracker;
}
@Override
public final boolean init(T activity, boolean alreadyOnHome) {
if (!mIsRegistered) {
// Don't receive any more updates
return false;
}
return handleInit(activity, alreadyOnHome);
}
protected boolean handleInit(T activity, boolean alreadyOnHome) {
return mOnInitListener.test(activity, alreadyOnHome);
}
/**
* Registers the activity-created listener. If the activity is already created, then the
* callback provided in the constructor will be called synchronously.
*/
public void register() {
mIsRegistered = true;
mActivityTracker.registerCallback(this);
}
/**
* After calling this, we won't {@link #init} even when the activity is ready.
*/
public void unregister() {
mActivityTracker.unregisterCallback(this);
mIsRegistered = false;
mOnInitListener = null;
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2022 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 android.util.Log;
import androidx.annotation.Nullable;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.util.ActivityTracker;
import com.android.launcher3.util.ActivityTracker.SchedulerCallback;
import java.util.function.BiPredicate;
/**
* Listener for activity initialized and/or destroyed.
*/
public class ActivityLifecycleListener<T extends BaseActivity> implements
SchedulerCallback<T> {
private static final String TAG = "ActivityLifecycleListener";
@Nullable private final BiPredicate<T, Boolean> mOnInitListener;
@Nullable private final Runnable mOnDestroyListener;
private final ActivityTracker<T> mActivityTracker;
private boolean mIsRegistered = false;
/**
* One or both of {@code onInitListener} and {@code onInitListener} must be provided, otherwise
* the created instance will effectively be a no-op.
*
* @param onInitListener a callback made when the activity is initialized. The callback should
* return true to continue receiving callbacks (ie. for if the activity is
* recreated).
* @param onDestroyListener a callback made when the activity is destroyed.
*/
public ActivityLifecycleListener(
@Nullable BiPredicate<T, Boolean> onInitListener,
@Nullable Runnable onDestroyListener,
ActivityTracker<T> tracker) {
if (onInitListener == null && onDestroyListener == null) {
throw new IllegalArgumentException("Both listeners cannot be null");
}
mOnInitListener = onInitListener;
mOnDestroyListener = onDestroyListener;
mActivityTracker = tracker;
}
@Override
public final boolean onActivityReady(T activity, boolean alreadyOnHome) {
if (!mIsRegistered) {
// Don't receive any more updates
return false;
}
return handleActivityReady(activity, alreadyOnHome);
}
protected boolean handleActivityReady(T activity, boolean alreadyOnHome) {
if (mOnInitListener == null) {
Log.e(TAG, "Cannot handle init: init listener is null", new Exception());
return false;
}
return mOnInitListener.test(activity, alreadyOnHome);
}
@Override
public void onActivityDestroyed() {
if (mOnDestroyListener == null) {
Log.e(TAG, "Cannot clean up: destroy listener is null", new Exception());
return;
}
mOnDestroyListener.run();
}
/**
* Registers the activity-created listener. If the activity is already created, then the
* callback provided in the constructor will be called synchronously.
*/
public void register() {
mIsRegistered = true;
mActivityTracker.registerCallback(this, getType());
}
/**
* After calling this, we won't call {@link #onActivityReady} even when the activity is ready.
*/
public void unregister() {
mActivityTracker.unregisterCallback(this, getType());
mIsRegistered = false;
}
private int getType() {
return mOnInitListener != null && mOnDestroyListener != null
? ActivityTracker.TYPE_BOTH
: (mOnInitListener != null
? ActivityTracker.TYPE_INIT
: ActivityTracker.TYPE_DESTROY);
}
}

View File

@@ -74,7 +74,7 @@ public abstract class BaseItemDragListener implements View.OnDragListener, DragS
}
@Override
public boolean init(Launcher launcher, boolean alreadyOnHome) {
public boolean onActivityReady(Launcher launcher, boolean alreadyOnHome) {
AbstractFloatingView.closeAllOpenViews(launcher, alreadyOnHome);
launcher.getStateManager().goToState(NORMAL, alreadyOnHome /* animated */);
launcher.getDragLayer().setOnDragListener(this);

View File

@@ -72,8 +72,8 @@ public class PinItemDragListener extends BaseItemDragListener {
}
@Override
public boolean init(Launcher launcher, boolean alreadyOnHome) {
super.init(launcher, alreadyOnHome);
public boolean onActivityReady(Launcher launcher, boolean alreadyOnHome) {
super.onActivityReady(launcher, alreadyOnHome);
if (!alreadyOnHome) {
launcher.useFadeOutAnimationForLauncherStart(mCancelSignal);
}

View File

@@ -20,8 +20,6 @@ import androidx.annotation.Nullable;
import com.android.launcher3.BaseActivity;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.concurrent.CopyOnWriteArrayList;
/**
@@ -30,8 +28,15 @@ import java.util.concurrent.CopyOnWriteArrayList;
*/
public final class ActivityTracker<T extends BaseActivity> {
public static final int TYPE_INIT = 0;
public static final int TYPE_DESTROY = 1;
public static final int TYPE_BOTH = 2;
private WeakReference<T> mCurrentActivity = new WeakReference<>(null);
private CopyOnWriteArrayList<SchedulerCallback<T>> mCallbacks = new CopyOnWriteArrayList<>();
private CopyOnWriteArrayList<SchedulerCallback<T>> mActivityReadyCallbacks =
new CopyOnWriteArrayList<>();
private CopyOnWriteArrayList<SchedulerCallback<T>> mActivityDestroyedCallbacks =
new CopyOnWriteArrayList<>();
@Nullable
public <R extends T> R getCreatedActivity() {
@@ -42,32 +47,74 @@ public final class ActivityTracker<T extends BaseActivity> {
if (mCurrentActivity.get() == activity) {
mCurrentActivity.clear();
}
for (SchedulerCallback<T> cb : mActivityDestroyedCallbacks) {
cb.onActivityDestroyed();
unregisterCallback(cb, TYPE_DESTROY);
}
}
/** Registers an activity create callback. */
public void registerCallback(SchedulerCallback<T> callback) {
registerCallback(callback, TYPE_INIT);
}
/**
* Call {@link SchedulerCallback#init(BaseActivity, boolean)} when the
* activity is ready. If the activity is already created, this is called immediately.
* Call {@link SchedulerCallback#onActivityReady(BaseActivity, boolean)} when the
* activity is ready and/or {@link SchedulerCallback#onActivityDestroyed()} when the activity
* is destroyed.
*
* The tracker maintains a strong ref to the callback, so it is up to the caller to return
* {@code false} in the callback OR to unregister the callback explicitly.
* If type is {@link ActivityTracker#TYPE_INIT} TYPE_INIT or
* {@link ActivityTracker#TYPE_BOTH} and the activity is already created, this
* {@link SchedulerCallback#onActivityReady(BaseActivity, boolean)} is called immediately.
*
* @param callback The callback to call init() on when the activity is ready.
* If type is {@link ActivityTracker#TYPE_DESTROY} or
* {@link ActivityTracker#TYPE_BOTH} and the activity is already destroyed,
* {@link SchedulerCallback#onActivityDestroyed()} is called immediately.
*
* The tracker maintains a strong ref to the callbacks, so it is up to the caller to return
* {@code false} in {@link SchedulerCallback#onActivityReady(BaseActivity, boolean)} OR to
* unregister the callback explicitly.
*
* @param callback The callback to call init() or cleanUp() on when the activity is ready or
* destroyed.
* @param type whether to use this callback on activity create, destroy or both.
*/
public void registerCallback(SchedulerCallback<T> callback) {
public void registerCallback(SchedulerCallback<T> callback, int type) {
T activity = mCurrentActivity.get();
mCallbacks.add(callback);
if (activity != null) {
if (!callback.init(activity, activity.isStarted())) {
unregisterCallback(callback);
if (type == TYPE_INIT || type == TYPE_BOTH) {
mActivityReadyCallbacks.add(callback);
if (activity != null) {
if (!callback.onActivityReady(activity, activity.isStarted())) {
unregisterCallback(callback, TYPE_INIT);
}
}
}
if (type == TYPE_DESTROY || type == TYPE_BOTH) {
mActivityDestroyedCallbacks.add(callback);
if (activity == null) {
callback.onActivityDestroyed();
unregisterCallback(callback, TYPE_DESTROY);
}
}
}
/**
* Unregisters a registered callback.
* Unregisters a registered activity create callback.
*/
public void unregisterCallback(SchedulerCallback<T> callback) {
mCallbacks.remove(callback);
unregisterCallback(callback, TYPE_INIT);
}
/**
* Unregisters a registered callback.
*/
public void unregisterCallback(SchedulerCallback<T> callback, int type) {
if (type == TYPE_INIT || type == TYPE_BOTH) {
mActivityReadyCallbacks.remove(callback);
}
if (type == TYPE_DESTROY || type == TYPE_BOTH) {
mActivityDestroyedCallbacks.remove(callback);
}
}
public boolean handleCreate(T activity) {
@@ -81,8 +128,8 @@ public final class ActivityTracker<T extends BaseActivity> {
private boolean handleIntent(T activity, boolean alreadyOnHome) {
boolean handled = false;
for (SchedulerCallback<T> cb : mCallbacks) {
if (!cb.init(activity, alreadyOnHome)) {
for (SchedulerCallback<T> cb : mActivityReadyCallbacks) {
if (!cb.onActivityReady(activity, alreadyOnHome)) {
// Callback doesn't want any more updates
unregisterCallback(cb);
}
@@ -98,6 +145,11 @@ public final class ActivityTracker<T extends BaseActivity> {
* @param alreadyOnHome Whether the activity is already started.
* @return Whether to continue receiving callbacks (i.e. if the activity is recreated).
*/
boolean init(T activity, boolean alreadyOnHome);
boolean onActivityReady(T activity, boolean alreadyOnHome);
/**
* Called then the activity gets destroyed.
*/
default void onActivityDestroyed() { }
}
}