Merge "Making transition out of recents look better" into jb-dev

This commit is contained in:
Michael Jurka
2012-05-10 10:35:19 -07:00
committed by Android (Google) Code Review
10 changed files with 161 additions and 35 deletions

View File

@ -98,6 +98,8 @@ public class ActivityOptions {
public static final int ANIM_SCALE_UP = 2;
/** @hide */
public static final int ANIM_THUMBNAIL = 3;
/** @hide */
public static final int ANIM_THUMBNAIL_DELAYED = 4;
private String mPackageName;
private int mAnimationType = ANIM_NONE;
@ -219,9 +221,38 @@ public class ActivityOptions {
*/
public static ActivityOptions makeThumbnailScaleUpAnimation(View source,
Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
return makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY, listener, false);
}
/**
* Create an ActivityOptions specifying an animation where a thumbnail
* is scaled from a given position to the new activity window that is
* being started. Before the animation, there is a short delay.
*
* @param source The View that this thumbnail is animating from. This
* defines the coordinate space for <var>startX</var> and <var>startY</var>.
* @param thumbnail The bitmap that will be shown as the initial thumbnail
* of the animation.
* @param startX The x starting location of the bitmap, relative to <var>source</var>.
* @param startY The y starting location of the bitmap, relative to <var>source</var>.
* @param listener Optional OnAnimationStartedListener to find out when the
* requested animation has started running. If for some reason the animation
* is not executed, the callback will happen immediately.
* @return Returns a new ActivityOptions object that you can use to
* supply these options as the options Bundle when starting an activity.
* @hide
*/
public static ActivityOptions makeDelayedThumbnailScaleUpAnimation(View source,
Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
return makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY, listener, true);
}
private static ActivityOptions makeThumbnailScaleUpAnimation(View source,
Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener,
boolean delayed) {
ActivityOptions opts = new ActivityOptions();
opts.mPackageName = source.getContext().getPackageName();
opts.mAnimationType = ANIM_THUMBNAIL;
opts.mAnimationType = delayed ? ANIM_THUMBNAIL_DELAYED : ANIM_THUMBNAIL;
opts.mThumbnail = thumbnail;
int[] pts = new int[2];
source.getLocationOnScreen(pts);
@ -258,7 +289,8 @@ public class ActivityOptions {
mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
mStartWidth = opts.getInt(KEY_ANIM_START_WIDTH, 0);
mStartHeight = opts.getInt(KEY_ANIM_START_HEIGHT, 0);
} else if (mAnimationType == ANIM_THUMBNAIL) {
} else if (mAnimationType == ANIM_THUMBNAIL ||
mAnimationType == ANIM_THUMBNAIL_DELAYED) {
mThumbnail = (Bitmap)opts.getParcelable(KEY_ANIM_THUMBNAIL);
mStartX = opts.getInt(KEY_ANIM_START_X, 0);
mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
@ -359,6 +391,7 @@ public class ActivityOptions {
mStartHeight = otherOptions.mStartHeight;
break;
case ANIM_THUMBNAIL:
case ANIM_THUMBNAIL_DELAYED:
mAnimationType = otherOptions.mAnimationType;
mThumbnail = otherOptions.mThumbnail;
mStartX = otherOptions.mStartX;
@ -401,6 +434,7 @@ public class ActivityOptions {
b.putInt(KEY_ANIM_START_HEIGHT, mStartHeight);
break;
case ANIM_THUMBNAIL:
case ANIM_THUMBNAIL_DELAYED:
b.putInt(KEY_ANIM_TYPE, mAnimationType);
b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail);
b.putInt(KEY_ANIM_START_X, mStartX);

View File

@ -84,7 +84,7 @@ interface IWindowManager
void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
int startHeight);
void overridePendingAppTransitionThumb(in Bitmap srcThumb, int startX, int startY,
IRemoteCallback startedCallback);
IRemoteCallback startedCallback, boolean delayed);
void executeAppTransition();
void setAppStartingWindow(IBinder token, String pkg, int theme,
in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,

View File

@ -27,6 +27,12 @@
systemui:recentItemLayout="@layout/status_bar_recent_item"
>
<ImageView
android:id="@+id/recents_transition_placeholder_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible" />
<FrameLayout
android:id="@+id/recents_bg_protect"
android:background="@drawable/status_bar_recents_background"

View File

@ -27,6 +27,12 @@
systemui:recentItemLayout="@layout/status_bar_recent_item"
>
<ImageView
android:id="@+id/recents_transition_placeholder_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible" />
<FrameLayout
android:id="@+id/recents_bg_protect"
android:background="@drawable/status_bar_recents_background"

View File

@ -39,6 +39,12 @@
android:clipToPadding="false"
android:clipChildren="false">
<ImageView
android:id="@+id/recents_transition_placeholder_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible" />
<com.android.systemui.recent.RecentsVerticalScrollView android:id="@+id/recents_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

View File

@ -21,21 +21,21 @@ import android.animation.AnimatorSet;
import android.animation.AnimatorSet.Builder;
import android.animation.ObjectAnimator;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.util.Slog;
import android.view.View;
import android.view.ViewRootImpl;
/* package */ class Choreographer implements Animator.AnimatorListener {
// should group this into a multi-property animation
private static final int OPEN_DURATION = 136;
private static final int CLOSE_DURATION = 250;
private static final int CLOSE_DURATION = 130;
private static final int SCRIM_DURATION = 400;
private static final String TAG = RecentsPanelView.TAG;
private static final boolean DEBUG = RecentsPanelView.DEBUG;
boolean mVisible;
int mPanelHeight;
View mRootView;
RecentsPanelView mRootView;
View mScrimView;
View mContentView;
View mNoRecentAppsView;
@ -45,7 +45,7 @@ import android.view.View;
// the panel will start to appear this many px from the end
final int HYPERSPACE_OFFRAMP = 200;
public Choreographer(View root, View scrim, View content,
public Choreographer(RecentsPanelView root, View scrim, View content,
View noRecentApps, Animator.AnimatorListener listener) {
mRootView = root;
mScrimView = scrim;
@ -67,7 +67,7 @@ import android.view.View;
end = 0;
} else {
start = y;
end = y + HYPERSPACE_OFFRAMP;
end = y;
}
Animator posAnim = ObjectAnimator.ofFloat(mContentView, "translationY",
@ -77,12 +77,12 @@ import android.view.View;
: new android.view.animation.AccelerateInterpolator(2.5f));
posAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION);
Animator glowAnim = ObjectAnimator.ofFloat(mContentView, "alpha",
Animator fadeAnim = ObjectAnimator.ofFloat(mContentView, "alpha",
mContentView.getAlpha(), appearing ? 1.0f : 0.0f);
glowAnim.setInterpolator(appearing
fadeAnim.setInterpolator(appearing
? new android.view.animation.AccelerateInterpolator(1.0f)
: new android.view.animation.DecelerateInterpolator(1.0f));
glowAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION);
fadeAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION);
Animator noRecentAppsFadeAnim = null;
if (mNoRecentAppsView != null && // doesn't exist on large devices
@ -96,7 +96,7 @@ import android.view.View;
}
mContentAnim = new AnimatorSet();
final Builder builder = mContentAnim.play(glowAnim).with(posAnim);
final Builder builder = mContentAnim.play(fadeAnim).with(posAnim);
if (noRecentAppsFadeAnim != null) {
builder.with(noRecentAppsFadeAnim);
@ -153,9 +153,10 @@ import android.view.View;
public void onAnimationEnd(Animator animation) {
if (DEBUG) Slog.d(TAG, "onAnimationEnd");
if (!mVisible) {
mRootView.setVisibility(View.GONE);
mRootView.hideWindow();
}
mContentView.setLayerType(View.LAYER_TYPE_NONE, null);
mContentView.setAlpha(1f);
mContentAnim = null;
}

View File

@ -26,7 +26,9 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.Shader.TileMode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
@ -48,11 +50,9 @@ import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.PopupMenu;
import android.widget.ScrollView;
import android.widget.TextView;
import com.android.systemui.R;
@ -83,6 +83,9 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener
private Choreographer mChoreo;
OnRecentsPanelVisibilityChangedListener mVisibilityChangedListener;
ImageView mPlaceholderThumbnail;
boolean mHideWindowAfterPlaceholderThumbnailIsHidden;
private RecentTasksLoader mRecentTasksLoader;
private ArrayList<TaskDescription> mRecentTaskDescriptions;
private Runnable mPreloadTasksRunnable;
@ -283,7 +286,9 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener
public void show(boolean show, boolean animate,
ArrayList<TaskDescription> recentTaskDescriptions, boolean firstScreenful) {
// For now, disable animations. We may want to re-enable in the future
animate = false;
if (show) {
animate = false;
}
if (show) {
// Need to update list of recent apps before we set visibility so this view's
// content description is updated before it gets focus for TalkBack mode
@ -687,11 +692,31 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener
context.getSystemService(Context.ACTIVITY_SERVICE);
holder.thumbnailViewImage.setDrawingCacheEnabled(true);
Bitmap bm = holder.thumbnailViewImage.getDrawingCache();
ActivityOptions opts = ActivityOptions.makeThumbnailScaleUpAnimation(
mPlaceholderThumbnail = (ImageView) findViewById(R.id.recents_transition_placeholder_icon);
final ImageView placeholderThumbnail = mPlaceholderThumbnail;
mHideWindowAfterPlaceholderThumbnailIsHidden = false;
placeholderThumbnail.setVisibility(VISIBLE);
Bitmap b2 = bm.copy(bm.getConfig(), true);
placeholderThumbnail.setImageBitmap(b2);
Rect r = new Rect();
holder.thumbnailViewImage.getGlobalVisibleRect(r);
placeholderThumbnail.setTranslationX(r.left);
placeholderThumbnail.setTranslationY(r.top);
show(false, true);
ActivityOptions opts = ActivityOptions.makeDelayedThumbnailScaleUpAnimation(
holder.thumbnailViewImage, bm, 0, 0,
new ActivityOptions.OnAnimationStartedListener() {
@Override public void onAnimationStarted() {
hide(true);
mPlaceholderThumbnail = null;
placeholderThumbnail.setVisibility(INVISIBLE);
if (mHideWindowAfterPlaceholderThumbnailIsHidden) {
hideWindow();
}
}
});
if (ad.taskId >= 0) {
@ -709,6 +734,15 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener
holder.thumbnailViewImage.setDrawingCacheEnabled(false);
}
public void hideWindow() {
if (mPlaceholderThumbnail != null) {
mHideWindowAfterPlaceholderThumbnailIsHidden = true;
} else {
setVisibility(GONE);
mHideWindowAfterPlaceholderThumbnailIsHidden = false;
}
}
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
handleOnClick(view);
}

View File

@ -552,7 +552,8 @@ final class ActivityRecord {
void applyOptionsLocked() {
if (pendingOptions != null) {
switch (pendingOptions.getAnimationType()) {
final int animationType = pendingOptions.getAnimationType();
switch (animationType) {
case ActivityOptions.ANIM_CUSTOM:
service.mWindowManager.overridePendingAppTransition(
pendingOptions.getPackageName(),
@ -571,10 +572,13 @@ final class ActivityRecord {
}
break;
case ActivityOptions.ANIM_THUMBNAIL:
case ActivityOptions.ANIM_THUMBNAIL_DELAYED:
boolean delayed = (animationType == ActivityOptions.ANIM_THUMBNAIL_DELAYED);
service.mWindowManager.overridePendingAppTransitionThumb(
pendingOptions.getThumbnail(),
pendingOptions.getStartX(), pendingOptions.getStartY(),
pendingOptions.getOnAnimationStartListener());
pendingOptions.getOnAnimationStartListener(),
delayed);
if (intent.getSourceBounds() == null) {
intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
pendingOptions.getStartY(),

View File

@ -512,6 +512,7 @@ public class WindowManagerService extends IWindowManager.Stub
int mNextAppTransitionType = ActivityOptions.ANIM_NONE;
String mNextAppTransitionPackage;
Bitmap mNextAppTransitionThumbnail;
boolean mNextAppTransitionDelayed;
IRemoteCallback mNextAppTransitionCallback;
int mNextAppTransitionEnter;
int mNextAppTransitionExit;
@ -3176,7 +3177,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
private Animation createThumbnailAnimationLocked(int transit,
boolean enter, boolean thumb) {
boolean enter, boolean thumb, boolean delayed) {
Animation a;
final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
@ -3186,6 +3187,7 @@ public class WindowManagerService extends IWindowManager.Stub
// it is the standard duration for that. Otherwise we use the longer
// task transition duration.
int duration;
int delayDuration = delayed ? 200 : 0;
switch (transit) {
case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN:
case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE:
@ -3193,7 +3195,7 @@ public class WindowManagerService extends IWindowManager.Stub
com.android.internal.R.integer.config_shortAnimTime);
break;
default:
duration = 300;
duration = delayed ? 200 : 300;
break;
}
if (thumb) {
@ -3201,6 +3203,7 @@ public class WindowManagerService extends IWindowManager.Stub
// filling the screen.
float scaleW = mAppDisplayWidth/thumbWidth;
float scaleH = mAppDisplayHeight/thumbHeight;
Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
computePivot(mNextAppTransitionStartX, 1/scaleW),
computePivot(mNextAppTransitionStartY, 1/scaleH));
@ -3210,17 +3213,38 @@ public class WindowManagerService extends IWindowManager.Stub
set.addAnimation(scale);
alpha.setDuration(duration);
set.addAnimation(alpha);
set.setFillBefore(true);
if (delayDuration > 0) {
set.setStartOffset(delayDuration);
}
a = set;
} else if (enter) {
// Entering app zooms out from the center of the thumbnail.
float scaleW = thumbWidth/mAppDisplayWidth;
float scaleH = thumbHeight/mAppDisplayHeight;
a = new ScaleAnimation(scaleW, 1, scaleH, 1,
float scaleW = thumbWidth / mAppDisplayWidth;
float scaleH = thumbHeight / mAppDisplayHeight;
AnimationSet set = new AnimationSet(true);
Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1,
computePivot(mNextAppTransitionStartX, scaleW),
computePivot(mNextAppTransitionStartY, scaleH));
a.setDuration(duration);
scale.setDuration(duration);
scale.setFillBefore(true);
set.addAnimation(scale);
// Need to set an alpha animation on the entering app window
// in case it appears one frame before the thumbnail window
// (this solves flicker)
Animation alpha = new AlphaAnimation(0, 1);
alpha.setDuration(1);
alpha.setFillAfter(true);
set.addAnimation(alpha);
a = set;
if (delayDuration > 0) {
a.setStartOffset(delayDuration);
}
} else {
a = createExitAnimationLocked(transit, duration);
if (delayDuration > 0) {
a.setStartOffset(delayDuration);
}
}
a.setFillAfter(true);
final Interpolator interpolator = AnimationUtils.loadInterpolator(mContext,
@ -3252,12 +3276,18 @@ public class WindowManagerService extends IWindowManager.Stub
if (DEBUG_ANIM) Slog.v(TAG, "applyAnimation: wtoken=" + wtoken
+ " anim=" + a + " nextAppTransition=ANIM_SCALE_UP"
+ " transit=" + transit + " Callers " + Debug.getCallers(3));
} else if (mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL) {
a = createThumbnailAnimationLocked(transit, enter, false);
} else if (mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL ||
mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL_DELAYED) {
boolean delayed = (mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL_DELAYED);
a = createThumbnailAnimationLocked(transit, enter, false, delayed);
initialized = true;
if (DEBUG_ANIM) Slog.v(TAG, "applyAnimation: wtoken=" + wtoken
+ " anim=" + a + " nextAppTransition=ANIM_THUMBNAIL"
+ " transit=" + transit + " Callers " + Debug.getCallers(3));
if (DEBUG_ANIM) {
String animName = delayed ? "ANIM_THUMBNAIL_DELAYED" : "ANIM_THUMBNAIL";
Slog.v(TAG, "applyAnimation: wtoken=" + wtoken
+ " anim=" + a + " nextAppTransition=" + animName
+ " transit=" + transit + " Callers " + Debug.getCallers(3));
}
} else {
int animAttr = 0;
switch (transit) {
@ -3879,11 +3909,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX,
int startY, IRemoteCallback startedCallback) {
int startY, IRemoteCallback startedCallback, boolean delayed) {
if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
mNextAppTransitionType = ActivityOptions.ANIM_THUMBNAIL;
mNextAppTransitionType =
delayed ? ActivityOptions.ANIM_THUMBNAIL_DELAYED : ActivityOptions.ANIM_THUMBNAIL;
mNextAppTransitionPackage = null;
mNextAppTransitionThumbnail = srcThumb;
mNextAppTransitionDelayed = delayed;
mNextAppTransitionStartX = startX;
mNextAppTransitionStartY = startY;
mNextAppTransitionCallback = startedCallback;
@ -8024,7 +8056,8 @@ public class WindowManagerService extends IWindowManager.Stub
drawSurface.unlockCanvasAndPost(c);
drawSurface.release();
topOpeningApp.mAppAnimator.thumbnailLayer = topOpeningLayer;
Animation anim = createThumbnailAnimationLocked(transit, true, true);
Animation anim = createThumbnailAnimationLocked(
transit, true, true, mNextAppTransitionDelayed);
topOpeningApp.mAppAnimator.thumbnailAnimation = anim;
anim.restrictDuration(MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(mTransitionAnimationScale);
@ -9602,10 +9635,12 @@ public class WindowManagerService extends IWindowManager.Stub
pw.println(mNextAppTransitionStartHeight);
break;
case ActivityOptions.ANIM_THUMBNAIL:
case ActivityOptions.ANIM_THUMBNAIL_DELAYED:
pw.print(" mNextAppTransitionThumbnail=");
pw.print(mNextAppTransitionThumbnail);
pw.print(" mNextAppTransitionStartX="); pw.print(mNextAppTransitionStartX);
pw.print(" mNextAppTransitionStartY="); pw.println(mNextAppTransitionStartY);
pw.print(" mNextAppTransitionDelayed="); pw.println(mNextAppTransitionDelayed);
break;
}
pw.print(" mStartingIconInTransition="); pw.print(mStartingIconInTransition);

View File

@ -240,7 +240,7 @@ public class BridgeWindowManager implements IWindowManager {
@Override
public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY,
IRemoteCallback startedCallback) throws RemoteException {
IRemoteCallback startedCallback, boolean delayed) throws RemoteException {
// TODO Auto-generated method stub
}