diff --git a/build.gradle b/build.gradle index 38d106590a1..45ca5ff0db7 100644 --- a/build.gradle +++ b/build.gradle @@ -92,6 +92,9 @@ allprojects { libraries.from(files(frameworkJar)) } } + dependencies { + compileOnly files(frameworkJar) + } } compileOnlyCommonJars = { @@ -99,7 +102,13 @@ allprojects { compileOnly fileTree(dir: FRAMEWORK_PREBUILTS_DIR, include: 'SystemUI-core.jar') compileOnly fileTree(dir: FRAMEWORK_PREBUILTS_DIR, include: 'SystemUI-statsd.jar') compileOnly fileTree(dir: FRAMEWORK_PREBUILTS_DIR, include: 'WindowManager-Shell-14.jar') - compileOnly fileTree(dir: FRAMEWORK_PREBUILTS_DIR, include: 'framework-14.jar') + + compileOnly projects.compatLib + compileOnly projects.compatLib.compatLibVQ + compileOnly projects.compatLib.compatLibVR + compileOnly projects.compatLib.compatLibVS + compileOnly projects.compatLib.compatLibVT + compileOnly projects.compatLib.compatLibVU } } } @@ -115,7 +124,7 @@ final def parts = versionString.split("\\.") final def majorVersion = parts[0] final def versionInt = parts[0].toInteger() * 1000000 + parts[1].toInteger() * 10000 + parts[2].toInteger() * 100 -final def quickstepMinSdk = "34" +final def quickstepMinSdk = "29" final def quickstepMaxSdk = "34" android { @@ -304,6 +313,14 @@ dependencies { withQuickstepImplementation projects.systemUIPluginCore withQuickstepCompileOnly projects.systemUICommon + // QuickSwitch Compat + withQuickstepImplementation projects.compatLib + withQuickstepImplementation projects.compatLib.compatLibVQ + withQuickstepImplementation projects.compatLib.compatLibVR + withQuickstepImplementation projects.compatLib.compatLibVS + withQuickstepImplementation projects.compatLib.compatLibVT + withQuickstepImplementation projects.compatLib.compatLibVU + implementation fileTree(dir: FRAMEWORK_PREBUILTS_DIR, include: 'SystemUI-statsd-14.jar') implementation fileTree(dir: FRAMEWORK_PREBUILTS_DIR, include: 'WindowManager-Shell-14.jar') @@ -382,6 +399,10 @@ ksp { } spotless { + java { + target("compatLib/**/src/**/*.java") + googleJavaFormat().aosp() + } kotlin { target("lawnchair/src/**/*.kt") ktlint().customRuleSets([ diff --git a/compatLib/README.md b/compatLib/README.md new file mode 100644 index 00000000000..d282b8c0880 --- /dev/null +++ b/compatLib/README.md @@ -0,0 +1 @@ +# Lawnchair Quickstep Compat Lib \ No newline at end of file diff --git a/compatLib/build.gradle b/compatLib/build.gradle index b13173a4540..e49ca5b2887 100644 --- a/compatLib/build.gradle +++ b/compatLib/build.gradle @@ -4,6 +4,16 @@ plugins { android { namespace "app.lawnchair.compatlib" + + buildFeatures { + aidl true + } + + sourceSets { + main { + aidl.srcDirs = ['src/main/java'] + } + } } -addFrameworkJar('framework-12.jar') +addFrameworkJar('framework-14.jar') diff --git a/compatLib/compatLibVQ/build.gradle b/compatLib/compatLibVQ/build.gradle new file mode 100644 index 00000000000..e4f82051d62 --- /dev/null +++ b/compatLib/compatLibVQ/build.gradle @@ -0,0 +1,14 @@ +plugins { + id 'com.android.library' +} + +android { + namespace "app.lawnchair.compatlib.ten" +} + +addFrameworkJar('framework-10.jar') + +dependencies { + implementation projects.compatLib + implementation projects.compatLib.compatLibVR +} diff --git a/compatLib/compatLibVQ/src/main/java/app/lawnchair/compatlib/ten/ActivityManagerCompatVQ.java b/compatLib/compatLibVQ/src/main/java/app/lawnchair/compatlib/ten/ActivityManagerCompatVQ.java new file mode 100644 index 00000000000..c5bbfa3db09 --- /dev/null +++ b/compatLib/compatLibVQ/src/main/java/app/lawnchair/compatlib/ten/ActivityManagerCompatVQ.java @@ -0,0 +1,123 @@ +package app.lawnchair.compatlib.ten; + +import android.app.Activity; +import android.app.ActivityManager; +import android.app.ActivityTaskManager; +import android.app.WindowConfiguration; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.Rect; +import android.os.RemoteException; +import android.view.IRecentsAnimationController; +import android.view.IRecentsAnimationRunner; +import android.view.RemoteAnimationTarget; +import app.lawnchair.compatlib.RecentsAnimationRunnerCompat; +import app.lawnchair.compatlib.eleven.ActivityManagerCompatVR; +import java.util.List; + +public class ActivityManagerCompatVQ extends ActivityManagerCompatVR { + + @Override + public void invalidateHomeTaskSnapshot(Activity homeActivity) { + // do nothing ,android Q not support + } + + @Override + public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisibleRecents) { + + int ignoreActivityType = WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; + if (filterOnlyVisibleRecents) { + ignoreActivityType = WindowConfiguration.ACTIVITY_TYPE_RECENTS; + } + + try { + + List tasks = + ActivityTaskManager.getService() + .getFilteredTasks( + NUM_RECENT_ACTIVITIES_REQUEST, + ignoreActivityType, + WindowConfiguration.WINDOWING_MODE_PINNED); + if (tasks.isEmpty()) { + return null; + } + return tasks.toArray(new ActivityManager.RunningTaskInfo[tasks.size()]); + } catch (RemoteException e) { + return null; + } + } + + @Override + public void startRecentsActivity( + Intent intent, long eventTime, RecentsAnimationRunnerCompat runnerCompat) { + + IRecentsAnimationRunner runner = null; + if (runnerCompat != null) { + runner = + new IRecentsAnimationRunner.Stub() { + @Override + public void onAnimationStart( + IRecentsAnimationController controller, + RemoteAnimationTarget[] apps, + Rect homeContentInsets, + Rect minimizedHomeBounds) { + runnerCompat.onAnimationStart( + controller, apps, null, homeContentInsets, minimizedHomeBounds); + } + + public void reportAllDrawn() {} + + @Override + public void onAnimationCanceled(boolean deferredWithScreenshot) { + runnerCompat.onAnimationCanceled(deferredWithScreenshot); + } + }; + } + try { + ActivityTaskManager.getService().startRecentsActivity(intent, null, runner); + } catch (RemoteException e) { + + } + } + + @Override + public ActivityManager.RunningTaskInfo getRunningTask(boolean filterOnlyVisibleRecents) { + + int ignoreActivityType = WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; + if (filterOnlyVisibleRecents) { + ignoreActivityType = WindowConfiguration.ACTIVITY_TYPE_RECENTS; + } + try { + List tasks = + ActivityTaskManager.getService() + .getFilteredTasks( + 1, + ignoreActivityType, + WindowConfiguration.WINDOWING_MODE_PINNED); + if (tasks.isEmpty()) { + return null; + } + return tasks.get(0); + } catch (RemoteException e) { + return null; + } + } + + @Override + public ThumbnailData makeThumbnailData(ActivityManager.TaskSnapshot snapshot) { + ThumbnailData data = new ThumbnailData(); + data.thumbnail = + Bitmap.wrapHardwareBuffer(snapshot.getSnapshot(), snapshot.getColorSpace()); + data.insets = new Rect(snapshot.getContentInsets()); + data.orientation = snapshot.getOrientation(); + data.reducedResolution = snapshot.isReducedResolution(); + // TODO(b/149579527): Pass task size instead of computing scale. + // Assume width and height were scaled the same; compute scale only for width + data.scale = snapshot.getScale(); + data.isRealSnapshot = snapshot.isRealSnapshot(); + data.isTranslucent = snapshot.isTranslucent(); + data.windowingMode = snapshot.getWindowingMode(); + data.systemUiVisibility = snapshot.getSystemUiVisibility(); + return data; + } +} diff --git a/compatLib/compatLibVQ/src/main/java/app/lawnchair/compatlib/ten/ActivityOptionsCompatVQ.java b/compatLib/compatLibVQ/src/main/java/app/lawnchair/compatlib/ten/ActivityOptionsCompatVQ.java new file mode 100644 index 00000000000..d542ae75de6 --- /dev/null +++ b/compatLib/compatLibVQ/src/main/java/app/lawnchair/compatlib/ten/ActivityOptionsCompatVQ.java @@ -0,0 +1,39 @@ +package app.lawnchair.compatlib.ten; + +import android.app.ActivityOptions; +import android.content.Context; +import android.os.Handler; +import android.view.RemoteAnimationAdapter; +import app.lawnchair.compatlib.ActivityOptionsCompat; + +public class ActivityOptionsCompatVQ extends ActivityOptionsCompat { + @Override + public ActivityOptions makeCustomAnimation( + Context context, + int enterResId, + int exitResId, + Runnable callback, + Handler callbackHandler) { + return ActivityOptions.makeCustomAnimation( + context, + enterResId, + exitResId, + callbackHandler, + new ActivityOptions.OnAnimationStartedListener() { + @Override + public void onAnimationStarted() { + if (callback != null) { + callbackHandler.post(callback); + } + } + }); + } + + @Override + public ActivityOptions makeRemoteAnimation( + RemoteAnimationAdapter remoteAnimationAdapter, + Object remoteTransition, + String debugName) { + return ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter); + } +} diff --git a/compatLib/compatLibVQ/src/main/java/app/lawnchair/compatlib/ten/QuickstepCompatFactoryVQ.java b/compatLib/compatLibVQ/src/main/java/app/lawnchair/compatlib/ten/QuickstepCompatFactoryVQ.java new file mode 100644 index 00000000000..ccb95c803cb --- /dev/null +++ b/compatLib/compatLibVQ/src/main/java/app/lawnchair/compatlib/ten/QuickstepCompatFactoryVQ.java @@ -0,0 +1,21 @@ +package app.lawnchair.compatlib.ten; + +import androidx.annotation.NonNull; +import app.lawnchair.compatlib.ActivityManagerCompat; +import app.lawnchair.compatlib.ActivityOptionsCompat; +import app.lawnchair.compatlib.eleven.QuickstepCompatFactoryVR; + +public class QuickstepCompatFactoryVQ extends QuickstepCompatFactoryVR { + + @NonNull + @Override + public ActivityManagerCompat getActivityManagerCompat() { + return new ActivityManagerCompatVQ(); + } + + @NonNull + @Override + public ActivityOptionsCompat getActivityOptionsCompat() { + return new ActivityOptionsCompatVQ(); + } +} diff --git a/compatLibVR/build.gradle b/compatLib/compatLibVR/build.gradle similarity index 100% rename from compatLibVR/build.gradle rename to compatLib/compatLibVR/build.gradle diff --git a/compatLibVR/src/main/java/app/lawnchair/compatlib/eleven/ActivityManagerCompatVR.java b/compatLib/compatLibVR/src/main/java/app/lawnchair/compatlib/eleven/ActivityManagerCompatVR.java similarity index 60% rename from compatLibVR/src/main/java/app/lawnchair/compatlib/eleven/ActivityManagerCompatVR.java rename to compatLib/compatLibVR/src/main/java/app/lawnchair/compatlib/eleven/ActivityManagerCompatVR.java index 48ca61758c6..11cc5c133b1 100644 --- a/compatLibVR/src/main/java/app/lawnchair/compatlib/eleven/ActivityManagerCompatVR.java +++ b/compatLib/compatLibVR/src/main/java/app/lawnchair/compatlib/eleven/ActivityManagerCompatVR.java @@ -4,6 +4,7 @@ import static android.app.ActivityTaskManager.getService; import static android.graphics.Bitmap.Config.ARGB_8888; +import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.content.Intent; @@ -18,41 +19,63 @@ import android.view.IRecentsAnimationController; import android.view.IRecentsAnimationRunner; import android.view.RemoteAnimationTarget; - +import app.lawnchair.compatlib.ActivityManagerCompat; +import app.lawnchair.compatlib.RecentsAnimationRunnerCompat; import java.util.ArrayList; import java.util.List; -import app.lawnchair.compatlib.ActivityManagerCompat; -import app.lawnchair.compatlib.RecentsAnimationRunnerStub; - public class ActivityManagerCompatVR extends ActivityManagerCompat { private static final String TAG = "ActivityManagerCompatVR"; @Override - public void startRecentsActivity(Intent intent, long eventTime, RecentsAnimationRunnerStub runner) throws RemoteException { - IRecentsAnimationRunner wrappedRunner = null; - if (runner != null) { - wrappedRunner = new IRecentsAnimationRunner.Stub() { - @Override - public void onAnimationStart(IRecentsAnimationController controller, - RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, - Rect homeContentInsets, Rect minimizedHomeBounds) { - runner.onAnimationStart(controller, apps, wallpapers, homeContentInsets, minimizedHomeBounds); - } - - @Override - public void onAnimationCanceled(ActivityManager.TaskSnapshot taskSnapshot) { - runner.onAnimationCanceled(taskSnapshot); - } - - @Override - public void onTaskAppeared(RemoteAnimationTarget app) { - runner.onTaskAppeared(app); - } - }; + public void invalidateHomeTaskSnapshot(Activity homeActivity) { + try { + ActivityTaskManager.getService() + .invalidateHomeTaskSnapshot(homeActivity.getActivityToken()); + } catch (RemoteException e) { + Log.w(TAG, "Failed to invalidate home snapshot", e); + } + } + + @Override + public void startRecentsActivity( + Intent intent, long eventTime, RecentsAnimationRunnerCompat runnerCompat) { + IRecentsAnimationRunner runner = null; + if (runnerCompat != null) { + runner = + new IRecentsAnimationRunner.Stub() { + @Override + public void onAnimationStart( + IRecentsAnimationController controller, + RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, + Rect homeContentInsets, + Rect minimizedHomeBounds) { + runnerCompat.onAnimationStart( + controller, + apps, + wallpapers, + homeContentInsets, + minimizedHomeBounds); + } + + @Override + public void onAnimationCanceled(ActivityManager.TaskSnapshot taskSnapshot) { + runnerCompat.onAnimationCanceled(taskSnapshot); + } + + @Override + public void onTaskAppeared(RemoteAnimationTarget app) { + runnerCompat.onTaskAppeared(app); + } + }; + } + try { + getService().startRecentsActivity(intent, null, runner); + } catch (RemoteException e) { + Log.e(TAG, "Failed to cancel recents animation", e); } - getService().startRecentsActivity(intent, null, wrappedRunner); } @Override @@ -73,14 +96,31 @@ public ActivityManager.RunningTaskInfo getRunningTask(boolean filterOnlyVisibleR @Override public List getRecentTasks(int numTasks, int userId) { try { - return ActivityTaskManager.getService().getRecentTasks(numTasks, - RECENT_IGNORE_UNAVAILABLE, userId).getList(); + return ActivityTaskManager.getService() + .getRecentTasks(numTasks, RECENT_IGNORE_UNAVAILABLE, userId) + .getList(); } catch (RemoteException e) { Log.e(TAG, "Failed to get recent tasks", e); return new ArrayList<>(); } } + @Override + public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisibleRecents) { + try { + List tasks = + ActivityTaskManager.getService() + .getFilteredTasks( + NUM_RECENT_ACTIVITIES_REQUEST, filterOnlyVisibleRecents); + if (tasks.isEmpty()) { + return null; + } + return tasks.toArray(new ActivityManager.RunningTaskInfo[tasks.size()]); + } catch (RemoteException e) { + return null; + } + } + public ThumbnailData getTaskThumbnail(int taskId, boolean isLowResolution) { ActivityManager.TaskSnapshot snapshot = null; try { @@ -95,7 +135,8 @@ public ThumbnailData getTaskThumbnail(int taskId, boolean isLowResolution) { } } - public ThumbnailData takeScreenshot(IRecentsAnimationController animationController, int taskId) { + public ThumbnailData takeScreenshot( + IRecentsAnimationController animationController, int taskId) { try { ActivityManager.TaskSnapshot snapshot = animationController.screenshotTask(taskId); return snapshot != null ? makeThumbnailData(snapshot) : new ThumbnailData(); @@ -110,8 +151,9 @@ public ThumbnailData makeThumbnailData(ActivityManager.TaskSnapshot snapshot) { final GraphicBuffer buffer = snapshot.getSnapshot(); if (buffer == null || (buffer.getUsage() & HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE) == 0) { // TODO(b/157562905): Workaround for a crash when we get a snapshot without this state - Log.e("ThumbnailData", "Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE: " - + buffer); + Log.e( + "ThumbnailData", + "Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE: " + buffer); Point taskSize = snapshot.getTaskSize(); data.thumbnail = Bitmap.createBitmap(taskSize.x, taskSize.y, ARGB_8888); data.thumbnail.eraseColor(Color.BLACK); diff --git a/compatLib/compatLibVR/src/main/java/app/lawnchair/compatlib/eleven/ActivityOptionsCompatVR.java b/compatLib/compatLibVR/src/main/java/app/lawnchair/compatlib/eleven/ActivityOptionsCompatVR.java new file mode 100644 index 00000000000..f9fecd37c49 --- /dev/null +++ b/compatLib/compatLibVR/src/main/java/app/lawnchair/compatlib/eleven/ActivityOptionsCompatVR.java @@ -0,0 +1,44 @@ +package app.lawnchair.compatlib.eleven; + +import android.app.ActivityOptions; +import android.content.Context; +import android.os.Handler; +import android.util.Log; +import android.view.RemoteAnimationAdapter; +import app.lawnchair.compatlib.ActivityOptionsCompat; + +public class ActivityOptionsCompatVR extends ActivityOptionsCompat { + private static final String TAG = "ActivityOptionsCompatVR"; + + @Override + public ActivityOptions makeCustomAnimation( + Context context, + int enterResId, + int exitResId, + Runnable callback, + Handler callbackHandler) { + return ActivityOptions.makeCustomAnimation( + context, + enterResId, + exitResId, + callbackHandler, + new ActivityOptions.OnAnimationStartedListener() { + @Override + public void onAnimationStarted() { + if (callback != null) { + callbackHandler.post(callback); + } + } + }, + null /* finishedListener */); + } + + @Override + public ActivityOptions makeRemoteAnimation( + RemoteAnimationAdapter remoteAnimationAdapter, + Object remoteTransition, + String debugName) { + Log.e(TAG, "makeRemoteAnimation: " + debugName); + return ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter); + } +} diff --git a/compatLib/compatLibVR/src/main/java/app/lawnchair/compatlib/eleven/QuickstepCompatFactoryVR.java b/compatLib/compatLibVR/src/main/java/app/lawnchair/compatlib/eleven/QuickstepCompatFactoryVR.java new file mode 100644 index 00000000000..fb4919d1d96 --- /dev/null +++ b/compatLib/compatLibVR/src/main/java/app/lawnchair/compatlib/eleven/QuickstepCompatFactoryVR.java @@ -0,0 +1,40 @@ +package app.lawnchair.compatlib.eleven; + +import android.app.IApplicationThread; +import android.window.IRemoteTransition; +import android.window.RemoteTransition; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import app.lawnchair.compatlib.ActivityManagerCompat; +import app.lawnchair.compatlib.ActivityOptionsCompat; +import app.lawnchair.compatlib.QuickstepCompatFactory; +import app.lawnchair.compatlib.RemoteTransitionCompat; + +public class QuickstepCompatFactoryVR extends QuickstepCompatFactory { + + @NonNull + @Override + public ActivityManagerCompat getActivityManagerCompat() { + return new ActivityManagerCompatVR(); + } + + @NonNull + @Override + public ActivityOptionsCompat getActivityOptionsCompat() { + return new ActivityOptionsCompatVR(); + } + + @NonNull + @Override + public RemoteTransitionCompat getRemoteTransitionCompat() { + return new RemoteTransitionCompat() { + @Override + public RemoteTransition getRemoteTransition( + @NonNull IRemoteTransition remoteTransition, + @Nullable IApplicationThread appThread, + @Nullable String debugName) { + return new RemoteTransition(remoteTransition, appThread, debugName); + } + }; + } +} diff --git a/compatLib/compatLibVR/src/main/java/app/lawnchair/compatlib/eleven/WindowManagerCompatVR.java b/compatLib/compatLibVR/src/main/java/app/lawnchair/compatlib/eleven/WindowManagerCompatVR.java new file mode 100644 index 00000000000..beef46c2d50 --- /dev/null +++ b/compatLib/compatLibVR/src/main/java/app/lawnchair/compatlib/eleven/WindowManagerCompatVR.java @@ -0,0 +1,33 @@ +package app.lawnchair.compatlib.eleven; + +import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT; + +import android.app.WindowConfiguration; +import android.os.RemoteException; +import android.util.Log; +import android.view.WindowManagerGlobal; + +public class WindowManagerCompatVR { + public static final int NAV_BAR_POS_INVALID = NAV_BAR_INVALID; + public static final int NAV_BAR_POS_LEFT = NAV_BAR_LEFT; + public static final int NAV_BAR_POS_RIGHT = NAV_BAR_RIGHT; + public static final int NAV_BAR_POS_BOTTOM = NAV_BAR_BOTTOM; + + public static final int WINDOWING_MODE_SPLIT_SCREEN_PRIMARY = + WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; + + public static final int WINDOWING_MODE_SPLIT_SCREEN_SECONDARY = + WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; + + public static int getNavBarPosition(int displayId) { + try { + return WindowManagerGlobal.getWindowManagerService().getNavBarPosition(displayId); + } catch (RemoteException e) { + Log.w("WindowManagerCompatVR", "Failed to get nav bar position"); + } + return NAV_BAR_POS_INVALID; + } +} diff --git a/compatLibVS/build.gradle b/compatLib/compatLibVS/build.gradle similarity index 100% rename from compatLibVS/build.gradle rename to compatLib/compatLibVS/build.gradle diff --git a/compatLib/compatLibVS/src/main/java/app/lawnchair/compatlib/twelve/ActivityManagerCompatVS.java b/compatLib/compatLibVS/src/main/java/app/lawnchair/compatlib/twelve/ActivityManagerCompatVS.java new file mode 100644 index 00000000000..0499c372936 --- /dev/null +++ b/compatLib/compatLibVS/src/main/java/app/lawnchair/compatlib/twelve/ActivityManagerCompatVS.java @@ -0,0 +1,112 @@ +package app.lawnchair.compatlib.twelve; + +import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; +import static android.app.ActivityTaskManager.getService; + +import android.app.Activity; +import android.app.ActivityClient; +import android.app.ActivityManager; +import android.app.ActivityTaskManager; +import android.content.Intent; +import android.graphics.Rect; +import android.os.RemoteException; +import android.util.Log; +import android.view.IRecentsAnimationController; +import android.view.IRecentsAnimationRunner; +import android.view.RemoteAnimationTarget; +import android.window.TaskSnapshot; +import androidx.annotation.Nullable; +import app.lawnchair.compatlib.ActivityManagerCompat; +import app.lawnchair.compatlib.RecentsAnimationRunnerCompat; +import java.util.List; + +public class ActivityManagerCompatVS extends ActivityManagerCompat { + + private static final String TAG = "ActivityManagerCompatVS"; + private final ActivityTaskManager mAtm = ActivityTaskManager.getInstance(); + + @Override + public void invalidateHomeTaskSnapshot(Activity homeActivity) { + try { + ActivityClient.getInstance() + .invalidateHomeTaskSnapshot( + homeActivity == null ? null : homeActivity.getActivityToken()); + } catch (Throwable e) { + Log.w(TAG, "Failed to invalidate home snapshot", e); + } + } + + @Nullable + @Override + public TaskSnapshot getTaskSnapshot( + int taskId, boolean isLowResolution, boolean takeSnapshotIfNeeded) { + try { + return getService().getTaskSnapshot(taskId, isLowResolution); + } catch (RemoteException e) { + Log.w(TAG, "Failed to getTaskSnapshot", e); + return null; + } + } + + @Override + public void startRecentsActivity( + Intent intent, long eventTime, RecentsAnimationRunnerCompat runnerCompat) { + IRecentsAnimationRunner runner = null; + if (runnerCompat != null) { + runner = + new IRecentsAnimationRunner.Stub() { + @Override + public void onAnimationStart( + IRecentsAnimationController controller, + RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, + Rect homeContentInsets, + Rect minimizedHomeBounds) { + runnerCompat.onAnimationStart( + controller, + apps, + wallpapers, + homeContentInsets, + minimizedHomeBounds); + } + + @Override + public void onAnimationCanceled(TaskSnapshot taskSnapshot) { + runnerCompat.onAnimationCanceled(taskSnapshot); + } + + @Override + public void onTaskAppeared(RemoteAnimationTarget app) { + runnerCompat.onTaskAppeared(app); + } + }; + } + try { + getService().startRecentsActivity(intent, eventTime, runner); + } catch (RemoteException e) { + Log.e(TAG, "Failed to cancel recents animation", e); + } + } + + @Override + public ActivityManager.RunningTaskInfo getRunningTask(boolean filterOnlyVisibleRecents) { + // Note: The set of running tasks from the system is ordered by recency + List tasks = mAtm.getTasks(1, filterOnlyVisibleRecents); + if (tasks.isEmpty()) { + return null; + } + return tasks.get(0); + } + + @Override + public List getRecentTasks(int numTasks, int userId) { + return mAtm.getRecentTasks(numTasks, RECENT_IGNORE_UNAVAILABLE, userId); + } + + @Override + public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisibleRecents) { + List tasks = + mAtm.getTasks(NUM_RECENT_ACTIVITIES_REQUEST, filterOnlyVisibleRecents); + return tasks.toArray(new ActivityManager.RunningTaskInfo[tasks.size()]); + } +} diff --git a/compatLib/compatLibVS/src/main/java/app/lawnchair/compatlib/twelve/ActivityOptionsCompatVS.java b/compatLib/compatLibVS/src/main/java/app/lawnchair/compatlib/twelve/ActivityOptionsCompatVS.java new file mode 100644 index 00000000000..13aa1b75738 --- /dev/null +++ b/compatLib/compatLibVS/src/main/java/app/lawnchair/compatlib/twelve/ActivityOptionsCompatVS.java @@ -0,0 +1,44 @@ +package app.lawnchair.compatlib.twelve; + +import android.app.ActivityOptions; +import android.content.Context; +import android.os.Handler; +import android.util.Log; +import android.view.RemoteAnimationAdapter; +import app.lawnchair.compatlib.ActivityOptionsCompat; + +public class ActivityOptionsCompatVS extends ActivityOptionsCompat { + private static final String TAG = "ActivityOptionsCompatVS"; + + @Override + public ActivityOptions makeCustomAnimation( + Context context, + int enterResId, + int exitResId, + Runnable callback, + Handler callbackHandler) { + return ActivityOptions.makeCustomTaskAnimation( + context, + enterResId, + exitResId, + callbackHandler, + new ActivityOptions.OnAnimationStartedListener() { + @Override + public void onAnimationStarted() { + if (callback != null) { + callbackHandler.post(callback); + } + } + }, + null /* finishedListener */); + } + + @Override + public ActivityOptions makeRemoteAnimation( + RemoteAnimationAdapter remoteAnimationAdapter, + Object remoteTransition, + String debugName) { + Log.e(TAG, "makeRemoteAnimation: " + debugName); + return ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter); + } +} diff --git a/compatLib/compatLibVS/src/main/java/app/lawnchair/compatlib/twelve/QuickstepCompatFactoryVS.java b/compatLib/compatLibVS/src/main/java/app/lawnchair/compatlib/twelve/QuickstepCompatFactoryVS.java new file mode 100644 index 00000000000..35e6ce53bdf --- /dev/null +++ b/compatLib/compatLibVS/src/main/java/app/lawnchair/compatlib/twelve/QuickstepCompatFactoryVS.java @@ -0,0 +1,60 @@ +package app.lawnchair.compatlib.twelve; + +import android.app.IApplicationThread; +import android.window.IRemoteTransition; +import android.window.RemoteTransition; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import app.lawnchair.compatlib.ActivityManagerCompat; +import app.lawnchair.compatlib.ActivityOptionsCompat; +import app.lawnchair.compatlib.QuickstepCompatFactory; +import app.lawnchair.compatlib.RemoteTransitionCompat; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +public class QuickstepCompatFactoryVS extends QuickstepCompatFactory { + + @NonNull + @Override + public ActivityManagerCompat getActivityManagerCompat() { + return new ActivityManagerCompatVS(); + } + + @NonNull + @Override + public ActivityOptionsCompat getActivityOptionsCompat() { + return new ActivityOptionsCompatVS(); + } + + @NonNull + @Override + public RemoteTransitionCompat getRemoteTransitionCompat() { + return new RemoteTransitionCompat() { + @Override + public RemoteTransition getRemoteTransition( + @NonNull IRemoteTransition remoteTransition, + @Nullable IApplicationThread appThread, + @Nullable String debugName) { + return createRemoteTransition(remoteTransition, appThread, debugName); + } + }; + } + + // TODO remove this as it causing glitches on first launch opening/closing app + private RemoteTransition createRemoteTransition( + IRemoteTransition remoteTransition, IApplicationThread appThread, String debugName) { + try { + Class remoteTransitionClass = Class.forName("android.window.RemoteTransition"); + Constructor constructor = + remoteTransitionClass.getConstructor( + IRemoteTransition.class, IApplicationThread.class, String.class); + return (RemoteTransition) constructor.newInstance(remoteTransition, appThread); + } catch (ClassNotFoundException + | IllegalAccessException + | InstantiationException + | InvocationTargetException + | NoSuchMethodException e) { + throw new RuntimeException("Error creating RemoteTransitionCompat" + debugName, e); + } + } +} diff --git a/compatLib/compatLibVT/build.gradle b/compatLib/compatLibVT/build.gradle new file mode 100644 index 00000000000..5a6188fc41e --- /dev/null +++ b/compatLib/compatLibVT/build.gradle @@ -0,0 +1,14 @@ +plugins { + id 'com.android.library' +} + +android { + namespace 'app.lawnchair.compatlib.thirteen' +} + +addFrameworkJar('framework-13.jar') + +dependencies { + implementation projects.compatLib + implementation projects.compatLib.compatLibVS +} diff --git a/compatLib/compatLibVT/src/main/java/app/lawnchair/compatlib/thirteen/ActivityManagerCompatVT.java b/compatLib/compatLibVT/src/main/java/app/lawnchair/compatlib/thirteen/ActivityManagerCompatVT.java new file mode 100644 index 00000000000..4a376d2b74b --- /dev/null +++ b/compatLib/compatLibVT/src/main/java/app/lawnchair/compatlib/thirteen/ActivityManagerCompatVT.java @@ -0,0 +1,78 @@ +package app.lawnchair.compatlib.thirteen; + +import static android.app.ActivityTaskManager.getService; + +import android.app.ActivityTaskManager; +import android.content.Intent; +import android.graphics.Rect; +import android.os.RemoteException; +import android.util.Log; +import android.view.IRecentsAnimationController; +import android.view.IRecentsAnimationRunner; +import android.view.RemoteAnimationTarget; +import android.window.TaskSnapshot; +import app.lawnchair.compatlib.RecentsAnimationRunnerCompat; +import app.lawnchair.compatlib.twelve.ActivityManagerCompatVS; + +public class ActivityManagerCompatVT extends ActivityManagerCompatVS { + + private static final String TAG = "ActivityManagerCompatVT"; + private final ActivityTaskManager mAtm = ActivityTaskManager.getInstance(); + + @Override + public void startRecentsActivity( + Intent intent, long eventTime, RecentsAnimationRunnerCompat runnerCompat) { + IRecentsAnimationRunner runner = null; + if (runnerCompat != null) { + runner = + new IRecentsAnimationRunner.Stub() { + @Override + public void onAnimationStart( + IRecentsAnimationController controller, + RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, + Rect homeContentInsets, + Rect minimizedHomeBounds) { + runnerCompat.onAnimationStart( + controller, + apps, + wallpapers, + homeContentInsets, + minimizedHomeBounds); + } + + @Override + public void onAnimationCanceled( + int[] taskIds, TaskSnapshot[] taskSnapshots) { + runnerCompat.onAnimationCanceled(taskIds, taskSnapshots); + } + + @Override + public void onTasksAppeared(RemoteAnimationTarget[] apps) { + runnerCompat.onTasksAppeared(apps); + } + }; + } + try { + getService().startRecentsActivity(intent, eventTime, runner); + } catch (RemoteException e) { + Log.e(TAG, "Failed to cancel recents animation", e); + } + } + + @Override + public TaskSnapshot getTaskSnapshot( + int taskId, boolean isLowResolution, boolean takeSnapshotIfNeeded) { + try { + // android13 qpr1 + return getService() + .getTaskSnapshot(taskId, isLowResolution, true /* takeSnapshotIfNeeded */); + } catch (RemoteException e) { + Log.e(TAG, "Failed to getTaskSnapshot", e); + return null; + } catch (NoSuchMethodError e) { + // android13/12 + return super.getTaskSnapshot(taskId, isLowResolution, takeSnapshotIfNeeded); + } + } +} diff --git a/compatLib/compatLibVT/src/main/java/app/lawnchair/compatlib/thirteen/ActivityOptionsCompatVT.java b/compatLib/compatLibVT/src/main/java/app/lawnchair/compatlib/thirteen/ActivityOptionsCompatVT.java new file mode 100644 index 00000000000..48a9f7682f0 --- /dev/null +++ b/compatLib/compatLibVT/src/main/java/app/lawnchair/compatlib/thirteen/ActivityOptionsCompatVT.java @@ -0,0 +1,34 @@ +package app.lawnchair.compatlib.thirteen; + +import android.app.ActivityOptions; +import android.content.Context; +import android.os.Handler; +import android.util.Log; +import android.view.RemoteAnimationAdapter; +import android.window.RemoteTransition; +import app.lawnchair.compatlib.twelve.ActivityOptionsCompatVS; + +public class ActivityOptionsCompatVT extends ActivityOptionsCompatVS { + + private static final String TAG = "ActivityOptionsCompatVT"; + + @Override + public ActivityOptions makeCustomAnimation( + Context context, + int enterResId, + int exitResId, + Runnable callback, + Handler callbackHandler) { + return super.makeCustomAnimation(context, enterResId, exitResId, callback, callbackHandler); + } + + @Override + public ActivityOptions makeRemoteAnimation( + RemoteAnimationAdapter remoteAnimationAdapter, + Object remoteTransition, + String debugName) { + Log.e(TAG, "makeRemoteAnimation: " + debugName); + return ActivityOptions.makeRemoteAnimation( + remoteAnimationAdapter, (RemoteTransition) remoteTransition); + } +} diff --git a/compatLib/compatLibVT/src/main/java/app/lawnchair/compatlib/thirteen/QuickstepCompatFactoryVT.java b/compatLib/compatLibVT/src/main/java/app/lawnchair/compatlib/thirteen/QuickstepCompatFactoryVT.java new file mode 100644 index 00000000000..b1a2573a3f9 --- /dev/null +++ b/compatLib/compatLibVT/src/main/java/app/lawnchair/compatlib/thirteen/QuickstepCompatFactoryVT.java @@ -0,0 +1,40 @@ +package app.lawnchair.compatlib.thirteen; + +import android.app.IApplicationThread; +import android.window.IRemoteTransition; +import android.window.RemoteTransition; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import app.lawnchair.compatlib.ActivityManagerCompat; +import app.lawnchair.compatlib.ActivityOptionsCompat; +import app.lawnchair.compatlib.RemoteTransitionCompat; +import app.lawnchair.compatlib.twelve.QuickstepCompatFactoryVS; + +public class QuickstepCompatFactoryVT extends QuickstepCompatFactoryVS { + + @NonNull + @Override + public ActivityManagerCompat getActivityManagerCompat() { + return new ActivityManagerCompatVT(); + } + + @NonNull + @Override + public ActivityOptionsCompat getActivityOptionsCompat() { + return new ActivityOptionsCompatVT(); + } + + @NonNull + @Override + public RemoteTransitionCompat getRemoteTransitionCompat() { + return new RemoteTransitionCompat() { + @Override + public RemoteTransition getRemoteTransition( + @NonNull IRemoteTransition remoteTransition, + @Nullable IApplicationThread appThread, + @Nullable String debugName) { + return new RemoteTransition(remoteTransition, appThread); + } + }; + } +} diff --git a/compatLib/compatLibVU/build.gradle b/compatLib/compatLibVU/build.gradle new file mode 100644 index 00000000000..8c7c0384dfc --- /dev/null +++ b/compatLib/compatLibVU/build.gradle @@ -0,0 +1,15 @@ +plugins { + id 'com.android.library' +} + +android { + namespace 'app.lawnchair.compatlib.fourteen' +} + +addFrameworkJar('framework-14.jar') + +dependencies { + implementation projects.compatLib + implementation projects.compatLib.compatLibVS + implementation projects.compatLib.compatLibVT +} diff --git a/compatLib/compatLibVU/src/main/java/app/lawnchair/compatlib/fourteen/ActivityManagerCompatVU.java b/compatLib/compatLibVU/src/main/java/app/lawnchair/compatlib/fourteen/ActivityManagerCompatVU.java new file mode 100644 index 00000000000..d10fcc8281b --- /dev/null +++ b/compatLib/compatLibVU/src/main/java/app/lawnchair/compatlib/fourteen/ActivityManagerCompatVU.java @@ -0,0 +1,74 @@ +package app.lawnchair.compatlib.fourteen; + +import static android.app.ActivityTaskManager.getService; + +import android.content.Intent; +import android.graphics.Rect; +import android.os.RemoteException; +import android.util.Log; +import android.view.IRecentsAnimationController; +import android.view.IRecentsAnimationRunner; +import android.view.RemoteAnimationTarget; +import android.window.TaskSnapshot; +import app.lawnchair.compatlib.RecentsAnimationRunnerCompat; +import app.lawnchair.compatlib.thirteen.ActivityManagerCompatVT; + +public class ActivityManagerCompatVU extends ActivityManagerCompatVT { + + private static final String TAG = "ActivityManagerCompatVU"; + + @Override + public void startRecentsActivity( + Intent intent, long eventTime, RecentsAnimationRunnerCompat runnerCompat) { + IRecentsAnimationRunner runner = null; + if (runnerCompat != null) { + runner = + new IRecentsAnimationRunner.Stub() { + @Override + public void onAnimationStart( + IRecentsAnimationController controller, + RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, + Rect homeContentInsets, + Rect minimizedHomeBounds) { + runnerCompat.onAnimationStart( + controller, + apps, + wallpapers, + homeContentInsets, + minimizedHomeBounds); + } + + @Override + public void onAnimationCanceled( + int[] taskIds, TaskSnapshot[] taskSnapshots) { + runnerCompat.onAnimationCanceled(taskIds, taskSnapshots); + } + + @Override + public void onTasksAppeared(RemoteAnimationTarget[] apps) { + runnerCompat.onTasksAppeared(apps); + } + }; + } + try { + getService().startRecentsActivity(intent, eventTime, runner); + } catch (RemoteException e) { + Log.e(TAG, "Failed to cancel recents animation", e); + } + } + + @Override + public TaskSnapshot getTaskSnapshot( + int taskId, boolean isLowResolution, boolean takeSnapshotIfNeeded) { + try { + return getService() + .getTaskSnapshot(taskId, isLowResolution, true /* takeSnapshotIfNeeded */); + } catch (RemoteException e) { + Log.e(TAG, "Failed to getTaskSnapshot", e); + return null; + } catch (NoSuchMethodError e) { + return super.getTaskSnapshot(taskId, isLowResolution, takeSnapshotIfNeeded); + } + } +} diff --git a/compatLib/compatLibVU/src/main/java/app/lawnchair/compatlib/fourteen/ActivityOptionsCompatVU.java b/compatLib/compatLibVU/src/main/java/app/lawnchair/compatlib/fourteen/ActivityOptionsCompatVU.java new file mode 100644 index 00000000000..9ad6b2a2abe --- /dev/null +++ b/compatLib/compatLibVU/src/main/java/app/lawnchair/compatlib/fourteen/ActivityOptionsCompatVU.java @@ -0,0 +1,29 @@ +package app.lawnchair.compatlib.fourteen; + +import android.app.ActivityOptions; +import android.content.Context; +import android.os.Handler; +import android.view.RemoteAnimationAdapter; +import android.window.RemoteTransition; +import app.lawnchair.compatlib.ActivityOptionsCompat; + +public class ActivityOptionsCompatVU extends ActivityOptionsCompat { + @Override + public ActivityOptions makeCustomAnimation( + Context context, + int enterResId, + int exitResId, + Runnable callback, + Handler callbackHandler) { + return null; + } + + @Override + public ActivityOptions makeRemoteAnimation( + RemoteAnimationAdapter remoteAnimationAdapter, + Object remoteTransition, + String debugName) { + return ActivityOptions.makeRemoteAnimation( + remoteAnimationAdapter, (RemoteTransition) remoteTransition); + } +} diff --git a/compatLib/compatLibVU/src/main/java/app/lawnchair/compatlib/fourteen/QuickstepCompatFactoryVU.java b/compatLib/compatLibVU/src/main/java/app/lawnchair/compatlib/fourteen/QuickstepCompatFactoryVU.java new file mode 100644 index 00000000000..194d0aafe75 --- /dev/null +++ b/compatLib/compatLibVU/src/main/java/app/lawnchair/compatlib/fourteen/QuickstepCompatFactoryVU.java @@ -0,0 +1,40 @@ +package app.lawnchair.compatlib.fourteen; + +import android.app.IApplicationThread; +import android.window.IRemoteTransition; +import android.window.RemoteTransition; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import app.lawnchair.compatlib.ActivityManagerCompat; +import app.lawnchair.compatlib.ActivityOptionsCompat; +import app.lawnchair.compatlib.RemoteTransitionCompat; +import app.lawnchair.compatlib.thirteen.QuickstepCompatFactoryVT; + +public class QuickstepCompatFactoryVU extends QuickstepCompatFactoryVT { + + @NonNull + @Override + public ActivityManagerCompat getActivityManagerCompat() { + return new ActivityManagerCompatVU(); + } + + @NonNull + @Override + public ActivityOptionsCompat getActivityOptionsCompat() { + return new ActivityOptionsCompatVU(); + } + + @NonNull + @Override + public RemoteTransitionCompat getRemoteTransitionCompat() { + return new RemoteTransitionCompat() { + @Override + public RemoteTransition getRemoteTransition( + @NonNull IRemoteTransition remoteTransition, + @Nullable IApplicationThread appThread, + @Nullable String debugName) { + return new RemoteTransition(remoteTransition, appThread, debugName); + } + }; + } +} diff --git a/compatLib/src/main/java/android/window/IRemoteTransition.aidl b/compatLib/src/main/java/android/window/IRemoteTransition.aidl new file mode 100644 index 00000000000..e33966d9006 --- /dev/null +++ b/compatLib/src/main/java/android/window/IRemoteTransition.aidl @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2020 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 android.window; + +import android.view.SurfaceControl; +import android.window.IRemoteTransitionFinishedCallback; +import android.window.TransitionInfo; + +/** + * Interface allowing remote processes to play transition animations. + * The usage flow is as follows: + *

    + *
  1. The remote tags a lifecycle event with an IRemoteTransition (via a parameter in + * ActivityOptions#makeRemoteAnimation) or a transition matches a filter registered via + * Transitions#registerRemote. + *
  2. Shell then associates the transition for the event with the IRemoteTransition + *
  3. Shell receives onTransitionReady and delegates the animation to the IRemoteTransition + * via {@link #startAnimation}. + *
  4. Once the IRemoteTransition is done animating, it will call the finishCallback. + *
  5. Shell/Core finish-up the transition. + * + * + * {@hide} + */ + +oneway interface IRemoteTransition { + /** + * Starts a transition animation. Once complete, the implementation should call + * `finishCallback`. + * + * @param token An identifier for the transition that should be animated. + */ + void startAnimation(in IBinder token, in TransitionInfo info, in SurfaceControl.Transaction t, + in IRemoteTransitionFinishedCallback finishCallback); + /** + * Attempts to merge a transition animation into the animation that is currently + * being played by this remote. If merge is not possible/supported, this should be a no-op. + * If it *is* merged, the implementation should call `finishCallback` immediately. + * + * @param transition An identifier for the transition that wants to be merged. + * @param mergeTarget The transition that is currently being animated by this remote. + * If it can be merged, call `finishCallback`; otherwise, do + * nothing. + */ + void mergeAnimation(in IBinder transition, in TransitionInfo info, + in SurfaceControl.Transaction t, in IBinder mergeTarget, + in IRemoteTransitionFinishedCallback finishCallback); +} \ No newline at end of file diff --git a/compatLib/src/main/java/android/window/IRemoteTransitionFinishedCallback.aidl b/compatLib/src/main/java/android/window/IRemoteTransitionFinishedCallback.aidl new file mode 100644 index 00000000000..e11ebe3156c --- /dev/null +++ b/compatLib/src/main/java/android/window/IRemoteTransitionFinishedCallback.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2020 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 android.window; +import android.view.SurfaceControl; +import android.window.WindowContainerTransaction; +/** + * Interface to be invoked by the controlling process when a remote transition has finished. + * + * @see IRemoteTransition + * @param wct An optional WindowContainerTransaction to apply before the transition finished. + * @param sct An optional Surface Transaction that is added to the end of the finish/cleanup + * transaction. This is applied by shell.Transitions (before submitting the wct). + * {@hide} + */ +interface IRemoteTransitionFinishedCallback { + void onTransitionFinished(in WindowContainerTransaction wct, in SurfaceControl.Transaction sct); +} \ No newline at end of file diff --git a/compatLib/src/main/java/android/window/RemoteTransition.aidl b/compatLib/src/main/java/android/window/RemoteTransition.aidl new file mode 100644 index 00000000000..2f6eaadb4b9 --- /dev/null +++ b/compatLib/src/main/java/android/window/RemoteTransition.aidl @@ -0,0 +1,19 @@ +/* + * 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 android.window; + +parcelable RemoteTransition; \ No newline at end of file diff --git a/compatLib/src/main/java/android/window/RemoteTransition.java b/compatLib/src/main/java/android/window/RemoteTransition.java new file mode 100644 index 00000000000..634800b90b0 --- /dev/null +++ b/compatLib/src/main/java/android/window/RemoteTransition.java @@ -0,0 +1,199 @@ +package android.window; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.IApplicationThread; +import android.os.IBinder; +import android.os.Parcelable; +import com.android.internal.util.DataClass; + +/** + * Represents a remote transition animation and information required to run it (eg. the app thread + * that needs to be boosted). + * + * @hide + */ +@DataClass(genToString = true, genSetters = true, genAidl = true) +public class RemoteTransition implements Parcelable { + /** The actual remote-transition interface used to run the transition animation. */ + private @NonNull IRemoteTransition mRemoteTransition; + + /** The application thread that will be running the remote transition. */ + private @Nullable IApplicationThread mAppThread; + + /** A name for this that can be used for debugging. */ + private @Nullable String mDebugName; + + /** Constructs with no app thread (animation runs in shell). */ + public RemoteTransition(@NonNull IRemoteTransition remoteTransition) { + this(remoteTransition, null /* appThread */, null /* debugName */); + } + + /** Constructs with no app thread (animation runs in shell). */ + public RemoteTransition( + @NonNull IRemoteTransition remoteTransition, @Nullable String debugName) { + this(remoteTransition, null /* appThread */, debugName); + } + + /** Get the IBinder associated with the underlying IRemoteTransition. */ + public @Nullable IBinder asBinder() { + return mRemoteTransition.asBinder(); + } + + // Code below generated by codegen v1.0.23. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/window/RemoteTransition.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + // @formatter:off + /** + * Creates a new RemoteTransition. + * + * @param remoteTransition The actual remote-transition interface used to run the transition + * animation. + * @param appThread The application thread that will be running the remote transition. + * @param debugName A name for this that can be used for debugging. + */ + @DataClass.Generated.Member + public RemoteTransition( + @NonNull IRemoteTransition remoteTransition, + @Nullable IApplicationThread appThread, + @Nullable String debugName) { + this.mRemoteTransition = remoteTransition; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mRemoteTransition); + this.mAppThread = appThread; + this.mDebugName = debugName; + // onConstructed(); // You can define this method to get a callback + } + + /** The actual remote-transition interface used to run the transition animation. */ + @DataClass.Generated.Member + public @NonNull IRemoteTransition getRemoteTransition() { + return mRemoteTransition; + } + + /** The application thread that will be running the remote transition. */ + @DataClass.Generated.Member + public @Nullable IApplicationThread getAppThread() { + return mAppThread; + } + + /** A name for this that can be used for debugging. */ + @DataClass.Generated.Member + public @Nullable String getDebugName() { + return mDebugName; + } + + /** The actual remote-transition interface used to run the transition animation. */ + @DataClass.Generated.Member + public @NonNull RemoteTransition setRemoteTransition(@NonNull IRemoteTransition value) { + mRemoteTransition = value; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mRemoteTransition); + return this; + } + + /** The application thread that will be running the remote transition. */ + @DataClass.Generated.Member + public @NonNull RemoteTransition setAppThread(@NonNull IApplicationThread value) { + mAppThread = value; + return this; + } + + /** A name for this that can be used for debugging. */ + @DataClass.Generated.Member + public @NonNull RemoteTransition setDebugName(@NonNull String value) { + mDebugName = value; + return this; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + return "RemoteTransition { " + + "remoteTransition = " + + mRemoteTransition + + ", " + + "appThread = " + + mAppThread + + ", " + + "debugName = " + + mDebugName + + " }"; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + byte flg = 0; + if (mAppThread != null) flg |= 0x2; + if (mDebugName != null) flg |= 0x4; + dest.writeByte(flg); + dest.writeStrongInterface(mRemoteTransition); + if (mAppThread != null) dest.writeStrongInterface(mAppThread); + if (mDebugName != null) dest.writeString(mDebugName); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { + return 0; + } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + protected RemoteTransition(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + byte flg = in.readByte(); + IRemoteTransition remoteTransition = + IRemoteTransition.Stub.asInterface(in.readStrongBinder()); + IApplicationThread appThread = + (flg & 0x2) == 0 + ? null + : IApplicationThread.Stub.asInterface(in.readStrongBinder()); + String debugName = (flg & 0x4) == 0 ? null : in.readString(); + this.mRemoteTransition = remoteTransition; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mRemoteTransition); + this.mAppThread = appThread; + this.mDebugName = debugName; + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public RemoteTransition[] newArray(int size) { + return new RemoteTransition[size]; + } + + @Override + public RemoteTransition createFromParcel(@NonNull android.os.Parcel in) { + return new RemoteTransition(in); + } + }; + + @DataClass.Generated( + time = 1678926409863L, + codegenVersion = "1.0.23", + sourceFile = "frameworks/base/core/java/android/window/RemoteTransition.java", + inputSignatures = + "private @android.annotation.NonNull android.window.IRemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.app.IApplicationThread mAppThread\nprivate @android.annotation.Nullable java.lang.String mDebugName\npublic @android.annotation.Nullable android.os.IBinder asBinder()\nclass RemoteTransition extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)") + @Deprecated + private void __metadata() {} + // @formatter:on + // End of generated code +} diff --git a/compatLib/src/main/java/android/window/TransitionFilter.aidl b/compatLib/src/main/java/android/window/TransitionFilter.aidl new file mode 100644 index 00000000000..3c3bb6acdc5 --- /dev/null +++ b/compatLib/src/main/java/android/window/TransitionFilter.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 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 android.window; + +parcelable TransitionFilter; \ No newline at end of file diff --git a/compatLib/src/main/java/android/window/TransitionFilter.java b/compatLib/src/main/java/android/window/TransitionFilter.java new file mode 100644 index 00000000000..738bccfd3f1 --- /dev/null +++ b/compatLib/src/main/java/android/window/TransitionFilter.java @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2020 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 android.window; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.view.WindowManager.TransitionType; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.WindowConfiguration; +import android.content.ComponentName; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.WindowManager; + +/** + * A parcelable filter that can be used for rerouting transitions to a remote. This is a local + * representation so that the transition system doesn't need to make blocking queries over binder. + * + * @hide + */ +public final class TransitionFilter implements Parcelable { + /** The associated requirement doesn't care about the z-order. */ + public static final int CONTAINER_ORDER_ANY = 0; + + /** The associated requirement only matches the top-most (z-order) container. */ + public static final int CONTAINER_ORDER_TOP = 1; + + /** @hide */ + @IntDef( + prefix = {"CONTAINER_ORDER_"}, + value = { + CONTAINER_ORDER_ANY, + CONTAINER_ORDER_TOP, + }) + public @interface ContainerOrder {} + + /** + * When non-null: this is a list of transition types that this filter applies to. This filter + * will fail for transitions that aren't one of these types. + */ + @Nullable public @TransitionType int[] mTypeSet = null; + + /** All flags must be set on a transition. */ + public @WindowManager.TransitionFlags int mFlags = 0; + + /** All flags must NOT be set on a transition. */ + public @WindowManager.TransitionFlags int mNotFlags = 0; + + /** A list of required changes. To pass, a transition must meet all requirements. */ + @Nullable public Requirement[] mRequirements = null; + + public TransitionFilter() {} + + private TransitionFilter(Parcel in) { + mTypeSet = in.createIntArray(); + mFlags = in.readInt(); + mNotFlags = in.readInt(); + mRequirements = in.createTypedArray(Requirement.CREATOR); + } + + /** + * @return true if `info` meets all the requirements to pass this filter. + */ + public boolean matches(@NonNull TransitionInfo info) { + if (mTypeSet != null) { + // non-null typeset, so make sure info is one of the types. + boolean typePass = false; + for (int i = 0; i < mTypeSet.length; ++i) { + if (info.getType() == mTypeSet[i]) { + typePass = true; + break; + } + } + if (!typePass) return false; + } + if ((info.getFlags() & mFlags) != mFlags) { + return false; + } + if ((info.getFlags() & mNotFlags) != 0) { + return false; + } + // Make sure info meets all of the requirements. + if (mRequirements != null) { + for (int i = 0; i < mRequirements.length; ++i) { + final boolean matches = mRequirements[i].matches(info); + if (matches == mRequirements[i].mNot) { + return false; + } + } + } + return true; + } + + @Override + /** @hide */ + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeIntArray(mTypeSet); + dest.writeInt(mFlags); + dest.writeInt(mNotFlags); + dest.writeTypedArray(mRequirements, flags); + } + + @NonNull + public static final Creator CREATOR = + new Creator() { + @Override + public TransitionFilter createFromParcel(Parcel in) { + return new TransitionFilter(in); + } + + @Override + public TransitionFilter[] newArray(int size) { + return new TransitionFilter[size]; + } + }; + + @Override + /** @hide */ + public int describeContents() { + return 0; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("{types=["); + if (mTypeSet != null) { + for (int i = 0; i < mTypeSet.length; ++i) { + sb.append((i == 0 ? "" : ",") + WindowManager.transitTypeToString(mTypeSet[i])); + } + } + sb.append("] flags=0x" + Integer.toHexString(mFlags)); + sb.append("] notFlags=0x" + Integer.toHexString(mNotFlags)); + sb.append(" checks=["); + if (mRequirements != null) { + for (int i = 0; i < mRequirements.length; ++i) { + sb.append((i == 0 ? "" : ",") + mRequirements[i]); + } + } + return sb.append("]}").toString(); + } + + /** + * Matches a change that a transition must contain to pass this filter. All requirements in a + * filter must be met to pass the filter. + */ + public static final class Requirement implements Parcelable { + public int mActivityType = ACTIVITY_TYPE_UNDEFINED; + + /** This only matches if the change is independent of its parents. */ + public boolean mMustBeIndependent = true; + + /** If this matches, the parent filter will fail */ + public boolean mNot = false; + + public int[] mModes = null; + + /** Matches only if all the flags here are set on the change. */ + public @TransitionInfo.ChangeFlags int mFlags = 0; + + /** If this needs to be a task. */ + public boolean mMustBeTask = false; + + public @ContainerOrder int mOrder = CONTAINER_ORDER_ANY; + public ComponentName mTopActivity; + + public Requirement() {} + + private Requirement(Parcel in) { + mActivityType = in.readInt(); + mMustBeIndependent = in.readBoolean(); + mNot = in.readBoolean(); + mModes = in.createIntArray(); + mFlags = in.readInt(); + mMustBeTask = in.readBoolean(); + mOrder = in.readInt(); + mTopActivity = in.readTypedObject(ComponentName.CREATOR); + } + + /** Go through changes and find if at-least one change matches this filter */ + boolean matches(@NonNull TransitionInfo info) { + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + if (mMustBeIndependent && !TransitionInfo.isIndependent(change, info)) { + // Only look at independent animating windows. + continue; + } + if (mOrder == CONTAINER_ORDER_TOP && i > 0) { + continue; + } + if (mActivityType != ACTIVITY_TYPE_UNDEFINED) { + if (change.getTaskInfo() == null + || change.getTaskInfo().getActivityType() != mActivityType) { + continue; + } + } + if (!matchesTopActivity(change.getTaskInfo())) continue; + if (mModes != null) { + boolean pass = false; + for (int m = 0; m < mModes.length; ++m) { + if (mModes[m] == change.getMode()) { + pass = true; + break; + } + } + if (!pass) continue; + } + if ((change.getFlags() & mFlags) != mFlags) { + continue; + } + if (mMustBeTask && change.getTaskInfo() == null) { + continue; + } + return true; + } + return false; + } + + private boolean matchesTopActivity(ActivityManager.RunningTaskInfo info) { + if (mTopActivity == null) return true; + if (info == null) return false; + final ComponentName component = info.topActivity; + return mTopActivity.equals(component); + } + + /** Check if the request matches this filter. It may generate false positives */ + boolean matches(@NonNull TransitionRequestInfo request) { + // Can't check modes/order since the transition hasn't been built at this point. + if (mActivityType == ACTIVITY_TYPE_UNDEFINED) return true; + return request.getTriggerTask() != null + && request.getTriggerTask().getActivityType() == mActivityType + && matchesTopActivity(request.getTriggerTask()); + } + + @Override + /** @hide */ + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mActivityType); + dest.writeBoolean(mMustBeIndependent); + dest.writeBoolean(mNot); + dest.writeIntArray(mModes); + dest.writeInt(mFlags); + dest.writeBoolean(mMustBeTask); + dest.writeInt(mOrder); + dest.writeTypedObject(mTopActivity, flags); + } + + @NonNull + public static final Creator CREATOR = + new Creator() { + @Override + public Requirement createFromParcel(Parcel in) { + return new Requirement(in); + } + + @Override + public Requirement[] newArray(int size) { + return new Requirement[size]; + } + }; + + @Override + /** @hide */ + public int describeContents() { + return 0; + } + + @Override + public String toString() { + StringBuilder out = new StringBuilder(); + out.append('{'); + if (mNot) out.append("NOT "); + out.append("atype=" + WindowConfiguration.activityTypeToString(mActivityType)); + out.append(" independent=" + mMustBeIndependent); + out.append(" modes=["); + if (mModes != null) { + for (int i = 0; i < mModes.length; ++i) { + out.append((i == 0 ? "" : ",") + TransitionInfo.modeToString(mModes[i])); + } + } + out.append("]"); + out.append(" flags=" + TransitionInfo.flagsToString(mFlags)); + out.append(" mustBeTask=" + mMustBeTask); + out.append(" order=" + containerOrderToString(mOrder)); + out.append(" topActivity=").append(mTopActivity); + out.append("}"); + return out.toString(); + } + } + + private static String containerOrderToString(int order) { + switch (order) { + case CONTAINER_ORDER_ANY: + return "ANY"; + case CONTAINER_ORDER_TOP: + return "TOP"; + } + return "UNKNOWN(" + order + ")"; + } +} diff --git a/compatLib/src/main/java/android/window/TransitionInfo.aidl b/compatLib/src/main/java/android/window/TransitionInfo.aidl new file mode 100644 index 00000000000..a3fa1c8641d --- /dev/null +++ b/compatLib/src/main/java/android/window/TransitionInfo.aidl @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2020 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 android.window; +parcelable TransitionInfo; +parcelable TransitionInfo.Change; \ No newline at end of file diff --git a/compatLib/src/main/java/android/window/TransitionInfo.java b/compatLib/src/main/java/android/window/TransitionInfo.java new file mode 100644 index 00000000000..e4c145cf68d --- /dev/null +++ b/compatLib/src/main/java/android/window/TransitionInfo.java @@ -0,0 +1,1403 @@ +/* + * Copyright (C) 2020 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 android.window; + +import static android.app.ActivityOptions.ANIM_CLIP_REVEAL; +import static android.app.ActivityOptions.ANIM_CUSTOM; +import static android.app.ActivityOptions.ANIM_FROM_STYLE; +import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS; +import static android.app.ActivityOptions.ANIM_SCALE_UP; +import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION; +import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN; +import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP; +import static android.app.WindowConfiguration.ROTATION_UNDEFINED; +import static android.view.Display.INVALID_DISPLAY; +import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED; +import static android.view.WindowManager.TRANSIT_CHANGE; +import static android.view.WindowManager.TRANSIT_CLOSE; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; +import static android.view.WindowManager.TRANSIT_NONE; +import static android.view.WindowManager.TRANSIT_OPEN; +import static android.view.WindowManager.TRANSIT_TO_BACK; +import static android.view.WindowManager.TRANSIT_TO_FRONT; +import static android.view.WindowManager.TransitionFlags; +import static android.view.WindowManager.TransitionType; +import static android.view.WindowManager.transitTypeToString; + +import android.annotation.ColorInt; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.graphics.Point; +import android.graphics.Rect; +import android.hardware.HardwareBuffer; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.Surface; +import android.view.SurfaceControl; +import android.view.WindowManager; +import java.util.ArrayList; +import java.util.List; + +/** + * Used to communicate information about what is changing during a transition to a TransitionPlayer. + * + * @hide + */ +public final class TransitionInfo implements Parcelable { + private static final String TAG = "TransitionInfo"; + + /** + * Modes are only a sub-set of all the transit-types since they are per-container + * + * @hide + */ + @IntDef( + prefix = {"TRANSIT_"}, + value = { + TRANSIT_NONE, + TRANSIT_OPEN, + TRANSIT_CLOSE, + // Note: to_front/to_back really mean show/hide respectively at the container level. + TRANSIT_TO_FRONT, + TRANSIT_TO_BACK, + TRANSIT_CHANGE + }) + public @interface TransitionMode {} + + /** No flags */ + public static final int FLAG_NONE = 0; + + /** The container shows the wallpaper behind it. */ + public static final int FLAG_SHOW_WALLPAPER = 1; + + /** The container IS the wallpaper. */ + public static final int FLAG_IS_WALLPAPER = 1 << 1; + + /** The container is translucent. */ + public static final int FLAG_TRANSLUCENT = 1 << 2; + + // TODO: remove when starting-window is moved to Task + /** The container is the recipient of a transferred starting-window */ + public static final int FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT = 1 << 3; + + /** The container has voice session. */ + public static final int FLAG_IS_VOICE_INTERACTION = 1 << 4; + + /** The container is the display. */ + public static final int FLAG_IS_DISPLAY = 1 << 5; + + /** + * Only for IS_DISPLAY containers. Is set if the display has system alert windows. This is used + * to prevent seamless rotation. TODO(b/194540864): Once we can include all windows in + * transition, then replace this with something like FLAG_IS_SYSTEM_ALERT instead. Then we can + * do mixed rotations. + */ + public static final int FLAG_DISPLAY_HAS_ALERT_WINDOWS = 1 << 7; + + /** The container is an input-method window. */ + public static final int FLAG_IS_INPUT_METHOD = 1 << 8; + + /** The container is in a Task with embedded activity. */ + public static final int FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY = 1 << 9; + + /** The container fills its parent Task before and after the transition. */ + public static final int FLAG_FILLS_TASK = 1 << 10; + + /** The container is going to show IME on its task after the transition. */ + public static final int FLAG_WILL_IME_SHOWN = 1 << 11; + + /** The container attaches owner profile thumbnail for cross profile animation. */ + public static final int FLAG_CROSS_PROFILE_OWNER_THUMBNAIL = 1 << 12; + + /** The container attaches work profile thumbnail for cross profile animation. */ + public static final int FLAG_CROSS_PROFILE_WORK_THUMBNAIL = 1 << 13; + + /** + * Whether the window is covered by an app starting window. This is different from {@link + * #FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT} which is only set on the Activity window that + * contains the starting window. + */ + public static final int FLAG_IS_BEHIND_STARTING_WINDOW = 1 << 14; + + /** This change happened underneath something else. */ + public static final int FLAG_IS_OCCLUDED = 1 << 15; + + /** The container is a system window, excluding wallpaper and input-method. */ + public static final int FLAG_IS_SYSTEM_WINDOW = 1 << 16; + + /** The window was animated by back gesture. */ + public static final int FLAG_BACK_GESTURE_ANIMATED = 1 << 17; + + /** The window should have no animation (by policy). */ + public static final int FLAG_NO_ANIMATION = 1 << 18; + + /** The task is launching behind home. */ + public static final int FLAG_TASK_LAUNCHING_BEHIND = 1 << 19; + + /** The task became the top-most task even if it didn't change visibility. */ + public static final int FLAG_MOVED_TO_TOP = 1 << 20; + + /** + * This transition must be the only transition when it starts (ie. it must wait for all other + * transition animations to finish). + */ + public static final int FLAG_SYNC = 1 << 21; + + /** The first unused bit. This can be used by remotes to attach custom flags to this change. */ + public static final int FLAG_FIRST_CUSTOM = 1 << 22; + + /** The change belongs to a window that won't contain activities. */ + public static final int FLAGS_IS_NON_APP_WINDOW = + FLAG_IS_WALLPAPER | FLAG_IS_INPUT_METHOD | FLAG_IS_SYSTEM_WINDOW; + + /** @hide */ + @IntDef( + prefix = {"FLAG_"}, + value = { + FLAG_NONE, + FLAG_SHOW_WALLPAPER, + FLAG_IS_WALLPAPER, + FLAG_TRANSLUCENT, + FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT, + FLAG_IS_VOICE_INTERACTION, + FLAG_IS_DISPLAY, + FLAG_DISPLAY_HAS_ALERT_WINDOWS, + FLAG_IS_INPUT_METHOD, + FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY, + FLAG_FILLS_TASK, + FLAG_WILL_IME_SHOWN, + FLAG_CROSS_PROFILE_OWNER_THUMBNAIL, + FLAG_CROSS_PROFILE_WORK_THUMBNAIL, + FLAG_IS_BEHIND_STARTING_WINDOW, + FLAG_IS_OCCLUDED, + FLAG_IS_SYSTEM_WINDOW, + FLAG_BACK_GESTURE_ANIMATED, + FLAG_NO_ANIMATION, + FLAG_TASK_LAUNCHING_BEHIND, + FLAG_MOVED_TO_TOP, + FLAG_SYNC, + FLAG_FIRST_CUSTOM + }) + public @interface ChangeFlags {} + + private final @TransitionType int mType; + private @TransitionFlags int mFlags; + private int mTrack = 0; + private final ArrayList mChanges = new ArrayList<>(); + private final ArrayList mRoots = new ArrayList<>(); + private AnimationOptions mOptions; + + /** This is only a BEST-EFFORT id used for log correlation. DO NOT USE for any real work! */ + private int mDebugId = -1; + + /** @hide */ + public TransitionInfo(@TransitionType int type, @TransitionFlags int flags) { + mType = type; + mFlags = flags; + } + + private TransitionInfo(Parcel in) { + mType = in.readInt(); + mFlags = in.readInt(); + in.readTypedList(mChanges, Change.CREATOR); + in.readTypedList(mRoots, Root.CREATOR); + mOptions = in.readTypedObject(AnimationOptions.CREATOR); + mDebugId = in.readInt(); + mTrack = in.readInt(); + } + + @Override + /** @hide */ + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mType); + dest.writeInt(mFlags); + dest.writeTypedList(mChanges); + dest.writeTypedList(mRoots, flags); + dest.writeTypedObject(mOptions, flags); + dest.writeInt(mDebugId); + dest.writeInt(mTrack); + } + + @NonNull + public static final Creator CREATOR = + new Creator() { + @Override + public TransitionInfo createFromParcel(Parcel in) { + return new TransitionInfo(in); + } + + @Override + public TransitionInfo[] newArray(int size) { + return new TransitionInfo[size]; + } + }; + + @Override + /** @hide */ + public int describeContents() { + return 0; + } + + /** + * @see #getRoot + */ + public void addRootLeash( + int displayId, @NonNull SurfaceControl leash, int offsetLeft, int offsetTop) { + mRoots.add(new Root(displayId, leash, offsetLeft, offsetTop)); + } + + /** + * @see #getRoot + */ + public void addRoot(Root other) { + mRoots.add(other); + } + + public void setAnimationOptions(AnimationOptions options) { + mOptions = options; + } + + public @TransitionType int getType() { + return mType; + } + + public void setFlags(int flags) { + mFlags = flags; + } + + public int getFlags() { + return mFlags; + } + + /** + * @return The number of animation roots. Most transitions should have 1, but there may be more + * in some cases (such as a transition spanning multiple displays). + */ + public int getRootCount() { + return mRoots.size(); + } + + /** + * @return the transition-root at a specific index. + */ + @NonNull + public Root getRoot(int idx) { + return mRoots.get(idx); + } + + /** + * @return the index of the transition-root associated with `displayId` or -1 if not found. + */ + public int findRootIndex(int displayId) { + for (int i = 0; i < mRoots.size(); ++i) { + if (mRoots.get(i).mDisplayId == displayId) { + return i; + } + } + return -1; + } + + /** + * @return a surfacecontrol that can serve as a parent surfacecontrol for all the changing + * participants to animate within. This will generally be placed at the highest-z-order + * shared ancestor of all participants. While this is non-null, it's possible for the + * rootleash to be invalid if the transition is a no-op. + * @deprecated Use {@link #getRoot} instead. This call assumes there is only one root. + */ + @Deprecated + @NonNull + public SurfaceControl getRootLeash() { + if (mRoots.isEmpty()) { + throw new IllegalStateException("Trying to get a root leash from a no-op transition."); + } + if (mRoots.size() > 1) { + android.util.Log.e( + TAG, "Assuming one animation root when there are more.", new Throwable()); + } + return mRoots.get(0).mLeash; + } + + public AnimationOptions getAnimationOptions() { + return mOptions; + } + + /** + * @return the list of {@link Change}s in this transition. The list is sorted top-to-bottom in Z + * (meaning index 0 is the top-most container). + */ + @NonNull + public List getChanges() { + return mChanges; + } + + /** + * @return the Change that a window is undergoing or {@code null} if not directly represented. + */ + @Nullable + public Change getChange(@NonNull WindowContainerToken token) { + for (int i = mChanges.size() - 1; i >= 0; --i) { + if (token.equals(mChanges.get(i).mContainer)) { + return mChanges.get(i); + } + } + return null; + } + + /** Add a {@link Change} to this transition. */ + public void addChange(@NonNull Change change) { + mChanges.add(change); + } + + /** + * Whether this transition contains any changes to the window hierarchy, including keyguard + * visibility. + */ + public boolean hasChangesOrSideEffects() { + return !mChanges.isEmpty() + || isKeyguardGoingAway() + || (mFlags & TRANSIT_FLAG_KEYGUARD_APPEARING) != 0; + } + + /** Whether this transition includes keyguard going away. */ + public boolean isKeyguardGoingAway() { + return (mFlags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0; + } + + /** Gets which animation track this transition should run on. */ + public int getTrack() { + return mTrack; + } + + /** Sets which animation track this transition should run on. */ + public void setTrack(int track) { + mTrack = track; + } + + /** + * Set an arbitrary "debug" id for this info. This id will not be used for any "real work", it + * is just for debugging and logging. + */ + public void setDebugId(int id) { + mDebugId = id; + } + + /** Get the "debug" id of this info. Do NOT use this for real work, only use for debugging. */ + public int getDebugId() { + return mDebugId; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("{id=") + .append(mDebugId) + .append(" t=") + .append(transitTypeToString(mType)) + .append(" f=0x") + .append(Integer.toHexString(mFlags)) + .append(" trk=") + .append(mTrack) + .append(" r=["); + for (int i = 0; i < mRoots.size(); ++i) { + if (i > 0) { + sb.append(','); + } + sb.append(mRoots.get(i).mDisplayId).append("@").append(mRoots.get(i).mOffset); + } + sb.append("] c=["); + for (int i = 0; i < mChanges.size(); ++i) { + if (i > 0) { + sb.append(','); + } + sb.append(mChanges.get(i)); + } + sb.append("]}"); + return sb.toString(); + } + + /** Converts a transition mode/action to its string representation. */ + @NonNull + public static String modeToString(@TransitionMode int mode) { + switch (mode) { + case TRANSIT_NONE: + return "NONE"; + case TRANSIT_OPEN: + return "OPEN"; + case TRANSIT_CLOSE: + return "CLOSE"; + case TRANSIT_TO_FRONT: + return "TO_FRONT"; + case TRANSIT_TO_BACK: + return "TO_BACK"; + case TRANSIT_CHANGE: + return "CHANGE"; + default: + return ""; + } + } + + /** Converts change flags into a string representation. */ + @NonNull + public static String flagsToString(@ChangeFlags int flags) { + if (flags == 0) return "NONE"; + final StringBuilder sb = new StringBuilder(); + if ((flags & FLAG_SHOW_WALLPAPER) != 0) { + sb.append("SHOW_WALLPAPER"); + } + if ((flags & FLAG_IS_WALLPAPER) != 0) { + sb.append("IS_WALLPAPER"); + } + if ((flags & FLAG_IS_INPUT_METHOD) != 0) { + sb.append("IS_INPUT_METHOD"); + } + if ((flags & FLAG_TRANSLUCENT) != 0) { + sb.append(sb.length() == 0 ? "" : "|").append("TRANSLUCENT"); + } + if ((flags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { + sb.append(sb.length() == 0 ? "" : "|").append("STARTING_WINDOW_TRANSFER"); + } + if ((flags & FLAG_IS_VOICE_INTERACTION) != 0) { + sb.append(sb.length() == 0 ? "" : "|").append("IS_VOICE_INTERACTION"); + } + if ((flags & FLAG_IS_DISPLAY) != 0) { + sb.append(sb.length() == 0 ? "" : "|").append("IS_DISPLAY"); + } + if ((flags & FLAG_DISPLAY_HAS_ALERT_WINDOWS) != 0) { + sb.append(sb.length() == 0 ? "" : "|").append("DISPLAY_HAS_ALERT_WINDOWS"); + } + if ((flags & FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY) != 0) { + sb.append(sb.length() == 0 ? "" : "|").append("IN_TASK_WITH_EMBEDDED_ACTIVITY"); + } + if ((flags & FLAG_FILLS_TASK) != 0) { + sb.append(sb.length() == 0 ? "" : "|").append("FILLS_TASK"); + } + if ((flags & FLAG_IS_BEHIND_STARTING_WINDOW) != 0) { + sb.append(sb.length() == 0 ? "" : "|").append("IS_BEHIND_STARTING_WINDOW"); + } + if ((flags & FLAG_IS_OCCLUDED) != 0) { + sb.append(sb.length() == 0 ? "" : "|").append("IS_OCCLUDED"); + } + if ((flags & FLAG_IS_SYSTEM_WINDOW) != 0) { + sb.append(sb.length() == 0 ? "" : "|").append("FLAG_IS_SYSTEM_WINDOW"); + } + if ((flags & FLAG_BACK_GESTURE_ANIMATED) != 0) { + sb.append(sb.length() == 0 ? "" : "|").append("FLAG_BACK_GESTURE_ANIMATED"); + } + if ((flags & FLAG_NO_ANIMATION) != 0) { + sb.append(sb.length() == 0 ? "" : "|").append("NO_ANIMATION"); + } + if ((flags & FLAG_TASK_LAUNCHING_BEHIND) != 0) { + sb.append((sb.length() == 0 ? "" : "|") + "TASK_LAUNCHING_BEHIND"); + } + if ((flags & FLAG_SYNC) != 0) { + sb.append((sb.length() == 0 ? "" : "|") + "SYNC"); + } + if ((flags & FLAG_FIRST_CUSTOM) != 0) { + sb.append(sb.length() == 0 ? "" : "|").append("FIRST_CUSTOM"); + } + if ((flags & FLAG_MOVED_TO_TOP) != 0) { + sb.append(sb.length() == 0 ? "" : "|").append("MOVE_TO_TOP"); + } + return sb.toString(); + } + + /** + * Indication that `change` is independent of parents (ie. it has a different type of transition + * vs. "going along for the ride") + */ + public static boolean isIndependent( + @NonNull TransitionInfo.Change change, @NonNull TransitionInfo info) { + // If the change has no parent (it is root), then it is independent + if (change.getParent() == null) return true; + // non-visibility changes will just be folded into the parent change, so they aren't + // independent either. + if (change.getMode() == TRANSIT_CHANGE) return false; + TransitionInfo.Change parentChg = info.getChange(change.getParent()); + while (parentChg != null) { + // If the parent is a visibility change, it will include the results of all child + // changes into itself, so none of its children can be independent. + if (parentChg.getMode() != TRANSIT_CHANGE) return false; + // If there are no more parents left, then all the parents, so far, have not been + // visibility changes which means this change is independent. + if (parentChg.getParent() == null) return true; + parentChg = info.getChange(parentChg.getParent()); + } + return false; + } + + /** + * Releases temporary-for-animation surfaces referenced by this to potentially free up memory. + * This includes root-leash and snapshots. + */ + public void releaseAnimSurfaces() { + for (int i = mChanges.size() - 1; i >= 0; --i) { + final Change c = mChanges.get(i); + if (c.mSnapshot != null) { + c.mSnapshot.release(); + c.mSnapshot = null; + } + } + for (int i = 0; i < mRoots.size(); ++i) { + mRoots.get(i).mLeash.release(); + } + } + + /** + * Releases ALL the surfaces referenced by this to potentially free up memory. Do NOT use this + * if the surface-controls get stored and used elsewhere in the process. To just release + * temporary-for-animation surfaces, use {@link #releaseAnimSurfaces}. + */ + public void releaseAllSurfaces() { + releaseAnimSurfaces(); + for (int i = mChanges.size() - 1; i >= 0; --i) { + mChanges.get(i).getLeash().release(); + } + } + + /** + * Updates the callsites of all the surfaces in this transition, which aids in the debugging of + * lingering surfaces. + */ + public void setUnreleasedWarningCallSiteForAllSurfaces(String callsite) { + for (int i = mChanges.size() - 1; i >= 0; --i) { + mChanges.get(i).getLeash().setUnreleasedWarningCallSite(callsite); + } + } + + /** + * Makes a copy of this as if it were parcel'd and unparcel'd. This implies that surfacecontrol + * refcounts are incremented which allows the "remote" receiver to release them without breaking + * the caller's references. Use this only if you need to "send" this to a local function which + * assumes it is being called from a remote caller. + */ + public TransitionInfo localRemoteCopy() { + final TransitionInfo out = new TransitionInfo(mType, mFlags); + out.mTrack = mTrack; + out.mDebugId = mDebugId; + for (int i = 0; i < mChanges.size(); ++i) { + out.mChanges.add(mChanges.get(i).localRemoteCopy()); + } + for (int i = 0; i < mRoots.size(); ++i) { + out.mRoots.add(mRoots.get(i).localRemoteCopy()); + } + // Doesn't have any native stuff, so no need for actual copy + out.mOptions = mOptions; + return out; + } + + /** Represents the change a WindowContainer undergoes during a transition */ + public static final class Change implements Parcelable { + private final WindowContainerToken mContainer; + private WindowContainerToken mParent; + private WindowContainerToken mLastParent; + private final SurfaceControl mLeash; + private @TransitionMode int mMode = TRANSIT_NONE; + private @ChangeFlags int mFlags = FLAG_NONE; + private final Rect mStartAbsBounds = new Rect(); + private final Rect mEndAbsBounds = new Rect(); + private final Point mEndRelOffset = new Point(); + private ActivityManager.RunningTaskInfo mTaskInfo = null; + private boolean mAllowEnterPip; + private int mStartDisplayId = INVALID_DISPLAY; + private int mEndDisplayId = INVALID_DISPLAY; + private @Surface.Rotation int mStartRotation = ROTATION_UNDEFINED; + private @Surface.Rotation int mEndRotation = ROTATION_UNDEFINED; + + /** + * The end rotation of the top activity after fixed rotation is finished. If the top + * activity is not in fixed rotation, it will be {@link ROTATION_UNDEFINED}. + */ + private @Surface.Rotation int mEndFixedRotation = ROTATION_UNDEFINED; + + private int mRotationAnimation = ROTATION_ANIMATION_UNSPECIFIED; + private @ColorInt int mBackgroundColor; + private SurfaceControl mSnapshot = null; + private float mSnapshotLuma; + + public Change(@Nullable WindowContainerToken container, @NonNull SurfaceControl leash) { + mContainer = container; + mLeash = leash; + } + + private Change(Parcel in) { + mContainer = in.readTypedObject(WindowContainerToken.CREATOR); + mParent = in.readTypedObject(WindowContainerToken.CREATOR); + mLastParent = in.readTypedObject(WindowContainerToken.CREATOR); + mLeash = new SurfaceControl(); + mLeash.readFromParcel(in); + mMode = in.readInt(); + mFlags = in.readInt(); + mStartAbsBounds.readFromParcel(in); + mEndAbsBounds.readFromParcel(in); + mEndRelOffset.readFromParcel(in); + mTaskInfo = in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR); + mAllowEnterPip = in.readBoolean(); + mStartDisplayId = in.readInt(); + mEndDisplayId = in.readInt(); + mStartRotation = in.readInt(); + mEndRotation = in.readInt(); + mEndFixedRotation = in.readInt(); + mRotationAnimation = in.readInt(); + mBackgroundColor = in.readInt(); + mSnapshot = in.readTypedObject(SurfaceControl.CREATOR); + mSnapshotLuma = in.readFloat(); + } + + private Change localRemoteCopy() { + final Change out = new Change(mContainer, new SurfaceControl(mLeash, "localRemote")); + out.mParent = mParent; + out.mLastParent = mLastParent; + out.mMode = mMode; + out.mFlags = mFlags; + out.mStartAbsBounds.set(mStartAbsBounds); + out.mEndAbsBounds.set(mEndAbsBounds); + out.mEndRelOffset.set(mEndRelOffset); + out.mTaskInfo = mTaskInfo; + out.mAllowEnterPip = mAllowEnterPip; + out.mStartDisplayId = mStartDisplayId; + out.mEndDisplayId = mEndDisplayId; + out.mStartRotation = mStartRotation; + out.mEndRotation = mEndRotation; + out.mEndFixedRotation = mEndFixedRotation; + out.mRotationAnimation = mRotationAnimation; + out.mBackgroundColor = mBackgroundColor; + out.mSnapshot = mSnapshot != null ? new SurfaceControl(mSnapshot, "localRemote") : null; + out.mSnapshotLuma = mSnapshotLuma; + return out; + } + + /** Sets the parent of this change's container. The parent must be a participant or null. */ + public void setParent(@Nullable WindowContainerToken parent) { + mParent = parent; + } + + /** + * Sets the parent of this change's container before the transition if this change's + * container is reparented in the transition. + */ + public void setLastParent(@Nullable WindowContainerToken lastParent) { + mLastParent = lastParent; + } + + /** Sets the transition mode for this change */ + public void setMode(@TransitionMode int mode) { + mMode = mode; + } + + /** Sets the flags for this change */ + public void setFlags(@ChangeFlags int flags) { + mFlags = flags; + } + + /** Sets the bounds this container occupied before the change in screen space */ + public void setStartAbsBounds(@Nullable Rect rect) { + mStartAbsBounds.set(rect); + } + + /** Sets the bounds this container will occupy after the change in screen space */ + public void setEndAbsBounds(@Nullable Rect rect) { + mEndAbsBounds.set(rect); + } + + /** Sets the offset of this container from its parent surface */ + public void setEndRelOffset(int left, int top) { + mEndRelOffset.set(left, top); + } + + /** + * Sets the taskinfo of this container if this is a task. WARNING: this takes the reference, + * so don't modify it afterwards. + */ + public void setTaskInfo(@Nullable ActivityManager.RunningTaskInfo taskInfo) { + mTaskInfo = taskInfo; + } + + /** Sets the allowEnterPip flag which represents AppOpsManager check on PiP permission */ + public void setAllowEnterPip(boolean allowEnterPip) { + mAllowEnterPip = allowEnterPip; + } + + /** Sets the start and end rotation of this container. */ + public void setDisplayId(int start, int end) { + mStartDisplayId = start; + mEndDisplayId = end; + } + + /** Sets the start and end rotation of this container. */ + public void setRotation(@Surface.Rotation int start, @Surface.Rotation int end) { + mStartRotation = start; + mEndRotation = end; + } + + /** Sets end rotation that top activity will be launched to after fixed rotation. */ + public void setEndFixedRotation(@Surface.Rotation int endFixedRotation) { + mEndFixedRotation = endFixedRotation; + } + + /** + * Sets the app-requested animation type for rotation. Will be one of the + * ROTATION_ANIMATION_ values in {@link android.view.WindowManager.LayoutParams}; + */ + public void setRotationAnimation(int anim) { + mRotationAnimation = anim; + } + + /** Sets the background color of this change's container. */ + public void setBackgroundColor(@ColorInt int backgroundColor) { + mBackgroundColor = backgroundColor; + } + + /** Sets a snapshot surface for the "start" state of the container. */ + public void setSnapshot(@Nullable SurfaceControl snapshot, float luma) { + mSnapshot = snapshot; + mSnapshotLuma = luma; + } + + /** + * @return the container that is changing. May be null if non-remotable (eg. activity) + */ + @Nullable + public WindowContainerToken getContainer() { + return mContainer; + } + + /** + * @return the parent of the changing container. This is the parent within the participants, + * not necessarily the actual parent. + */ + @Nullable + public WindowContainerToken getParent() { + return mParent; + } + + /** + * @return the parent of the changing container before the transition if it is reparented in + * the transition. The parent window may not be collected in the transition as a + * participant, and it may have been detached from the display. {@code null} if the + * changing container has not been reparented in the transition, or if the parent is not + * organizable. + */ + @Nullable + public WindowContainerToken getLastParent() { + return mLastParent; + } + + /** + * @return which action this change represents. + */ + public @TransitionMode int getMode() { + return mMode; + } + + /** + * @return the flags for this change. + */ + public @ChangeFlags int getFlags() { + return mFlags; + } + + /** Whether this change contains any of the given change flags. */ + public boolean hasFlags(@ChangeFlags int flags) { + return (mFlags & flags) != 0; + } + + /** Whether this change contains all of the given change flags. */ + public boolean hasAllFlags(@ChangeFlags int flags) { + return (mFlags & flags) == flags; + } + + /** + * @return the bounds of the container before the change. It may be empty if the container + * is coming into existence. + */ + @NonNull + public Rect getStartAbsBounds() { + return mStartAbsBounds; + } + + /** + * @return the bounds of the container after the change. It may be empty if the container is + * disappearing. + */ + @NonNull + public Rect getEndAbsBounds() { + return mEndAbsBounds; + } + + /** + * @return the offset of the container's surface from its parent surface after the change. + */ + @NonNull + public Point getEndRelOffset() { + return mEndRelOffset; + } + + /** + * @return the leash or surface to animate for this container + */ + @NonNull + public SurfaceControl getLeash() { + return mLeash; + } + + /** + * @return the task info or null if this isn't a task + */ + @Nullable + public ActivityManager.RunningTaskInfo getTaskInfo() { + return mTaskInfo; + } + + public boolean getAllowEnterPip() { + return mAllowEnterPip; + } + + public int getStartDisplayId() { + return mStartDisplayId; + } + + public int getEndDisplayId() { + return mEndDisplayId; + } + + @Surface.Rotation + public int getStartRotation() { + return mStartRotation; + } + + @Surface.Rotation + public int getEndRotation() { + return mEndRotation; + } + + @Surface.Rotation + public int getEndFixedRotation() { + return mEndFixedRotation; + } + + /** + * @return the rotation animation. + */ + public int getRotationAnimation() { + return mRotationAnimation; + } + + /** + * @return get the background color of this change's container. + */ + @ColorInt + public int getBackgroundColor() { + return mBackgroundColor; + } + + /** + * @return a snapshot surface (if applicable). + */ + @Nullable + public SurfaceControl getSnapshot() { + return mSnapshot; + } + + /** + * @return the luma calculated for the snapshot surface (if applicable). + */ + public float getSnapshotLuma() { + return mSnapshotLuma; + } + + /** @hide */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeTypedObject(mContainer, flags); + dest.writeTypedObject(mParent, flags); + dest.writeTypedObject(mLastParent, flags); + mLeash.writeToParcel(dest, flags); + dest.writeInt(mMode); + dest.writeInt(mFlags); + mStartAbsBounds.writeToParcel(dest, flags); + mEndAbsBounds.writeToParcel(dest, flags); + mEndRelOffset.writeToParcel(dest, flags); + dest.writeTypedObject(mTaskInfo, flags); + dest.writeBoolean(mAllowEnterPip); + dest.writeInt(mStartDisplayId); + dest.writeInt(mEndDisplayId); + dest.writeInt(mStartRotation); + dest.writeInt(mEndRotation); + dest.writeInt(mEndFixedRotation); + dest.writeInt(mRotationAnimation); + dest.writeInt(mBackgroundColor); + dest.writeTypedObject(mSnapshot, flags); + dest.writeFloat(mSnapshotLuma); + } + + @NonNull + public static final Creator CREATOR = + new Creator() { + @Override + public Change createFromParcel(Parcel in) { + return new Change(in); + } + + @Override + public Change[] newArray(int size) { + return new Change[size]; + } + }; + + /** @hide */ + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append('{'); + sb.append(mContainer); + sb.append(" m="); + sb.append(modeToString(mMode)); + sb.append(" f="); + sb.append(flagsToString(mFlags)); + if (mParent != null) { + sb.append(" p="); + sb.append(mParent); + } + if (mLeash != null) { + sb.append(" leash="); + sb.append(mLeash); + } + sb.append(" sb="); + sb.append(mStartAbsBounds); + sb.append(" eb="); + sb.append(mEndAbsBounds); + if (mEndRelOffset.x != 0 || mEndRelOffset.y != 0) { + sb.append(" eo="); + sb.append(mEndRelOffset); + } + sb.append(" d="); + if (mStartDisplayId != mEndDisplayId) { + sb.append(mStartDisplayId).append("->"); + } + sb.append(mEndDisplayId); + if (mStartRotation != mEndRotation) { + sb.append(" r="); + sb.append(mStartRotation); + sb.append("->"); + sb.append(mEndRotation); + sb.append(':'); + sb.append(mRotationAnimation); + } + if (mEndFixedRotation != ROTATION_UNDEFINED) { + sb.append(" endFixedRotation="); + sb.append(mEndFixedRotation); + } + if (mSnapshot != null) { + sb.append(" snapshot="); + sb.append(mSnapshot); + } + if (mLastParent != null) { + sb.append(" lastParent="); + sb.append(mLastParent); + } + sb.append('}'); + return sb.toString(); + } + } + + /** Represents animation options during a transition */ + public static final class AnimationOptions implements Parcelable { + private int mType; + private int mEnterResId; + private int mExitResId; + private boolean mOverrideTaskTransition; + private String mPackageName; + private final Rect mTransitionBounds = new Rect(); + private HardwareBuffer mThumbnail; + private int mAnimations; + private @ColorInt int mBackgroundColor; + // Customize activity transition animation + private CustomActivityTransition mCustomActivityOpenTransition; + private CustomActivityTransition mCustomActivityCloseTransition; + + private AnimationOptions(int type) { + mType = type; + } + + public AnimationOptions(Parcel in) { + mType = in.readInt(); + mEnterResId = in.readInt(); + mExitResId = in.readInt(); + mBackgroundColor = in.readInt(); + mOverrideTaskTransition = in.readBoolean(); + mPackageName = in.readString(); + mTransitionBounds.readFromParcel(in); + mThumbnail = in.readTypedObject(HardwareBuffer.CREATOR); + mAnimations = in.readInt(); + mCustomActivityOpenTransition = in.readTypedObject(CustomActivityTransition.CREATOR); + mCustomActivityCloseTransition = in.readTypedObject(CustomActivityTransition.CREATOR); + } + + /** Make basic customized animation for a package */ + public static AnimationOptions makeCommonAnimOptions(String packageName) { + AnimationOptions options = new AnimationOptions(ANIM_FROM_STYLE); + options.mPackageName = packageName; + return options; + } + + public static AnimationOptions makeAnimOptionsFromLayoutParameters( + WindowManager.LayoutParams lp) { + AnimationOptions options = new AnimationOptions(ANIM_FROM_STYLE); + options.mPackageName = lp.packageName; + options.mAnimations = lp.windowAnimations; + return options; + } + + /** Add customized window animations */ + public void addOptionsFromLayoutParameters(WindowManager.LayoutParams lp) { + mAnimations = lp.windowAnimations; + } + + /** Add customized activity animation attributes */ + public void addCustomActivityTransition( + boolean isOpen, int enterResId, int exitResId, int backgroundColor) { + CustomActivityTransition customTransition = + isOpen ? mCustomActivityOpenTransition : mCustomActivityCloseTransition; + if (customTransition == null) { + customTransition = new CustomActivityTransition(); + if (isOpen) { + mCustomActivityOpenTransition = customTransition; + } else { + mCustomActivityCloseTransition = customTransition; + } + } + customTransition.addCustomActivityTransition(enterResId, exitResId, backgroundColor); + } + + public static AnimationOptions makeCustomAnimOptions( + String packageName, + int enterResId, + int exitResId, + @ColorInt int backgroundColor, + boolean overrideTaskTransition) { + AnimationOptions options = new AnimationOptions(ANIM_CUSTOM); + options.mPackageName = packageName; + options.mEnterResId = enterResId; + options.mExitResId = exitResId; + options.mBackgroundColor = backgroundColor; + options.mOverrideTaskTransition = overrideTaskTransition; + return options; + } + + public static AnimationOptions makeClipRevealAnimOptions( + int startX, int startY, int width, int height) { + AnimationOptions options = new AnimationOptions(ANIM_CLIP_REVEAL); + options.mTransitionBounds.set(startX, startY, startX + width, startY + height); + return options; + } + + public static AnimationOptions makeScaleUpAnimOptions( + int startX, int startY, int width, int height) { + AnimationOptions options = new AnimationOptions(ANIM_SCALE_UP); + options.mTransitionBounds.set(startX, startY, startX + width, startY + height); + return options; + } + + public static AnimationOptions makeThumbnailAnimOptions( + HardwareBuffer srcThumb, int startX, int startY, boolean scaleUp) { + AnimationOptions options = + new AnimationOptions( + scaleUp ? ANIM_THUMBNAIL_SCALE_UP : ANIM_THUMBNAIL_SCALE_DOWN); + options.mTransitionBounds.set(startX, startY, startX, startY); + options.mThumbnail = srcThumb; + return options; + } + + public static AnimationOptions makeCrossProfileAnimOptions() { + AnimationOptions options = new AnimationOptions(ANIM_OPEN_CROSS_PROFILE_APPS); + return options; + } + + public static AnimationOptions makeSceneTransitionAnimOptions() { + AnimationOptions options = new AnimationOptions(ANIM_SCENE_TRANSITION); + return options; + } + + public int getType() { + return mType; + } + + public int getEnterResId() { + return mEnterResId; + } + + public int getExitResId() { + return mExitResId; + } + + public @ColorInt int getBackgroundColor() { + return mBackgroundColor; + } + + public boolean getOverrideTaskTransition() { + return mOverrideTaskTransition; + } + + public String getPackageName() { + return mPackageName; + } + + public Rect getTransitionBounds() { + return mTransitionBounds; + } + + public HardwareBuffer getThumbnail() { + return mThumbnail; + } + + public int getAnimations() { + return mAnimations; + } + + /** Return customized activity transition if existed. */ + public CustomActivityTransition getCustomActivityTransition(boolean open) { + return open ? mCustomActivityOpenTransition : mCustomActivityCloseTransition; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mType); + dest.writeInt(mEnterResId); + dest.writeInt(mExitResId); + dest.writeInt(mBackgroundColor); + dest.writeBoolean(mOverrideTaskTransition); + dest.writeString(mPackageName); + mTransitionBounds.writeToParcel(dest, flags); + dest.writeTypedObject(mThumbnail, flags); + dest.writeInt(mAnimations); + dest.writeTypedObject(mCustomActivityOpenTransition, flags); + dest.writeTypedObject(mCustomActivityCloseTransition, flags); + } + + @NonNull + public static final Creator CREATOR = + new Creator() { + @Override + public AnimationOptions createFromParcel(Parcel in) { + return new AnimationOptions(in); + } + + @Override + public AnimationOptions[] newArray(int size) { + return new AnimationOptions[size]; + } + }; + + /** @hide */ + @Override + public int describeContents() { + return 0; + } + + @NonNull + private static String typeToString(int mode) { + switch (mode) { + case ANIM_CUSTOM: + return "ANIM_CUSTOM"; + case ANIM_CLIP_REVEAL: + return "ANIM_CLIP_REVEAL"; + case ANIM_SCALE_UP: + return "ANIM_SCALE_UP"; + case ANIM_THUMBNAIL_SCALE_UP: + return "ANIM_THUMBNAIL_SCALE_UP"; + case ANIM_THUMBNAIL_SCALE_DOWN: + return "ANIM_THUMBNAIL_SCALE_DOWN"; + case ANIM_OPEN_CROSS_PROFILE_APPS: + return "ANIM_OPEN_CROSS_PROFILE_APPS"; + default: + return ""; + } + } + + @Override + public String toString() { + return "{ AnimationOptions type= " + + typeToString(mType) + + " package=" + + mPackageName + + " override=" + + mOverrideTaskTransition + + " b=" + + mTransitionBounds + + "}"; + } + + /** Customized activity transition. */ + public static class CustomActivityTransition implements Parcelable { + private int mCustomEnterResId; + private int mCustomExitResId; + private int mCustomBackgroundColor; + + /** Returns customize activity animation enter resource id */ + public int getCustomEnterResId() { + return mCustomEnterResId; + } + + /** Returns customize activity animation exit resource id */ + public int getCustomExitResId() { + return mCustomExitResId; + } + + /** Returns customize activity animation background color */ + public int getCustomBackgroundColor() { + return mCustomBackgroundColor; + } + + CustomActivityTransition() {} + + CustomActivityTransition(Parcel in) { + mCustomEnterResId = in.readInt(); + mCustomExitResId = in.readInt(); + mCustomBackgroundColor = in.readInt(); + } + + /** Add customized activity animation attributes */ + public void addCustomActivityTransition( + int enterResId, int exitResId, int backgroundColor) { + mCustomEnterResId = enterResId; + mCustomExitResId = exitResId; + mCustomBackgroundColor = backgroundColor; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mCustomEnterResId); + dest.writeInt(mCustomExitResId); + dest.writeInt(mCustomBackgroundColor); + } + + @NonNull + public static final Creator CREATOR = + new Creator() { + @Override + public CustomActivityTransition createFromParcel(Parcel in) { + return new CustomActivityTransition(in); + } + + @Override + public CustomActivityTransition[] newArray(int size) { + return new CustomActivityTransition[size]; + } + }; + } + } + + /** + * An animation root in a transition. There is one of these for each display that contains + * participants. It will be placed, in z-order, right above the top-most participant and at the + * same position in the hierarchy. As a result, if all participants are animating within a part + * of the screen, the root-leash will only be in that part of the screen. In these cases, it's + * relative position (from the screen) is stored in {@link Root#getOffset}. + */ + public static final class Root implements Parcelable { + private final int mDisplayId; + private final SurfaceControl mLeash; + private final Point mOffset = new Point(); + + public Root(int displayId, @NonNull SurfaceControl leash, int offsetLeft, int offsetTop) { + mDisplayId = displayId; + mLeash = leash; + mOffset.set(offsetLeft, offsetTop); + } + + private Root(Parcel in) { + mDisplayId = in.readInt(); + mLeash = new SurfaceControl(); + mLeash.readFromParcel(in); + mLeash.setUnreleasedWarningCallSite("TransitionInfo.Root"); + mOffset.readFromParcel(in); + } + + private Root localRemoteCopy() { + return new Root( + mDisplayId, new SurfaceControl(mLeash, "localRemote"), mOffset.x, mOffset.y); + } + + /** + * @return the id of the display this root is on. + */ + public int getDisplayId() { + return mDisplayId; + } + + /** + * @return the root's leash. Surfaces should be parented to this while animating. + */ + @NonNull + public SurfaceControl getLeash() { + return mLeash; + } + + /** + * @return the offset (relative to its screen) of the root leash. + */ + @NonNull + public Point getOffset() { + return mOffset; + } + + /** @hide */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mDisplayId); + mLeash.writeToParcel(dest, flags); + mOffset.writeToParcel(dest, flags); + } + + @NonNull + public static final Creator CREATOR = + new Creator() { + @Override + public Root createFromParcel(Parcel in) { + return new Root(in); + } + + @Override + public Root[] newArray(int size) { + return new Root[size]; + } + }; + + /** @hide */ + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return mDisplayId + "@" + mOffset + ":" + mLeash; + } + } +} diff --git a/compatLib/src/main/java/app/lawnchair/compatlib/ActivityManagerCompat.java b/compatLib/src/main/java/app/lawnchair/compatlib/ActivityManagerCompat.java index 6f7881d2b87..349bb66204b 100644 --- a/compatLib/src/main/java/app/lawnchair/compatlib/ActivityManagerCompat.java +++ b/compatLib/src/main/java/app/lawnchair/compatlib/ActivityManagerCompat.java @@ -1,17 +1,40 @@ package app.lawnchair.compatlib; +import android.app.Activity; import android.app.ActivityManager; import android.content.Intent; -import android.os.RemoteException; -import android.view.IRecentsAnimationRunner; - +import android.window.TaskSnapshot; +import androidx.annotation.Nullable; import java.util.List; public abstract class ActivityManagerCompat { - public abstract void startRecentsActivity(Intent intent, long eventTime, RecentsAnimationRunnerStub runner) throws RemoteException; + public static final int NUM_RECENT_ACTIVITIES_REQUEST = 3; + + public abstract void invalidateHomeTaskSnapshot(final Activity homeActivity); + + /** + * Called only in S+ platform + * + * @param taskId + * @param isLowResolution + * @param takeSnapshotIfNeeded + * @return + */ + @Nullable + public TaskSnapshot getTaskSnapshot( + int taskId, boolean isLowResolution, boolean takeSnapshotIfNeeded) { + return null; + } - public abstract ActivityManager.RunningTaskInfo getRunningTask(boolean filterOnlyVisibleRecents); + public abstract void startRecentsActivity( + Intent intent, long eventTime, RecentsAnimationRunnerCompat runnerCompat); + + public abstract ActivityManager.RunningTaskInfo getRunningTask( + boolean filterOnlyVisibleRecents); public abstract List getRecentTasks(int numTasks, int userId); + + public abstract ActivityManager.RunningTaskInfo[] getRunningTasks( + boolean filterOnlyVisibleRecents); } diff --git a/compatLib/src/main/java/app/lawnchair/compatlib/ActivityOptionsCompat.java b/compatLib/src/main/java/app/lawnchair/compatlib/ActivityOptionsCompat.java new file mode 100644 index 00000000000..8ab4d41f62d --- /dev/null +++ b/compatLib/src/main/java/app/lawnchair/compatlib/ActivityOptionsCompat.java @@ -0,0 +1,20 @@ +package app.lawnchair.compatlib; + +import android.app.ActivityOptions; +import android.content.Context; +import android.os.Handler; +import android.view.RemoteAnimationAdapter; + +public abstract class ActivityOptionsCompat { + public abstract ActivityOptions makeCustomAnimation( + Context context, + int enterResId, + int exitResId, + final Runnable callback, + final Handler callbackHandler); + + public abstract ActivityOptions makeRemoteAnimation( + RemoteAnimationAdapter remoteAnimationAdapter, + Object remoteTransition, + String debugName); +} diff --git a/compatLib/src/main/java/app/lawnchair/compatlib/QuickstepCompatFactory.java b/compatLib/src/main/java/app/lawnchair/compatlib/QuickstepCompatFactory.java index 51c263a6b4f..e40b788ac82 100644 --- a/compatLib/src/main/java/app/lawnchair/compatlib/QuickstepCompatFactory.java +++ b/compatLib/src/main/java/app/lawnchair/compatlib/QuickstepCompatFactory.java @@ -1,10 +1,5 @@ package app.lawnchair.compatlib; -import android.graphics.Bitmap; -import android.graphics.Rect; -import android.view.AppTransitionAnimationSpec; -import android.view.IRemoteAnimationRunner; - import androidx.annotation.NonNull; public abstract class QuickstepCompatFactory { @@ -12,7 +7,9 @@ public abstract class QuickstepCompatFactory { @NonNull public abstract ActivityManagerCompat getActivityManagerCompat(); - public abstract IRemoteAnimationRunner.Stub wrapRemoteAnimationRunnerStub(RemoteAnimationRunnerStub compatStub); + @NonNull + public abstract ActivityOptionsCompat getActivityOptionsCompat(); - public abstract AppTransitionAnimationSpec createAppTransitionAnimationSpec(int taskId, Bitmap buffer, Rect rect); + @NonNull + public abstract RemoteTransitionCompat getRemoteTransitionCompat(); } diff --git a/compatLib/src/main/java/app/lawnchair/compatlib/RecentsAnimationRunnerCompat.java b/compatLib/src/main/java/app/lawnchair/compatlib/RecentsAnimationRunnerCompat.java new file mode 100644 index 00000000000..42ffcceda78 --- /dev/null +++ b/compatLib/src/main/java/app/lawnchair/compatlib/RecentsAnimationRunnerCompat.java @@ -0,0 +1,28 @@ +package app.lawnchair.compatlib; + +import android.graphics.Rect; +import android.view.IRecentsAnimationController; +import android.view.RemoteAnimationTarget; +import android.window.TaskSnapshot; + +public interface RecentsAnimationRunnerCompat { + + void onAnimationStart( + IRecentsAnimationController controller, + RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, + Rect homeContentInsets, + Rect minimizedHomeBounds); + + /** Called only in T platform */ + void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots); + + /** Called only in Q/R/S platform */ + void onAnimationCanceled(Object taskSnapshot); + + /** Called only in R/S platform */ + void onTaskAppeared(RemoteAnimationTarget app); + + /** Called only in T+ platform */ + void onTasksAppeared(RemoteAnimationTarget[] apps); +} diff --git a/compatLib/src/main/java/app/lawnchair/compatlib/RecentsAnimationRunnerStub.java b/compatLib/src/main/java/app/lawnchair/compatlib/RecentsAnimationRunnerStub.java deleted file mode 100644 index ef05ee2580e..00000000000 --- a/compatLib/src/main/java/app/lawnchair/compatlib/RecentsAnimationRunnerStub.java +++ /dev/null @@ -1,15 +0,0 @@ -package app.lawnchair.compatlib; - -import android.graphics.Rect; -import android.view.IRecentsAnimationController; -import android.view.RemoteAnimationTarget; - -public interface RecentsAnimationRunnerStub { - void onAnimationStart(IRecentsAnimationController controller, - RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, - Rect homeContentInsets, Rect minimizedHomeBounds); - - void onAnimationCanceled(Object taskSnapshot); - - void onTaskAppeared(RemoteAnimationTarget app); -} diff --git a/compatLib/src/main/java/app/lawnchair/compatlib/RemoteAnimationRunnerStub.java b/compatLib/src/main/java/app/lawnchair/compatlib/RemoteAnimationRunnerStub.java deleted file mode 100644 index fc513389cb9..00000000000 --- a/compatLib/src/main/java/app/lawnchair/compatlib/RemoteAnimationRunnerStub.java +++ /dev/null @@ -1,13 +0,0 @@ -package app.lawnchair.compatlib; - -import android.view.RemoteAnimationTarget; -import android.view.IRemoteAnimationFinishedCallback; - -public interface RemoteAnimationRunnerStub { - - void onAnimationStart(int transit, RemoteAnimationTarget[] apps, - RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, - final IRemoteAnimationFinishedCallback finishedCallback); - - void onAnimationCancelled(); -} diff --git a/compatLib/src/main/java/app/lawnchair/compatlib/RemoteTransitionCompat.java b/compatLib/src/main/java/app/lawnchair/compatlib/RemoteTransitionCompat.java new file mode 100644 index 00000000000..2fc96731fc7 --- /dev/null +++ b/compatLib/src/main/java/app/lawnchair/compatlib/RemoteTransitionCompat.java @@ -0,0 +1,15 @@ +package app.lawnchair.compatlib; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.IApplicationThread; +import android.window.IRemoteTransition; +import android.window.RemoteTransition; + +public abstract class RemoteTransitionCompat { + + public abstract RemoteTransition getRemoteTransition( + @NonNull IRemoteTransition remoteTransition, + @Nullable IApplicationThread appThread, + @Nullable String debugName); +} diff --git a/compatLib/src/main/java/app/lawnchair/compatlib/RemoteTransitionStub.java b/compatLib/src/main/java/app/lawnchair/compatlib/RemoteTransitionStub.java deleted file mode 100644 index 6f96e17e32f..00000000000 --- a/compatLib/src/main/java/app/lawnchair/compatlib/RemoteTransitionStub.java +++ /dev/null @@ -1,31 +0,0 @@ -package app.lawnchair.compatlib; - -import android.os.IBinder; -import android.view.SurfaceControl; -import android.window.IRemoteTransitionFinishedCallback; -import android.window.TransitionInfo; - -public interface RemoteTransitionStub { - /** - * Starts a transition animation. Once complete, the implementation should call - * `finishCallback`. - * - * @param token An identifier for the transition that should be animated. - */ - void startAnimation(IBinder token, TransitionInfo info, SurfaceControl.Transaction t, - IRemoteTransitionFinishedCallback finishCallback); - - /** - * Attempts to merge a transition animation into the animation that is currently - * being played by this remote. If merge is not possible/supported, this should be a no-op. - * If it *is* merged, the implementation should call `finishCallback` immediately. - * - * @param transition An identifier for the transition that wants to be merged. - * @param mergeTarget The transition that is currently being animated by this remote. - * If it can be merged, call `finishCallback`; otherwise, do - * nothing. - */ - void mergeAnimation(IBinder transition, TransitionInfo info, - SurfaceControl.Transaction t, IBinder mergeTarget, - IRemoteTransitionFinishedCallback finishCallback); -} diff --git a/compatLib/src/main/java/app/lawnchair/compatlib/TaskSnapshotCompat.java b/compatLib/src/main/java/app/lawnchair/compatlib/TaskSnapshotCompat.java deleted file mode 100644 index 9e758fc79cb..00000000000 --- a/compatLib/src/main/java/app/lawnchair/compatlib/TaskSnapshotCompat.java +++ /dev/null @@ -1,4 +0,0 @@ -package app.lawnchair.compatlib; - -public class TaskSnapshotCompat { -} diff --git a/compatLib/src/main/java/app/lawnchair/compatlib/utils/BitmapUtil.java b/compatLib/src/main/java/app/lawnchair/compatlib/utils/BitmapUtil.java new file mode 100644 index 00000000000..0fca337b13f --- /dev/null +++ b/compatLib/src/main/java/app/lawnchair/compatlib/utils/BitmapUtil.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2020 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 app.lawnchair.compatlib.utils; + +import android.graphics.Bitmap; +import android.graphics.ColorSpace; +import android.graphics.ParcelableColorSpace; +import android.hardware.HardwareBuffer; +import android.os.Bundle; +import java.util.Objects; + +/** Utils for working with Bitmaps. */ +public final class BitmapUtil { + private static final String KEY_BUFFER = "bitmap_util_buffer"; + private static final String KEY_COLOR_SPACE = "bitmap_util_color_space"; + + private BitmapUtil() {} + + /** + * Creates a Bundle that represents the given Bitmap. + * + *

    The Bundle will contain a wrapped version of the Bitmaps HardwareBuffer, so will avoid + * copies when passing across processes, only pass to processes you trust. + * + *

    Returns a new Bundle rather than modifying an exiting one to avoid key collisions, the + * returned Bundle should be treated as a standalone object. + * + * @param bitmap to convert to bundle + * @return a Bundle representing the bitmap, should only be parsed by {@link + * #bundleToHardwareBitmap(Bundle)} + */ + public static Bundle hardwareBitmapToBundle(Bitmap bitmap) { + if (bitmap.getConfig() != Bitmap.Config.HARDWARE) { + throw new IllegalArgumentException( + "Passed bitmap must have hardware config, found: " + bitmap.getConfig()); + } + // Bitmap assumes SRGB for null color space + ParcelableColorSpace colorSpace = + bitmap.getColorSpace() == null + ? new ParcelableColorSpace(ColorSpace.get(ColorSpace.Named.SRGB)) + : new ParcelableColorSpace(bitmap.getColorSpace()); + Bundle bundle = new Bundle(); + bundle.putParcelable(KEY_BUFFER, bitmap.getHardwareBuffer()); + bundle.putParcelable(KEY_COLOR_SPACE, colorSpace); + return bundle; + } + + /** + * Extracts the Bitmap added to a Bundle with {@link #hardwareBitmapToBundle(Bitmap)} .} + * + *

    This Bitmap contains the HardwareBuffer from the original caller, be careful passing this + * Bitmap on to any other source. + * + * @param bundle containing the bitmap + * @return a hardware Bitmap + */ + public static Bitmap bundleToHardwareBitmap(Bundle bundle) { + if (!bundle.containsKey(KEY_BUFFER) || !bundle.containsKey(KEY_COLOR_SPACE)) { + throw new IllegalArgumentException("Bundle does not contain a hardware bitmap"); + } + HardwareBuffer buffer = bundle.getParcelable(KEY_BUFFER); + ParcelableColorSpace colorSpace = bundle.getParcelable(KEY_COLOR_SPACE); + return Bitmap.wrapHardwareBuffer( + Objects.requireNonNull(buffer), colorSpace.getColorSpace()); + } +} diff --git a/compatLibVR/src/main/java/app/lawnchair/compatlib/eleven/QuickstepCompatFactoryVR.java b/compatLibVR/src/main/java/app/lawnchair/compatlib/eleven/QuickstepCompatFactoryVR.java deleted file mode 100644 index 87dc7ef5ab0..00000000000 --- a/compatLibVR/src/main/java/app/lawnchair/compatlib/eleven/QuickstepCompatFactoryVR.java +++ /dev/null @@ -1,47 +0,0 @@ -package app.lawnchair.compatlib.eleven; - -import android.graphics.Bitmap; -import android.graphics.Rect; -import android.os.RemoteException; -import android.view.AppTransitionAnimationSpec; -import android.view.IRemoteAnimationFinishedCallback; -import android.view.IRemoteAnimationRunner; -import android.view.RemoteAnimationTarget; - -import androidx.annotation.NonNull; - -import app.lawnchair.compatlib.ActivityManagerCompat; -import app.lawnchair.compatlib.QuickstepCompatFactory; -import app.lawnchair.compatlib.RemoteAnimationRunnerStub; - -public class QuickstepCompatFactoryVR extends QuickstepCompatFactory { - - @NonNull - @Override - public ActivityManagerCompat getActivityManagerCompat() { - return new ActivityManagerCompatVR(); - } - - @Override - public IRemoteAnimationRunner.Stub wrapRemoteAnimationRunnerStub(RemoteAnimationRunnerStub compatStub) { - return new IRemoteAnimationRunner.Stub() { - @Override - public void onAnimationStart(RemoteAnimationTarget[] apps, - RemoteAnimationTarget[] wallpapers, - final IRemoteAnimationFinishedCallback finishedCallback) { - compatStub.onAnimationStart(0 /* transit */, apps, wallpapers, null, finishedCallback); - } - - @Override - public void onAnimationCancelled() { - compatStub.onAnimationCancelled(); - } - }; - } - - @Override - public AppTransitionAnimationSpec createAppTransitionAnimationSpec(int taskId, Bitmap buffer, Rect rect) { - return new AppTransitionAnimationSpec(taskId, - buffer != null ? buffer.createGraphicBufferHandle() : null, rect); - } -} diff --git a/compatLibVS/src/main/java/app/lawnchair/compatlib/twelve/ActivityManagerCompatVS.java b/compatLibVS/src/main/java/app/lawnchair/compatlib/twelve/ActivityManagerCompatVS.java deleted file mode 100644 index 6b8e0e0824d..00000000000 --- a/compatLibVS/src/main/java/app/lawnchair/compatlib/twelve/ActivityManagerCompatVS.java +++ /dev/null @@ -1,66 +0,0 @@ -package app.lawnchair.compatlib.twelve; - -import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; -import static android.app.ActivityTaskManager.getService; - -import android.app.ActivityManager; -import android.app.ActivityTaskManager; -import android.content.Intent; -import android.graphics.Rect; -import android.os.RemoteException; -import android.view.IRecentsAnimationController; -import android.view.IRecentsAnimationRunner; -import android.view.RemoteAnimationTarget; -import android.window.TaskSnapshot; - -import java.util.List; - -import app.lawnchair.compatlib.ActivityManagerCompat; -import app.lawnchair.compatlib.RecentsAnimationRunnerStub; - -public class ActivityManagerCompatVS extends ActivityManagerCompat { - - private final ActivityTaskManager mAtm = ActivityTaskManager.getInstance(); - - @Override - public void startRecentsActivity(Intent intent, long eventTime, RecentsAnimationRunnerStub runner) throws RemoteException { - IRecentsAnimationRunner wrappedRunner = null; - if (runner != null) { - wrappedRunner = new IRecentsAnimationRunner.Stub() { - @Override - public void onAnimationStart(IRecentsAnimationController controller, - RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, - Rect homeContentInsets, Rect minimizedHomeBounds) { - runner.onAnimationStart(controller, apps, wallpapers, homeContentInsets, minimizedHomeBounds); - } - - @Override - public void onAnimationCanceled(TaskSnapshot taskSnapshot) { - runner.onAnimationCanceled(taskSnapshot); - } - - @Override - public void onTaskAppeared(RemoteAnimationTarget app) { - runner.onTaskAppeared(app); - } - }; - } - getService().startRecentsActivity(intent, eventTime, wrappedRunner); - } - - @Override - public ActivityManager.RunningTaskInfo getRunningTask(boolean filterOnlyVisibleRecents) { - // Note: The set of running tasks from the system is ordered by recency - List tasks = - mAtm.getTasks(1, filterOnlyVisibleRecents); - if (tasks.isEmpty()) { - return null; - } - return tasks.get(0); - } - - @Override - public List getRecentTasks(int numTasks, int userId) { - return mAtm.getRecentTasks(numTasks, RECENT_IGNORE_UNAVAILABLE, userId); - } -} diff --git a/compatLibVS/src/main/java/app/lawnchair/compatlib/twelve/QuickstepCompatFactoryVS.java b/compatLibVS/src/main/java/app/lawnchair/compatlib/twelve/QuickstepCompatFactoryVS.java deleted file mode 100644 index d55ef553965..00000000000 --- a/compatLibVS/src/main/java/app/lawnchair/compatlib/twelve/QuickstepCompatFactoryVS.java +++ /dev/null @@ -1,55 +0,0 @@ -package app.lawnchair.compatlib.twelve; - -import android.graphics.Bitmap; -import android.graphics.Rect; -import android.os.IBinder; -import android.os.RemoteException; -import android.view.AppTransitionAnimationSpec; -import android.view.IRemoteAnimationFinishedCallback; -import android.view.IRemoteAnimationRunner; -import android.view.RemoteAnimationTarget; -import android.view.SurfaceControl; -import android.window.IRemoteTransition; -import android.window.IRemoteTransitionFinishedCallback; -import android.window.TransitionInfo; - -import androidx.annotation.NonNull; - -import app.lawnchair.compatlib.ActivityManagerCompat; -import app.lawnchair.compatlib.QuickstepCompatFactory; -import app.lawnchair.compatlib.RemoteAnimationRunnerStub; -import app.lawnchair.compatlib.RemoteTransitionStub; - -public class QuickstepCompatFactoryVS extends QuickstepCompatFactory { - - @NonNull - @Override - public ActivityManagerCompat getActivityManagerCompat() { - return new ActivityManagerCompatVS(); - } - - @Override - public IRemoteAnimationRunner.Stub wrapRemoteAnimationRunnerStub(RemoteAnimationRunnerStub compatStub) { - return new IRemoteAnimationRunner.Stub() { - @Override - public void onAnimationStart(int transit, - RemoteAnimationTarget[] apps, - RemoteAnimationTarget[] wallpapers, - RemoteAnimationTarget[] nonApps, - final IRemoteAnimationFinishedCallback finishedCallback) { - compatStub.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback); - } - - @Override - public void onAnimationCancelled() { - compatStub.onAnimationCancelled(); - } - }; - } - - @Override - public AppTransitionAnimationSpec createAppTransitionAnimationSpec(int taskId, Bitmap buffer, Rect rect) { - return new AppTransitionAnimationSpec(taskId, - buffer != null ? buffer.getHardwareBuffer() : null, rect); - } -} diff --git a/lawnchair/res/values-v30/config.xml b/lawnchair/res/values-v30/config.xml new file mode 100644 index 00000000000..0186a0f4bc5 --- /dev/null +++ b/lawnchair/res/values-v30/config.xml @@ -0,0 +1,4 @@ + + + 60dp + \ No newline at end of file diff --git a/lawnchair/res/values/config.xml b/lawnchair/res/values/config.xml index 29e5a2a8a95..2283cb51d8c 100644 --- a/lawnchair/res/values/config.xml +++ b/lawnchair/res/values/config.xml @@ -93,6 +93,7 @@ true false false + false false false false diff --git a/lawnchair/res/values/strings.xml b/lawnchair/res/values/strings.xml index 6fe180b0430..ee851778687 100644 --- a/lawnchair/res/values/strings.xml +++ b/lawnchair/res/values/strings.xml @@ -209,6 +209,8 @@ Pop-up Menu Show Lock Button Show System Settings Entry + Show Edit Button + Status Bar Show Status Bar diff --git a/lawnchair/src/app/lawnchair/LawnchairLauncher.kt b/lawnchair/src/app/lawnchair/LawnchairLauncher.kt index bb50b04c84f..d53e9211c18 100644 --- a/lawnchair/src/app/lawnchair/LawnchairLauncher.kt +++ b/lawnchair/src/app/lawnchair/LawnchairLauncher.kt @@ -20,12 +20,18 @@ import android.app.ActivityOptions import android.content.Context import android.content.Intent import android.graphics.Color +import android.graphics.drawable.Drawable import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.view.Display +import android.view.View import android.view.ViewTreeObserver import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsControllerCompat import androidx.lifecycle.lifecycleScope import app.lawnchair.LawnchairApp.Companion.showQuickstepWarningIfNecessary +import app.lawnchair.compat.LawnchairQuickstepCompat import app.lawnchair.factory.LawnchairWidgetHolder import app.lawnchair.gestures.GestureController import app.lawnchair.gestures.VerticalSwipeTouchController @@ -41,11 +47,13 @@ import app.lawnchair.util.getThemedIconPacksInstalled import app.lawnchair.util.unsafeLazy import com.android.launcher3.AbstractFloatingView import com.android.launcher3.BaseActivity +import com.android.launcher3.BubbleTextView import com.android.launcher3.GestureNavContract import com.android.launcher3.LauncherAppState import com.android.launcher3.LauncherState import com.android.launcher3.R import com.android.launcher3.Utilities +import com.android.launcher3.model.data.ItemInfo import com.android.launcher3.popup.SystemShortcut import com.android.launcher3.statemanager.StateManager import com.android.launcher3.uioverrides.QuickstepLauncher @@ -226,9 +234,9 @@ class LawnchairLauncher : QuickstepLauncher() { override fun makeDefaultActivityOptions(splashScreenStyle: Int): ActivityOptionsWrapper { val callbacks = RunnableList() - val options = if (Utilities.ATLEAST_P || Utilities.ATLEAST_Q) { + val options = if (!Utilities.ATLEAST_R) { ActivityOptions.makeBasic() - } else { + } else if (Utilities.ATLEAST_T) { ActivityOptions.makeCustomAnimation( this, 0, @@ -237,6 +245,9 @@ class LawnchairLauncher : QuickstepLauncher() { Executors.MAIN_EXECUTOR.handler, null, ) { _ -> callbacks.executeAllAndDestroy() } + } else { + LawnchairQuickstepCompat.activityOptionsCompat + .makeCustomAnimation(this, 0, 0, null, Executors.MAIN_EXECUTOR.handler) } if (Utilities.ATLEAST_T) { options.setSplashScreenStyle(splashScreenStyle) @@ -246,6 +257,46 @@ class LawnchairLauncher : QuickstepLauncher() { return ActivityOptionsWrapper(options, callbacks) } + override fun getActivityLaunchOptions(v: View?, item: ItemInfo?): ActivityOptionsWrapper { + return runCatching { + super.getActivityLaunchOptions(v, item) + }.getOrElse { + getActivityLaunchOptionsDefault(v, item) + } + } + + private fun getActivityLaunchOptionsDefault(v: View?, item: ItemInfo?): ActivityOptionsWrapper { + var left = 0 + var top = 0 + var width = v!!.measuredWidth + var height = v.measuredHeight + if (v is BubbleTextView) { + // Launch from center of icon, not entire view + val icon: Drawable? = v.icon + if (icon != null) { + val bounds = icon.bounds + left = (width - bounds.width()) / 2 + top = v.getPaddingTop() + width = bounds.width() + height = bounds.height() + } + } + val options = Utilities.allowBGLaunch( + ActivityOptions.makeClipRevealAnimation( + v, + left, + top, + width, + height, + ), + ) + options.setLaunchDisplayId( + if (v != null && v.display != null) v.display.displayId else Display.DEFAULT_DISPLAY, + ) + val callback = RunnableList() + return ActivityOptionsWrapper(options, callback) + } + override fun onResume() { super.onResume() restartIfPending() diff --git a/lawnchair/src/app/lawnchair/allapps/AllAppsSearchInput.kt b/lawnchair/src/app/lawnchair/allapps/AllAppsSearchInput.kt index f0359bc9c4e..2bbfabec6f0 100644 --- a/lawnchair/src/app/lawnchair/allapps/AllAppsSearchInput.kt +++ b/lawnchair/src/app/lawnchair/allapps/AllAppsSearchInput.kt @@ -13,6 +13,7 @@ import android.text.style.ForegroundColorSpan import android.util.AttributeSet import android.view.KeyEvent import android.view.View +import android.view.View.OnFocusChangeListener import android.view.ViewTreeObserver import android.widget.FrameLayout import android.widget.ImageButton @@ -33,7 +34,6 @@ import com.android.launcher3.R import com.android.launcher3.Utilities import com.android.launcher3.allapps.ActivityAllAppsContainerView import com.android.launcher3.allapps.AllAppsStore -import com.android.launcher3.allapps.AlphabeticalAppsList import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem import com.android.launcher3.allapps.SearchUiManager import com.android.launcher3.allapps.search.AllAppsSearchBarController @@ -64,7 +64,7 @@ class AllAppsSearchInput(context: Context, attrs: AttributeSet?) : Selection.setSelection(this, 0) } - private lateinit var apps: AlphabeticalAppsList<*> + private lateinit var apps: LawnchairAlphabeticalAppsList<*> private lateinit var appsView: ActivityAllAppsContainerView<*> private var focusedResultTitle = "" @@ -192,7 +192,7 @@ class AllAppsSearchInput(context: Context, attrs: AttributeSet?) : } override fun initializeSearch(appsView: ActivityAllAppsContainerView<*>) { - apps = appsView.searchResultList + apps = appsView.searchResultList as LawnchairAlphabeticalAppsList<*> this.appsView = appsView searchBarController.initialize( LawnchairSearchAlgorithm.create(context), diff --git a/lawnchair/src/app/lawnchair/allapps/LawnchairAlphabeticalAppsList.kt b/lawnchair/src/app/lawnchair/allapps/LawnchairAlphabeticalAppsList.kt index 3abe3fb6804..1b880539f70 100644 --- a/lawnchair/src/app/lawnchair/allapps/LawnchairAlphabeticalAppsList.kt +++ b/lawnchair/src/app/lawnchair/allapps/LawnchairAlphabeticalAppsList.kt @@ -1,6 +1,7 @@ package app.lawnchair.allapps import android.content.Context +import android.util.Log import androidx.lifecycle.lifecycleScope import app.lawnchair.launcher import app.lawnchair.preferences2.PreferenceManager2 @@ -21,12 +22,16 @@ class LawnchairAlphabeticalAppsList( where T : Context, T : ActivityContext { private var hiddenApps: Set = setOf() + private val prefs = PreferenceManager2.getInstance(context) init { - val prefs = PreferenceManager2.getInstance(context) - prefs.hiddenApps.onEach(launchIn = context.launcher.lifecycleScope) { - hiddenApps = it - onAppsUpdated() + try { + prefs.hiddenApps.onEach(launchIn = context.launcher.lifecycleScope) { + hiddenApps = it + onAppsUpdated() + } + } catch (t: Throwable) { + Log.w(TAG, "Failed initialize ignore: ", t) } } diff --git a/lawnchair/src/app/lawnchair/overview/TaskShortcutFactory.kt b/lawnchair/src/app/lawnchair/overview/TaskShortcutFactory.kt new file mode 100644 index 00000000000..a357cee0a23 --- /dev/null +++ b/lawnchair/src/app/lawnchair/overview/TaskShortcutFactory.kt @@ -0,0 +1,156 @@ +package app.lawnchair.overview + +import android.app.ActivityOptions +import android.graphics.Color +import android.graphics.Rect +import android.os.Handler +import android.os.Looper +import android.os.RemoteException +import android.util.Log +import android.view.Display +import android.view.View +import android.view.WindowManagerGlobal +import app.lawnchair.compat.LawnchairQuickstepCompat +import app.lawnchair.compatlib.eleven.WindowManagerCompatVR +import com.android.launcher3.BaseDraggingActivity +import com.android.launcher3.R +import com.android.launcher3.logging.StatsLogManager.LauncherEvent +import com.android.launcher3.popup.SystemShortcut +import com.android.quickstep.TaskShortcutFactory +import com.android.quickstep.views.RecentsView +import com.android.quickstep.views.TaskThumbnailView +import com.android.quickstep.views.TaskView +import com.android.quickstep.views.TaskView.TaskIdAttributeContainer +import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat +import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture +import com.android.systemui.shared.recents.view.RecentsTransition +import com.android.systemui.shared.system.ActivityManagerWrapper + +object TaskShortcutFactory { + + class LegacySplitSystemShortcut( + iconRes: Int, + textRes: Int, + activity: BaseDraggingActivity, + taskContainer: TaskIdAttributeContainer, + private val mLauncherEvent: LauncherEvent, + ) : SystemShortcut( + iconRes, + textRes, + activity, + taskContainer.itemInfo, + taskContainer.taskView, + ) { + private val mHandler: Handler = Handler(Looper.getMainLooper()) + private val mRecentsView: RecentsView<*, *> = activity.getOverviewPanel() + private val mThumbnailView: TaskThumbnailView = taskContainer.thumbnailView + private val mTaskView: TaskView = taskContainer.taskView + + private fun makeLaunchOptions(activity: BaseDraggingActivity): ActivityOptions? { + val navBarPosition: Int = WindowManagerCompatVR.getNavBarPosition(activity.displayId) + if (navBarPosition == WindowManagerCompatVR.NAV_BAR_POS_INVALID) { + return null + } + val options = ActivityOptions.makeBasic() + options.launchWindowingMode = WindowManagerCompatVR.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY + return options + } + + override fun onClick(view: View) { + val taskKey = mTaskView.task!!.key + val taskId = taskKey.id + dismissTaskMenuView(mTarget) + val options = makeLaunchOptions(mTarget!!) + if (options != null && ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId, options)) { + val animStartedListener = + Runnable { mRecentsView.dismissTask(mTaskView, false, false) } + val position = IntArray(2) + mThumbnailView.getLocationOnScreen(position) + val width = (mThumbnailView.width * mTaskView.scaleX).toInt() + val height = (mThumbnailView.height * mTaskView.scaleY).toInt() + val taskBounds = Rect( + position[0], + position[1], + position[0] + width, + position[1] + height, + ) + + // Take the thumbnail of the task without a scrim and apply it back after + val alpha = mThumbnailView.dimAlpha + mThumbnailView.dimAlpha = 0f + val thumbnail = RecentsTransition.drawViewIntoHardwareBitmap( + taskBounds.width(), + taskBounds.height(), + mThumbnailView, + 1f, + Color.BLACK, + ) + mThumbnailView.dimAlpha = alpha + val future: AppTransitionAnimationSpecsFuture = object : AppTransitionAnimationSpecsFuture(mHandler) { + override fun composeSpecs(): List { + return listOf( + AppTransitionAnimationSpecCompat( + taskId, + thumbnail, + taskBounds, + ), + ) + } + } + try { + WindowManagerGlobal.getWindowManagerService() + .overridePendingAppTransitionMultiThumbFuture( + future.future, + RecentsTransition.wrapStartedListener( + mHandler, + animStartedListener, + ), + true, + taskKey.displayId, + ) + } catch (e: RemoteException) { + Log.w("TAG", "Failed to override pending app transition (multi-thumbnail future): ", e) + } + mTarget.statsLogManager.logger().withItemInfo(mTaskView.itemInfo).log(mLauncherEvent) + } + } + } + + @JvmField + var legacySplit: TaskShortcutFactory = object : TaskShortcutFactory { + + override fun getShortcuts( + activity: BaseDraggingActivity, + taskContainer: TaskIdAttributeContainer, + ): List>? { + val task = taskContainer.task + if (!task.isDockable) { + return null + } + return if (isAvailable(activity, task.key.displayId)) { + listOf( + LegacySplitSystemShortcut( + R.drawable.ic_split_vertical, + R.string.recent_task_option_split_screen, + activity, + taskContainer, + LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_SPLIT_SCREEN_TAP, + ), + ) + } else { + null + } + } + + private fun isAvailable(activity: BaseDraggingActivity, displayId: Int): Boolean { + return if (LawnchairQuickstepCompat.ATLEAST_T) { + false + } else { + ( + !activity.deviceProfile.isMultiWindowMode && + (displayId == -1 || displayId == Display.DEFAULT_DISPLAY) + ) + } + } + } +} diff --git a/lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt b/lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt index 66dc1de7d98..da1d2294536 100644 --- a/lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt +++ b/lawnchair/src/app/lawnchair/preferences2/PreferenceManager2.kt @@ -212,6 +212,12 @@ class PreferenceManager2 private constructor(private val context: Context) : Pre onSet = { reloadHelper.reloadGrid() }, ) + val editHomeScreenButtonOnPopUp = preference( + key = booleanPreferencesKey(name = "edit_home_screen_on_popup"), + defaultValue = context.resources.getBoolean(R.bool.config_default_edit_home_screen_on_popup), + onSet = { reloadHelper.reloadGrid() }, + ) + val showSystemSettingsEntryOnPopUp = preference( key = booleanPreferencesKey(name = "show_system_settings_entry_on_popup"), defaultValue = context.resources.getBoolean(R.bool.config_default_show_system_settings_entry_on_popup), diff --git a/lawnchair/src/app/lawnchair/search/LawnchairDeviceSearchAlgorithm.kt b/lawnchair/src/app/lawnchair/search/LawnchairDeviceSearchAlgorithm.kt index 6629d2e39cb..e9f7b6ce8a6 100644 --- a/lawnchair/src/app/lawnchair/search/LawnchairDeviceSearchAlgorithm.kt +++ b/lawnchair/src/app/lawnchair/search/LawnchairDeviceSearchAlgorithm.kt @@ -9,6 +9,7 @@ import android.content.Context import android.os.Bundle import android.os.Handler import androidx.core.os.bundleOf +import app.lawnchair.compat.LawnchairQuickstepCompat import app.lawnchair.preferences.PreferenceChangeListener import app.lawnchair.preferences.PreferenceManager import app.lawnchair.util.requireSystemService @@ -66,6 +67,7 @@ class LawnchairDeviceSearchAlgorithm(context: Context) : } private fun createSearchSession() { + if (LawnchairQuickstepCompat.ATLEAST_U) return Executors.UI_HELPER_EXECUTOR.execute { searchSession?.destroy() val idp = LauncherAppState.getIDP(context) @@ -122,6 +124,7 @@ class LawnchairDeviceSearchAlgorithm(context: Context) : companion object { fun checkSearchCompatibility(context: Context) { + if (LawnchairQuickstepCompat.ATLEAST_U) return Executors.UI_HELPER_EXECUTOR.execute { val searchContext = SearchContext(1 or 2, 200, Bundle()) val searchManager = context.requireSystemService() diff --git a/lawnchair/src/app/lawnchair/ui/preferences/destinations/HomeScreenPreferences.kt b/lawnchair/src/app/lawnchair/ui/preferences/destinations/HomeScreenPreferences.kt index 6d4df296f27..44fd9a0fcf5 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/destinations/HomeScreenPreferences.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/destinations/HomeScreenPreferences.kt @@ -129,6 +129,11 @@ fun HomeScreenPreferences() { adapter = prefs2.showSystemSettingsEntryOnPopUp.getAdapter(), label = stringResource(id = R.string.show_system_settings_entry), ) + SwitchPreference( + adapter = prefs2.editHomeScreenButtonOnPopUp.getAdapter(), + label = stringResource(id = R.string.home_screen_edit_toggle_from_home_popup), + enabled = lockHomeScreenAdapter.state.value.not(), + ) } PreferenceGroup(heading = stringResource(id = R.string.status_bar_label)) { val showStatusBarAdapter = prefs2.showStatusBar.getAdapter() diff --git a/lawnchair/src/app/lawnchair/util/LawnchairWindowManagerProxy.kt b/lawnchair/src/app/lawnchair/util/LawnchairWindowManagerProxy.kt index 81cf1568652..2a424cecc87 100644 --- a/lawnchair/src/app/lawnchair/util/LawnchairWindowManagerProxy.kt +++ b/lawnchair/src/app/lawnchair/util/LawnchairWindowManagerProxy.kt @@ -8,12 +8,13 @@ import android.view.WindowManager import androidx.annotation.Keep import app.lawnchair.LawnchairApp import com.android.internal.policy.SystemBarUtils +import com.android.launcher3.Utilities import com.android.launcher3.util.WindowBounds import com.android.launcher3.util.window.CachedDisplayInfo import com.android.launcher3.util.window.WindowManagerProxy @Keep -class LawnchairWindowManagerProxy(context: Context) : WindowManagerProxy(true) { +class LawnchairWindowManagerProxy(context: Context) : WindowManagerProxy(Utilities.ATLEAST_T) { override fun getRotation(displayInfoContext: Context): Int { if (LawnchairApp.isAtleastT) { diff --git a/lawnchair/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java b/lawnchair/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java new file mode 100644 index 00000000000..c0f5ad3bd15 --- /dev/null +++ b/lawnchair/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java @@ -0,0 +1,202 @@ +/* + * 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.wm.shell.util; + +import android.annotation.IntDef; +import android.app.ActivityManager; +import android.app.WindowConfiguration; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.Arrays; +import java.util.List; + +/** + * Simple container for recent tasks. May contain either a single or pair of tasks. + */ +public class GroupedRecentTaskInfo implements Parcelable { + + public static final int TYPE_SINGLE = 1; + public static final int TYPE_SPLIT = 2; + public static final int TYPE_FREEFORM = 3; + + @IntDef(prefix = {"TYPE_"}, value = { + TYPE_SINGLE , + TYPE_SPLIT , + TYPE_FREEFORM + }) + public @interface GroupType { + } + + @NonNull + private final ActivityManager.RecentTaskInfo[] mTasks; + @Nullable + private final SplitBounds mSplitBounds; + @GroupType + private final int mType; + + /** + * Create new for a single task + */ + + public static GroupedRecentTaskInfo forSingleTask( + @NonNull ActivityManager.RecentTaskInfo task) { + return new GroupedRecentTaskInfo (new ActivityManager.RecentTaskInfo[]{task} , null , + TYPE_SINGLE); + } + + /** + * Create new for a pair of tasks in split screen + */ + public static GroupedRecentTaskInfo forSplitTasks(@NonNull ActivityManager.RecentTaskInfo task1 , + @NonNull ActivityManager.RecentTaskInfo task2 , @Nullable SplitBounds splitBounds) { + return new GroupedRecentTaskInfo (new ActivityManager.RecentTaskInfo[]{task1 , task2} , + splitBounds , TYPE_SPLIT); + } + + /** + * Create new for a group of freeform tasks + */ + public static GroupedRecentTaskInfo forFreeformTasks( + @NonNull ActivityManager.RecentTaskInfo... tasks) { + return new GroupedRecentTaskInfo (tasks , null , TYPE_FREEFORM); + } + + private GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo[] tasks , + @Nullable SplitBounds splitBounds , @GroupType int type) { + mTasks = tasks; + mSplitBounds = splitBounds; + mType = type; + } + + GroupedRecentTaskInfo(Parcel parcel) { + mTasks = parcel.createTypedArray(ActivityManager.RecentTaskInfo.CREATOR); + mSplitBounds = parcel.readTypedObject(SplitBounds.CREATOR); + mType = parcel.readInt(); + } + + /** + * Get primary {@link ActivityManager.RecentTaskInfo} + */ + @NonNull + public ActivityManager.RecentTaskInfo getTaskInfo1() { + return mTasks[0]; + } + + /** + * Get secondary {@link ActivityManager.RecentTaskInfo}. + *

    + * Used in split screen. + */ + @Nullable + public ActivityManager.RecentTaskInfo getTaskInfo2() { + if (mTasks.length > 1) { + return mTasks[1]; + } + return null; + } + + /** + * Get all {@link ActivityManager.RecentTaskInfo}s grouped together. + */ + @NonNull + public List getTaskInfoList() { + return Arrays.asList (mTasks); + } + + /** + * Return {@link SplitBounds} if this is a split screen entry or {@code null} + */ + @Nullable + public SplitBounds getSplitBounds() { + return mSplitBounds; + } + + /** + * Get type of this recents entry. One of {@link GroupType} + */ + @GroupType + public int getType() { + return mType; + } + + @Override + public String toString() { + StringBuilder taskString = new StringBuilder ( ); + for (int i = 0; i < mTasks.length; i++) { + if (i == 0) { + taskString.append ("Task"); + } else { + taskString.append (", Task"); + } + taskString.append (i + 1).append (": ").append (getTaskInfo (mTasks[i])); + } + if (mSplitBounds != null) { + taskString.append (", SplitBounds: ").append (mSplitBounds); + } + taskString.append (", Type="); + switch (mType) { + case TYPE_SINGLE: + taskString.append ("TYPE_SINGLE"); + break; + case TYPE_SPLIT: + taskString.append ("TYPE_SPLIT"); + break; + case TYPE_FREEFORM: + taskString.append ("TYPE_FREEFORM"); + break; + } + return taskString.toString(); + } + + private String getTaskInfo(ActivityManager.RecentTaskInfo taskInfo) { + if (taskInfo == null) { + return null; + } + return "id=" + taskInfo.taskId + + " baseIntent=" + (taskInfo.baseIntent != null + ? taskInfo.baseIntent.getComponent ( ) + : "null") + + " winMode=" + WindowConfiguration.windowingModeToString ( + taskInfo.getWindowingMode()); + } + + @Override + public void writeToParcel(Parcel parcel , int flags) { + parcel.writeTypedArray(mTasks , flags); + parcel.writeTypedObject(mSplitBounds , flags); + parcel.writeInt(mType); + } + + @Override + public int describeContents() { + return 0; + } + + public static final @android.annotation.NonNull Creator CREATOR = + new Creator ( ) { + public GroupedRecentTaskInfo createFromParcel(Parcel source) { + return new GroupedRecentTaskInfo(source); + } + + public GroupedRecentTaskInfo[] newArray(int size) { + return new GroupedRecentTaskInfo[size]; + } + }; +} \ No newline at end of file diff --git a/platform_frameworks_libs_systemui b/platform_frameworks_libs_systemui index 02aee0e36d5..5f57efed9ac 160000 --- a/platform_frameworks_libs_systemui +++ b/platform_frameworks_libs_systemui @@ -1 +1 @@ -Subproject commit 02aee0e36d501413267d1e5d0564e816bfe53686 +Subproject commit 5f57efed9ac2cab63a3bb5d82408fcacfbfe7ca5 diff --git a/prebuilts/libs/WindowManager-Shell-14.jar b/prebuilts/libs/WindowManager-Shell-14.jar index 7851c925b61..a1258b3f25b 100644 Binary files a/prebuilts/libs/WindowManager-Shell-14.jar and b/prebuilts/libs/WindowManager-Shell-14.jar differ diff --git a/prebuilts/libs/framework-10.jar b/prebuilts/libs/framework-10.jar new file mode 100644 index 00000000000..592572c90df Binary files /dev/null and b/prebuilts/libs/framework-10.jar differ diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java index 5d4e19dbcc3..f2d112b02e0 100644 --- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java +++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java @@ -127,6 +127,10 @@ public void onAnimationCancelled() { }); } + public void onAnimationCancelled(boolean isKeyguardOccluded) { + onAnimationCancelled(); + } + /** * Used by RemoteAnimationFactory implementations to run the actual animation and its lifecycle * callbacks. @@ -196,13 +200,19 @@ public void onAnimationEnd(Animator animation) { finish(); } }); + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.TIRAMISU) { + mAnimator.start(); + } + if (skipFirstFrame) { // Because t=0 has the app icon in its original spot, we can skip the // first frame and have the same movement one frame earlier. mAnimator.setCurrentPlayTime( Math.min(getSingleFrameMs(context), mAnimator.getTotalDuration())); } - mAnimator.start(); + if (Utilities.ATLEAST_U) { + mAnimator.start(); + } } } @@ -234,6 +244,11 @@ void onAnimationStart(int transit, RemoteAnimationTarget[] nonAppTargets, LauncherAnimationRunner.AnimationResult result); + @UiThread + default void onAnimationCancelled(boolean isKeyguardOccluded) { + onAnimationCancelled(); + } + /** * Called when the animation is cancelled. This can happen with or without * the create being called. diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 53c1ee6e7d2..ba2c4110969 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -164,6 +164,7 @@ import java.util.List; import app.lawnchair.LawnchairApp; +import app.lawnchair.compat.LawnchairQuickstepCompat; import app.lawnchair.icons.shape.IconShapeManager; import app.lawnchair.theme.color.ColorTokens; @@ -282,9 +283,11 @@ protected boolean removeEldestEntry(Entry> entry } }; - mStartingWindowListener.setTransitionManager(this); - SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener( - mStartingWindowListener); + if (Utilities.ATLEAST_T) { + mStartingWindowListener.setTransitionManager(this); + SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener( + mStartingWindowListener); + } } mOpeningXInterpolator = AnimationUtils.loadInterpolator(context, R.interpolator.app_open_x); @@ -333,10 +336,12 @@ public ActivityOptionsWrapper getActivityLaunchOptions(View v) { long statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION - STATUS_BAR_TRANSITION_PRE_DELAY; - ActivityOptions options = ActivityOptions.makeRemoteAnimation( + ActivityOptions options = LawnchairQuickstepCompat.getActivityOptionsCompat().makeRemoteAnimation( new RemoteAnimationAdapter(runner, duration, statusBarTransitionDelay), - new RemoteTransition(runner.toRemoteTransition(), - mLauncher.getIApplicationThread(), "QuickstepLaunch")); + LawnchairQuickstepCompat.getRemoteTransitionCompat().getRemoteTransition(runner.toRemoteTransition(), + mLauncher.getIApplicationThread(), "QuickstepLaunch"), + "Lawnchair"); + return new ActivityOptionsWrapper(options, onEndCallback); } @@ -457,7 +462,7 @@ private Rect getWindowTargetBounds(@NonNull RemoteAnimationTarget[] appTargets, if (target == null) return new Rect(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx); final Rect bounds = new Rect(target.screenSpaceBounds); - if (target.localBounds != null) { + if (Utilities.ATLEAST_R && target.localBounds != null) { bounds.set(target.localBounds); } else { bounds.offsetTo(target.position.x, target.position.y); @@ -864,7 +869,7 @@ public void onUpdate(float percent, boolean initOnly) { } else { tmpPos.set(target.position.x, target.position.y); } - final Rect crop = new Rect(target.screenSpaceBounds); + final Rect crop = new Rect(Utilities.ATLEAST_R ? target.screenSpaceBounds : target.sourceContainerBounds); crop.offsetTo(0, 0); if ((rotationChange % 2) == 1) { @@ -1107,9 +1112,7 @@ private ObjectAnimator getBackgroundAnimator() { * Registers remote animations used when closing apps to home screen. */ public void registerRemoteAnimations() { - if (SEPARATE_RECENTS_ACTIVITY.get()) { - return; - } + if (SEPARATE_RECENTS_ACTIVITY.get() || !Utilities.ATLEAST_S) return; if (hasControlRemoteAppTransitionPermission()) { RemoteAnimationDefinition definition = new RemoteAnimationDefinition(); addRemoteAnimations(definition); @@ -1147,17 +1150,16 @@ private void addRemoteAnimations(RemoteAnimationDefinition definition) { * Registers remote animations used when closing apps to home screen. */ public void registerRemoteTransitions() { - if (ENABLE_SHELL_TRANSITIONS && Utilities.ATLEAST_U) { + if (ENABLE_SHELL_TRANSITIONS && LawnchairQuickstepCompat.ATLEAST_U) { SystemUiProxy.INSTANCE.get(mLauncher).shareTransactionQueue(); } if (SEPARATE_RECENTS_ACTIVITY.get()) { return; } - if (!Utilities.ATLEAST_S) - return; + if (!LawnchairQuickstepCompat.ATLEAST_T) return; if (hasControlRemoteAppTransitionPermission()) { mWallpaperOpenTransitionRunner = createWallpaperOpenRunner(false /* fromUnlock */); - mLauncherOpenTransition = new RemoteTransition( + mLauncherOpenTransition = LawnchairQuickstepCompat.getRemoteTransitionCompat().getRemoteTransition( new LauncherAnimationRunner(mHandler, mWallpaperOpenTransitionRunner, false /* startAtFrontOfQueue */).toRemoteTransition(), mLauncher.getIApplicationThread(), "QuickstepLaunchHome"); @@ -1184,8 +1186,10 @@ public void registerRemoteTransitions() { public void onActivityDestroyed() { unregisterRemoteAnimations(); unregisterRemoteTransitions(); - mStartingWindowListener.setTransitionManager(null); - SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener(null); + if (Utilities.ATLEAST_T) { + mStartingWindowListener.setTransitionManager(null); + SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener(null); + } } private void unregisterRemoteAnimations() { @@ -1193,8 +1197,9 @@ private void unregisterRemoteAnimations() { return; } if (hasControlRemoteAppTransitionPermission()) { - mLauncher.unregisterRemoteAnimations(); - + if (Utilities.ATLEAST_R) { + mLauncher.unregisterRemoteAnimations(); + } // Also clear strong references to the runners registered with the remote // animation // definition so we don't have to wait for the system gc @@ -1212,8 +1217,7 @@ protected void unregisterRemoteTransitions() { return; } if (hasControlRemoteAppTransitionPermission()) { - if (mLauncherOpenTransition == null) - return; + if (mLauncherOpenTransition == null) return; SystemUiProxy.INSTANCE.get(mLauncher).unregisterRemoteTransition( mLauncherOpenTransition); mLauncherOpenTransition = null; @@ -1227,6 +1231,9 @@ protected void unregisterRemoteTransitions() { private boolean launcherIsATargetWithMode(RemoteAnimationTarget[] targets, int mode) { for (RemoteAnimationTarget target : targets) { + if (!Utilities.ATLEAST_S) { + return target.mode == mode && target.taskId == mLauncher.getTaskId(); + } if (target.mode == mode && target.taskInfo != null // Compare component name instead of task-id because transitions will promote // the target up to the root task while getTaskId returns the leaf. @@ -1277,7 +1284,7 @@ public void onAnimationStart(Animator animation) { RemoteAnimationTarget target = appTargets[i]; transaction.forSurface(target.leash) .setAlpha(1f) - .setWindowCrop(target.screenSpaceBounds) + .setWindowCrop(Utilities.ATLEAST_R ? target.screenSpaceBounds : target.sourceContainerBounds) .setCornerRadius(cornerRadius); } surfaceApplier.scheduleApply(transaction); @@ -1304,6 +1311,7 @@ private static int getRotationChange(RemoteAnimationTarget[] appTargets) { * app targets */ private @Nullable View findLauncherView(RemoteAnimationTarget[] appTargets) { + if (!Utilities.ATLEAST_S) return null; for (RemoteAnimationTarget appTarget : appTargets) { if (appTarget.mode == MODE_CLOSING) { View launcherView = findLauncherView(appTarget); @@ -2018,7 +2026,7 @@ public void onUpdate(RectF currentRectF, float progress) { RemoteAnimationTarget target = mAppTargets[i]; SurfaceProperties builder = transaction.forSurface(target.leash); - if (target.localBounds != null) { + if (Utilities.ATLEAST_R && target.localBounds != null) { mTmpPos.set(target.localBounds.left, target.localBounds.top); } else { mTmpPos.set(target.position.x, target.position.y); diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java index 619f19ad704..c6009fc000e 100644 --- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java +++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java @@ -86,6 +86,7 @@ import java.util.stream.IntStream; import app.lawnchair.LawnchairApp; +import app.lawnchair.compat.LawnchairQuickstepCompat; /** * Model delegate which loads prediction items @@ -259,8 +260,9 @@ protected void additionalSnapshotEvents(InstanceId snapshotInstanceId) { * atom. */ protected void registerSnapshotLoggingCallback() { - if (mStatsManager == null) { + if (mStatsManager == null || !LawnchairQuickstepCompat.ATLEAST_R) { Log.d(TAG, "Failed to get StatsManager"); + return; } try { @@ -330,7 +332,7 @@ public void destroy() { super.destroy(); mActive = false; StatsLogCompatManager.LOGS_CONSUMER.remove(mAppEventProducer); - if (mIsPrimaryInstance) { + if (mIsPrimaryInstance && LawnchairQuickstepCompat.ATLEAST_R) { mStatsManager.clearPullAtomCallback(SysUiStatsLog.LAUNCHER_LAYOUT_SNAPSHOT); } destroyPredictors(); diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java index 95559a7c87f..94a746a224a 100644 --- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java +++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java @@ -41,6 +41,8 @@ import java.io.PrintWriter; import java.util.function.Consumer; +import app.lawnchair.compat.LawnchairQuickstepCompat; + /** * Controls blur and wallpaper zoom, for the Launcher surface only. */ @@ -77,7 +79,7 @@ private void ensureDependencies() { mOnAttachListener = new View.OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View view) { - if (app.lawnchair.LawnchairApp.isAtleastT()) { + if (LawnchairQuickstepCompat.ATLEAST_S) { CrossWindowBlurListeners.getInstance().addListener(mLauncher.getMainExecutor(), mCrossWindowBlurListener); } @@ -114,7 +116,7 @@ public void dispose() { } private void removeSecondaryListeners() { - if (mCrossWindowBlurListener != null) { + if (mCrossWindowBlurListener != null && LawnchairQuickstepCompat.ATLEAST_S) { CrossWindowBlurListeners.getInstance().removeListener(mCrossWindowBlurListener); } if (mOpaquenessListener != null) { diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java index 4e834ec3efd..a23e1267211 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -239,9 +239,11 @@ private void updateTaskTransitionSpec(boolean taskbarIsHidden) { WindowManagerGlobal.getWindowManagerService().clearTaskTransitionSpec(); } else { // Adjust task transition spec to account for taskbar being visible - WindowManagerGlobal.getWindowManagerService().setTaskTransitionSpec( - new TaskTransitionSpec( - mLauncher.getColor(R.color.taskbar_background))); + if (Utilities.ATLEAST_U) { + WindowManagerGlobal.getWindowManagerService().setTaskTransitionSpec( + new TaskTransitionSpec( + mLauncher.getColor(R.color.taskbar_background))); + } } } catch (RemoteException e) { // This shouldn't happen but if it does task animations won't look good until the diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 39e3660cc24..2cb3db5bee6 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -645,7 +645,9 @@ public ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) this, 0, 0, Color.TRANSPARENT, Executors.MAIN_EXECUTOR.getHandler(), null, elapsedRealTime -> callbacks.executeAllAndDestroy()); - options.setSplashScreenStyle(splashScreenStyle); + if (Utilities.ATLEAST_T) { + options.setSplashScreenStyle(splashScreenStyle); + } Utilities.allowBGLaunch(options); return new ActivityOptionsWrapper(options, callbacks); } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java index e521154497e..560ac447698 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java @@ -34,6 +34,7 @@ import com.android.app.viewcapture.SettingsAwareViewCapture; import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.Utilities; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.MultiPropertyFactory; @@ -131,14 +132,18 @@ protected void onDestroy() { protected void onAttachedToWindow() { super.onAttachedToWindow(); getViewTreeObserver().addOnComputeInternalInsetsListener(mTaskbarInsetsComputer); - mViewCaptureCloseable = SettingsAwareViewCapture.getInstance(getContext()) - .startCapture(getRootView(), ".Taskbar"); + if (Utilities.ATLEAST_U) { + mViewCaptureCloseable = SettingsAwareViewCapture.getInstance(getContext()) + .startCapture(getRootView(), ".Taskbar"); + } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - mViewCaptureCloseable.close(); + if (Utilities.ATLEAST_U) { + mViewCaptureCloseable.close(); + } onDestroy(true); } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt index 45ce6aa51e9..a744e2b2a9c 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt @@ -77,7 +77,11 @@ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTas onTaskbarOrBubblebarWindowHeightOrInsetsChanged() context.addOnDeviceProfileChangeListener(deviceProfileChangeListener) - gestureNavSettingsObserver.registerForCallingUser() + try { + gestureNavSettingsObserver.registerForCallingUser() + } catch (t: Throwable) { + // Ignore + } } fun onDestroy() { @@ -161,19 +165,23 @@ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTas private fun getProvidedInsets(insetsRoundedCornerFlag: Int): Array { val navBarsFlag = (if (context.isGestureNav) FLAG_SUPPRESS_SCRIM else 0) or insetsRoundedCornerFlag - return arrayOf( + try { + return arrayOf( InsetsFrameProvider(insetsOwner, 0, navigationBars()) - .setFlags( - navBarsFlag, - FLAG_SUPPRESS_SCRIM or FLAG_INSETS_ROUNDED_CORNER - ), + .setFlags( + navBarsFlag, + FLAG_SUPPRESS_SCRIM or FLAG_INSETS_ROUNDED_CORNER + ), InsetsFrameProvider(insetsOwner, 0, tappableElement()), InsetsFrameProvider(insetsOwner, 0, mandatorySystemGestures()), InsetsFrameProvider(insetsOwner, INDEX_LEFT, systemGestures()) - .setSource(SOURCE_DISPLAY), + .setSource(SOURCE_DISPLAY), InsetsFrameProvider(insetsOwner, INDEX_RIGHT, systemGestures()) - .setSource(SOURCE_DISPLAY) - ) + .setSource(SOURCE_DISPLAY) + ) + }catch (t: Throwable) { + return emptyArray() + } } private fun setProviderInsets(provider: InsetsFrameProvider, gravity: Int) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java index e7024baf8d8..9435bb636df 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java @@ -18,6 +18,7 @@ import static android.content.Context.RECEIVER_NOT_EXPORTED; import static android.content.pm.PackageManager.FEATURE_PC; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING; @@ -54,6 +55,7 @@ import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherPrefs; +import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.taskbar.unfold.NonDestroyableScopedUnfoldTransitionProgressProvider; @@ -72,6 +74,8 @@ import java.io.PrintWriter; import java.util.StringJoiner; +import app.lawnchair.compat.LawnchairQuickstepCompat; + /** * Class to manage taskbar lifecycle */ @@ -207,8 +211,16 @@ public void onTransitionFinished() { @SuppressLint("WrongConstant") public TaskbarManager(TouchInteractionService service) { - Display display = service.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY); - mContext = service.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null); + Display display = + service.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY); + mContext = (LawnchairQuickstepCompat.ATLEAST_T) ? + service.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null) : + (LawnchairQuickstepCompat.ATLEAST_S) ? + service.createWindowContext(display, TYPE_APPLICATION_OVERLAY, null) : + (LawnchairQuickstepCompat.ATLEAST_R) ? + service.createWindowContext(TYPE_APPLICATION_OVERLAY, null) : + service; + mNavButtonController = new TaskbarNavButtonController(service, SystemUiProxy.INSTANCE.get(mContext), new Handler(), AssistUtils.newInstance(mContext)); @@ -217,8 +229,10 @@ public TaskbarManager(TouchInteractionService service) { @Override public void onConfigurationChanged(Configuration newConfig) { - Trace.instantForTrack(Trace.TRACE_TAG_APP, "TaskbarManager", - "onConfigurationChanged: " + newConfig); + if (LawnchairQuickstepCompat.ATLEAST_T) { + Trace.instantForTrack(Trace.TRACE_TAG_APP, "TaskbarManager", + "onConfigurationChanged: " + newConfig); + } debugWhyTaskbarNotDestroyed( "TaskbarManager#mComponentCallbacks.onConfigurationChanged: " + newConfig); DeviceProfile dp = mUserUnlocked diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java index cb1c7b56ec8..5de985db6ed 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java @@ -96,7 +96,9 @@ public boolean onInteraction(View view, PendingIntent pendingIntent, // Do nothing. } activityOptions.options.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR); + if (Utilities.ATLEAST_T) { + activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR); + } Utilities.allowBGLaunch(activityOptions.options); } diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 6ea84d0fae6..2a33485899c 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -16,7 +16,6 @@ package com.android.launcher3.uioverrides; import static android.app.ActivityTaskManager.INVALID_TASK_ID; -import static android.os.Trace.TRACE_TAG_APP; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE; import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED; @@ -67,11 +66,9 @@ import android.graphics.RectF; import android.hardware.display.DisplayManager; import android.media.permission.SafeCloseable; -import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.os.SystemProperties; -import android.os.Trace; import android.util.AttributeSet; import android.util.Log; import android.view.Display; @@ -86,7 +83,7 @@ import androidx.annotation.BinderThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; + import com.android.app.viewcapture.SettingsAwareViewCapture; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; @@ -186,6 +183,8 @@ import java.util.function.Predicate; import java.util.stream.Stream; +import app.lawnchair.compat.LawnchairQuickstepCompat; + public class QuickstepLauncher extends Launcher { private static final boolean TRACE_LAYOUTS = SystemProperties.getBoolean("persist.debug.trace_layouts", false); private static final String TRACE_RELAYOUT_CLASS = SystemProperties.get("persist.debug.trace_request_layout_class", @@ -1020,24 +1019,24 @@ public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInf ActivityOptionsWrapper activityOptions = mAppTransitionManager.hasControlRemoteAppTransitionPermission() ? mAppTransitionManager.getActivityLaunchOptions(v) : super.getActivityLaunchOptions(v, item); - if (mLastTouchUpTime > 0 && app.lawnchair.LawnchairApp.isAtleastT()) { + if (mLastTouchUpTime > 0 && LawnchairQuickstepCompat.ATLEAST_S) { activityOptions.options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_LAUNCHER, mLastTouchUpTime); } if (item != null && (item.animationType == DEFAULT_NO_ICON - || item.animationType == VIEW_BACKGROUND) && app.lawnchair.LawnchairApp.isAtleastT()) { + || item.animationType == VIEW_BACKGROUND) && LawnchairQuickstepCompat.ATLEAST_T) { activityOptions.options.setSplashScreenStyle( SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR); } else { - if (app.lawnchair.LawnchairApp.isAtleastT()) { + if (LawnchairQuickstepCompat.ATLEAST_T) { activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON); } } activityOptions.options.setLaunchDisplayId( (v != null && v.getDisplay() != null) ? v.getDisplay().getDisplayId() : Display.DEFAULT_DISPLAY); - if (app.lawnchair.LawnchairApp.isAtleastT()) { - Utilities.allowBGLaunch(activityOptions.options); + Utilities.allowBGLaunch(activityOptions.options); + if (LawnchairQuickstepCompat.ATLEAST_T) { addLaunchCookie(item, activityOptions.options); } return activityOptions; @@ -1046,11 +1045,13 @@ public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInf @Override public ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) { RunnableList callbacks = new RunnableList(); - ActivityOptions options = ActivityOptions.makeCustomAnimation( + ActivityOptions options = LawnchairQuickstepCompat.ATLEAST_R ? ActivityOptions.makeCustomAnimation( this, 0, 0, Color.TRANSPARENT, Executors.MAIN_EXECUTOR.getHandler(), null, - elapsedRealTime -> callbacks.executeAllAndDestroy()); - options.setSplashScreenStyle(splashScreenStyle); + elapsedRealTime -> callbacks.executeAllAndDestroy()) : ActivityOptions.makeBasic(); + if (LawnchairQuickstepCompat.ATLEAST_T) { + options.setSplashScreenStyle(splashScreenStyle); + } Utilities.allowBGLaunch(options); return new ActivityOptionsWrapper(options, callbacks); } diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 6d222a3364d..0f8eed17ba2 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -101,15 +101,12 @@ import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.logging.StatsLogManager; -import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.logging.StatsLogManager.StatsLogger; import com.android.launcher3.statehandlers.DesktopVisibilityController; import com.android.launcher3.statemanager.BaseState; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.taskbar.TaskbarThresholdUtils; import com.android.launcher3.taskbar.TaskbarUIController; -import com.android.launcher3.tracing.InputConsumerProto; -import com.android.launcher3.tracing.SwipeHandlerProto; import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter; import com.android.launcher3.util.DisplayController; @@ -157,6 +154,7 @@ import java.util.OptionalInt; import java.util.function.Consumer; +import app.lawnchair.compat.LawnchairQuickstepCompat; import app.lawnchair.util.CompatibilityKt; /** @@ -193,19 +191,18 @@ public abstract class AbsSwipeUpHandler, Q extends protected MultiStateCallback mStateCallback; protected boolean mCanceled; private boolean mRecentsViewScrollLinked = false; - private final ActivityLifecycleCallbacksAdapter mLifecycleCallbacks = - new ActivityLifecycleCallbacksAdapter() { - @Override - public void onActivityDestroyed(Activity activity) { - if (mActivity != activity) { - return; - } - ActiveGestureLog.INSTANCE.addLog("Launcher destroyed", LAUNCHER_DESTROYED); - mRecentsView = null; - mActivity = null; - mStateCallback.clearState(STATE_LAUNCHER_PRESENT); - } - }; + private final ActivityLifecycleCallbacksAdapter mLifecycleCallbacks = new ActivityLifecycleCallbacksAdapter() { + @Override + public void onActivityDestroyed(Activity activity) { + if (mActivity != activity) { + return; + } + ActiveGestureLog.INSTANCE.addLog("Launcher destroyed", LAUNCHER_DESTROYED); + mRecentsView = null; + mActivity = null; + mStateCallback.clearState(STATE_LAUNCHER_PRESENT); + } + }; private static int FLAG_COUNT = 0; private static int getNextStateFlag(String name) { @@ -672,16 +669,6 @@ protected void notifyGestureAnimationStartToRecents() { runningTasks = cachedTaskInfo.getPlaceholderTasks(); } - // Safeguard against any null tasks being sent to recents view, happens when - // quickswitching - // very quickly w/ split tasks because TopTaskTracker provides stale information - // compared to - // actual running tasks in the recents animation. - // TODO(b/236226779), Proper fix (ag/22237143) - if (Arrays.stream(runningTasks).anyMatch(Objects::isNull)) { - return; - } - // Safeguard against any null tasks being sent to recents view, happens when // quickswitching // very quickly w/ split tasks because TopTaskTracker provides stale information @@ -702,8 +689,10 @@ private void initializeLauncherAnimationController() { buildAnimationController(); try (SafeCloseable c = TraceHelper.INSTANCE.allowIpcs("logToggleRecents")) { - LatencyTracker.getInstance(mContext).logAction(LatencyTracker.ACTION_TOGGLE_RECENTS, - (int) (mLauncherFrameDrawnTime - mTouchTimeMs)); + if (Utilities.ATLEAST_S) { + LatencyTracker.getInstance(mContext).logAction(LatencyTracker.ACTION_TOGGLE_RECENTS, + (int) (mLauncherFrameDrawnTime - mTouchTimeMs)); + } } // This method is only called when STATE_GESTURE_STARTED is set, so we can @@ -713,8 +702,6 @@ private void initializeLauncherAnimationController() { RecentsModel.INSTANCE.get(mContext).getThumbnailCache() .getHighResLoadingState().setVisible(true); - DepthController depthController = mActivityInterface.getDepthController(); - } public MotionPauseDetector.OnMotionPauseListener getMotionPauseListener() { @@ -1513,13 +1500,25 @@ protected abstract HomeAnimationFactory createHomeAnimationFactory( ArrayList launchCookies, long duration, boolean isTargetTranslucent, boolean appCanEnterPip, RemoteAnimationTarget runningTaskTarget); - private final TaskStackChangeListener mActivityRestartListener=new TaskStackChangeListener(){@Override public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,boolean homeTaskVisible,boolean clearedTask,boolean wasVisible){boolean taskRunningAndNotHome=Arrays.stream(mGestureState.getRunningTaskIds(true /* - * getMultipleTasks - */)).anyMatch(taskId->task.taskId==taskId&&task.configuration.windowConfiguration.getActivityType()!=ACTIVITY_TYPE_HOME);if(taskRunningAndNotHome){ - // Since this is an edge case, just cancel and relaunch with default activity - // options (since we don't know if there's an associated app icon to launch - // from) - endRunningWindowAnim(true /* cancel */);TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mActivityRestartListener);ActivityManagerWrapper.getInstance().startActivityFromRecents(task.taskId,null);}}}; + private final TaskStackChangeListener mActivityRestartListener = new TaskStackChangeListener() { + @Override + public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, boolean homeTaskVisible, + boolean clearedTask, boolean wasVisible) { + boolean taskRunningAndNotHome = Arrays.stream(mGestureState.getRunningTaskIds(true /* + * getMultipleTasks + */)) + .anyMatch(taskId -> task.taskId == taskId + && task.configuration.windowConfiguration.getActivityType() != ACTIVITY_TYPE_HOME); + if (taskRunningAndNotHome) { + // Since this is an edge case, just cancel and relaunch with default activity + // options (since we don't know if there's an associated app icon to launch + // from) + endRunningWindowAnim(true /* cancel */); + TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mActivityRestartListener); + ActivityManagerWrapper.getInstance().startActivityFromRecents(task.taskId, null); + } + } + }; @UiThread private void animateToProgressInternal(float start, float end, long duration, @@ -1555,13 +1554,14 @@ public void onAnimationEnd(Animator animation) { if (mGestureState.getEndTarget() == HOME) { getOrientationHandler().adjustFloatingIconStartVelocity(velocityPxPerMs); // Take first task ID, if there are multiple we don't have any special home - // animation so doesn't matter for splitscreen.. though the "allowEnterPip" might change + // animation so doesn't matter for splitscreen.. though the "allowEnterPip" + // might change // depending on which task it is.. final RemoteAnimationTarget runningTaskTarget = mRecentsAnimationTargets != null ? mRecentsAnimationTargets - .findTask(mGestureState.getTopRunningTaskId()) + .findTask(mGestureState.getTopRunningTaskId()) : null; - final ArrayList cookies = runningTaskTarget != null + final ArrayList cookies = Utilities.ATLEAST_S && runningTaskTarget != null && runningTaskTarget.taskInfo != null ? runningTaskTarget.taskInfo.launchCookies : new ArrayList<>(); @@ -1571,6 +1571,7 @@ public void onAnimationEnd(Animator animation) { && runningTaskTarget.leash.isValid(); boolean appCanEnterPip = !mDeviceState.isPipActive() && hasValidLeash + && Utilities.ATLEAST_T && runningTaskTarget.allowEnterPip && runningTaskTarget.taskInfo.pictureInPictureParams != null && runningTaskTarget.taskInfo.pictureInPictureParams.isAutoEnterEnabled(); @@ -1587,7 +1588,8 @@ public void onAnimationEnd(Animator animation) { mSwipePipToHomeReleaseCheck.setCanRelease(false); } - // grab a screenshot before the PipContentOverlay gets parented on top of the task + // grab a screenshot before the PipContentOverlay gets parented on top of the + // task UI_HELPER_EXECUTOR.execute(() -> { // Directly use top task, split to pip handled on shell side final int taskId = mGestureState.getTopRunningTaskId(); @@ -1613,7 +1615,7 @@ public void onAnimationEnd(Animator animation) { windowAnim[0].addAnimatorListener(new AnimationSuccessListener() { - @Override + @Override public void onAnimationSuccess(Animator animator) { if (mRecentsAnimationController == null) { // If the recents animation is interrupted, we still end the running @@ -2221,14 +2223,22 @@ private void maybeFinishSwipePipToHome() { } else if (mIsSwipeForSplit) { // Transaction to hide the task to avoid flicker for entering PiP from // split-screen. - PictureInPictureSurfaceTransaction tx = new PictureInPictureSurfaceTransaction.Builder() - .setAlpha(0f) - .build(); - tx.setShouldDisableCanAffectSystemUiFlags(false); - int[] taskIds = TopTaskTracker.INSTANCE.get(mContext).getRunningSplitTaskIds(); - for (int taskId : taskIds) { - mRecentsAnimationController.setFinishTaskTransaction(taskId, - tx, null /* overlay */); + try { + PictureInPictureSurfaceTransaction tx = new PictureInPictureSurfaceTransaction.Builder() + .setAlpha(0f) + .build(); + try { + tx.setShouldDisableCanAffectSystemUiFlags(false); + } catch (NoSuchMethodError error) { + Log.w(TAG, "not android 13 qpr1 : ", error); + } + int[] taskIds = TopTaskTracker.INSTANCE.get(mContext).getRunningSplitTaskIds(); + for (int taskId : taskIds) { + mRecentsAnimationController.setFinishTaskTransaction(taskId, + tx, null /* overlay */); + } + } catch (Throwable t) { + Log.w(TAG, "Failed PictureInPictureSurfaceTransaction: ", t); } } } @@ -2437,7 +2447,8 @@ public void onTasksAppeared(RemoteAnimationTarget[] appearedTaskTargets) { transaction.forSurface(target.leash).setAlpha(1).setLayer(-1).setShow(); } surfaceApplier.scheduleApply(transaction); - + if (!LawnchairQuickstepCompat.ATLEAST_S) + return; SplashScreenExitAnimationUtils.startAnimations(splashView, taskTarget.leash, mSplashMainWindowShiftLength, new TransactionPool(), new Rect(), SPLASH_ANIMATION_DURATION, SPLASH_FADE_OUT_DURATION, @@ -2523,6 +2534,7 @@ protected void applyScrollAndTransform() { startInterceptingTouchesForGesture(); } for (RemoteTargetHandle remoteHandle : mRemoteTargetHandles) { + float scrollScale = setRecentsScroll ? mRecentsView.getScrollScale(remoteHandle) : 1f; AnimatorControllerWithResistance playbackController = remoteHandle.getPlaybackController(); if (playbackController != null) { playbackController.setProgress(progress, mDragLengthFactor); @@ -2530,6 +2542,7 @@ protected void applyScrollAndTransform() { if (notSwipingToHome) { TaskViewSimulator taskViewSimulator = remoteHandle.getTaskViewSimulator(); + taskViewSimulator.scrollScale.value = scrollScale; if (setRecentsScroll) { taskViewSimulator.setScroll(scrollOffset); } diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java index c5a88bc073c..091aec40e25 100644 --- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java +++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java @@ -30,6 +30,7 @@ import android.animation.ObjectAnimator; import android.annotation.TargetApi; +import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityOptions; import android.content.Context; @@ -71,6 +72,7 @@ import com.android.quickstep.util.TransformParams; import com.android.quickstep.util.TransformParams.BuilderProxy; import com.android.systemui.shared.recents.model.Task.TaskKey; +import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputConsumerController; import java.lang.ref.WeakReference; @@ -78,6 +80,8 @@ import java.util.UUID; import java.util.function.Consumer; +import app.lawnchair.compat.LawnchairQuickstepCompat; + /** * Handles the navigation gestures when a 3rd party launcher is the default home activity. */ @@ -160,8 +164,9 @@ private void startHomeIntent( @Nullable RemoteAnimationTarget runningTaskTarget) { ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0); Intent intent = new Intent(mGestureState.getHomeIntent()); - if (gestureContractAnimationFactory != null && runningTaskTarget != null) { - gestureContractAnimationFactory.addGestureContract(intent, runningTaskTarget.taskInfo); + var runningTask = TopTaskTracker.INSTANCE.get(mContext).getCachedTopTask(true); + if (gestureContractAnimationFactory != null && runningTaskTarget != null && runningTask.getTaskId() == runningTaskTarget.taskId) { + gestureContractAnimationFactory.addGestureContract(intent, runningTask.mAllCachedTasks.get(0)); } startHomeIntentSafely(mContext, intent, options.toBundle()); } diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java index 993c6152706..1fa0b1ff01d 100644 --- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java +++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java @@ -214,7 +214,10 @@ public void onAnimationStart(int transit, RemoteAnimationTarget[] apps, mAnimationFinishedCallback = finishedCallback; } - @Override + void onAnimationCancelled(boolean isKeyguardOccluded) { + onAnimationCancelled(); + } + public void onAnimationCancelled() { } }; diff --git a/quickstep/src/com/android/quickstep/OrientationRectF.java b/quickstep/src/com/android/quickstep/OrientationRectF.java index aa01b05b47c..734ca084781 100644 --- a/quickstep/src/com/android/quickstep/OrientationRectF.java +++ b/quickstep/src/com/android/quickstep/OrientationRectF.java @@ -23,6 +23,8 @@ import android.util.Log; import android.view.MotionEvent; +import com.android.launcher3.Utilities; + public class OrientationRectF extends RectF { private static final String TAG = "OrientationRectF"; @@ -76,7 +78,11 @@ public boolean applyTransform(MotionEvent event, int deltaRotation, boolean forc + "mRotation: " + mRotation + " this: " + this); } - event.applyTransform(mTmpMatrix); + if (Utilities.ATLEAST_S) { + event.applyTransform(mTmpMatrix); + } else { + event.transform(mTmpMatrix); + } return true; } mTmpPoint[0] = event.getX(); @@ -92,7 +98,11 @@ public boolean applyTransform(MotionEvent event, int deltaRotation, boolean forc } if (contains(mTmpPoint[0], mTmpPoint[1])) { - event.applyTransform(mTmpMatrix); + if (Utilities.ATLEAST_S) { + event.applyTransform(mTmpMatrix); + } else { + event.transform(mTmpMatrix); + } return true; } return false; diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java index d3f59d15b61..f9f668fc3aa 100644 --- a/quickstep/src/com/android/quickstep/RecentTasksList.java +++ b/quickstep/src/com/android/quickstep/RecentTasksList.java @@ -34,11 +34,14 @@ import androidx.annotation.VisibleForTesting; +import com.android.launcher3.Utilities; import com.android.launcher3.util.LooperExecutor; import com.android.launcher3.util.SplitConfigurationOptions; import com.android.quickstep.util.DesktopTask; import com.android.quickstep.util.GroupTask; import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.system.TaskStackChangeListener; +import com.android.systemui.shared.system.TaskStackChangeListeners; import com.android.wm.shell.recents.IRecentTasksListener; import com.android.wm.shell.util.GroupedRecentTaskInfo; import com.android.wm.shell.util.SplitBounds; @@ -51,6 +54,7 @@ import java.util.stream.Collectors; import app.lawnchair.LawnchairApp; +import app.lawnchair.compat.LawnchairQuickstepCompat; /** * Manages the recent task list from the system, caching it as necessary. @@ -83,26 +87,60 @@ public RecentTasksList(LooperExecutor mainThreadExecutor, KeyguardManager keygua mKeyguardManager = keyguardManager; mChangeId = 1; mSysUiProxy = sysUiProxy; - sysUiProxy.registerRecentTasksListener(new IRecentTasksListener.Stub() { - @Override - public void onRecentTasksChanged() throws RemoteException { - mMainThreadExecutor.execute(RecentTasksList.this::onRecentTasksChanged); - } - @Override - public void onRunningTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) { - mMainThreadExecutor.execute(() -> { - RecentTasksList.this.onRunningTaskAppeared(taskInfo); + if (LawnchairApp.isRecentsEnabled()) { + if (LawnchairQuickstepCompat.ATLEAST_U) { + sysUiProxy.registerRecentTasksListener(new IRecentTasksListener.Stub() { + @Override + public void onRecentTasksChanged() throws RemoteException { + mMainThreadExecutor.execute(RecentTasksList.this::onRecentTasksChanged); + } + + @Override + public void onRunningTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) { + mMainThreadExecutor.execute(() -> { + RecentTasksList.this.onRunningTaskAppeared(taskInfo); + }); + } + + @Override + public void onRunningTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { + mMainThreadExecutor.execute(() -> { + RecentTasksList.this.onRunningTaskVanished(taskInfo); + }); + } }); - } + } else if (LawnchairQuickstepCompat.ATLEAST_Q) { + TaskStackChangeListeners.getInstance().registerTaskStackListener(new TaskStackChangeListener () { + @Override + public void onTaskStackChanged() { + onRecentTasksChanged(); + } - @Override - public void onRunningTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { - mMainThreadExecutor.execute(() -> { - RecentTasksList.this.onRunningTaskVanished(taskInfo); + @Override + public void onRecentTaskListUpdated() { + onRecentTasksChanged(); + } + + @Override + public void onTaskRemoved(int taskId) { + onRecentTasksChanged(); + } + + @Override + public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { + onRecentTasksChanged(); + } + + @Override + public void onActivityUnpinned() { + onRecentTasksChanged(); + } }); } - }); + + } + // We may receive onRunningTaskAppeared events later for tasks which have already been // included in the list returned by mSysUiProxy.getRunningTasks(), or may receive // onRunningTaskVanished for tasks not included in the returned list. These cases will be @@ -297,7 +335,7 @@ public boolean get(int key) { : Task.from(task2Key, taskInfo2, tmpLockedUsers.get(task2Key.userId) /* isLocked */); task2.setLastSnapshotData(taskInfo2); - } else { + } else if (Utilities.ATLEAST_S) { // Is fullscreen task if (numVisibleTasks > 0) { boolean isExcluded = (taskInfo1.baseIntent.getFlags() @@ -309,8 +347,10 @@ public boolean get(int key) { } } } - if (taskInfo1.isVisible) { - numVisibleTasks++; + if (Utilities.ATLEAST_S) { + if (taskInfo1.isVisible) { + numVisibleTasks++; + } } final SplitConfigurationOptions.SplitBounds launcherSplitBounds = convertSplitBounds(rawTask.getSplitBounds()); diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java index 4aa5112c3a0..77f61d136be 100644 --- a/quickstep/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/src/com/android/quickstep/RecentsActivity.java @@ -15,7 +15,6 @@ */ package com.android.quickstep; -import static android.os.Trace.TRACE_TAG_APP; import static android.view.RemoteAnimationTarget.MODE_CLOSING; import static android.view.RemoteAnimationTarget.MODE_OPENING; @@ -36,7 +35,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.os.Trace; import android.util.Log; import android.view.Display; import android.view.RemoteAnimationAdapter; @@ -91,6 +89,8 @@ import java.io.PrintWriter; import java.util.List; +import app.lawnchair.compat.LawnchairQuickstepCompat; + /** * A recents activity that shows the recently launched tasks as swipable task * cards. @@ -275,15 +275,16 @@ public void onAnimationCancelled() { final LauncherAnimationRunner wrapper = new LauncherAnimationRunner( mUiHandler, mActivityLaunchAnimationRunner, true /* startAtFrontOfQueue */); - final ActivityOptions options = ActivityOptions.makeRemoteAnimation( + final ActivityOptions options = LawnchairQuickstepCompat.getActivityOptionsCompat().makeRemoteAnimation( new RemoteAnimationAdapter(wrapper, RECENTS_LAUNCH_DURATION, RECENTS_LAUNCH_DURATION - STATUS_BAR_TRANSITION_DURATION - STATUS_BAR_TRANSITION_PRE_DELAY), - new RemoteTransition(wrapper.toRemoteTransition(), getIApplicationThread(), - "LaunchFromRecents")); + LawnchairQuickstepCompat.getRemoteTransitionCompat().getRemoteTransition(wrapper.toRemoteTransition(), getIApplicationThread(), + "LaunchFromRecents"), + "Lawnchair"); final ActivityOptionsWrapper activityOptions = new ActivityOptionsWrapper(options, onEndCallback); - if (Utilities.ATLEAST_S) { + if (Utilities.ATLEAST_T) { activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON); activityOptions.options.setLaunchDisplayId( (v != null && v.getDisplay() != null) ? v.getDisplay().getDisplayId() @@ -422,10 +423,11 @@ public void startHome() { private void startHomeInternal() { LauncherAnimationRunner runner = new LauncherAnimationRunner( getMainThreadHandler(), mAnimationToHomeFactory, true); - ActivityOptions options = ActivityOptions.makeRemoteAnimation( + ActivityOptions options = LawnchairQuickstepCompat.getActivityOptionsCompat().makeRemoteAnimation( new RemoteAnimationAdapter(runner, HOME_APPEAR_DURATION, 0), - new RemoteTransition(runner.toRemoteTransition(), getIApplicationThread(), - "StartHomeFromRecents")); + LawnchairQuickstepCompat.getRemoteTransitionCompat().getRemoteTransition(runner.toRemoteTransition(), getIApplicationThread(), + "StartHomeFromRecents"), + "Lawnchair"); startHomeIntentSafely(this, options.toBundle()); } diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java index 2256cbf14bf..e8183e132ed 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java @@ -42,6 +42,8 @@ import java.util.HashMap; import java.util.Set; +import app.lawnchair.compat.LawnchairQuickstepCompat; + /** * Wrapper around {@link com.android.systemui.shared.system.RecentsAnimationListener} which * delegates callbacks to multiple listeners on the main thread @@ -125,7 +127,7 @@ public final void onAnimationStart(RecentsAnimationControllerCompat animationCon } else { RemoteAnimationTarget[] nonAppTargets; if (!TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) { - nonAppTargets = mSystemUiProxy.onGoingToRecentsLegacy(appTargets); + nonAppTargets = LawnchairQuickstepCompat.ATLEAST_T ? mSystemUiProxy.onGoingToRecentsLegacy(appTargets) : null; } else { final ArrayList apps = new ArrayList<>(); final ArrayList nonApps = new ArrayList<>(); diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java index 80aaad0dd9e..7161cf27b0f 100644 --- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java +++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java @@ -130,6 +130,7 @@ public void release() { } private static void release(RemoteAnimationTarget[] targets) { + if (targets == null) return; for (RemoteAnimationTarget target : targets) { if (target.leash != null) { target.leash.release(); diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index 88b4b3d6742..a4d08ea6296 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -29,8 +29,9 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; -import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; +import android.graphics.Bitmap; +import android.graphics.Insets; import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; @@ -59,12 +60,15 @@ import com.android.internal.logging.InstanceId; import com.android.internal.util.ScreenshotRequest; import com.android.internal.view.AppearanceRegion; +import com.android.launcher3.Utilities; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.SplitConfigurationOptions; import com.android.quickstep.util.AssistUtils; import com.android.systemui.shared.recents.ISystemUiProxy; +import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; +import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.RecentsAnimationControllerCompat; import com.android.systemui.shared.system.RecentsAnimationListener; import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController; @@ -94,6 +98,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; +import java.util.List; +import java.util.stream.Collectors; + +import app.lawnchair.compat.LawnchairQuickstepCompat; /** * Holds the reference to SystemUI. @@ -231,7 +239,7 @@ public void setProxy(ISystemUiProxy proxy, IPip pip, IBubbles bubbles, ISplitScr unlinkToDeath(); mSystemUiProxy = proxy; mPip = pip; - mBubbles = bubbles; + mBubbles = Utilities.ATLEAST_U ? bubbles : null; mSplitScreen = splitScreen; mOneHanded = oneHanded; mShellTransitions = shellTransitions; @@ -241,7 +249,7 @@ public void setProxy(ISystemUiProxy proxy, IPip pip, IBubbles bubbles, ISplitScr mBackAnimation = backAnimation; mDesktopMode = desktopMode; mUnfoldAnimation = unfoldAnimation; - mDragAndDrop = dragAndDrop; + mDragAndDrop = Utilities.ATLEAST_U ? dragAndDrop : null; linkToDeath(); // re-attach the listeners once missing due to setProxy has not been initialized // yet. @@ -360,6 +368,11 @@ public void onAssistantGestureCompletion(float velocity) { } } + @Override + public Rect getNonMinimizedSplitScreenSecondaryBounds() { + return null; + } + @Override public void startAssistant(Bundle args) { if (mSystemUiProxy != null) { @@ -382,6 +395,18 @@ public void setAssistantOverridesRequested(int[] invocationTypes) { } } + @Override + public Bundle monitorGestureInput(String name, int displayId) { + if (mSystemUiProxy != null) { + try { + return mSystemUiProxy.monitorGestureInput(name, displayId); + } catch (RemoteException e) { + Log.w(TAG, "Failed call monitorGestureInput: " + name, e); + } + } + return null; + } + @Override public void notifyAccessibilityButtonClicked(int displayId) { if (mSystemUiProxy != null) { @@ -426,17 +451,6 @@ public void notifyPrioritizedRotation(int rotation) { } } - // public void setTaskbarEnabled(boolean enabled) { - // if (mSystemUiProxy != null) { - // try { - // mSystemUiProxy.setTaskbarEnabled(enabled); - // } catch (RemoteException e) { - // Log.w(TAG, "Failed call setTaskbarEnabled with arg: " + - // enabled, e); - // } - // } - // } - public void notifyTaskbarStatus(boolean visible, boolean stashed) { if (mSystemUiProxy != null) { try { @@ -477,6 +491,54 @@ public void takeScreenshot(ScreenshotRequest request) { } } + @Override + public void handleImageBundleAsScreenshot(Bundle screenImageBundle, Rect locationInScreen, + Insets visibleInsets, Task.TaskKey task) { + if (mSystemUiProxy != null) { + try { + mSystemUiProxy.handleImageBundleAsScreenshot(screenImageBundle, locationInScreen, + visibleInsets, task); + } catch (RemoteException e) { + Log.w(TAG, "Failed call handleImageBundleAsScreenshot"); + } + } + } + + public void handleImageAsScreenshot(Bitmap bitmap, Rect rect, Insets insets, int i) { + if (mSystemUiProxy != null) { + try { + mSystemUiProxy.handleImageAsScreenshot(bitmap, rect, insets, i); + } catch (RemoteException e) { + Log.w(TAG, "Failed call handleImageAsScreenshot", e); + } + } + } + + @Override + public void setSplitScreenMinimized(boolean minimized) throws RemoteException {} + + @Override + public void notifySwipeToHomeFinished() { + if (mSystemUiProxy != null) { + try { + mSystemUiProxy.notifySwipeUpGestureStarted(); + } catch (RemoteException e) { + Log.w(TAG, "Failed call notifySwipeUpGestureStarted", e); + } + } + } + + @Override + public void notifySwipeUpGestureStarted() { + if (mSystemUiProxy != null) { + try { + mSystemUiProxy.notifySwipeToHomeFinished(); + } catch (RemoteException e) { + Log.w(TAG, "Failed call notifySwipeToHomeFinished", e); + } + } + } + @Override public void onStatusBarTrackpadEvent(MotionEvent event) { if (mSystemUiProxy != null) { @@ -527,7 +589,7 @@ private void setShelfHeightAsync(int visibleInt, int shelfHeight) { boolean visible = visibleInt != 0; boolean changed = visible != mLastShelfVisible || shelfHeight != mLastShelfHeight; IPip pip = mPip; - if (pip != null && changed) { + if (pip != null && changed && Utilities.ATLEAST_T) { mLastShelfVisible = visible; mLastShelfHeight = shelfHeight; try { @@ -544,8 +606,10 @@ private void setShelfHeightAsync(int visibleInt, int shelfHeight) { * the Launcher for the Hotseat. */ public void setLauncherKeepClearAreaHeight(boolean visible, int height) { - Message.obtain(mAsyncHandler, MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT, - visible ? 1 : 0, height).sendToTarget(); + if (Utilities.ATLEAST_T) { + Message.obtain(mAsyncHandler, MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT, + visible ? 1 : 0, height).sendToTarget(); + } } @WorkerThread @@ -570,7 +634,7 @@ private void setLauncherKeepClearAreaHeight(int visibleInt, int height) { * Sets listener to get pip animation callbacks. */ public void setPipAnimationListener(IPipAnimationListener listener) { - if (mPip != null) { + if (mPip != null && Utilities.ATLEAST_T) { try { mPip.setPipAnimationListener(listener); } catch (RemoteException e) { @@ -592,7 +656,7 @@ public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activi try { return mPip.startSwipePipToHome(componentName, activityInfo, pictureInPictureParams, launcherRotation, hotseatKeepClearArea); - } catch (RemoteException e) { + } catch (Throwable e) { Log.w(TAG, "Failed call startSwipePipToHome", e); } } @@ -636,12 +700,16 @@ public void abortSwipePipToHome(int taskId, ComponentName componentName) { * Sets the next pip animation type to be the alpha animation. */ public void setPipAnimationTypeToAlpha() { - if (mPip != null) { - try { - mPip.setPipAnimationTypeToAlpha(); - } catch (RemoteException e) { - Log.w(TAG, "Failed call setPipAnimationTypeToAlpha", e); + try { + if (mPip != null) { + try { + mPip.setPipAnimationTypeToAlpha(); + } catch (RemoteException e) { + Log.w(TAG, "Failed call setPipAnimationTypeToAlpha", e); + } } + } catch (Throwable t) { + Log.w(TAG, "Failed call setPipAnimationTypeToAlpha not supported", t); } } @@ -649,7 +717,7 @@ public void setPipAnimationTypeToAlpha() { * Sets the app icon size in pixel used by Launcher all apps. */ public void setLauncherAppIconSize(int iconSizePx) { - if (mPip != null) { + if (mPip != null && Utilities.ATLEAST_T) { try { mPip.setLauncherAppIconSize(iconSizePx); } catch (RemoteException e) { @@ -766,14 +834,18 @@ public void onBubbleDrag(@Nullable String bubbleKey, boolean isBeingDragged) { // public void registerSplitScreenListener(ISplitScreenListener listener) { - if (mSplitScreen != null) { - try { - mSplitScreen.registerSplitScreenListener(listener); - } catch (RemoteException e) { - Log.w(TAG, "Failed call registerSplitScreenListener"); + try { + if (mSplitScreen != null) { + try { + mSplitScreen.registerSplitScreenListener(listener); + } catch (RemoteException e) { + Log.w(TAG, "Failed call registerSplitScreenListener"); + } } + mSplitScreenListener = listener; + } catch (Throwable t) { + Log.w (TAG , "Not supported: " , t); } - mSplitScreenListener = listener; } public void unregisterSplitScreenListener(ISplitScreenListener listener) { @@ -871,7 +943,7 @@ public void startShortcutAndTask(ShortcutInfo shortcutInfo, Bundle options1, int public void startTasksWithLegacyTransition(int taskId1, Bundle options1, int taskId2, Bundle options2, @SplitConfigurationOptions.StagePosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) { - if (mSystemUiProxy != null) { + if (mSystemUiProxy != null && Utilities.ATLEAST_T) { try { mSplitScreen.startTasksWithLegacyTransition(taskId1, options1, taskId2, options2, splitPosition, splitRatio, adapter, instanceId); @@ -992,7 +1064,7 @@ public RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] app if (mSplitScreen != null) { try { return mSplitScreen.onStartingSplitLegacy(apps); - } catch (RemoteException e) { + } catch (Throwable e) { Log.w(TAG, "Failed call onStartingSplitLegacy"); } } @@ -1243,27 +1315,34 @@ public void customizeStatusBarAppearance(AppearanceRegion appearance) { } public ArrayList getRecentTasks(int numTasks, int userId) { - if (mRecentTasks != null) { - try { + if (!LawnchairQuickstepCompat.ATLEAST_U) { + return getRecentTasksFromWrapper(numTasks, userId); + } + try { + if (mRecentTasks != null) { final GroupedRecentTaskInfo[] rawTasks = mRecentTasks.getRecentTasks(numTasks, RECENT_IGNORE_UNAVAILABLE, userId); - if (rawTasks == null) { - return new ArrayList<>(); - } return new ArrayList<>(Arrays.asList(rawTasks)); - } catch (RemoteException e) { - Log.w(TAG, "Failed call getRecentTasks", e); } + } catch (Throwable t) { + Log.w(TAG, "Failed call getRecentTasks ,may be android 13- ", t); + return getRecentTasksFromWrapper(numTasks, userId); } return new ArrayList<>(); } + private ArrayList getRecentTasksFromWrapper(int numTasks, int userId) { + List recentTaskInfoList = ActivityManagerWrapper.getInstance().getRecentTasks(numTasks, userId); + return recentTaskInfoList.stream() + .map(GroupedRecentTaskInfo::forSingleTask) + .collect(Collectors.toCollection(ArrayList::new)); + } + /** * Gets the set of running tasks. */ public ArrayList getRunningTasks(int numTasks) { - if (mRecentTasks != null - && mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) { + if (mRecentTasks != null) { try { return new ArrayList<>(Arrays.asList(mRecentTasks.getRunningTasks(numTasks))); } catch (RemoteException e) { diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java index 35cb28d8450..a96b514f00a 100644 --- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java +++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java @@ -32,6 +32,7 @@ import android.os.SystemProperties; import android.util.Log; import android.view.RemoteAnimationTarget; +import android.window.RemoteTransition; import androidx.annotation.Nullable; import androidx.annotation.UiThread; @@ -53,7 +54,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener { public static final boolean ENABLE_SHELL_TRANSITIONS = SystemProperties.getBoolean("persist.wm.debug.shell_transit", - true); + Utilities.ATLEAST_U); public static final boolean SHELL_TRANSITIONS_ROTATION = ENABLE_SHELL_TRANSITIONS && SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false); @@ -274,10 +275,13 @@ public boolean onSwitchToScreenshot(Runnable onFinished) { if (!homeIsOnTop) { options.setTransientLaunch(); } - if (app.lawnchair.LawnchairApp.isAtleastT()) { + if (Utilities.ATLEAST_S) { options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime); - SystemUiProxy.INSTANCE.getNoCreate().startRecentsActivity(intent, options, mCallbacks); + if (Utilities.ATLEAST_U) { + SystemUiProxy.INSTANCE.getNoCreate().startRecentsActivity(intent, options, mCallbacks); + } } + UI_HELPER_EXECUTOR.execute(() -> mCtx.startActivity(intent, options.toBundle())); } else { UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance() .startRecentsActivity(intent, eventTime, mCallbacks, null, null)); diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java index fe3a590c5ff..2d545fb4185 100644 --- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java +++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java @@ -140,6 +140,7 @@ public void clearAllActiveState() { */ private static final TaskShortcutFactory[] MENU_OPTIONS = new TaskShortcutFactory[] { TaskShortcutFactory.APP_INFO, + app.lawnchair.overview.TaskShortcutFactory.legacySplit, TaskShortcutFactory.SPLIT_SELECT, TaskShortcutFactory.PIN, TaskShortcutFactory.INSTALL, diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java index 2af81d43101..5f915046379 100644 --- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java +++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java @@ -65,6 +65,8 @@ import java.util.function.Function; import java.util.stream.Collectors; +import app.lawnchair.compat.LawnchairQuickstepCompat; + /** * Represents a system shortcut that can be shown for a recent task. Appears as * a single entry in @@ -281,6 +283,7 @@ private ActivityOptions makeLaunchOptions(Activity activity) { @Override public List getShortcuts(BaseDraggingActivity activity, TaskIdAttributeContainer taskContainer) { + if (!LawnchairQuickstepCompat.ATLEAST_T) return null; DeviceProfile deviceProfile = activity.getDeviceProfile(); final Task task = taskContainer.getTask(); final int intentFlags = task.key.baseIntent.getFlags(); diff --git a/quickstep/src/com/android/quickstep/TopTaskTracker.java b/quickstep/src/com/android/quickstep/TopTaskTracker.java index f1af2edda8b..e0fcdcbcba1 100644 --- a/quickstep/src/com/android/quickstep/TopTaskTracker.java +++ b/quickstep/src/com/android/quickstep/TopTaskTracker.java @@ -51,6 +51,9 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.stream.Collectors; + +import app.lawnchair.compat.LawnchairQuickstepCompat; /** * This class tracked the top-most task and some 'approximate' task history to allow faster @@ -81,11 +84,14 @@ private TopTaskTracker(Context context) { @Override public void onTaskRemoved(int taskId) { + if (!LawnchairQuickstepCompat.ATLEAST_T) return; mOrderedTaskList.removeIf(rto -> rto.taskId == taskId); } @Override public void onTaskMovedToFront(RunningTaskInfo taskInfo) { + if (!LawnchairQuickstepCompat.ATLEAST_T) return; + mOrderedTaskList.removeIf(rto -> rto.taskId == taskInfo.taskId); mOrderedTaskList.addFirst(taskInfo); @@ -180,6 +186,14 @@ public int[] getRunningSplitTaskIds() { */ @UiThread public CachedTaskInfo getCachedTopTask(boolean filterOnlyVisibleRecents) { + if (!LawnchairQuickstepCompat.ATLEAST_U) { + RunningTaskInfo task = TraceHelper.allowIpcs("getCachedTopTask.false", () -> + ActivityManagerWrapper.getInstance().getRunningTask( + false /* filterOnlyVisibleRecents */)); + ArrayList taskList = new ArrayList<>(); + Collections.addAll(taskList, task); + return new CachedTaskInfo(taskList); + } if (filterOnlyVisibleRecents) { // Since we only know about the top most task, any filtering may not be applied on the // cache. The second to top task may change while the top task is still the same. @@ -240,7 +254,7 @@ public boolean isRootChooseActivity() { List visibleNonExcludedTasks = mAllCachedTasks.stream() .filter(t -> t.isVisible && (t.baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0) - .toList(); + .collect(Collectors.toList()); return visibleNonExcludedTasks.isEmpty() ? null : new CachedTaskInfo(visibleNonExcludedTasks); } diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index 925fd2e97e8..12d3604f56f 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -88,6 +88,7 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherPrefs; import com.android.launcher3.R; +import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.provider.RestoreDbTask; @@ -148,6 +149,7 @@ import java.util.function.Function; import app.lawnchair.LawnchairApp; +import app.lawnchair.compat.LawnchairQuickstepCompat; /** * Service connected by system-UI for handling touch interaction. @@ -206,12 +208,16 @@ public void onInitialize(Bundle bundle) { IDragAndDrop dragAndDrop = IDragAndDrop.Stub.asInterface( bundle.getBinder(KEY_EXTRA_SHELL_DRAG_AND_DROP)); MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> { - SystemUiProxy.INSTANCE.get(tis).setProxy(proxy, pip, - bubbles, splitscreen, onehanded, shellTransitions, startingWindow, - recentTasks, launcherUnlockAnimationController, backAnimation, desktopMode, - unfoldTransition, dragAndDrop); - tis.initInputMonitor("TISBinder#onInitialize()"); - tis.preloadOverview(true /* fromInit */); + try { + SystemUiProxy.INSTANCE.get(tis).setProxy(proxy, pip, + bubbles, splitscreen, onehanded, shellTransitions, startingWindow, + recentTasks, launcherUnlockAnimationController, backAnimation, desktopMode, + unfoldTransition, dragAndDrop); + tis.initInputMonitor("TISBinder#onInitialize()"); + tis.preloadOverview(true /* fromInit */); + } catch (Throwable t) { + // Ignore + } })); sIsInitialized = true; } @@ -219,7 +225,7 @@ public void onInitialize(Bundle bundle) { @BinderThread @Override public void onTaskbarToggled() { - if (!FeatureFlags.ENABLE_KEYBOARD_TASKBAR_TOGGLE.get()) + if (!FeatureFlags.ENABLE_KEYBOARD_TASKBAR_TOGGLE.get() || !LawnchairQuickstepCompat.ATLEAST_S) return; MAIN_EXECUTOR.execute(() -> executeForTouchInteractionService(tis -> { TaskbarActivityContext activityContext = tis.mTaskbarManager.getCurrentActivityContext(); @@ -394,7 +400,7 @@ private void executeForTaskbarManager( @Nullable public TaskbarManager getTaskbarManager() { TouchInteractionService tis = mTis.get(); - if (tis == null) + if (tis == null || !LawnchairQuickstepCompat.ATLEAST_S) return null; return tis.mTaskbarManager; } @@ -489,14 +495,16 @@ public void onCreate() { mMainChoreographer = Choreographer.getInstance(); mAM = ActivityManagerWrapper.getInstance(); mDeviceState = new RecentsAnimationDeviceState(this, true); - mTaskbarManager = new TaskbarManager(this); + mTaskbarManager = LawnchairQuickstepCompat.ATLEAST_S ? new TaskbarManager(this) : null; mRotationTouchHelper = mDeviceState.getRotationTouchHelper(); BootAwarePreloader.start(this); // Call runOnUserUnlocked() before any other callbacks to ensure everything is // initialized. LockedUserState.get(this).runOnUserUnlocked(this::onUserUnlocked); - LockedUserState.get(this).runOnUserUnlocked(mTaskbarManager::onUserUnlocked); + if (LawnchairQuickstepCompat.ATLEAST_S) { + LockedUserState.get(this).runOnUserUnlocked(mTaskbarManager::onUserUnlocked); + } mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged); sConnected = true; } @@ -520,7 +528,16 @@ private void initInputMonitor(String reason) { return; } - mInputMonitorCompat = new InputMonitorCompat("swipe-up", mDeviceState.getDisplayId()); + if (LawnchairQuickstepCompat.ATLEAST_S) { + mInputMonitorCompat = new InputMonitorCompat("swipe-up", mDeviceState.getDisplayId()); + } else { + if (!SystemUiProxy.INSTANCE.get(this).isActive()) { + return; + } + Bundle bundle = SystemUiProxy.INSTANCE.get(this).monitorGestureInput("swipe-up", + mDeviceState.getDisplayId()); + mInputMonitorCompat = InputMonitorCompat.fromBundle(bundle, "extra_input_monitor"); + } mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(), mMainChoreographer, this::onInputEvent); @@ -542,8 +559,10 @@ public void onUserUnlocked() { mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState); mOverviewCommandHelper = new OverviewCommandHelper(this, mOverviewComponentObserver, mTaskAnimationManager); - mResetGestureInputConsumer = new ResetGestureInputConsumer( - mTaskAnimationManager, mTaskbarManager::getCurrentActivityContext); + if (LawnchairQuickstepCompat.ATLEAST_S && mTaskbarManager != null) { + mResetGestureInputConsumer = new ResetGestureInputConsumer( + mTaskAnimationManager, mTaskbarManager::getCurrentActivityContext); + } mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer(); mInputConsumer.registerInputConsumer(); onSystemUiFlagsChanged(mDeviceState.getSystemUiStateFlags()); @@ -585,15 +604,17 @@ private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() { private void onOverviewTargetChange(boolean isHomeAndOverviewSame) { AccessibilityManager am = getSystemService(AccessibilityManager.class); - if (isHomeAndOverviewSame) { - am.registerSystemAction(createAllAppsAction(), GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS); - } else { - am.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS); + if (LawnchairQuickstepCompat.ATLEAST_R) { + if (isHomeAndOverviewSame) { + am.registerSystemAction(createAllAppsAction(), GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS); + } else { + am.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS); + } } StatefulActivity newOverviewActivity = mOverviewComponentObserver.getActivityInterface() .getCreatedActivity(); - if (newOverviewActivity != null) { + if (newOverviewActivity != null && mTaskbarManager != null) { mTaskbarManager.setActivity(newOverviewActivity); } mTISBinder.onOverviewTargetChange(); @@ -604,7 +625,7 @@ private RemoteAction createAllAppsAction() { .setAction(INTENT_ACTION_ALL_APPS_TOGGLE); final PendingIntent actionPendingIntent; - if (FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) { + if (FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get() && mTaskbarManager != null) { actionPendingIntent = new PendingIntent(new IIntentSender.Stub() { @Override public void send(int code, Intent intent, String resolvedType, @@ -634,7 +655,9 @@ private void onSystemUiFlagsChanged(int lastSysUIFlags) { int systemUiStateFlags = mDeviceState.getSystemUiStateFlags(); SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags); mOverviewComponentObserver.onSystemUiStateChanged(); - mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags); + if (mTaskbarManager != null) { + mTaskbarManager.onSystemUiFlagsChanged(systemUiStateFlags); + } int isShadeExpandedFlag = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED | SYSUI_STATE_QUICK_SETTINGS_EXPANDED; boolean wasExpanded = (lastSysUIFlags & isShadeExpandedFlag) != 0; @@ -671,10 +694,14 @@ public void onDestroy() { mDeviceState.destroy(); SystemUiProxy.INSTANCE.get(this).clearProxy(); - getSystemService(AccessibilityManager.class) - .unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS); + if (LawnchairQuickstepCompat.ATLEAST_R) { + getSystemService(AccessibilityManager.class) + .unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS); + } - mTaskbarManager.destroy(); + if (mTaskbarManager != null) { + mTaskbarManager.destroy(); + } sConnected = false; super.onDestroy(); } @@ -706,7 +733,7 @@ private void onInputEvent(InputEvent ev) { // Note this will create a new consumer every mouse click, as after ACTION_UP // from the click // an ACTION_HOVER_ENTER will fire as well. - boolean isHoverActionWithoutConsumer = event.isHoverEvent() + boolean isHoverActionWithoutConsumer = isHoverEvent(action) && (mUncheckedConsumer.getType() & TYPE_CURSOR_HOVER) == 0; if (action == ACTION_DOWN || isHoverActionWithoutConsumer) { mRotationTouchHelper.setOrientationTransformIfNeeded(event); @@ -801,9 +828,15 @@ private void onInputEvent(InputEvent ev) { traceToken.close(); } + private boolean isHoverEvent(int action) { + return action == MotionEvent.ACTION_HOVER_ENTER + || action == MotionEvent.ACTION_HOVER_MOVE + || action == MotionEvent.ACTION_HOVER_EXIT; + } + // Talkback generates hover events on touch, which we do not want to consume. private boolean isCursorHoverEvent(MotionEvent event) { - return event.isHoverEvent() && event.getSource() == InputDevice.SOURCE_MOUSE; + return isHoverEvent(event.getActionMasked()) && event.getSource() == InputDevice.SOURCE_MOUSE; } private InputConsumer tryCreateAssistantInputConsumer( @@ -925,7 +958,7 @@ private InputConsumer newConsumer( // If Taskbar is present, we listen for swipe or cursor hover events to unstash // it. - TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext(); + TaskbarActivityContext tac = mTaskbarManager != null ? mTaskbarManager.getCurrentActivityContext() : null; if (tac != null && !(base instanceof AssistantInputConsumer)) { // Present always on large screen or on small screen w/ flag DeviceProfile dp = tac.getDeviceProfile(); diff --git a/quickstep/src/com/android/quickstep/ViewUtils.java b/quickstep/src/com/android/quickstep/ViewUtils.java index b1320674e69..8f6b3d22fc1 100644 --- a/quickstep/src/com/android/quickstep/ViewUtils.java +++ b/quickstep/src/com/android/quickstep/ViewUtils.java @@ -25,6 +25,8 @@ import java.util.function.BooleanSupplier; +import app.lawnchair.compat.LawnchairQuickstepCompat; + /** * Utility class for helpful methods related to {@link View} objects. */ @@ -43,6 +45,9 @@ public static boolean postFrameDrawn(View view, Runnable onFinishRunnable) { */ public static boolean postFrameDrawn( View view, Runnable onFinishRunnable, BooleanSupplier canceled) { + if (!LawnchairQuickstepCompat.ATLEAST_U) { + return new FrameHandlerVR(view, onFinishRunnable, canceled).schedule(); + } return new FrameHandler(view, onFinishRunnable, canceled).schedule(); } @@ -129,4 +134,51 @@ private void finish() { } } } + + private static class FrameHandlerVR implements HardwareRenderer.FrameDrawingCallback { + + final ViewRootImpl mViewRoot; + final Runnable mFinishCallback; + final BooleanSupplier mCancelled; + final Handler mHandler; + + int mDeferFrameCount = 1; + + FrameHandlerVR(View view, Runnable finishCallback, BooleanSupplier cancelled) { + mViewRoot = view.getViewRootImpl(); + mFinishCallback = finishCallback; + mCancelled = cancelled; + mHandler = new Handler(); + } + + @Override + public void onFrameDraw(long frame) { + Utilities.postAsyncCallback(mHandler, this::onFrame); + } + + private void onFrame() { + if (mCancelled.getAsBoolean()) { + return; + } + + if (mDeferFrameCount > 0) { + mDeferFrameCount--; + schedule(); + return; + } + + if (mFinishCallback != null) { + mFinishCallback.run(); + } + } + + private boolean schedule() { + if (mViewRoot != null && mViewRoot.getView() != null) { + mViewRoot.registerRtFrameCallback(this); + mViewRoot.getView().invalidate(); + return true; + } + return false; + } + } } diff --git a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java index b2f5620905e..44920422cf0 100644 --- a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java +++ b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java @@ -54,6 +54,7 @@ import com.android.launcher3.BuildConfig; import com.android.quickstep.SystemUiProxy; import com.android.systemui.shared.recents.model.Task; +import com.topjohnwu.superuser.Shell; import java.io.File; import java.io.FileOutputStream; @@ -61,6 +62,8 @@ import java.util.function.BiFunction; import java.util.function.Supplier; +import app.lawnchair.compatlib.utils.BitmapUtil; + /** * Utility class containing methods to help manage image actions such as sharing, cropping, and * saving image. @@ -78,16 +81,25 @@ public class ImageActionUtils { */ public static void saveScreenshot(SystemUiProxy systemUiProxy, Bitmap screenshot, Rect screenshotBounds, Insets visibleInsets, Task.TaskKey task) { - ScreenshotRequest request = - new ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OVERVIEW) - .setTopComponent(task.sourceComponent) - .setTaskId(task.id) - .setUserId(task.userId) - .setBitmap(screenshot) - .setBoundsOnScreen(screenshotBounds) - .setInsets(visibleInsets) - .build(); - systemUiProxy.takeScreenshot(request); + try { + ScreenshotRequest request = + new ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OVERVIEW) + .setTopComponent(task.sourceComponent) + .setTaskId(task.id) + .setUserId(task.userId) + .setBitmap(screenshot) + .setBoundsOnScreen(screenshotBounds) + .setInsets(visibleInsets) + .build(); + systemUiProxy.takeScreenshot(request); + } catch (Throwable t) { + try { + systemUiProxy.handleImageBundleAsScreenshot(BitmapUtil.hardwareBitmapToBundle(screenshot), + screenshotBounds, visibleInsets, task); + } catch (Throwable ee) { + Shell.su("input keyevent 120").exec(); + } + } } /** diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java index d4ddf76c413..454d2cbb19c 100644 --- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java +++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java @@ -107,6 +107,8 @@ import java.util.List; import java.util.function.Consumer; +import app.lawnchair.compat.LawnchairQuickstepCompat; + /** * Represent data needed for the transient state when user has selected one app for split screen * and is in the process of either a) selecting a second app or b) exiting intention to invoke split @@ -468,7 +470,7 @@ public void launchInitialAppFullscreen(Consumer callback) { final RemoteSplitLaunchTransitionRunner animationRunner = new RemoteSplitLaunchTransitionRunner(firstTaskId, secondTaskId, callback); - final RemoteTransition remoteTransition = new RemoteTransition(animationRunner, + final RemoteTransition remoteTransition = LawnchairQuickstepCompat.getRemoteTransitionCompat().getRemoteTransition(animationRunner, ActivityThread.currentActivityThread().getApplicationThread(), "LaunchAppFullscreen"); InstanceId instanceId = LogUtils.getShellShareableInstanceId().first; @@ -513,7 +515,7 @@ private RemoteTransition getShellRemoteTransition(int firstTaskId, int secondTas @Nullable Consumer callback, String transitionName) { final RemoteSplitLaunchTransitionRunner animationRunner = new RemoteSplitLaunchTransitionRunner(firstTaskId, secondTaskId, callback); - return new RemoteTransition(animationRunner, + return LawnchairQuickstepCompat.getRemoteTransitionCompat().getRemoteTransition(animationRunner, ActivityThread.currentActivityThread().getApplicationThread(), transitionName); } diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java b/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java index 5fd86c036f9..8006a979367 100644 --- a/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java +++ b/quickstep/src/com/android/quickstep/util/SurfaceTransaction.java @@ -20,6 +20,8 @@ import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; +import com.android.launcher3.Utilities; + /** * Helper class for building a {@link Transaction}. */ @@ -103,7 +105,9 @@ public SurfaceProperties setCornerRadius(float radius) { * @return this Builder */ public SurfaceProperties setShadowRadius(float radius) { - mTransaction.setShadowRadius(mSurface, radius); + if (Utilities.ATLEAST_R) { + mTransaction.setShadowRadius(mSurface, radius); + } return this; } diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java index bb028a700cb..b4204e64e04 100644 --- a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java +++ b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java @@ -27,6 +27,8 @@ import com.android.quickstep.RemoteAnimationTargets.ReleaseCheck; +import app.lawnchair.compat.LawnchairQuickstepCompat; + /** * Helper class to apply surface transactions in sync with RenderThread similar to * android.view.SyncRtSurfaceTransactionApplier @@ -112,7 +114,11 @@ public void scheduleApply(SurfaceTransaction params) { .sendToTarget(); return; } - mTargetViewRootImpl.mergeWithNextTransaction(t, frame); + if (LawnchairQuickstepCompat.ATLEAST_S) { + mTargetViewRootImpl.mergeWithNextTransaction(t, frame); + } else { + t.apply(); + } Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0) .sendToTarget(); }); diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java index 7cc2c468fa1..f45945628bf 100644 --- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java +++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java @@ -271,7 +271,11 @@ public PictureInPictureSurfaceTransaction getFinishTransaction() { PipSurfaceTransactionHelper.newSurfaceControlTransaction(); final PictureInPictureSurfaceTransaction pipTx = onAnimationUpdate(tx, new RectF(mDestinationBounds), END_PROGRESS); - pipTx.setShouldDisableCanAffectSystemUiFlags(true); + try { + pipTx.setShouldDisableCanAffectSystemUiFlags(true); + } catch (NoSuchMethodError error) { + Log.w(TAG, "not android 13 qpr1 : ", error); + } return pipTx; } diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java index 9b2a4493374..a6881d0a9b8 100644 --- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java +++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java @@ -90,6 +90,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { private final FullscreenDrawParams mCurrentFullscreenParams; public final AnimatedFloat taskPrimaryTranslation = new AnimatedFloat(); public final AnimatedFloat taskSecondaryTranslation = new AnimatedFloat(); + public final AnimatedFloat scrollScale = new AnimatedFloat(); // RecentsView properties public final AnimatedFloat recentsViewScale = new AnimatedFloat(); @@ -119,6 +120,9 @@ public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy) { mOrientationStateId = mOrientationState.getStateId(); Resources resources = context.getResources(); mIsRecentsRtl = mOrientationState.getOrientationHandler().getRecentsRtlSetting(resources); + //nick@lmo-20231004 this does belong here to avoid flicker in animation due to race of setting + // value to 1. other code assumes it starts with 1 anyway, so let's just do it here + this.scrollScale.value = 1; } /** @@ -182,7 +186,7 @@ public float getFullScreenScale() { public void setPreview(RemoteAnimationTarget runningTarget) { setPreviewBounds( runningTarget.startBounds == null - ? runningTarget.screenSpaceBounds : runningTarget.startBounds, + ? (Utilities.ATLEAST_R ? runningTarget.screenSpaceBounds : runningTarget.sourceContainerBounds) : runningTarget.startBounds, runningTarget.contentInsets); } @@ -344,6 +348,7 @@ public void apply(TransformParams params) { } float fullScreenProgress = Utilities.boundToRange(this.fullScreenProgress.value, 0, 1); + float scrollScale = this.scrollScale.value * (1f - fullScreenProgress) + fullScreenProgress; mCurrentFullscreenParams.setProgress(fullScreenProgress, recentsViewScale.value, /* taskViewScale= */1f, mTaskRect.width(), mDp, mPositionHelper); @@ -355,6 +360,8 @@ public void apply(TransformParams params) { // Apply TaskView matrix: taskRect, translate mMatrix.postTranslate(mTaskRect.left, mTaskRect.top); + mMatrix.postScale(scrollScale, scrollScale, mTaskRect.left + (mTaskRect.width() / 2), + mTaskRect.top + (mTaskRect.height() / 2)); mOrientationState.getOrientationHandler().setPrimary(mMatrix, MATRIX_POST_TRANSLATE, taskPrimaryTranslation.value); mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE, @@ -392,6 +399,8 @@ public void apply(TransformParams params) { + " recentsSecondaryT: " + recentsViewSecondaryTranslation.value + " taskSecondaryT: " + taskSecondaryTranslation.value + " recentsScroll: " + recentsViewScroll.value + + " scrollScale: " + scrollScale + + " this.scrollScale.value: " + this.scrollScale.value + " pivot: " + mPivot ); } diff --git a/quickstep/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java index 3ded6b141f9..7ef7a21fde2 100644 --- a/quickstep/src/com/android/quickstep/util/TransformParams.java +++ b/quickstep/src/com/android/quickstep/util/TransformParams.java @@ -16,15 +16,20 @@ package com.android.quickstep.util; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; import android.util.FloatProperty; import android.view.RemoteAnimationTarget; +import android.view.SurfaceControl; import com.android.app.animation.Interpolators; import com.android.launcher3.Utilities; import com.android.quickstep.RemoteAnimationTargets; import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties; +import com.android.systemui.shared.system.ActivityManagerWrapper; + +import app.lawnchair.compat.LawnchairQuickstepCompat; public class TransformParams { @@ -162,7 +167,7 @@ public SurfaceTransaction createSurfaceParams(BuilderProxy proxy) { } else { // Fade out translucent overlay. // TODO(b/303351074): use app.isNotInRecents directly once it is fixed. - boolean isNotInRecents = app.taskInfo != null + boolean isNotInRecents = LawnchairQuickstepCompat.ATLEAST_S && app.taskInfo != null && (app.taskInfo.baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0; if (app.isTranslucent && isNotInRecents) { diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index a8e78dce953..5ab8ea8939d 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -18,6 +18,7 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_180; import static android.view.View.MeasureSpec.EXACTLY; import static android.view.View.MeasureSpec.makeMeasureSpec; @@ -34,6 +35,7 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU; import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType; import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS; +import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS; import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA; import static com.android.launcher3.LauncherState.BACKGROUND_APP; @@ -219,6 +221,7 @@ import java.util.stream.Collectors; import app.lawnchair.LawnchairApp; +import app.lawnchair.compat.LawnchairQuickstepCompat; import app.lawnchair.theme.color.ColorTokens; import app.lawnchair.util.OverScrollerCompat; @@ -551,6 +554,8 @@ public Float get(RecentsView view) { private int mOverScrollShift = 0; private long mScrollLastHapticTimestamp; + private float mScrollScale = 1f; + /** * TODO: Call reloadIdNeeded in onTaskStackChanged. */ @@ -846,6 +851,8 @@ public RecentsView(Context context, @Nullable AttributeSet attrs, int defStyleAt mTintingColor = getForegroundScrimDimColor(context); + mScrollScale = getResources().getFloat(R.dimen.overview_scroll_scale); + // if multi-instance feature is enabled if (FeatureFlags.ENABLE_MULTI_INSTANCE.get()) { // invalidate the current list of tasks if filter changes with a fading in/out @@ -2736,11 +2743,16 @@ public void onGestureAnimationEnd() { setEnableFreeScroll(true); setEnableDrawingLiveTile(mCurrentGestureEndTarget == GestureState.GestureEndTarget.RECENTS); + setRunningTaskViewShowScreenshot(true); setRunningTaskHidden(false); animateUpTaskIconScale(); animateActionsViewIn(); mCurrentGestureEndTarget = null; + + switchToScreenshot( + () -> finishRecentsAnimation(true /* toRecents */, false /* shouldPip */, + null)); } /** @@ -3704,6 +3716,41 @@ public void onAnimationEnd(Animator animation) { } int scrollDiff = newScroll[i] - oldScroll[i] + offset; + if (i == dismissedIndex + 1 || + dismissedIndex == taskCount -1 && i == dismissedIndex - 1) { + if (child.getScaleX() <= dismissedTaskView.getScaleX()) { + anim.setFloat(child, SCALE_PROPERTY, + dismissedTaskView.getScaleX(), LINEAR); + if (child instanceof TaskView && mRemoteTargetHandles != null) { + TaskView tv = (TaskView) child; + for (RemoteTargetHandle rth : mRemoteTargetHandles) { + TransformParams params = rth.getTransformParams(); + RemoteAnimationTargets targets = params.getTargetSet(); + boolean match = false; + for (int id : tv.getTaskIds()) { + if (targets != null && targets.findTask(id) != null) { + match = true; + } + } + if (match) { + anim.addOnFrameCallback(() -> { + rth.getTaskViewSimulator().scrollScale.value = + mOrientationHandler.getPrimaryValue( + tv.getScaleX(), + tv.getScaleY() + ); + // if scrollDiff != 0, we redraw in later(AOSP) code + if (mEnableDrawingLiveTile && scrollDiff == 0) { + redrawLiveTile(); + } + }); + } + } + } + } else + anim.setFloat(child, SCALE_PROPERTY, 1f, LINEAR); + } + if (scrollDiff != 0) { FloatProperty translationProperty = child instanceof TaskView ? ((TaskView) child).getPrimaryDismissTranslationProperty() @@ -4459,6 +4506,7 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto .setScroll(getScrollOffset())); setImportantForAccessibility(isModal() ? IMPORTANT_FOR_ACCESSIBILITY_NO : IMPORTANT_FOR_ACCESSIBILITY_AUTO); + doScrollScale(); } private void updatePivots() { @@ -5461,7 +5509,7 @@ public void finishRecentsAnimation(boolean toRecents, boolean shouldPip, return; } - final boolean sendUserLeaveHint = toRecents && shouldPip; + final boolean sendUserLeaveHint = toRecents && shouldPip && LawnchairQuickstepCompat.ATLEAST_S; if (sendUserLeaveHint) { // Notify the SysUI to use fade-in animation when entering PiP from live tile. final SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.get(getContext()); @@ -5470,14 +5518,22 @@ public void finishRecentsAnimation(boolean toRecents, boolean shouldPip, // Transaction to hide the task to avoid flicker for entering PiP from // split-screen. // See also {@link AbsSwipeUpHandler#maybeFinishSwipeToHome}. - PictureInPictureSurfaceTransaction tx = new PictureInPictureSurfaceTransaction.Builder() - .setAlpha(0f) - .build(); - tx.setShouldDisableCanAffectSystemUiFlags(false); - int[] taskIds = TopTaskTracker.INSTANCE.get(getContext()).getRunningSplitTaskIds(); - for (int taskId : taskIds) { - mRecentsAnimationController.setFinishTaskTransaction(taskId, - tx, null /* overlay */); + try { + PictureInPictureSurfaceTransaction tx = new PictureInPictureSurfaceTransaction.Builder() + .setAlpha(0f) + .build(); + try { + tx.setShouldDisableCanAffectSystemUiFlags(false); + } catch (NoSuchMethodError n) { + Log.w(TAG, "not Android 13 qpr1 : ", n); + } + int[] taskIds = TopTaskTracker.INSTANCE.get(getContext()).getRunningSplitTaskIds(); + for (int taskId : taskIds) { + mRecentsAnimationController.setFinishTaskTransaction(taskId, + tx, null /* overlay */); + } + } catch (Throwable error) { + Log.w(TAG, "Failed PictureInPictureSurfaceTransaction: ", error); } } mRecentsAnimationController.finish(toRecents, () -> { @@ -5919,6 +5975,7 @@ private void switchToScreenshotInternal(Runnable onFinishRunnable) { ThumbnailData td = mRecentsAnimationController.screenshotTask(container.getTask().key.id); TaskThumbnailView thumbnailView = container.getThumbnailView(); if (td != null) { + container.getTask().thumbnail = td; thumbnailView.setThumbnail(container.getTask(), td); } else { thumbnailView.refresh(); @@ -6142,6 +6199,73 @@ public boolean scrollRight() { protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); dispatchScrollChanged(); + doScrollScale(); + } + + private void doScrollScale() { + if (showAsGrid()) + return; + + //nick@lmo-20231004 if rotating launcher is enabled, rotation works differently + // There are many edge cases (going from landscape app to recents, rotating in recents etc) + boolean touchInLandscape = mOrientationState.getTouchRotation() != ROTATION_0 + && mOrientationState.getTouchRotation() != ROTATION_180; + boolean layoutInLandscape = mOrientationState.getRecentsActivityRotation() != ROTATION_0 + && mOrientationState.getRecentsActivityRotation() != ROTATION_180; + boolean canRotateRecents = mOrientationState.isRecentsActivityRotationAllowed(); + int childCount = Math.min(mPageScrolls.length, getChildCount()); + int curScroll = !canRotateRecents && touchInLandscape && !layoutInLandscape + ? getScrollY() : getScrollX(); + + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + int scaleArea = child.getWidth() + mPageSpacing; + int childPosition = mPageScrolls[i]; + int scrollDelta = Math.abs(curScroll - childPosition); + if (scrollDelta > scaleArea) { + child.setScaleX(mScrollScale); + child.setScaleY(mScrollScale); + } else { + float scale = mapToRange(scrollDelta, 0, scaleArea, 1f, mScrollScale, LINEAR); + child.setScaleX(scale); + child.setScaleY(scale); + } + if (!(child instanceof TaskView && mRemoteTargetHandles != null)) continue; + TaskView tv = (TaskView) child; + for (RemoteTargetHandle rth : mRemoteTargetHandles) { + TransformParams params = rth.getTransformParams(); + RemoteAnimationTargets targets = params.getTargetSet(); + for (int id : tv.getTaskIds()) { + if (targets != null && targets.findTask(id) != null) { + rth.getTaskViewSimulator().scrollScale.value = + mOrientationHandler.getPrimaryValue( + tv.getScaleX(), + tv.getScaleY() + ); + } + } + } + } + } + + public float getScrollScale(RemoteTargetHandle rth) { + int childCount = Math.min(mPageScrolls.length, getChildCount()); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (!(child instanceof TaskView && !showAsGrid())) continue; + TaskView tv = (TaskView) child; + TransformParams params = rth.getTransformParams(); + RemoteAnimationTargets targets = params.getTargetSet(); + for (int id : tv.getTaskIds()) { + if (targets != null && targets.findTask(id) != null) { + return mOrientationHandler.getPrimaryValue( + tv.getScaleX(), + tv.getScaleY() + ); + } + } + } + return 1f; } private void dispatchScrollChanged() { diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index 849d7057a0f..439a10813f7 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -122,6 +122,8 @@ import java.util.function.Consumer; import java.util.stream.Stream; +import app.lawnchair.compat.LawnchairQuickstepCompat; + /** * A task in the Recents view. */ @@ -830,6 +832,7 @@ private void onClick(View view) { * second app. {@code false} otherwise */ protected boolean confirmSecondSplitSelectApp() { + if (!LawnchairQuickstepCompat.ATLEAST_T) return false; int index = getLastSelectedChildTaskIndex(); TaskIdAttributeContainer container = mTaskIdAttributeContainer[index]; if (container != null) { @@ -936,20 +939,25 @@ public void launchTask(@NonNull Consumer callback, boolean isQuickswitc } // Indicate success once the system has indicated that the transition has // started - ActivityOptions opts = ActivityOptions.makeCustomTaskAnimation(getContext(), 0, 0, + ActivityOptions opts = LawnchairQuickstepCompat.ATLEAST_T ? ActivityOptions.makeCustomTaskAnimation(getContext(), 0, 0, MAIN_EXECUTOR.getHandler(), elapsedRealTime -> { callback.accept(true); }, elapsedRealTime -> { failureListener.onTransitionFinished(); - }); + }) : makeCustomAnimation(getContext(), 0, 0, + () -> callback.accept(true), MAIN_EXECUTOR.getHandler());; opts.setLaunchDisplayId( getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId()); if (isQuickswitch) { opts.setFreezeRecentTasksReordering(); } - opts.setDisableStartingWindow(mSnapshotView.shouldShowSplashView()); + try { + opts.setDisableStartingWindow(mSnapshotView.shouldShowSplashView()); + } catch (Throwable t) { + // ignore + } Task.TaskKey key = mTask.key; UI_HELPER_EXECUTOR.execute(() -> { if (!ActivityManagerWrapper.getInstance().startActivityFromRecents(key, opts)) { @@ -972,6 +980,9 @@ public void launchTask(@NonNull Consumer callback, boolean isQuickswitc */ private ActivityOptions makeCustomAnimation(Context context, int enterResId, int exitResId, final Runnable callback, final Handler callbackHandler) { + if (!LawnchairQuickstepCompat.ATLEAST_T) { + return LawnchairQuickstepCompat.getActivityOptionsCompat().makeCustomAnimation(context, enterResId, exitResId, callback, callbackHandler); + } return ActivityOptions.makeCustomTaskAnimation(context, enterResId, exitResId, callbackHandler, elapsedRealTime -> { @@ -1403,6 +1414,12 @@ public void setGridProgress(float gridProgress) { } private void applyScale() { + /* This is now only for grid mode on tablets. Unconditionally calling this breaks + * our overview scrolling animation. Block this method, otherwise we have to + * introduce a multivalue scale fusion class to handle this. */ + if (!mActivity.getDeviceProfile().isTablet) + return; + float scale = 1; scale *= getPersistentScale(); scale *= mDismissScale; diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 9826ada3877..d221d0c237e 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -443,4 +443,7 @@ 300dp + + + 0.9 diff --git a/settings.gradle b/settings.gradle index c9ec12b2c35..c6049f0d581 100644 --- a/settings.gradle +++ b/settings.gradle @@ -58,7 +58,12 @@ include ':systemUILog' include ':systemUIAnim' include ':systemUnFold' include ':systemUIViewCapture' + include ':compatLib' -include ':compatLibVR' -include ':compatLibVS' +include ':compatLib:compatLibVQ' +include ':compatLib:compatLibVR' +include ':compatLib:compatLibVS' +include ':compatLib:compatLibVT' +include ':compatLib:compatLibVU' + include ':baseline-profile' diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java index b7bf4a3c6c9..a36258499e2 100644 --- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java @@ -1495,7 +1495,7 @@ public class AdapterHolder { final Rect mPadding = new Rect(); AllAppsRecyclerView mRecyclerView; - AdapterHolder(int type, LawnchairAlphabeticalAppsList appsList) { + AdapterHolder(int type, AlphabeticalAppsList appsList) { mType = type; mAppsList = appsList; mAdapter = createAdapter(mAppsList); diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java index b7db2bd698b..a054280dfe3 100644 --- a/src/com/android/launcher3/allapps/WorkModeSwitch.java +++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java @@ -99,8 +99,6 @@ protected void onFinishInflate() { var textColor = ColorTokens.ColorPrimary.resolveColor(getContext()); mIcon.setColorFilter(textColor); mTextView.setTextColor(textColor); - DeviceProfile grid = BaseDraggingActivity.fromContext(getContext()).getDeviceProfile(); - setInsets(grid.getInsets()); setInsets(mActivityContext.getDeviceProfile().getInsets()); mTextView.setText(R.string.work_apps_pause_btn_text); diff --git a/src/com/android/launcher3/graphics/SysUiScrim.java b/src/com/android/launcher3/graphics/SysUiScrim.java index b720714d464..f73e05d28b9 100644 --- a/src/com/android/launcher3/graphics/SysUiScrim.java +++ b/src/com/android/launcher3/graphics/SysUiScrim.java @@ -139,6 +139,7 @@ public SysUiScrim(View view) { * Draw the top and bottom scrims */ public void draw(Canvas canvas) { + if (canvas == null) return; if (!mHideSysUiScrim) { if (mSysUiProgress.value <= 0) { mAnimateScrimOnNextDraw = false; @@ -156,10 +157,10 @@ public void draw(Canvas canvas) { mAnimateScrimOnNextDraw = false; } - if (mDrawTopScrim) { + if (mDrawTopScrim && mTopMaskBitmap != null) { canvas.drawBitmap(mTopMaskBitmap, null, mTopMaskRect, mTopMaskPaint); } - if (mDrawBottomScrim) { + if (mDrawBottomScrim && mBottomMaskBitmap != null) { canvas.drawBitmap(mBottomMaskBitmap, null, mBottomMaskRect, mBottomMaskPaint); } } diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java index 360ff7ea253..510348a0afc 100644 --- a/src/com/android/launcher3/statemanager/StateManager.java +++ b/src/com/android/launcher3/statemanager/StateManager.java @@ -31,6 +31,7 @@ import androidx.annotation.FloatRange; +import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; @@ -238,7 +239,7 @@ private void goToState( } return; } else if ((!mConfig.userControlled && animated && mConfig.targetState == state) - || mState.shouldPreserveDataStateOnReapply()) { + || (mState.shouldPreserveDataStateOnReapply() && Utilities.ATLEAST_U)) { // We are running the same animation as requested, and/or target state should not be // reset -- allow the current animation to complete instead of canceling it. if (listener != null) { diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java index 568662fa4df..4a2665291af 100644 --- a/src/com/android/launcher3/views/OptionsPopupView.java +++ b/src/com/android/launcher3/views/OptionsPopupView.java @@ -206,6 +206,7 @@ public static ArrayList getOptions(Launcher launcher) { .firstBlocking(preferenceManager2.getLockHomeScreenButtonOnPopUp()); boolean showSystemSettings = PreferenceExtensionsKt .firstBlocking(preferenceManager2.getShowSystemSettingsEntryOnPopUp()); + boolean showEditMode = PreferenceExtensionsKt.firstBlocking(preferenceManager2.getEditHomeScreenButtonOnPopUp()); ArrayList options = new ArrayList<>(); if (showLockToggle) { @@ -235,7 +236,7 @@ public static ArrayList getOptions(Launcher launcher) { LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS, OptionsPopupView::onWidgetsClicked)); } - if (MULTI_SELECT_EDIT_MODE.get()) { + if (!lockHomeScreen && showEditMode) { options.add(new OptionItem(launcher, R.string.edit_home_screen, R.drawable.enter_home_gardening_icon, diff --git a/systemUIAnim/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/systemUIAnim/src/com/android/systemui/animation/ActivityLaunchAnimator.kt index 0f10cfd6165..cbe131c713d 100644 --- a/systemUIAnim/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/systemUIAnim/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -24,7 +24,6 @@ import android.graphics.Matrix import android.graphics.Path import android.graphics.Rect import android.graphics.RectF -import android.os.Build import android.os.Handler import android.os.Looper import android.os.RemoteException @@ -523,6 +522,10 @@ class ActivityLaunchAnimator( } } + fun onAnimationCancelled(isKeyguardOccluded: Boolean) { + onAnimationCancelled() + } + @BinderThread override fun onAnimationCancelled() { val delegate = delegate @@ -686,11 +689,12 @@ class ActivityLaunchAnimator( // instead of recomputing isExpandingFullyAbove here. val isExpandingFullyAbove = launchAnimator.isExpandingFullyAbove(controller.launchContainer, endState) + val windowCornerRadius = getWindowCornerRadius() val endRadius = if (isExpandingFullyAbove) { // Most of the time, expanding fully above the root view means expanding in full // screen. - ScreenDecorationsUtils.getWindowCornerRadius(context) + windowCornerRadius } else { // This usually means we are in split screen mode, so 2 out of 4 corners will // have @@ -758,6 +762,15 @@ class ActivityLaunchAnimator( ) } + private fun getWindowCornerRadius() : Float { + return try { + ScreenDecorationsUtils.getWindowCornerRadius(context) + } catch (t: Throwable) { + 0f + } + + } + private fun applyStateToWindow( window: RemoteAnimationTarget, state: LaunchAnimator.State, diff --git a/systemUIAnim/src/com/android/systemui/animation/ViewRootSync.kt b/systemUIAnim/src/com/android/systemui/animation/ViewRootSync.kt index e4f6db57f68..bb82b4d8c9a 100644 --- a/systemUIAnim/src/com/android/systemui/animation/ViewRootSync.kt +++ b/systemUIAnim/src/com/android/systemui/animation/ViewRootSync.kt @@ -1,5 +1,6 @@ package com.android.systemui.animation +import android.os.Build import android.view.View import android.window.SurfaceSyncGroup @@ -28,11 +29,13 @@ object ViewRootSync { return } - val syncGroup = SurfaceSyncGroup("SysUIAnimation") - syncGroup.addSyncCompleteCallback(view.context.mainExecutor) { then() } - syncGroup.add(view.rootSurfaceControl, null /* runnable */) - syncGroup.add(otherView.rootSurfaceControl, null /* runnable */) - syncGroup.markSyncReady() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + val syncGroup = SurfaceSyncGroup("SysUIAnimation") + syncGroup.addSyncCompleteCallback(view.context.mainExecutor) { then() } + syncGroup.add(view.rootSurfaceControl, null /* runnable */) + syncGroup.add(otherView.rootSurfaceControl, null /* runnable */) + syncGroup.markSyncReady() + } } /** A Java-friendly API for [synchronizeNextDraw]. */ diff --git a/systemUIShared/src/app/lawnchair/compat/LawnchairQuickstepCompat.kt b/systemUIShared/src/app/lawnchair/compat/LawnchairQuickstepCompat.kt new file mode 100644 index 00000000000..e7f36dbb462 --- /dev/null +++ b/systemUIShared/src/app/lawnchair/compat/LawnchairQuickstepCompat.kt @@ -0,0 +1,53 @@ +package app.lawnchair.compat + +import android.os.Build +import app.lawnchair.compatlib.ActivityManagerCompat +import app.lawnchair.compatlib.ActivityOptionsCompat +import app.lawnchair.compatlib.QuickstepCompatFactory +import app.lawnchair.compatlib.RemoteTransitionCompat +import app.lawnchair.compatlib.eleven.QuickstepCompatFactoryVR +import app.lawnchair.compatlib.fourteen.QuickstepCompatFactoryVU +import app.lawnchair.compatlib.ten.QuickstepCompatFactoryVQ +import app.lawnchair.compatlib.thirteen.QuickstepCompatFactoryVT +import app.lawnchair.compatlib.twelve.QuickstepCompatFactoryVS + +object LawnchairQuickstepCompat { + + @JvmField + val ATLEAST_Q: Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q + + @JvmField + val ATLEAST_R: Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R + + @JvmField + val ATLEAST_S: Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S + + @JvmField + val ATLEAST_T: Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU + + @JvmField + val ATLEAST_U: Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE + + + @JvmStatic + val factory: QuickstepCompatFactory = if (ATLEAST_U) { + QuickstepCompatFactoryVU() + } else if (ATLEAST_T) { + QuickstepCompatFactoryVT() + } else if (ATLEAST_S) { + QuickstepCompatFactoryVS() + } else if (ATLEAST_R) { + QuickstepCompatFactoryVR() + } else { + QuickstepCompatFactoryVQ() + } + + @JvmStatic + val activityManagerCompat: ActivityManagerCompat = factory.activityManagerCompat + + @JvmStatic + val activityOptionsCompat: ActivityOptionsCompat = factory.activityOptionsCompat + + @JvmStatic + val remoteTransitionCompat: RemoteTransitionCompat = factory.remoteTransitionCompat +} \ No newline at end of file diff --git a/systemUIShared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/systemUIShared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl index dc34ef75482..07fc08047e2 100644 --- a/systemUIShared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl +++ b/systemUIShared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl @@ -61,6 +61,16 @@ interface ISystemUiProxy { */ oneway void onAssistantGestureCompletion(float velocity) = 18; + /** + * Get the secondary split screen app's rectangle when not minimized. + */ + Rect getNonMinimizedSplitScreenSecondaryBounds() = 7; + + /** + * Creates a new gesture monitor + */ + Bundle monitorGestureInput(String name, int displayId) = 14; + /** * Start the assistant. */ @@ -90,6 +100,28 @@ interface ISystemUiProxy { */ oneway void stopScreenPinning() = 17; + /** + * Handle the provided image as if it was a screenshot. + * + * Deprecated, use handleImageBundleAsScreenshot with image bundle and UserTask + * @deprecated + */ + void handleImageAsScreenshot(in Bitmap screenImage, in Rect locationInScreen, + in Insets visibleInsets, int taskId) = 21; + + /** + * Sets the split-screen divider minimized state + */ + void setSplitScreenMinimized(boolean minimized) = 22; + + /* + * Notifies that the swipe-to-home (recents animation) is finished. + */ + void notifySwipeToHomeFinished() = 23; + + /** Notifies that a swipe-up gesture has started */ + void notifySwipeUpGestureStarted() = 46; + /** * Notifies that quickstep will switch to a new task * @param rotation indicates which Surface.Rotation the gesture was started in @@ -134,6 +166,12 @@ interface ISystemUiProxy { */ oneway void takeScreenshot(in ScreenshotRequest request) = 51; + /** + * Handle the provided image as if it was a screenshot. + */ + void handleImageBundleAsScreenshot(in Bundle screenImageBundle, in Rect locationInScreen, + in Insets visibleInsets, in Task.TaskKey task) = 26; + /** * Dispatches trackpad status bar motion event to the notification shade. Currently these events * are from the input monitor in {@link TouchInteractionService}. This is different from diff --git a/systemUIShared/src/com/android/systemui/shared/recents/model/Task.java b/systemUIShared/src/com/android/systemui/shared/recents/model/Task.java index 3605ac2bfc6..62aeaa07c20 100644 --- a/systemUIShared/src/com/android/systemui/shared/recents/model/Task.java +++ b/systemUIShared/src/com/android/systemui/shared/recents/model/Task.java @@ -42,6 +42,8 @@ import java.io.PrintWriter; import java.util.Objects; +import app.lawnchair.compat.LawnchairQuickstepCompat; + /** * A task in the recent tasks list. * TODO: Move this into Launcher or see if we can remove now @@ -245,7 +247,9 @@ public int describeContents() { // Last snapshot data, only used for recent tasks public ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData = - new ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData(); + LawnchairQuickstepCompat.ATLEAST_S + ? new ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData() + : null; public Task() { // Do nothing @@ -258,7 +262,7 @@ public static Task from(TaskKey taskKey, TaskInfo taskInfo, boolean isLocked) { ActivityManager.TaskDescription td = taskInfo.taskDescription; // Also consider undefined activity type to include tasks in overview right after rebooting // the device. - final boolean isDockable = taskInfo.supportsMultiWindow + final boolean isDockable = LawnchairQuickstepCompat.ATLEAST_S && taskInfo.supportsMultiWindow && ArrayUtils.contains( CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE, taskInfo.getWindowingMode()) && (taskInfo.getActivityType() == ACTIVITY_TYPE_UNDEFINED @@ -277,7 +281,10 @@ public Task(TaskKey key) { public Task(Task other) { this(other.key, other.colorPrimary, other.colorBackground, other.isDockable, other.isLocked, other.taskDescription, other.topActivity); - lastSnapshotData.set(other.lastSnapshotData); + if (LawnchairQuickstepCompat.ATLEAST_S) { + ((ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData) lastSnapshotData) + .set((ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData) other.lastSnapshotData); + } positionInParent = other.positionInParent; appBounds = other.appBounds; } @@ -308,7 +315,10 @@ public ComponentName getTopComponent() { } public void setLastSnapshotData(ActivityManager.RecentTaskInfo rawTask) { - lastSnapshotData.set(rawTask.lastSnapshotData); + if (LawnchairQuickstepCompat.ATLEAST_S) { + ((ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData) lastSnapshotData) + .set(rawTask.lastSnapshotData); + } } public TaskKey getKey() { @@ -319,6 +329,9 @@ public TaskKey getKey() { * Returns the visible width to height ratio. Returns 0f if snapshot data is not available. */ public float getVisibleThumbnailRatio(boolean clipInsets) { + ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData = + (ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData) this.lastSnapshotData; + if (lastSnapshotData.taskSize == null || lastSnapshotData.contentInsets == null) { return 0f; } diff --git a/systemUIShared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/systemUIShared/src/com/android/systemui/shared/recents/model/ThumbnailData.java index 0b0df833e91..ade699e8b34 100644 --- a/systemUIShared/src/com/android/systemui/shared/recents/model/ThumbnailData.java +++ b/systemUIShared/src/com/android/systemui/shared/recents/model/ThumbnailData.java @@ -20,6 +20,10 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import static android.graphics.Bitmap.Config.ARGB_8888; +import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; +import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; +import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; +import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; import android.graphics.Bitmap; import android.graphics.Color; @@ -32,6 +36,9 @@ import java.util.HashMap; +import app.lawnchair.compat.LawnchairQuickstepCompat; +import app.lawnchair.compatlib.eleven.ActivityManagerCompatVR; + /** * Data for a single thumbnail. */ @@ -101,10 +108,31 @@ public static HashMap wrap(int[] taskIds, TaskSnapshot[] return temp; } + public ThumbnailData(ActivityManagerCompatVR.ThumbnailData data) { + letterboxInsets = new Rect(); + thumbnail = data.thumbnail; + insets = data.insets; + orientation = data.orientation; + rotation = data.rotation; + reducedResolution = data.reducedResolution; + scale = data.scale; + isRealSnapshot = data.isRealSnapshot; + isTranslucent = data.isTranslucent; + windowingMode = data.windowingMode; + appearance = 0; + if ((data.systemUiVisibility & SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0) { + appearance |= APPEARANCE_LIGHT_STATUS_BARS; + } + if ((data.systemUiVisibility & SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0) { + appearance |= APPEARANCE_LIGHT_NAVIGATION_BARS; + } + snapshotId = data.snapshotId; + } + public ThumbnailData(TaskSnapshot snapshot) { thumbnail = makeThumbnail(snapshot); insets = new Rect(snapshot.getContentInsets()); - letterboxInsets = new Rect(snapshot.getLetterboxInsets()); + letterboxInsets = LawnchairQuickstepCompat.ATLEAST_T ? new Rect(snapshot.getLetterboxInsets()) : new Rect(); orientation = snapshot.getOrientation(); rotation = snapshot.getRotation(); reducedResolution = snapshot.isLowResolution(); diff --git a/systemUIShared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/systemUIShared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index 45a5ce34f83..630032d8de5 100644 --- a/systemUIShared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/systemUIShared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -23,7 +23,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; -import android.app.ActivityClient; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityOptions; @@ -44,9 +43,7 @@ import android.os.SystemClock; import android.provider.Settings; import android.util.Log; -import android.view.Display; import android.view.IRecentsAnimationController; -import android.view.IRecentsAnimationRunner; import android.view.RemoteAnimationTarget; import android.window.TaskSnapshot; @@ -54,9 +51,15 @@ import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.function.Consumer; +import app.lawnchair.compat.LawnchairQuickstepCompat; +import app.lawnchair.compatlib.RecentsAnimationRunnerCompat; +import app.lawnchair.compatlib.eleven.ActivityManagerCompatVR; + public class ActivityManagerWrapper { private static final String TAG = "ActivityManagerWrapper"; @@ -70,7 +73,7 @@ public class ActivityManagerWrapper { // Should match the value in AssistManager private static final String INVOCATION_TIME_MS_KEY = "invocation_time_ms"; - private final ActivityTaskManager mAtm = ActivityTaskManager.getInstance(); + private final ActivityTaskManager mAtm = LawnchairQuickstepCompat.ATLEAST_S ? ActivityTaskManager.getInstance() : null; private ActivityManagerWrapper() { } public static ActivityManagerWrapper getInstance() { @@ -103,19 +106,21 @@ public ActivityManager.RunningTaskInfo getRunningTask() { */ public ActivityManager.RunningTaskInfo getRunningTask(boolean filterOnlyVisibleRecents) { // Note: The set of running tasks from the system is ordered by recency - List tasks = - mAtm.getTasks(1, filterOnlyVisibleRecents); - if (tasks.isEmpty()) { - return null; - } - return tasks.get(0); + return LawnchairQuickstepCompat.getActivityManagerCompat().getRunningTask(filterOnlyVisibleRecents); } /** * @see #getRunningTasks(boolean , int) */ public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisibleRecents) { - return getRunningTasks(filterOnlyVisibleRecents, Display.INVALID_DISPLAY); + return LawnchairQuickstepCompat.getActivityManagerCompat().getRunningTasks(filterOnlyVisibleRecents); + } + + /** + * @return a list of the recents tasks. + */ + public List getRecentTasks(int numTasks, int userId) { + return LawnchairQuickstepCompat.getActivityManagerCompat().getRecentTasks(numTasks, userId); } /** @@ -130,10 +135,11 @@ public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisib public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisibleRecents, int displayId) { // Note: The set of running tasks from the system is ordered by recency - List tasks = + List tasks = mAtm != null ? mAtm.getTasks(NUM_RECENT_ACTIVITIES_REQUEST, - filterOnlyVisibleRecents, /* keepInExtras= */ false, displayId); - return tasks.toArray(new RunningTaskInfo[tasks.size()]); + filterOnlyVisibleRecents, /* keepInExtras= */ false, displayId) : new ArrayList<>(); + return LawnchairQuickstepCompat.ATLEAST_U ? tasks.toArray(new RunningTaskInfo[tasks.size()]) + : LawnchairQuickstepCompat.getActivityManagerCompat().getRunningTasks(filterOnlyVisibleRecents); } /** @@ -141,13 +147,16 @@ public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisib * The snapshot will be triggered if no cached {@link TaskSnapshot} exists. */ public @NonNull ThumbnailData getTaskThumbnail(int taskId, boolean isLowResolution) { - TaskSnapshot snapshot = null; - try { - snapshot = getService().getTaskSnapshot(taskId, isLowResolution, - true /* takeSnapshotIfNeeded */); - } catch (RemoteException e) { - Log.w(TAG, "Failed to retrieve task snapshot", e); + if (!LawnchairQuickstepCompat.ATLEAST_S){ + ActivityManagerCompatVR compat = ((ActivityManagerCompatVR) LawnchairQuickstepCompat.getActivityManagerCompat()); + ActivityManagerCompatVR.ThumbnailData data = compat.getTaskThumbnail(taskId, isLowResolution); + if (data != null) { + return new ThumbnailData(data); + } else { + return new ThumbnailData(); + } } + TaskSnapshot snapshot = LawnchairQuickstepCompat.getActivityManagerCompat().getTaskSnapshot(taskId, isLowResolution, true); if (snapshot != null) { return new ThumbnailData(snapshot); } else { @@ -163,12 +172,7 @@ public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisib * want us to find the home task for you. */ public void invalidateHomeTaskSnapshot(@Nullable final Activity homeActivity) { - try { - ActivityClient.getInstance().invalidateHomeTaskSnapshot( - homeActivity == null ? null : homeActivity.getActivityToken()); - } catch (Throwable e) { - Log.w(TAG, "Failed to invalidate home snapshot", e); - } + LawnchairQuickstepCompat.getActivityManagerCompat().invalidateHomeTaskSnapshot(homeActivity); } /** @@ -194,13 +198,13 @@ public void run() { public boolean startRecentsActivity( Intent intent, long eventTime, RecentsAnimationListener animationHandler) { try { - IRecentsAnimationRunner runner = null; + RecentsAnimationRunnerCompat runner = null; if (animationHandler != null) { - runner = new IRecentsAnimationRunner.Stub() { + runner = new RecentsAnimationRunnerCompat() { @Override public void onAnimationStart(IRecentsAnimationController controller, - RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, - Rect homeContentInsets, Rect minimizedHomeBounds) { + RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, + Rect homeContentInsets, Rect minimizedHomeBounds) { final RecentsAnimationControllerCompat controllerCompat = new RecentsAnimationControllerCompat(controller); animationHandler.onAnimationStart(controllerCompat, apps, @@ -213,13 +217,41 @@ public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots) { ThumbnailData.wrap(taskIds, taskSnapshots)); } + + /** + * compat for android 12/11/10 + */ + public void onAnimationCanceled(Object taskSnapshot) { + if (LawnchairQuickstepCompat.ATLEAST_S) { + animationHandler.onAnimationCanceled( + ThumbnailData.wrap(new int[]{0}, new TaskSnapshot[]{(TaskSnapshot) taskSnapshot})); + } else if (LawnchairQuickstepCompat.ATLEAST_R) { + ActivityManagerCompatVR compat = (ActivityManagerCompatVR) LawnchairQuickstepCompat.getActivityManagerCompat(); + ActivityManagerCompatVR.ThumbnailData data = compat.convertTaskSnapshotToThumbnailData(taskSnapshot); + HashMap thumbnailDatas = new HashMap<>(); + if (data != null) { + thumbnailDatas.put(0, new ThumbnailData(data)); + } + animationHandler.onAnimationCanceled(thumbnailDatas); + } else { + animationHandler.onAnimationCanceled(new HashMap<>()); + } + } + + /** + * compat for android 12/11 + */ + public void onTaskAppeared(RemoteAnimationTarget app) { + animationHandler.onTasksAppeared(new RemoteAnimationTarget[]{app}); + } + @Override public void onTasksAppeared(RemoteAnimationTarget[] apps) { animationHandler.onTasksAppeared(apps); } }; } - getService().startRecentsActivity(intent, eventTime, runner); + LawnchairQuickstepCompat.getActivityManagerCompat().startRecentsActivity(intent, eventTime, runner); return true; } catch (Exception e) { return false; diff --git a/systemUIShared/src/com/android/systemui/shared/system/InputChannelCompat.java b/systemUIShared/src/com/android/systemui/shared/system/InputChannelCompat.java index 259cca8c01e..7a29b70f91a 100644 --- a/systemUIShared/src/com/android/systemui/shared/system/InputChannelCompat.java +++ b/systemUIShared/src/com/android/systemui/shared/system/InputChannelCompat.java @@ -24,6 +24,8 @@ import android.view.InputEvent; import android.view.MotionEvent; +import app.lawnchair.compat.LawnchairQuickstepCompat; + /** * @see android.view.InputChannel */ @@ -81,7 +83,9 @@ public void onInputEvent(InputEvent event) { * @see BatchedInputEventReceiver#setBatchingEnabled() */ public void setBatchingEnabled(boolean batchingEnabled) { - mReceiver.setBatchingEnabled(batchingEnabled); + if (LawnchairQuickstepCompat.ATLEAST_S) { + mReceiver.setBatchingEnabled(batchingEnabled); + } } /** diff --git a/systemUIShared/src/com/android/systemui/shared/system/InputMonitorCompat.java b/systemUIShared/src/com/android/systemui/shared/system/InputMonitorCompat.java index c4aac111f24..d5f3e5de2f1 100644 --- a/systemUIShared/src/com/android/systemui/shared/system/InputMonitorCompat.java +++ b/systemUIShared/src/com/android/systemui/shared/system/InputMonitorCompat.java @@ -15,7 +15,10 @@ */ package com.android.systemui.shared.system; +import android.hardware.input.InputManager; import android.hardware.input.InputManagerGlobal; +import android.os.Build; +import android.os.Bundle; import android.os.Looper; import android.view.Choreographer; import android.view.InputMonitor; @@ -33,8 +36,7 @@ public class InputMonitorCompat { * Monitor input on the specified display for gestures. */ public InputMonitorCompat(String name, int displayId) { - mInputMonitor = InputManagerGlobal.getInstance() - .monitorGestureInput(name, displayId); + mInputMonitor = InputManager.getInstance().monitorGestureInput(name, displayId); } /** @@ -59,4 +61,14 @@ public InputEventReceiver getInputReceiver(Looper looper, Choreographer choreogr return new InputEventReceiver(mInputMonitor.getInputChannel(), looper, choreographer, listener); } + + private InputMonitorCompat(InputMonitor monitor) { + mInputMonitor = monitor; + } + /** + * Gets the input monitor stored in a bundle + */ + public static InputMonitorCompat fromBundle(Bundle bundle, String key) { + return new InputMonitorCompat((InputMonitor)(bundle.getParcelable(key))); + } } diff --git a/systemUIShared/src/com/android/systemui/shared/system/PackageManagerWrapper.java b/systemUIShared/src/com/android/systemui/shared/system/PackageManagerWrapper.java index 5c37eccef12..79fc219266b 100644 --- a/systemUIShared/src/com/android/systemui/shared/system/PackageManagerWrapper.java +++ b/systemUIShared/src/com/android/systemui/shared/system/PackageManagerWrapper.java @@ -18,6 +18,7 @@ import android.app.AppGlobals; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.IPackageManager; @@ -54,6 +55,14 @@ public ActivityInfo getActivityInfo(ComponentName componentName, int userId) { } catch (RemoteException e) { e.printStackTrace(); return null; + } catch (Throwable throwable){ + try { + Context context = AppGlobals.getInitialApplication(); + return context.getPackageManager().getActivityInfo(componentName, PackageManager.GET_META_DATA); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + return null; + } } } diff --git a/systemUIShared/src/com/android/systemui/shared/system/QuickStepContract.java b/systemUIShared/src/com/android/systemui/shared/system/QuickStepContract.java index f5b1832cc58..b044b433095 100644 --- a/systemUIShared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/systemUIShared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -33,6 +33,8 @@ import java.lang.annotation.RetentionPolicy; import java.util.StringJoiner; +import app.lawnchair.compat.LawnchairQuickstepCompat; + /** * Various shared constants between Launcher and SysUI as part of quickstep */ @@ -383,13 +385,17 @@ public static boolean isLegacyMode(int mode) { * scaling, this means that we don't have to reload them on config changes. */ public static float getWindowCornerRadius(Context context) { - if (sRecentsDisabled) { + if (sRecentsDisabled || !LawnchairQuickstepCompat.ATLEAST_S) { return 0; } if (sHasCustomCornerRadius) { return sCustomCornerRadius; } - return ScreenDecorationsUtils.getWindowCornerRadius(context); + try { + return ScreenDecorationsUtils.getWindowCornerRadius(context); + } catch (Throwable t) { + return 0; + } } /** diff --git a/systemUIShared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/systemUIShared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java index 0094820f0da..c0bdc395cbd 100644 --- a/systemUIShared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java +++ b/systemUIShared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java @@ -25,6 +25,9 @@ import com.android.systemui.shared.recents.model.ThumbnailData; +import app.lawnchair.compat.LawnchairQuickstepCompat; +import app.lawnchair.compatlib.eleven.ActivityManagerCompatVR; + public class RecentsAnimationControllerCompat { private static final String TAG = RecentsAnimationControllerCompat.class.getSimpleName(); @@ -38,6 +41,11 @@ public RecentsAnimationControllerCompat(IRecentsAnimationController animationCon } public ThumbnailData screenshotTask(int taskId) { + if (!LawnchairQuickstepCompat.ATLEAST_S) { + ActivityManagerCompatVR compat = (ActivityManagerCompatVR) LawnchairQuickstepCompat.getActivityManagerCompat(); + ActivityManagerCompatVR.ThumbnailData data = compat.takeScreenshot(mAnimationController, taskId); + return data != null ? new ThumbnailData(data) : new ThumbnailData(); + } try { final TaskSnapshot snapshot = mAnimationController.screenshotTask(taskId); if (snapshot != null) { @@ -117,6 +125,7 @@ public void cleanupScreenshot() { * @see {{@link IRecentsAnimationController#setWillFinishToHome(boolean)}}. */ public void setWillFinishToHome(boolean willFinishToHome) { + if (!LawnchairQuickstepCompat.ATLEAST_R) return; try { mAnimationController.setWillFinishToHome(willFinishToHome); } catch (RemoteException e) { @@ -140,6 +149,7 @@ public boolean removeTask(int taskId) { * @see IRecentsAnimationController#detachNavigationBarFromApp */ public void detachNavigationBarFromApp(boolean moveHomeToTop) { + if (!LawnchairQuickstepCompat.ATLEAST_S) return; try { mAnimationController.detachNavigationBarFromApp(moveHomeToTop); } catch (RemoteException e) { @@ -151,6 +161,7 @@ public void detachNavigationBarFromApp(boolean moveHomeToTop) { * @see IRecentsAnimationController#animateNavigationBarToApp(long) */ public void animateNavigationBarToApp(long duration) { + if (!LawnchairQuickstepCompat.ATLEAST_S) return; try { mAnimationController.animateNavigationBarToApp(duration); } catch (RemoteException e) { diff --git a/systemUIShared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java b/systemUIShared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java index 6178da43bfc..6e7462d86b1 100644 --- a/systemUIShared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java +++ b/systemUIShared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java @@ -43,6 +43,8 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import app.lawnchair.compat.LawnchairQuickstepCompat; + public abstract class RemoteAnimationRunnerCompat extends IRemoteAnimationRunner.Stub { private static final String TAG = "RemoteAnimRunnerCompat"; @@ -67,19 +69,34 @@ public final void onAnimationStart(@TransitionOldType int transit, }); } - public IRemoteTransition toRemoteTransition() { - return wrap(this); + // Called only in R + public void onAnimationStart(RemoteAnimationTarget[] appTargets, + RemoteAnimationTarget[] wallpaperTargets, IRemoteAnimationFinishedCallback finishedCallback) { + onAnimationStart(0 /* transit */, appTargets, wallpaperTargets, + new RemoteAnimationTarget[0], finishedCallback); + } + + // Called only in Q + public void onAnimationStart(RemoteAnimationTarget[] appTargets, + IRemoteAnimationFinishedCallback finishedCallback) { + onAnimationStart(appTargets, new RemoteAnimationTarget[0], finishedCallback); + } + + public void onAnimationCancelled(boolean isKeyguardOccluded) {} + + // Called only in S + public void onAnimationCancelled() { + onAnimationCancelled(true); } - /** Wraps a remote animation runner in a remote-transition. */ - public static IRemoteTransition.Stub wrap(IRemoteAnimationRunner runner) { + public IRemoteTransition toRemoteTransition() { return new IRemoteTransition.Stub() { final ArrayMap mFinishRunnables = new ArrayMap<>(); @Override public void startAnimation(IBinder token, TransitionInfo info, SurfaceControl.Transaction t, - IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { + IRemoteTransitionFinishedCallback finishCallback) { final ArrayMap leashMap = new ArrayMap<>(); final RemoteAnimationTarget[] apps = RemoteAnimationTargetCompat.wrapApps(info, t, leashMap); @@ -164,6 +181,8 @@ public void startAnimation(IBinder token, TransitionInfo info, counterLauncher.addChild(t, leashMap.get(launcherTask.getLeash())); } if (wallpaper != null && rotateDelta != 0 && wallpaper.getParent() != null) { + counterWallpaper.setup(t, info.getChange(wallpaper.getParent()).getLeash(), + rotateDelta, displayW, displayH); final TransitionInfo.Change parent = info.getChange(wallpaper.getParent()); if (parent != null) { counterWallpaper.setup(t, parent.getLeash(), rotateDelta, displayW, @@ -187,13 +206,7 @@ public void startAnimation(IBinder token, TransitionInfo info, counterWallpaper.cleanUp(finishTransaction); // Release surface references now. This is apparently to free GPU memory // before GC would. - try { - Method method = info.getClass ().getMethod ("releaseAllSurfaces"); - method.invoke (info); - } catch (NoSuchMethodException | IllegalAccessException | - InvocationTargetException e) { - Log.e ("animationFinishedCallback" , "mergeAnimation: ", e); - } + info.releaseAllSurfaces(); // Don't release here since launcher might still be using them. Instead // let launcher release them (eg. via RemoteAnimationTargets) leashMap.clear(); @@ -208,20 +221,12 @@ public void startAnimation(IBinder token, TransitionInfo info, mFinishRunnables.put(token, animationFinishedCallback); } // TODO(bc-unlcok): Pass correct transit type. - runner.onAnimationStart(TRANSIT_OLD_NONE, - apps, wallpapers, nonApps, new IRemoteAnimationFinishedCallback() { - @Override - public void onAnimationFinished() { - synchronized (mFinishRunnables) { - if (mFinishRunnables.remove(token) == null) return; - } - animationFinishedCallback.run(); - } - - @Override - public IBinder asBinder() { - return null; + onAnimationStart(TRANSIT_OLD_NONE, + apps, wallpapers, nonApps, () -> { + synchronized (mFinishRunnables) { + if (mFinishRunnables.remove(token) == null) return; } + animationFinishedCallback.run(); }); } @@ -237,14 +242,9 @@ public void mergeAnimation(IBinder token, TransitionInfo info, } // Since we're not actually animating, release native memory now t.close(); - try { - Method method = info.getClass ().getMethod ("releaseAllSurfaces"); - method.invoke (info); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - Log.e ("" , "mergeAnimation: ", e); - } + info.releaseAllSurfaces(); if (finishRunnable == null) return; - runner.onAnimationCancelled(); + onAnimationCancelled(false /* isKeyguardOccluded */); finishRunnable.run(); } }; diff --git a/systemUIShared/src/com/android/systemui/shared/system/SystemGestureExclusionListenerCompat.java b/systemUIShared/src/com/android/systemui/shared/system/SystemGestureExclusionListenerCompat.java index aeb0415c8c4..b5b2ac6e7e3 100644 --- a/systemUIShared/src/com/android/systemui/shared/system/SystemGestureExclusionListenerCompat.java +++ b/systemUIShared/src/com/android/systemui/shared/system/SystemGestureExclusionListenerCompat.java @@ -21,6 +21,8 @@ import android.view.ISystemGestureExclusionListener; import android.view.WindowManagerGlobal; +import app.lawnchair.compat.LawnchairQuickstepCompat; + /** * Utility class to listen for exclusion rect changes. */ @@ -41,6 +43,12 @@ public void onSystemGestureExclusionChanged(int displayId, onExclusionChanged(systemGestureExclusion, unrestricted); } } + + public void onSystemGestureExclusionChanged(int displayId, + Region systemGestureExclusion) { + if (LawnchairQuickstepCompat.ATLEAST_U) return; + onSystemGestureExclusionChanged(displayId, systemGestureExclusion, null); + } }; private boolean mRegistered; diff --git a/systemUIViewCapture/src/com/android/app/viewcapture/SettingsAwareViewCapture.kt b/systemUIViewCapture/src/com/android/app/viewcapture/SettingsAwareViewCapture.kt index 9a857c35449..f75166b73e1 100644 --- a/systemUIViewCapture/src/com/android/app/viewcapture/SettingsAwareViewCapture.kt +++ b/systemUIViewCapture/src/com/android/app/viewcapture/SettingsAwareViewCapture.kt @@ -19,6 +19,7 @@ package com.android.app.viewcapture import android.content.Context import android.content.pm.LauncherApps import android.database.ContentObserver +import android.os.Build import android.os.Handler import android.os.Looper import android.os.ParcelFileDescriptor @@ -42,7 +43,7 @@ class SettingsAwareViewCapture internal constructor(private val context: Context, executor: Executor) : ViewCapture(DEFAULT_MEMORY_SIZE, DEFAULT_INIT_POOL_SIZE, executor) { /** Dumps all the active view captures to the wm trace directory via LauncherAppService */ - private val mDumpCallback: IDumpCallback.Stub = object : IDumpCallback.Stub() { + private val mDumpCallback: IDumpCallback.Stub? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) object : IDumpCallback.Stub() { override fun onDump(out: ParcelFileDescriptor) { try { ParcelFileDescriptor.AutoCloseOutputStream(out).use { os -> dumpTo(os, context) } @@ -50,7 +51,7 @@ internal constructor(private val context: Context, executor: Executor) Log.e(TAG, "failed to dump data to wm trace", e) } } - } + } else null init { enableOrDisableWindowListeners() @@ -73,10 +74,12 @@ internal constructor(private val context: Context, executor: Executor) enableOrDisableWindowListeners(isEnabled) } val launcherApps = context.getSystemService(LauncherApps::class.java) - if (isEnabled) { - launcherApps?.registerDumpCallback(mDumpCallback) - } else { - launcherApps?.unRegisterDumpCallback(mDumpCallback) + if (mDumpCallback != null) { + if (isEnabled) { + launcherApps?.registerDumpCallback(mDumpCallback) + } else { + launcherApps?.unRegisterDumpCallback(mDumpCallback) + } } } }