Loading core/java/android/app/ActivityOptions.java +29 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ import android.view.RemoteAnimationAdapter; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.window.IRemoteTransition; import android.window.WindowContainerToken; import java.lang.annotation.Retention; Loading Loading @@ -298,6 +299,8 @@ public class ActivityOptions { private static final String KEY_SPECS_FUTURE = "android:activity.specsFuture"; private static final String KEY_REMOTE_ANIMATION_ADAPTER = "android:activity.remoteAnimationAdapter"; private static final String KEY_REMOTE_TRANSITION = "android:activity.remoteTransition"; /** * @see #setLaunchCookie Loading Loading @@ -380,6 +383,7 @@ public class ActivityOptions { private IAppTransitionAnimationSpecsFuture mSpecsFuture; private RemoteAnimationAdapter mRemoteAnimationAdapter; private IBinder mLaunchCookie; private IRemoteTransition mRemoteTransition; /** * Create an ActivityOptions specifying a custom animation to run when Loading Loading @@ -959,6 +963,21 @@ public class ActivityOptions { return opts; } /** * Create an {@link ActivityOptions} instance that lets the application control the entire * animation using a {@link RemoteAnimationAdapter}. * @hide */ @RequiresPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS) public static ActivityOptions makeRemoteAnimation(RemoteAnimationAdapter remoteAnimationAdapter, IRemoteTransition remoteTransition) { final ActivityOptions opts = new ActivityOptions(); opts.mRemoteAnimationAdapter = remoteAnimationAdapter; opts.mAnimationType = ANIM_REMOTE_ANIMATION; opts.mRemoteTransition = remoteTransition; return opts; } /** @hide */ public boolean getLaunchTaskBehind() { return mAnimationType == ANIM_LAUNCH_TASK_BEHIND; Loading Loading @@ -1064,6 +1083,8 @@ public class ActivityOptions { } mRemoteAnimationAdapter = opts.getParcelable(KEY_REMOTE_ANIMATION_ADAPTER); mLaunchCookie = opts.getBinder(KEY_LAUNCH_COOKIE); mRemoteTransition = IRemoteTransition.Stub.asInterface(opts.getBinder( KEY_REMOTE_TRANSITION)); } /** Loading Loading @@ -1222,6 +1243,11 @@ public class ActivityOptions { mRemoteAnimationAdapter = remoteAnimationAdapter; } /** @hide */ public IRemoteTransition getRemoteTransition() { return mRemoteTransition; } /** @hide */ public static ActivityOptions fromBundle(Bundle bOptions) { return bOptions != null ? new ActivityOptions(bOptions) : null; Loading Loading @@ -1724,6 +1750,9 @@ public class ActivityOptions { if (mLaunchCookie != null) { b.putBinder(KEY_LAUNCH_COOKIE, mLaunchCookie); } if (mRemoteTransition != null) { b.putBinder(KEY_REMOTE_TRANSITION, mRemoteTransition.asBinder()); } return b; } Loading core/java/android/window/IRemoteTransition.aidl 0 → 100644 +46 −0 Original line number Diff line number Diff line /* * 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.IRemoteAnimationFinishedCallback; import android.view.SurfaceControl; 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`. */ void startAnimation(in TransitionInfo info, in SurfaceControl.Transaction t, in IRemoteAnimationFinishedCallback finishCallback); } core/java/android/window/ITransitionPlayer.aidl +3 −7 Original line number Diff line number Diff line Loading @@ -16,10 +16,9 @@ package android.window; import android.app.ActivityManager; import android.view.SurfaceControl; import android.window.TransitionInfo; import android.window.WindowContainerTransaction; import android.window.TransitionRequestInfo; /** * Implemented by WMShell to initiate and play transition animations. Loading Loading @@ -56,12 +55,9 @@ oneway interface ITransitionPlayer { * Called when something in WMCore requires a transition to play -- for example when an Activity * is started in a new Task. * * @param type The {@link WindowManager#TransitionType} of the transition to start. * @param transitionToken An identifying token for the transition that needs to be started. * Pass this to {@link IWindowOrganizerController#startTransition}. * @param triggerTask If non-null, the task containing the activity whose lifecycle change * (start or finish) has caused this transition to occur. * @param request Information about this particular request. */ void requestStartTransition(int type, in IBinder transitionToken, in ActivityManager.RunningTaskInfo triggerTask); void requestStartTransition(in IBinder transitionToken, in TransitionRequestInfo request); } core/java/android/window/TransitionFilter.aidl 0 → 100644 +19 −0 Original line number Diff line number Diff line /* * 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; core/java/android/window/TransitionFilter.java 0 → 100644 +215 −0 Original line number Diff line number Diff line /* * 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 android.annotation.NonNull; import android.annotation.Nullable; import android.app.WindowConfiguration; import android.os.Parcel; import android.os.Parcelable; /** * 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 { /** * 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 int[] mTypeSet = null; /** * 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(); 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; } // Make sure info meets all of the requirements. if (mRequirements != null) { for (int i = 0; i < mRequirements.length; ++i) { if (!mRequirements[i].matches(info)) return false; } } return true; } @Override /** @hide */ public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeIntArray(mTypeSet); 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 ? "" : ",") + mTypeSet[i]); } } 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; public int[] mModes = null; public Requirement() { } private Requirement(Parcel in) { mActivityType = in.readInt(); mModes = in.createIntArray(); } /** 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 (change.getParent() != null) { // Only look at the top animating windows. continue; } if (mActivityType != ACTIVITY_TYPE_UNDEFINED) { if (change.getTaskInfo() == null || change.getTaskInfo().getActivityType() != mActivityType) { 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; } return true; } return false; } /** Check if the request matches this filter. It may generate false positives */ boolean matches(@NonNull TransitionRequestInfo request) { // Can't check modes 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; } @Override /** @hide */ public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mActivityType); dest.writeIntArray(mModes); } @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("{atype=" + WindowConfiguration.activityTypeToString(mActivityType)); out.append(" modes=["); if (mModes != null) { for (int i = 0; i < mModes.length; ++i) { out.append((i == 0 ? "" : ",") + TransitionInfo.modeToString(mModes[i])); } } return out.append("]}").toString(); } } } Loading
core/java/android/app/ActivityOptions.java +29 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,7 @@ import android.view.RemoteAnimationAdapter; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.window.IRemoteTransition; import android.window.WindowContainerToken; import java.lang.annotation.Retention; Loading Loading @@ -298,6 +299,8 @@ public class ActivityOptions { private static final String KEY_SPECS_FUTURE = "android:activity.specsFuture"; private static final String KEY_REMOTE_ANIMATION_ADAPTER = "android:activity.remoteAnimationAdapter"; private static final String KEY_REMOTE_TRANSITION = "android:activity.remoteTransition"; /** * @see #setLaunchCookie Loading Loading @@ -380,6 +383,7 @@ public class ActivityOptions { private IAppTransitionAnimationSpecsFuture mSpecsFuture; private RemoteAnimationAdapter mRemoteAnimationAdapter; private IBinder mLaunchCookie; private IRemoteTransition mRemoteTransition; /** * Create an ActivityOptions specifying a custom animation to run when Loading Loading @@ -959,6 +963,21 @@ public class ActivityOptions { return opts; } /** * Create an {@link ActivityOptions} instance that lets the application control the entire * animation using a {@link RemoteAnimationAdapter}. * @hide */ @RequiresPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS) public static ActivityOptions makeRemoteAnimation(RemoteAnimationAdapter remoteAnimationAdapter, IRemoteTransition remoteTransition) { final ActivityOptions opts = new ActivityOptions(); opts.mRemoteAnimationAdapter = remoteAnimationAdapter; opts.mAnimationType = ANIM_REMOTE_ANIMATION; opts.mRemoteTransition = remoteTransition; return opts; } /** @hide */ public boolean getLaunchTaskBehind() { return mAnimationType == ANIM_LAUNCH_TASK_BEHIND; Loading Loading @@ -1064,6 +1083,8 @@ public class ActivityOptions { } mRemoteAnimationAdapter = opts.getParcelable(KEY_REMOTE_ANIMATION_ADAPTER); mLaunchCookie = opts.getBinder(KEY_LAUNCH_COOKIE); mRemoteTransition = IRemoteTransition.Stub.asInterface(opts.getBinder( KEY_REMOTE_TRANSITION)); } /** Loading Loading @@ -1222,6 +1243,11 @@ public class ActivityOptions { mRemoteAnimationAdapter = remoteAnimationAdapter; } /** @hide */ public IRemoteTransition getRemoteTransition() { return mRemoteTransition; } /** @hide */ public static ActivityOptions fromBundle(Bundle bOptions) { return bOptions != null ? new ActivityOptions(bOptions) : null; Loading Loading @@ -1724,6 +1750,9 @@ public class ActivityOptions { if (mLaunchCookie != null) { b.putBinder(KEY_LAUNCH_COOKIE, mLaunchCookie); } if (mRemoteTransition != null) { b.putBinder(KEY_REMOTE_TRANSITION, mRemoteTransition.asBinder()); } return b; } Loading
core/java/android/window/IRemoteTransition.aidl 0 → 100644 +46 −0 Original line number Diff line number Diff line /* * 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.IRemoteAnimationFinishedCallback; import android.view.SurfaceControl; 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`. */ void startAnimation(in TransitionInfo info, in SurfaceControl.Transaction t, in IRemoteAnimationFinishedCallback finishCallback); }
core/java/android/window/ITransitionPlayer.aidl +3 −7 Original line number Diff line number Diff line Loading @@ -16,10 +16,9 @@ package android.window; import android.app.ActivityManager; import android.view.SurfaceControl; import android.window.TransitionInfo; import android.window.WindowContainerTransaction; import android.window.TransitionRequestInfo; /** * Implemented by WMShell to initiate and play transition animations. Loading Loading @@ -56,12 +55,9 @@ oneway interface ITransitionPlayer { * Called when something in WMCore requires a transition to play -- for example when an Activity * is started in a new Task. * * @param type The {@link WindowManager#TransitionType} of the transition to start. * @param transitionToken An identifying token for the transition that needs to be started. * Pass this to {@link IWindowOrganizerController#startTransition}. * @param triggerTask If non-null, the task containing the activity whose lifecycle change * (start or finish) has caused this transition to occur. * @param request Information about this particular request. */ void requestStartTransition(int type, in IBinder transitionToken, in ActivityManager.RunningTaskInfo triggerTask); void requestStartTransition(in IBinder transitionToken, in TransitionRequestInfo request); }
core/java/android/window/TransitionFilter.aidl 0 → 100644 +19 −0 Original line number Diff line number Diff line /* * 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;
core/java/android/window/TransitionFilter.java 0 → 100644 +215 −0 Original line number Diff line number Diff line /* * 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 android.annotation.NonNull; import android.annotation.Nullable; import android.app.WindowConfiguration; import android.os.Parcel; import android.os.Parcelable; /** * 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 { /** * 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 int[] mTypeSet = null; /** * 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(); 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; } // Make sure info meets all of the requirements. if (mRequirements != null) { for (int i = 0; i < mRequirements.length; ++i) { if (!mRequirements[i].matches(info)) return false; } } return true; } @Override /** @hide */ public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeIntArray(mTypeSet); 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 ? "" : ",") + mTypeSet[i]); } } 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; public int[] mModes = null; public Requirement() { } private Requirement(Parcel in) { mActivityType = in.readInt(); mModes = in.createIntArray(); } /** 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 (change.getParent() != null) { // Only look at the top animating windows. continue; } if (mActivityType != ACTIVITY_TYPE_UNDEFINED) { if (change.getTaskInfo() == null || change.getTaskInfo().getActivityType() != mActivityType) { 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; } return true; } return false; } /** Check if the request matches this filter. It may generate false positives */ boolean matches(@NonNull TransitionRequestInfo request) { // Can't check modes 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; } @Override /** @hide */ public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mActivityType); dest.writeIntArray(mModes); } @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("{atype=" + WindowConfiguration.activityTypeToString(mActivityType)); out.append(" modes=["); if (mModes != null) { for (int i = 0; i < mModes.length; ++i) { out.append((i == 0 ? "" : ",") + TransitionInfo.modeToString(mModes[i])); } } return out.append("]}").toString(); } } }