From 20a12da3ce5bf1bea6e580ef36e92ce26b21b0ea Mon Sep 17 00:00:00 2001 From: Vladislav Kaznacheev Date: Tue, 6 Dec 2016 14:30:17 -0800 Subject: [PATCH] Adjust tooltip layout Position a mouse hover tooltip closer to the target. Limit tooltip width and make it multiline (maxLines=3). Show a long press tooltip above the target, not below. Bug: 33352391 Bug: 33353823 Bug: 33354000 Test: manual Change-Id: Ie00353d715d73d432b5d892a0a7c04508a003a78 --- core/java/android/view/View.java | 20 +++++++++-- .../android/internal/view/TooltipPopup.java | 34 +++++++++++++------ core/res/res/layout/tooltip.xml | 8 +++-- core/res/res/values/dimens.xml | 12 ++++--- core/res/res/values/symbols.xml | 4 ++- 5 files changed, 58 insertions(+), 20 deletions(-) diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 12658bd8f0cef..5f7c9dab9768f 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -2477,7 +2477,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 1 PFLAG3_ASSIST_BLOCKED * 1 PFLAG3_CLUSTER * 1 PFLAG3_SECTION - * xxxxxx * NO LONGER NEEDED, SHOULD BE REUSED * + * 1 PFLAG3_FINGER_DOWN + * xxxxx * NO LONGER NEEDED, SHOULD BE REUSED * * 1 PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE * 1 PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED * 1 PFLAG3_TEMPORARY_DETACH @@ -2691,6 +2692,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private static final int PFLAG3_SECTION = 0x10000; + /** + * Indicates that the user is currently touching the screen. + * Currently used for the tooltip positioning only. + */ + private static final int PFLAG3_FINGER_DOWN = 0x20000; + /** * Whether this view has rendered elements that overlap (see {@link * #hasOverlappingRendering()}, {@link #forceHasOverlappingRendering(boolean)}, and @@ -10661,6 +10668,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (isPressed()) { setPressed(false); } + mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN; if (imm != null && (mPrivateFlags & PFLAG_FOCUSED) != 0) { imm.focusOut(this); } @@ -11562,6 +11570,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) { setPressed(false); } + mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN; // A disabled view that is clickable still consumes the touch // events, it just doesn't respond to them. return clickable; @@ -11575,6 +11584,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) { switch (action) { case MotionEvent.ACTION_UP: + mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN; if ((viewFlags & TOOLTIP) == TOOLTIP) { handleTooltipUp(); } @@ -11639,6 +11649,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, break; case MotionEvent.ACTION_DOWN: + if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) { + mPrivateFlags3 |= PFLAG3_FINGER_DOWN; + } mHasPerformedLongPress = false; if (!clickable) { @@ -11679,6 +11692,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mInContextButtonPress = false; mHasPerformedLongPress = false; mIgnoreNextUpEvent = false; + mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN; break; case MotionEvent.ACTION_MOVE: @@ -11695,6 +11709,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if ((mPrivateFlags & PFLAG_PRESSED) != 0) { setPressed(false); } + mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN; } break; } @@ -24440,7 +24455,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, hideTooltip(); mTooltipInfo.mTooltipFromLongClick = fromLongClick; mTooltipInfo.mTooltipPopup = new TooltipPopup(getContext()); - mTooltipInfo.mTooltipPopup.show(this, x, y, tooltipText); + final boolean fromTouch = (mPrivateFlags3 & PFLAG3_FINGER_DOWN) == PFLAG3_FINGER_DOWN; + mTooltipInfo.mTooltipPopup.show(this, x, y, fromTouch, tooltipText); mAttachInfo.mTooltipHost = this; return true; } diff --git a/core/java/com/android/internal/view/TooltipPopup.java b/core/java/com/android/internal/view/TooltipPopup.java index 4f48b9645c696..c5f6356e92b17 100644 --- a/core/java/com/android/internal/view/TooltipPopup.java +++ b/core/java/com/android/internal/view/TooltipPopup.java @@ -55,14 +55,15 @@ public class TooltipPopup { | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; } - public void show(View anchorView, int anchorX, int anchorY, CharSequence tooltipText) { + public void show(View anchorView, int anchorX, int anchorY, boolean fromTouch, + CharSequence tooltipText) { if (isShowing()) { hide(); } mMessageView.setText(tooltipText); - computePosition(anchorView, anchorX, anchorY, mLayoutParams); + computePosition(anchorView, anchorX, anchorY, fromTouch, mLayoutParams); WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); wm.addView(mContentView, mLayoutParams); @@ -89,7 +90,7 @@ public class TooltipPopup { mMessageView.setText(tooltipText); } - private void computePosition(View anchorView, int anchorX, int anchorY, + private void computePosition(View anchorView, int anchorX, int anchorY, boolean fromTouch, WindowManager.LayoutParams outParams) { final int tooltipPreciseAnchorThreshold = mContext.getResources().getDimensionPixelOffset( com.android.internal.R.dimen.tooltip_precise_anchor_threshold); @@ -107,8 +108,10 @@ public class TooltipPopup { final int offsetAbove; if (anchorView.getHeight() >= tooltipPreciseAnchorThreshold) { // Tall view. Align the tooltip vertically to the precise Y position. - offsetBelow = anchorY; - offsetAbove = anchorY; + final int offsetExtra = mContext.getResources().getDimensionPixelOffset( + com.android.internal.R.dimen.tooltip_precise_anchor_extra_offset); + offsetBelow = anchorY + offsetExtra; + offsetAbove = anchorY - offsetExtra; } else { // Otherwise anchor the tooltip to the view center. offsetBelow = anchorView.getHeight(); // Place below the view in most cases. @@ -118,20 +121,31 @@ public class TooltipPopup { outParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP; final int tooltipOffset = mContext.getResources().getDimensionPixelOffset( - com.android.internal.R.dimen.tooltip_y_offset); + fromTouch ? com.android.internal.R.dimen.tooltip_y_offset_touch + : com.android.internal.R.dimen.tooltip_y_offset_non_touch); anchorView.getWindowVisibleDisplayFrame(mTmpDisplayFrame); anchorView.getLocationInWindow(mTmpAnchorPos); outParams.x = mTmpAnchorPos[0] + offsetX - mTmpDisplayFrame.width() / 2; - outParams.y = mTmpAnchorPos[1] + offsetBelow + tooltipOffset; final int spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); mContentView.measure(spec, spec); final int tooltipHeight = mContentView.getMeasuredHeight(); - if (outParams.y + tooltipHeight > mTmpDisplayFrame.height()) { - // The tooltip does not fit below the anchor point, show above instead. - outParams.y = mTmpAnchorPos[1] + offsetAbove - (tooltipOffset + tooltipHeight); + final int yAbove = mTmpAnchorPos[1] + offsetAbove - tooltipOffset - tooltipHeight; + final int yBelow = mTmpAnchorPos[1] + offsetBelow + tooltipOffset; + if (fromTouch) { + if (yAbove >= 0) { + outParams.y = yAbove; + } else { + outParams.y = yBelow; + } + } else { + if (yBelow + tooltipHeight <= mTmpDisplayFrame.height()) { + outParams.y = yBelow; + } else { + outParams.y = yAbove; + } } } } diff --git a/core/res/res/layout/tooltip.xml b/core/res/res/layout/tooltip.xml index 0aa6a8781d91e..376c5eb125f4d 100644 --- a/core/res/res/layout/tooltip.xml +++ b/core/res/res/layout/tooltip.xml @@ -25,15 +25,17 @@ diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 5efa55ca11388..e3b7f02c35dab 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -489,17 +489,21 @@ 720dp - - 20dp - - 32dp + + 16dp + + 0dp 8dp 16dp + + 6.5dp 2dp 96dp + + 8dp diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index ac502e0f803bb..989a79db78378 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -437,7 +437,9 @@ - + + +