Merge changes from topic "remote_animations"
* changes: Remote animations (app-controlled animations) Defer hiding clients until animation is done
This commit is contained in:
@@ -324,6 +324,8 @@ java_library {
|
||||
"core/java/android/view/IOnKeyguardExitResult.aidl",
|
||||
"core/java/android/view/IPinnedStackController.aidl",
|
||||
"core/java/android/view/IPinnedStackListener.aidl",
|
||||
"core/java/android/view/IRemoteAnimationRunner.aidl",
|
||||
"core/java/android/view/IRemoteAnimationFinishedCallback.aidl",
|
||||
"core/java/android/view/IRotationWatcher.aidl",
|
||||
"core/java/android/view/IWallpaperVisibilityListener.aidl",
|
||||
"core/java/android/view/IWindow.aidl",
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
|
||||
package android.app;
|
||||
|
||||
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
|
||||
import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||
import static android.view.Display.INVALID_DISPLAY;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.TestApi;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
@@ -44,6 +46,7 @@ import android.util.Pair;
|
||||
import android.util.Slog;
|
||||
import android.view.AppTransitionAnimationSpec;
|
||||
import android.view.IAppTransitionAnimationSpecsFuture;
|
||||
import android.view.RemoteAnimationAdapter;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
@@ -241,6 +244,8 @@ public class ActivityOptions {
|
||||
private static final String KEY_INSTANT_APP_VERIFICATION_BUNDLE
|
||||
= "android:instantapps.installerbundle";
|
||||
private static final String KEY_SPECS_FUTURE = "android:activity.specsFuture";
|
||||
private static final String KEY_REMOTE_ANIMATION_ADAPTER
|
||||
= "android:activity.remoteAnimationAdapter";
|
||||
|
||||
/** @hide */
|
||||
public static final int ANIM_NONE = 0;
|
||||
@@ -268,6 +273,8 @@ public class ActivityOptions {
|
||||
public static final int ANIM_CLIP_REVEAL = 11;
|
||||
/** @hide */
|
||||
public static final int ANIM_OPEN_CROSS_PROFILE_APPS = 12;
|
||||
/** @hide */
|
||||
public static final int ANIM_REMOTE_ANIMATION = 13;
|
||||
|
||||
private String mPackageName;
|
||||
private Rect mLaunchBounds;
|
||||
@@ -304,6 +311,7 @@ public class ActivityOptions {
|
||||
private int mRotationAnimationHint = -1;
|
||||
private Bundle mAppVerificationBundle;
|
||||
private IAppTransitionAnimationSpecsFuture mSpecsFuture;
|
||||
private RemoteAnimationAdapter mRemoteAnimationAdapter;
|
||||
|
||||
/**
|
||||
* Create an ActivityOptions specifying a custom animation to run when
|
||||
@@ -826,6 +834,20 @@ 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) {
|
||||
final ActivityOptions opts = new ActivityOptions();
|
||||
opts.mRemoteAnimationAdapter = remoteAnimationAdapter;
|
||||
opts.mAnimationType = ANIM_REMOTE_ANIMATION;
|
||||
return opts;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public boolean getLaunchTaskBehind() {
|
||||
return mAnimationType == ANIM_LAUNCH_TASK_BEHIND;
|
||||
@@ -922,6 +944,7 @@ public class ActivityOptions {
|
||||
mSpecsFuture = IAppTransitionAnimationSpecsFuture.Stub.asInterface(opts.getBinder(
|
||||
KEY_SPECS_FUTURE));
|
||||
}
|
||||
mRemoteAnimationAdapter = opts.getParcelable(KEY_REMOTE_ANIMATION_ADAPTER);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1069,6 +1092,11 @@ public class ActivityOptions {
|
||||
return mSpecsFuture;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public RemoteAnimationAdapter getRemoteAnimationAdapter() {
|
||||
return mRemoteAnimationAdapter;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static ActivityOptions fromBundle(Bundle bOptions) {
|
||||
return bOptions != null ? new ActivityOptions(bOptions) : null;
|
||||
@@ -1309,6 +1337,7 @@ public class ActivityOptions {
|
||||
mAnimSpecs = otherOptions.mAnimSpecs;
|
||||
mAnimationFinishedListener = otherOptions.mAnimationFinishedListener;
|
||||
mSpecsFuture = otherOptions.mSpecsFuture;
|
||||
mRemoteAnimationAdapter = otherOptions.mRemoteAnimationAdapter;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1403,7 +1432,9 @@ public class ActivityOptions {
|
||||
if (mAppVerificationBundle != null) {
|
||||
b.putBundle(KEY_INSTANT_APP_VERIFICATION_BUNDLE, mAppVerificationBundle);
|
||||
}
|
||||
|
||||
if (mRemoteAnimationAdapter != null) {
|
||||
b.putParcelable(KEY_REMOTE_ANIMATION_ADAPTER, mRemoteAnimationAdapter);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
27
core/java/android/view/IRemoteAnimationFinishedCallback.aidl
Normal file
27
core/java/android/view/IRemoteAnimationFinishedCallback.aidl
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
package android.view;
|
||||
|
||||
/**
|
||||
* Interface to be invoked by the controlling process when a remote animation has finished.
|
||||
*
|
||||
* @see IRemoteAnimationRunner
|
||||
* {@hide}
|
||||
*/
|
||||
interface IRemoteAnimationFinishedCallback {
|
||||
void onAnimationFinished();
|
||||
}
|
||||
44
core/java/android/view/IRemoteAnimationRunner.aidl
Normal file
44
core/java/android/view/IRemoteAnimationRunner.aidl
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.view;
|
||||
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.view.IRemoteAnimationFinishedCallback;
|
||||
|
||||
/**
|
||||
* Interface that is used to callback from window manager to the process that runs a remote
|
||||
* animation to start or cancel it.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
oneway interface IRemoteAnimationRunner {
|
||||
|
||||
/**
|
||||
* Called when the process needs to start the remote animation.
|
||||
*
|
||||
* @param apps The list of apps to animate.
|
||||
* @param finishedCallback The callback to invoke when the animation is finished.
|
||||
*/
|
||||
void onAnimationStart(in RemoteAnimationTarget[] apps,
|
||||
in IRemoteAnimationFinishedCallback finishedCallback);
|
||||
|
||||
/**
|
||||
* Called when the animation was cancelled. From this point on, any updates onto the leashes
|
||||
* won't have any effect anymore.
|
||||
*/
|
||||
void onAnimationCancelled();
|
||||
}
|
||||
@@ -38,6 +38,7 @@ import android.view.IAppTransitionAnimationSpecsFuture;
|
||||
import android.view.IDockedStackListener;
|
||||
import android.view.IOnKeyguardExitResult;
|
||||
import android.view.IPinnedStackListener;
|
||||
import android.view.RemoteAnimationAdapter;
|
||||
import android.view.IRotationWatcher;
|
||||
import android.view.IWallpaperVisibilityListener;
|
||||
import android.view.IWindowSession;
|
||||
@@ -124,6 +125,7 @@ interface IWindowManager
|
||||
void overridePendingAppTransitionMultiThumbFuture(
|
||||
IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback startedCallback,
|
||||
boolean scaleUp);
|
||||
void overridePendingAppTransitionRemote(in RemoteAnimationAdapter remoteAnimationAdapter);
|
||||
void executeAppTransition();
|
||||
|
||||
/** Used by system ui to report that recents has shown itself. */
|
||||
|
||||
19
core/java/android/view/RemoteAnimationAdapter.aidl
Normal file
19
core/java/android/view/RemoteAnimationAdapter.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
package android.view;
|
||||
|
||||
parcelable RemoteAnimationAdapter;
|
||||
108
core/java/android/view/RemoteAnimationAdapter.java
Normal file
108
core/java/android/view/RemoteAnimationAdapter.java
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
package android.view;
|
||||
|
||||
import android.app.ActivityOptions;
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* Object that describes how to run a remote animation.
|
||||
* <p>
|
||||
* A remote animation lets another app control the entire app transition. It does so by
|
||||
* <ul>
|
||||
* <li>using {@link ActivityOptions#makeRemoteAnimation}</li>
|
||||
* <li>using {@link IWindowManager#overridePendingAppTransitionRemote}</li>
|
||||
* </ul>
|
||||
* to register a {@link RemoteAnimationAdapter} that describes how the animation should be run:
|
||||
* Along some meta-data, this object contains a callback that gets invoked from window manager when
|
||||
* the transition is ready to be started.
|
||||
* <p>
|
||||
* Window manager supplies a list of {@link RemoteAnimationTarget}s into the callback. Each target
|
||||
* contains information about the activity that is animating as well as
|
||||
* {@link RemoteAnimationTarget#leash}. The controlling app can modify the leash like any other
|
||||
* {@link SurfaceControl}, including the possibility to synchronize updating the leash's surface
|
||||
* properties with a frame to be drawn using
|
||||
* {@link SurfaceControl.Transaction#deferTransactionUntil}.
|
||||
* <p>
|
||||
* When the animation is done, the controlling app can invoke
|
||||
* {@link IRemoteAnimationFinishedCallback} that gets supplied into
|
||||
* {@link IRemoteAnimationRunner#onStartAnimation}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class RemoteAnimationAdapter implements Parcelable {
|
||||
|
||||
private final IRemoteAnimationRunner mRunner;
|
||||
private final long mDuration;
|
||||
private final long mStatusBarTransitionDelay;
|
||||
|
||||
/**
|
||||
* @param runner The interface that gets notified when we actually need to start the animation.
|
||||
* @param duration The duration of the animation.
|
||||
* @param statusBarTransitionDelay The desired delay for all visual animations in the
|
||||
* status bar caused by this app animation in millis.
|
||||
*/
|
||||
public RemoteAnimationAdapter(IRemoteAnimationRunner runner, long duration,
|
||||
long statusBarTransitionDelay) {
|
||||
mRunner = runner;
|
||||
mDuration = duration;
|
||||
mStatusBarTransitionDelay = statusBarTransitionDelay;
|
||||
}
|
||||
|
||||
public RemoteAnimationAdapter(Parcel in) {
|
||||
mRunner = IRemoteAnimationRunner.Stub.asInterface(in.readStrongBinder());
|
||||
mDuration = in.readLong();
|
||||
mStatusBarTransitionDelay = in.readLong();
|
||||
}
|
||||
|
||||
public IRemoteAnimationRunner getRunner() {
|
||||
return mRunner;
|
||||
}
|
||||
|
||||
public long getDuration() {
|
||||
return mDuration;
|
||||
}
|
||||
|
||||
public long getStatusBarTransitionDelay() {
|
||||
return mStatusBarTransitionDelay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeStrongInterface(mRunner);
|
||||
dest.writeLong(mDuration);
|
||||
dest.writeLong(mStatusBarTransitionDelay);
|
||||
}
|
||||
|
||||
public static final Creator<RemoteAnimationAdapter> CREATOR
|
||||
= new Creator<RemoteAnimationAdapter>() {
|
||||
public RemoteAnimationAdapter createFromParcel(Parcel in) {
|
||||
return new RemoteAnimationAdapter(in);
|
||||
}
|
||||
|
||||
public RemoteAnimationAdapter[] newArray(int size) {
|
||||
return new RemoteAnimationAdapter[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
19
core/java/android/view/RemoteAnimationTarget.aidl
Normal file
19
core/java/android/view/RemoteAnimationTarget.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
package android.view;
|
||||
|
||||
parcelable RemoteAnimationTarget;
|
||||
151
core/java/android/view/RemoteAnimationTarget.java
Normal file
151
core/java/android/view/RemoteAnimationTarget.java
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
package android.view;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Describes an activity to be animated as part of a remote animation.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class RemoteAnimationTarget implements Parcelable {
|
||||
|
||||
/**
|
||||
* The app is in the set of opening apps of this transition.
|
||||
*/
|
||||
public static final int MODE_OPENING = 0;
|
||||
|
||||
/**
|
||||
* The app is in the set of closing apps of this transition.
|
||||
*/
|
||||
public static final int MODE_CLOSING = 1;
|
||||
|
||||
@IntDef(prefix = { "MODE_" }, value = {
|
||||
MODE_OPENING,
|
||||
MODE_CLOSING
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Mode {}
|
||||
|
||||
/**
|
||||
* The {@link Mode} to describe whether this app is opening or closing.
|
||||
*/
|
||||
public final @Mode int mode;
|
||||
|
||||
/**
|
||||
* The id of the task this app belongs to.
|
||||
*/
|
||||
public final int taskId;
|
||||
|
||||
/**
|
||||
* The {@link SurfaceControl} object to actually control the transform of the app.
|
||||
*/
|
||||
public final SurfaceControl leash;
|
||||
|
||||
/**
|
||||
* Whether the app is translucent and may reveal apps behind.
|
||||
*/
|
||||
public final boolean isTranslucent;
|
||||
|
||||
/**
|
||||
* The clip rect window manager applies when clipping the app's main surface in screen space
|
||||
* coordinates. This is just a hint to the animation runner: If running a clip-rect animation,
|
||||
* anything that extends beyond these bounds will not have any effect. This implies that any
|
||||
* clip-rect animation should likely stop at these bounds.
|
||||
*/
|
||||
public final Rect clipRect;
|
||||
|
||||
/**
|
||||
* The index of the element in the tree in prefix order. This should be used for z-layering
|
||||
* to preserve original z-layer order in the hierarchy tree assuming no "boosting" needs to
|
||||
* happen.
|
||||
*/
|
||||
public final int prefixOrderIndex;
|
||||
|
||||
/**
|
||||
* The source position of the app, in screen spaces coordinates. If the position of the leash
|
||||
* is modified from the controlling app, any animation transform needs to be offset by this
|
||||
* amount.
|
||||
*/
|
||||
public final Point position;
|
||||
|
||||
/**
|
||||
* The bounds of the source container the app lives in, in screen space coordinates. If the crop
|
||||
* of the leash is modified from the controlling app, it needs to take the source container
|
||||
* bounds into account when calculating the crop.
|
||||
*/
|
||||
public final Rect sourceContainerBounds;
|
||||
|
||||
public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent,
|
||||
Rect clipRect, int prefixOrderIndex, Point position, Rect sourceContainerBounds) {
|
||||
this.mode = mode;
|
||||
this.taskId = taskId;
|
||||
this.leash = leash;
|
||||
this.isTranslucent = isTranslucent;
|
||||
this.clipRect = new Rect(clipRect);
|
||||
this.prefixOrderIndex = prefixOrderIndex;
|
||||
this.position = new Point(position);
|
||||
this.sourceContainerBounds = new Rect(sourceContainerBounds);
|
||||
}
|
||||
|
||||
public RemoteAnimationTarget(Parcel in) {
|
||||
taskId = in.readInt();
|
||||
mode = in.readInt();
|
||||
leash = in.readParcelable(null);
|
||||
isTranslucent = in.readBoolean();
|
||||
clipRect = in.readParcelable(null);
|
||||
prefixOrderIndex = in.readInt();
|
||||
position = in.readParcelable(null);
|
||||
sourceContainerBounds = in.readParcelable(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(taskId);
|
||||
dest.writeInt(mode);
|
||||
dest.writeParcelable(leash, 0 /* flags */);
|
||||
dest.writeBoolean(isTranslucent);
|
||||
dest.writeParcelable(clipRect, 0 /* flags */);
|
||||
dest.writeInt(prefixOrderIndex);
|
||||
dest.writeParcelable(position, 0 /* flags */);
|
||||
dest.writeParcelable(sourceContainerBounds, 0 /* flags */);
|
||||
}
|
||||
|
||||
public static final Creator<RemoteAnimationTarget> CREATOR
|
||||
= new Creator<RemoteAnimationTarget>() {
|
||||
public RemoteAnimationTarget createFromParcel(Parcel in) {
|
||||
return new RemoteAnimationTarget(in);
|
||||
}
|
||||
|
||||
public RemoteAnimationTarget[] newArray(int size) {
|
||||
return new RemoteAnimationTarget[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -3692,6 +3692,12 @@
|
||||
<permission android:name="android.permission.MODIFY_QUIET_MODE"
|
||||
android:protectionLevel="signature|privileged" />
|
||||
|
||||
<!-- Allows an application to control remote animations. See
|
||||
{@link ActivityOptions#makeRemoteAnimation}
|
||||
@hide <p>Not for use by third-party applications. -->
|
||||
<permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"
|
||||
android:protectionLevel="signature|privileged" />
|
||||
|
||||
<application android:process="system"
|
||||
android:persistent="true"
|
||||
android:hasCode="false"
|
||||
|
||||
@@ -75,6 +75,7 @@ applications that come with the platform
|
||||
|
||||
<privapp-permissions package="com.android.launcher3">
|
||||
<permission name="android.permission.BIND_APPWIDGET"/>
|
||||
<permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
|
||||
<permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
|
||||
</privapp-permissions>
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
|
||||
import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
|
||||
import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
|
||||
import static android.app.ActivityOptions.ANIM_CUSTOM;
|
||||
import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
|
||||
import static android.app.ActivityOptions.ANIM_SCALE_UP;
|
||||
import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
|
||||
import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
|
||||
@@ -1481,6 +1482,10 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
|
||||
case ANIM_OPEN_CROSS_PROFILE_APPS:
|
||||
service.mWindowManager.overridePendingAppTransitionStartCrossProfileApps();
|
||||
break;
|
||||
case ANIM_REMOTE_ANIMATION:
|
||||
service.mWindowManager.overridePendingAppTransitionRemote(
|
||||
pendingOptions.getRemoteAnimationAdapter());
|
||||
break;
|
||||
default:
|
||||
Slog.e(TAG, "applyOptionsLocked: Unknown animationType=" + animationType);
|
||||
break;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.am;
|
||||
|
||||
import static android.Manifest.permission.ACTIVITY_EMBEDDING;
|
||||
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
|
||||
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
|
||||
import static android.Manifest.permission.START_ANY_ACTIVITY;
|
||||
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
|
||||
@@ -162,6 +163,7 @@ import android.util.SparseIntArray;
|
||||
import android.util.TimeUtils;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
import android.view.Display;
|
||||
import android.view.RemoteAnimationAdapter;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.content.ReferrerIntent;
|
||||
@@ -1680,6 +1682,18 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
|
||||
Slog.w(TAG, msg);
|
||||
throw new SecurityException(msg);
|
||||
}
|
||||
|
||||
// Check permission for remote animations
|
||||
final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
|
||||
if (adapter != null && mService.checkPermission(
|
||||
CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, callingPid, callingUid)
|
||||
!= PERMISSION_GRANTED) {
|
||||
final String msg = "Permission Denial: starting " + intent.toString()
|
||||
+ " from " + callerApp + " (pid=" + callingPid
|
||||
+ ", uid=" + callingUid + ") with remoteAnimationAdapter";
|
||||
Slog.w(TAG, msg);
|
||||
throw new SecurityException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -75,6 +75,7 @@ import android.util.proto.ProtoOutputStream;
|
||||
import android.view.AppTransitionAnimationSpec;
|
||||
import android.view.DisplayListCanvas;
|
||||
import android.view.IAppTransitionAnimationSpecsFuture;
|
||||
import android.view.RemoteAnimationAdapter;
|
||||
import android.view.RenderNode;
|
||||
import android.view.ThreadedRenderer;
|
||||
import android.view.WindowManager;
|
||||
@@ -213,6 +214,8 @@ public class AppTransition implements Dump {
|
||||
* }.
|
||||
*/
|
||||
private static final int NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS = 9;
|
||||
private static final int NEXT_TRANSIT_TYPE_REMOTE = 10;
|
||||
|
||||
private int mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
|
||||
|
||||
// These are the possible states for the enter/exit activities during a thumbnail transition
|
||||
@@ -275,6 +278,8 @@ public class AppTransition implements Dump {
|
||||
private final boolean mGridLayoutRecentsEnabled;
|
||||
private final boolean mLowRamRecentsEnabled;
|
||||
|
||||
private RemoteAnimationController mRemoteAnimationController;
|
||||
|
||||
AppTransition(Context context, WindowManagerService service) {
|
||||
mContext = context;
|
||||
mService = service;
|
||||
@@ -454,6 +459,9 @@ public class AppTransition implements Dump {
|
||||
app.startDelayingAnimationStart();
|
||||
}
|
||||
}
|
||||
if (mRemoteAnimationController != null) {
|
||||
mRemoteAnimationController.goodToGo();
|
||||
}
|
||||
return redoLayout;
|
||||
}
|
||||
|
||||
@@ -468,6 +476,7 @@ public class AppTransition implements Dump {
|
||||
mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
|
||||
mNextAppTransitionPackage = null;
|
||||
mNextAppTransitionAnimationsSpecs.clear();
|
||||
mRemoteAnimationController = null;
|
||||
mNextAppTransitionAnimationsSpecsFuture = null;
|
||||
mDefaultNextAppTransitionAnimationSpec = null;
|
||||
mAnimationFinishedCallback = null;
|
||||
@@ -1551,6 +1560,10 @@ public class AppTransition implements Dump {
|
||||
&& mNextAppTransition != TRANSIT_KEYGUARD_GOING_AWAY;
|
||||
}
|
||||
|
||||
RemoteAnimationController getRemoteAnimationController() {
|
||||
return mRemoteAnimationController;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param frame These are the bounds of the window when it finishes the animation. This is where
|
||||
@@ -1788,7 +1801,7 @@ public class AppTransition implements Dump {
|
||||
|
||||
void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
|
||||
IRemoteCallback startedCallback) {
|
||||
if (isTransitionSet()) {
|
||||
if (canOverridePendingAppTransition()) {
|
||||
clear();
|
||||
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
|
||||
mNextAppTransitionPackage = packageName;
|
||||
@@ -1796,14 +1809,12 @@ public class AppTransition implements Dump {
|
||||
mNextAppTransitionExit = exitAnim;
|
||||
postAnimationCallback();
|
||||
mNextAppTransitionCallback = startedCallback;
|
||||
} else {
|
||||
postAnimationCallback();
|
||||
}
|
||||
}
|
||||
|
||||
void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
|
||||
int startHeight) {
|
||||
if (isTransitionSet()) {
|
||||
if (canOverridePendingAppTransition()) {
|
||||
clear();
|
||||
mNextAppTransitionType = NEXT_TRANSIT_TYPE_SCALE_UP;
|
||||
putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight, null);
|
||||
@@ -1813,7 +1824,7 @@ public class AppTransition implements Dump {
|
||||
|
||||
void overridePendingAppTransitionClipReveal(int startX, int startY,
|
||||
int startWidth, int startHeight) {
|
||||
if (isTransitionSet()) {
|
||||
if (canOverridePendingAppTransition()) {
|
||||
clear();
|
||||
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CLIP_REVEAL;
|
||||
putDefaultNextAppTransitionCoordinates(startX, startY, startWidth, startHeight, null);
|
||||
@@ -1823,7 +1834,7 @@ public class AppTransition implements Dump {
|
||||
|
||||
void overridePendingAppTransitionThumb(GraphicBuffer srcThumb, int startX, int startY,
|
||||
IRemoteCallback startedCallback, boolean scaleUp) {
|
||||
if (isTransitionSet()) {
|
||||
if (canOverridePendingAppTransition()) {
|
||||
clear();
|
||||
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP
|
||||
: NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN;
|
||||
@@ -1831,14 +1842,12 @@ public class AppTransition implements Dump {
|
||||
putDefaultNextAppTransitionCoordinates(startX, startY, 0, 0, srcThumb);
|
||||
postAnimationCallback();
|
||||
mNextAppTransitionCallback = startedCallback;
|
||||
} else {
|
||||
postAnimationCallback();
|
||||
}
|
||||
}
|
||||
|
||||
void overridePendingAppTransitionAspectScaledThumb(GraphicBuffer srcThumb, int startX, int startY,
|
||||
int targetWidth, int targetHeight, IRemoteCallback startedCallback, boolean scaleUp) {
|
||||
if (isTransitionSet()) {
|
||||
if (canOverridePendingAppTransition()) {
|
||||
clear();
|
||||
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
|
||||
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
|
||||
@@ -1847,15 +1856,13 @@ public class AppTransition implements Dump {
|
||||
srcThumb);
|
||||
postAnimationCallback();
|
||||
mNextAppTransitionCallback = startedCallback;
|
||||
} else {
|
||||
postAnimationCallback();
|
||||
}
|
||||
}
|
||||
|
||||
public void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
|
||||
void overridePendingAppTransitionMultiThumb(AppTransitionAnimationSpec[] specs,
|
||||
IRemoteCallback onAnimationStartedCallback, IRemoteCallback onAnimationFinishedCallback,
|
||||
boolean scaleUp) {
|
||||
if (isTransitionSet()) {
|
||||
if (canOverridePendingAppTransition()) {
|
||||
clear();
|
||||
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
|
||||
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
|
||||
@@ -1878,15 +1885,13 @@ public class AppTransition implements Dump {
|
||||
postAnimationCallback();
|
||||
mNextAppTransitionCallback = onAnimationStartedCallback;
|
||||
mAnimationFinishedCallback = onAnimationFinishedCallback;
|
||||
} else {
|
||||
postAnimationCallback();
|
||||
}
|
||||
}
|
||||
|
||||
void overridePendingAppTransitionMultiThumbFuture(
|
||||
IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback,
|
||||
boolean scaleUp) {
|
||||
if (isTransitionSet()) {
|
||||
if (canOverridePendingAppTransition()) {
|
||||
clear();
|
||||
mNextAppTransitionType = scaleUp ? NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP
|
||||
: NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
|
||||
@@ -1896,14 +1901,21 @@ public class AppTransition implements Dump {
|
||||
}
|
||||
}
|
||||
|
||||
void overrideInPlaceAppTransition(String packageName, int anim) {
|
||||
void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter) {
|
||||
if (isTransitionSet()) {
|
||||
clear();
|
||||
mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE;
|
||||
mRemoteAnimationController = new RemoteAnimationController(mService,
|
||||
remoteAnimationAdapter, mService.mH);
|
||||
}
|
||||
}
|
||||
|
||||
void overrideInPlaceAppTransition(String packageName, int anim) {
|
||||
if (canOverridePendingAppTransition()) {
|
||||
clear();
|
||||
mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE;
|
||||
mNextAppTransitionPackage = packageName;
|
||||
mNextAppTransitionInPlace = anim;
|
||||
} else {
|
||||
postAnimationCallback();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1911,13 +1923,18 @@ public class AppTransition implements Dump {
|
||||
* @see {@link #NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS}
|
||||
*/
|
||||
void overridePendingAppTransitionStartCrossProfileApps() {
|
||||
if (isTransitionSet()) {
|
||||
if (canOverridePendingAppTransition()) {
|
||||
clear();
|
||||
mNextAppTransitionType = NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS;
|
||||
postAnimationCallback();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canOverridePendingAppTransition() {
|
||||
// Remote animations always take precedence
|
||||
return isTransitionSet() && mNextAppTransitionType != NEXT_TRANSIT_TYPE_REMOTE;
|
||||
}
|
||||
|
||||
/**
|
||||
* If a future is set for the app transition specs, fetch it in another thread.
|
||||
*/
|
||||
|
||||
@@ -378,7 +378,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
|
||||
// Reset the state of mHiddenSetFromTransferredStartingWindow since visibility is actually
|
||||
// been set by the app now.
|
||||
mHiddenSetFromTransferredStartingWindow = false;
|
||||
setClientHidden(!visible);
|
||||
|
||||
// Allow for state changes and animation to be applied if:
|
||||
// * token is transitioning visibility state
|
||||
@@ -463,6 +462,12 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
|
||||
mService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(token);
|
||||
}
|
||||
|
||||
// Update the client visibility if we are not running an animation. Otherwise, we'll
|
||||
// update client visibility state in onAnimationFinished.
|
||||
if (!visible && !delayed) {
|
||||
setClientHidden(true);
|
||||
}
|
||||
|
||||
// If we are hidden but there is no delay needed we immediately
|
||||
// apply the Surface transaction so that the ActivityManager
|
||||
// can have some guarantee on the Surface state following
|
||||
@@ -1611,27 +1616,37 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
|
||||
// different animation is running.
|
||||
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AWT#applyAnimationLocked");
|
||||
if (okToAnimate()) {
|
||||
final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
|
||||
if (a != null) {
|
||||
final TaskStack stack = getStack();
|
||||
mTmpPoint.set(0, 0);
|
||||
mTmpRect.setEmpty();
|
||||
if (stack != null) {
|
||||
stack.getRelativePosition(mTmpPoint);
|
||||
stack.getBounds(mTmpRect);
|
||||
mTmpRect.offsetTo(0, 0);
|
||||
}
|
||||
final AnimationAdapter adapter = new LocalAnimationAdapter(
|
||||
new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
|
||||
mService.mAppTransition.canSkipFirstFrame(),
|
||||
mService.mAppTransition.getAppStackClipMode()),
|
||||
mService.mSurfaceAnimationRunner);
|
||||
if (a.getZAdjustment() == Animation.ZORDER_TOP) {
|
||||
mNeedsZBoost = true;
|
||||
final AnimationAdapter adapter;
|
||||
final TaskStack stack = getStack();
|
||||
mTmpPoint.set(0, 0);
|
||||
mTmpRect.setEmpty();
|
||||
if (stack != null) {
|
||||
stack.getRelativePosition(mTmpPoint);
|
||||
stack.getBounds(mTmpRect);
|
||||
mTmpRect.offsetTo(0, 0);
|
||||
}
|
||||
if (mService.mAppTransition.getRemoteAnimationController() != null) {
|
||||
adapter = mService.mAppTransition.getRemoteAnimationController()
|
||||
.createAnimationAdapter(this, mTmpPoint, mTmpRect);
|
||||
} else {
|
||||
final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
|
||||
if (a != null) {
|
||||
adapter = new LocalAnimationAdapter(
|
||||
new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
|
||||
mService.mAppTransition.canSkipFirstFrame(),
|
||||
mService.mAppTransition.getAppStackClipMode()),
|
||||
mService.mSurfaceAnimationRunner);
|
||||
if (a.getZAdjustment() == Animation.ZORDER_TOP) {
|
||||
mNeedsZBoost = true;
|
||||
}
|
||||
mTransit = transit;
|
||||
mTransitFlags = mService.mAppTransition.getTransitFlags();
|
||||
} else {
|
||||
adapter = null;
|
||||
}
|
||||
}
|
||||
if (adapter != null) {
|
||||
startAnimation(getPendingTransaction(), adapter, !isVisible());
|
||||
mTransit = transit;
|
||||
mTransitFlags = mService.mAppTransition.getTransitFlags();
|
||||
}
|
||||
} else {
|
||||
cancelAnimation();
|
||||
@@ -1754,6 +1769,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
|
||||
"AppWindowToken");
|
||||
|
||||
clearThumbnail();
|
||||
setClientHidden(isHidden());
|
||||
|
||||
if (mService.mInputMethodTarget != null && mService.mInputMethodTarget.mAppToken == this) {
|
||||
getDisplayContent().computeImeTarget(true /* updateImeTarget */);
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Slog;
|
||||
import android.view.IRemoteAnimationFinishedCallback;
|
||||
import android.view.IRemoteAnimationFinishedCallback.Stub;
|
||||
import android.view.RemoteAnimationAdapter;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceControl.Transaction;
|
||||
|
||||
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Helper class to run app animations in a remote process.
|
||||
*/
|
||||
class RemoteAnimationController {
|
||||
private static final String TAG = TAG_WITH_CLASS_NAME ? "RemoteAnimationController" : TAG_WM;
|
||||
private static final long TIMEOUT_MS = 2000;
|
||||
|
||||
private final WindowManagerService mService;
|
||||
private final RemoteAnimationAdapter mRemoteAnimationAdapter;
|
||||
private final ArrayList<RemoteAnimationAdapterWrapper> mPendingAnimations = new ArrayList<>();
|
||||
private final Rect mTmpRect = new Rect();
|
||||
private final Handler mHandler;
|
||||
|
||||
private final IRemoteAnimationFinishedCallback mFinishedCallback = new Stub() {
|
||||
@Override
|
||||
public void onAnimationFinished() throws RemoteException {
|
||||
RemoteAnimationController.this.onAnimationFinished();
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable mTimeoutRunnable = () -> {
|
||||
onAnimationFinished();
|
||||
invokeAnimationCancelled();
|
||||
};
|
||||
|
||||
RemoteAnimationController(WindowManagerService service,
|
||||
RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) {
|
||||
mService = service;
|
||||
mRemoteAnimationAdapter = remoteAnimationAdapter;
|
||||
mHandler = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an animation for each individual {@link AppWindowToken}.
|
||||
*
|
||||
* @param appWindowToken The app to animate.
|
||||
* @param position The position app bounds, in screen coordinates.
|
||||
* @param stackBounds The stack bounds of the app.
|
||||
* @return The adapter to be run on the app.
|
||||
*/
|
||||
AnimationAdapter createAnimationAdapter(AppWindowToken appWindowToken, Point position,
|
||||
Rect stackBounds) {
|
||||
final RemoteAnimationAdapterWrapper adapter = new RemoteAnimationAdapterWrapper(
|
||||
appWindowToken, position, stackBounds);
|
||||
mPendingAnimations.add(adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the transition is ready to be started, and all leashes have been set up.
|
||||
*/
|
||||
void goodToGo() {
|
||||
mHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MS);
|
||||
try {
|
||||
mRemoteAnimationAdapter.getRunner().onAnimationStart(createAnimations(),
|
||||
mFinishedCallback);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to start remote animation", e);
|
||||
onAnimationFinished();
|
||||
}
|
||||
}
|
||||
|
||||
private RemoteAnimationTarget[] createAnimations() {
|
||||
final RemoteAnimationTarget[] result = new RemoteAnimationTarget[mPendingAnimations.size()];
|
||||
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
|
||||
result[i] = mPendingAnimations.get(i).createRemoteAppAnimation();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void onAnimationFinished() {
|
||||
mHandler.removeCallbacks(mTimeoutRunnable);
|
||||
synchronized (mService.mWindowMap) {
|
||||
mService.openSurfaceTransaction();
|
||||
try {
|
||||
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
|
||||
final RemoteAnimationAdapterWrapper adapter = mPendingAnimations.get(i);
|
||||
adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
|
||||
}
|
||||
} finally {
|
||||
mService.closeSurfaceTransaction("RemoteAnimationController#finished");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void invokeAnimationCancelled() {
|
||||
try {
|
||||
mRemoteAnimationAdapter.getRunner().onAnimationCancelled();
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to notify cancel", e);
|
||||
}
|
||||
}
|
||||
|
||||
private class RemoteAnimationAdapterWrapper implements AnimationAdapter {
|
||||
|
||||
private final AppWindowToken mAppWindowToken;
|
||||
private SurfaceControl mCapturedLeash;
|
||||
private OnAnimationFinishedCallback mCapturedFinishCallback;
|
||||
private final Point mPosition = new Point();
|
||||
private final Rect mStackBounds = new Rect();
|
||||
|
||||
RemoteAnimationAdapterWrapper(AppWindowToken appWindowToken, Point position,
|
||||
Rect stackBounds) {
|
||||
mAppWindowToken = appWindowToken;
|
||||
mPosition.set(position.x, position.y);
|
||||
mStackBounds.set(stackBounds);
|
||||
}
|
||||
|
||||
RemoteAnimationTarget createRemoteAppAnimation() {
|
||||
return new RemoteAnimationTarget(mAppWindowToken.getTask().mTaskId, getMode(),
|
||||
mCapturedLeash, !mAppWindowToken.fillsParent(),
|
||||
mAppWindowToken.findMainWindow().mWinAnimator.mLastClipRect,
|
||||
mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds);
|
||||
}
|
||||
|
||||
private int getMode() {
|
||||
if (mService.mOpeningApps.contains(mAppWindowToken)) {
|
||||
return RemoteAnimationTarget.MODE_OPENING;
|
||||
} else {
|
||||
return RemoteAnimationTarget.MODE_CLOSING;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDetachWallpaper() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBackgroundColor() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startAnimation(SurfaceControl animationLeash, Transaction t,
|
||||
OnAnimationFinishedCallback finishCallback) {
|
||||
|
||||
// Restore z-layering, position and stack crop until client has a chance to modify it.
|
||||
t.setLayer(animationLeash, mAppWindowToken.getPrefixOrderIndex());
|
||||
t.setPosition(animationLeash, mPosition.x, mPosition.y);
|
||||
mTmpRect.set(mStackBounds);
|
||||
mTmpRect.offsetTo(0, 0);
|
||||
t.setWindowCrop(animationLeash, mTmpRect);
|
||||
mCapturedLeash = animationLeash;
|
||||
mCapturedFinishCallback = finishCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancelled(SurfaceControl animationLeash) {
|
||||
mPendingAnimations.remove(this);
|
||||
if (mPendingAnimations.isEmpty()) {
|
||||
mHandler.removeCallbacks(mTimeoutRunnable);
|
||||
invokeAnimationCancelled();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDurationHint() {
|
||||
return mRemoteAnimationAdapter.getDuration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStatusBarTransitionsStartTime() {
|
||||
return SystemClock.uptimeMillis()
|
||||
+ mRemoteAnimationAdapter.getStatusBarTransitionDelay();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
|
||||
import static android.Manifest.permission.MANAGE_APP_TOKENS;
|
||||
import static android.Manifest.permission.READ_FRAME_BUFFER;
|
||||
import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS;
|
||||
@@ -210,6 +211,7 @@ import android.view.KeyEvent;
|
||||
import android.view.MagnificationSpec;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.PointerIcon;
|
||||
import android.view.RemoteAnimationAdapter;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceControl.Builder;
|
||||
@@ -2623,6 +2625,18 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter) {
|
||||
if (!checkCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
|
||||
"overridePendingAppTransitionRemote()")) {
|
||||
throw new SecurityException(
|
||||
"Requires CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS permission");
|
||||
}
|
||||
synchronized (mWindowMap) {
|
||||
mAppTransition.overridePendingAppTransitionRemote(remoteAnimationAdapter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endProlongedAnimations() {
|
||||
synchronized (mWindowMap) {
|
||||
|
||||
@@ -193,6 +193,7 @@ import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.ToBooleanFunction;
|
||||
import com.android.server.input.InputWindowHandle;
|
||||
import com.android.server.policy.WindowManagerPolicy;
|
||||
@@ -3952,6 +3953,22 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if we our one of our ancestors has {@link #mAnimatingExit} set to true, false
|
||||
* otherwise.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean isSelfOrAncestorWindowAnimatingExit() {
|
||||
WindowState window = this;
|
||||
do {
|
||||
if (window.mAnimatingExit) {
|
||||
return true;
|
||||
}
|
||||
window = window.getParentWindow();
|
||||
} while (window != null);
|
||||
return false;
|
||||
}
|
||||
|
||||
void onExitAnimationDone() {
|
||||
if (DEBUG_ANIM) Slog.v(TAG, "onExitAnimationDone in " + this
|
||||
+ ": exiting=" + mAnimatingExit + " remove=" + mRemoveOnExit
|
||||
@@ -3987,7 +4004,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|
||||
mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
|
||||
}
|
||||
|
||||
if (!mAnimatingExit) {
|
||||
if (!isSelfOrAncestorWindowAnimatingExit()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.platform.test.annotations.Postsubmit;
|
||||
import android.support.test.filters.FlakyTest;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.view.IRemoteAnimationFinishedCallback;
|
||||
import android.view.IRemoteAnimationRunner;
|
||||
import android.view.RemoteAnimationAdapter;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceControl.Transaction;
|
||||
|
||||
import com.android.server.testutils.OffsettableClock;
|
||||
import com.android.server.testutils.TestHandler;
|
||||
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
/**
|
||||
* atest FrameworksServicesTests:com.android.server.wm.RemoteAnimationControllerTest
|
||||
*/
|
||||
@SmallTest
|
||||
@FlakyTest(detail = "Promote to presubmit if non-flakyness is established")
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class RemoteAnimationControllerTest extends WindowTestsBase {
|
||||
|
||||
@Mock SurfaceControl mMockLeash;
|
||||
@Mock Transaction mMockTransaction;
|
||||
@Mock OnAnimationFinishedCallback mFinishedCallback;
|
||||
@Mock IRemoteAnimationRunner mMockRunner;
|
||||
private RemoteAnimationAdapter mAdapter;
|
||||
private RemoteAnimationController mController;
|
||||
private final OffsettableClock mClock = new OffsettableClock.Stopped();
|
||||
private TestHandler mHandler;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50);
|
||||
sWm.mH.runWithScissors(() -> {
|
||||
mHandler = new TestHandler(null, mClock);
|
||||
}, 0);
|
||||
mController = new RemoteAnimationController(sWm, mAdapter, mHandler);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRun() throws Exception {
|
||||
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
|
||||
sWm.mOpeningApps.add(win.mAppToken);
|
||||
try {
|
||||
final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
|
||||
new Point(50, 100), new Rect(50, 100, 150, 150));
|
||||
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
|
||||
mController.goodToGo();
|
||||
|
||||
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
|
||||
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
|
||||
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
|
||||
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
|
||||
verify(mMockRunner).onAnimationStart(appsCaptor.capture(), finishedCaptor.capture());
|
||||
assertEquals(1, appsCaptor.getValue().length);
|
||||
final RemoteAnimationTarget app = appsCaptor.getValue()[0];
|
||||
assertEquals(new Point(50, 100), app.position);
|
||||
assertEquals(new Rect(50, 100, 150, 150), app.sourceContainerBounds);
|
||||
assertEquals(win.mAppToken.getPrefixOrderIndex(), app.prefixOrderIndex);
|
||||
assertEquals(win.mAppToken.getTask().mTaskId, app.taskId);
|
||||
assertEquals(mMockLeash, app.leash);
|
||||
assertEquals(win.mWinAnimator.mLastClipRect, app.clipRect);
|
||||
assertEquals(false, app.isTranslucent);
|
||||
verify(mMockTransaction).setLayer(mMockLeash, app.prefixOrderIndex);
|
||||
verify(mMockTransaction).setPosition(mMockLeash, app.position.x, app.position.y);
|
||||
verify(mMockTransaction).setWindowCrop(mMockLeash, new Rect(0, 0, 100, 50));
|
||||
|
||||
finishedCaptor.getValue().onAnimationFinished();
|
||||
verify(mFinishedCallback).onAnimationFinished(eq(adapter));
|
||||
} finally {
|
||||
sWm.mOpeningApps.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancel() throws Exception {
|
||||
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
|
||||
final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
|
||||
new Point(50, 100), new Rect(50, 100, 150, 150));
|
||||
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
|
||||
mController.goodToGo();
|
||||
|
||||
adapter.onAnimationCancelled(mMockLeash);
|
||||
verify(mMockRunner).onAnimationCancelled();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimeout() throws Exception {
|
||||
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
|
||||
final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
|
||||
new Point(50, 100), new Rect(50, 100, 150, 150));
|
||||
adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
|
||||
mController.goodToGo();
|
||||
|
||||
mClock.fastForward(2500);
|
||||
mHandler.timeAdvance();
|
||||
|
||||
verify(mMockRunner).onAnimationCancelled();
|
||||
verify(mFinishedCallback).onAnimationFinished(eq(adapter));
|
||||
}
|
||||
}
|
||||
@@ -223,6 +223,19 @@ public class WindowStateTests extends WindowTestsBase {
|
||||
assertFalse(app.canAffectSystemUiFlags());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsSelfOrAncestorWindowAnimating() throws Exception {
|
||||
final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
|
||||
final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1");
|
||||
final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, "child2");
|
||||
assertFalse(child2.isSelfOrAncestorWindowAnimatingExit());
|
||||
child2.mAnimatingExit = true;
|
||||
assertTrue(child2.isSelfOrAncestorWindowAnimatingExit());
|
||||
child2.mAnimatingExit = false;
|
||||
root.mAnimatingExit = true;
|
||||
assertTrue(child2.isSelfOrAncestorWindowAnimatingExit());
|
||||
}
|
||||
|
||||
private void testPrepareWindowToDisplayDuringRelayout(boolean wasVisible) {
|
||||
final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
|
||||
root.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
|
||||
|
||||
Reference in New Issue
Block a user