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 Original line Diff line number Diff line
@@ -220070,6 +220070,19 @@
<parameter name="focusableMode" type="int">
<parameter name="focusableMode" type="int">
</parameter>
</parameter>
</method>
</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"
<method name="addOnLayoutChangeListener"
 return="void"
 return="void"
 abstract="false"
 abstract="false"
@@ -223073,6 +223086,19 @@
<parameter name="action" type="java.lang.Runnable">
<parameter name="action" type="java.lang.Runnable">
</parameter>
</parameter>
</method>
</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"
<method name="removeOnLayoutChangeListener"
 return="void"
 return="void"
 abstract="false"
 abstract="false"
@@ -225394,6 +225420,40 @@
>
>
</field>
</field>
</class>
</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"
<interface name="View.OnClickListener"
 abstract="true"
 abstract="true"
 static="true"
 static="true"
+79 −0
Original line number Original line Diff line number Diff line
@@ -74,6 +74,7 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Arrays;
import java.util.WeakHashMap;
import java.util.WeakHashMap;
import java.util.concurrent.CopyOnWriteArrayList;


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


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

    /**
    /**
     * Listener used to dispatch click events.
     * Listener used to dispatch click events.
     * This field should be made private, so it is hidden from the SDK.
     * 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);
        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.
     * Returns the focus-change callback registered for this view.
     *
     *
@@ -7953,6 +7990,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
        }
        }
        performCollectViewAttributes(visibility);
        performCollectViewAttributes(visibility);
        onAttachedToWindow();
        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;
        int vis = info.mWindowVisibility;
        if (vis != GONE) {
        if (vis != GONE) {
            onWindowVisibilityChanged(vis);
            onWindowVisibilityChanged(vis);
@@ -7974,6 +8024,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility


        onDetachedFromWindow();
        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) {
        if ((mPrivateFlags & SCROLL_CONTAINER_ADDED) != 0) {
            mAttachInfo.mScrollContainers.remove(this);
            mAttachInfo.mScrollContainers.remove(this);
            mPrivateFlags &= ~SCROLL_CONTAINER_ADDED;
            mPrivateFlags &= ~SCROLL_CONTAINER_ADDED;
@@ -11767,6 +11829,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
        public void onSystemUiVisibilityChange(int visibility);
        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 {
    private final class UnsetPressedState implements Runnable {
        public void run() {
        public void run() {
            setPressed(false);
            setPressed(false);
+30 −21
Original line number Original line Diff line number Diff line
@@ -36,14 +36,15 @@ import java.lang.ref.WeakReference;
 * @hide
 * @hide
 */
 */
public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.OnKeyListener,
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 static final String TAG = "MenuPopupHelper";


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


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


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


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


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


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


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


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


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


    @Override
    @Override
    public void onGlobalLayout() {
    public void onGlobalLayout() {
        if (!isShowing()) {
        if (isShowing()) {
            if (mTreeObserver.isAlive()) {
            final View anchor = mAnchorView;
                mTreeObserver.removeGlobalOnLayoutListener(this);
            }
            mTreeObserver = null;
        } else {
            final View anchor = mAnchorView != null ? mAnchorView.get() : null;
            if (anchor == null || !anchor.isShown()) {
            if (anchor == null || !anchor.isShown()) {
                dismiss();
                dismiss();
            } else if (isShowing()) {
            } 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);
    }
}
}