Merge "Add enter-animation-done callback for system windows" into lmp-mr1-dev

This commit is contained in:
Craig Mautner
2014-10-30 22:12:30 +00:00
committed by Android (Google) Code Review
8 changed files with 156 additions and 22 deletions

View File

@ -85,4 +85,9 @@ oneway interface IWindow {
* is done.
*/
void doneAnimating();
/**
* Called for non-application windows when the enter animation has completed.
*/
void dispatchWindowShown();
}

View File

@ -3089,6 +3089,7 @@ public final class ViewRootImpl implements ViewParent,
private final static int MSG_INVALIDATE_WORLD = 23;
private final static int MSG_WINDOW_MOVED = 24;
private final static int MSG_SYNTHESIZE_INPUT_EVENT = 25;
private final static int MSG_DISPATCH_WINDOW_SHOWN = 26;
final class ViewRootHandler extends Handler {
@Override
@ -3138,6 +3139,8 @@ public final class ViewRootImpl implements ViewParent,
return "MSG_WINDOW_MOVED";
case MSG_SYNTHESIZE_INPUT_EVENT:
return "MSG_SYNTHESIZE_INPUT_EVENT";
case MSG_DISPATCH_WINDOW_SHOWN:
return "MSG_DISPATCH_WINDOW_SHOWN";
}
return super.getMessageName(message);
}
@ -3366,6 +3369,9 @@ public final class ViewRootImpl implements ViewParent,
invalidateWorld(mView);
}
} break;
case MSG_DISPATCH_WINDOW_SHOWN: {
handleDispatchWindowShown();
}
}
}
}
@ -5212,6 +5218,10 @@ public final class ViewRootImpl implements ViewParent,
}
}
public void handleDispatchWindowShown() {
mAttachInfo.mTreeObserver.dispatchOnWindowShown();
}
public void getLastTouchPoint(Point outLocation) {
outLocation.x = (int) mLastTouchPoint.x;
outLocation.y = (int) mLastTouchPoint.y;
@ -6072,6 +6082,10 @@ public final class ViewRootImpl implements ViewParent,
mHandler.sendMessage(msg);
}
public void dispatchWindowShown() {
mHandler.sendEmptyMessage(MSG_DISPATCH_WINDOW_SHOWN);
}
public void dispatchCloseSystemDialogs(String reason) {
Message msg = Message.obtain();
msg.what = MSG_CLOSE_SYSTEM_DIALOGS;
@ -6582,6 +6596,14 @@ public final class ViewRootImpl implements ViewParent,
viewAncestor.dispatchDoneAnimating();
}
}
@Override
public void dispatchWindowShown() {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchWindowShown();
}
}
}
public static final class CalledFromWrongThreadException extends AndroidRuntimeException {

View File

@ -44,10 +44,15 @@ public final class ViewTreeObserver {
private CopyOnWriteArray<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners;
private CopyOnWriteArray<OnScrollChangedListener> mOnScrollChangedListeners;
private CopyOnWriteArray<OnPreDrawListener> mOnPreDrawListeners;
private CopyOnWriteArray<OnWindowShownListener> mOnWindowShownListeners;
// These listeners cannot be mutated during dispatch
private ArrayList<OnDrawListener> mOnDrawListeners;
/** Remains false until #dispatchOnWindowShown() is called. If a listener registers after
* that the listener will be immediately called. */
private boolean mWindowShown;
private boolean mAlive = true;
/**
@ -173,6 +178,19 @@ public final class ViewTreeObserver {
public void onScrollChanged();
}
/**
* Interface definition for a callback noting when a system window has been displayed.
* This is only used for non-Activity windows. Activity windows can use
* Activity.onEnterAnimationComplete() to get the same signal.
* @hide
*/
public interface OnWindowShownListener {
/**
* Callback method to be invoked when a non-activity window is fully shown.
*/
void onWindowShown();
}
/**
* Parameters used with OnComputeInternalInsetsListener.
*
@ -375,6 +393,14 @@ public final class ViewTreeObserver {
}
}
if (observer.mOnWindowShownListeners != null) {
if (mOnWindowShownListeners != null) {
mOnWindowShownListeners.addAll(observer.mOnWindowShownListeners);
} else {
mOnWindowShownListeners = observer.mOnWindowShownListeners;
}
}
observer.kill();
}
@ -567,6 +593,45 @@ public final class ViewTreeObserver {
mOnPreDrawListeners.remove(victim);
}
/**
* Register a callback to be invoked when the view tree window has been shown
*
* @param listener The callback to add
*
* @throws IllegalStateException If {@link #isAlive()} returns false
* @hide
*/
public void addOnWindowShownListener(OnWindowShownListener listener) {
checkIsAlive();
if (mOnWindowShownListeners == null) {
mOnWindowShownListeners = new CopyOnWriteArray<OnWindowShownListener>();
}
mOnWindowShownListeners.add(listener);
if (mWindowShown) {
listener.onWindowShown();
}
}
/**
* Remove a previously installed window shown callback
*
* @param victim The callback to remove
*
* @throws IllegalStateException If {@link #isAlive()} returns false
*
* @see #addOnWindowShownListener(OnWindowShownListener)
* @hide
*/
public void removeOnWindowShownListener(OnWindowShownListener victim) {
checkIsAlive();
if (mOnWindowShownListeners == null) {
return;
}
mOnWindowShownListeners.remove(victim);
}
/**
* <p>Register a callback to be invoked when the view tree is about to be drawn.</p>
* <p><strong>Note:</strong> this method <strong>cannot</strong> be invoked from
@ -853,6 +918,27 @@ public final class ViewTreeObserver {
return cancelDraw;
}
/**
* Notifies registered listeners that the window is now shown
* @hide
*/
@SuppressWarnings("unchecked")
public final void dispatchOnWindowShown() {
mWindowShown = true;
final CopyOnWriteArray<OnWindowShownListener> listeners = mOnWindowShownListeners;
if (listeners != null && listeners.size() > 0) {
CopyOnWriteArray.Access<OnWindowShownListener> access = listeners.start();
try {
int count = access.size();
for (int i = 0; i < count; i++) {
access.get(i).onWindowShown();
}
} finally {
listeners.end();
}
}
}
/**
* Notifies registered listeners that the drawing pass is about to start.
*/

View File

@ -102,4 +102,8 @@ public class BaseIWindow extends IWindow.Stub {
@Override
public void doneAnimating() {
}
@Override
public void dispatchWindowShown() {
}
}

View File

@ -17,17 +17,11 @@
package com.android.server.am;
import android.app.AlertDialog;
import android.app.Service;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Message;
import android.util.Slog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.TextView;
@ -39,11 +33,10 @@ import com.android.internal.R;
* in the background rather than just freeze the screen and not know if the user-switch affordance
* was being handled.
*/
final class UserSwitchingDialog extends AlertDialog {
final class UserSwitchingDialog extends AlertDialog
implements ViewTreeObserver.OnWindowShownListener {
private static final String TAG = "ActivityManagerUserSwitchingDialog";
private static final int MSG_START_USER = 1;
private final ActivityManagerService mService;
private final int mUserId;
@ -74,19 +67,21 @@ final class UserSwitchingDialog extends AlertDialog {
@Override
public void show() {
// Slog.v(TAG, "show called");
super.show();
// TODO: Instead of just an arbitrary delay, wait for a signal that the window was fully
// displayed by the window manager
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_USER), 250);
final View decorView = getWindow().getDecorView();
if (decorView != null) {
decorView.getViewTreeObserver().addOnWindowShownListener(this);
}
}
private final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_START_USER:
mService.startUserInForeground(mUserId, UserSwitchingDialog.this);
break;
}
@Override
public void onWindowShown() {
// Slog.v(TAG, "onWindowShown called");
mService.startUserInForeground(mUserId, this);
final View decorView = getWindow().getDecorView();
if (decorView != null) {
decorView.getViewTreeObserver().removeOnWindowShownListener(this);
}
};
}
}

View File

@ -2487,7 +2487,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
win.mWinAnimator.mEnterAnimationPending = true;
final WindowStateAnimator winAnimator = win.mWinAnimator;
winAnimator.mEnterAnimationPending = true;
winAnimator.mEnteringAnimation = true;
if (displayContent.isDefaultDisplay) {
mPolicy.getContentInsetHintLw(attrs, outContentInsets);
@ -3099,6 +3101,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (oldVisibility == View.GONE) {
winAnimator.mEnterAnimationPending = true;
}
winAnimator.mEnteringAnimation = true;
if (toBeDisplayed) {
if (win.isDrawnLw() && okToDisplay()) {
winAnimator.applyEnterAnimationLocked();
@ -3167,6 +3170,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
} else {
winAnimator.mEnterAnimationPending = false;
winAnimator.mEnteringAnimation = false;
if (winAnimator.mSurfaceControl != null) {
if (DEBUG_VISIBILITY) Slog.i(TAG, "Relayout invis " + win
+ ": mExiting=" + win.mExiting);

View File

@ -40,6 +40,7 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.os.Debug;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;
import android.view.Display;
@ -141,6 +142,11 @@ class WindowStateAnimator {
// an enter animation.
boolean mEnterAnimationPending;
/** Used to indicate that this window is undergoing an enter animation. Used for system
* windows to make the callback to View.dispatchOnWindowShownCallback(). Set when the
* window is first added or shown, cleared when the callback has been made. */
boolean mEnteringAnimation;
boolean keyguardGoingAwayAnimation;
/** This is set when there is no Surface */
@ -428,6 +434,14 @@ class WindowStateAnimator {
mWin.mChildWindows.get(i).mWinAnimator.finishExit();
}
if (mEnteringAnimation && mWin.mAppToken == null) {
try {
mEnteringAnimation = false;
mWin.mClient.dispatchWindowShown();
} catch (RemoteException e) {
}
}
if (!mWin.mExiting) {
return;
}

View File

@ -94,6 +94,10 @@ public final class BridgeWindow implements IWindow {
public void doneAnimating() {
}
@Override
public void dispatchWindowShown() {
}
@Override
public IBinder asBinder() {
// pass for now.