Merge changes from topic "refactor_back"
* changes: Migrate back animation to shell transition Add show wallpaper feature for back navigation Refactor back navigation animtion (1/2)
This commit is contained in:
@@ -73,6 +73,7 @@ import android.view.IWindowFocusObserver;
|
||||
import android.view.RemoteAnimationDefinition;
|
||||
import android.view.RemoteAnimationAdapter;
|
||||
import android.window.IWindowOrganizerController;
|
||||
import android.window.BackAnimationAdapter;
|
||||
import android.window.BackNavigationInfo;
|
||||
import android.window.SplashScreenView;
|
||||
import com.android.internal.app.IVoiceInteractor;
|
||||
@@ -353,9 +354,10 @@ interface IActivityTaskManager {
|
||||
/**
|
||||
* Prepare the back navigation in the server. This setups the leashed for sysui to animate
|
||||
* the back gesture and returns the data needed for the animation.
|
||||
* @param requestAnimation true if the caller wishes to animate the back navigation
|
||||
* @param focusObserver a remote callback to nofify shell when the focused window lost focus.
|
||||
* @param adaptor a remote animation to be run for the back navigation plays the animation.
|
||||
* @return Returns the back navigation info.
|
||||
*/
|
||||
android.window.BackNavigationInfo startBackNavigation(in boolean requestAnimation,
|
||||
in IWindowFocusObserver focusObserver);
|
||||
android.window.BackNavigationInfo startBackNavigation(
|
||||
in IWindowFocusObserver focusObserver, in BackAnimationAdapter adaptor);
|
||||
}
|
||||
|
||||
22
core/java/android/window/BackAnimationAdapter.aidl
Normal file
22
core/java/android/window/BackAnimationAdapter.aidl
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
parcelable BackAnimationAdapter;
|
||||
62
core/java/android/window/BackAnimationAdapter.java
Normal file
62
core/java/android/window/BackAnimationAdapter.java
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* Object that describes how to run a remote back animation.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class BackAnimationAdapter implements Parcelable {
|
||||
private final IBackAnimationRunner mRunner;
|
||||
|
||||
public BackAnimationAdapter(IBackAnimationRunner runner) {
|
||||
mRunner = runner;
|
||||
}
|
||||
|
||||
public BackAnimationAdapter(Parcel in) {
|
||||
mRunner = IBackAnimationRunner.Stub.asInterface(in.readStrongBinder());
|
||||
}
|
||||
|
||||
public IBackAnimationRunner getRunner() {
|
||||
return mRunner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeStrongInterface(mRunner);
|
||||
}
|
||||
|
||||
public static final @android.annotation.NonNull Creator<BackAnimationAdapter> CREATOR =
|
||||
new Creator<BackAnimationAdapter>() {
|
||||
public BackAnimationAdapter createFromParcel(Parcel in) {
|
||||
return new BackAnimationAdapter(in);
|
||||
}
|
||||
|
||||
public BackAnimationAdapter[] newArray(int size) {
|
||||
return new BackAnimationAdapter[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -18,10 +18,8 @@ package android.window;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@@ -52,8 +50,6 @@ public class BackEvent implements Parcelable {
|
||||
|
||||
@SwipeEdge
|
||||
private final int mSwipeEdge;
|
||||
@Nullable
|
||||
private final RemoteAnimationTarget mDepartingAnimationTarget;
|
||||
|
||||
/**
|
||||
* Creates a new {@link BackEvent} instance.
|
||||
@@ -62,16 +58,12 @@ public class BackEvent implements Parcelable {
|
||||
* @param touchY Absolute Y location of the touch point of this event.
|
||||
* @param progress Value between 0 and 1 on how far along the back gesture is.
|
||||
* @param swipeEdge Indicates which edge the swipe starts from.
|
||||
* @param departingAnimationTarget The remote animation target of the departing application
|
||||
* window.
|
||||
*/
|
||||
public BackEvent(float touchX, float touchY, float progress, @SwipeEdge int swipeEdge,
|
||||
@Nullable RemoteAnimationTarget departingAnimationTarget) {
|
||||
public BackEvent(float touchX, float touchY, float progress, @SwipeEdge int swipeEdge) {
|
||||
mTouchX = touchX;
|
||||
mTouchY = touchY;
|
||||
mProgress = progress;
|
||||
mSwipeEdge = swipeEdge;
|
||||
mDepartingAnimationTarget = departingAnimationTarget;
|
||||
}
|
||||
|
||||
private BackEvent(@NonNull Parcel in) {
|
||||
@@ -79,7 +71,6 @@ public class BackEvent implements Parcelable {
|
||||
mTouchY = in.readFloat();
|
||||
mProgress = in.readFloat();
|
||||
mSwipeEdge = in.readInt();
|
||||
mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR);
|
||||
}
|
||||
|
||||
public static final Creator<BackEvent> CREATOR = new Creator<BackEvent>() {
|
||||
@@ -105,7 +96,6 @@ public class BackEvent implements Parcelable {
|
||||
dest.writeFloat(mTouchY);
|
||||
dest.writeFloat(mProgress);
|
||||
dest.writeInt(mSwipeEdge);
|
||||
dest.writeTypedObject(mDepartingAnimationTarget, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,16 +126,6 @@ public class BackEvent implements Parcelable {
|
||||
return mSwipeEdge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link RemoteAnimationTarget} of the top departing application window,
|
||||
* or {@code null} if the top window should not be moved for the current type of back
|
||||
* destination.
|
||||
*/
|
||||
@Nullable
|
||||
public RemoteAnimationTarget getDepartingAnimationTarget() {
|
||||
return mDepartingAnimationTarget;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BackEvent{"
|
||||
@@ -153,7 +133,6 @@ public class BackEvent implements Parcelable {
|
||||
+ ", mTouchY=" + mTouchY
|
||||
+ ", mProgress=" + mProgress
|
||||
+ ", mSwipeEdge" + mSwipeEdge
|
||||
+ ", mDepartingAnimationTarget" + mDepartingAnimationTarget
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,14 +19,10 @@ package android.window;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.WindowConfiguration;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteCallback;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.view.SurfaceControl;
|
||||
|
||||
/**
|
||||
* Information to be sent to SysUI about a back event.
|
||||
@@ -84,75 +80,57 @@ public final class BackNavigationInfo implements Parcelable {
|
||||
TYPE_CROSS_TASK,
|
||||
TYPE_CALLBACK
|
||||
})
|
||||
@interface BackTargetType {
|
||||
public @interface BackTargetType {
|
||||
}
|
||||
|
||||
private final int mType;
|
||||
@Nullable
|
||||
private final RemoteAnimationTarget mDepartingAnimationTarget;
|
||||
@Nullable
|
||||
private final SurfaceControl mScreenshotSurface;
|
||||
@Nullable
|
||||
private final HardwareBuffer mScreenshotBuffer;
|
||||
@Nullable
|
||||
private final RemoteCallback mOnBackNavigationDone;
|
||||
@Nullable
|
||||
private final WindowConfiguration mTaskWindowConfiguration;
|
||||
@Nullable
|
||||
private final IOnBackInvokedCallback mOnBackInvokedCallback;
|
||||
private final boolean mPrepareRemoteAnimation;
|
||||
@Nullable
|
||||
private WindowContainerToken mDepartingWindowContainerToken;
|
||||
|
||||
/**
|
||||
* Create a new {@link BackNavigationInfo} instance.
|
||||
*
|
||||
* @param type The {@link BackTargetType} of the destination (what will be
|
||||
* displayed after the back action).
|
||||
* @param departingAnimationTarget The remote animation target, containing a leash to animate
|
||||
* away the departing window. The consumer of the leash is
|
||||
* responsible for removing it.
|
||||
* @param screenshotSurface The screenshot of the previous activity to be displayed.
|
||||
* @param screenshotBuffer A buffer containing a screenshot used to display the activity.
|
||||
* See {@link #getScreenshotHardwareBuffer()} for information
|
||||
* about nullity.
|
||||
* @param taskWindowConfiguration The window configuration of the Task being animated beneath.
|
||||
* @param onBackNavigationDone The callback to be called once the client is done with the
|
||||
* back preview.
|
||||
* @param onBackInvokedCallback The back callback registered by the current top level window.
|
||||
* @param departingWindowContainerToken The {@link WindowContainerToken} of departing window.
|
||||
* @param isPrepareRemoteAnimation Return whether the core is preparing a back gesture
|
||||
* animation, if true, the caller of startBackNavigation should
|
||||
* be expected to receive an animation start callback.
|
||||
*/
|
||||
private BackNavigationInfo(@BackTargetType int type,
|
||||
@Nullable RemoteAnimationTarget departingAnimationTarget,
|
||||
@Nullable SurfaceControl screenshotSurface,
|
||||
@Nullable HardwareBuffer screenshotBuffer,
|
||||
@Nullable WindowConfiguration taskWindowConfiguration,
|
||||
@Nullable RemoteCallback onBackNavigationDone,
|
||||
@Nullable IOnBackInvokedCallback onBackInvokedCallback) {
|
||||
@Nullable IOnBackInvokedCallback onBackInvokedCallback,
|
||||
boolean isPrepareRemoteAnimation,
|
||||
@Nullable WindowContainerToken departingWindowContainerToken) {
|
||||
mType = type;
|
||||
mDepartingAnimationTarget = departingAnimationTarget;
|
||||
mScreenshotSurface = screenshotSurface;
|
||||
mScreenshotBuffer = screenshotBuffer;
|
||||
mTaskWindowConfiguration = taskWindowConfiguration;
|
||||
mOnBackNavigationDone = onBackNavigationDone;
|
||||
mOnBackInvokedCallback = onBackInvokedCallback;
|
||||
mPrepareRemoteAnimation = isPrepareRemoteAnimation;
|
||||
mDepartingWindowContainerToken = departingWindowContainerToken;
|
||||
}
|
||||
|
||||
private BackNavigationInfo(@NonNull Parcel in) {
|
||||
mType = in.readInt();
|
||||
mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR);
|
||||
mScreenshotSurface = in.readTypedObject(SurfaceControl.CREATOR);
|
||||
mScreenshotBuffer = in.readTypedObject(HardwareBuffer.CREATOR);
|
||||
mTaskWindowConfiguration = in.readTypedObject(WindowConfiguration.CREATOR);
|
||||
mOnBackNavigationDone = in.readTypedObject(RemoteCallback.CREATOR);
|
||||
mOnBackInvokedCallback = IOnBackInvokedCallback.Stub.asInterface(in.readStrongBinder());
|
||||
mPrepareRemoteAnimation = in.readBoolean();
|
||||
mDepartingWindowContainerToken = in.readTypedObject(WindowContainerToken.CREATOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeInt(mType);
|
||||
dest.writeTypedObject(mDepartingAnimationTarget, flags);
|
||||
dest.writeTypedObject(mScreenshotSurface, flags);
|
||||
dest.writeTypedObject(mScreenshotBuffer, flags);
|
||||
dest.writeTypedObject(mTaskWindowConfiguration, flags);
|
||||
dest.writeTypedObject(mOnBackNavigationDone, flags);
|
||||
dest.writeStrongInterface(mOnBackInvokedCallback);
|
||||
dest.writeBoolean(mPrepareRemoteAnimation);
|
||||
dest.writeTypedObject(mDepartingWindowContainerToken, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,49 +142,6 @@ public final class BackNavigationInfo implements Parcelable {
|
||||
return mType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link RemoteAnimationTarget}, containing a leash to the top window container
|
||||
* that needs to be animated. This can be null if the back animation is controlled by
|
||||
* the application.
|
||||
*/
|
||||
@Nullable
|
||||
public RemoteAnimationTarget getDepartingAnimationTarget() {
|
||||
return mDepartingAnimationTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link SurfaceControl} that should be used to display a screenshot of the
|
||||
* previous activity.
|
||||
*/
|
||||
@Nullable
|
||||
public SurfaceControl getScreenshotSurface() {
|
||||
return mScreenshotSurface;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link HardwareBuffer} containing the screenshot the activity about to be
|
||||
* shown. This can be null if one of the following conditions is met:
|
||||
* <ul>
|
||||
* <li>The screenshot is not available
|
||||
* <li> The previous activity is the home screen ( {@link #TYPE_RETURN_TO_HOME}
|
||||
* <li> The current window is a dialog ({@link #TYPE_DIALOG_CLOSE}
|
||||
* <li> The back animation is controlled by the application
|
||||
* </ul>
|
||||
*/
|
||||
@Nullable
|
||||
public HardwareBuffer getScreenshotHardwareBuffer() {
|
||||
return mScreenshotBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link WindowConfiguration} of the current task. This is null when the top
|
||||
* application is controlling the back animation.
|
||||
*/
|
||||
@Nullable
|
||||
public WindowConfiguration getTaskWindowConfiguration() {
|
||||
return mTaskWindowConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link OnBackInvokedCallback} of the top level window or null if
|
||||
* the client didn't register a callback.
|
||||
@@ -221,6 +156,25 @@ public final class BackNavigationInfo implements Parcelable {
|
||||
return mOnBackInvokedCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the core is preparing a back gesture nimation.
|
||||
*/
|
||||
public boolean isPrepareRemoteAnimation() {
|
||||
return mPrepareRemoteAnimation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link WindowContainerToken} of the highest container in the hierarchy being
|
||||
* removed.
|
||||
* <p>
|
||||
* For example, if an Activity is the last one of its Task, the Task's token will be given.
|
||||
* Otherwise, it will be the Activity's token.
|
||||
*/
|
||||
@Nullable
|
||||
public WindowContainerToken getDepartingWindowContainerToken() {
|
||||
return mDepartingWindowContainerToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to be called when the back preview is finished in order to notify the server that
|
||||
* it can clean up the resources created for the animation.
|
||||
@@ -256,12 +210,9 @@ public final class BackNavigationInfo implements Parcelable {
|
||||
public String toString() {
|
||||
return "BackNavigationInfo{"
|
||||
+ "mType=" + typeToString(mType) + " (" + mType + ")"
|
||||
+ ", mDepartingAnimationTarget=" + mDepartingAnimationTarget
|
||||
+ ", mScreenshotSurface=" + mScreenshotSurface
|
||||
+ ", mTaskWindowConfiguration= " + mTaskWindowConfiguration
|
||||
+ ", mScreenshotBuffer=" + mScreenshotBuffer
|
||||
+ ", mOnBackNavigationDone=" + mOnBackNavigationDone
|
||||
+ ", mOnBackInvokedCallback=" + mOnBackInvokedCallback
|
||||
+ ", mWindowContainerToken=" + mDepartingWindowContainerToken
|
||||
+ '}';
|
||||
}
|
||||
|
||||
@@ -291,20 +242,14 @@ public final class BackNavigationInfo implements Parcelable {
|
||||
*/
|
||||
@SuppressWarnings("UnusedReturnValue") // Builder pattern
|
||||
public static class Builder {
|
||||
|
||||
private int mType = TYPE_UNDEFINED;
|
||||
@Nullable
|
||||
private RemoteAnimationTarget mDepartingAnimationTarget = null;
|
||||
@Nullable
|
||||
private SurfaceControl mScreenshotSurface = null;
|
||||
@Nullable
|
||||
private HardwareBuffer mScreenshotBuffer = null;
|
||||
@Nullable
|
||||
private WindowConfiguration mTaskWindowConfiguration = null;
|
||||
@Nullable
|
||||
private RemoteCallback mOnBackNavigationDone = null;
|
||||
@Nullable
|
||||
private IOnBackInvokedCallback mOnBackInvokedCallback = null;
|
||||
private boolean mPrepareRemoteAnimation;
|
||||
@Nullable
|
||||
private WindowContainerToken mDepartingWindowContainerToken = null;
|
||||
|
||||
/**
|
||||
* @see BackNavigationInfo#getType()
|
||||
@@ -314,40 +259,6 @@ public final class BackNavigationInfo implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BackNavigationInfo#getDepartingAnimationTarget
|
||||
*/
|
||||
public Builder setDepartingAnimationTarget(
|
||||
@Nullable RemoteAnimationTarget departingAnimationTarget) {
|
||||
mDepartingAnimationTarget = departingAnimationTarget;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BackNavigationInfo#getScreenshotSurface
|
||||
*/
|
||||
public Builder setScreenshotSurface(@Nullable SurfaceControl screenshotSurface) {
|
||||
mScreenshotSurface = screenshotSurface;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BackNavigationInfo#getScreenshotHardwareBuffer()
|
||||
*/
|
||||
public Builder setScreenshotBuffer(@Nullable HardwareBuffer screenshotBuffer) {
|
||||
mScreenshotBuffer = screenshotBuffer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BackNavigationInfo#getTaskWindowConfiguration
|
||||
*/
|
||||
public Builder setTaskWindowConfiguration(
|
||||
@Nullable WindowConfiguration taskWindowConfiguration) {
|
||||
mTaskWindowConfiguration = taskWindowConfiguration;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BackNavigationInfo#onBackNavigationFinished(boolean)
|
||||
*/
|
||||
@@ -365,13 +276,29 @@ public final class BackNavigationInfo implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param prepareRemoteAnimation Whether core prepare animation for shell.
|
||||
*/
|
||||
public Builder setPrepareRemoteAnimation(boolean prepareRemoteAnimation) {
|
||||
mPrepareRemoteAnimation = prepareRemoteAnimation;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see BackNavigationInfo#getDepartingWindowContainerToken()
|
||||
*/
|
||||
public void setDepartingWCT(@NonNull WindowContainerToken windowContainerToken) {
|
||||
mDepartingWindowContainerToken = windowContainerToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns an instance of {@link BackNavigationInfo}
|
||||
*/
|
||||
public BackNavigationInfo build() {
|
||||
return new BackNavigationInfo(mType, mDepartingAnimationTarget, mScreenshotSurface,
|
||||
mScreenshotBuffer, mTaskWindowConfiguration, mOnBackNavigationDone,
|
||||
mOnBackInvokedCallback);
|
||||
return new BackNavigationInfo(mType, mOnBackNavigationDone,
|
||||
mOnBackInvokedCallback,
|
||||
mPrepareRemoteAnimation,
|
||||
mDepartingWindowContainerToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
27
core/java/android/window/IBackAnimationFinishedCallback.aidl
Normal file
27
core/java/android/window/IBackAnimationFinishedCallback.aidl
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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;
|
||||
|
||||
/**
|
||||
* Interface to be invoked by the controlling process when a back animation has finished.
|
||||
*
|
||||
* @param trigger Whether the back gesture has passed the triggering threshold.
|
||||
* {@hide}
|
||||
*/
|
||||
oneway interface IBackAnimationFinishedCallback {
|
||||
void onAnimationFinished(in boolean triggerBack);
|
||||
}
|
||||
52
core/java/android/window/IBackAnimationRunner.aidl
Normal file
52
core/java/android/window/IBackAnimationRunner.aidl
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.RemoteAnimationTarget;
|
||||
import android.window.IBackAnimationFinishedCallback;
|
||||
|
||||
/**
|
||||
* Interface that is used to callback from window manager to the process that runs a back gesture
|
||||
* animation to start or cancel it.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
oneway interface IBackAnimationRunner {
|
||||
|
||||
/**
|
||||
* Called when the system needs to cancel the current animation. This can be due to the
|
||||
* wallpaper not drawing in time, or the handler not finishing the animation within a predefined
|
||||
* amount of time.
|
||||
*
|
||||
*/
|
||||
void onAnimationCancelled() = 1;
|
||||
|
||||
/**
|
||||
* Called when the system is ready for the handler to start animating all the visible tasks.
|
||||
* @param type The back navigation type.
|
||||
* @param apps The list of departing (type=MODE_CLOSING) and entering (type=MODE_OPENING)
|
||||
windows to animate,
|
||||
* @param wallpapers The list of wallpapers to animate.
|
||||
* @param nonApps The list of non-app windows such as Bubbles to animate.
|
||||
* @param finishedCallback The callback to invoke when the animation is finished.
|
||||
*/
|
||||
void onAnimationStart(in int type,
|
||||
in RemoteAnimationTarget[] apps,
|
||||
in RemoteAnimationTarget[] wallpapers,
|
||||
in RemoteAnimationTarget[] nonApps,
|
||||
in IBackAnimationFinishedCallback finishedCallback) = 2;
|
||||
}
|
||||
@@ -92,7 +92,7 @@ public class BackNavigationTest {
|
||||
try {
|
||||
mInstrumentation.getUiAutomation().waitForIdle(500, 1000);
|
||||
BackNavigationInfo info = ActivityTaskManager.getService()
|
||||
.startBackNavigation(true, null);
|
||||
.startBackNavigation(null, null);
|
||||
assertNotNull("BackNavigationInfo is null", info);
|
||||
assertNotNull("OnBackInvokedCallback is null", info.getOnBackInvokedCallback());
|
||||
info.getOnBackInvokedCallback().onBackInvoked();
|
||||
|
||||
@@ -1111,6 +1111,12 @@
|
||||
"group": "WM_ERROR",
|
||||
"at": "com\/android\/server\/wm\/WindowManagerService.java"
|
||||
},
|
||||
"-1033630971": {
|
||||
"message": "onBackNavigationDone backType=%s, triggerBack=%b",
|
||||
"level": "DEBUG",
|
||||
"group": "WM_DEBUG_BACK_PREVIEW",
|
||||
"at": "com\/android\/server\/wm\/BackNavigationController.java"
|
||||
},
|
||||
"-1022146708": {
|
||||
"message": "Skipping %s: mismatch activity type",
|
||||
"level": "DEBUG",
|
||||
@@ -3991,12 +3997,6 @@
|
||||
"group": "WM_ERROR",
|
||||
"at": "com\/android\/server\/wm\/WindowManagerService.java"
|
||||
},
|
||||
"1778919449": {
|
||||
"message": "onBackNavigationDone backType=%s, task=%s, prevActivity=%s",
|
||||
"level": "DEBUG",
|
||||
"group": "WM_DEBUG_BACK_PREVIEW",
|
||||
"at": "com\/android\/server\/wm\/BackNavigationController.java"
|
||||
},
|
||||
"1781673113": {
|
||||
"message": "onAnimationFinished(): targetRootTask=%s targetActivity=%s mRestoreTargetBehindRootTask=%s",
|
||||
"level": "DEBUG",
|
||||
|
||||
@@ -23,13 +23,9 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.app.IActivityTaskManager;
|
||||
import android.app.WindowConfiguration;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
@@ -40,15 +36,19 @@ import android.os.SystemProperties;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings.Global;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.view.IRemoteAnimationRunner;
|
||||
import android.view.IWindowFocusObserver;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyCharacterMap;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.BackAnimationAdapter;
|
||||
import android.window.BackEvent;
|
||||
import android.window.BackNavigationInfo;
|
||||
import android.window.IBackAnimationFinishedCallback;
|
||||
import android.window.IBackAnimationRunner;
|
||||
import android.window.IOnBackInvokedCallback;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -58,6 +58,7 @@ import com.android.wm.shell.common.ShellExecutor;
|
||||
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
|
||||
import com.android.wm.shell.common.annotations.ShellMainThread;
|
||||
import com.android.wm.shell.sysui.ShellInit;
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
@@ -75,6 +76,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
SETTING_VALUE_ON) != SETTING_VALUE_OFF;
|
||||
private static final int PROGRESS_THRESHOLD = SystemProperties
|
||||
.getInt(PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP, -1);
|
||||
|
||||
// TODO (b/241808055) Find a appropriate time to remove during refactor
|
||||
private static final boolean ENABLE_SHELL_TRANSITIONS = Transitions.ENABLE_SHELL_TRANSITIONS;
|
||||
/**
|
||||
* Max duration to wait for a transition to finish before accepting another gesture start
|
||||
* request.
|
||||
@@ -83,16 +87,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
|
||||
private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false);
|
||||
|
||||
/**
|
||||
* Location of the initial touch event of the back gesture.
|
||||
*/
|
||||
private final PointF mInitTouchLocation = new PointF();
|
||||
|
||||
/**
|
||||
* Raw delta between {@link #mInitTouchLocation} and the last touch location.
|
||||
*/
|
||||
private final Point mTouchEventDelta = new Point();
|
||||
|
||||
/** True when a back gesture is ongoing */
|
||||
private boolean mBackGestureStarted = false;
|
||||
|
||||
@@ -105,23 +99,27 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
|
||||
@Nullable
|
||||
private BackNavigationInfo mBackNavigationInfo;
|
||||
private final SurfaceControl.Transaction mTransaction;
|
||||
private final IActivityTaskManager mActivityTaskManager;
|
||||
private final Context mContext;
|
||||
private final ContentResolver mContentResolver;
|
||||
private final ShellExecutor mShellExecutor;
|
||||
private final Handler mBgHandler;
|
||||
@Nullable
|
||||
private IOnBackInvokedCallback mBackToLauncherCallback;
|
||||
private float mTriggerThreshold;
|
||||
private float mProgressThreshold;
|
||||
private final Runnable mResetTransitionRunnable = () -> {
|
||||
finishAnimation();
|
||||
mTransitionInProgress = false;
|
||||
ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Transition didn't finish in %d ms. Resetting...",
|
||||
MAX_TRANSITION_DURATION);
|
||||
onBackAnimationFinished();
|
||||
};
|
||||
|
||||
private IBackAnimationFinishedCallback mBackAnimationFinishedCallback;
|
||||
@VisibleForTesting
|
||||
BackAnimationAdapter mBackAnimationAdapter;
|
||||
|
||||
private final TouchTracker mTouchTracker = new TouchTracker();
|
||||
|
||||
private final SparseArray<BackAnimationRunner> mAnimationDefinition = new SparseArray<>();
|
||||
private final Transitions mTransitions;
|
||||
private BackTransitionHandler mBackTransitionHandler;
|
||||
|
||||
@VisibleForTesting
|
||||
final IWindowFocusObserver mFocusObserver = new IWindowFocusObserver.Stub() {
|
||||
@Override
|
||||
@@ -134,19 +132,69 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
// this due to the transition may cause focus lost. (alpha = 0)
|
||||
return;
|
||||
}
|
||||
ProtoLog.i(WM_SHELL_BACK_PREVIEW, "Target window lost focus.");
|
||||
setTriggerBack(false);
|
||||
onGestureFinished(false);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper class to record the touch location for gesture start and latest.
|
||||
*/
|
||||
private static class TouchTracker {
|
||||
/**
|
||||
* Location of the latest touch event
|
||||
*/
|
||||
private float mLatestTouchX;
|
||||
private float mLatestTouchY;
|
||||
private int mSwipeEdge;
|
||||
private float mProgressThreshold;
|
||||
|
||||
/**
|
||||
* Location of the initial touch event of the back gesture.
|
||||
*/
|
||||
private float mInitTouchX;
|
||||
private float mInitTouchY;
|
||||
|
||||
void update(float touchX, float touchY, int swipeEdge) {
|
||||
mLatestTouchX = touchX;
|
||||
mLatestTouchY = touchY;
|
||||
mSwipeEdge = swipeEdge;
|
||||
}
|
||||
|
||||
void setGestureStartLocation(float touchX, float touchY) {
|
||||
mInitTouchX = touchX;
|
||||
mInitTouchY = touchY;
|
||||
}
|
||||
|
||||
void setProgressThreshold(float progressThreshold) {
|
||||
mProgressThreshold = progressThreshold;
|
||||
}
|
||||
|
||||
float getProgress(float touchX) {
|
||||
int deltaX = Math.round(touchX - mInitTouchX);
|
||||
float progressThreshold = PROGRESS_THRESHOLD >= 0
|
||||
? PROGRESS_THRESHOLD : mProgressThreshold;
|
||||
return Math.min(Math.max(Math.abs(deltaX) / progressThreshold, 0), 1);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
mInitTouchX = 0;
|
||||
mInitTouchY = 0;
|
||||
mSwipeEdge = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public BackAnimationController(
|
||||
@NonNull ShellInit shellInit,
|
||||
@NonNull @ShellMainThread ShellExecutor shellExecutor,
|
||||
@NonNull @ShellBackgroundThread Handler backgroundHandler,
|
||||
Context context) {
|
||||
this(shellInit, shellExecutor, backgroundHandler, new SurfaceControl.Transaction(),
|
||||
ActivityTaskManager.getService(), context, context.getContentResolver());
|
||||
Context context,
|
||||
Transitions transitions) {
|
||||
this(shellInit, shellExecutor, backgroundHandler,
|
||||
ActivityTaskManager.getService(), context, context.getContentResolver(),
|
||||
transitions);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -154,20 +202,25 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
@NonNull ShellInit shellInit,
|
||||
@NonNull @ShellMainThread ShellExecutor shellExecutor,
|
||||
@NonNull @ShellBackgroundThread Handler bgHandler,
|
||||
@NonNull SurfaceControl.Transaction transaction,
|
||||
@NonNull IActivityTaskManager activityTaskManager,
|
||||
Context context, ContentResolver contentResolver) {
|
||||
Context context, ContentResolver contentResolver,
|
||||
Transitions transitions) {
|
||||
mShellExecutor = shellExecutor;
|
||||
mTransaction = transaction;
|
||||
mActivityTaskManager = activityTaskManager;
|
||||
mContext = context;
|
||||
mContentResolver = contentResolver;
|
||||
mBgHandler = bgHandler;
|
||||
shellInit.addInitCallback(this::onInit, this);
|
||||
mTransitions = transitions;
|
||||
}
|
||||
|
||||
private void onInit() {
|
||||
setupAnimationDeveloperSettingsObserver(mContentResolver, mBgHandler);
|
||||
createAdapter();
|
||||
if (ENABLE_SHELL_TRANSITIONS) {
|
||||
mBackTransitionHandler = new BackTransitionHandler(this);
|
||||
mTransitions.addHandler(mBackTransitionHandler);
|
||||
}
|
||||
}
|
||||
|
||||
private void setupAnimationDeveloperSettingsObserver(
|
||||
@@ -250,9 +303,10 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBackToLauncherCallback(IOnBackInvokedCallback callback) {
|
||||
public void setBackToLauncherCallback(IOnBackInvokedCallback callback,
|
||||
IRemoteAnimationRunner runner) {
|
||||
executeRemoteCallWithTaskPermission(mController, "setBackToLauncherCallback",
|
||||
(controller) -> controller.setBackToLauncherCallback(callback));
|
||||
(controller) -> controller.setBackToLauncherCallback(callback, runner));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -261,28 +315,30 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
(controller) -> controller.clearBackToLauncherCallback());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackToLauncherAnimationFinished() {
|
||||
executeRemoteCallWithTaskPermission(mController, "onBackToLauncherAnimationFinished",
|
||||
(controller) -> controller.onBackToLauncherAnimationFinished());
|
||||
}
|
||||
|
||||
void invalidate() {
|
||||
mController = null;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setBackToLauncherCallback(IOnBackInvokedCallback callback) {
|
||||
mBackToLauncherCallback = callback;
|
||||
void setBackToLauncherCallback(IOnBackInvokedCallback callback, IRemoteAnimationRunner runner) {
|
||||
mAnimationDefinition.set(BackNavigationInfo.TYPE_RETURN_TO_HOME,
|
||||
new BackAnimationRunner(callback, runner));
|
||||
}
|
||||
|
||||
private void clearBackToLauncherCallback() {
|
||||
mBackToLauncherCallback = null;
|
||||
mAnimationDefinition.remove(BackNavigationInfo.TYPE_RETURN_TO_HOME);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void onBackToLauncherAnimationFinished() {
|
||||
void onBackAnimationFinished() {
|
||||
if (!mTransitionInProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: onBackAnimationFinished()");
|
||||
|
||||
// Trigger real back.
|
||||
if (mBackNavigationInfo != null) {
|
||||
IOnBackInvokedCallback callback = mBackNavigationInfo.getOnBackInvokedCallback();
|
||||
if (mTriggerBack) {
|
||||
@@ -291,7 +347,18 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
dispatchOnBackCancelled(callback);
|
||||
}
|
||||
}
|
||||
finishAnimation();
|
||||
|
||||
// In legacy transition, it would use `Task.mBackGestureStarted` in core to handle the
|
||||
// following transition when back callback is invoked.
|
||||
// If the back callback is not invoked, we should reset the token and finish the whole back
|
||||
// navigation without waiting the transition.
|
||||
if (!ENABLE_SHELL_TRANSITIONS) {
|
||||
finishBackNavigation();
|
||||
} else if (!mTriggerBack) {
|
||||
// reset the token to prevent it consume next transition.
|
||||
mBackTransitionHandler.setDepartingWindowContainerToken(null);
|
||||
finishBackNavigation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -303,6 +370,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
if (mTransitionInProgress) {
|
||||
return;
|
||||
}
|
||||
mTouchTracker.update(touchX, touchY, swipeEdge);
|
||||
if (keyAction == MotionEvent.ACTION_DOWN) {
|
||||
if (!mBackGestureStarted) {
|
||||
mShouldStartOnNextMoveEvent = true;
|
||||
@@ -330,20 +398,19 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "initAnimation mMotionStarted=%b", mBackGestureStarted);
|
||||
if (mBackGestureStarted || mBackNavigationInfo != null) {
|
||||
Log.e(TAG, "Animation is being initialized but is already started.");
|
||||
finishAnimation();
|
||||
finishBackNavigation();
|
||||
}
|
||||
|
||||
mInitTouchLocation.set(touchX, touchY);
|
||||
mTouchTracker.setGestureStartLocation(touchX, touchY);
|
||||
mBackGestureStarted = true;
|
||||
|
||||
try {
|
||||
boolean requestAnimation = mEnableAnimations.get();
|
||||
mBackNavigationInfo =
|
||||
mActivityTaskManager.startBackNavigation(requestAnimation, mFocusObserver);
|
||||
mBackNavigationInfo = mActivityTaskManager.startBackNavigation(
|
||||
mFocusObserver, mEnableAnimations.get() ? mBackAnimationAdapter : null);
|
||||
onBackNavigationInfoReceived(mBackNavigationInfo);
|
||||
} catch (RemoteException remoteException) {
|
||||
Log.e(TAG, "Failed to initAnimation", remoteException);
|
||||
finishAnimation();
|
||||
finishBackNavigation();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,74 +420,32 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
Log.e(TAG, "Received BackNavigationInfo is null.");
|
||||
return;
|
||||
}
|
||||
int backType = backNavigationInfo.getType();
|
||||
IOnBackInvokedCallback targetCallback = null;
|
||||
if (backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY) {
|
||||
HardwareBuffer hardwareBuffer = backNavigationInfo.getScreenshotHardwareBuffer();
|
||||
if (hardwareBuffer != null) {
|
||||
displayTargetScreenshot(hardwareBuffer,
|
||||
backNavigationInfo.getTaskWindowConfiguration());
|
||||
}
|
||||
mTransaction.apply();
|
||||
} else if (shouldDispatchToLauncher(backType)) {
|
||||
targetCallback = mBackToLauncherCallback;
|
||||
} else if (backType == BackNavigationInfo.TYPE_CALLBACK) {
|
||||
final int backType = backNavigationInfo.getType();
|
||||
final IOnBackInvokedCallback targetCallback;
|
||||
final boolean shouldDispatchToAnimator = shouldDispatchToAnimator(backType);
|
||||
if (shouldDispatchToAnimator) {
|
||||
targetCallback = mAnimationDefinition.get(backType).getGestureStartedCallback();
|
||||
} else {
|
||||
targetCallback = mBackNavigationInfo.getOnBackInvokedCallback();
|
||||
}
|
||||
dispatchOnBackStarted(targetCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the screenshot of the activity beneath.
|
||||
*
|
||||
* @param hardwareBuffer The buffer containing the screenshot.
|
||||
*/
|
||||
private void displayTargetScreenshot(@NonNull HardwareBuffer hardwareBuffer,
|
||||
WindowConfiguration taskWindowConfiguration) {
|
||||
SurfaceControl screenshotSurface =
|
||||
mBackNavigationInfo == null ? null : mBackNavigationInfo.getScreenshotSurface();
|
||||
if (screenshotSurface == null) {
|
||||
Log.e(TAG, "BackNavigationInfo doesn't contain a surface for the screenshot. ");
|
||||
return;
|
||||
if (shouldDispatchToAnimator) {
|
||||
dispatchOnBackStarted(targetCallback);
|
||||
}
|
||||
|
||||
// Scale the buffer to fill the whole Task
|
||||
float sx = 1;
|
||||
float sy = 1;
|
||||
float w = taskWindowConfiguration.getBounds().width();
|
||||
float h = taskWindowConfiguration.getBounds().height();
|
||||
|
||||
if (w != hardwareBuffer.getWidth()) {
|
||||
sx = w / hardwareBuffer.getWidth();
|
||||
}
|
||||
|
||||
if (h != hardwareBuffer.getHeight()) {
|
||||
sy = h / hardwareBuffer.getHeight();
|
||||
}
|
||||
mTransaction.setScale(screenshotSurface, sx, sy);
|
||||
mTransaction.setBuffer(screenshotSurface, hardwareBuffer);
|
||||
mTransaction.setVisibility(screenshotSurface, true);
|
||||
}
|
||||
|
||||
private void onMove(float touchX, float touchY, @BackEvent.SwipeEdge int swipeEdge) {
|
||||
if (!mBackGestureStarted || mBackNavigationInfo == null) {
|
||||
if (!mBackGestureStarted || mBackNavigationInfo == null || !mEnableAnimations.get()) {
|
||||
return;
|
||||
}
|
||||
int deltaX = Math.round(touchX - mInitTouchLocation.x);
|
||||
float progressThreshold = PROGRESS_THRESHOLD >= 0 ? PROGRESS_THRESHOLD : mProgressThreshold;
|
||||
float progress = Math.min(Math.max(Math.abs(deltaX) / progressThreshold, 0), 1);
|
||||
mTouchTracker.update(touchX, touchY, swipeEdge);
|
||||
float progress = mTouchTracker.getProgress(touchX);
|
||||
int backType = mBackNavigationInfo.getType();
|
||||
RemoteAnimationTarget animationTarget = mBackNavigationInfo.getDepartingAnimationTarget();
|
||||
|
||||
BackEvent backEvent = new BackEvent(
|
||||
touchX, touchY, progress, swipeEdge, animationTarget);
|
||||
BackEvent backEvent = new BackEvent(touchX, touchY, progress, swipeEdge);
|
||||
IOnBackInvokedCallback targetCallback = null;
|
||||
if (shouldDispatchToLauncher(backType)) {
|
||||
targetCallback = mBackToLauncherCallback;
|
||||
} else if (backType == BackNavigationInfo.TYPE_CROSS_TASK
|
||||
|| backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY) {
|
||||
// TODO(208427216) Run the actual animation
|
||||
} else if (backType == BackNavigationInfo.TYPE_CALLBACK) {
|
||||
if (shouldDispatchToAnimator(backType)) {
|
||||
targetCallback = mAnimationDefinition.get(backType).getCallback();
|
||||
} else {
|
||||
targetCallback = mBackNavigationInfo.getOnBackInvokedCallback();
|
||||
}
|
||||
dispatchOnBackProgressed(targetCallback, backEvent);
|
||||
@@ -448,7 +473,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
private void onGestureFinished(boolean fromTouch) {
|
||||
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "onGestureFinished() mTriggerBack == %s", mTriggerBack);
|
||||
if (!mBackGestureStarted) {
|
||||
finishAnimation();
|
||||
finishBackNavigation();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -468,16 +493,21 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
if (mTriggerBack) {
|
||||
injectBackKey();
|
||||
}
|
||||
finishAnimation();
|
||||
finishBackNavigation();
|
||||
return;
|
||||
}
|
||||
|
||||
int backType = mBackNavigationInfo.getType();
|
||||
boolean shouldDispatchToLauncher = shouldDispatchToLauncher(backType);
|
||||
IOnBackInvokedCallback targetCallback = shouldDispatchToLauncher
|
||||
? mBackToLauncherCallback
|
||||
: mBackNavigationInfo.getOnBackInvokedCallback();
|
||||
if (shouldDispatchToLauncher) {
|
||||
boolean shouldDispatchToAnimator = shouldDispatchToAnimator(backType);
|
||||
final BackAnimationRunner runner = mAnimationDefinition.get(backType);
|
||||
IOnBackInvokedCallback targetCallback = shouldDispatchToAnimator
|
||||
? runner.getCallback() : mBackNavigationInfo.getOnBackInvokedCallback();
|
||||
|
||||
if (shouldDispatchToAnimator) {
|
||||
if (runner.onGestureFinished(mTriggerBack)) {
|
||||
Log.w(TAG, "Gesture released, but animation didn't ready.");
|
||||
return;
|
||||
}
|
||||
startTransition();
|
||||
}
|
||||
if (mTriggerBack) {
|
||||
@@ -485,18 +515,17 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
} else {
|
||||
dispatchOnBackCancelled(targetCallback);
|
||||
}
|
||||
if (backType != BackNavigationInfo.TYPE_RETURN_TO_HOME || !shouldDispatchToLauncher) {
|
||||
// Launcher callback missing. Simply finish animation.
|
||||
finishAnimation();
|
||||
if (!shouldDispatchToAnimator) {
|
||||
// Animation callback missing. Simply finish animation.
|
||||
finishBackNavigation();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldDispatchToLauncher(int backType) {
|
||||
return backType == BackNavigationInfo.TYPE_RETURN_TO_HOME
|
||||
&& mBackToLauncherCallback != null
|
||||
&& mEnableAnimations.get()
|
||||
private boolean shouldDispatchToAnimator(int backType) {
|
||||
return mEnableAnimations.get()
|
||||
&& mBackNavigationInfo != null
|
||||
&& mBackNavigationInfo.getDepartingAnimationTarget() != null;
|
||||
&& mBackNavigationInfo.isPrepareRemoteAnimation()
|
||||
&& mAnimationDefinition.contains(backType);
|
||||
}
|
||||
|
||||
private static void dispatchOnBackStarted(IOnBackInvokedCallback callback) {
|
||||
@@ -555,35 +584,30 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
}
|
||||
|
||||
private void setSwipeThresholds(float triggerThreshold, float progressThreshold) {
|
||||
mProgressThreshold = progressThreshold;
|
||||
mTriggerThreshold = triggerThreshold;
|
||||
mTouchTracker.setProgressThreshold(progressThreshold);
|
||||
}
|
||||
|
||||
private void finishAnimation() {
|
||||
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishAnimation()");
|
||||
mTouchEventDelta.set(0, 0);
|
||||
mInitTouchLocation.set(0, 0);
|
||||
@VisibleForTesting
|
||||
void finishBackNavigation() {
|
||||
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishBackNavigation()");
|
||||
BackNavigationInfo backNavigationInfo = mBackNavigationInfo;
|
||||
boolean triggerBack = mTriggerBack;
|
||||
mBackNavigationInfo = null;
|
||||
mTriggerBack = false;
|
||||
mShouldStartOnNextMoveEvent = false;
|
||||
mTouchTracker.reset();
|
||||
if (backNavigationInfo == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
RemoteAnimationTarget animationTarget = backNavigationInfo.getDepartingAnimationTarget();
|
||||
if (animationTarget != null) {
|
||||
if (animationTarget.leash != null && animationTarget.leash.isValid()) {
|
||||
mTransaction.remove(animationTarget.leash);
|
||||
}
|
||||
}
|
||||
SurfaceControl screenshotSurface = backNavigationInfo.getScreenshotSurface();
|
||||
if (screenshotSurface != null && screenshotSurface.isValid()) {
|
||||
mTransaction.remove(screenshotSurface);
|
||||
}
|
||||
mTransaction.apply();
|
||||
stopTransition();
|
||||
if (mBackAnimationFinishedCallback != null) {
|
||||
try {
|
||||
mBackAnimationFinishedCallback.onAnimationFinished(triggerBack);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed call IBackAnimationFinishedCallback", e);
|
||||
}
|
||||
mBackAnimationFinishedCallback = null;
|
||||
}
|
||||
backNavigationInfo.onBackNavigationFinished(triggerBack);
|
||||
}
|
||||
|
||||
@@ -592,14 +616,72 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
|
||||
return;
|
||||
}
|
||||
mTransitionInProgress = true;
|
||||
if (ENABLE_SHELL_TRANSITIONS) {
|
||||
mBackTransitionHandler.setDepartingWindowContainerToken(
|
||||
mBackNavigationInfo.getDepartingWindowContainerToken());
|
||||
}
|
||||
mShellExecutor.executeDelayed(mResetTransitionRunnable, MAX_TRANSITION_DURATION);
|
||||
}
|
||||
|
||||
private void stopTransition() {
|
||||
if (!mTransitionInProgress) {
|
||||
return;
|
||||
}
|
||||
void stopTransition() {
|
||||
mShellExecutor.removeCallbacks(mResetTransitionRunnable);
|
||||
mTransitionInProgress = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be called from {@link BackTransitionHandler#startAnimation} when the following
|
||||
* transition is triggered by the real back callback in {@link #onBackAnimationFinished}.
|
||||
* Will consume the default transition and finish current back navigation.
|
||||
*/
|
||||
void finishTransition(Transitions.TransitionFinishCallback finishCallback) {
|
||||
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishTransition()");
|
||||
mShellExecutor.execute(() -> {
|
||||
finishBackNavigation();
|
||||
finishCallback.onTransitionFinished(null, null);
|
||||
});
|
||||
}
|
||||
|
||||
private void createAdapter() {
|
||||
IBackAnimationRunner runner = new IBackAnimationRunner.Stub() {
|
||||
@Override
|
||||
public void onAnimationStart(int type, RemoteAnimationTarget[] apps,
|
||||
RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
|
||||
IBackAnimationFinishedCallback finishedCallback) {
|
||||
mShellExecutor.execute(() -> {
|
||||
final BackAnimationRunner runner = mAnimationDefinition.get(type);
|
||||
if (runner == null) {
|
||||
Log.e(TAG, "Animation didn't be defined for type "
|
||||
+ BackNavigationInfo.typeToString(type));
|
||||
if (finishedCallback != null) {
|
||||
try {
|
||||
finishedCallback.onAnimationFinished(false);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed call IBackNaviAnimationController", e);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
mBackAnimationFinishedCallback = finishedCallback;
|
||||
|
||||
ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: startAnimation()");
|
||||
runner.startAnimation(apps, wallpapers, nonApps,
|
||||
BackAnimationController.this::onBackAnimationFinished);
|
||||
|
||||
if (!mBackGestureStarted) {
|
||||
// if the down -> up gesture happened before animation start, we have to
|
||||
// trigger the uninterruptible transition to finish the back animation.
|
||||
final BackEvent backFinish = new BackEvent(
|
||||
mTouchTracker.mLatestTouchX, mTouchTracker.mLatestTouchY, 1,
|
||||
mTouchTracker.mSwipeEdge);
|
||||
startTransition();
|
||||
runner.consumeIfGestureFinished(backFinish);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancelled() { }
|
||||
};
|
||||
mBackAnimationAdapter = new BackAnimationAdapter(runner);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.wm.shell.back;
|
||||
|
||||
import static android.view.WindowManager.TRANSIT_OLD_UNSET;
|
||||
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.IRemoteAnimationFinishedCallback;
|
||||
import android.view.IRemoteAnimationRunner;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.window.BackEvent;
|
||||
import android.window.IBackAnimationRunner;
|
||||
import android.window.IOnBackInvokedCallback;
|
||||
|
||||
/**
|
||||
* Used to register the animation callback and runner, it will trigger result if gesture was finish
|
||||
* before it received IBackAnimationRunner#onAnimationStart, so the controller could continue
|
||||
* trigger the real back behavior.
|
||||
*/
|
||||
class BackAnimationRunner {
|
||||
private static final String TAG = "ShellBackPreview";
|
||||
|
||||
private final IOnBackInvokedCallback mCallback;
|
||||
private final IRemoteAnimationRunner mRunner;
|
||||
|
||||
private boolean mTriggerBack;
|
||||
// Whether we are waiting to receive onAnimationStart
|
||||
private boolean mWaitingAnimation;
|
||||
|
||||
BackAnimationRunner(IOnBackInvokedCallback callback, IRemoteAnimationRunner runner) {
|
||||
mCallback = callback;
|
||||
mRunner = runner;
|
||||
}
|
||||
|
||||
/** Returns the registered animation runner */
|
||||
IRemoteAnimationRunner getRunner() {
|
||||
return mRunner;
|
||||
}
|
||||
|
||||
/** Returns the registered animation callback */
|
||||
IOnBackInvokedCallback getCallback() {
|
||||
return mCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from {@link IBackAnimationRunner}, it will deliver these
|
||||
* {@link RemoteAnimationTarget}s to the corresponding runner.
|
||||
*/
|
||||
void startAnimation(RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
|
||||
RemoteAnimationTarget[] nonApps, Runnable finishedCallback) {
|
||||
final IRemoteAnimationFinishedCallback callback =
|
||||
new IRemoteAnimationFinishedCallback.Stub() {
|
||||
@Override
|
||||
public void onAnimationFinished() {
|
||||
finishedCallback.run();
|
||||
}
|
||||
};
|
||||
mWaitingAnimation = false;
|
||||
try {
|
||||
mRunner.onAnimationStart(TRANSIT_OLD_UNSET, apps, wallpapers,
|
||||
nonApps, callback);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed call onAnimationStart", e);
|
||||
}
|
||||
}
|
||||
|
||||
IOnBackInvokedCallback getGestureStartedCallback() {
|
||||
mWaitingAnimation = true;
|
||||
return mCallback;
|
||||
}
|
||||
|
||||
boolean onGestureFinished(boolean triggerBack) {
|
||||
if (mWaitingAnimation) {
|
||||
mTriggerBack = triggerBack;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void consumeIfGestureFinished(final BackEvent backFinish) {
|
||||
Log.d(TAG, "Start transition due to gesture is finished");
|
||||
try {
|
||||
mCallback.onBackProgressed(backFinish);
|
||||
if (mTriggerBack) {
|
||||
mCallback.onBackInvoked();
|
||||
} else {
|
||||
mCallback.onBackCancelled();
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "dispatch error: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.wm.shell.back;
|
||||
|
||||
import android.os.IBinder;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.TransitionInfo;
|
||||
import android.window.TransitionRequestInfo;
|
||||
import android.window.WindowContainerToken;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
|
||||
class BackTransitionHandler implements Transitions.TransitionHandler {
|
||||
private BackAnimationController mBackAnimationController;
|
||||
private WindowContainerToken mDepartingWindowContainerToken;
|
||||
|
||||
BackTransitionHandler(@NonNull BackAnimationController backAnimationController) {
|
||||
mBackAnimationController = backAnimationController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||
@NonNull SurfaceControl.Transaction startTransaction,
|
||||
@NonNull SurfaceControl.Transaction finishTransaction,
|
||||
@NonNull Transitions.TransitionFinishCallback finishCallback) {
|
||||
if (mDepartingWindowContainerToken != null) {
|
||||
final TransitionInfo.Change change = info.getChange(mDepartingWindowContainerToken);
|
||||
if (change == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
startTransaction.hide(change.getLeash());
|
||||
startTransaction.apply();
|
||||
mDepartingWindowContainerToken = null;
|
||||
mBackAnimationController.finishTransition(finishCallback);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
|
||||
@NonNull TransitionRequestInfo request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
|
||||
@NonNull Transitions.TransitionFinishCallback finishCallback) {
|
||||
}
|
||||
|
||||
void setDepartingWindowContainerToken(
|
||||
@Nullable WindowContainerToken departingWindowContainerToken) {
|
||||
mDepartingWindowContainerToken = departingWindowContainerToken;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,29 +17,21 @@
|
||||
package com.android.wm.shell.back;
|
||||
|
||||
import android.window.IOnBackInvokedCallback;
|
||||
import android.view.IRemoteAnimationRunner;
|
||||
|
||||
/**
|
||||
* Interface for Launcher process to register back invocation callbacks.
|
||||
*/
|
||||
interface IBackAnimation {
|
||||
|
||||
/**
|
||||
* Sets a {@link IOnBackInvokedCallback} to be invoked when
|
||||
* Sets a {@link IOnBackInvokedCallback} and a {@link IRemoteAnimationRunner} to be invoked when
|
||||
* back navigation has type {@link BackNavigationInfo#TYPE_RETURN_TO_HOME}.
|
||||
*/
|
||||
void setBackToLauncherCallback(in IOnBackInvokedCallback callback);
|
||||
void setBackToLauncherCallback(in IOnBackInvokedCallback callback,
|
||||
in IRemoteAnimationRunner runner);
|
||||
|
||||
/**
|
||||
* Clears the previously registered {@link IOnBackInvokedCallback}.
|
||||
*/
|
||||
void clearBackToLauncherCallback();
|
||||
|
||||
/**
|
||||
* Notifies Shell that the back to launcher animation has fully finished
|
||||
* (including the transition animation that runs after the finger is lifted).
|
||||
*
|
||||
* At this point the top window leash (if one was created) should be ready to be released.
|
||||
* //TODO: Remove once we play the transition animation through shell transitions.
|
||||
*/
|
||||
void onBackToLauncherAnimationFinished();
|
||||
}
|
||||
|
||||
@@ -279,12 +279,13 @@ public abstract class WMShellBaseModule {
|
||||
Context context,
|
||||
ShellInit shellInit,
|
||||
@ShellMainThread ShellExecutor shellExecutor,
|
||||
@ShellBackgroundThread Handler backgroundHandler
|
||||
@ShellBackgroundThread Handler backgroundHandler,
|
||||
Transitions transitions
|
||||
) {
|
||||
if (BackAnimationController.IS_ENABLED) {
|
||||
return Optional.of(
|
||||
new BackAnimationController(shellInit, shellExecutor, backgroundHandler,
|
||||
context));
|
||||
context, transitions));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@@ -18,12 +18,9 @@ package com.android.wm.shell.back;
|
||||
|
||||
import static android.window.BackNavigationInfo.KEY_TRIGGER_BACK;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -39,7 +36,6 @@ import android.app.WindowConfiguration;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteCallback;
|
||||
@@ -49,11 +45,13 @@ import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableContentResolver;
|
||||
import android.testing.TestableContext;
|
||||
import android.testing.TestableLooper;
|
||||
import android.view.IRemoteAnimationRunner;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.BackEvent;
|
||||
import android.window.BackNavigationInfo;
|
||||
import android.window.IBackAnimationFinishedCallback;
|
||||
import android.window.IOnBackInvokedCallback;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
@@ -63,9 +61,9 @@ import com.android.internal.util.test.FakeSettingsProvider;
|
||||
import com.android.wm.shell.ShellTestCase;
|
||||
import com.android.wm.shell.TestShellExecutor;
|
||||
import com.android.wm.shell.sysui.ShellInit;
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -89,15 +87,21 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
public TestableContext mContext =
|
||||
new TestableContext(InstrumentationRegistry.getInstrumentation().getContext());
|
||||
|
||||
@Mock
|
||||
private SurfaceControl.Transaction mTransaction;
|
||||
|
||||
@Mock
|
||||
private IActivityTaskManager mActivityTaskManager;
|
||||
|
||||
@Mock
|
||||
private IOnBackInvokedCallback mIOnBackInvokedCallback;
|
||||
|
||||
@Mock
|
||||
private IBackAnimationFinishedCallback mBackAnimationFinishedCallback;
|
||||
|
||||
@Mock
|
||||
private IRemoteAnimationRunner mBackAnimationRunner;
|
||||
|
||||
@Mock
|
||||
private Transitions mTransitions;
|
||||
|
||||
private BackAnimationController mController;
|
||||
|
||||
private int mEventTime = 0;
|
||||
@@ -115,27 +119,20 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
mTestableLooper = TestableLooper.get(this);
|
||||
mShellInit = spy(new ShellInit(mShellExecutor));
|
||||
mController = new BackAnimationController(mShellInit,
|
||||
mShellExecutor, new Handler(mTestableLooper.getLooper()), mTransaction,
|
||||
mShellExecutor, new Handler(mTestableLooper.getLooper()),
|
||||
mActivityTaskManager, mContext,
|
||||
mContentResolver);
|
||||
mContentResolver, mTransitions);
|
||||
mShellInit.init();
|
||||
mEventTime = 0;
|
||||
mShellExecutor.flushAll();
|
||||
}
|
||||
|
||||
private void createNavigationInfo(RemoteAnimationTarget topAnimationTarget,
|
||||
SurfaceControl screenshotSurface,
|
||||
HardwareBuffer hardwareBuffer,
|
||||
int backType,
|
||||
IOnBackInvokedCallback onBackInvokedCallback) {
|
||||
private void createNavigationInfo(int backType, IOnBackInvokedCallback onBackInvokedCallback) {
|
||||
BackNavigationInfo.Builder builder = new BackNavigationInfo.Builder()
|
||||
.setType(backType)
|
||||
.setDepartingAnimationTarget(topAnimationTarget)
|
||||
.setScreenshotSurface(screenshotSurface)
|
||||
.setScreenshotBuffer(hardwareBuffer)
|
||||
.setTaskWindowConfiguration(new WindowConfiguration())
|
||||
.setOnBackNavigationDone(new RemoteCallback((bundle) -> {}))
|
||||
.setOnBackInvokedCallback(onBackInvokedCallback);
|
||||
.setOnBackInvokedCallback(onBackInvokedCallback)
|
||||
.setPrepareRemoteAnimation(true);
|
||||
|
||||
createNavigationInfo(builder);
|
||||
}
|
||||
@@ -143,7 +140,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
private void createNavigationInfo(BackNavigationInfo.Builder builder) {
|
||||
try {
|
||||
doReturn(builder.build()).when(mActivityTaskManager)
|
||||
.startBackNavigation(anyBoolean(), any());
|
||||
.startBackNavigation(any(), any());
|
||||
} catch (RemoteException ex) {
|
||||
ex.rethrowFromSystemServer();
|
||||
}
|
||||
@@ -169,43 +166,12 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
verify(mShellInit, times(1)).addInitCallback(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("b/207481538")
|
||||
public void crossActivity_screenshotAttachedAndVisible() {
|
||||
SurfaceControl screenshotSurface = new SurfaceControl();
|
||||
HardwareBuffer hardwareBuffer = mock(HardwareBuffer.class);
|
||||
createNavigationInfo(createAnimationTarget(), screenshotSurface, hardwareBuffer,
|
||||
BackNavigationInfo.TYPE_CROSS_ACTIVITY, null);
|
||||
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
|
||||
verify(mTransaction).setBuffer(screenshotSurface, hardwareBuffer);
|
||||
verify(mTransaction).setVisibility(screenshotSurface, true);
|
||||
verify(mTransaction).apply();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void crossActivity_surfaceMovesWithGesture() {
|
||||
SurfaceControl screenshotSurface = new SurfaceControl();
|
||||
HardwareBuffer hardwareBuffer = mock(HardwareBuffer.class);
|
||||
RemoteAnimationTarget animationTarget = createAnimationTarget();
|
||||
createNavigationInfo(animationTarget, screenshotSurface, hardwareBuffer,
|
||||
BackNavigationInfo.TYPE_CROSS_ACTIVITY, null);
|
||||
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
|
||||
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
|
||||
// b/207481538, we check that the surface is not moved for now, we can re-enable this once
|
||||
// we implement the animation
|
||||
verify(mTransaction, never()).setScale(eq(screenshotSurface), anyInt(), anyInt());
|
||||
verify(mTransaction, never()).setPosition(
|
||||
animationTarget.leash, 100, 100);
|
||||
verify(mTransaction, atLeastOnce()).apply();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyAnimationFinishes() {
|
||||
RemoteAnimationTarget animationTarget = createAnimationTarget();
|
||||
boolean[] backNavigationDone = new boolean[]{false};
|
||||
boolean[] triggerBack = new boolean[]{false};
|
||||
createNavigationInfo(new BackNavigationInfo.Builder()
|
||||
.setDepartingAnimationTarget(animationTarget)
|
||||
.setType(BackNavigationInfo.TYPE_CROSS_ACTIVITY)
|
||||
.setOnBackNavigationDone(
|
||||
new RemoteCallback(result -> {
|
||||
@@ -219,19 +185,19 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
|
||||
@Test
|
||||
public void backToHome_dispatchesEvents() throws RemoteException {
|
||||
mController.setBackToLauncherCallback(mIOnBackInvokedCallback);
|
||||
RemoteAnimationTarget animationTarget = createAnimationTarget();
|
||||
createNavigationInfo(animationTarget, null, null,
|
||||
BackNavigationInfo.TYPE_RETURN_TO_HOME, null);
|
||||
mController.setBackToLauncherCallback(mIOnBackInvokedCallback, mBackAnimationRunner);
|
||||
createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, mIOnBackInvokedCallback);
|
||||
|
||||
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
|
||||
|
||||
// Check that back start and progress is dispatched when first move.
|
||||
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
|
||||
|
||||
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
|
||||
verify(mIOnBackInvokedCallback).onBackStarted();
|
||||
verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
|
||||
ArgumentCaptor<BackEvent> backEventCaptor = ArgumentCaptor.forClass(BackEvent.class);
|
||||
verify(mIOnBackInvokedCallback).onBackProgressed(backEventCaptor.capture());
|
||||
assertEquals(animationTarget, backEventCaptor.getValue().getDepartingAnimationTarget());
|
||||
verify(mIOnBackInvokedCallback, atLeastOnce()).onBackProgressed(backEventCaptor.capture());
|
||||
|
||||
// Check that back invocation is dispatched.
|
||||
mController.setTriggerBack(true); // Fake trigger back
|
||||
@@ -245,17 +211,16 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
Settings.Global.putString(mContentResolver, Settings.Global.ENABLE_BACK_ANIMATION, "0");
|
||||
ShellInit shellInit = new ShellInit(mShellExecutor);
|
||||
mController = new BackAnimationController(shellInit,
|
||||
mShellExecutor, new Handler(mTestableLooper.getLooper()), mTransaction,
|
||||
mShellExecutor, new Handler(mTestableLooper.getLooper()),
|
||||
mActivityTaskManager, mContext,
|
||||
mContentResolver);
|
||||
mContentResolver, mTransitions);
|
||||
shellInit.init();
|
||||
mController.setBackToLauncherCallback(mIOnBackInvokedCallback);
|
||||
mController.setBackToLauncherCallback(mIOnBackInvokedCallback, mBackAnimationRunner);
|
||||
|
||||
RemoteAnimationTarget animationTarget = createAnimationTarget();
|
||||
IOnBackInvokedCallback appCallback = mock(IOnBackInvokedCallback.class);
|
||||
ArgumentCaptor<BackEvent> backEventCaptor = ArgumentCaptor.forClass(BackEvent.class);
|
||||
createNavigationInfo(animationTarget, null, null,
|
||||
BackNavigationInfo.TYPE_RETURN_TO_HOME, appCallback);
|
||||
|
||||
createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, appCallback);
|
||||
|
||||
triggerBackGesture();
|
||||
|
||||
@@ -266,25 +231,31 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
verify(mIOnBackInvokedCallback, never()).onBackStarted();
|
||||
verify(mIOnBackInvokedCallback, never()).onBackProgressed(backEventCaptor.capture());
|
||||
verify(mIOnBackInvokedCallback, never()).onBackInvoked();
|
||||
verify(mBackAnimationRunner, never()).onAnimationStart(
|
||||
anyInt(), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ignoresGesture_transitionInProgress() throws RemoteException {
|
||||
mController.setBackToLauncherCallback(mIOnBackInvokedCallback);
|
||||
RemoteAnimationTarget animationTarget = createAnimationTarget();
|
||||
createNavigationInfo(animationTarget, null, null,
|
||||
BackNavigationInfo.TYPE_RETURN_TO_HOME, null);
|
||||
mController.setBackToLauncherCallback(mIOnBackInvokedCallback, mBackAnimationRunner);
|
||||
createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, null);
|
||||
|
||||
triggerBackGesture();
|
||||
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
|
||||
// Check that back invocation is dispatched.
|
||||
verify(mIOnBackInvokedCallback).onBackInvoked();
|
||||
verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
|
||||
|
||||
reset(mIOnBackInvokedCallback);
|
||||
reset(mBackAnimationRunner);
|
||||
|
||||
// Verify that we prevent animation from restarting if another gestures happens before
|
||||
// the previous transition is finished.
|
||||
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
|
||||
verifyNoMoreInteractions(mIOnBackInvokedCallback);
|
||||
mController.onBackToLauncherAnimationFinished();
|
||||
mController.onBackAnimationFinished();
|
||||
// Pretend the transition handler called finishAnimation.
|
||||
mController.finishBackNavigation();
|
||||
|
||||
// Verify that more events from a rejected swipe cannot start animation.
|
||||
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
|
||||
@@ -294,39 +265,49 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
// Verify that we start accepting gestures again once transition finishes.
|
||||
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
|
||||
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
|
||||
|
||||
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
|
||||
verify(mIOnBackInvokedCallback).onBackStarted();
|
||||
verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void acceptsGesture_transitionTimeout() throws RemoteException {
|
||||
mController.setBackToLauncherCallback(mIOnBackInvokedCallback);
|
||||
RemoteAnimationTarget animationTarget = createAnimationTarget();
|
||||
createNavigationInfo(animationTarget, null, null,
|
||||
BackNavigationInfo.TYPE_RETURN_TO_HOME, null);
|
||||
mController.setBackToLauncherCallback(mIOnBackInvokedCallback, mBackAnimationRunner);
|
||||
createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, null);
|
||||
|
||||
triggerBackGesture();
|
||||
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
|
||||
|
||||
reset(mIOnBackInvokedCallback);
|
||||
|
||||
// Simulate transition timeout.
|
||||
mShellExecutor.flushAll();
|
||||
mController.onBackAnimationFinished();
|
||||
// Pretend the transition handler called finishAnimation.
|
||||
mController.finishBackNavigation();
|
||||
|
||||
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
|
||||
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
|
||||
|
||||
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
|
||||
verify(mIOnBackInvokedCallback).onBackStarted();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void cancelBackInvokeWhenLostFocus() throws RemoteException {
|
||||
mController.setBackToLauncherCallback(mIOnBackInvokedCallback);
|
||||
RemoteAnimationTarget animationTarget = createAnimationTarget();
|
||||
mController.setBackToLauncherCallback(mIOnBackInvokedCallback, mBackAnimationRunner);
|
||||
|
||||
createNavigationInfo(animationTarget, null, null,
|
||||
BackNavigationInfo.TYPE_RETURN_TO_HOME, null);
|
||||
createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, null);
|
||||
|
||||
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
|
||||
// Check that back start and progress is dispatched when first move.
|
||||
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
|
||||
|
||||
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME);
|
||||
verify(mIOnBackInvokedCallback).onBackStarted();
|
||||
verify(mBackAnimationRunner).onAnimationStart(anyInt(), any(), any(), any(), any());
|
||||
|
||||
// Check that back invocation is dispatched.
|
||||
mController.setTriggerBack(true); // Fake trigger back
|
||||
@@ -349,4 +330,14 @@ public class BackAnimationControllerTest extends ShellTestCase {
|
||||
BackEvent.EDGE_LEFT);
|
||||
mEventTime += 10;
|
||||
}
|
||||
|
||||
private void simulateRemoteAnimationStart(int type) throws RemoteException {
|
||||
RemoteAnimationTarget animationTarget = createAnimationTarget();
|
||||
RemoteAnimationTarget[] targets = new RemoteAnimationTarget[]{animationTarget};
|
||||
if (mController.mBackAnimationAdapter != null) {
|
||||
mController.mBackAnimationAdapter.getRunner().onAnimationStart(type,
|
||||
targets, null, null, mBackAnimationFinishedCallback);
|
||||
mShellExecutor.flushAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,6 +227,7 @@ import android.view.IWindowFocusObserver;
|
||||
import android.view.RemoteAnimationAdapter;
|
||||
import android.view.RemoteAnimationDefinition;
|
||||
import android.view.WindowManager;
|
||||
import android.window.BackAnimationAdapter;
|
||||
import android.window.BackNavigationInfo;
|
||||
import android.window.IWindowOrganizerController;
|
||||
import android.window.SplashScreenView.SplashScreenViewParcelable;
|
||||
@@ -459,7 +460,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
|
||||
private final ClientLifecycleManager mLifecycleManager;
|
||||
|
||||
@Nullable
|
||||
private final BackNavigationController mBackNavigationController;
|
||||
final BackNavigationController mBackNavigationController;
|
||||
|
||||
private TaskChangeNotificationController mTaskChangeNotificationController;
|
||||
/** The controller for all operations related to locktask. */
|
||||
@@ -1846,14 +1847,15 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BackNavigationInfo startBackNavigation(boolean requestAnimation,
|
||||
IWindowFocusObserver observer) {
|
||||
public BackNavigationInfo startBackNavigation(
|
||||
IWindowFocusObserver observer, BackAnimationAdapter adapter) {
|
||||
mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
|
||||
"startBackNavigation()");
|
||||
if (mBackNavigationController == null) {
|
||||
return null;
|
||||
}
|
||||
return mBackNavigationController.startBackNavigation(requestAnimation, observer);
|
||||
|
||||
return mBackNavigationController.startBackNavigation(observer, adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,11 +16,14 @@
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
|
||||
import static android.view.RemoteAnimationTarget.MODE_OPENING;
|
||||
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
|
||||
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.WindowConfiguration;
|
||||
import android.content.ComponentName;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
@@ -34,15 +37,20 @@ import android.util.Slog;
|
||||
import android.view.IWindowFocusObserver;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.BackAnimationAdapter;
|
||||
import android.window.BackNavigationInfo;
|
||||
import android.window.IBackAnimationFinishedCallback;
|
||||
import android.window.OnBackInvokedCallbackInfo;
|
||||
import android.window.ScreenCapture;
|
||||
import android.window.TaskSnapshot;
|
||||
import android.window.WindowContainerToken;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.server.LocalServices;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Controller to handle actions related to the back gesture on the server side.
|
||||
*/
|
||||
@@ -50,7 +58,13 @@ class BackNavigationController {
|
||||
private static final String TAG = "BackNavigationController";
|
||||
private WindowManagerService mWindowManagerService;
|
||||
private IWindowFocusObserver mFocusObserver;
|
||||
private boolean mBackAnimationInProgress;
|
||||
private boolean mShowWallpaper;
|
||||
private Runnable mPendingAnimation;
|
||||
|
||||
// TODO (b/241808055) Find a appropriate time to remove during refactor
|
||||
// Execute back animation with legacy transition system. Temporary flag for easier debugging.
|
||||
static final boolean ENABLE_SHELL_TRANSITIONS = WindowManagerService.sEnableShellTransitions;
|
||||
/**
|
||||
* Returns true if the back predictability feature is enabled
|
||||
*/
|
||||
@@ -72,10 +86,9 @@ class BackNavigationController {
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
BackNavigationInfo startBackNavigation(boolean requestAnimation,
|
||||
IWindowFocusObserver observer) {
|
||||
BackNavigationInfo startBackNavigation(
|
||||
IWindowFocusObserver observer, BackAnimationAdapter adapter) {
|
||||
final WindowManagerService wmService = mWindowManagerService;
|
||||
final SurfaceControl.Transaction tx = wmService.mTransactionFactory.get();
|
||||
mFocusObserver = observer;
|
||||
|
||||
int backType = BackNavigationInfo.TYPE_UNDEFINED;
|
||||
@@ -95,18 +108,10 @@ class BackNavigationController {
|
||||
// currentActivity is the last child of currentTask.
|
||||
ActivityRecord prevActivity;
|
||||
WindowContainer<?> removedWindowContainer = null;
|
||||
SurfaceControl animationLeashParent = null;
|
||||
HardwareBuffer screenshotBuffer = null;
|
||||
RemoteAnimationTarget topAppTarget = null;
|
||||
WindowState window;
|
||||
|
||||
int prevTaskId;
|
||||
int prevUserId;
|
||||
boolean prepareAnimation;
|
||||
|
||||
BackNavigationInfo.Builder infoBuilder = new BackNavigationInfo.Builder();
|
||||
synchronized (wmService.mGlobalLock) {
|
||||
WindowConfiguration taskWindowConfiguration;
|
||||
WindowManagerInternal windowManagerInternal =
|
||||
LocalServices.getService(WindowManagerInternal.class);
|
||||
IBinder focusedWindowToken = windowManagerInternal.getFocusedWindowToken();
|
||||
@@ -204,12 +209,13 @@ class BackNavigationController {
|
||||
infoBuilder.setType(BackNavigationInfo.TYPE_CALLBACK);
|
||||
final WindowState finalFocusedWindow = window;
|
||||
infoBuilder.setOnBackNavigationDone(new RemoteCallback(result ->
|
||||
onBackNavigationDone(result, finalFocusedWindow, finalFocusedWindow,
|
||||
BackNavigationInfo.TYPE_CALLBACK, null, null, false)));
|
||||
onBackNavigationDone(result, finalFocusedWindow,
|
||||
BackNavigationInfo.TYPE_CALLBACK)));
|
||||
|
||||
return infoBuilder.setType(backType).build();
|
||||
}
|
||||
|
||||
mBackAnimationInProgress = true;
|
||||
// We don't have an application callback, let's find the destination of the back gesture
|
||||
Task finalTask = currentTask;
|
||||
prevActivity = currentTask.getActivity(
|
||||
@@ -230,6 +236,7 @@ class BackNavigationController {
|
||||
// Our Task should bring back to home
|
||||
removedWindowContainer = currentTask;
|
||||
backType = BackNavigationInfo.TYPE_RETURN_TO_HOME;
|
||||
mShowWallpaper = true;
|
||||
} else if (currentActivity.isRootOfTask()) {
|
||||
// TODO(208789724): Create single source of truth for this, maybe in
|
||||
// RootWindowContainer
|
||||
@@ -242,12 +249,10 @@ class BackNavigationController {
|
||||
} else {
|
||||
backType = BackNavigationInfo.TYPE_CROSS_TASK;
|
||||
}
|
||||
mShowWallpaper = true;
|
||||
}
|
||||
infoBuilder.setType(backType);
|
||||
|
||||
prevTaskId = prevTask != null ? prevTask.mTaskId : 0;
|
||||
prevUserId = prevTask != null ? prevTask.mUserId : 0;
|
||||
|
||||
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Previous Destination is Activity:%s Task:%s "
|
||||
+ "removedContainer:%s, backType=%s",
|
||||
prevActivity != null ? prevActivity.mActivityComponent : null,
|
||||
@@ -256,167 +261,275 @@ class BackNavigationController {
|
||||
BackNavigationInfo.typeToString(backType));
|
||||
|
||||
// For now, we only animate when going home.
|
||||
prepareAnimation = backType == BackNavigationInfo.TYPE_RETURN_TO_HOME
|
||||
&& requestAnimation
|
||||
// Only create a new leash if no leash has been created.
|
||||
// Otherwise return null for animation target to avoid conflict.
|
||||
&& !removedWindowContainer.hasCommittedReparentToAnimationLeash();
|
||||
boolean prepareAnimation = backType == BackNavigationInfo.TYPE_RETURN_TO_HOME
|
||||
&& adapter != null;
|
||||
|
||||
// Only prepare animation if no leash has been created (no animation is running).
|
||||
// TODO(b/241808055): Cancel animation when preparing back animation.
|
||||
if (prepareAnimation
|
||||
&& (removedWindowContainer.hasCommittedReparentToAnimationLeash()
|
||||
|| removedWindowContainer.mTransitionController.inTransition())) {
|
||||
Slog.w(TAG, "Can't prepare back animation due to another animation is running.");
|
||||
prepareAnimation = false;
|
||||
}
|
||||
|
||||
if (prepareAnimation) {
|
||||
taskWindowConfiguration =
|
||||
currentTask.getTaskInfo().configuration.windowConfiguration;
|
||||
|
||||
infoBuilder.setTaskWindowConfiguration(taskWindowConfiguration);
|
||||
// Prepare a leash to animate the current top window
|
||||
// TODO(b/220934562): Use surface animator to better manage animation conflicts.
|
||||
SurfaceControl animLeash = removedWindowContainer.makeAnimationLeash()
|
||||
.setName("BackPreview Leash for " + removedWindowContainer)
|
||||
.setHidden(false)
|
||||
.setBLASTLayer()
|
||||
.build();
|
||||
removedWindowContainer.reparentSurfaceControl(tx, animLeash);
|
||||
animationLeashParent = removedWindowContainer.getAnimationLeashParent();
|
||||
topAppTarget = createRemoteAnimationTargetLocked(removedWindowContainer,
|
||||
currentActivity,
|
||||
currentTask, animLeash);
|
||||
infoBuilder.setDepartingAnimationTarget(topAppTarget);
|
||||
}
|
||||
|
||||
//TODO(207481538) Remove once the infrastructure to support per-activity screenshot is
|
||||
// implemented. For now we simply have the mBackScreenshots hash map that dumbly
|
||||
// saves the screenshots.
|
||||
if (needsScreenshot(backType) && prevActivity != null
|
||||
&& prevActivity.mActivityComponent != null) {
|
||||
screenshotBuffer =
|
||||
getActivitySnapshot(currentTask, prevActivity.mActivityComponent);
|
||||
}
|
||||
|
||||
// Special handling for back to home animation
|
||||
if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && prepareAnimation
|
||||
&& prevTask != null) {
|
||||
currentTask.mBackGestureStarted = true;
|
||||
// Make launcher show from behind by marking its top activity as visible and
|
||||
// launch-behind to bump its visibility for the duration of the back gesture.
|
||||
prevActivity = prevTask.getTopNonFinishingActivity();
|
||||
if (prevActivity != null) {
|
||||
if (!prevActivity.mVisibleRequested) {
|
||||
prevActivity.setVisibility(true);
|
||||
}
|
||||
prevActivity.mLaunchTaskBehind = true;
|
||||
ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
|
||||
"Setting Activity.mLauncherTaskBehind to true. Activity=%s",
|
||||
prevActivity);
|
||||
prevActivity.mRootWindowContainer.ensureActivitiesVisible(
|
||||
null /* starting */, 0 /* configChanges */,
|
||||
false /* preserveWindows */);
|
||||
}
|
||||
infoBuilder.setDepartingWCT(toWindowContainerToken(currentTask));
|
||||
prepareAnimationIfNeeded(currentTask, prevTask, prevActivity,
|
||||
removedWindowContainer, backType, adapter);
|
||||
}
|
||||
infoBuilder.setPrepareRemoteAnimation(prepareAnimation);
|
||||
} // Release wm Lock
|
||||
|
||||
// Find a screenshot of the previous activity if we actually have an animation
|
||||
if (topAppTarget != null && needsScreenshot(backType) && prevTask != null
|
||||
&& screenshotBuffer == null) {
|
||||
SurfaceControl.Builder builder = new SurfaceControl.Builder()
|
||||
.setName("BackPreview Screenshot for " + prevActivity)
|
||||
.setParent(animationLeashParent)
|
||||
.setHidden(false)
|
||||
.setBLASTLayer();
|
||||
infoBuilder.setScreenshotSurface(builder.build());
|
||||
screenshotBuffer = getTaskSnapshot(prevTaskId, prevUserId);
|
||||
infoBuilder.setScreenshotBuffer(screenshotBuffer);
|
||||
|
||||
|
||||
// The Animation leash needs to be above the screenshot surface, but the animation leash
|
||||
// needs to be added before to be in the synchronized block.
|
||||
tx.setLayer(topAppTarget.leash, 1);
|
||||
}
|
||||
|
||||
WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer;
|
||||
if (finalRemovedWindowContainer != null) {
|
||||
try {
|
||||
currentActivity.token.linkToDeath(
|
||||
() -> resetSurfaces(finalRemovedWindowContainer), 0);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to link to death", e);
|
||||
resetSurfaces(removedWindowContainer);
|
||||
return null;
|
||||
}
|
||||
|
||||
int finalBackType = backType;
|
||||
final ActivityRecord finalprevActivity = prevActivity;
|
||||
final Task finalTask = currentTask;
|
||||
final int finalBackType = backType;
|
||||
final WindowState finalFocusedWindow = window;
|
||||
RemoteCallback onBackNavigationDone = new RemoteCallback(result -> onBackNavigationDone(
|
||||
result, finalFocusedWindow, finalRemovedWindowContainer, finalBackType,
|
||||
finalTask, finalprevActivity, prepareAnimation));
|
||||
result, finalFocusedWindow, finalBackType));
|
||||
infoBuilder.setOnBackNavigationDone(onBackNavigationDone);
|
||||
}
|
||||
|
||||
tx.apply();
|
||||
return infoBuilder.build();
|
||||
}
|
||||
|
||||
private static WindowContainerToken toWindowContainerToken(WindowContainer<?> windowContainer) {
|
||||
if (windowContainer == null || windowContainer.mRemoteToken == null) {
|
||||
return null;
|
||||
}
|
||||
return windowContainer.mRemoteToken.toWindowContainerToken();
|
||||
}
|
||||
|
||||
private void prepareAnimationIfNeeded(Task currentTask,
|
||||
Task prevTask, ActivityRecord prevActivity, WindowContainer<?> removedWindowContainer,
|
||||
int backType, BackAnimationAdapter adapter) {
|
||||
final ArrayList<SurfaceControl> leashes = new ArrayList<>();
|
||||
final SurfaceControl.Transaction startedTransaction = currentTask.getPendingTransaction();
|
||||
final SurfaceControl.Transaction finishedTransaction = new SurfaceControl.Transaction();
|
||||
// Prepare a leash to animate for the departing window
|
||||
final SurfaceControl animLeash = currentTask.makeAnimationLeash()
|
||||
.setName("BackPreview Leash for " + currentTask)
|
||||
.setHidden(false)
|
||||
.build();
|
||||
removedWindowContainer.reparentSurfaceControl(startedTransaction, animLeash);
|
||||
|
||||
final RemoteAnimationTarget topAppTarget = createRemoteAnimationTargetLocked(
|
||||
currentTask, animLeash, MODE_CLOSING);
|
||||
|
||||
// reset leash after animation finished.
|
||||
leashes.add(animLeash);
|
||||
removedWindowContainer.reparentSurfaceControl(finishedTransaction,
|
||||
removedWindowContainer.getParentSurfaceControl());
|
||||
|
||||
// Prepare a leash to animate for the entering window.
|
||||
RemoteAnimationTarget behindAppTarget = null;
|
||||
if (needsScreenshot(backType)) {
|
||||
HardwareBuffer screenshotBuffer = null;
|
||||
switch(backType) {
|
||||
case BackNavigationInfo.TYPE_CROSS_TASK:
|
||||
int prevTaskId = prevTask != null ? prevTask.mTaskId : 0;
|
||||
int prevUserId = prevTask != null ? prevTask.mUserId : 0;
|
||||
screenshotBuffer = getTaskSnapshot(prevTaskId, prevUserId);
|
||||
break;
|
||||
case BackNavigationInfo.TYPE_CROSS_ACTIVITY:
|
||||
//TODO(207481538) Remove once the infrastructure to support per-activity
|
||||
// screenshot is implemented. For now we simply have the mBackScreenshots hash
|
||||
// map that dumbly saves the screenshots.
|
||||
if (prevActivity != null
|
||||
&& prevActivity.mActivityComponent != null) {
|
||||
screenshotBuffer =
|
||||
getActivitySnapshot(currentTask, prevActivity.mActivityComponent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Find a screenshot of the previous activity if we actually have an animation
|
||||
SurfaceControl animationLeashParent = removedWindowContainer.getAnimationLeashParent();
|
||||
if (screenshotBuffer != null) {
|
||||
final SurfaceControl screenshotSurface = new SurfaceControl.Builder()
|
||||
.setName("BackPreview Screenshot for " + prevActivity)
|
||||
.setHidden(false)
|
||||
.setParent(animationLeashParent)
|
||||
.setBLASTLayer()
|
||||
.build();
|
||||
startedTransaction.setBuffer(screenshotSurface, screenshotBuffer);
|
||||
|
||||
// The Animation leash needs to be above the screenshot surface, but the animation
|
||||
// leash needs to be added before to be in the synchronized block.
|
||||
startedTransaction.setLayer(topAppTarget.leash, 1);
|
||||
|
||||
behindAppTarget = createRemoteAnimationTargetLocked(
|
||||
prevTask, screenshotSurface, MODE_OPENING);
|
||||
|
||||
// reset leash after animation finished.
|
||||
leashes.add(screenshotSurface);
|
||||
}
|
||||
} else if (prevTask != null) {
|
||||
if (!ENABLE_SHELL_TRANSITIONS) {
|
||||
// Special handling for preventing next transition.
|
||||
currentTask.mBackGestureStarted = true;
|
||||
}
|
||||
prevActivity = prevTask.getTopNonFinishingActivity();
|
||||
if (prevActivity != null) {
|
||||
// Make previous task show from behind by marking its top activity as visible
|
||||
// and launch-behind to bump its visibility for the duration of the back gesture.
|
||||
setLaunchBehind(prevActivity);
|
||||
|
||||
final SurfaceControl leash = prevActivity.makeAnimationLeash()
|
||||
.setName("BackPreview Leash for " + prevActivity)
|
||||
.setHidden(false)
|
||||
.build();
|
||||
prevActivity.reparentSurfaceControl(startedTransaction, leash);
|
||||
behindAppTarget = createRemoteAnimationTargetLocked(
|
||||
prevTask, leash, MODE_OPENING);
|
||||
|
||||
// reset leash after animation finished.
|
||||
leashes.add(leash);
|
||||
prevActivity.reparentSurfaceControl(finishedTransaction,
|
||||
prevActivity.getParentSurfaceControl());
|
||||
}
|
||||
}
|
||||
|
||||
if (mShowWallpaper) {
|
||||
currentTask.getDisplayContent().mWallpaperController.adjustWallpaperWindows();
|
||||
// TODO(b/241808055): If the current animation need to show wallpaper and animate the
|
||||
// wallpaper, start the wallpaper animation to collect wallpaper target and deliver it
|
||||
// to the back animation controller.
|
||||
}
|
||||
|
||||
final RemoteAnimationTarget[] targets = (behindAppTarget == null)
|
||||
? new RemoteAnimationTarget[] {topAppTarget}
|
||||
: new RemoteAnimationTarget[] {topAppTarget, behindAppTarget};
|
||||
|
||||
final ActivityRecord finalPrevActivity = prevActivity;
|
||||
final IBackAnimationFinishedCallback callback =
|
||||
new IBackAnimationFinishedCallback.Stub() {
|
||||
@Override
|
||||
public void onAnimationFinished(boolean triggerBack) {
|
||||
for (SurfaceControl sc: leashes) {
|
||||
finishedTransaction.remove(sc);
|
||||
}
|
||||
|
||||
synchronized (mWindowManagerService.mGlobalLock) {
|
||||
if (ENABLE_SHELL_TRANSITIONS) {
|
||||
if (!triggerBack) {
|
||||
if (!needsScreenshot(backType)) {
|
||||
restoreLaunchBehind(finalPrevActivity);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (triggerBack) {
|
||||
final SurfaceControl surfaceControl =
|
||||
removedWindowContainer.getSurfaceControl();
|
||||
if (surfaceControl != null && surfaceControl.isValid()) {
|
||||
// When going back to home, hide the task surface before it
|
||||
// re-parented to avoid flicker.
|
||||
finishedTransaction.hide(surfaceControl);
|
||||
}
|
||||
} else {
|
||||
currentTask.mBackGestureStarted = false;
|
||||
if (!needsScreenshot(backType)) {
|
||||
restoreLaunchBehind(finalPrevActivity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finishedTransaction.apply();
|
||||
}
|
||||
};
|
||||
|
||||
scheduleAnimationLocked(backType, targets, adapter, callback);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static RemoteAnimationTarget createRemoteAnimationTargetLocked(
|
||||
WindowContainer<?> removedWindowContainer,
|
||||
ActivityRecord activityRecord, Task task, SurfaceControl animLeash) {
|
||||
Task task, SurfaceControl animLeash, int mode) {
|
||||
ActivityRecord topApp = task.getTopRealVisibleActivity();
|
||||
if (topApp == null) {
|
||||
topApp = task.getTopNonFinishingActivity();
|
||||
}
|
||||
|
||||
final WindowState mainWindow = topApp != null
|
||||
? topApp.findMainWindow()
|
||||
: null;
|
||||
int windowType = INVALID_WINDOW_TYPE;
|
||||
if (mainWindow != null) {
|
||||
windowType = mainWindow.getWindowType();
|
||||
}
|
||||
|
||||
Rect bounds = new Rect(task.getBounds());
|
||||
Rect localBounds = new Rect(bounds);
|
||||
Point tmpPos = new Point();
|
||||
task.getRelativePosition(tmpPos);
|
||||
localBounds.offsetTo(tmpPos.x, tmpPos.y);
|
||||
|
||||
return new RemoteAnimationTarget(
|
||||
task.mTaskId,
|
||||
RemoteAnimationTarget.MODE_CLOSING,
|
||||
mode,
|
||||
animLeash,
|
||||
false /* isTransluscent */,
|
||||
new Rect() /* clipRect */,
|
||||
new Rect() /* contentInsets */,
|
||||
activityRecord.getPrefixOrderIndex(),
|
||||
new Point(0, 0) /* position */,
|
||||
new Rect() /* localBounds */,
|
||||
new Rect() /* screenSpaceBounds */,
|
||||
removedWindowContainer.getWindowConfiguration(),
|
||||
task.getPrefixOrderIndex(),
|
||||
tmpPos /* position */,
|
||||
localBounds /* localBounds */,
|
||||
bounds /* screenSpaceBounds */,
|
||||
task.getWindowConfiguration(),
|
||||
true /* isNotInRecent */,
|
||||
null,
|
||||
null,
|
||||
task.getTaskInfo(),
|
||||
false,
|
||||
activityRecord.windowType);
|
||||
windowType);
|
||||
}
|
||||
|
||||
private void onBackNavigationDone(
|
||||
Bundle result, WindowState focusedWindow, WindowContainer<?> windowContainer,
|
||||
int backType, @Nullable Task task, @Nullable ActivityRecord prevActivity,
|
||||
boolean prepareAnimation) {
|
||||
SurfaceControl surfaceControl = windowContainer.getSurfaceControl();
|
||||
@VisibleForTesting
|
||||
void scheduleAnimationLocked(@BackNavigationInfo.BackTargetType int type,
|
||||
RemoteAnimationTarget[] targets, BackAnimationAdapter backAnimationAdapter,
|
||||
IBackAnimationFinishedCallback callback) {
|
||||
mPendingAnimation = () -> {
|
||||
try {
|
||||
backAnimationAdapter.getRunner().onAnimationStart(type,
|
||||
targets, null, null, callback);
|
||||
} catch (RemoteException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
};
|
||||
mWindowManagerService.mWindowPlacerLocked.requestTraversal();
|
||||
}
|
||||
|
||||
void checkAnimationReady(WallpaperController wallpaperController) {
|
||||
if (!mBackAnimationInProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean wallpaperReady = !mShowWallpaper
|
||||
|| (wallpaperController.getWallpaperTarget() != null
|
||||
&& wallpaperController.wallpaperTransitionReady());
|
||||
if (wallpaperReady && mPendingAnimation != null) {
|
||||
startAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
void startAnimation() {
|
||||
if (mPendingAnimation != null) {
|
||||
mPendingAnimation.run();
|
||||
mPendingAnimation = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void onBackNavigationDone(Bundle result, WindowState focusedWindow, int backType) {
|
||||
boolean triggerBack = result != null && result.getBoolean(
|
||||
BackNavigationInfo.KEY_TRIGGER_BACK);
|
||||
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "onBackNavigationDone backType=%s, "
|
||||
+ "task=%s, prevActivity=%s", backType, task, prevActivity);
|
||||
|
||||
if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && prepareAnimation) {
|
||||
if (triggerBack) {
|
||||
if (surfaceControl != null && surfaceControl.isValid()) {
|
||||
// When going back to home, hide the task surface before it is re-parented to
|
||||
// avoid flicker.
|
||||
SurfaceControl.Transaction t = windowContainer.getSyncTransaction();
|
||||
t.hide(surfaceControl);
|
||||
t.apply();
|
||||
}
|
||||
}
|
||||
if (prevActivity != null && !triggerBack) {
|
||||
// Restore the launch-behind state.
|
||||
task.mTaskSupervisor.scheduleLaunchTaskBehindComplete(prevActivity.token);
|
||||
prevActivity.mLaunchTaskBehind = false;
|
||||
ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
|
||||
"Setting Activity.mLauncherTaskBehind to false. Activity=%s",
|
||||
prevActivity);
|
||||
}
|
||||
} else if (task != null) {
|
||||
task.mBackGestureStarted = false;
|
||||
}
|
||||
resetSurfaces(windowContainer);
|
||||
+ "triggerBack=%b", backType, triggerBack);
|
||||
|
||||
if (mFocusObserver != null) {
|
||||
focusedWindow.unregisterFocusObserver(mFocusObserver);
|
||||
mFocusObserver = null;
|
||||
}
|
||||
mBackAnimationInProgress = false;
|
||||
mShowWallpaper = false;
|
||||
}
|
||||
|
||||
private HardwareBuffer getActivitySnapshot(@NonNull Task task,
|
||||
@@ -450,20 +563,54 @@ class BackNavigationController {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void resetSurfaces(@NonNull WindowContainer<?> windowContainer) {
|
||||
synchronized (windowContainer.mWmService.mGlobalLock) {
|
||||
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Back: Reset surfaces");
|
||||
SurfaceControl.Transaction tx = windowContainer.getSyncTransaction();
|
||||
SurfaceControl surfaceControl = windowContainer.getSurfaceControl();
|
||||
if (surfaceControl != null) {
|
||||
tx.reparent(surfaceControl,
|
||||
windowContainer.getParent().getSurfaceControl());
|
||||
tx.apply();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setWindowManager(WindowManagerService wm) {
|
||||
mWindowManagerService = wm;
|
||||
}
|
||||
|
||||
private void setLaunchBehind(ActivityRecord activity) {
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
if (!activity.mVisibleRequested) {
|
||||
activity.setVisibility(true);
|
||||
}
|
||||
activity.mLaunchTaskBehind = true;
|
||||
|
||||
// Handle fixed rotation launching app.
|
||||
final DisplayContent dc = activity.mDisplayContent;
|
||||
dc.rotateInDifferentOrientationIfNeeded(activity);
|
||||
if (activity.hasFixedRotationTransform()) {
|
||||
// Set the record so we can recognize it to continue to update display orientation
|
||||
// if the previous activity becomes the top later.
|
||||
dc.setFixedRotationLaunchingApp(activity,
|
||||
activity.getWindowConfiguration().getRotation());
|
||||
}
|
||||
|
||||
ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
|
||||
"Setting Activity.mLauncherTaskBehind to true. Activity=%s", activity);
|
||||
activity.getDisplayContent().ensureActivitiesVisible(null /* starting */,
|
||||
0 /* configChanges */, false /* preserveWindows */, true);
|
||||
}
|
||||
|
||||
private void restoreLaunchBehind(ActivityRecord activity) {
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
activity.mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp();
|
||||
|
||||
// Restore the launch-behind state.
|
||||
activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token);
|
||||
activity.mLaunchTaskBehind = false;
|
||||
ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
|
||||
"Setting Activity.mLauncherTaskBehind to false. Activity=%s",
|
||||
activity);
|
||||
}
|
||||
|
||||
boolean isWallpaperVisible(WindowState w) {
|
||||
if (mBackAnimationInProgress && w.isFocused()) {
|
||||
return mShowWallpaper;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -837,6 +837,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
|
||||
if (recentsAnimationController != null) {
|
||||
recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController);
|
||||
}
|
||||
final BackNavigationController backNavigationController =
|
||||
mWmService.mAtmService.mBackNavigationController;
|
||||
if (backNavigationController != null) {
|
||||
backNavigationController.checkAnimationReady(defaultDisplay.mWallpaperController);
|
||||
}
|
||||
|
||||
for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
|
||||
final DisplayContent displayContent = mChildren.get(displayNdx);
|
||||
|
||||
@@ -197,7 +197,7 @@ class WallpaperController {
|
||||
&& animatingContainer.getAnimation() != null
|
||||
&& animatingContainer.getAnimation().getShowWallpaper();
|
||||
final boolean hasWallpaper = w.hasWallpaper() || animationWallpaper;
|
||||
if (isRecentsTransitionTarget(w)) {
|
||||
if (isRecentsTransitionTarget(w) || isBackNavigationTarget(w)) {
|
||||
if (DEBUG_WALLPAPER) Slog.v(TAG, "Found recents animation wallpaper target: " + w);
|
||||
mFindResults.setWallpaperTarget(w);
|
||||
return true;
|
||||
@@ -237,6 +237,12 @@ class WallpaperController {
|
||||
return controller != null && controller.isWallpaperVisible(w);
|
||||
}
|
||||
|
||||
private boolean isBackNavigationTarget(WindowState w) {
|
||||
// The window is in animating by back navigation and set to show wallpaper.
|
||||
final BackNavigationController controller = mService.mAtmService.mBackNavigationController;
|
||||
return controller != null && controller.isWallpaperVisible(w);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #computeLastWallpaperZoomOut()
|
||||
*/
|
||||
@@ -822,6 +828,12 @@ class WallpaperController {
|
||||
if (mService.getRecentsAnimationController() != null) {
|
||||
mService.getRecentsAnimationController().startAnimation();
|
||||
}
|
||||
|
||||
// If there was a pending back navigation animation that would show wallpaper, start
|
||||
// the animation due to it was skipped in previous surface placement.
|
||||
if (mService.mAtmService.mBackNavigationController != null) {
|
||||
mService.mAtmService.mBackNavigationController.startAnimation();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -32,6 +32,7 @@ import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
@@ -40,6 +41,7 @@ import android.hardware.HardwareBuffer;
|
||||
import android.os.RemoteException;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.view.WindowManager;
|
||||
import android.window.BackAnimationAdapter;
|
||||
import android.window.BackEvent;
|
||||
import android.window.BackNavigationInfo;
|
||||
import android.window.IOnBackInvokedCallback;
|
||||
@@ -54,6 +56,7 @@ import com.android.server.LocalServices;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -61,17 +64,18 @@ import java.util.concurrent.TimeUnit;
|
||||
@Presubmit
|
||||
@RunWith(WindowTestRunner.class)
|
||||
public class BackNavigationControllerTests extends WindowTestsBase {
|
||||
|
||||
private BackNavigationController mBackNavigationController;
|
||||
private WindowManagerInternal mWindowManagerInternal;
|
||||
private BackAnimationAdapter mBackAnimationAdapter;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mBackNavigationController = new BackNavigationController();
|
||||
mBackNavigationController = Mockito.spy(new BackNavigationController());
|
||||
LocalServices.removeServiceForTest(WindowManagerInternal.class);
|
||||
mWindowManagerInternal = mock(WindowManagerInternal.class);
|
||||
LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal);
|
||||
mBackNavigationController.setWindowManager(mWm);
|
||||
mBackAnimationAdapter = mock(BackAnimationAdapter.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -79,14 +83,16 @@ public class BackNavigationControllerTests extends WindowTestsBase {
|
||||
Task task = createTopTaskWithActivity();
|
||||
IOnBackInvokedCallback callback = withSystemCallback(task);
|
||||
|
||||
BackNavigationInfo backNavigationInfo =
|
||||
mBackNavigationController.startBackNavigation(true, null);
|
||||
BackNavigationInfo backNavigationInfo = startBackNavigation();
|
||||
assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull();
|
||||
assertThat(backNavigationInfo.getDepartingAnimationTarget()).isNotNull();
|
||||
assertThat(backNavigationInfo.getTaskWindowConfiguration()).isNotNull();
|
||||
assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback);
|
||||
assertThat(typeToString(backNavigationInfo.getType()))
|
||||
.isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME));
|
||||
|
||||
// verify if back animation would start.
|
||||
verify(mBackNavigationController).scheduleAnimationLocked(
|
||||
eq(BackNavigationInfo.TYPE_RETURN_TO_HOME), any(), eq(mBackAnimationAdapter),
|
||||
any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -114,10 +120,6 @@ public class BackNavigationControllerTests extends WindowTestsBase {
|
||||
.isEqualTo(typeToString(BackNavigationInfo.TYPE_CROSS_ACTIVITY));
|
||||
assertWithMessage("Activity callback").that(
|
||||
backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback);
|
||||
|
||||
// Until b/207481538 is implemented, this should be null
|
||||
assertThat(backNavigationInfo.getScreenshotSurface()).isNull();
|
||||
assertThat(backNavigationInfo.getScreenshotHardwareBuffer()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -233,7 +235,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
|
||||
|
||||
@Nullable
|
||||
private BackNavigationInfo startBackNavigation() {
|
||||
return mBackNavigationController.startBackNavigation(true, null);
|
||||
return mBackNavigationController.startBackNavigation(null, mBackAnimationAdapter);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -287,6 +289,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
|
||||
PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
|
||||
WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, record, "window");
|
||||
when(record.mSurfaceControl.isValid()).thenReturn(true);
|
||||
Mockito.doNothing().when(task).reparentSurfaceControl(any(), any());
|
||||
mAtm.setFocusedTask(task.mTaskId, record);
|
||||
addToWindowMap(window, true);
|
||||
return task;
|
||||
|
||||
Reference in New Issue
Block a user