diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index fecb9e6aec43..79bdc004ae67 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -19102,7 +19102,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return mAttachInfo.mTreeObserver; } if (mFloatingTreeObserver == null) { - mFloatingTreeObserver = new ViewTreeObserver(); + mFloatingTreeObserver = new ViewTreeObserver(mContext); } return mFloatingTreeObserver; } @@ -23051,7 +23051,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * The view tree observer used to dispatch global events like * layout, pre-draw, touch mode change, etc. */ - final ViewTreeObserver mTreeObserver = new ViewTreeObserver(); + final ViewTreeObserver mTreeObserver; /** * A Canvas used by the view hierarchy to perform bitmap caching. @@ -23173,7 +23173,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param handler the events handler the view must use */ AttachInfo(IWindowSession session, IWindow window, Display display, - ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer) { + ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer, + Context context) { mSession = session; mWindow = window; mWindowToken = window.asBinder(); @@ -23181,6 +23182,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mViewRootImpl = viewRootImpl; mHandler = handler; mRootCallbacks = effectPlayer; + mTreeObserver = new ViewTreeObserver(context); } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index ece0e1b9d01b..3bbf75e8e856 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -429,7 +429,8 @@ public final class ViewRootImpl implements ViewParent, mPreviousTransparentRegion = new Region(); mFirst = true; // true for the first time the view is added mAdded = false; - mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this); + mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this, + context); mAccessibilityManager = AccessibilityManager.getInstance(context); mAccessibilityInteractionConnectionManager = new AccessibilityInteractionConnectionManager(); diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java index 521fd3117329..7dd2fc23642d 100644 --- a/core/java/android/view/ViewTreeObserver.java +++ b/core/java/android/view/ViewTreeObserver.java @@ -16,8 +16,11 @@ package android.view; +import android.content.Context; import android.graphics.Rect; import android.graphics.Region; +import android.os.Build; +import android.util.Log; import java.util.ArrayList; import java.util.concurrent.CopyOnWriteArrayList; @@ -49,7 +52,9 @@ public final class ViewTreeObserver { private CopyOnWriteArray mOnWindowShownListeners; // These listeners cannot be mutated during dispatch + private boolean mInDispatchOnDraw; private ArrayList mOnDrawListeners; + private static boolean sIllegalOnDrawModificationIsFatal; /** Remains false until #dispatchOnWindowShown() is called. If a listener registers after * that the listener will be immediately called. */ @@ -327,7 +332,9 @@ public final class ViewTreeObserver { /** * Creates a new ViewTreeObserver. This constructor should not be called */ - ViewTreeObserver() { + ViewTreeObserver(Context context) { + sIllegalOnDrawModificationIsFatal = + context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.N_MR1; } /** @@ -657,6 +664,15 @@ public final class ViewTreeObserver { mOnDrawListeners = new ArrayList(); } + if (mInDispatchOnDraw) { + IllegalStateException ex = new IllegalStateException( + "Cannot call addOnDrawListener inside of onDraw"); + if (sIllegalOnDrawModificationIsFatal) { + throw ex; + } else { + Log.e("ViewTreeObserver", ex.getMessage(), ex); + } + } mOnDrawListeners.add(listener); } @@ -676,6 +692,15 @@ public final class ViewTreeObserver { if (mOnDrawListeners == null) { return; } + if (mInDispatchOnDraw) { + IllegalStateException ex = new IllegalStateException( + "Cannot call removeOnDrawListener inside of onDraw"); + if (sIllegalOnDrawModificationIsFatal) { + throw ex; + } else { + Log.e("ViewTreeObserver", ex.getMessage(), ex); + } + } mOnDrawListeners.remove(victim); } @@ -976,11 +1001,13 @@ public final class ViewTreeObserver { */ public final void dispatchOnDraw() { if (mOnDrawListeners != null) { + mInDispatchOnDraw = true; final ArrayList listeners = mOnDrawListeners; int numListeners = listeners.size(); for (int i = 0; i < numListeners; ++i) { listeners.get(i).onDraw(); } + mInDispatchOnDraw = false; } } diff --git a/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java b/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java index 94f3f546d0c3..85584d3ed2cc 100644 --- a/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java +++ b/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java @@ -34,7 +34,7 @@ public class AttachInfo_Accessor { Display display = wm.getDefaultDisplay(); ViewRootImpl root = new ViewRootImpl(context, display); AttachInfo info = new AttachInfo(new BridgeWindowSession(), new BridgeWindow(), - display, root, new Handler(), null); + display, root, new Handler(), null, context); info.mHasWindowFocus = true; info.mWindowVisibility = View.VISIBLE; info.mInTouchMode = false; // this is so that we can display selections.