mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-03-01 16:26:47 +00:00
Cancel gestures on launcher destroy
A wide variety of gesture navigation bugs were caused if launcher happened to be recreated mid-gesture and we didn't handle it gracefully. Updated lifecycle callbacks to immediately cancel the ongoing gesture if launcher is destroyed. Fixes: 244593270 Fixes: 257976590 Test: forcefully(programmatically) recreate on every other gesture nav handle touch down; check that following gestures are not broken and animations are not frozen Change-Id: Ia8e936e26a42cfe26fc6bcd9eefb25d37bc08749
This commit is contained in:
@@ -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() { }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user