本文介绍安卓动画系统中的过渡动画流程。
过渡动画简介
一般过渡动画有在system_server进程中执行的本地动画和在例如systemui等进程执行的远程动画两种情况。
常见的本地动画:App内切换Activity等。
常见的远程动画:从桌面进入App,从App回到桌面,从Notification悬浮窗进入Activity等。
本地动画
从窗口布局流程中RootWindowContainer的performSurfacePlacement方法开始:
void performSurfacePlacement() {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performSurfacePlacement");
try {
performSurfacePlacementNoTrace();
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
// "Something has changed! Let's make it correct now."
// TODO: Super crazy long method that should be broken down...
void performSurfacePlacementNoTrace() {
if (DEBUG_WINDOW_TRACE) Slog.v(TAG, "performSurfacePlacementInner: entry. Called by "
+ Debug.getCallers(3));
int i;
if (mWmService.mFocusMayChange) {
mWmService.mFocusMayChange = false;
mWmService.updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
}
// Initialize state of exiting tokens.
final int numDisplays = mChildren.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
displayContent.setExitingTokensHasVisible(false);
}
mHoldScreen = null;
mScreenBrightnessOverride = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mUserActivityTimeout = -1;
mObscureApplicationContentOnSecondaryDisplays = false;
mSustainedPerformanceModeCurrent = false;
mWmService.mTransactionSequence++;
// TODO(multi-display): recents animation & wallpaper need support multi-display.
final DisplayContent defaultDisplay = mWmService.getDefaultDisplayContentLocked();
final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges");
mWmService.openSurfaceTransaction();
try {
applySurfaceChangesTransaction();
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
"<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
}
mWmService.mAnimator.executeAfterPrepareSurfacesRunnables();
checkAppTransitionReady(surfacePlacer);
// Defer starting the recents animation until the wallpaper has drawn
final RecentsAnimationController recentsAnimationController =
mWmService.getRecentsAnimationController();
if (recentsAnimationController != null) {
recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController);
}
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
if (displayContent.mWallpaperMayChange) {
if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change! Adjusting");
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
if (DEBUG_LAYOUT_REPEATS) {
surfacePlacer.debugLayoutRepeats("WallpaperMayChange",
displayContent.pendingLayoutChanges);
}
}
}
if (mWmService.mFocusMayChange) {
mWmService.mFocusMayChange = false;
mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
false /*updateInputWindows*/);
}
if (isLayoutNeeded()) {
defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("mLayoutNeeded",
defaultDisplay.pendingLayoutChanges);
}
handleResizingWindows();
if (mWmService.mDisplayFrozen) {
ProtoLog.v(WM_DEBUG_ORIENTATION,
"With display frozen, orientationChangeComplete=%b",
mOrientationChangeComplete);
}
if (mOrientationChangeComplete) {
if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
mWmService.mLastFinishedFreezeSource = mLastWindowFreezeSource;
mWmService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);
}
mWmService.stopFreezingDisplayLocked();
}
// Destroy the surface of any windows that are no longer visible.
i = mWmService.mDestroySurface.size();
if (i > 0) {
do {
i--;
WindowState win = mWmService.mDestroySurface.get(i);
win.mDestroying = false;
final DisplayContent displayContent = win.getDisplayContent();
if (displayContent.mInputMethodWindow == win) {
displayContent.setInputMethodWindowLocked(null);
}
if (displayContent.mWallpaperController.isWallpaperTarget(win)) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
win.destroySurfaceUnchecked();
win.mWinAnimator.destroyPreservedSurfaceLocked();
} while (i > 0);
mWmService.mDestroySurface.clear();
}
// Time to remove any exiting tokens?
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
displayContent.removeExistingTokensIfPossible();
}
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
if (displayContent.pendingLayoutChanges != 0) {
displayContent.setLayoutNeeded();
}
}
mWmService.setHoldScreenLocked(mHoldScreen);
if (!mWmService.mDisplayFrozen) {
final float brightnessOverride = mScreenBrightnessOverride < PowerManager.BRIGHTNESS_MIN
|| mScreenBrightnessOverride > PowerManager.BRIGHTNESS_MAX
? PowerManager.BRIGHTNESS_INVALID_FLOAT : mScreenBrightnessOverride;
int brightnessFloatAsIntBits = Float.floatToIntBits(brightnessOverride);
// Post these on a handler such that we don't call into power manager service while
// holding the window manager lock to avoid lock contention with power manager lock.
mHandler.obtainMessage(SET_SCREEN_BRIGHTNESS_OVERRIDE, brightnessFloatAsIntBits,
0).sendToTarget();
mHandler.obtainMessage(SET_USER_ACTIVITY_TIMEOUT, mUserActivityTimeout).sendToTarget();
}
if (mSustainedPerformanceModeCurrent != mSustainedPerformanceModeEnabled) {
mSustainedPerformanceModeEnabled = mSustainedPerformanceModeCurrent;
mWmService.mPowerManagerInternal.powerHint(
PowerHint.SUSTAINED_PERFORMANCE,
(mSustainedPerformanceModeEnabled ? 1 : 0));
}
if (mUpdateRotation) {
ProtoLog.d(WM_DEBUG_ORIENTATION, "Performing post-rotate rotation");
mUpdateRotation = updateRotationUnchecked();
}
if (!mWmService.mWaitingForDrawnCallbacks.isEmpty()
|| (mOrientationChangeComplete && !isLayoutNeeded()
&& !mUpdateRotation)) {
mWmService.checkDrawnWindowsLocked();
}
final int N = mWmService.mPendingRemove.size();
if (N > 0) {
if (mWmService.mPendingRemoveTmp.length < N) {
mWmService.mPendingRemoveTmp = new WindowState[N + 10];
}
mWmService.mPendingRemove.toArray(mWmService.mPendingRemoveTmp);
mWmService.mPendingRemove.clear();
ArrayList<DisplayContent> displayList = new ArrayList();
for (i = 0; i < N; i++) {
final WindowState w = mWmService.mPendingRemoveTmp[i];
w.removeImmediately();
final DisplayContent displayContent = w.getDisplayContent();
if (displayContent != null && !displayList.contains(displayContent)) {
displayList.add(displayContent);
}
}
for (int j = displayList.size() - 1; j >= 0; --j) {
final DisplayContent dc = displayList.get(j);
dc.assignWindowLayers(true /*setLayoutNeeded*/);
}
}
forAllDisplays(dc -> {
dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
dc.updateSystemGestureExclusion();
dc.updateTouchExcludeRegion();
});
// Check to see if we are now in a state where the screen should
// be enabled, because the window obscured flags have changed.
mWmService.enableScreenIfNeededLocked();
mWmService.scheduleAnimationLocked();
// Send any pending task-info changes that were queued-up during a layout deferment
mWmService.mAtmService.mTaskOrganizerController.dispatchPendingTaskInfoChanges();
if (DEBUG_WINDOW_TRACE) Slog.e(TAG, "performSurfacePlacementInner exit");
}
调用了RootWindowContainer的checkAppTransitionReady方法:
private void checkAppTransitionReady(WindowSurfacePlacer surfacePlacer) {
// Trace all displays app transition by Z-order for pending layout change.
for (int i = mChildren.size() - 1; i >= 0; --i) {
final DisplayContent curDisplay = mChildren.get(i);
// If we are ready to perform an app transition, check through all of the app tokens
// to be shown and see if they are ready to go.
if (curDisplay.mAppTransition.isReady()) {
// handleAppTransitionReady may modify curDisplay.pendingLayoutChanges.
curDisplay.mAppTransitionController.handleAppTransitionReady();
if (DEBUG_LAYOUT_REPEATS) {
surfacePlacer.debugLayoutRepeats("after handleAppTransitionReady",
curDisplay.pendingLayoutChanges);
}
}
if (curDisplay.mAppTransition.isRunning() && !curDisplay.isAppTransitioning()) {
// We have finished the animation of an app transition. To do this, we have
// delayed a lot of operations like showing and hiding apps, moving apps in
// Z-order, etc.
// The app token list reflects the correct Z-order, but the window list may now
// be out of sync with it. So here we will just rebuild the entire app window
// list. Fun!
curDisplay.handleAnimatingStoppedAndTransition();
if (DEBUG_LAYOUT_REPEATS) {
surfacePlacer.debugLayoutRepeats("after handleAnimStopAndXitionLock",
curDisplay.pendingLayoutChanges);
}
}
}
}
调用了AppTransitionController的handleAppTransitionReady方法:
/**
* Handle application transition for given display.
*/
void handleAppTransitionReady() {
mTempTransitionReasons.clear();
if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons)
|| !transitionGoodToGo(mDisplayContent.mChangingContainers,
mTempTransitionReasons)) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "**** GOOD TO GO");
final AppTransition appTransition = mDisplayContent.mAppTransition;
int transit = appTransition.getAppTransition();
if (mDisplayContent.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) {
transit = WindowManager.TRANSIT_UNSET;
}
mDisplayContent.mSkipAppTransitionAnimation = false;
mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear();
appTransition.removeAppTransitionTimeoutCallbacks();
mDisplayContent.mWallpaperMayChange = false;
int appCount = mDisplayContent.mOpeningApps.size();
for (int i = 0; i < appCount; ++i) {
// Clearing the mAnimatingExit flag before entering animation. It's set to true if app
// window is removed, or window relayout to invisible. This also affects window
// visibility. We need to clear it *before* maybeUpdateTransitToWallpaper() as the
// transition selection depends on wallpaper target visibility.
mDisplayContent.mOpeningApps.valueAtUnchecked(i).clearAnimatingFlags();
}
appCount = mDisplayContent.mChangingContainers.size();
for (int i = 0; i < appCount; ++i) {
// Clearing for same reason as above.
final ActivityRecord activity = getAppFromContainer(
mDisplayContent.mChangingContainers.valueAtUnchecked(i));
if (activity != null) {
activity.clearAnimatingFlags();
}
}
// Adjust wallpaper before we pull the lower/upper target, since pending changes
// (like the clearAnimatingFlags() above) might affect wallpaper target result.
// Or, the opening app window should be a wallpaper target.
mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(
mDisplayContent.mOpeningApps);
// Determine if closing and opening app token sets are wallpaper targets, in which case
// special animations are needed.
final boolean hasWallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget() != null;
final boolean openingAppHasWallpaper = canBeWallpaperTarget(mDisplayContent.mOpeningApps)
&& hasWallpaperTarget;
final boolean closingAppHasWallpaper = canBeWallpaperTarget(mDisplayContent.mClosingApps)
&& hasWallpaperTarget;
transit = maybeUpdateTransitToTranslucentAnim(transit);
transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper,
closingAppHasWallpaper);
// Find the layout params of the top-most application window in the tokens, which is
// what will control the animation theme. If all closing windows are obscured, then there is
// no need to do an animation. This is the case, for example, when this transition is being
// done behind a dream window.
final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps,
mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers);
final ActivityRecord animLpActivity = findAnimLayoutParamsToken(transit, activityTypes);
final ActivityRecord topOpeningApp =
getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */);
final ActivityRecord topClosingApp =
getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */);
final ActivityRecord topChangingApp =
getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */);
final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mOpeningApps)
|| containsVoiceInteraction(mDisplayContent.mOpeningApps);
final int layoutRedo;
mService.mSurfaceAnimationRunner.deferStartingAnimations();
try {
applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit,
animLp, voiceInteraction);
handleClosingApps();
handleOpeningApps();
handleChangingApps(transit);
appTransition.setLastAppTransition(transit, topOpeningApp,
topClosingApp, topChangingApp);
final int flags = appTransition.getTransitFlags();
layoutRedo = appTransition.goodToGo(transit, topOpeningApp,
mDisplayContent.mOpeningApps);
handleNonAppWindowsInTransition(transit, flags);
appTransition.postAnimationCallback();
appTransition.clear();
} finally {
mService.mSurfaceAnimationRunner.continueStartingAnimations();
}
mService.mTaskSnapshotController.onTransitionStarting(mDisplayContent);
mDisplayContent.mOpeningApps.clear();
mDisplayContent.mClosingApps.clear();
mDisplayContent.mChangingContainers.clear();
mDisplayContent.mUnknownAppVisibilityController.clear();
// This has changed the visibility of windows, so perform
// a new layout to get them all up-to-date.
mDisplayContent.setLayoutNeeded();
mDisplayContent.computeImeTarget(true /* updateImeTarget */);
mService.mAtmService.mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
mTempTransitionReasons);
if (transit == TRANSIT_SHOW_SINGLE_TASK_DISPLAY) {
mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
mService.mAtmInternal.notifySingleTaskDisplayDrawn(mDisplayContent.getDisplayId());
});
}
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
mDisplayContent.pendingLayoutChanges |=
layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG;
}
其中调用了AppTransitionController的applyAnimations方法:
/**
* Apply an app transition animation based on a set of {@link ActivityRecord}
*
* @param openingApps The list of opening apps to which an app transition animation applies.
* @param closingApps The list of closing apps to which an app transition animation applies.
* @param transit The current transition type.
* @param animLp Layout parameters in which an app transition animation runs.
* @param voiceInteraction {@code true} if one of the apps in this transition belongs to a voice
* interaction session driving task.
*/
private void applyAnimations(ArraySet<ActivityRecord> openingApps,
ArraySet<ActivityRecord> closingApps, @TransitionType int transit,
LayoutParams animLp, boolean voiceInteraction) {
if (transit == WindowManager.TRANSIT_UNSET
|| (openingApps.isEmpty() && closingApps.isEmpty())) {
return;
}
final ArraySet<WindowContainer> openingWcs = getAnimationTargets(
openingApps, closingApps, true /* visible */);
final ArraySet<WindowContainer> closingWcs = getAnimationTargets(
openingApps, closingApps, false /* visible */);
applyAnimations(openingWcs, openingApps, transit, true /* visible */, animLp,
voiceInteraction);
applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,
voiceInteraction);
final AccessibilityController accessibilityController =
mDisplayContent.mWmService.mAccessibilityController;
if (accessibilityController != null) {
accessibilityController.onAppWindowTransitionLocked(
mDisplayContent.getDisplayId(), transit);
}
}
/**
* Apply animation to the set of window containers.
*
* @param wcs The list of {@link WindowContainer}s to which an app transition animation applies.
* @param apps The list of {@link ActivityRecord}s being transitioning.
* @param transit The current transition type.
* @param visible {@code true} if the apps becomes visible, {@code false} if the apps becomes
* invisible.
* @param animLp Layout parameters in which an app transition animation runs.
* @param voiceInteraction {@code true} if one of the apps in this transition belongs to a voice
* interaction session driving task.
*/
private void applyAnimations(ArraySet<WindowContainer> wcs, ArraySet<ActivityRecord> apps,
@TransitionType int transit, boolean visible, LayoutParams animLp,
boolean voiceInteraction) {
final int wcsCount = wcs.size();
for (int i = 0; i < wcsCount; i++) {
final WindowContainer wc = wcs.valueAt(i);
// If app transition animation target is promoted to higher level, SurfaceAnimator
// triggers WC#onAnimationFinished only on the promoted target. So we need to take care
// of triggering AR#onAnimationFinished on each ActivityRecord which is a part of the
// app transition.
final ArrayList<ActivityRecord> transitioningDescendants = new ArrayList<>();
for (int j = 0; j < apps.size(); ++j) {
final ActivityRecord app = apps.valueAt(j);
if (app.isDescendantOf(wc)) {
transitioningDescendants.add(app);
}
}
wc.applyAnimation(animLp, transit, visible, voiceInteraction, transitioningDescendants);
}
}
调用了WindowContainer的applyAnimation方法:
/**
* Applies the app transition animation according the given the layout properties in the
* window hierarchy.
*
* @param lp The layout parameters of the window.
* @param transit The app transition type indicates what kind of transition to be applied.
* @param enter Whether the app transition is entering transition or not.
* @param isVoiceInteraction Whether the container is participating in voice interaction or not.
* @param sources {@link ActivityRecord}s which causes this app transition animation.
*
* @return {@code true} when the container applied the app transition, {@code false} if the
* app transition is disabled or skipped.
*
* @see #getAnimationAdapter
*/
boolean applyAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
boolean isVoiceInteraction, @Nullable ArrayList<WindowContainer> sources) {
if (mWmService.mDisableTransitionAnimation) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: transition animation is disabled or skipped. "
+ "container=%s", this);
cancelAnimation();
return false;
}
// Only apply an animation if the display isn't frozen. If it is frozen, there is no reason
// to animate and it can cause strange artifacts when we unfreeze the display if some
// different animation is running.
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WC#applyAnimation");
if (okToAnimate()) {
applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
} else {
cancelAnimation();
}
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
return isAnimating();
}
调用了WindowContainer的applyAnimationUnchecked方法:
protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter,
int transit, boolean isVoiceInteraction,
@Nullable ArrayList<WindowContainer> sources) {
final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,
transit, enter, isVoiceInteraction);
AnimationAdapter adapter = adapters.first;
AnimationAdapter thumbnailAdapter = adapters.second;
if (adapter != null) {
if (sources != null) {
mSurfaceAnimationSources.addAll(sources);
}
startAnimation(getPendingTransaction(), adapter, !isVisible(),
ANIMATION_TYPE_APP_TRANSITION);
if (adapter.getShowWallpaper()) {
getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
if (thumbnailAdapter != null) {
mSurfaceFreezer.mSnapshot.startAnimation(getPendingTransaction(),
thumbnailAdapter, ANIMATION_TYPE_APP_TRANSITION, (type, anim) -> { });
}
}
}
首先调用了WindowContainer的getAnimationAdapter方法:
/**
* Gets the {@link AnimationAdapter} according the given window layout properties in the window
* hierarchy.
*
* @return The return value will always contain two elements, one for normal animations and the
* other for thumbnail animation, both can be {@code null}.
*
* @See com.android.server.wm.RemoteAnimationController.RemoteAnimationRecord
* @See LocalAnimationAdapter
*/
Pair<AnimationAdapter, AnimationAdapter> getAnimationAdapter(WindowManager.LayoutParams lp,
int transit, boolean enter, boolean isVoiceInteraction) {
final Pair<AnimationAdapter, AnimationAdapter> resultAdapters;
......
if (controller != null && !mSurfaceAnimator.isAnimationStartDelayed()) {
......
} else if (isChanging) {
......
} else {
mNeedsAnimationBoundsLayer = (appStackClipMode == STACK_CLIP_AFTER_ANIM);
final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
if (a != null) {
// Only apply corner radius to animation if we're not in multi window mode.
// We don't want rounded corners when in pip or split screen.
final float windowCornerRadius = !inMultiWindowMode()
? getDisplayContent().getWindowCornerRadius()
: 0;
AnimationAdapter adapter = new LocalAnimationAdapter(
new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
getDisplayContent().mAppTransition.canSkipFirstFrame(),
appStackClipMode, true /* isAppAnimation */, windowCornerRadius),
getSurfaceAnimationRunner());
resultAdapters = new Pair<>(adapter, null);
mNeedsZBoost = a.getZAdjustment() == Animation.ZORDER_TOP
|| AppTransition.isClosingTransit(transit);
mTransit = transit;
mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
} else {
resultAdapters = new Pair<>(null, null);
}
}
return resultAdapters;
}
创建了LocalAnimationAdapter作为AnimationAdapter。
WindowContainer的applyAnimationUnchecked后面还调用了WindowContainer的startAnimation方法:
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type) {
startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */);
}
/**
* Starts an animation on the container.
*
* @param anim The animation to run.
* @param hidden Whether our container is currently hidden. TODO This should use isVisible at
* some point but the meaning is too weird to work for all containers.
* @param type The type of animation defined as {@link AnimationType}.
* @param animationFinishedCallback The callback being triggered when the animation finishes.
*/
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback) {
if (DEBUG_ANIM) {
Slog.v(TAG, "Starting animation on " + this + ": type=" + type + ", anim=" + anim);
}
// TODO: This should use isVisible() but because isVisible has a really weird meaning at
// the moment this doesn't work for all animatable window containers.
mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,
mSurfaceFreezer);
}
最后在SurfaceAnimator的startAnimation方法中创建Leash并调用Animatable(WindowContainer)的onAnimationLeashCreated方法和AnimationAdapter(LocalAnimationAdapter)的startAnimation方法来开始执行动画。
远程动画
以在桌面点击悬浮窗形式的Notification打开Activity为例,整个动画流程是这样的:
1 在systemui进程中接收点击事件,创建RemoteAnimationAdapter
从StatusBarNotificationActivityStarter的onNotificationClicked方法开始:
/**
* Called when a notification is clicked.
*
* @param sbn notification that was clicked
* @param row row for that notification
*/
@Override
public void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row) {
......
ActivityStarter.OnDismissAction postKeyguardAction =
() -> handleNotificationClickAfterKeyguardDismissed(
sbn, row, controller, intent,
isActivityIntent, wasOccluded, showOverLockscreen);
if (showOverLockscreen) {
mIsCollapsingToShowActivityOverLockscreen = true;
postKeyguardAction.onDismiss();
} else {
mActivityStarter.dismissKeyguardThenExecute(
postKeyguardAction, null /* cancel */, afterKeyguardGone);
}
}
在Keyguard消失后会调用StatusBarNotificationActivityStarter的handleNotificationClickAfterKeyguardDismissed方法:
private boolean handleNotificationClickAfterKeyguardDismissed(
StatusBarNotification sbn,
ExpandableNotificationRow row,
RemoteInputController controller,
PendingIntent intent,
boolean isActivityIntent,
boolean wasOccluded,
boolean showOverLockscreen) {
......
final Runnable runnable = () -> handleNotificationClickAfterPanelCollapsed(
sbn, row, controller, intent,
isActivityIntent, wasOccluded, parentToCancelFinal);
if (showOverLockscreen) {
mShadeController.addPostCollapseAction(runnable);
mShadeController.collapsePanel(true /* animate */);
} else if (mKeyguardStateController.isShowing()
&& mStatusBar.isOccluded()) {
mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
mShadeController.collapsePanel();
} else {
mBackgroundHandler.postAtFrontOfQueue(runnable);
}
return !mNotificationPanel.isFullyCollapsed();
}
最终会调用StatusBarNotificationActivityStarter的handleNotificationClickAfterPanelCollapsed方法:
private void handleNotificationClickAfterPanelCollapsed(
StatusBarNotification sbn,
ExpandableNotificationRow row,
RemoteInputController controller,
PendingIntent intent,
boolean isActivityIntent,
boolean wasOccluded,
NotificationEntry parentToCancelFinal) {
......
startNotificationIntent(
intent, fillInIntent, entry, row, wasOccluded, isActivityIntent);
......
}
调用了StatusBarNotificationActivityStarter的startNotificationIntent方法:
private void startNotificationIntent(
PendingIntent intent,
Intent fillInIntent,
NotificationEntry entry,
View row,
boolean wasOccluded,
boolean isActivityIntent) {
RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation(row,
wasOccluded);
mLogger.logStartNotificationIntent(entry.getKey(), intent);
try {
if (adapter != null) {
ActivityTaskManager.getService()
.registerRemoteAnimationForNextActivityStart(
intent.getCreatorPackage(), adapter);
}
int launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
null, null, getActivityOptions(adapter));
mMainThreadHandler.post(() -> {
mActivityLaunchAnimator.setLaunchResult(launchResult, isActivityIntent);
});
} catch (RemoteException | PendingIntent.CanceledException e) {
// the stack trace isn't very helpful here.
// Just log the exception message.
mLogger.logSendingIntentFailed(e);
// TODO: Dismiss Keyguard.
}
}
调用了ActivityLaunchAnimator的getLaunchAnimation方法:
public RemoteAnimationAdapter getLaunchAnimation(
View sourceView, boolean occluded) {
if (!(sourceView instanceof ExpandableNotificationRow)
|| !mCallback.areLaunchAnimationsEnabled() || occluded) {
return null;
}
AnimationRunner animationRunner = new AnimationRunner(
(ExpandableNotificationRow) sourceView);
return new RemoteAnimationAdapter(animationRunner, ANIMATION_DURATION,
ANIMATION_DURATION - 150 /* statusBarTransitionDelay */);
}
其中AnimationRunner是ActivityLaunchAnimator的内部类,其继承了IRemoteAnimationRunner.Stub:
class AnimationRunner extends IRemoteAnimationRunner.Stub {
private final ExpandableNotificationRow mSourceNotification;
private final ExpandAnimationParameters mParams;
private final Rect mWindowCrop = new Rect();
private final float mNotificationCornerRadius;
private float mCornerRadius;
private boolean mIsFullScreenLaunch = true;
private final SyncRtSurfaceTransactionApplier mSyncRtTransactionApplier;
public AnimationRunner(ExpandableNotificationRow sourceNofitication) {
mSourceNotification = sourceNofitication;
mParams = new ExpandAnimationParameters();
mSyncRtTransactionApplier = new SyncRtSurfaceTransactionApplier(mSourceNotification);
mNotificationCornerRadius = Math.max(mSourceNotification.getCurrentTopRoundness(),
mSourceNotification.getCurrentBottomRoundness());
}
@Override
public void onAnimationStart(RemoteAnimationTarget[] remoteAnimationTargets,
RemoteAnimationTarget[] remoteAnimationWallpaperTargets,
IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback)
throws RemoteException {
mMainExecutor.execute(() -> {
RemoteAnimationTarget primary = getPrimaryRemoteAnimationTarget(
remoteAnimationTargets);
if (primary == null) {
setAnimationPending(false);
invokeCallback(iRemoteAnimationFinishedCallback);
mNotificationPanel.collapse(false /* delayed */, 1.0f /* speedUpFactor */);
return;
}
setExpandAnimationRunning(true);
mIsFullScreenLaunch = primary.position.y == 0
&& primary.sourceContainerBounds.height()
>= mNotificationPanel.getHeight();
if (!mIsFullScreenLaunch) {
mNotificationPanel.collapseWithDuration(ANIMATION_DURATION);
}
ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
mParams.startPosition = mSourceNotification.getLocationOnScreen();
mParams.startTranslationZ = mSourceNotification.getTranslationZ();
mParams.startClipTopAmount = mSourceNotification.getClipTopAmount();
if (mSourceNotification.isChildInGroup()) {
int parentClip = mSourceNotification
.getNotificationParent().getClipTopAmount();
mParams.parentStartClipTopAmount = parentClip;
// We need to calculate how much the child is clipped by the parent
// because children always have 0 clipTopAmount
if (parentClip != 0) {
float childClip = parentClip
- mSourceNotification.getTranslationY();
if (childClip > 0.0f) {
mParams.startClipTopAmount = (int) Math.ceil(childClip);
}
}
}
int targetWidth = primary.sourceContainerBounds.width();
// If the notification panel is collapsed, the clip may be larger than the height.
int notificationHeight = Math.max(mSourceNotification.getActualHeight()
- mSourceNotification.getClipBottomAmount(), 0);
int notificationWidth = mSourceNotification.getWidth();
anim.setDuration(ANIMATION_DURATION);
anim.setInterpolator(Interpolators.LINEAR);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mParams.linearProgress = animation.getAnimatedFraction();
float progress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
mParams.linearProgress);
int newWidth = (int) MathUtils.lerp(notificationWidth,
targetWidth, progress);
mParams.left = (int) ((targetWidth - newWidth) / 2.0f);
mParams.right = mParams.left + newWidth;
mParams.top = (int) MathUtils.lerp(mParams.startPosition[1],
primary.position.y, progress);
mParams.bottom = (int) MathUtils.lerp(mParams.startPosition[1]
+ notificationHeight,
primary.position.y + primary.sourceContainerBounds.bottom,
progress);
mCornerRadius = MathUtils.lerp(mNotificationCornerRadius,
mWindowCornerRadius, progress);
applyParamsToWindow(primary);
applyParamsToNotification(mParams);
applyParamsToNotificationShade(mParams);
}
});
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
setExpandAnimationRunning(false);
invokeCallback(iRemoteAnimationFinishedCallback);
}
});
anim.start();
setAnimationPending(false);
});
}
private void invokeCallback(IRemoteAnimationFinishedCallback callback) {
try {
callback.onAnimationFinished();
} catch (RemoteException e) {
e.printStackTrace();
}
}
private RemoteAnimationTarget getPrimaryRemoteAnimationTarget(
RemoteAnimationTarget[] remoteAnimationTargets) {
RemoteAnimationTarget primary = null;
for (RemoteAnimationTarget app : remoteAnimationTargets) {
if (app.mode == RemoteAnimationTarget.MODE_OPENING) {
primary = app;
break;
}
}
return primary;
}
private void setExpandAnimationRunning(boolean running) {
mNotificationPanel.setLaunchingNotification(running);
mSourceNotification.setExpandAnimationRunning(running);
mNotificationShadeWindowViewController.setExpandAnimationRunning(running);
mNotificationContainer.setExpandingNotification(running ? mSourceNotification : null);
mAnimationRunning = running;
if (!running) {
mCallback.onExpandAnimationFinished(mIsFullScreenLaunch);
applyParamsToNotification(null);
applyParamsToNotificationShade(null);
}
}
private void applyParamsToNotificationShade(ExpandAnimationParameters params) {
mNotificationContainer.applyExpandAnimationParams(params);
mNotificationPanel.applyExpandAnimationParams(params);
mDepthController.setNotificationLaunchAnimationParams(params);
}
private void applyParamsToNotification(ExpandAnimationParameters params) {
mSourceNotification.applyExpandAnimationParams(params);
}
private void applyParamsToWindow(RemoteAnimationTarget app) {
Matrix m = new Matrix();
m.postTranslate(0, (float) (mParams.top - app.position.y));
mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight());
SurfaceParams params = new SurfaceParams.Builder(app.leash)
.withAlpha(1f)
.withMatrix(m)
.withWindowCrop(mWindowCrop)
.withLayer(app.prefixOrderIndex)
.withCornerRadius(mCornerRadius)
.withVisibility(true)
.build();
mSyncRtTransactionApplier.scheduleApply(params);
}
@Override
public void onAnimationCancelled() throws RemoteException {
mMainExecutor.execute(() -> {
setAnimationPending(false);
mCallback.onLaunchAnimationCancelled();
});
}
};
在StatusBarNotificationActivityStarter的startNotificationIntent方法中,创建好了RemoteAnimationAdapter后,通过binder调用了ActivityTaskManagerService的registerRemoteAnimationForNextActivityStart方法:
@Override
public void registerRemoteAnimationForNextActivityStart(String packageName,
RemoteAnimationAdapter adapter) {
mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
"registerRemoteAnimationForNextActivityStart");
adapter.setCallingPidUid(Binder.getCallingPid(), Binder.getCallingUid());
synchronized (mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
try {
getActivityStartController().registerRemoteAnimationForNextActivityStart(
packageName, adapter);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
调用了ActivityStartController的registerRemoteAnimationForNextActivityStart方法:
void registerRemoteAnimationForNextActivityStart(String packageName,
RemoteAnimationAdapter adapter) {
mPendingRemoteAnimationRegistry.addPendingAnimation(packageName, adapter);
}
调用了PendingRemoteAnimationRegistry的addPendingAnimation方法:
private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
/**
* Adds a remote animation to be run for all activity starts originating from a certain package.
*/
void addPendingAnimation(String packageName, RemoteAnimationAdapter adapter) {
mEntries.put(packageName, new Entry(packageName, adapter));
}
最终将RemoteAnimationAdapter保存到了ActivityTaskManagerService.mActivityStartController.mPendingRemoteAnimationRegistry.mEntries中。
2 在ActivityStarter启动Activity时,将RemoteAnimationAdapter保存在ActivityOptions中
在ActivityStarter的executeRequest方法中:
/**
* Executing activity start request and starts the journey of starting an activity. Here
* begins with performing several preliminary checks. The normally activity launch flow will
* go through {@link #startActivityUnchecked} to {@link #startActivityInner}.
*/
private int executeRequest(Request request) {
......
if (request.allowPendingRemoteAnimationRegistryLookup) {
checkedOptions = mService.getActivityStartController()
.getPendingRemoteAnimationRegistry()
.overrideOptionsIfNeeded(callingPackage, checkedOptions);
}
......
final ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
callingPackage, callingFeatureId, intent, resolvedType, aInfo,
mService.getGlobalConfiguration(), resultRecord, resultWho, requestCode,
request.componentSpecified, voiceSession != null, mSupervisor, checkedOptions,
sourceRecord);
......
}
调用了PendingRemoteAnimationRegistry的overrideOptionsIfNeeded方法:
/**
* Overrides the activity options with a registered remote animation for a certain calling
* package if such a remote animation is registered.
*/
ActivityOptions overrideOptionsIfNeeded(String callingPackage,
@Nullable ActivityOptions options) {
final Entry entry = mEntries.get(callingPackage);
if (entry == null) {
return options;
}
if (options == null) {
options = ActivityOptions.makeRemoteAnimation(entry.adapter);
} else {
options.setRemoteAnimationAdapter(entry.adapter);
}
mEntries.remove(callingPackage);
return options;
}
将RemoteAnimationAdapter保存在了ActivityOptions中。
在ActivityStarter的executeRequest方法后面还调用了ActivityRecord的构造器:
ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,
int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage,
@Nullable String _launchedFromFeature, Intent _intent, String _resolvedType,
ActivityInfo aInfo, Configuration _configuration, ActivityRecord _resultTo,
String _resultWho, int _reqCode, boolean _componentSpecified,
boolean _rootVoiceInteraction, ActivityStackSupervisor supervisor,
ActivityOptions options, ActivityRecord sourceRecord) {
......
if (options != null) {
pendingOptions = options;
......
}
将这个ActivityOptions保存在了ActivityRecord的成员变量pendingOptions中。
3 ATMS将桌面Pause后,要对新的Activity进行Resume前,检查ActivityOptions时,将RemoteAnimationAdapter保存在了AppTransition的RemoteAnimationController中
先看ActivityTaskManagerService的activityPaused方法:
@Override
public final void activityPaused(IBinder token) {
final long origId = Binder.clearCallingIdentity();
synchronized (mGlobalLock) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityPaused");
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r != null) {
r.activityPaused(false);
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
Binder.restoreCallingIdentity(origId);
}
其中r就是桌面的ActivityRecord,调用了其activityPaused方法:
void activityPaused(boolean timeout) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE,
"Activity paused: token=" + appToken + ", timeout=" + timeout);
final ActivityStack stack = getStack();
if (stack != null) {
removePauseTimeout();
if (stack.mPausingActivity == this) {
if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSED: " + this
+ (timeout ? " (due to timeout)" : " (pause complete)"));
mAtmService.deferWindowLayout();
try {
stack.completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
} finally {
mAtmService.continueWindowLayout();
}
return;
} else {
EventLogTags.writeWmFailedToPause(mUserId, System.identityHashCode(this),
shortComponentName, stack.mPausingActivity != null
? stack.mPausingActivity.shortComponentName : "(none)");
if (isState(PAUSING)) {
callServiceTrackeronActivityStatechange(PAUSED, true);
setState(PAUSED, "activityPausedLocked");
if (finishing) {
if (DEBUG_PAUSE) Slog.v(TAG,
"Executing finish of failed to pause activity: " + this);
completeFinishing("activityPausedLocked");
}
}
}
}
mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
}
又调用了其ActivityStack的completePauseLocked方法:
@VisibleForTesting
void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
ActivityRecord prev = mPausingActivity;
......
if (resumeNext) {
final ActivityStack topStack = mRootWindowContainer.getTopDisplayFocusedStack();
if (topStack != null && !topStack.shouldSleepOrShutDownActivities()) {
mRootWindowContainer.resumeFocusedStacksTopActivities(topStack, prev, null);
}
......
}
......
}
调用了RootWindowContainer的resumeFocusedStacksTopActivities方法:
boolean resumeFocusedStacksTopActivities(
ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
......
if (targetStack != null && (targetStack.isTopStackInDisplayArea()
|| getTopDisplayFocusedStack() == targetStack)) {
result = targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
......
}
调用了ActivityStack的resumeTopActivityUncheckedLocked方法:
/**
* Ensure that the top activity in the stack is resumed.
*
* @param prev The previously resumed activity, for when in the process
* of pausing; can be null to call from elsewhere.
* @param options Activity options.
*
* @return Returns true if something is being resumed, or false if
* nothing happened.
*
* NOTE: It is not safe to call this method directly as it can cause an activity in a
* non-focused stack to be resumed.
* Use {@link RootWindowContainer#resumeFocusedStacksTopActivities} to resume the
* right activity for the current system state.
*/
@GuardedBy("mService")
boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
......
try {
......
result = resumeTopActivityInnerLocked(prev, options);
......
}
......
}
调用了ActivityStack的resumeTopActivityInnerLocked方法:
@GuardedBy("mService")
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
......
if (anim) {
next.applyOptionsLocked();
}
......
}
其中next是要启动的Activity的ActivityRecord,调用了其applyOptionsLocked方法:
void applyOptionsLocked() {
if (pendingOptions != null
&& pendingOptions.getAnimationType() != ANIM_SCENE_TRANSITION) {
......
applyOptionsLocked(pendingOptions, intent);
......
}
}
/**
* Apply override app transition base on options & animation type.
*/
void applyOptionsLocked(ActivityOptions pendingOptions, Intent intent) {
final int animationType = pendingOptions.getAnimationType();
final DisplayContent displayContent = getDisplayContent();
switch (animationType) {
......
case ANIM_REMOTE_ANIMATION:
displayContent.mAppTransition.overridePendingAppTransitionRemote(
pendingOptions.getRemoteAnimationAdapter());
break;
......
}
}
根据上一步可知,这里的pendingOptions中保存了systemui进程传来的RemoteAnimationAdapter。
最后调用了AppTransition的overridePendingAppTransitionRemote方法:
void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter) {
ProtoLog.i(WM_DEBUG_APP_TRANSITIONS, "Override pending remote transitionSet=%b adapter=%s",
isTransitionSet(), remoteAnimationAdapter);
if (isTransitionSet()) {
clear();
mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE;
mRemoteAnimationController = new RemoteAnimationController(mService,
remoteAnimationAdapter, mHandler);
}
}
在这里创建了RemoteAnimationController,其中保存了RemoteAnimationAdapter。
4 在app过渡动画流程中,使用RemoteAnimationAdapterWrapper作为AnimationAdapter
在WindowContainer的applyAnimationUnchecked方法中调用了WindowContainer的getAnimationAdapter方法:
/**
* Gets the {@link AnimationAdapter} according the given window layout properties in the window
* hierarchy.
*
* @return The return value will always contain two elements, one for normal animations and the
* other for thumbnail animation, both can be {@code null}.
*
* @See com.android.server.wm.RemoteAnimationController.RemoteAnimationRecord
* @See LocalAnimationAdapter
*/
Pair<AnimationAdapter, AnimationAdapter> getAnimationAdapter(WindowManager.LayoutParams lp,
int transit, boolean enter, boolean isVoiceInteraction) {
final Pair<AnimationAdapter, AnimationAdapter> resultAdapters;
......
final RemoteAnimationController controller =
getDisplayContent().mAppTransition.getRemoteAnimationController();
......
// Delaying animation start isn't compatible with remote animations at all.
if (controller != null && !mSurfaceAnimator.isAnimationStartDelayed()) {
final Rect localBounds = new Rect(mTmpRect);
localBounds.offsetTo(mTmpPoint.x, mTmpPoint.y);
final RemoteAnimationController.RemoteAnimationRecord adapters =
controller.createRemoteAnimationRecord(this, mTmpPoint, localBounds,
screenBounds, (isChanging ? mSurfaceFreezer.mFreezeBounds : null));
resultAdapters = new Pair<>(adapters.mAdapter, adapters.mThumbnailAdapter);
}
......
return resultAdapters;
}
其中调用了RemoteAnimationController的createRemoteAnimationRecord方法:
/**
* Creates an animation record for each individual {@link WindowContainer}.
*
* @param windowContainer The windows to animate.
* @param position The position app bounds, in screen coordinates.
* @param localBounds The bounds of the app relative to its parent.
* @param stackBounds The stack bounds of the app relative to position.
* @param startBounds The stack bounds before the transition, in screen coordinates
* @return The record representing animation(s) to run on the app.
*/
RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer,
Point position, Rect localBounds, Rect stackBounds, Rect startBounds) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAnimationAdapter(): container=%s",
windowContainer);
final RemoteAnimationRecord adapters = new RemoteAnimationRecord(windowContainer, position,
localBounds, stackBounds, startBounds);
mPendingAnimations.add(adapters);
return adapters;
}
RemoteAnimationRecord是RemoteAnimationController的内部类:
/**
* Contains information about a remote-animation for one WindowContainer. This keeps track of,
* potentially, multiple animating surfaces (AdapterWrappers) associated with one
* Window/Transition. For example, a change transition has an adapter controller for the
* main window and an adapter controlling the start-state snapshot.
* <p>
* This can be thought of as a bridge between the information that the remote animator sees (via
* {@link RemoteAnimationTarget}) and what the server sees (the
* {@link RemoteAnimationAdapterWrapper}(s) interfacing with the moving surfaces).
*/
public class RemoteAnimationRecord {
RemoteAnimationAdapterWrapper mAdapter;
RemoteAnimationAdapterWrapper mThumbnailAdapter = null;
RemoteAnimationTarget mTarget;
final WindowContainer mWindowContainer;
final Rect mStartBounds;
RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds,
Rect endBounds, Rect startBounds) {
mWindowContainer = windowContainer;
mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds);
if (startBounds != null) {
mStartBounds = new Rect(startBounds);
mTmpRect.set(startBounds);
mTmpRect.offsetTo(0, 0);
if (mRemoteAnimationAdapter.getChangeNeedsSnapshot()) {
mThumbnailAdapter =
new RemoteAnimationAdapterWrapper(this, new Point(0, 0), localBounds,
mTmpRect);
}
} else {
mStartBounds = null;
}
}
RemoteAnimationTarget createRemoteAnimationTarget() {
if (mAdapter == null
|| mAdapter.mCapturedFinishCallback == null
|| mAdapter.mCapturedLeash == null) {
return null;
}
mTarget = mWindowContainer.createRemoteAnimationTarget(this);
return mTarget;
}
int getMode() {
final DisplayContent dc = mWindowContainer.getDisplayContent();
final ActivityRecord topActivity = mWindowContainer.getTopMostActivity();
// Note that opening/closing transitions are per-activity while changing transitions
// are per-task.
if (dc.mOpeningApps.contains(topActivity)) {
return RemoteAnimationTarget.MODE_OPENING;
} else if (dc.mChangingContainers.contains(mWindowContainer)) {
return RemoteAnimationTarget.MODE_CHANGING;
} else {
return RemoteAnimationTarget.MODE_CLOSING;
}
}
}
RemoteAnimationAdapterWrapper是RemoteAnimationController的内部类,其实现了AnimationAdapter接口:
class RemoteAnimationAdapterWrapper implements AnimationAdapter {
private final RemoteAnimationRecord mRecord;
SurfaceControl mCapturedLeash;
private OnAnimationFinishedCallback mCapturedFinishCallback;
private @AnimationType int mAnimationType;
final Point mPosition = new Point();
final Rect mLocalBounds;
final Rect mStackBounds = new Rect();
RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position,
Rect localBounds, Rect stackBounds) {
mRecord = record;
mPosition.set(position.x, position.y);
mLocalBounds = localBounds;
mStackBounds.set(stackBounds);
}
@Override
public boolean getShowWallpaper() {
return false;
}
@Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
@AnimationType int type, OnAnimationFinishedCallback finishCallback) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation");
// Restore position and stack crop until client has a chance to modify it.
if (mRecord.mStartBounds != null) {
t.setPosition(animationLeash, mRecord.mStartBounds.left, mRecord.mStartBounds.top);
t.setWindowCrop(animationLeash, mRecord.mStartBounds.width(),
mRecord.mStartBounds.height());
} else {
t.setPosition(animationLeash, mPosition.x, mPosition.y);
t.setWindowCrop(animationLeash, mStackBounds.width(), mStackBounds.height());
}
mCapturedLeash = animationLeash;
mCapturedFinishCallback = finishCallback;
mAnimationType = type;
}
@Override
public void onAnimationCancelled(SurfaceControl animationLeash) {
if (mRecord.mAdapter == this) {
mRecord.mAdapter = null;
} else {
mRecord.mThumbnailAdapter = null;
}
if (mRecord.mAdapter == null && mRecord.mThumbnailAdapter == null) {
mPendingAnimations.remove(mRecord);
}
if (mPendingAnimations.isEmpty()) {
cancelAnimation("allAppAnimationsCanceled");
}
}
@Override
public long getDurationHint() {
return mRemoteAnimationAdapter.getDuration();
}
@Override
public long getStatusBarTransitionsStartTime() {
return SystemClock.uptimeMillis()
+ mRemoteAnimationAdapter.getStatusBarTransitionDelay();
}
@Override
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("container="); pw.println(mRecord.mWindowContainer);
if (mRecord.mTarget != null) {
pw.print(prefix); pw.println("Target:");
mRecord.mTarget.dump(pw, prefix + " ");
} else {
pw.print(prefix); pw.println("Target: null");
}
}
@Override
public void dumpDebug(ProtoOutputStream proto) {
final long token = proto.start(REMOTE);
if (mRecord.mTarget != null) {
mRecord.mTarget.dumpDebug(proto, TARGET);
}
proto.end(token);
}
}
5 调用IRemoteAnimationRunner的onAnimationStart方法开始进行远程动画
在AppTransitionController的handleAppTransitionReady方法后面还调用了AppTransition的goodToGo方法:
/**
* @return bit-map of WindowManagerPolicy#FINISH_LAYOUT_REDO_* to indicate whether another
* layout pass needs to be done
*/
int goodToGo(int transit, ActivityRecord topOpeningApp, ArraySet<ActivityRecord> openingApps) {
......
if (mRemoteAnimationController != null) {
mRemoteAnimationController.goodToGo();
}
......
}
调用了RemoteAnimationController的goodToGo方法:
/**
* Called when the transition is ready to be started, and all leashes have been set up.
*/
void goodToGo() {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo()");
if (mPendingAnimations.isEmpty() || mCanceled) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
"goodToGo(): Animation finished already, canceled=%s mPendingAnimations=%d",
mCanceled, mPendingAnimations.size());
onAnimationFinished();
return;
}
// Scale the timeout with the animator scale the controlling app is using.
mHandler.postDelayed(mTimeoutRunnable,
(long) (TIMEOUT_MS * mService.getCurrentAnimatorScale()));
mFinishedCallback = new FinishedCallback(this);
// Create the app targets
final RemoteAnimationTarget[] appTargets = createAppAnimations();
if (appTargets.length == 0) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo(): No apps to animate");
onAnimationFinished();
return;
}
// Create the remote wallpaper animation targets (if any)
final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
try {
linkToDeathOfRunner();
mRemoteAnimationAdapter.getRunner().onAnimationStart(appTargets, wallpaperTargets,
mFinishedCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to start remote animation", e);
onAnimationFinished();
}
if (ProtoLogImpl.isEnabled(WM_DEBUG_REMOTE_ANIMATIONS)) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation(): Notify animation start:");
writeStartDebugStatement();
}
});
setRunningRemoteAnimation(true);
}
最终调用了IRemoteAnimationRunner的onAnimationStart方法开始进行远程动画。