Adding support for listening for app launch animation completion
Bug: 181165935
Bug: 179065491
Test: Verified on device
Change-Id: Ifa6a91560cb31b4dfb72a0f582607e873d8a002d
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 062ab71..f77f7e8 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -279,7 +279,7 @@
/**
* Used to set the override visibility state, used only to handle the transition home with the
* recents animation.
- * @see QuickstepAppTransitionManagerImpl#createWallpaperOpenRunner
+ * @see QuickstepTransitionManager#createWallpaperOpenRunner
*/
public void addForceInvisibleFlag(@InvisibilityFlags int flag) {
mForceInvisible |= flag;
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 5bfde15..e38ab74 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -27,6 +27,7 @@
import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Process;
import android.os.StrictMode;
@@ -40,6 +41,7 @@
import android.view.WindowMetrics;
import android.widget.Toast;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -52,10 +54,12 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
+import com.android.launcher3.util.ActivityOptionsWrapper;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.WindowBounds;
@@ -76,6 +80,7 @@
protected boolean mIsSafeModeEnabled;
private Runnable mOnStartCallback;
+ private RunnableList mOnResumeCallbacks = new RunnableList();
private int mThemeRes = R.style.AppTheme;
@@ -98,6 +103,16 @@
}
@Override
+ protected void onResume() {
+ super.onResume();
+ mOnResumeCallbacks.executeAllAndClear();
+ }
+
+ public void addOnResumeCallback(Runnable callback) {
+ mOnResumeCallbacks.add(callback);
+ }
+
+ @Override
public void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo) {
updateTheme();
}
@@ -149,20 +164,35 @@
return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight());
}
- public final Bundle getActivityLaunchOptionsAsBundle(View v) {
- ActivityOptions activityOptions = getActivityLaunchOptions(v);
- return activityOptions == null ? null : activityOptions.toBundle();
+ @NonNull
+ public ActivityOptionsWrapper getActivityLaunchOptions(View v) {
+ int left = 0, top = 0;
+ int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
+ if (v instanceof BubbleTextView) {
+ // Launch from center of icon, not entire view
+ Drawable icon = ((BubbleTextView) v).getIcon();
+ if (icon != null) {
+ Rect bounds = icon.getBounds();
+ left = (width - bounds.width()) / 2;
+ top = v.getPaddingTop();
+ width = bounds.width();
+ height = bounds.height();
+ }
+ }
+ ActivityOptions options =
+ ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
+ RunnableList callback = new RunnableList();
+ addOnResumeCallback(callback::executeAllAndDestroy);
+ return new ActivityOptionsWrapper(options, callback);
}
- public abstract ActivityOptions getActivityLaunchOptions(View v);
-
public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item) {
if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
return false;
}
- Bundle optsBundle = (v != null) ? getActivityLaunchOptionsAsBundle(v) : null;
+ Bundle optsBundle = (v != null) ? getActivityLaunchOptions(v).toBundle() : null;
UserHandle user = item == null ? null : item.user;
// Prepare intent
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index e0be6de..2b58fb6 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -50,7 +50,6 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
-import com.android.launcher3.Launcher.OnResumeCallback;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dot.DotInfo;
@@ -81,7 +80,7 @@
* because we want to make the bubble taller than the text and TextView's clip is
* too aggressive.
*/
-public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback,
+public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
IconLabelDotView, DraggableView, Reorderable {
private static final int DISPLAY_WORKSPACE = 0;
@@ -431,13 +430,6 @@
}
}
- @Override
- public void onLauncherResume() {
- // Reset the pressed state of icon that was locked in the press state while activity
- // was launching
- setStayPressed(false);
- }
-
void clearPressedBackground() {
setPressed(false);
setStayPressed(false);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 78e6f68..1546ee3 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -61,7 +61,6 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
-import android.app.ActivityOptions;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
import android.content.ActivityNotFoundException;
@@ -276,7 +275,6 @@
private static final int THEME_CROSS_FADE_ANIMATION_DURATION = 375;
- private LauncherAppTransitionManager mAppTransitionManager;
private Configuration mOldConfig;
private LiveSearchManager mLiveSearchManager;
@@ -312,8 +310,6 @@
@Thunk
boolean mWorkspaceLoading = true;
- private ArrayList<OnResumeCallback> mOnResumeCallbacks = new ArrayList<>();
-
// Used to notify when an activity launch has been deferred because launcher is not yet resumed
// TODO: See if we can remove this later
private Runnable mOnDeferredActivityLaunchCallback;
@@ -419,9 +415,6 @@
crossFadeWithPreviousAppearance();
mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
- mAppTransitionManager = LauncherAppTransitionManager.newInstance(this);
- mAppTransitionManager.registerRemoteAnimations();
-
boolean internalStateHandled = ACTIVITY_TRACKER.handleCreate(this);
if (internalStateHandled) {
if (savedInstanceState != null) {
@@ -1090,15 +1083,6 @@
TraceHelper.FLAG_UI_EVENT);
super.onResume();
- if (!mOnResumeCallbacks.isEmpty()) {
- final ArrayList<OnResumeCallback> resumeCallbacks = new ArrayList<>(mOnResumeCallbacks);
- mOnResumeCallbacks.clear();
- for (int i = resumeCallbacks.size() - 1; i >= 0; i--) {
- resumeCallbacks.get(i).onLauncherResume();
- }
- resumeCallbacks.clear();
- }
-
if (mDeferOverlayCallbacks) {
scheduleDeferredCheck();
} else {
@@ -1609,7 +1593,6 @@
LauncherAppState.getIDP(this).removeOnChangeListener(this);
mOverlayManager.onActivityDestroyed(this);
- mAppTransitionManager.onActivityDestroyed();
mUserChangedCallbackCloseable.close();
mLiveSearchManager.stop();
}
@@ -1935,16 +1918,6 @@
@TargetApi(Build.VERSION_CODES.M)
@Override
- public ActivityOptions getActivityLaunchOptions(View v) {
- return mAppTransitionManager.getActivityLaunchOptions(this, v);
- }
-
- public LauncherAppTransitionManager getAppTransitionManager() {
- return mAppTransitionManager;
- }
-
- @TargetApi(Build.VERSION_CODES.M)
- @Override
protected boolean onErrorStartingShortcut(Intent intent, ItemInfo info) {
// Due to legacy reasons, direct call shortcuts require Launchers to have the
// corresponding permission. Show the appropriate permission prompt if that
@@ -1993,7 +1966,7 @@
// state when we return to launcher.
BubbleTextView btv = (BubbleTextView) v;
btv.setStayPressed(true);
- addOnResumeCallback(btv);
+ addOnResumeCallback(() -> btv.setStayPressed(false));
}
return success;
}
@@ -2037,10 +2010,6 @@
return result;
}
- public void addOnResumeCallback(OnResumeCallback callback) {
- mOnResumeCallbacks.add(callback);
- }
-
/**
* Persistant callback which notifies when an activity launch is deferred because the activity
* was not yet resumed.
@@ -2814,15 +2783,6 @@
return (T) activityContext;
}
-
- /**
- * Callback for listening for onResume
- */
- public interface OnResumeCallback {
-
- void onLauncherResume();
- }
-
/**
* Cross-fades the launcher's updated appearance with its previous appearance.
*
@@ -2865,6 +2825,10 @@
return false;
}
+ public boolean supportsAdaptiveIconAnimation(View clickedView) {
+ return false;
+ }
+
public DragOptions getDefaultWorkspaceDragOptions() {
return new DragOptions();
}
diff --git a/src/com/android/launcher3/LauncherAppTransitionManager.java b/src/com/android/launcher3/LauncherAppTransitionManager.java
deleted file mode 100644
index d0bf577..0000000
--- a/src/com/android/launcher3/LauncherAppTransitionManager.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2018 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;
-
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.view.View;
-
-import com.android.launcher3.util.ResourceBasedOverride;
-
-/**
- * Manages the opening and closing app transitions from Launcher.
- */
-public class LauncherAppTransitionManager implements ResourceBasedOverride {
-
- public static LauncherAppTransitionManager newInstance(Context context) {
- return Overrides.getObject(LauncherAppTransitionManager.class,
- context, R.string.app_transition_manager_class);
- }
-
- public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) {
- int left = 0, top = 0;
- int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
- if (v instanceof BubbleTextView) {
- // Launch from center of icon, not entire view
- Drawable icon = ((BubbleTextView) v).getIcon();
- if (icon != null) {
- Rect bounds = icon.getBounds();
- left = (width - bounds.width()) / 2;
- top = v.getPaddingTop();
- width = bounds.width();
- height = bounds.height();
- }
- }
- return ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
- }
-
- public boolean supportsAdaptiveIconAnimation(View clickedView) {
- return false;
- }
-
- /**
- * Registers remote animations for certain system transitions.
- */
- public void registerRemoteAnimations() {
- // Do nothing
- }
-
- /**
- * Handles clean up when activity is destroyed.
- */
- public void onActivityDestroyed() {
- // Do nothing
- }
-
- /**
- * Unregisters all remote animations.
- */
- public void unregisterRemoteAnimations() {
- // Do nothing
- }
-
- /**
- * Registers remote transitions for certain system transitions.
- */
- public void registerRemoteTransitions() {
- // Do nothing
- }
-
- /**
- * Unregisters all remote transitions.
- */
- public void unregisterRemoteTransitions() {
- // Do nothing
- }
-}
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 4fd87cb..8bc5ad0 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -33,7 +33,6 @@
import android.view.View;
import android.widget.Toast;
-import com.android.launcher3.Launcher.OnResumeCallback;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.logging.FileLog;
@@ -228,7 +227,7 @@
DeferredOnComplete deferred = (DeferredOnComplete) d.dragSource;
if (target != null) {
deferred.mPackageName = target.getPackageName();
- mLauncher.addOnResumeCallback(deferred);
+ mLauncher.addOnResumeCallback(deferred::onLauncherResume);
} else {
deferred.sendFailure();
}
@@ -311,7 +310,7 @@
* A wrapper around {@link DragSource} which delays the {@link #onDropCompleted} action until
* {@link #onLauncherResume}
*/
- private class DeferredOnComplete implements DragSource, OnResumeCallback {
+ private class DeferredOnComplete implements DragSource {
private final DragSource mOriginal;
private final Context mContext;
@@ -330,7 +329,6 @@
mDragObject = d;
}
- @Override
public void onLauncherResume() {
// We use MATCH_UNINSTALLED_PACKAGES as the app can be on SD card as well.
if (new PackageManagerHelper(mContext).getApplicationInfo(mPackageName,
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index 0e8d4ae..f5e74b7 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -17,7 +17,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.app.ActivityOptions;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
@@ -168,11 +167,6 @@
}
@Override
- public ActivityOptions getActivityLaunchOptions(View v) {
- return null;
- }
-
- @Override
protected void reapplyUi() { }
@Override
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 61bd30a..2e54904 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -180,7 +180,7 @@
LauncherApps launcherApps = launcher.getSystemService(LauncherApps.class);
try {
launcherApps.startPackageInstallerSessionDetailsActivity(sessionInfo, null,
- launcher.getActivityLaunchOptionsAsBundle(v));
+ launcher.getActivityLaunchOptions(v).toBundle());
return;
} catch (Exception e) {
Log.e(TAG, "Unable to launch market intent for package=" + packageName, e);
@@ -304,7 +304,7 @@
intent.setPackage(null);
}
}
- if (v != null && launcher.getAppTransitionManager().supportsAdaptiveIconAnimation(v)) {
+ if (v != null && launcher.supportsAdaptiveIconAnimation(v)) {
// Preload the icon to reduce latency b/w swapping the floating view with the original.
FloatingIconView.fetchIcon(launcher, v, item, true /* isOpening */);
}
diff --git a/src/com/android/launcher3/util/ActivityOptionsWrapper.java b/src/com/android/launcher3/util/ActivityOptionsWrapper.java
new file mode 100644
index 0000000..99cc1f7
--- /dev/null
+++ b/src/com/android/launcher3/util/ActivityOptionsWrapper.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 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.app.ActivityOptions;
+import android.os.Bundle;
+
+/**
+ * A wrapper around {@link ActivityOptions} to allow custom functionality in launcher
+ */
+public class ActivityOptionsWrapper {
+
+ public final ActivityOptions options;
+ public final RunnableList onEndCallback;
+
+ public ActivityOptionsWrapper(ActivityOptions options, RunnableList onEndCallback) {
+ this.options = options;
+ this.onEndCallback = onEndCallback;
+ }
+
+ /**
+ * @see {@link ActivityOptions#toBundle()}
+ */
+ public Bundle toBundle() {
+ return options.toBundle();
+ }
+}
diff --git a/src/com/android/launcher3/util/RunnableList.java b/src/com/android/launcher3/util/RunnableList.java
new file mode 100644
index 0000000..55add14
--- /dev/null
+++ b/src/com/android/launcher3/util/RunnableList.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 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 java.util.ArrayList;
+
+/**
+ * Utility class to hold a list of runnable
+ */
+public class RunnableList {
+
+ private ArrayList<Runnable> mList = null;
+ private boolean mDestroyed = false;
+
+ /**
+ * Ads a runnable to this list
+ */
+ public void add(Runnable runnable) {
+ if (mDestroyed) {
+ runnable.run();
+ return;
+ }
+ if (mList == null) {
+ mList = new ArrayList<>();
+ }
+ mList.add(runnable);
+ }
+
+ /**
+ * Destroys the list, executing any pending callbacks. All new callbacks are
+ * immediately executed
+ */
+ public void executeAllAndDestroy() {
+ mDestroyed = true;
+ executeAllAndClear();
+ }
+
+ /**
+ * Executes all previously added runnable and clears the list
+ */
+ public void executeAllAndClear() {
+ if (mList != null) {
+ ArrayList<Runnable> list = mList;
+ mList = null;
+ int count = list.size();
+ for (int i = 0; i < count; i++) {
+ list.get(i).run();
+ }
+ }
+ }
+}