diff options
9 files changed, 117 insertions, 50 deletions
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index fdea1ba157f4..cd9dd97468b3 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -16,6 +16,8 @@ package android.view; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY; + import android.Manifest; import android.animation.LayoutTransition; import android.app.ActivityManagerNative; @@ -37,7 +39,6 @@ import android.graphics.Region; import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; -import android.hardware.input.InputManager; import android.media.AudioManager; import android.os.Binder; import android.os.Build; @@ -74,7 +75,6 @@ import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.Interpolator; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; -import android.view.WindowCallbacks; import android.widget.Scroller; import com.android.internal.R; @@ -176,6 +176,11 @@ public final class ViewRootImpl implements ViewParent, int mViewVisibility; boolean mAppVisible = true; + // For recents to freeform transition we need to keep drawing after the app receives information + // that it became invisible. This will ignore that information and depend on the decor view + // visibility to control drawing. The decor view visibility will get adjusted when the app get + // stopped and that's when the app will stop drawing further frames. + private boolean mForceDecorViewVisibility = false; int mOrigWindowType = -1; /** Whether the window had focus during the most recent traversal. */ @@ -561,6 +566,8 @@ public final class ViewRootImpl implements ViewParent, & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { mInputChannel = new InputChannel(); } + mForceDecorViewVisibility = (mWindowAttributes.privateFlags + & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0; try { mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; @@ -1063,7 +1070,7 @@ public final class ViewRootImpl implements ViewParent, } int getHostVisibility() { - return mAppVisible ? mView.getVisibility() : View.GONE; + return (mAppVisible || mForceDecorViewVisibility) ? mView.getVisibility() : View.GONE; } /** @@ -1568,7 +1575,7 @@ public final class ViewRootImpl implements ViewParent, boolean insetsPending = false; int relayoutResult = 0; - boolean isViewVisible = viewVisibility == View.VISIBLE; + final boolean isViewVisible = viewVisibility == View.VISIBLE; if (mFirst || windowShouldResize || insetsChanged || viewVisibilityChanged || params != null) { @@ -2471,8 +2478,7 @@ public final class ViewRootImpl implements ViewParent, if (callbacks != null) { for (SurfaceHolder.Callback c : callbacks) { if (c instanceof SurfaceHolder.Callback2) { - ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded( - mSurfaceHolder); + ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(mSurfaceHolder); } } } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index edf4297afdea..1521f2ed07fe 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -118,8 +118,7 @@ public interface WindowManager extends ViewManager { */ public void removeViewImmediate(View view); - public static class LayoutParams extends ViewGroup.LayoutParams - implements Parcelable { + public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable { /** * X position for this window. With the default gravity it is ignored. * When using {@link Gravity#LEFT} or {@link Gravity#START} or {@link Gravity#RIGHT} or @@ -1149,13 +1148,21 @@ public interface WindowManager extends ViewManager { /** * Flag indicating that the x, y, width, and height members should be - * ignored (and thus their previous value preserved). For example + * ignored (and thus their previous value preserved). For example * because they are being managed externally through repositionChild. * * {@hide} */ public static final int PRIVATE_FLAG_PRESERVE_GEOMETRY = 0x00002000; + /** + * Flag that will make window ignore app visibility and instead depend purely on the decor + * view visibility for determining window visibility. This is used by recents to keep + * drawing after it launches an app. + * @hide + */ + public static final int PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY = 0x00004000; + /** * Control flags that are private to the platform. diff --git a/packages/SystemUI/src/com/android/systemui/recents/ExitRecentsWindowFirstAnimationFrameEvent.java b/packages/SystemUI/src/com/android/systemui/recents/ExitRecentsWindowFirstAnimationFrameEvent.java new file mode 100644 index 000000000000..8ae8c53a9197 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/ExitRecentsWindowFirstAnimationFrameEvent.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2015 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 com.android.systemui.recents; + +import com.android.systemui.recents.events.EventBus; + +/** + * Event sent when the exit animation is started. + * + * This is sent so parts of UI can synchronize on this event and adjust their appearance. An example + * of that is hiding the tasks when the launched application window becomes visible. + */ +public class ExitRecentsWindowFirstAnimationFrameEvent extends EventBus.Event { +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 3ae8827b3fc6..1e46e6f54245 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -37,6 +37,7 @@ import android.view.KeyEvent; import android.view.View; import android.view.ViewStub; import android.view.ViewTreeObserver; +import android.view.WindowManager; import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.recents.events.EventBus; @@ -352,6 +353,8 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); mEmptyViewStub = (ViewStub) findViewById(R.id.empty_view_stub); mScrimViews = new SystemBarScrimViews(this); + getWindow().getAttributes().privateFlags |= + WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY; // Create the home intent runnable Intent homeIntent = new Intent(Intent.ACTION_MAIN, null); @@ -648,6 +651,11 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView mRecentsView.getViewTreeObserver().addOnPreDrawListener(this); } + public final void onBusEvent(ExitRecentsWindowFirstAnimationFrameEvent event) { + mRecentsView.setStackViewVisibility(View.INVISIBLE); + mRecentsView.getViewTreeObserver().addOnPreDrawListener(this); + } + public final void onBusEvent(CancelEnterRecentsWindowAnimationEvent event) { RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); int launchToTaskId = launchState.launchedToTaskId; diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 50aa2f746f23..b491f943734d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -68,8 +68,8 @@ import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; * An implementation of the Recents component for the current user. For secondary users, this can * be called remotely from the system user. */ -public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub - implements ActivityOptions.OnAnimationFinishedListener { +public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub implements + ActivityOptions.OnAnimationFinishedListener { private final static String TAG = "RecentsImpl"; private final static boolean DEBUG = false; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java index 85b8fcfb4a7f..b2f716d2add5 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java @@ -27,12 +27,12 @@ import android.os.Handler; import android.os.IRemoteCallback; import android.os.RemoteException; import android.util.Log; -import android.util.SparseArray; import android.view.AppTransitionAnimationSpec; import android.view.IAppTransitionAnimationSpecsFuture; import android.view.WindowManagerGlobal; import com.android.internal.annotations.GuardedBy; import com.android.systemui.recents.Constants; +import com.android.systemui.recents.ExitRecentsWindowFirstAnimationFrameEvent; import com.android.systemui.recents.Recents; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent; @@ -106,6 +106,7 @@ public class RecentsTransitionHelper { // If we are launching into another task, cancel the previous task's // window transition EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); + EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent()); if (lockToTask) { // Request screen pinning after the animation runs @@ -116,7 +117,12 @@ public class RecentsTransitionHelper { } else { // This is only the case if the task is not on screen (scrolled offscreen for example) transitionFuture = null; - animStartedListener = null; + animStartedListener = new ActivityOptions.OnAnimationStartedListener() { + @Override + public void onAnimationStarted() { + EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent()); + } + }; } if (taskView == null) { diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 89f565816a62..943c9ed39262 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -139,7 +139,7 @@ public class AppTransition implements Dump { private static final long APP_TRANSITION_TIMEOUT_MS = 5000; private final Context mContext; - private final Handler mH; + private final WindowManagerService mService; private int mNextAppTransition = TRANSIT_UNSET; @@ -208,15 +208,10 @@ public class AppTransition implements Dump { private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>(); private final ExecutorService mDefaultExecutor = Executors.newSingleThreadExecutor(); - private final Object mServiceLock; - private final WindowSurfacePlacer mWindowSurfacePlacer; - AppTransition(Context context, Handler h, Object serviceLock, - WindowSurfacePlacer windowSurfacePlacer) { + AppTransition(Context context, WindowManagerService service) { mContext = context; - mH = h; - mServiceLock = serviceLock; - mWindowSurfacePlacer = windowSurfacePlacer; + mService = service; mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, com.android.internal.R.interpolator.linear_out_slow_in); mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context, @@ -971,7 +966,7 @@ public class AppTransition implements Dump { @Override public void onAnimationEnd(Animation animation) { - mH.obtainMessage(H.DO_ANIMATION_CALLBACK, callback).sendToTarget(); + mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK, callback).sendToTarget(); } @Override @@ -1326,7 +1321,8 @@ public class AppTransition implements Dump { void postAnimationCallback() { if (mNextAppTransitionCallback != null) { - mH.sendMessage(mH.obtainMessage(H.DO_ANIMATION_CALLBACK, mNextAppTransitionCallback)); + mService.mH.sendMessage(mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK, + mNextAppTransitionCallback)); mNextAppTransitionCallback = null; } } @@ -1478,14 +1474,15 @@ public class AppTransition implements Dump { } catch (RemoteException e) { Slog.w(TAG, "Failed to fetch app transition specs: " + e); } - synchronized (mServiceLock) { + synchronized (mService.mWindowMap) { mNextAppTransitionAnimationsSpecsPending = false; overridePendingAppTransitionMultiThumb(specs, mNextAppTransitionFutureCallback, null /* finishedCallback */, mNextAppTransitionScaleUp); mNextAppTransitionFutureCallback = null; - mWindowSurfacePlacer.requestTraversal(); + mService.prolongAnimationsFromSpecs(specs, mNextAppTransitionScaleUp); } + mService.requestTraversal(); } }); } @@ -1672,8 +1669,8 @@ public class AppTransition implements Dump { } boolean prepared = prepare(); if (isTransitionSet()) { - mH.removeMessages(H.APP_TRANSITION_TIMEOUT); - mH.sendEmptyMessageDelayed(H.APP_TRANSITION_TIMEOUT, APP_TRANSITION_TIMEOUT_MS); + mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT); + mService.mH.sendEmptyMessageDelayed(H.APP_TRANSITION_TIMEOUT, APP_TRANSITION_TIMEOUT_MS); } return prepared; } diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java index 2905269eb21c..dfd01efe36d0 100644 --- a/services/core/java/com/android/server/wm/AppWindowAnimator.java +++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java @@ -37,6 +37,10 @@ import java.util.ArrayList; public class AppWindowAnimator { static final String TAG = "AppWindowAnimator"; + private static final int PROLONG_ANIMATION_DISABLED = 0; + static final int PROLONG_ANIMATION_AT_END = 1; + static final int PROLONG_ANIMATION_AT_START = 2; + final AppWindowToken mAppToken; final WindowManagerService mService; final WindowAnimator mAnimator; @@ -85,7 +89,7 @@ public class AppWindowAnimator { // If true when the animation hits the last frame, it will keep running on that last frame. // This is used to synchronize animation with Recents and we wait for Recents to tell us to // finish or for a new animation be set as fail-safe mechanism. - private boolean mProlongAnimation; + private int mProlongAnimation; // Whether the prolong animation can be removed when animation is set. The purpose of this is // that if recents doesn't tell us to remove the prolonged animation, we will get rid of it // when new animation is set. @@ -142,7 +146,7 @@ public class AppWindowAnimator { anim.setBackgroundColor(0); } if (mClearProlongedAnimation) { - mProlongAnimation = false; + mProlongAnimation = PROLONG_ANIMATION_DISABLED; } else { mClearProlongedAnimation = true; } @@ -266,6 +270,10 @@ public class AppWindowAnimator { return false; } transformation.clear(); + if (mProlongAnimation == PROLONG_ANIMATION_AT_START) { + animation.setStartTime(currentTime); + currentTime += 1; + } boolean hasMoreFrames = animation.getTransformation(currentTime, transformation); if (!hasMoreFrames) { if (deferThumbnailDestruction && !deferFinalFrameCleanup) { @@ -278,7 +286,7 @@ public class AppWindowAnimator { "Stepped animation in " + mAppToken + ": more=" + hasMoreFrames + ", xform=" + transformation + ", mProlongAnimation=" + mProlongAnimation); deferFinalFrameCleanup = false; - if (mProlongAnimation) { + if (mProlongAnimation == PROLONG_ANIMATION_AT_END) { hasMoreFrames = true; } else { animation = null; @@ -434,13 +442,13 @@ public class AppWindowAnimator { } } - void startProlongAnimation() { - mProlongAnimation = true; + void startProlongAnimation(int prolongType) { + mProlongAnimation = prolongType; mClearProlongedAnimation = false; } void endProlongedAnimation() { - mProlongAnimation = false; + mProlongAnimation = PROLONG_ANIMATION_DISABLED; } // This is an animation that does nothing: it just immediately finishes diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c359c5306b4c..a9bd71ff703b 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -50,6 +50,9 @@ import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY; import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; +import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_END; +import static com.android.server.wm.AppWindowAnimator.PROLONG_ANIMATION_AT_START; + import android.Manifest; import android.animation.ValueAnimator; import android.annotation.Nullable; @@ -909,7 +912,7 @@ public class WindowManagerService extends IWindowManager.Stub PowerManager.PARTIAL_WAKE_LOCK, "SCREEN_FROZEN"); mScreenFrozenLock.setReferenceCounted(false); - mAppTransition = new AppTransition(context, mH, mWindowMap, mWindowPlacerLocked); + mAppTransition = new AppTransition(context, this); mAppTransition.registerListenerLocked(mActivityManagerAppTransitionNotifier); mActivityManager = ActivityManagerNative.getDefault(); @@ -3692,22 +3695,26 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mWindowMap) { mAppTransition.overridePendingAppTransitionMultiThumb(specs, onAnimationStartedCallback, onAnimationFinishedCallback, scaleUp); - if (!scaleUp) { - // This is used by freeform to recents windows transition. We need to synchronize - // the animation with the appearance of the content of recents, so we will make - // animation stay on the last frame a little longer. - mTmpTaskIds.clear(); - for (int i = specs.length - 1; i >= 0; i--) { - mTmpTaskIds.put(specs[i].taskId, 0); - } - for (final WindowState win : mWindowMap.values()) { - final Task task = win.getTask(); - if (task != null && mTmpTaskIds.get(task.mTaskId, -1) != -1) { - final AppWindowToken appToken = win.mAppToken; - if (appToken != null && appToken.mAppAnimator != null) { - appToken.mAppAnimator.startProlongAnimation(); - } - } + prolongAnimationsFromSpecs(specs, scaleUp); + + } + } + + void prolongAnimationsFromSpecs(AppTransitionAnimationSpec[] specs, boolean scaleUp) { + // This is used by freeform <-> recents windows transition. We need to synchronize + // the animation with the appearance of the content of recents, so we will make + // animation stay on the first or last frame a little longer. + mTmpTaskIds.clear(); + for (int i = specs.length - 1; i >= 0; i--) { + mTmpTaskIds.put(specs[i].taskId, 0); + } + for (final WindowState win : mWindowMap.values()) { + final Task task = win.getTask(); + if (task != null && mTmpTaskIds.get(task.mTaskId, -1) != -1) { + final AppWindowToken appToken = win.mAppToken; + if (appToken != null && appToken.mAppAnimator != null) { + appToken.mAppAnimator.startProlongAnimation(scaleUp ? + PROLONG_ANIMATION_AT_START : PROLONG_ANIMATION_AT_END); } } } |