QuickSwitch Compat Q to U (#3948)

Initial support quickstep, Android versions from Q to U, ensuring a smooth and efficient user experience. Enjoy effortless navigation and swift app switching on your Android device with QuickSwitch's compatibility across the Q, R, S, T, and U.


Co-authored-by: Goooler <wangzongler@gmail.com>
Co-authored-by: 无言 <57122860+liu-wanshun@users.noreply.github.com>
This commit is contained in:
John Andrew Camu
2024-01-26 05:59:47 +08:00
committed by GitHub
parent f63aa1e315
commit c31b41ae73
111 changed files with 4549 additions and 604 deletions
@@ -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:
* <p><ol>
* <li>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.
* <li>Shell then associates the transition for the event with the IRemoteTransition
* <li>Shell receives onTransitionReady and delegates the animation to the IRemoteTransition
* via {@link #startAnimation}.
* <li>Once the IRemoteTransition is done animating, it will call the finishCallback.
* <li>Shell/Core finish-up the transition.
* </ul>
*
* {@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);
}
@@ -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);
}
@@ -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;
@@ -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<RemoteTransition> CREATOR =
new Parcelable.Creator<RemoteTransition>() {
@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
}
@@ -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;
@@ -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<TransitionFilter> CREATOR =
new Creator<TransitionFilter>() {
@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<Requirement> CREATOR =
new Creator<Requirement>() {
@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 + ")";
}
}
@@ -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;
File diff suppressed because it is too large Load Diff