Skip to content

Commit 421dceb

Browse files
mikejurkaAndroid (Google) Code Review
authored andcommitted
Merge "Making transition out of recents look better" into jb-dev
2 parents dc44253 + 21385cd commit 421dceb

File tree

10 files changed

+161
-35
lines changed

10 files changed

+161
-35
lines changed

core/java/android/app/ActivityOptions.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ public class ActivityOptions {
9898
public static final int ANIM_SCALE_UP = 2;
9999
/** @hide */
100100
public static final int ANIM_THUMBNAIL = 3;
101+
/** @hide */
102+
public static final int ANIM_THUMBNAIL_DELAYED = 4;
101103

102104
private String mPackageName;
103105
private int mAnimationType = ANIM_NONE;
@@ -219,9 +221,38 @@ public static ActivityOptions makeThumbnailScaleUpAnimation(View source,
219221
*/
220222
public static ActivityOptions makeThumbnailScaleUpAnimation(View source,
221223
Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
224+
return makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY, listener, false);
225+
}
226+
227+
/**
228+
* Create an ActivityOptions specifying an animation where a thumbnail
229+
* is scaled from a given position to the new activity window that is
230+
* being started. Before the animation, there is a short delay.
231+
*
232+
* @param source The View that this thumbnail is animating from. This
233+
* defines the coordinate space for <var>startX</var> and <var>startY</var>.
234+
* @param thumbnail The bitmap that will be shown as the initial thumbnail
235+
* of the animation.
236+
* @param startX The x starting location of the bitmap, relative to <var>source</var>.
237+
* @param startY The y starting location of the bitmap, relative to <var>source</var>.
238+
* @param listener Optional OnAnimationStartedListener to find out when the
239+
* requested animation has started running. If for some reason the animation
240+
* is not executed, the callback will happen immediately.
241+
* @return Returns a new ActivityOptions object that you can use to
242+
* supply these options as the options Bundle when starting an activity.
243+
* @hide
244+
*/
245+
public static ActivityOptions makeDelayedThumbnailScaleUpAnimation(View source,
246+
Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
247+
return makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY, listener, true);
248+
}
249+
250+
private static ActivityOptions makeThumbnailScaleUpAnimation(View source,
251+
Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener,
252+
boolean delayed) {
222253
ActivityOptions opts = new ActivityOptions();
223254
opts.mPackageName = source.getContext().getPackageName();
224-
opts.mAnimationType = ANIM_THUMBNAIL;
255+
opts.mAnimationType = delayed ? ANIM_THUMBNAIL_DELAYED : ANIM_THUMBNAIL;
225256
opts.mThumbnail = thumbnail;
226257
int[] pts = new int[2];
227258
source.getLocationOnScreen(pts);
@@ -258,7 +289,8 @@ public ActivityOptions(Bundle opts) {
258289
mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
259290
mStartWidth = opts.getInt(KEY_ANIM_START_WIDTH, 0);
260291
mStartHeight = opts.getInt(KEY_ANIM_START_HEIGHT, 0);
261-
} else if (mAnimationType == ANIM_THUMBNAIL) {
292+
} else if (mAnimationType == ANIM_THUMBNAIL ||
293+
mAnimationType == ANIM_THUMBNAIL_DELAYED) {
262294
mThumbnail = (Bitmap)opts.getParcelable(KEY_ANIM_THUMBNAIL);
263295
mStartX = opts.getInt(KEY_ANIM_START_X, 0);
264296
mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
@@ -359,6 +391,7 @@ public void update(ActivityOptions otherOptions) {
359391
mStartHeight = otherOptions.mStartHeight;
360392
break;
361393
case ANIM_THUMBNAIL:
394+
case ANIM_THUMBNAIL_DELAYED:
362395
mAnimationType = otherOptions.mAnimationType;
363396
mThumbnail = otherOptions.mThumbnail;
364397
mStartX = otherOptions.mStartX;
@@ -401,6 +434,7 @@ public Bundle toBundle() {
401434
b.putInt(KEY_ANIM_START_HEIGHT, mStartHeight);
402435
break;
403436
case ANIM_THUMBNAIL:
437+
case ANIM_THUMBNAIL_DELAYED:
404438
b.putInt(KEY_ANIM_TYPE, mAnimationType);
405439
b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail);
406440
b.putInt(KEY_ANIM_START_X, mStartX);

core/java/android/view/IWindowManager.aidl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ interface IWindowManager
8484
void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
8585
int startHeight);
8686
void overridePendingAppTransitionThumb(in Bitmap srcThumb, int startX, int startY,
87-
IRemoteCallback startedCallback);
87+
IRemoteCallback startedCallback, boolean delayed);
8888
void executeAppTransition();
8989
void setAppStartingWindow(IBinder token, String pkg, int theme,
9090
in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,

packages/SystemUI/res/layout-land/status_bar_recent_panel.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@
2727
systemui:recentItemLayout="@layout/status_bar_recent_item"
2828
>
2929

30+
<ImageView
31+
android:id="@+id/recents_transition_placeholder_icon"
32+
android:layout_width="wrap_content"
33+
android:layout_height="wrap_content"
34+
android:visibility="invisible" />
35+
3036
<FrameLayout
3137
android:id="@+id/recents_bg_protect"
3238
android:background="@drawable/status_bar_recents_background"

packages/SystemUI/res/layout-port/status_bar_recent_panel.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@
2727
systemui:recentItemLayout="@layout/status_bar_recent_item"
2828
>
2929

30+
<ImageView
31+
android:id="@+id/recents_transition_placeholder_icon"
32+
android:layout_width="wrap_content"
33+
android:layout_height="wrap_content"
34+
android:visibility="invisible" />
35+
3036
<FrameLayout
3137
android:id="@+id/recents_bg_protect"
3238
android:background="@drawable/status_bar_recents_background"

packages/SystemUI/res/layout/system_bar_recent_panel.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@
3939
android:clipToPadding="false"
4040
android:clipChildren="false">
4141

42+
<ImageView
43+
android:id="@+id/recents_transition_placeholder_icon"
44+
android:layout_width="wrap_content"
45+
android:layout_height="wrap_content"
46+
android:visibility="invisible" />
47+
4248
<com.android.systemui.recent.RecentsVerticalScrollView android:id="@+id/recents_container"
4349
android:layout_width="wrap_content"
4450
android:layout_height="wrap_content"

packages/SystemUI/src/com/android/systemui/recent/Choreographer.java

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,21 @@
2121
import android.animation.AnimatorSet.Builder;
2222
import android.animation.ObjectAnimator;
2323
import android.graphics.drawable.Drawable;
24-
import android.util.Log;
2524
import android.util.Slog;
2625
import android.view.View;
26+
import android.view.ViewRootImpl;
2727

2828
/* package */ class Choreographer implements Animator.AnimatorListener {
2929
// should group this into a multi-property animation
3030
private static final int OPEN_DURATION = 136;
31-
private static final int CLOSE_DURATION = 250;
31+
private static final int CLOSE_DURATION = 130;
3232
private static final int SCRIM_DURATION = 400;
3333
private static final String TAG = RecentsPanelView.TAG;
3434
private static final boolean DEBUG = RecentsPanelView.DEBUG;
3535

3636
boolean mVisible;
3737
int mPanelHeight;
38-
View mRootView;
38+
RecentsPanelView mRootView;
3939
View mScrimView;
4040
View mContentView;
4141
View mNoRecentAppsView;
@@ -45,7 +45,7 @@
4545
// the panel will start to appear this many px from the end
4646
final int HYPERSPACE_OFFRAMP = 200;
4747

48-
public Choreographer(View root, View scrim, View content,
48+
public Choreographer(RecentsPanelView root, View scrim, View content,
4949
View noRecentApps, Animator.AnimatorListener listener) {
5050
mRootView = root;
5151
mScrimView = scrim;
@@ -67,7 +67,7 @@ void createAnimation(boolean appearing) {
6767
end = 0;
6868
} else {
6969
start = y;
70-
end = y + HYPERSPACE_OFFRAMP;
70+
end = y;
7171
}
7272

7373
Animator posAnim = ObjectAnimator.ofFloat(mContentView, "translationY",
@@ -77,12 +77,12 @@ void createAnimation(boolean appearing) {
7777
: new android.view.animation.AccelerateInterpolator(2.5f));
7878
posAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION);
7979

80-
Animator glowAnim = ObjectAnimator.ofFloat(mContentView, "alpha",
80+
Animator fadeAnim = ObjectAnimator.ofFloat(mContentView, "alpha",
8181
mContentView.getAlpha(), appearing ? 1.0f : 0.0f);
82-
glowAnim.setInterpolator(appearing
82+
fadeAnim.setInterpolator(appearing
8383
? new android.view.animation.AccelerateInterpolator(1.0f)
8484
: new android.view.animation.DecelerateInterpolator(1.0f));
85-
glowAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION);
85+
fadeAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION);
8686

8787
Animator noRecentAppsFadeAnim = null;
8888
if (mNoRecentAppsView != null && // doesn't exist on large devices
@@ -96,7 +96,7 @@ void createAnimation(boolean appearing) {
9696
}
9797

9898
mContentAnim = new AnimatorSet();
99-
final Builder builder = mContentAnim.play(glowAnim).with(posAnim);
99+
final Builder builder = mContentAnim.play(fadeAnim).with(posAnim);
100100

101101
if (noRecentAppsFadeAnim != null) {
102102
builder.with(noRecentAppsFadeAnim);
@@ -153,9 +153,10 @@ public void onAnimationCancel(Animator animation) {
153153
public void onAnimationEnd(Animator animation) {
154154
if (DEBUG) Slog.d(TAG, "onAnimationEnd");
155155
if (!mVisible) {
156-
mRootView.setVisibility(View.GONE);
156+
mRootView.hideWindow();
157157
}
158158
mContentView.setLayerType(View.LAYER_TYPE_NONE, null);
159+
mContentView.setAlpha(1f);
159160
mContentAnim = null;
160161
}
161162

packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
import android.content.res.Resources;
2727
import android.content.res.TypedArray;
2828
import android.graphics.Bitmap;
29+
import android.graphics.Canvas;
2930
import android.graphics.Matrix;
31+
import android.graphics.Rect;
3032
import android.graphics.Shader.TileMode;
3133
import android.graphics.drawable.BitmapDrawable;
3234
import android.graphics.drawable.Drawable;
@@ -48,11 +50,9 @@
4850
import android.widget.AdapterView.OnItemClickListener;
4951
import android.widget.BaseAdapter;
5052
import android.widget.FrameLayout;
51-
import android.widget.HorizontalScrollView;
5253
import android.widget.ImageView;
5354
import android.widget.ImageView.ScaleType;
5455
import android.widget.PopupMenu;
55-
import android.widget.ScrollView;
5656
import android.widget.TextView;
5757

5858
import com.android.systemui.R;
@@ -83,6 +83,9 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener
8383
private Choreographer mChoreo;
8484
OnRecentsPanelVisibilityChangedListener mVisibilityChangedListener;
8585

86+
ImageView mPlaceholderThumbnail;
87+
boolean mHideWindowAfterPlaceholderThumbnailIsHidden;
88+
8689
private RecentTasksLoader mRecentTasksLoader;
8790
private ArrayList<TaskDescription> mRecentTaskDescriptions;
8891
private Runnable mPreloadTasksRunnable;
@@ -283,7 +286,9 @@ private void showIfReady() {
283286
public void show(boolean show, boolean animate,
284287
ArrayList<TaskDescription> recentTaskDescriptions, boolean firstScreenful) {
285288
// For now, disable animations. We may want to re-enable in the future
286-
animate = false;
289+
if (show) {
290+
animate = false;
291+
}
287292
if (show) {
288293
// Need to update list of recent apps before we set visibility so this view's
289294
// content description is updated before it gets focus for TalkBack mode
@@ -687,11 +692,31 @@ public void handleOnClick(View view) {
687692
context.getSystemService(Context.ACTIVITY_SERVICE);
688693
holder.thumbnailViewImage.setDrawingCacheEnabled(true);
689694
Bitmap bm = holder.thumbnailViewImage.getDrawingCache();
690-
ActivityOptions opts = ActivityOptions.makeThumbnailScaleUpAnimation(
695+
mPlaceholderThumbnail = (ImageView) findViewById(R.id.recents_transition_placeholder_icon);
696+
697+
final ImageView placeholderThumbnail = mPlaceholderThumbnail;
698+
mHideWindowAfterPlaceholderThumbnailIsHidden = false;
699+
placeholderThumbnail.setVisibility(VISIBLE);
700+
Bitmap b2 = bm.copy(bm.getConfig(), true);
701+
placeholderThumbnail.setImageBitmap(b2);
702+
703+
Rect r = new Rect();
704+
holder.thumbnailViewImage.getGlobalVisibleRect(r);
705+
706+
placeholderThumbnail.setTranslationX(r.left);
707+
placeholderThumbnail.setTranslationY(r.top);
708+
709+
show(false, true);
710+
711+
ActivityOptions opts = ActivityOptions.makeDelayedThumbnailScaleUpAnimation(
691712
holder.thumbnailViewImage, bm, 0, 0,
692713
new ActivityOptions.OnAnimationStartedListener() {
693714
@Override public void onAnimationStarted() {
694-
hide(true);
715+
mPlaceholderThumbnail = null;
716+
placeholderThumbnail.setVisibility(INVISIBLE);
717+
if (mHideWindowAfterPlaceholderThumbnailIsHidden) {
718+
hideWindow();
719+
}
695720
}
696721
});
697722
if (ad.taskId >= 0) {
@@ -709,6 +734,15 @@ public void handleOnClick(View view) {
709734
holder.thumbnailViewImage.setDrawingCacheEnabled(false);
710735
}
711736

737+
public void hideWindow() {
738+
if (mPlaceholderThumbnail != null) {
739+
mHideWindowAfterPlaceholderThumbnailIsHidden = true;
740+
} else {
741+
setVisibility(GONE);
742+
mHideWindowAfterPlaceholderThumbnailIsHidden = false;
743+
}
744+
}
745+
712746
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
713747
handleOnClick(view);
714748
}

services/java/com/android/server/am/ActivityRecord.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,8 @@ void updateOptionsLocked(Bundle options) {
552552

553553
void applyOptionsLocked() {
554554
if (pendingOptions != null) {
555-
switch (pendingOptions.getAnimationType()) {
555+
final int animationType = pendingOptions.getAnimationType();
556+
switch (animationType) {
556557
case ActivityOptions.ANIM_CUSTOM:
557558
service.mWindowManager.overridePendingAppTransition(
558559
pendingOptions.getPackageName(),
@@ -571,10 +572,13 @@ void applyOptionsLocked() {
571572
}
572573
break;
573574
case ActivityOptions.ANIM_THUMBNAIL:
575+
case ActivityOptions.ANIM_THUMBNAIL_DELAYED:
576+
boolean delayed = (animationType == ActivityOptions.ANIM_THUMBNAIL_DELAYED);
574577
service.mWindowManager.overridePendingAppTransitionThumb(
575578
pendingOptions.getThumbnail(),
576579
pendingOptions.getStartX(), pendingOptions.getStartY(),
577-
pendingOptions.getOnAnimationStartListener());
580+
pendingOptions.getOnAnimationStartListener(),
581+
delayed);
578582
if (intent.getSourceBounds() == null) {
579583
intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
580584
pendingOptions.getStartY(),

0 commit comments

Comments
 (0)