Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e7c94392 authored by Adam Powell's avatar Adam Powell Committed by Android (Google) Code Review
Browse files

Merge "Add an API to listen for window attach/detach events on a View."

parents 375adc69 4afd62b1
Loading
Loading
Loading
Loading
+60 −0
Original line number Diff line number Diff line
@@ -220070,6 +220070,19 @@
<parameter name="focusableMode" type="int">
</parameter>
</method>
<method name="addOnAttachStateChangeListener"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="listener" type="android.view.View.OnAttachStateChangeListener">
</parameter>
</method>
<method name="addOnLayoutChangeListener"
 return="void"
 abstract="false"
@@ -223073,6 +223086,19 @@
<parameter name="action" type="java.lang.Runnable">
</parameter>
</method>
<method name="removeOnAttachStateChangeListener"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="listener" type="android.view.View.OnAttachStateChangeListener">
</parameter>
</method>
<method name="removeOnLayoutChangeListener"
 return="void"
 abstract="false"
@@ -225394,6 +225420,40 @@
>
</field>
</class>
<interface name="View.OnAttachStateChangeListener"
 abstract="true"
 static="true"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<method name="onViewAttachedToWindow"
 return="void"
 abstract="true"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="v" type="android.view.View">
</parameter>
</method>
<method name="onViewDetachedFromWindow"
 return="void"
 abstract="true"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="public"
>
<parameter name="v" type="android.view.View">
</parameter>
</method>
</interface>
<interface name="View.OnClickListener"
 abstract="true"
 static="true"
+79 −0
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.WeakHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * <p>
@@ -2098,6 +2099,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
     */
    private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners;

    /**
     * Listeners for attach events.
     */
    private CopyOnWriteArrayList<OnAttachStateChangeListener> mOnAttachStateChangeListeners;

    /**
     * Listener used to dispatch click events.
     * This field should be made private, so it is hidden from the SDK.
@@ -2995,6 +3001,37 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
        mOnLayoutChangeListeners.remove(listener);
    }

    /**
     * Add a listener for attach state changes.
     *
     * This listener will be called whenever this view is attached or detached
     * from a window. Remove the listener using
     * {@link #removeOnAttachStateChangeListener(OnAttachStateChangeListener)}.
     *
     * @param listener Listener to attach
     * @see #removeOnAttachStateChangeListener(OnAttachStateChangeListener)
     */
    public void addOnAttachStateChangeListener(OnAttachStateChangeListener listener) {
        if (mOnAttachStateChangeListeners == null) {
            mOnAttachStateChangeListeners = new CopyOnWriteArrayList<OnAttachStateChangeListener>();
        }
        mOnAttachStateChangeListeners.add(listener);
    }

    /**
     * Remove a listener for attach state changes. The listener will receive no further
     * notification of window attach/detach events.
     *
     * @param listener Listener to remove
     * @see #addOnAttachStateChangeListener(OnAttachStateChangeListener)
     */
    public void removeOnAttachStateChangeListener(OnAttachStateChangeListener listener) {
        if (mOnAttachStateChangeListeners == null) {
            return;
        }
        mOnAttachStateChangeListeners.remove(listener);
    }

    /**
     * Returns the focus-change callback registered for this view.
     *
@@ -7953,6 +7990,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
        }
        performCollectViewAttributes(visibility);
        onAttachedToWindow();

        final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =
                mOnAttachStateChangeListeners;
        if (listeners != null && listeners.size() > 0) {
            // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
            // perform the dispatching. The iterator is a safe guard against listeners that
            // could mutate the list by calling the various add/remove methods. This prevents
            // the array from being modified while we iterate it.
            for (OnAttachStateChangeListener listener : listeners) {
                listener.onViewAttachedToWindow(this);
            }
        }

        int vis = info.mWindowVisibility;
        if (vis != GONE) {
            onWindowVisibilityChanged(vis);
@@ -7974,6 +8024,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility

        onDetachedFromWindow();

        final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =
                mOnAttachStateChangeListeners;
        if (listeners != null && listeners.size() > 0) {
            // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
            // perform the dispatching. The iterator is a safe guard against listeners that
            // could mutate the list by calling the various add/remove methods. This prevents
            // the array from being modified while we iterate it.
            for (OnAttachStateChangeListener listener : listeners) {
                listener.onViewDetachedFromWindow(this);
            }
        }

        if ((mPrivateFlags & SCROLL_CONTAINER_ADDED) != 0) {
            mAttachInfo.mScrollContainers.remove(this);
            mPrivateFlags &= ~SCROLL_CONTAINER_ADDED;
@@ -11767,6 +11829,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
        public void onSystemUiVisibilityChange(int visibility);
    }

    /**
     * Interface definition for a callback to be invoked when this view is attached
     * or detached from its window.
     */
    public interface OnAttachStateChangeListener {
        /**
         * Called when the view is attached to a window.
         * @param v The view that was attached
         */
        public void onViewAttachedToWindow(View v);
        /**
         * Called when the view is detached from a window.
         * @param v The view that was detached
         */
        public void onViewDetachedFromWindow(View v);
    }

    private final class UnsetPressedState implements Runnable {
        public void run() {
            setPressed(false);
+30 −21
Original line number Diff line number Diff line
@@ -36,14 +36,15 @@ import java.lang.ref.WeakReference;
 * @hide
 */
public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.OnKeyListener,
        ViewTreeObserver.OnGlobalLayoutListener, PopupWindow.OnDismissListener {
        ViewTreeObserver.OnGlobalLayoutListener, PopupWindow.OnDismissListener,
        View.OnAttachStateChangeListener {
    private static final String TAG = "MenuPopupHelper";

    private Context mContext;
    private ListPopupWindow mPopup;
    private MenuBuilder mMenu;
    private int mPopupMaxWidth;
    private WeakReference<View> mAnchorView;
    private View mAnchorView;
    private boolean mOverflowOnly;
    private ViewTreeObserver mTreeObserver;

@@ -66,13 +67,11 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
        final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
        mPopupMaxWidth = metrics.widthPixels / 2;

        if (anchorView != null) {
            mAnchorView = new WeakReference<View>(anchorView);
        }
        mAnchorView = anchorView;
    }

    public void setAnchorView(View anchor) {
        mAnchorView = new WeakReference<View>(anchor);
        mAnchorView = anchor;
    }

    public void show() {
@@ -92,19 +91,19 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
        mPopup.setAdapter(adapter);
        mPopup.setModal(true);

        View anchor = mAnchorView != null ? mAnchorView.get() : null;
        View anchor = mAnchorView;
        if (anchor == null && mMenu instanceof SubMenuBuilder) {
            SubMenuBuilder subMenu = (SubMenuBuilder) mMenu;
            final MenuItemImpl itemImpl = (MenuItemImpl) subMenu.getItem();
            anchor = itemImpl.getItemView(MenuBuilder.TYPE_ACTION_BUTTON, null);
            mAnchorView = new WeakReference<View>(anchor);
            mAnchorView = anchor;
        }

        if (anchor != null) {
            if (mTreeObserver == null) {
                mTreeObserver = anchor.getViewTreeObserver();
                mTreeObserver.addOnGlobalLayoutListener(this);
            }
            final boolean addGlobalListener = mTreeObserver == null;
            mTreeObserver = anchor.getViewTreeObserver(); // Refresh to latest
            if (addGlobalListener) mTreeObserver.addOnGlobalLayoutListener(this);
            anchor.addOnAttachStateChangeListener(this);
            mPopup.setAnchorView(anchor);
        } else {
            return false;
@@ -125,11 +124,13 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On

    public void onDismiss() {
        mPopup = null;
        if (mTreeObserver != null && mTreeObserver.isAlive()) {
        if (mTreeObserver != null) {
            if (!mTreeObserver.isAlive()) mTreeObserver = mAnchorView.getViewTreeObserver();
            mTreeObserver.removeGlobalOnLayoutListener(this);
        }
            mTreeObserver = null;
        }
        mAnchorView.removeOnAttachStateChangeListener(this);
    }

    public boolean isShowing() {
        return mPopup != null && mPopup.isShowing();
@@ -187,13 +188,8 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On

    @Override
    public void onGlobalLayout() {
        if (!isShowing()) {
            if (mTreeObserver.isAlive()) {
                mTreeObserver.removeGlobalOnLayoutListener(this);
            }
            mTreeObserver = null;
        } else {
            final View anchor = mAnchorView != null ? mAnchorView.get() : null;
        if (isShowing()) {
            final View anchor = mAnchorView;
            if (anchor == null || !anchor.isShown()) {
                dismiss();
            } else if (isShowing()) {
@@ -202,4 +198,17 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On
            }
        }
    }

    @Override
    public void onViewAttachedToWindow(View v) {
    }

    @Override
    public void onViewDetachedFromWindow(View v) {
        if (mTreeObserver != null) {
            if (!mTreeObserver.isAlive()) mTreeObserver = v.getViewTreeObserver();
            mTreeObserver.removeGlobalOnLayoutListener(this);
        }
        v.removeOnAttachStateChangeListener(this);
    }
}