Loading core/java/android/view/IWindow.aidl +5 −0 Original line number Diff line number Diff line Loading @@ -85,4 +85,9 @@ oneway interface IWindow { * is done. */ void doneAnimating(); /** * Called for non-application windows when the enter animation has completed. */ void dispatchWindowShown(); } core/java/android/view/ViewRootImpl.java +22 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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); } Loading Loading @@ -3366,6 +3369,9 @@ public final class ViewRootImpl implements ViewParent, invalidateWorld(mView); } } break; case MSG_DISPATCH_WINDOW_SHOWN: { handleDispatchWindowShown(); } } } } Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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 { Loading core/java/android/view/ViewTreeObserver.java +86 −0 Original line number Diff line number Diff line Loading @@ -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; /** Loading Loading @@ -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. * Loading Loading @@ -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(); } Loading Loading @@ -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 Loading Loading @@ -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. */ Loading core/java/com/android/internal/view/BaseIWindow.java +4 −0 Original line number Diff line number Diff line Loading @@ -102,4 +102,8 @@ public class BaseIWindow extends IWindow.Stub { @Override public void doneAnimating() { } @Override public void dispatchWindowShown() { } } services/core/java/com/android/server/am/UserSwitchingDialog.java +16 −21 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); } } }; } Loading
core/java/android/view/IWindow.aidl +5 −0 Original line number Diff line number Diff line Loading @@ -85,4 +85,9 @@ oneway interface IWindow { * is done. */ void doneAnimating(); /** * Called for non-application windows when the enter animation has completed. */ void dispatchWindowShown(); }
core/java/android/view/ViewRootImpl.java +22 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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); } Loading Loading @@ -3366,6 +3369,9 @@ public final class ViewRootImpl implements ViewParent, invalidateWorld(mView); } } break; case MSG_DISPATCH_WINDOW_SHOWN: { handleDispatchWindowShown(); } } } } Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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 { Loading
core/java/android/view/ViewTreeObserver.java +86 −0 Original line number Diff line number Diff line Loading @@ -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; /** Loading Loading @@ -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. * Loading Loading @@ -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(); } Loading Loading @@ -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 Loading Loading @@ -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. */ Loading
core/java/com/android/internal/view/BaseIWindow.java +4 −0 Original line number Diff line number Diff line Loading @@ -102,4 +102,8 @@ public class BaseIWindow extends IWindow.Stub { @Override public void doneAnimating() { } @Override public void dispatchWindowShown() { } }
services/core/java/com/android/server/am/UserSwitchingDialog.java +16 −21 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); } } }; }