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

Commit f847ee3c authored by Vladislav Kaznacheev's avatar Vladislav Kaznacheev
Browse files

Implement tooltip support in View

Adding View.setTooltip/getTooltip and 'tooltip' layout attribute.
Following Material Design spec for styles and behavior.

Bug: 31515376
Test:  cts-tradefed run singleCommand cts -m CtsViewTestCases
  --test android.view.cts.TooltipTest

Change-Id: I2d2527f642cd7446ffc88d4beffc7b81d7a2f6d6
parent 76932df9
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -1357,6 +1357,7 @@ package android {
    field public static final int toYDelta = 16843209; // 0x10101c9
    field public static final int toYDelta = 16843209; // 0x10101c9
    field public static final int toYScale = 16843205; // 0x10101c5
    field public static final int toYScale = 16843205; // 0x10101c5
    field public static final int toolbarStyle = 16843946; // 0x10104aa
    field public static final int toolbarStyle = 16843946; // 0x10104aa
    field public static final int tooltip = 16844084; // 0x1010534
    field public static final int top = 16843182; // 0x10101ae
    field public static final int top = 16843182; // 0x10101ae
    field public static final int topBright = 16842955; // 0x10100cb
    field public static final int topBright = 16842955; // 0x10100cb
    field public static final int topDark = 16842951; // 0x10100c7
    field public static final int topDark = 16842951; // 0x10100c7
@@ -42827,6 +42828,7 @@ package android.view {
    method public java.lang.Object getTag(int);
    method public java.lang.Object getTag(int);
    method public int getTextAlignment();
    method public int getTextAlignment();
    method public int getTextDirection();
    method public int getTextDirection();
    method public final java.lang.CharSequence getTooltip();
    method public final int getTop();
    method public final int getTop();
    method protected float getTopFadingEdgeStrength();
    method protected float getTopFadingEdgeStrength();
    method protected int getTopPaddingOffset();
    method protected int getTopPaddingOffset();
@@ -43115,6 +43117,7 @@ package android.view {
    method public void setTag(int, java.lang.Object);
    method public void setTag(int, java.lang.Object);
    method public void setTextAlignment(int);
    method public void setTextAlignment(int);
    method public void setTextDirection(int);
    method public void setTextDirection(int);
    method public final void setTooltip(java.lang.CharSequence);
    method public final void setTop(int);
    method public final void setTop(int);
    method public void setTouchDelegate(android.view.TouchDelegate);
    method public void setTouchDelegate(android.view.TouchDelegate);
    method public final void setTransitionName(java.lang.String);
    method public final void setTransitionName(java.lang.String);
+3 −0
Original line number Original line Diff line number Diff line
@@ -1468,6 +1468,7 @@ package android {
    field public static final int toYDelta = 16843209; // 0x10101c9
    field public static final int toYDelta = 16843209; // 0x10101c9
    field public static final int toYScale = 16843205; // 0x10101c5
    field public static final int toYScale = 16843205; // 0x10101c5
    field public static final int toolbarStyle = 16843946; // 0x10104aa
    field public static final int toolbarStyle = 16843946; // 0x10104aa
    field public static final int tooltip = 16844084; // 0x1010534
    field public static final int top = 16843182; // 0x10101ae
    field public static final int top = 16843182; // 0x10101ae
    field public static final int topBright = 16842955; // 0x10100cb
    field public static final int topBright = 16842955; // 0x10100cb
    field public static final int topDark = 16842951; // 0x10100c7
    field public static final int topDark = 16842951; // 0x10100c7
@@ -45995,6 +45996,7 @@ package android.view {
    method public java.lang.Object getTag(int);
    method public java.lang.Object getTag(int);
    method public int getTextAlignment();
    method public int getTextAlignment();
    method public int getTextDirection();
    method public int getTextDirection();
    method public final java.lang.CharSequence getTooltip();
    method public final int getTop();
    method public final int getTop();
    method protected float getTopFadingEdgeStrength();
    method protected float getTopFadingEdgeStrength();
    method protected int getTopPaddingOffset();
    method protected int getTopPaddingOffset();
@@ -46283,6 +46285,7 @@ package android.view {
    method public void setTag(int, java.lang.Object);
    method public void setTag(int, java.lang.Object);
    method public void setTextAlignment(int);
    method public void setTextAlignment(int);
    method public void setTextDirection(int);
    method public void setTextDirection(int);
    method public final void setTooltip(java.lang.CharSequence);
    method public final void setTop(int);
    method public final void setTop(int);
    method public void setTouchDelegate(android.view.TouchDelegate);
    method public void setTouchDelegate(android.view.TouchDelegate);
    method public final void setTransitionName(java.lang.String);
    method public final void setTransitionName(java.lang.String);
+8 −0
Original line number Original line Diff line number Diff line
@@ -1357,6 +1357,7 @@ package android {
    field public static final int toYDelta = 16843209; // 0x10101c9
    field public static final int toYDelta = 16843209; // 0x10101c9
    field public static final int toYScale = 16843205; // 0x10101c5
    field public static final int toYScale = 16843205; // 0x10101c5
    field public static final int toolbarStyle = 16843946; // 0x10104aa
    field public static final int toolbarStyle = 16843946; // 0x10104aa
    field public static final int tooltip = 16844084; // 0x1010534
    field public static final int top = 16843182; // 0x10101ae
    field public static final int top = 16843182; // 0x10101ae
    field public static final int topBright = 16842955; // 0x10100cb
    field public static final int topBright = 16842955; // 0x10100cb
    field public static final int topDark = 16842951; // 0x10100c7
    field public static final int topDark = 16842951; // 0x10100c7
@@ -43072,6 +43073,8 @@ package android.view {
    method public java.lang.Object getTag(int);
    method public java.lang.Object getTag(int);
    method public int getTextAlignment();
    method public int getTextAlignment();
    method public int getTextDirection();
    method public int getTextDirection();
    method public final java.lang.CharSequence getTooltip();
    method public android.view.View getTooltipView();
    method public final int getTop();
    method public final int getTop();
    method protected float getTopFadingEdgeStrength();
    method protected float getTopFadingEdgeStrength();
    method protected int getTopPaddingOffset();
    method protected int getTopPaddingOffset();
@@ -43360,6 +43363,7 @@ package android.view {
    method public void setTag(int, java.lang.Object);
    method public void setTag(int, java.lang.Object);
    method public void setTextAlignment(int);
    method public void setTextAlignment(int);
    method public void setTextDirection(int);
    method public void setTextDirection(int);
    method public final void setTooltip(java.lang.CharSequence);
    method public final void setTop(int);
    method public final void setTop(int);
    method public void setTouchDelegate(android.view.TouchDelegate);
    method public void setTouchDelegate(android.view.TouchDelegate);
    method public final void setTransitionName(java.lang.String);
    method public final void setTransitionName(java.lang.String);
@@ -43642,10 +43646,14 @@ package android.view {
    method public static deprecated int getEdgeSlop();
    method public static deprecated int getEdgeSlop();
    method public static deprecated int getFadingEdgeLength();
    method public static deprecated int getFadingEdgeLength();
    method public static deprecated long getGlobalActionKeyTimeout();
    method public static deprecated long getGlobalActionKeyTimeout();
    method public static int getHoverTooltipHideShortTimeout();
    method public static int getHoverTooltipHideTimeout();
    method public static int getHoverTooltipShowTimeout();
    method public static int getJumpTapTimeout();
    method public static int getJumpTapTimeout();
    method public static int getKeyRepeatDelay();
    method public static int getKeyRepeatDelay();
    method public static int getKeyRepeatTimeout();
    method public static int getKeyRepeatTimeout();
    method public static int getLongPressTimeout();
    method public static int getLongPressTimeout();
    method public static int getLongPressTooltipHideTimeout();
    method public static deprecated int getMaximumDrawingCacheSize();
    method public static deprecated int getMaximumDrawingCacheSize();
    method public static deprecated int getMaximumFlingVelocity();
    method public static deprecated int getMaximumFlingVelocity();
    method public static deprecated int getMinimumFlingVelocity();
    method public static deprecated int getMinimumFlingVelocity();
+277 −26
Original line number Original line Diff line number Diff line
@@ -37,6 +37,7 @@ import android.annotation.LayoutRes;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.annotation.Size;
import android.annotation.Size;
import android.annotation.TestApi;
import android.annotation.UiThread;
import android.annotation.UiThread;
import android.content.ClipData;
import android.content.ClipData;
import android.content.Context;
import android.content.Context;
@@ -111,6 +112,7 @@ import android.widget.ScrollBarDrawable;
import com.android.internal.R;
import com.android.internal.R;
import com.android.internal.util.Predicate;
import com.android.internal.util.Predicate;
import com.android.internal.view.TooltipPopup;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.widget.ScrollBarUtils;
import com.android.internal.widget.ScrollBarUtils;
@@ -1196,6 +1198,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    private static Paint sDebugPaint;
    private static Paint sDebugPaint;
    /**
     * <p>Indicates this view can display a tooltip on hover or long press.</p>
     * {@hide}
     */
    static final int TOOLTIP = 0x40000000;
    /** @hide */
    /** @hide */
    @IntDef(flag = true,
    @IntDef(flag = true,
            value = {
            value = {
@@ -3619,6 +3627,39 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    ListenerInfo mListenerInfo;
    ListenerInfo mListenerInfo;
    private static class TooltipInfo {
        /**
         * Text to be displayed in a tooltip popup.
         */
        @Nullable
        CharSequence mTooltip;
        /**
         * View-relative position of the tooltip anchor point.
         */
        int mAnchorX;
        int mAnchorY;
        /**
         * The tooltip popup.
         */
        @Nullable
        TooltipPopup mTooltipPopup;
        /**
         * Set to true if the tooltip was shown as a result of a long click.
         */
        boolean mTooltipFromLongClick;
        /**
         * Keep these Runnables so that they can be used to reschedule.
         */
        Runnable mShowTooltipRunnable;
        Runnable mHideTooltipRunnable;
    }
    TooltipInfo mTooltipInfo;
    // Temporary values used to hold (x,y) coordinates when delegating from the
    // Temporary values used to hold (x,y) coordinates when delegating from the
    // two-arg performLongClick() method to the legacy no-arg version.
    // two-arg performLongClick() method to the legacy no-arg version.
    private float mLongClickX = Float.NaN;
    private float mLongClickX = Float.NaN;
@@ -4576,6 +4617,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                    }
                    }
                    break;
                    break;
                case R.styleable.View_tooltip:
                    setTooltip(a.getText(attr));
                    break;
            }
            }
        }
        }
@@ -5712,6 +5756,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            final boolean isAnchored = !Float.isNaN(x) && !Float.isNaN(y);
            final boolean isAnchored = !Float.isNaN(x) && !Float.isNaN(y);
            handled = isAnchored ? showContextMenu(x, y) : showContextMenu();
            handled = isAnchored ? showContextMenu(x, y) : showContextMenu();
        }
        }
        if ((mViewFlags & TOOLTIP) == TOOLTIP) {
            if (!handled) {
                handled = showLongClickTooltip((int) x, (int) y);
            }
        }
        if (handled) {
        if (handled) {
            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
        }
        }
@@ -10603,19 +10652,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                return true;
                return true;
            }
            }
            if (event.getRepeatCount() == 0) {
                // Long clickable items don't necessarily have to be clickable.
                // Long clickable items don't necessarily have to be clickable.
            if (((mViewFlags & CLICKABLE) == CLICKABLE
                final boolean clickable = (mViewFlags & CLICKABLE) == CLICKABLE
                    || (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                        || (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE;
                    && (event.getRepeatCount() == 0)) {
                if (clickable || (mViewFlags & TOOLTIP) == TOOLTIP) {
                    // For the purposes of menu anchoring and drawable hotspots,
                    // For the purposes of menu anchoring and drawable hotspots,
                    // key events are considered to be at the center of the view.
                    // key events are considered to be at the center of the view.
                    final float x = getWidth() / 2f;
                    final float x = getWidth() / 2f;
                    final float y = getHeight() / 2f;
                    final float y = getHeight() / 2f;
                    if (clickable) {
                        setPressed(true, x, y);
                        setPressed(true, x, y);
                    }
                    checkForLongClick(0, x, y);
                    checkForLongClick(0, x, y);
                    return true;
                    return true;
                }
                }
            }
            }
        }
        return false;
        return false;
    }
    }
@@ -11160,15 +11213,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        final int viewFlags = mViewFlags;
        final int viewFlags = mViewFlags;
        final int action = event.getAction();
        final int action = event.getAction();
        final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
        if ((viewFlags & ENABLED_MASK) == DISABLED) {
        if ((viewFlags & ENABLED_MASK) == DISABLED) {
            if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
            if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
                setPressed(false);
                setPressed(false);
            }
            }
            // A disabled view that is clickable still consumes the touch
            // A disabled view that is clickable still consumes the touch
            // events, it just doesn't respond to them.
            // events, it just doesn't respond to them.
            return (((viewFlags & CLICKABLE) == CLICKABLE
            return clickable;
                    || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                    || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
        }
        }
        if (mTouchDelegate != null) {
        if (mTouchDelegate != null) {
            if (mTouchDelegate.onTouchEvent(event)) {
            if (mTouchDelegate.onTouchEvent(event)) {
@@ -11176,11 +11231,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            }
            }
        }
        }
        if (((viewFlags & CLICKABLE) == CLICKABLE ||
        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
                (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
            switch (action) {
            switch (action) {
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_UP:
                    if ((viewFlags & TOOLTIP) == TOOLTIP) {
                        handleTooltipUp();
                    }
                    if (!clickable) {
                        removeTapCallback();
                        removeLongPressCallback();
                        mInContextButtonPress = false;
                        mHasPerformedLongPress = false;
                        mIgnoreNextUpEvent = false;
                        break;
                    }
                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                        // take focus if we don't have it already and we should in
                        // take focus if we don't have it already and we should in
@@ -11236,6 +11300,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                case MotionEvent.ACTION_DOWN:
                case MotionEvent.ACTION_DOWN:
                    mHasPerformedLongPress = false;
                    mHasPerformedLongPress = false;
                    if (!clickable) {
                        checkForLongClick(0, x, y);
                        break;
                    }
                    if (performButtonActionOnTouchDown(event)) {
                    if (performButtonActionOnTouchDown(event)) {
                        break;
                        break;
                    }
                    }
@@ -11261,7 +11330,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                    break;
                    break;
                case MotionEvent.ACTION_CANCEL:
                case MotionEvent.ACTION_CANCEL:
                    if (clickable) {
                        setPressed(false);
                        setPressed(false);
                    }
                    removeTapCallback();
                    removeTapCallback();
                    removeLongPressCallback();
                    removeLongPressCallback();
                    mInContextButtonPress = false;
                    mInContextButtonPress = false;
@@ -11270,16 +11341,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                    break;
                    break;
                case MotionEvent.ACTION_MOVE:
                case MotionEvent.ACTION_MOVE:
                    if (clickable) {
                        drawableHotspotChanged(x, y);
                        drawableHotspotChanged(x, y);
                    }
                    // Be lenient about moving outside of buttons
                    // Be lenient about moving outside of buttons
                    if (!pointInView(x, y, mTouchSlop)) {
                    if (!pointInView(x, y, mTouchSlop)) {
                        // Outside button
                        // Outside button
                        removeTapCallback();
                        if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
                        // Remove any future long press/tap checks
                        // Remove any future long press/tap checks
                        removeTapCallback();
                        removeLongPressCallback();
                        removeLongPressCallback();
                        if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
                            setPressed(false);
                            setPressed(false);
                        }
                        }
                    }
                    }
@@ -15379,6 +15451,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        cleanupDraw();
        cleanupDraw();
        mCurrentAnimation = null;
        mCurrentAnimation = null;
        if ((mViewFlags & TOOLTIP) == TOOLTIP) {
            hideTooltip();
        }
    }
    }
    private void cleanupDraw() {
    private void cleanupDraw() {
@@ -21031,7 +21107,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    }
    }
    private void checkForLongClick(int delayOffset, float x, float y) {
    private void checkForLongClick(int delayOffset, float x, float y) {
        if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
        if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE || (mViewFlags & TOOLTIP) == TOOLTIP) {
            mHasPerformedLongPress = false;
            mHasPerformedLongPress = false;
            if (mPendingCheckForLongPress == null) {
            if (mPendingCheckForLongPress == null) {
@@ -21039,6 +21115,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            }
            }
            mPendingCheckForLongPress.setAnchor(x, y);
            mPendingCheckForLongPress.setAnchor(x, y);
            mPendingCheckForLongPress.rememberWindowAttachCount();
            mPendingCheckForLongPress.rememberWindowAttachCount();
            mPendingCheckForLongPress.rememberPressedState();
            postDelayed(mPendingCheckForLongPress,
            postDelayed(mPendingCheckForLongPress,
                    ViewConfiguration.getLongPressTimeout() - delayOffset);
                    ViewConfiguration.getLongPressTimeout() - delayOffset);
        }
        }
@@ -22439,10 +22516,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        private int mOriginalWindowAttachCount;
        private int mOriginalWindowAttachCount;
        private float mX;
        private float mX;
        private float mY;
        private float mY;
        private boolean mOriginalPressedState;
        @Override
        @Override
        public void run() {
        public void run() {
            if (isPressed() && (mParent != null)
            if ((mOriginalPressedState == isPressed()) && (mParent != null)
                    && mOriginalWindowAttachCount == mWindowAttachCount) {
                    && mOriginalWindowAttachCount == mWindowAttachCount) {
                if (performLongClick(mX, mY)) {
                if (performLongClick(mX, mY)) {
                    mHasPerformedLongPress = true;
                    mHasPerformedLongPress = true;
@@ -22458,6 +22536,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        public void rememberWindowAttachCount() {
        public void rememberWindowAttachCount() {
            mOriginalWindowAttachCount = mWindowAttachCount;
            mOriginalWindowAttachCount = mWindowAttachCount;
        }
        }
        public void rememberPressedState() {
            mOriginalPressedState = isPressed();
        }
    }
    }
    private final class CheckForTap implements Runnable {
    private final class CheckForTap implements Runnable {
@@ -23246,6 +23328,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
         */
         */
        public Surface mDragSurface;
        public Surface mDragSurface;
        /**
         * The view that currently has a tooltip displayed.
         */
        View mTooltipHost;
        /**
        /**
         * Creates a new set of attachment information with the specified
         * Creates a new set of attachment information with the specified
         * events handler and thread.
         * events handler and thread.
@@ -23982,4 +24070,167 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        return mAttachInfo.mTmpLocation[0] == insets.getStableInsetLeft()
        return mAttachInfo.mTmpLocation[0] == insets.getStableInsetLeft()
                && mAttachInfo.mTmpLocation[1] == insets.getStableInsetTop();
                && mAttachInfo.mTmpLocation[1] == insets.getStableInsetTop();
    }
    }
    /**
     * Sets the tooltip text which will be displayed in a small popup next to the view.
     * <p>
     * The tooltip will be displayed:
     * <li>On long click, unless is not handled otherwise (by OnLongClickListener or a context
     * menu). </li>
     * <li>On hover, after a brief delay since the pointer has stopped moving </li>
     *
     * @param tooltip the tooltip text, or null if no tooltip is required
     */
    public final void setTooltip(@Nullable CharSequence tooltip) {
        if (TextUtils.isEmpty(tooltip)) {
            setFlags(0, TOOLTIP);
            hideTooltip();
            mTooltipInfo = null;
        } else {
            setFlags(TOOLTIP, TOOLTIP);
            if (mTooltipInfo == null) {
                mTooltipInfo = new TooltipInfo();
                mTooltipInfo.mShowTooltipRunnable = this::showHoverTooltip;
                mTooltipInfo.mHideTooltipRunnable = this::hideTooltip;
            }
            mTooltipInfo.mTooltip = tooltip;
            if (mTooltipInfo.mTooltipPopup != null && mTooltipInfo.mTooltipPopup.isShowing()) {
                mTooltipInfo.mTooltipPopup.updateContent(mTooltipInfo.mTooltip);
            }
        }
    }
    /**
     * Returns the view's tooltip text.
     *
     * @return the tooltip text
     */
    @Nullable
    public final CharSequence getTooltip() {
        return mTooltipInfo != null ? mTooltipInfo.mTooltip : null;
    }
    private boolean showTooltip(int x, int y, boolean fromLongClick) {
        if (mAttachInfo == null) {
            return false;
        }
        if ((mViewFlags & ENABLED_MASK) != ENABLED) {
            return false;
        }
        final CharSequence tooltipText = getTooltip();
        if (TextUtils.isEmpty(tooltipText)) {
            return false;
        }
        hideTooltip();
        mTooltipInfo.mTooltipFromLongClick = fromLongClick;
        mTooltipInfo.mTooltipPopup = new TooltipPopup(getContext());
        mTooltipInfo.mTooltipPopup.show(this, x, y, tooltipText);
        mAttachInfo.mTooltipHost = this;
        return true;
    }
    void hideTooltip() {
        if (mTooltipInfo == null) {
            return;
        }
        removeCallbacks(mTooltipInfo.mShowTooltipRunnable);
        if (mTooltipInfo.mTooltipPopup == null) {
            return;
        }
        mTooltipInfo.mTooltipPopup.hide();
        mTooltipInfo.mTooltipPopup = null;
        mTooltipInfo.mTooltipFromLongClick = false;
        if (mAttachInfo != null) {
            mAttachInfo.mTooltipHost = null;
        }
    }
    private boolean showLongClickTooltip(int x, int y) {
        removeCallbacks(mTooltipInfo.mShowTooltipRunnable);
        removeCallbacks(mTooltipInfo.mHideTooltipRunnable);
        return showTooltip(x, y, true);
    }
    private void showHoverTooltip() {
        showTooltip(mTooltipInfo.mAnchorX, mTooltipInfo.mAnchorY, false);
    }
    boolean dispatchTooltipHoverEvent(MotionEvent event) {
        if (mTooltipInfo == null) {
            return false;
        }
        switch(event.getAction()) {
            case MotionEvent.ACTION_HOVER_MOVE:
                if ((mViewFlags & TOOLTIP) != TOOLTIP || (mViewFlags & ENABLED_MASK) != ENABLED) {
                    break;
                }
                if (!mTooltipInfo.mTooltipFromLongClick) {
                    if (mTooltipInfo.mTooltipPopup == null) {
                        // Schedule showing the tooltip after a timeout.
                        mTooltipInfo.mAnchorX = (int) event.getX();
                        mTooltipInfo.mAnchorY = (int) event.getY();
                        removeCallbacks(mTooltipInfo.mShowTooltipRunnable);
                        postDelayed(mTooltipInfo.mShowTooltipRunnable,
                                ViewConfiguration.getHoverTooltipShowTimeout());
                    }
                    // Hide hover-triggered tooltip after a period of inactivity.
                    // Match the timeout used by NativeInputManager to hide the mouse pointer
                    // (depends on SYSTEM_UI_FLAG_LOW_PROFILE being set).
                    final int timeout;
                    if ((getWindowSystemUiVisibility() & SYSTEM_UI_FLAG_LOW_PROFILE)
                            == SYSTEM_UI_FLAG_LOW_PROFILE) {
                        timeout = ViewConfiguration.getHoverTooltipHideShortTimeout();
                    } else {
                        timeout = ViewConfiguration.getHoverTooltipHideTimeout();
                    }
                    removeCallbacks(mTooltipInfo.mHideTooltipRunnable);
                    postDelayed(mTooltipInfo.mHideTooltipRunnable, timeout);
                }
                return true;
            case MotionEvent.ACTION_HOVER_EXIT:
                if (!mTooltipInfo.mTooltipFromLongClick) {
                    hideTooltip();
                }
                break;
        }
        return false;
    }
    void handleTooltipKey(KeyEvent event) {
        switch (event.getAction()) {
            case KeyEvent.ACTION_DOWN:
                if (event.getRepeatCount() == 0) {
                    hideTooltip();
                }
                break;
            case KeyEvent.ACTION_UP:
                handleTooltipUp();
                break;
        }
    }
    private void handleTooltipUp() {
        if (mTooltipInfo == null || mTooltipInfo.mTooltipPopup == null) {
            return;
        }
        removeCallbacks(mTooltipInfo.mHideTooltipRunnable);
        postDelayed(mTooltipInfo.mHideTooltipRunnable,
                ViewConfiguration.getLongPressTooltipHideTimeout());
    }
    /**
     * @return The content view of the tooltip popup currently being shown, or null if the tooltip
     * is not showing.
     * @hide
     */
    @TestApi
    public View getTooltipView() {
        if (mTooltipInfo == null || mTooltipInfo.mTooltipPopup == null) {
            return null;
        }
        return mTooltipInfo.mTooltipPopup.getContentView();
    }
}
}
+63 −0
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package android.view;
package android.view;


import android.annotation.TestApi;
import android.app.AppGlobals;
import android.app.AppGlobals;
import android.content.Context;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Configuration;
@@ -229,6 +230,29 @@ public class ViewConfiguration {
     */
     */
    private static final long ACTION_MODE_HIDE_DURATION_DEFAULT = 2000;
    private static final long ACTION_MODE_HIDE_DURATION_DEFAULT = 2000;


    /**
     * Defines the duration in milliseconds before an end of a long press causes a tooltip to be
     * hidden.
     */
    private static final int LONG_PRESS_TOOLTIP_HIDE_TIMEOUT = 1500;

    /**
     * Defines the duration in milliseconds before a hover event causes a tooltip to be shown.
     */
    private static final int HOVER_TOOLTIP_SHOW_TIMEOUT = 500;

    /**
     * Defines the duration in milliseconds before mouse inactivity causes a tooltip to be hidden.
     * (default variant to be used when {@link View#SYSTEM_UI_FLAG_LOW_PROFILE} is not set).
     */
    private static final int HOVER_TOOLTIP_HIDE_TIMEOUT = 15000;

    /**
     * Defines the duration in milliseconds before mouse inactivity causes a tooltip to be hidden
     * (short version to be used when {@link View#SYSTEM_UI_FLAG_LOW_PROFILE} is set).
     */
    private static final int HOVER_TOOLTIP_HIDE_SHORT_TIMEOUT = 3000;

    /**
    /**
     * Configuration values for overriding {@link #hasPermanentMenuKey()} behavior.
     * Configuration values for overriding {@link #hasPermanentMenuKey()} behavior.
     * These constants must match the definition in res/values/config.xml.
     * These constants must match the definition in res/values/config.xml.
@@ -800,4 +824,43 @@ public class ViewConfiguration {
    public boolean isFadingMarqueeEnabled() {
    public boolean isFadingMarqueeEnabled() {
        return mFadingMarqueeEnabled;
        return mFadingMarqueeEnabled;
    }
    }

    /**
     * @return the duration in milliseconds before an end of a long press causes a tooltip to be
     * hidden
     * @hide
     */
    @TestApi
    public static int getLongPressTooltipHideTimeout() {
        return LONG_PRESS_TOOLTIP_HIDE_TIMEOUT;
    }

    /**
     * @return the duration in milliseconds before a hover event causes a tooltip to be shown
     * @hide
     */
    @TestApi
    public static int getHoverTooltipShowTimeout() {
        return HOVER_TOOLTIP_SHOW_TIMEOUT;
    }

    /**
     * @return the duration in milliseconds before mouse inactivity causes a tooltip to be hidden
     * (default variant to be used when {@link View#SYSTEM_UI_FLAG_LOW_PROFILE} is not set).
     * @hide
     */
    @TestApi
    public static int getHoverTooltipHideTimeout() {
        return HOVER_TOOLTIP_HIDE_TIMEOUT;
    }

    /**
     * @return the duration in milliseconds before mouse inactivity causes a tooltip to be hidden
     * (shorter variant to be used when {@link View#SYSTEM_UI_FLAG_LOW_PROFILE} is set).
     * @hide
     */
    @TestApi
    public static int getHoverTooltipHideShortTimeout() {
        return HOVER_TOOLTIP_HIDE_SHORT_TIMEOUT;
    }
}
}
Loading