diff options
author | Robert Carr <racarr@google.com> | 2016-06-29 16:41:50 -0700 |
---|---|---|
committer | Robert Carr <racarr@google.com> | 2016-07-01 11:05:40 -0700 |
commit | fd10cd1989966d01011a0cf75f3282f3e12ca5a6 (patch) | |
tree | fb8afc89c8fa0e814e394b3e69173559ecb81b79 | |
parent | ef5c3aad4df40913d62de4fd9b12e2c7a9bacdf5 (diff) |
Force CROSSFADE rotation when launching from double tap gesture.
When activity transition triggers a rotation change, the starting
window will normally be the top window at the time we try
to select the window animation. However, these layout params won't
have the apps rotation animation set (as the client code will set that
on the real window, not the starting window). Eventually we would
like to add API to specify rotation animation via manifest to solve
this problem cleanly. In the mean time, we can force a specific rotation
animation from the double tap gesture, and clean up some camera
ugliness. We accomplish this by attaching an animation hint to
ActivityOptions.
Bug: 28838855
Change-Id: If052cd8cbae76651da43f3b4c590cd9dcc1afc0f
13 files changed, 82 insertions, 9 deletions
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index ccc37d72e846..d9a46903ee38 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -193,6 +193,7 @@ public class ActivityOptions { = "android:activity.exitCoordinatorIndex"; private static final String KEY_USAGE_TIME_REPORT = "android:activity.usageTimeReport"; + private static final String KEY_ROTATION_ANIMATION_HINT = "android:activity.rotationAnimationHint"; /** @hide */ public static final int ANIM_NONE = 0; @@ -244,6 +245,7 @@ public class ActivityOptions { private int mDockCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; private boolean mTaskOverlay; private AppTransitionAnimationSpec mAnimSpecs[]; + private int mRotationAnimationHint = -1; /** * Create an ActivityOptions specifying a custom animation to run when @@ -863,6 +865,7 @@ public class ActivityOptions { mAnimationFinishedListener = IRemoteCallback.Stub.asInterface( opts.getBinder(KEY_ANIMATION_FINISHED_LISTENER)); } + mRotationAnimationHint = opts.getInt(KEY_ROTATION_ANIMATION_HINT); } /** @@ -1216,6 +1219,7 @@ public class ActivityOptions { if (mAnimationFinishedListener != null) { b.putBinder(KEY_ANIMATION_FINISHED_LISTENER, mAnimationFinishedListener.asBinder()); } + b.putInt(KEY_ROTATION_ANIMATION_HINT, mRotationAnimationHint); return b; } @@ -1262,6 +1266,27 @@ public class ActivityOptions { return null; } + /** + * Returns the rotation animation set by {@link setRotationAnimationHint} or -1 + * if unspecified. + * @hide + */ + public int getRotationAnimationHint() { + return mRotationAnimationHint; + } + + + /** + * Set a rotation animation to be used if launching the activity + * triggers an orientation change, or -1 to clear. See + * {@link android.view.WindowManager.LayoutParams} for rotation + * animation values. + * @hide + */ + public void setRotationAnimationHint(int hint) { + mRotationAnimationHint = hint; + } + /** @hide */ @Override public String toString() { diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 72126d0329a3..e3ff54d40316 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -112,7 +112,7 @@ interface IWindowManager int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId, int configChanges, boolean voiceInteraction, boolean launchTaskBehind, in Rect taskBounds, in Configuration configuration, int taskResizeMode, - boolean alwaysFocusable, boolean homeTask, int targetSdkVersion); + boolean alwaysFocusable, boolean homeTask, int targetSdkVersion, int rotationAnimationHint); /** * * @param token The token we are adding to the input task Id. diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index a8afaf20a1c5..9a8c8a86499a 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -416,6 +416,8 @@ public interface WindowManagerPolicy { * screen with other application windows. */ public boolean isInMultiWindowMode(); + + public int getRotationAnimationHint(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index 772c76642b9e..a98601a33300 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone; import android.app.ActivityManager; import android.app.ActivityManagerNative; +import android.app.ActivityOptions; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -44,6 +45,7 @@ import android.util.Log; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; +import android.view.WindowManager; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; import android.widget.TextView; @@ -448,12 +450,24 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL @Override public void run() { int result = ActivityManager.START_CANCELED; + + // Normally an activity will set it's requested rotation + // animation on its window. However when launching an activity + // causes the orientation to change this is too late. In these cases + // the default animation is used. This doesn't look good for + // the camera (as it rotates the camera contents out of sync + // with physical reality). So, we ask the WindowManager to + // force the crossfade animation if an orientation change + // happens to occur during the launch. + ActivityOptions o = ActivityOptions.makeBasic(); + o.setRotationAnimationHint( + WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE); try { result = ActivityManagerNative.getDefault().startActivityAsUser( null, getContext().getBasePackageName(), intent, intent.resolveTypeIfNeeded(getContext().getContentResolver()), - null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, null, + null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, o.toBundle(), UserHandle.CURRENT.getIdentifier()); } catch (RemoteException e) { Log.w(TAG, "Unable to start camera activity", e); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 4de769db61c6..fcb1da0033bb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -22,6 +22,7 @@ import android.animation.AnimatorListenerAdapter; import android.annotation.NonNull; import android.app.ActivityManager; import android.app.ActivityManagerNative; +import android.app.ActivityOptions; import android.app.IActivityManager; import android.app.Notification; import android.app.PendingIntent; @@ -3322,13 +3323,26 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, intent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); int result = ActivityManager.START_CANCELED; + ActivityOptions options = new ActivityOptions(getActivityOptions()); + if (intent == KeyguardBottomAreaView.INSECURE_CAMERA_INTENT) { + // Normally an activity will set it's requested rotation + // animation on its window. However when launching an activity + // causes the orientation to change this is too late. In these cases + // the default animation is used. This doesn't look good for + // the camera (as it rotates the camera contents out of sync + // with physical reality). So, we ask the WindowManager to + // force the crossfade animation if an orientation change + // happens to occur during the launch. + options.setRotationAnimationHint( + WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE); + } try { result = ActivityManagerNative.getDefault().startActivityAsUser( null, mContext.getBasePackageName(), intent, intent.resolveTypeIfNeeded(mContext.getContentResolver()), null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, - getActivityOptions(), UserHandle.CURRENT.getIdentifier()); + options.toBundle(), UserHandle.CURRENT.getIdentifier()); } catch (RemoteException e) { Log.w(TAG, "Unable to start activity", e); } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 50b6c0c7502f..6e40cfff5411 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -221,6 +221,8 @@ final class ActivityRecord { boolean pendingVoiceInteractionStart; // Waiting for activity-invoked voice session IVoiceInteractionSession voiceSession; // Voice interaction session for this activity + int mRotationAnimationHint; + private static String startingWindowStateToString(int state) { switch (state) { case STARTING_WINDOW_NOT_SHOWN: @@ -635,6 +637,7 @@ final class ActivityRecord { if (options != null) { pendingOptions = options; mLaunchTaskBehind = pendingOptions.getLaunchTaskBehind(); + mRotationAnimationHint = pendingOptions.getRotationAnimationHint(); PendingIntent usageReport = pendingOptions.getUsageTimeReport(); if (usageReport != null) { appTimeTracker = new AppTimeTracker(usageReport); diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 6d229466ebf9..fc649a1ea140 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -5199,7 +5199,7 @@ final class ActivityStack { (r.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId, r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind, bounds, task.mOverrideConfig, task.mResizeMode, r.isAlwaysFocusable(), task.isHomeTask(), - r.appInfo.targetSdkVersion); + r.appInfo.targetSdkVersion, r.mRotationAnimationHint); r.taskConfigOverride = task.mOverrideConfig; } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 22521b916ba8..0e732271403b 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -2874,8 +2874,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { + mTopFullscreenOpaqueWindowState + " rotationAnimation=" + (mTopFullscreenOpaqueWindowState == null ? "0" : mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation)); - if (mTopFullscreenOpaqueWindowState != null && mTopIsFullscreen) { - switch (mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation) { + if (mTopFullscreenOpaqueWindowState != null) { + int animationHint = mTopFullscreenOpaqueWindowState.getRotationAnimationHint(); + if (animationHint < 0 && mTopIsFullscreen) { + animationHint = mTopFullscreenOpaqueWindowState.getAttrs().rotationAnimation; + } + switch (animationHint) { case ROTATION_ANIMATION_CROSSFADE: anim[0] = R.anim.rotation_animation_xfade_exit; anim[1] = R.anim.rotation_animation_enter; diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index edcc32e5b587..776a1753aa72 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -132,6 +132,7 @@ class AppWindowToken extends WindowToken { boolean mAlwaysFocusable; boolean mAppStopped; + int mRotationAnimationHint; int mPendingRelaunchCount; ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>(); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index b8accf547686..59ddab08c4c8 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -3429,7 +3429,7 @@ public class WindowManagerService extends IWindowManager.Stub int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int userId, int configChanges, boolean voiceInteraction, boolean launchTaskBehind, Rect taskBounds, Configuration config, int taskResizeMode, boolean alwaysFocusable, - boolean homeTask, int targetSdkVersion) { + boolean homeTask, int targetSdkVersion, int rotationAnimationHint) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "addAppToken()")) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); @@ -3467,6 +3467,7 @@ public class WindowManagerService extends IWindowManager.Stub atoken.mAlwaysFocusable = alwaysFocusable; if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addAppToken: " + atoken + " to stack=" + stackId + " task=" + taskId + " at " + addPos); + atoken.mRotationAnimationHint = rotationAnimationHint; Task task = mTaskIdToTask.get(taskId); if (task == null) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 99fec7bdb791..1ac3d44c7a45 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2908,4 +2908,12 @@ final class WindowState implements WindowManagerPolicy.WindowState { boolean shouldBeReplacedWithChildren() { return isChildWindow() || mAttrs.type == TYPE_APPLICATION; } + + public int getRotationAnimationHint() { + if (mAppToken != null) { + return mAppToken.mRotationAnimationHint; + } else { + return -1; + } + } } diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java index a726a15f7cc2..063dd8631565 100644 --- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java @@ -93,7 +93,7 @@ public class WindowManagerPermissionTests extends TestCase { try { mWm.addAppToken(0, null, 0, 0, 0, false, false, 0, 0, false, false, null, - Configuration.EMPTY, 0, false, false, 0); + Configuration.EMPTY, 0, false, false, 0, -1); fail("IWindowManager.addAppToken did not throw SecurityException as" + " expected"); } catch (SecurityException e) { diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java index 04a59bcfb5ae..4e4da8bb3f08 100644 --- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java +++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java @@ -78,7 +78,8 @@ public class IWindowManagerImpl implements IWindowManager { @Override public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, int arg4, boolean arg5, boolean arg6, int arg7, int arg8, boolean arg9, boolean arg10, - Rect arg11, Configuration arg12, int arg13, boolean arg14, boolean arg15, int arg16) + Rect arg11, Configuration arg12, int arg13, boolean arg14, boolean arg15, int arg16, + int arg17) throws RemoteException { // TODO Auto-generated method stub } |