Loading core/java/android/view/animation/ClipRectAnimation.java 0 → 100644 +59 −0 Original line number Diff line number Diff line /* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view.animation; import android.graphics.Rect; /** * An animation that controls the clip of an object. See the * {@link android.view.animation full package} description for details and * sample code. * * @hide */ public class ClipRectAnimation extends Animation { private Rect mFromRect = new Rect(); private Rect mToRect = new Rect(); /** * Constructor to use when building a ClipRectAnimation from code * * @param fromClip the clip rect to animate from * @param toClip the clip rect to animate to */ public ClipRectAnimation(Rect fromClip, Rect toClip) { if (fromClip == null || toClip == null) { throw new RuntimeException("Expected non-null animation clip rects"); } mFromRect.set(fromClip); mToRect.set(toClip); } @Override protected void applyTransformation(float it, Transformation tr) { int l = mFromRect.left + (int) ((mToRect.left - mFromRect.left) * it); int t = mFromRect.top + (int) ((mToRect.top - mFromRect.top) * it); int r = mFromRect.right + (int) ((mToRect.right - mFromRect.right) * it); int b = mFromRect.bottom + (int) ((mToRect.bottom - mFromRect.bottom) * it); tr.setClipRect(l, t, r, b); } @Override public boolean willChangeTransformationMatrix() { return false; } } core/java/android/view/animation/Transformation.java +52 −1 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.view.animation; import android.graphics.Matrix; import android.graphics.Rect; import java.io.PrintWriter; Loading Loading @@ -47,6 +48,9 @@ public class Transformation { protected float mAlpha; protected int mTransformationType; private boolean mHasClipRect; private Rect mClipRect = new Rect(); /** * Creates a new transformation with alpha = 1 and the identity matrix. */ Loading @@ -65,6 +69,8 @@ public class Transformation { } else { mMatrix.reset(); } mClipRect.setEmpty(); mHasClipRect = false; mAlpha = 1.0f; mTransformationType = TYPE_BOTH; } Loading Loading @@ -98,6 +104,12 @@ public class Transformation { public void set(Transformation t) { mAlpha = t.getAlpha(); mMatrix.set(t.getMatrix()); if (t.mHasClipRect) { setClipRect(t.getClipRect()); } else { mHasClipRect = false; mClipRect.setEmpty(); } mTransformationType = t.getTransformationType(); } Loading @@ -109,6 +121,9 @@ public class Transformation { public void compose(Transformation t) { mAlpha *= t.getAlpha(); mMatrix.preConcat(t.getMatrix()); if (t.mHasClipRect) { setClipRect(t.getClipRect()); } } /** Loading @@ -119,6 +134,9 @@ public class Transformation { public void postCompose(Transformation t) { mAlpha *= t.getAlpha(); mMatrix.postConcat(t.getMatrix()); if (t.mHasClipRect) { setClipRect(t.getClipRect()); } } /** Loading @@ -137,6 +155,39 @@ public class Transformation { mAlpha = alpha; } /** * Sets the current Transform's clip rect * @hide */ public void setClipRect(Rect r) { setClipRect(r.left, r.top, r.right, r.bottom); } /** * Sets the current Transform's clip rect * @hide */ public void setClipRect(int l, int t, int r, int b) { mClipRect.set(l, t, r, b); mHasClipRect = true; } /** * Returns the current Transform's clip rect * @hide */ public Rect getClipRect() { return mClipRect; } /** * Returns whether the current Transform's clip rect is set * @hide */ public boolean hasClipRect() { return mHasClipRect; } /** * @return The degree of transparency */ Loading services/core/java/com/android/server/wm/AppTransition.java +218 −54 Original line number Diff line number Diff line Loading @@ -19,18 +19,22 @@ package com.android.server.wm; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.Rect; import android.os.Debug; import android.os.Handler; import android.os.IRemoteCallback; import android.os.SystemProperties; import android.util.Slog; import android.view.WindowManager; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.AnimationSet; import android.view.animation.AnimationUtils; import android.view.animation.ClipRectAnimation; import android.view.animation.Interpolator; import android.view.animation.ScaleAnimation; import android.view.animation.TranslateAnimation; import com.android.internal.util.DumpUtils.Dump; import com.android.server.AttributeCache; import com.android.server.wm.WindowManagerService.H; Loading Loading @@ -125,6 +129,12 @@ public class AppTransition implements Dump { private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN = 4; private int mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE; // These are the possible states for the enter/exit activities during a thumbnail transition private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0; private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1; private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2; private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3; private String mNextAppTransitionPackage; private Bitmap mNextAppTransitionThumbnail; // Used for thumbnail transitions. True if we're scaling up, false if scaling down Loading @@ -148,10 +158,13 @@ public class AppTransition implements Dump { private final Interpolator mThumbnailFadeoutInterpolator; private int mCurrentUserId = 0; private boolean mUseAlternateThumbnailAnimation; AppTransition(Context context, Handler h) { mContext = context; mH = h; mUseAlternateThumbnailAnimation = SystemProperties.getBoolean("persist.anim.use_alt_thumbnail", false); mConfigShortAnimTime = context.getResources().getInteger( com.android.internal.R.integer.config_shortAnimTime); mDecelerateInterpolator = AnimationUtils.loadInterpolator(context, Loading Loading @@ -384,17 +397,62 @@ public class AppTransition implements Dump { return a; } Animation createThumbnailAnimationLocked(int transit, boolean enter, boolean thumb, int appWidth, int appHeight) { /** * Prepares the specified animation with a standard duration, interpolator, etc. */ Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, int transit) { // Pick the desired duration. If this is an inter-activity transition, // it is the standard duration for that. Otherwise we use the longer // task transition duration. final long duration; switch (transit) { case TRANSIT_ACTIVITY_OPEN: case TRANSIT_ACTIVITY_CLOSE: duration = mConfigShortAnimTime; break; default: duration = DEFAULT_APP_TRANSITION_DURATION; break; } a.setDuration(duration); a.setFillAfter(true); a.setInterpolator(mDecelerateInterpolator); a.initialize(appWidth, appHeight, appWidth, appHeight); return a; } /** * Return the current thumbnail transition state. */ int getThumbnailTransitionState(boolean enter) { if (enter) { if (mNextAppTransitionScaleUp) { return THUMBNAIL_TRANSITION_ENTER_SCALE_UP; } else { return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN; } } else { if (mNextAppTransitionScaleUp) { return THUMBNAIL_TRANSITION_EXIT_SCALE_UP; } else { return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN; } } } /** * This animation runs for the thumbnail that gets cross faded with the enter/exit activity * when a thumbnail is specified with the activity options. */ Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit) { Animation a; final int thumbWidthI = mNextAppTransitionThumbnail.getWidth(); final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; final int thumbHeightI = mNextAppTransitionThumbnail.getHeight(); final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1; if (thumb) { // Animation for zooming thumbnail from its initial size to // filling the screen. if (mNextAppTransitionScaleUp) { // Animation for the thumbnail zooming from its initial size to the full screen float scaleW = appWidth / thumbWidth; float scaleH = appHeight / thumbHeight; Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH, Loading @@ -411,27 +469,131 @@ public class AppTransition implements Dump { set.addAnimation(alpha); a = set; } else { // Animation for the thumbnail zooming down from the full screen to its final size float scaleW = appWidth / thumbWidth; float scaleH = appHeight / thumbHeight; a = new ScaleAnimation(scaleW, 1, scaleH, 1, computePivot(mNextAppTransitionStartX, 1 / scaleW), computePivot(mNextAppTransitionStartY, 1 / scaleH)); } } else if (enter) { // Entering app zooms out from the center of the thumbnail. if (mNextAppTransitionScaleUp) { return prepareThumbnailAnimation(a, appWidth, appHeight, transit); } /** * This alternate animation is created when we are doing a thumbnail transition, for the * activity that is leaving, and the activity that is entering. */ Animation createAlternateThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth, int appHeight, int transit, Rect containingFrame, Rect contentInsets) { Animation a; final int thumbWidthI = mNextAppTransitionThumbnail.getWidth(); final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; final int thumbHeightI = mNextAppTransitionThumbnail.getHeight(); final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1; switch (thumbTransitState) { case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: { // Entering app scales up with the thumbnail float scale = thumbWidth / appWidth; int unscaledThumbHeight = (int) (thumbHeight / scale); int scaledTopDecor = (int) (scale * contentInsets.top); Rect fromClipRect = new Rect(containingFrame); fromClipRect.bottom = (fromClipRect.top + unscaledThumbHeight); Rect toClipRect = new Rect(containingFrame); Animation scaleAnim = new ScaleAnimation(scale, 1, scale, 1, computePivot(mNextAppTransitionStartX, scale), computePivot(mNextAppTransitionStartY, scale)); Animation alphaAnim = new AlphaAnimation(1, 1); Animation clipAnim = new ClipRectAnimation(fromClipRect, toClipRect); Animation translateAnim = new TranslateAnimation(0, 0, -scaledTopDecor, 0); AnimationSet set = new AnimationSet(true); set.addAnimation(alphaAnim); set.addAnimation(clipAnim); set.addAnimation(scaleAnim); set.addAnimation(translateAnim); a = set; break; } case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: { // Exiting app while the thumbnail is scaling up should fade if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) { // Fade out while bringing up selected activity. This keeps the // current activity from showing through a launching wallpaper // activity. a = new AlphaAnimation(1, 0); } else { // noop animation a = new AlphaAnimation(1, 1); } break; } case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: { // Entering the other app, it should just be visible while we scale the thumbnail // down above it a = new AlphaAnimation(1, 1); break; } case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: { // Exiting the current app, the app should scale down with the thumbnail float scale = thumbWidth / appWidth; int unscaledThumbHeight = (int) (thumbHeight / scale); int scaledTopDecor = (int) (scale * contentInsets.top); Rect fromClipRect = new Rect(containingFrame); Rect toClipRect = new Rect(containingFrame); toClipRect.bottom = (toClipRect.top + unscaledThumbHeight); Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale, computePivot(mNextAppTransitionStartX, scale), computePivot(mNextAppTransitionStartY, scale)); Animation alphaAnim = new AlphaAnimation(1, 1); Animation clipAnim = new ClipRectAnimation(fromClipRect, toClipRect); Animation translateAnim = new TranslateAnimation(0, 0, 0, -scaledTopDecor); AnimationSet set = new AnimationSet(true); set.addAnimation(alphaAnim); set.addAnimation(clipAnim); set.addAnimation(scaleAnim); set.addAnimation(translateAnim); a = set; a.setZAdjustment(Animation.ZORDER_TOP); break; } default: throw new RuntimeException("Invalid thumbnail transition state"); } return prepareThumbnailAnimation(a, appWidth, appHeight, transit); } /** * This animation is created when we are doing a thumbnail transition, for the activity that is * leaving, and the activity that is entering. */ Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth, int appHeight, int transit) { Animation a; final int thumbWidthI = mNextAppTransitionThumbnail.getWidth(); final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; final int thumbHeightI = mNextAppTransitionThumbnail.getHeight(); final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1; switch (thumbTransitState) { case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: { // Entering app scales up with the thumbnail float scaleW = thumbWidth / appWidth; float scaleH = thumbHeight / appHeight; a = new ScaleAnimation(scaleW, 1, scaleH, 1, computePivot(mNextAppTransitionStartX, scaleW), computePivot(mNextAppTransitionStartY, scaleH)); } else { // noop animation a = new AlphaAnimation(1, 1); break; } } else { // Exiting app if (mNextAppTransitionScaleUp) { case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: { // Exiting app while the thumbnail is scaling up should fade or stay in place if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) { // Fade out while bringing up selected activity. This keeps the // current activity from showing through a launching wallpaper Loading @@ -441,7 +603,16 @@ public class AppTransition implements Dump { // noop animation a = new AlphaAnimation(1, 1); } } else { break; } case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: { // Entering the other app, it should just be visible while we scale the thumbnail // down above it a = new AlphaAnimation(1, 1); break; } case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: { // Exiting the current app, the app should scale down with the thumbnail float scaleW = thumbWidth / appWidth; float scaleH = thumbHeight / appHeight; Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH, Loading @@ -455,32 +626,18 @@ public class AppTransition implements Dump { set.addAnimation(alpha); set.setZAdjustment(Animation.ZORDER_TOP); a = set; } } // Pick the desired duration. If this is an inter-activity transition, // it is the standard duration for that. Otherwise we use the longer // task transition duration. final long duration; switch (transit) { case TRANSIT_ACTIVITY_OPEN: case TRANSIT_ACTIVITY_CLOSE: duration = mConfigShortAnimTime; break; } default: duration = DEFAULT_APP_TRANSITION_DURATION; break; throw new RuntimeException("Invalid thumbnail transition state"); } a.setDuration(duration); a.setFillAfter(true); a.setInterpolator(mDecelerateInterpolator); a.initialize(appWidth, appHeight, appWidth, appHeight); return a; return prepareThumbnailAnimation(a, appWidth, appHeight, transit); } Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, int appWidth, int appHeight) { int appWidth, int appHeight, Rect containingFrame, Rect contentInsets) { Animation a; if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) { a = loadAnimation(mNextAppTransitionPackage, enter ? Loading @@ -501,7 +658,14 @@ public class AppTransition implements Dump { mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) { mNextAppTransitionScaleUp = (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP); a = createThumbnailAnimationLocked(transit, enter, false, appWidth, appHeight); if (mUseAlternateThumbnailAnimation) { a = createAlternateThumbnailEnterExitAnimationLocked( getThumbnailTransitionState(enter), appWidth, appHeight, transit, containingFrame, contentInsets); } else { a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter), appWidth, appHeight, transit); } if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { String animName = mNextAppTransitionScaleUp ? "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN"; Loading services/core/java/com/android/server/wm/WindowManagerService.java +22 −5 Original line number Diff line number Diff line Loading @@ -3155,7 +3155,22 @@ public class WindowManagerService extends IWindowManager.Stub final int height = displayInfo.appHeight; if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, "applyAnimation: atoken=" + atoken); Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height); // Determine the visible rect to calculate the thumbnail clip WindowState win = atoken.findMainWindow(); Rect containingFrame = new Rect(0, 0, width, height); Rect contentInsets = new Rect(); if (win != null) { if (win.mContainingFrame != null) { containingFrame.set(win.mContainingFrame); } if (win.mContentInsets != null) { contentInsets.set(win.mContentInsets); } } Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height, containingFrame, contentInsets); if (a != null) { if (DEBUG_ANIM) { RuntimeException e = null; Loading Loading @@ -8608,11 +8623,13 @@ public class WindowManagerService extends IWindowManager.Stub wtoken.deferClearAllDrawn = false; } boolean useAlternateThumbnailAnimation = SystemProperties.getBoolean("persist.anim.use_alt_thumbnail", false); AppWindowAnimator appAnimator = topOpeningApp == null ? null : topOpeningApp.mAppAnimator; Bitmap nextAppTransitionThumbnail = mAppTransition.getNextAppTransitionThumbnail(); if (nextAppTransitionThumbnail != null && appAnimator != null && appAnimator.animation != null) { if (!useAlternateThumbnailAnimation && nextAppTransitionThumbnail != null && appAnimator != null && appAnimator.animation != null) { // This thumbnail animation is very special, we need to have // an extra surface with the thumbnail included with the animation. Rect dirty = new Rect(0, 0, nextAppTransitionThumbnail.getWidth(), Loading @@ -8636,8 +8653,8 @@ public class WindowManagerService extends IWindowManager.Stub drawSurface.release(); appAnimator.thumbnailLayer = topOpeningLayer; DisplayInfo displayInfo = getDefaultDisplayInfoLocked(); Animation anim = mAppTransition.createThumbnailAnimationLocked( transit, true, true, displayInfo.appWidth, displayInfo.appHeight); Animation anim = mAppTransition.createThumbnailScaleAnimationLocked( displayInfo.appWidth, displayInfo.appHeight, transit); appAnimator.thumbnailAnimation = anim; anim.restrictDuration(MAX_ANIMATION_DURATION); anim.scaleCurrentDuration(mTransitionAnimationScale); Loading services/core/java/com/android/server/wm/WindowStateAnimator.java +32 −7 Original line number Diff line number Diff line Loading @@ -113,6 +113,11 @@ class WindowStateAnimator { float mAlpha = 0; float mLastAlpha = 0; boolean mHasClipRect; Rect mClipRect = new Rect(); Rect mTmpClipRect = new Rect(); Rect mLastClipRect = new Rect(); // Used to save animation distances between the time they are calculated and when they are // used. int mAnimDw; Loading Loading @@ -951,6 +956,7 @@ class WindowStateAnimator { if (screenAnimation) { tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix()); } //TODO (multidisplay): Magnification is supported only for the default display. if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) { MagnificationSpec spec = mService.mAccessibilityController Loading Loading @@ -985,6 +991,7 @@ class WindowStateAnimator { // transforming since it is more important to have that // animation be smooth. mShownAlpha = mAlpha; mHasClipRect = false; if (!mService.mLimitedAlphaCompositing || (!PixelFormat.formatHasAlpha(mWin.mAttrs.format) || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy) Loading @@ -998,6 +1005,10 @@ class WindowStateAnimator { } if (appTransformation != null) { mShownAlpha *= appTransformation.getAlpha(); if (appTransformation.hasClipRect()) { mClipRect.set(appTransformation.getClipRect()); mHasClipRect = true; } } if (mAnimator.mUniverseBackground != null) { mShownAlpha *= mAnimator.mUniverseBackground.mUniverseTransform.getAlpha(); Loading Loading @@ -1149,15 +1160,29 @@ class WindowStateAnimator { applyDecorRect(w.mDecorFrame); } if (!w.mSystemDecorRect.equals(w.mLastSystemDecorRect)) { w.mLastSystemDecorRect.set(w.mSystemDecorRect); // By default, the clip rect is the system decor rect Rect clipRect = w.mSystemDecorRect; if (mHasClipRect) { // If we have an animated clip rect, intersect it with the system decor rect int offsetTop = w.mSystemDecorRect.top; mTmpClipRect.set(w.mSystemDecorRect); mTmpClipRect.offset(0, -offsetTop); mTmpClipRect.intersect(mClipRect); mTmpClipRect.offset(0, offsetTop); clipRect = mTmpClipRect; } if (!clipRect.equals(mLastClipRect)) { mLastClipRect.set(clipRect); try { if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w, "CROP " + w.mSystemDecorRect.toShortString(), null); mSurfaceControl.setWindowCrop(w.mSystemDecorRect); "CROP " + clipRect.toShortString(), null); mSurfaceControl.setWindowCrop(clipRect); } catch (RuntimeException e) { Slog.w(TAG, "Error setting crop surface of " + w + " crop=" + w.mSystemDecorRect.toShortString(), e); + " crop=" + clipRect.toShortString(), e); if (!recoveringMemory) { mService.reclaimSomeSurfaceMemoryLocked(this, "crop", true); } Loading Loading
core/java/android/view/animation/ClipRectAnimation.java 0 → 100644 +59 −0 Original line number Diff line number Diff line /* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view.animation; import android.graphics.Rect; /** * An animation that controls the clip of an object. See the * {@link android.view.animation full package} description for details and * sample code. * * @hide */ public class ClipRectAnimation extends Animation { private Rect mFromRect = new Rect(); private Rect mToRect = new Rect(); /** * Constructor to use when building a ClipRectAnimation from code * * @param fromClip the clip rect to animate from * @param toClip the clip rect to animate to */ public ClipRectAnimation(Rect fromClip, Rect toClip) { if (fromClip == null || toClip == null) { throw new RuntimeException("Expected non-null animation clip rects"); } mFromRect.set(fromClip); mToRect.set(toClip); } @Override protected void applyTransformation(float it, Transformation tr) { int l = mFromRect.left + (int) ((mToRect.left - mFromRect.left) * it); int t = mFromRect.top + (int) ((mToRect.top - mFromRect.top) * it); int r = mFromRect.right + (int) ((mToRect.right - mFromRect.right) * it); int b = mFromRect.bottom + (int) ((mToRect.bottom - mFromRect.bottom) * it); tr.setClipRect(l, t, r, b); } @Override public boolean willChangeTransformationMatrix() { return false; } }
core/java/android/view/animation/Transformation.java +52 −1 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.view.animation; import android.graphics.Matrix; import android.graphics.Rect; import java.io.PrintWriter; Loading Loading @@ -47,6 +48,9 @@ public class Transformation { protected float mAlpha; protected int mTransformationType; private boolean mHasClipRect; private Rect mClipRect = new Rect(); /** * Creates a new transformation with alpha = 1 and the identity matrix. */ Loading @@ -65,6 +69,8 @@ public class Transformation { } else { mMatrix.reset(); } mClipRect.setEmpty(); mHasClipRect = false; mAlpha = 1.0f; mTransformationType = TYPE_BOTH; } Loading Loading @@ -98,6 +104,12 @@ public class Transformation { public void set(Transformation t) { mAlpha = t.getAlpha(); mMatrix.set(t.getMatrix()); if (t.mHasClipRect) { setClipRect(t.getClipRect()); } else { mHasClipRect = false; mClipRect.setEmpty(); } mTransformationType = t.getTransformationType(); } Loading @@ -109,6 +121,9 @@ public class Transformation { public void compose(Transformation t) { mAlpha *= t.getAlpha(); mMatrix.preConcat(t.getMatrix()); if (t.mHasClipRect) { setClipRect(t.getClipRect()); } } /** Loading @@ -119,6 +134,9 @@ public class Transformation { public void postCompose(Transformation t) { mAlpha *= t.getAlpha(); mMatrix.postConcat(t.getMatrix()); if (t.mHasClipRect) { setClipRect(t.getClipRect()); } } /** Loading @@ -137,6 +155,39 @@ public class Transformation { mAlpha = alpha; } /** * Sets the current Transform's clip rect * @hide */ public void setClipRect(Rect r) { setClipRect(r.left, r.top, r.right, r.bottom); } /** * Sets the current Transform's clip rect * @hide */ public void setClipRect(int l, int t, int r, int b) { mClipRect.set(l, t, r, b); mHasClipRect = true; } /** * Returns the current Transform's clip rect * @hide */ public Rect getClipRect() { return mClipRect; } /** * Returns whether the current Transform's clip rect is set * @hide */ public boolean hasClipRect() { return mHasClipRect; } /** * @return The degree of transparency */ Loading
services/core/java/com/android/server/wm/AppTransition.java +218 −54 Original line number Diff line number Diff line Loading @@ -19,18 +19,22 @@ package com.android.server.wm; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.Rect; import android.os.Debug; import android.os.Handler; import android.os.IRemoteCallback; import android.os.SystemProperties; import android.util.Slog; import android.view.WindowManager; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.AnimationSet; import android.view.animation.AnimationUtils; import android.view.animation.ClipRectAnimation; import android.view.animation.Interpolator; import android.view.animation.ScaleAnimation; import android.view.animation.TranslateAnimation; import com.android.internal.util.DumpUtils.Dump; import com.android.server.AttributeCache; import com.android.server.wm.WindowManagerService.H; Loading Loading @@ -125,6 +129,12 @@ public class AppTransition implements Dump { private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN = 4; private int mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE; // These are the possible states for the enter/exit activities during a thumbnail transition private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0; private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1; private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2; private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3; private String mNextAppTransitionPackage; private Bitmap mNextAppTransitionThumbnail; // Used for thumbnail transitions. True if we're scaling up, false if scaling down Loading @@ -148,10 +158,13 @@ public class AppTransition implements Dump { private final Interpolator mThumbnailFadeoutInterpolator; private int mCurrentUserId = 0; private boolean mUseAlternateThumbnailAnimation; AppTransition(Context context, Handler h) { mContext = context; mH = h; mUseAlternateThumbnailAnimation = SystemProperties.getBoolean("persist.anim.use_alt_thumbnail", false); mConfigShortAnimTime = context.getResources().getInteger( com.android.internal.R.integer.config_shortAnimTime); mDecelerateInterpolator = AnimationUtils.loadInterpolator(context, Loading Loading @@ -384,17 +397,62 @@ public class AppTransition implements Dump { return a; } Animation createThumbnailAnimationLocked(int transit, boolean enter, boolean thumb, int appWidth, int appHeight) { /** * Prepares the specified animation with a standard duration, interpolator, etc. */ Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, int transit) { // Pick the desired duration. If this is an inter-activity transition, // it is the standard duration for that. Otherwise we use the longer // task transition duration. final long duration; switch (transit) { case TRANSIT_ACTIVITY_OPEN: case TRANSIT_ACTIVITY_CLOSE: duration = mConfigShortAnimTime; break; default: duration = DEFAULT_APP_TRANSITION_DURATION; break; } a.setDuration(duration); a.setFillAfter(true); a.setInterpolator(mDecelerateInterpolator); a.initialize(appWidth, appHeight, appWidth, appHeight); return a; } /** * Return the current thumbnail transition state. */ int getThumbnailTransitionState(boolean enter) { if (enter) { if (mNextAppTransitionScaleUp) { return THUMBNAIL_TRANSITION_ENTER_SCALE_UP; } else { return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN; } } else { if (mNextAppTransitionScaleUp) { return THUMBNAIL_TRANSITION_EXIT_SCALE_UP; } else { return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN; } } } /** * This animation runs for the thumbnail that gets cross faded with the enter/exit activity * when a thumbnail is specified with the activity options. */ Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit) { Animation a; final int thumbWidthI = mNextAppTransitionThumbnail.getWidth(); final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; final int thumbHeightI = mNextAppTransitionThumbnail.getHeight(); final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1; if (thumb) { // Animation for zooming thumbnail from its initial size to // filling the screen. if (mNextAppTransitionScaleUp) { // Animation for the thumbnail zooming from its initial size to the full screen float scaleW = appWidth / thumbWidth; float scaleH = appHeight / thumbHeight; Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH, Loading @@ -411,27 +469,131 @@ public class AppTransition implements Dump { set.addAnimation(alpha); a = set; } else { // Animation for the thumbnail zooming down from the full screen to its final size float scaleW = appWidth / thumbWidth; float scaleH = appHeight / thumbHeight; a = new ScaleAnimation(scaleW, 1, scaleH, 1, computePivot(mNextAppTransitionStartX, 1 / scaleW), computePivot(mNextAppTransitionStartY, 1 / scaleH)); } } else if (enter) { // Entering app zooms out from the center of the thumbnail. if (mNextAppTransitionScaleUp) { return prepareThumbnailAnimation(a, appWidth, appHeight, transit); } /** * This alternate animation is created when we are doing a thumbnail transition, for the * activity that is leaving, and the activity that is entering. */ Animation createAlternateThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth, int appHeight, int transit, Rect containingFrame, Rect contentInsets) { Animation a; final int thumbWidthI = mNextAppTransitionThumbnail.getWidth(); final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; final int thumbHeightI = mNextAppTransitionThumbnail.getHeight(); final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1; switch (thumbTransitState) { case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: { // Entering app scales up with the thumbnail float scale = thumbWidth / appWidth; int unscaledThumbHeight = (int) (thumbHeight / scale); int scaledTopDecor = (int) (scale * contentInsets.top); Rect fromClipRect = new Rect(containingFrame); fromClipRect.bottom = (fromClipRect.top + unscaledThumbHeight); Rect toClipRect = new Rect(containingFrame); Animation scaleAnim = new ScaleAnimation(scale, 1, scale, 1, computePivot(mNextAppTransitionStartX, scale), computePivot(mNextAppTransitionStartY, scale)); Animation alphaAnim = new AlphaAnimation(1, 1); Animation clipAnim = new ClipRectAnimation(fromClipRect, toClipRect); Animation translateAnim = new TranslateAnimation(0, 0, -scaledTopDecor, 0); AnimationSet set = new AnimationSet(true); set.addAnimation(alphaAnim); set.addAnimation(clipAnim); set.addAnimation(scaleAnim); set.addAnimation(translateAnim); a = set; break; } case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: { // Exiting app while the thumbnail is scaling up should fade if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) { // Fade out while bringing up selected activity. This keeps the // current activity from showing through a launching wallpaper // activity. a = new AlphaAnimation(1, 0); } else { // noop animation a = new AlphaAnimation(1, 1); } break; } case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: { // Entering the other app, it should just be visible while we scale the thumbnail // down above it a = new AlphaAnimation(1, 1); break; } case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: { // Exiting the current app, the app should scale down with the thumbnail float scale = thumbWidth / appWidth; int unscaledThumbHeight = (int) (thumbHeight / scale); int scaledTopDecor = (int) (scale * contentInsets.top); Rect fromClipRect = new Rect(containingFrame); Rect toClipRect = new Rect(containingFrame); toClipRect.bottom = (toClipRect.top + unscaledThumbHeight); Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale, computePivot(mNextAppTransitionStartX, scale), computePivot(mNextAppTransitionStartY, scale)); Animation alphaAnim = new AlphaAnimation(1, 1); Animation clipAnim = new ClipRectAnimation(fromClipRect, toClipRect); Animation translateAnim = new TranslateAnimation(0, 0, 0, -scaledTopDecor); AnimationSet set = new AnimationSet(true); set.addAnimation(alphaAnim); set.addAnimation(clipAnim); set.addAnimation(scaleAnim); set.addAnimation(translateAnim); a = set; a.setZAdjustment(Animation.ZORDER_TOP); break; } default: throw new RuntimeException("Invalid thumbnail transition state"); } return prepareThumbnailAnimation(a, appWidth, appHeight, transit); } /** * This animation is created when we are doing a thumbnail transition, for the activity that is * leaving, and the activity that is entering. */ Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth, int appHeight, int transit) { Animation a; final int thumbWidthI = mNextAppTransitionThumbnail.getWidth(); final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1; final int thumbHeightI = mNextAppTransitionThumbnail.getHeight(); final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1; switch (thumbTransitState) { case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: { // Entering app scales up with the thumbnail float scaleW = thumbWidth / appWidth; float scaleH = thumbHeight / appHeight; a = new ScaleAnimation(scaleW, 1, scaleH, 1, computePivot(mNextAppTransitionStartX, scaleW), computePivot(mNextAppTransitionStartY, scaleH)); } else { // noop animation a = new AlphaAnimation(1, 1); break; } } else { // Exiting app if (mNextAppTransitionScaleUp) { case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: { // Exiting app while the thumbnail is scaling up should fade or stay in place if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) { // Fade out while bringing up selected activity. This keeps the // current activity from showing through a launching wallpaper Loading @@ -441,7 +603,16 @@ public class AppTransition implements Dump { // noop animation a = new AlphaAnimation(1, 1); } } else { break; } case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: { // Entering the other app, it should just be visible while we scale the thumbnail // down above it a = new AlphaAnimation(1, 1); break; } case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: { // Exiting the current app, the app should scale down with the thumbnail float scaleW = thumbWidth / appWidth; float scaleH = thumbHeight / appHeight; Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH, Loading @@ -455,32 +626,18 @@ public class AppTransition implements Dump { set.addAnimation(alpha); set.setZAdjustment(Animation.ZORDER_TOP); a = set; } } // Pick the desired duration. If this is an inter-activity transition, // it is the standard duration for that. Otherwise we use the longer // task transition duration. final long duration; switch (transit) { case TRANSIT_ACTIVITY_OPEN: case TRANSIT_ACTIVITY_CLOSE: duration = mConfigShortAnimTime; break; } default: duration = DEFAULT_APP_TRANSITION_DURATION; break; throw new RuntimeException("Invalid thumbnail transition state"); } a.setDuration(duration); a.setFillAfter(true); a.setInterpolator(mDecelerateInterpolator); a.initialize(appWidth, appHeight, appWidth, appHeight); return a; return prepareThumbnailAnimation(a, appWidth, appHeight, transit); } Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, int appWidth, int appHeight) { int appWidth, int appHeight, Rect containingFrame, Rect contentInsets) { Animation a; if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) { a = loadAnimation(mNextAppTransitionPackage, enter ? Loading @@ -501,7 +658,14 @@ public class AppTransition implements Dump { mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) { mNextAppTransitionScaleUp = (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP); a = createThumbnailAnimationLocked(transit, enter, false, appWidth, appHeight); if (mUseAlternateThumbnailAnimation) { a = createAlternateThumbnailEnterExitAnimationLocked( getThumbnailTransitionState(enter), appWidth, appHeight, transit, containingFrame, contentInsets); } else { a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter), appWidth, appHeight, transit); } if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { String animName = mNextAppTransitionScaleUp ? "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN"; Loading
services/core/java/com/android/server/wm/WindowManagerService.java +22 −5 Original line number Diff line number Diff line Loading @@ -3155,7 +3155,22 @@ public class WindowManagerService extends IWindowManager.Stub final int height = displayInfo.appHeight; if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, "applyAnimation: atoken=" + atoken); Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height); // Determine the visible rect to calculate the thumbnail clip WindowState win = atoken.findMainWindow(); Rect containingFrame = new Rect(0, 0, width, height); Rect contentInsets = new Rect(); if (win != null) { if (win.mContainingFrame != null) { containingFrame.set(win.mContainingFrame); } if (win.mContentInsets != null) { contentInsets.set(win.mContentInsets); } } Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height, containingFrame, contentInsets); if (a != null) { if (DEBUG_ANIM) { RuntimeException e = null; Loading Loading @@ -8608,11 +8623,13 @@ public class WindowManagerService extends IWindowManager.Stub wtoken.deferClearAllDrawn = false; } boolean useAlternateThumbnailAnimation = SystemProperties.getBoolean("persist.anim.use_alt_thumbnail", false); AppWindowAnimator appAnimator = topOpeningApp == null ? null : topOpeningApp.mAppAnimator; Bitmap nextAppTransitionThumbnail = mAppTransition.getNextAppTransitionThumbnail(); if (nextAppTransitionThumbnail != null && appAnimator != null && appAnimator.animation != null) { if (!useAlternateThumbnailAnimation && nextAppTransitionThumbnail != null && appAnimator != null && appAnimator.animation != null) { // This thumbnail animation is very special, we need to have // an extra surface with the thumbnail included with the animation. Rect dirty = new Rect(0, 0, nextAppTransitionThumbnail.getWidth(), Loading @@ -8636,8 +8653,8 @@ public class WindowManagerService extends IWindowManager.Stub drawSurface.release(); appAnimator.thumbnailLayer = topOpeningLayer; DisplayInfo displayInfo = getDefaultDisplayInfoLocked(); Animation anim = mAppTransition.createThumbnailAnimationLocked( transit, true, true, displayInfo.appWidth, displayInfo.appHeight); Animation anim = mAppTransition.createThumbnailScaleAnimationLocked( displayInfo.appWidth, displayInfo.appHeight, transit); appAnimator.thumbnailAnimation = anim; anim.restrictDuration(MAX_ANIMATION_DURATION); anim.scaleCurrentDuration(mTransitionAnimationScale); Loading
services/core/java/com/android/server/wm/WindowStateAnimator.java +32 −7 Original line number Diff line number Diff line Loading @@ -113,6 +113,11 @@ class WindowStateAnimator { float mAlpha = 0; float mLastAlpha = 0; boolean mHasClipRect; Rect mClipRect = new Rect(); Rect mTmpClipRect = new Rect(); Rect mLastClipRect = new Rect(); // Used to save animation distances between the time they are calculated and when they are // used. int mAnimDw; Loading Loading @@ -951,6 +956,7 @@ class WindowStateAnimator { if (screenAnimation) { tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix()); } //TODO (multidisplay): Magnification is supported only for the default display. if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) { MagnificationSpec spec = mService.mAccessibilityController Loading Loading @@ -985,6 +991,7 @@ class WindowStateAnimator { // transforming since it is more important to have that // animation be smooth. mShownAlpha = mAlpha; mHasClipRect = false; if (!mService.mLimitedAlphaCompositing || (!PixelFormat.formatHasAlpha(mWin.mAttrs.format) || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy) Loading @@ -998,6 +1005,10 @@ class WindowStateAnimator { } if (appTransformation != null) { mShownAlpha *= appTransformation.getAlpha(); if (appTransformation.hasClipRect()) { mClipRect.set(appTransformation.getClipRect()); mHasClipRect = true; } } if (mAnimator.mUniverseBackground != null) { mShownAlpha *= mAnimator.mUniverseBackground.mUniverseTransform.getAlpha(); Loading Loading @@ -1149,15 +1160,29 @@ class WindowStateAnimator { applyDecorRect(w.mDecorFrame); } if (!w.mSystemDecorRect.equals(w.mLastSystemDecorRect)) { w.mLastSystemDecorRect.set(w.mSystemDecorRect); // By default, the clip rect is the system decor rect Rect clipRect = w.mSystemDecorRect; if (mHasClipRect) { // If we have an animated clip rect, intersect it with the system decor rect int offsetTop = w.mSystemDecorRect.top; mTmpClipRect.set(w.mSystemDecorRect); mTmpClipRect.offset(0, -offsetTop); mTmpClipRect.intersect(mClipRect); mTmpClipRect.offset(0, offsetTop); clipRect = mTmpClipRect; } if (!clipRect.equals(mLastClipRect)) { mLastClipRect.set(clipRect); try { if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w, "CROP " + w.mSystemDecorRect.toShortString(), null); mSurfaceControl.setWindowCrop(w.mSystemDecorRect); "CROP " + clipRect.toShortString(), null); mSurfaceControl.setWindowCrop(clipRect); } catch (RuntimeException e) { Slog.w(TAG, "Error setting crop surface of " + w + " crop=" + w.mSystemDecorRect.toShortString(), e); + " crop=" + clipRect.toShortString(), e); if (!recoveringMemory) { mService.reclaimSomeSurfaceMemoryLocked(this, "crop", true); } Loading