Unifying activity tracker callback
> Using a common class for both Launcher and RecentsActivity
> Removing static refenrece to LauncherModel and using a common pattern for
tracking activities
Bug: 141376165
Bug: 137568159
Change-Id: Ic1897abe6913ec78e25725118eedf5b468d5ec70
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4f8b20e..9326e95 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -113,7 +113,6 @@
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.qsb.QsbContainerView;
-import com.android.launcher3.states.InternalStateHandler;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.uioverrides.DejankBinderTracker;
@@ -124,6 +123,7 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ActivityResultInfo;
+import com.android.launcher3.util.ActivityTracker;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -176,6 +176,9 @@
Callbacks, LauncherProviderChangeListener, UserEventDelegate,
InvariantDeviceProfile.OnIDPChangeListener, PluginListener<OverlayPlugin> {
public static final String TAG = "Launcher";
+
+ public static final ActivityTracker<Launcher> ACTIVITY_TRACKER = new ActivityTracker<>();
+
static final boolean LOGD = false;
static final boolean DEBUG_STRICT_MODE = false;
@@ -359,7 +362,7 @@
mAppTransitionManager = LauncherAppTransitionManager.newInstance(this);
- boolean internalStateHandled = InternalStateHandler.handleCreate(this, getIntent());
+ boolean internalStateHandled = ACTIVITY_TRACKER.handleCreate(this);
if (internalStateHandled) {
if (savedInstanceState != null) {
// InternalStateHandler has already set the appropriate state.
@@ -1444,8 +1447,7 @@
boolean shouldMoveToDefaultScreen = alreadyOnHome && isInState(NORMAL)
&& AbstractFloatingView.getTopOpenView(this) == null;
boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction());
- boolean internalStateHandled = InternalStateHandler
- .handleNewIntent(this, intent, isStarted());
+ boolean internalStateHandled = ACTIVITY_TRACKER.handleNewIntent(this, intent);
if (isActionMain) {
if (!internalStateHandled) {
@@ -1535,6 +1537,7 @@
@Override
public void onDestroy() {
super.onDestroy();
+ ACTIVITY_TRACKER.onActivityDestroyed(this);
unregisterReceiver(mScreenOffReceiver);
mWorkspace.removeFolderListeners();
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index 8c59626..dc2f7bd 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -31,12 +31,10 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.LauncherStateManager.StateListener;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.states.InternalStateHandler;
/**
* Abstract base class of floating view responsible for showing discovery bounce animation
@@ -181,7 +179,7 @@
if (withDelay) {
new Handler().postDelayed(() -> showForOverviewIfNeeded(launcher, false), DELAY_MS);
return;
- } else if (InternalStateHandler.hasPending()
+ } else if (Launcher.ACTIVITY_TRACKER.hasPending()
|| AbstractFloatingView.getTopOpenView(launcher) != null) {
// TODO: Move these checks to the top and call this method after invalidate handler.
return;
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 9fb1090..b8981b6 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -45,6 +45,7 @@
import com.android.launcher3.BaseActivity;
import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetHost;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
@@ -176,7 +177,7 @@
.setPackage(getPackageName())
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- listener.initWhenReady();
+ Launcher.ACTIVITY_TRACKER.schedule(listener);
startActivity(homeIntent,
ActivityOptions.makeCustomAnimation(this, 0, android.R.anim.fade_out).toBundle());
mFinishOnPause = true;
diff --git a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
index 1b08723..75693c6 100644
--- a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
@@ -36,8 +36,7 @@
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
-import com.android.launcher3.states.InternalStateHandler;
-import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.util.ActivityTracker.SchedulerCallback;
import com.android.launcher3.widget.PendingItemDragHelper;
import java.util.UUID;
@@ -45,8 +44,8 @@
/**
* {@link DragSource} for handling drop from a different window.
*/
-public abstract class BaseItemDragListener extends InternalStateHandler implements
- View.OnDragListener, DragSource, DragOptions.PreDragCondition {
+public abstract class BaseItemDragListener implements View.OnDragListener, DragSource,
+ DragOptions.PreDragCondition, SchedulerCallback<Launcher> {
private static final String TAG = "BaseItemDragListener";
@@ -165,7 +164,7 @@
}
protected void postCleanup() {
- clearReference();
+ Launcher.ACTIVITY_TRACKER.clearReference(this);
if (mLauncher != null) {
// Remove any drag params from the launcher intent since the drag operation is complete.
Intent newIntent = new Intent(mLauncher.getIntent());
diff --git a/src/com/android/launcher3/states/InternalStateHandler.java b/src/com/android/launcher3/states/InternalStateHandler.java
deleted file mode 100644
index a23cd6d..0000000
--- a/src/com/android/launcher3/states/InternalStateHandler.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2017 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.util.Executors.MAIN_EXECUTOR;
-
-import android.content.Intent;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.IBinder;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.model.BgDataModel.Callbacks;
-
-import java.lang.ref.WeakReference;
-
-/**
- * Utility class to sending state handling logic to Launcher from within the same process.
- *
- * Extending {@link Binder} ensures that the platform maintains a single instance of each object
- * which allows this object to safely navigate the system process.
- */
-public abstract class InternalStateHandler extends Binder {
-
- public static final String EXTRA_STATE_HANDLER = "launcher.state_handler";
-
- private static final Scheduler sScheduler = new Scheduler();
-
- /**
- * Initializes the handler when the launcher is ready.
- * @return true if the handler wants to stay alive.
- */
- protected abstract boolean init(Launcher launcher, boolean alreadyOnHome);
-
- public final Intent addToIntent(Intent intent) {
- Bundle extras = new Bundle();
- extras.putBinder(EXTRA_STATE_HANDLER, this);
- intent.putExtras(extras);
- return intent;
- }
-
- public final void initWhenReady() {
- sScheduler.schedule(this);
- }
-
- public boolean clearReference() {
- return sScheduler.clearReference(this);
- }
-
- public static boolean hasPending() {
- return sScheduler.hasPending();
- }
-
- public static boolean handleCreate(Launcher launcher, Intent intent) {
- return handleIntent(launcher, intent, false, false);
- }
-
- public static boolean handleNewIntent(Launcher launcher, Intent intent, boolean alreadyOnHome) {
- return handleIntent(launcher, intent, alreadyOnHome, true);
- }
-
- private static boolean handleIntent(
- Launcher launcher, Intent intent, boolean alreadyOnHome, boolean explicitIntent) {
- boolean result = false;
- if (intent != null && intent.getExtras() != null) {
- IBinder stateBinder = intent.getExtras().getBinder(EXTRA_STATE_HANDLER);
- if (stateBinder instanceof InternalStateHandler) {
- InternalStateHandler handler = (InternalStateHandler) stateBinder;
- if (!handler.init(launcher, alreadyOnHome)) {
- intent.getExtras().remove(EXTRA_STATE_HANDLER);
- }
- result = true;
- }
- }
- if (!result && !explicitIntent) {
- result = sScheduler.initIfPending(launcher, alreadyOnHome);
- }
- return result;
- }
-
- private static class Scheduler implements Runnable {
-
- private WeakReference<InternalStateHandler> mPendingHandler = new WeakReference<>(null);
-
- public void schedule(InternalStateHandler handler) {
- synchronized (this) {
- mPendingHandler = new WeakReference<>(handler);
- }
- MAIN_EXECUTOR.execute(this);
- }
-
- @Override
- public void run() {
- LauncherAppState app = LauncherAppState.getInstanceNoCreate();
- if (app == null) {
- return;
- }
- Callbacks cb = app.getModel().getCallback();
- if (!(cb instanceof Launcher)) {
- return;
- }
- Launcher launcher = (Launcher) cb;
- initIfPending(launcher, launcher.isStarted());
- }
-
- public boolean initIfPending(Launcher launcher, boolean alreadyOnHome) {
- InternalStateHandler pendingHandler = mPendingHandler.get();
- if (pendingHandler != null) {
- if (!pendingHandler.init(launcher, alreadyOnHome)) {
- clearReference(pendingHandler);
- }
- return true;
- }
- return false;
- }
-
- public boolean clearReference(InternalStateHandler handler) {
- synchronized (this) {
- if (mPendingHandler.get() == handler) {
- mPendingHandler.clear();
- return true;
- }
- return false;
- }
- }
-
- public boolean hasPending() {
- return mPendingHandler.get() != null;
- }
- }
-}
\ No newline at end of file
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index abf90e2..852928b 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -24,7 +24,6 @@
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.Resources;
import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
@@ -59,7 +58,9 @@
private boolean mAutoRotateEnabled;
/**
- * Rotation request made by {@link InternalStateHandler}. This supersedes any other request.
+ * Rotation request made by
+ * {@link com.android.launcher3.util.ActivityTracker.SchedulerCallback}.
+ * This supersedes any other request.
*/
private int mStateHandlerRequest = REQUEST_NONE;
/**
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index a71a53b..619293b 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -56,8 +56,7 @@
mDeviceProfile = InvariantDeviceProfile.INSTANCE.
get(context).getDeviceProfile(context);
mLauncherAppState = LauncherAppState.getInstanceNoCreate();
- mLauncher = mLauncherAppState != null ?
- (Launcher) mLauncherAppState.getModel().getCallback() : null;
+ mLauncher = Launcher.ACTIVITY_TRACKER.getCreatedActivity();
}
public Bundle call(String method) {
diff --git a/src/com/android/launcher3/util/ActivityTracker.java b/src/com/android/launcher3/util/ActivityTracker.java
new file mode 100644
index 0000000..e85a4e8
--- /dev/null
+++ b/src/com/android/launcher3/util/ActivityTracker.java
@@ -0,0 +1,131 @@
+/*
+ * 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.launcher3.util;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseActivity;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Helper class to statically track activity creation
+ */
+public final class ActivityTracker<T extends BaseActivity> implements Runnable {
+
+ private WeakReference<T> mCurrentActivity = new WeakReference<>(null);
+ private WeakReference<SchedulerCallback<T>> mPendingCallback = new WeakReference<>(null);
+
+ private static final String EXTRA_SCHEDULER_CALLBACK = "launcher.scheduler_callback";
+
+ @Nullable
+ public <R extends T> R getCreatedActivity() {
+ return (R) mCurrentActivity.get();
+ }
+
+ public void onActivityDestroyed(T activity) {
+ if (mCurrentActivity.get() == activity) {
+ mCurrentActivity.clear();
+ }
+ }
+
+ public void schedule(SchedulerCallback<? extends T> callback) {
+ synchronized (this) {
+ mPendingCallback = new WeakReference<>((SchedulerCallback<T>) callback);
+ }
+ MAIN_EXECUTOR.execute(this);
+ }
+
+ @Override
+ public void run() {
+ T activity = mCurrentActivity.get();
+ if (activity != null) {
+ initIfPending(activity, activity.isStarted());
+ }
+ }
+
+ public boolean initIfPending(T activity, boolean alreadyOnHome) {
+ SchedulerCallback<T> pendingCallback = mPendingCallback.get();
+ if (pendingCallback != null) {
+ if (!pendingCallback.init(activity, alreadyOnHome)) {
+ clearReference(pendingCallback);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public boolean clearReference(SchedulerCallback<? extends T> handler) {
+ synchronized (this) {
+ if (mPendingCallback.get() == handler) {
+ mPendingCallback.clear();
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public boolean hasPending() {
+ return mPendingCallback.get() != null;
+ }
+
+ public boolean handleCreate(T activity) {
+ mCurrentActivity = new WeakReference<>(activity);
+ return handleIntent(activity, activity.getIntent(), false, false);
+ }
+
+ public boolean handleNewIntent(T activity, Intent intent) {
+ return handleIntent(activity, intent, activity.isStarted(), true);
+ }
+
+ private boolean handleIntent(
+ T activity, Intent intent, boolean alreadyOnHome, boolean explicitIntent) {
+ boolean result = false;
+ if (intent != null && intent.getExtras() != null) {
+ IBinder stateBinder = intent.getExtras().getBinder(EXTRA_SCHEDULER_CALLBACK);
+ if (stateBinder instanceof ObjectWrapper) {
+ SchedulerCallback<T> handler =
+ ((ObjectWrapper<SchedulerCallback>) stateBinder).get();
+ if (!handler.init(activity, alreadyOnHome)) {
+ intent.getExtras().remove(EXTRA_SCHEDULER_CALLBACK);
+ }
+ result = true;
+ }
+ }
+ if (!result && !explicitIntent) {
+ result = initIfPending(activity, alreadyOnHome);
+ }
+ return result;
+ }
+
+ public interface SchedulerCallback<T extends BaseActivity> {
+
+ boolean init(T activity, boolean alreadyOnHome);
+
+ default Intent addToIntent(Intent intent) {
+ Bundle extras = new Bundle();
+ extras.putBinder(EXTRA_SCHEDULER_CALLBACK, ObjectWrapper.wrap(this));
+ intent.putExtras(extras);
+ return intent;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/util/ObjectWrapper.java b/src/com/android/launcher3/util/ObjectWrapper.java
new file mode 100644
index 0000000..b692431
--- /dev/null
+++ b/src/com/android/launcher3/util/ObjectWrapper.java
@@ -0,0 +1,41 @@
+/*
+ * 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.launcher3.util;
+
+import android.os.Binder;
+import android.os.IBinder;
+
+/**
+ * Utility class to pass non-parcealable objects within same process using parcealable payload.
+ *
+ * It wraps the object in a binder as binders are singleton within a process
+ */
+public class ObjectWrapper<T> extends Binder {
+
+ private final T mObject;
+
+ public ObjectWrapper(T object) {
+ mObject = object;
+ }
+
+ public T get() {
+ return mObject;
+ }
+
+ public static IBinder wrap(Object obj) {
+ return new ObjectWrapper<>(obj);
+ }
+}