Cross profile apps animation part 2
Show and animate work/personal thumbnail during the transition.
Bug: 70799822
Test: Observe the animation in both normal mode / multi-window mode.
Test: go/wm-smoke
Change-Id: I9d5be689ce8dc9ce5c8197263d35514c68ab6f72
diff --git a/core/res/res/anim/cross_profile_apps_thumbnail_enter.xml b/core/res/res/anim/cross_profile_apps_thumbnail_enter.xml
new file mode 100644
index 0000000..3254ebb
--- /dev/null
+++ b/core/res/res/anim/cross_profile_apps_thumbnail_enter.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2018, 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.
+*/
+-->
+<!-- This should be kept in sync with task_open_enter.xml -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false" android:zAdjustment="top">
+
+ <alpha android:fromAlpha="0" android:toAlpha="1.0"
+ android:startOffset="300"
+ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@interpolator/decelerate_quart"
+ android:duration="167"/>
+
+ <translate android:fromYDelta="110%" android:toYDelta="0"
+ android:startOffset="300"
+ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:duration="417"/>
+
+ <!-- To keep the thumbnail around longer -->
+ <alpha android:fromAlpha="1.0" android:toAlpha="0"
+ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:startOffset="717"
+ android:duration="200"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/task_open_enter.xml b/core/res/res/anim/task_open_enter.xml
index e511cc9..b73e14f 100644
--- a/core/res/res/anim/task_open_enter.xml
+++ b/core/res/res/anim/task_open_enter.xml
@@ -16,7 +16,7 @@
** limitations under the License.
*/
-->
-
+<!-- This should in sync with task_open_enter_cross_profile_apps.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false" android:zAdjustment="top">
diff --git a/core/res/res/anim/task_open_enter_cross_profile_apps.xml b/core/res/res/anim/task_open_enter_cross_profile_apps.xml
new file mode 100644
index 0000000..ad89fde
--- /dev/null
+++ b/core/res/res/anim/task_open_enter_cross_profile_apps.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2018, 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.
+*/
+-->
+<!-- This should in sync with task_open_enter.xml -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false" android:zAdjustment="top">
+
+ <alpha android:fromAlpha="0" android:toAlpha="1.0"
+ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@interpolator/decelerate_quart"
+ android:startOffset="300"
+ android:duration="167"/>
+
+ <translate android:fromYDelta="110%" android:toYDelta="0"
+ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:startOffset="300"
+ android:duration="417"/>
+
+ <!-- To keep the transition around longer for the thumbnail, should be kept in sync with
+ cross_profile_apps_thumbmail.xml -->
+ <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+ android:startOffset="717"
+ android:duration="200"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index a659b37..6c1661c 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -631,4 +631,8 @@
<!-- Default dialog corner radius -->
<dimen name="dialog_corner_radius">2dp</dimen>
+
+ <!-- Size of thumbnail used in the cross profile apps animation -->
+ <dimen name="cross_profile_apps_thumbnail_size">72dp</dimen>
+
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4343ba0..9f582ad 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1578,8 +1578,10 @@
<java-symbol type="anim" name="voice_activity_close_enter" />
<java-symbol type="anim" name="voice_activity_open_exit" />
<java-symbol type="anim" name="voice_activity_open_enter" />
- <java-symbol type="anim" name="activity_open_exit" />
- <java-symbol type="anim" name="activity_open_enter" />
+ <java-symbol type="anim" name="task_open_exit" />
+ <java-symbol type="anim" name="task_open_enter" />
+ <java-symbol type="anim" name="cross_profile_apps_thumbnail_enter" />
+ <java-symbol type="anim" name="task_open_enter_cross_profile_apps" />
<java-symbol type="array" name="config_autoRotationTiltTolerance" />
<java-symbol type="array" name="config_keyboardTapVibePattern" />
@@ -1726,6 +1728,7 @@
<java-symbol type="style" name="Theme.ExpandedMenu" />
<java-symbol type="string" name="forward_intent_to_owner" />
<java-symbol type="string" name="forward_intent_to_work" />
+ <java-symbol type="dimen" name="cross_profile_apps_thumbnail_size" />
<!-- From services -->
<java-symbol type="anim" name="screen_rotate_0_enter" />
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 2ac7583..d4b437a 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -49,14 +49,17 @@
import static com.android.server.wm.proto.AppTransitionProto.APP_TRANSITION_STATE;
import static com.android.server.wm.proto.AppTransitionProto.LAST_USED_APP_TRANSITION;
+import android.annotation.DrawableRes;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Color;
import android.graphics.GraphicBuffer;
import android.graphics.Path;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.os.Binder;
import android.os.Debug;
import android.os.IBinder;
@@ -70,7 +73,10 @@
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import android.view.AppTransitionAnimationSpec;
+import android.view.DisplayListCanvas;
import android.view.IAppTransitionAnimationSpecsFuture;
+import android.view.RenderNode;
+import android.view.ThreadedRenderer;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
@@ -391,6 +397,11 @@
mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN;
}
+
+ boolean isNextAppTransitionOpenCrossProfileApps() {
+ return mNextAppTransitionType == NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS;
+ }
+
/**
* @return true if and only if we are currently fetching app transition specs from the future
* passed into {@link #overridePendingAppTransitionMultiThumbFuture}
@@ -978,6 +989,43 @@
}
/**
+ * Creates an overlay with a background color and a thumbnail for the cross profile apps
+ * animation.
+ */
+ GraphicBuffer createCrossProfileAppsThumbnail(
+ @DrawableRes int thumbnailDrawableRes, Rect frame) {
+ final int width = frame.width();
+ final int height = frame.height();
+
+ final RenderNode node = RenderNode.create("CrossProfileAppsThumbnail", null);
+ node.setLeftTopRightBottom(0, 0, width, height);
+ node.setClipToBounds(false);
+
+ final DisplayListCanvas canvas = node.start(width, height);
+ canvas.drawColor(Color.argb(0.6f, 0, 0, 0));
+ final int thumbnailSize = mService.mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.cross_profile_apps_thumbnail_size);
+ final Drawable drawable = mService.mContext.getDrawable(thumbnailDrawableRes);
+ drawable.setBounds(
+ (width - thumbnailSize) / 2,
+ (height - thumbnailSize) / 2,
+ (width + thumbnailSize) / 2,
+ (height + thumbnailSize) / 2);
+ drawable.draw(canvas);
+ node.end(canvas);
+
+ return ThreadedRenderer.createHardwareBitmap(node, width, height)
+ .createGraphicBufferHandle();
+ }
+
+ Animation createCrossProfileAppsThumbnailAnimationLocked(Rect appRect) {
+ final Animation animation = loadAnimationRes(
+ "android", com.android.internal.R.anim.cross_profile_apps_thumbnail_enter);
+ return prepareThumbnailAnimationWithDuration(animation, appRect.width(),
+ appRect.height(), 0, null);
+ }
+
+ /**
* This animation runs for the thumbnail that gets cross faded with the enter/exit activity
* when a thumbnail is specified with the pending animation override.
*/
@@ -1624,9 +1672,10 @@
&& (transit == TRANSIT_ACTIVITY_OPEN
|| transit == TRANSIT_TASK_OPEN
|| transit == TRANSIT_TASK_TO_FRONT)) {
+
a = loadAnimationRes("android", enter
- ? com.android.internal.R.anim.activity_open_enter
- : com.android.internal.R.anim.activity_open_exit);
+ ? com.android.internal.R.anim.task_open_enter_cross_profile_apps
+ : com.android.internal.R.anim.task_open_exit);
Slog.v(TAG,
"applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS:"
+ " anim=" + a + " transit=" + appTransitionToString(transit)
@@ -2007,6 +2056,8 @@
return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP";
case NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN:
return "NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN";
+ case NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS:
+ return "NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS";
default:
return "unknown type=" + mNextAppTransitionType;
}
diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
index c16a531..f1b3e54 100644
--- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java
+++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
@@ -84,10 +84,14 @@
}
void startAnimation(Transaction t, Animation anim) {
+ startAnimation(t, anim, null /* position */);
+ }
+
+ void startAnimation(Transaction t, Animation anim, Point position) {
anim.restrictDuration(MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(mAppToken.mService.getTransitionAnimationScaleLocked());
mSurfaceAnimator.startAnimation(t, new LocalAnimationAdapter(
- new WindowAnimationSpec(anim, null /* position */,
+ new WindowAnimationSpec(anim, position,
mAppToken.mService.mAppTransition.canSkipFirstFrame()),
mAppToken.mService.mSurfaceAnimationRunner), false /* hidden */);
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 01e925e..fc0564d 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
@@ -31,6 +30,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
@@ -56,7 +56,6 @@
import android.annotation.CallSuper;
import android.app.Activity;
-import android.app.WindowConfiguration.WindowingMode;
import android.content.res.Configuration;
import android.graphics.GraphicBuffer;
import android.graphics.Point;
@@ -69,13 +68,14 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
-import android.view.SurfaceControl.Transaction;
-import android.view.animation.Animation;
import android.view.IApplicationToken;
import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
+import android.view.animation.Animation;
+import com.android.internal.R;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.input.InputApplicationHandle;
import com.android.server.policy.WindowManagerPolicy.StartingSurface;
@@ -1775,6 +1775,37 @@
mThumbnail.startAnimation(getPendingTransaction(), loadThumbnailAnimation(thumbnailHeader));
}
+ /**
+ * Attaches a surface with a thumbnail for the
+ * {@link android.app.ActivityOptions#ANIM_OPEN_CROSS_PROFILE_APPS} animation.
+ */
+ void attachCrossProfileAppsThumbnailAnimation() {
+ if (!isReallyAnimating()) {
+ return;
+ }
+ clearThumbnail();
+
+ final WindowState win = findMainWindow();
+ if (win == null) {
+ return;
+ }
+ final Rect frame = win.mFrame;
+ final int thumbnailDrawableRes = getTask().mUserId == mService.mCurrentUserId
+ ? R.drawable.ic_account_circle
+ : R.drawable.ic_corp_badge_no_background;
+ final GraphicBuffer thumbnail =
+ mService.mAppTransition
+ .createCrossProfileAppsThumbnail(thumbnailDrawableRes, frame);
+ if (thumbnail == null) {
+ return;
+ }
+ mThumbnail = new AppWindowThumbnail(getPendingTransaction(), this, thumbnail);
+ final Animation animation =
+ mService.mAppTransition.createCrossProfileAppsThumbnailAnimationLocked(win.mFrame);
+ mThumbnail.startAnimation(getPendingTransaction(), animation, new Point(frame.left,
+ frame.top));
+ }
+
private Animation loadThumbnailAnimation(GraphicBuffer thumbnailHeader) {
final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 08f49f6..a512fdf 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -20,6 +20,7 @@
import static android.app.ActivityManagerInternal.APP_TRANSITION_SNAPSHOT;
import static android.app.ActivityManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
import static android.app.ActivityManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
+
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_CLOSE;
@@ -43,28 +44,19 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
import static com.android.server.wm.WindowManagerService.H.REPORT_WINDOWS_CHANGE;
import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
-import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
-import android.content.res.Configuration;
-import android.graphics.GraphicBuffer;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.Binder;
import android.os.Debug;
import android.os.Trace;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseIntArray;
import android.view.Display;
-import android.view.DisplayInfo;
-import android.view.Surface;
import android.view.SurfaceControl;
import android.view.WindowManager.LayoutParams;
import android.view.animation.Animation;
@@ -414,7 +406,6 @@
}
wtoken.updateReportedVisibilityLocked();
wtoken.waitingToShow = false;
-
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
">>> OPEN TRANSACTION handleAppTransitionReadyLocked()");
mService.openSurfaceTransaction();
@@ -435,6 +426,8 @@
}
if (mService.mAppTransition.isNextAppTransitionThumbnailUp()) {
wtoken.attachThumbnailAnimation();
+ } else if (mService.mAppTransition.isNextAppTransitionOpenCrossProfileApps()) {
+ wtoken.attachCrossProfileAppsThumbnailAnimation();
}
}
return topOpeningApp;