Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(922)

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/widget/TextBubble.java

Issue 810853003: Upstream FullscreenManager (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Update comments Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/widget/TextBubble.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/TextBubble.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/TextBubble.java
new file mode 100644
index 0000000000000000000000000000000000000000..8e38cbaa0e19d27acf1b1a06d0ef4d66ec780491
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/TextBubble.java
@@ -0,0 +1,361 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.View.OnAttachStateChangeListener;
+import android.view.View.OnLayoutChangeListener;
+import android.view.ViewGroup;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+
+import org.chromium.chrome.R;
+import org.chromium.ui.base.LocalizationUtils;
+
+/**
+ * UI component that handles showing text bubbles.
+ */
+public class TextBubble
+ extends PopupWindow implements OnLayoutChangeListener, OnAttachStateChangeListener {
+ /** Whether to use the intrinsic padding of the bubble background as padding (boolean). */
+ public static final String BACKGROUND_INTRINSIC_PADDING = "Background_Intrinsic_Padding";
+
+ /**
+ * Boolean to be used for deciding whether the bubble should be anchored above or below
+ * the view
+ */
+ public static final String UP_DOWN = "Up_Down";
+
+ /** Style resource Id to be used for text inside the bubble. Should be of type int. */
+ public static final String TEXT_STYLE_ID = "Text_Style_Id";
+
+ /** Boolean to be used for deciding whether the bubble should be centered to the view */
+ public static final String CENTER = "Center";
+
+ public static final String ANIM_STYLE_ID = "Animation_Style";
+
+ private final int mTooltipEdgeMargin;
+ private final int mTooltipTopMargin;
+ private final int mBubbleTipXMargin;
+ private boolean mAnchorBelow = false;
+ private boolean mCenterView = true;
+ private int mXPosition;
+ private int mYPosition;
+ private View mAnchorView;
+ private final Rect mCachedPaddingRect = new Rect();
+
+ // The text view inside the popup containing the tooltip text.
+ private final TextView mTooltipText;
+
+ /**
+ * Constructor that uses a bundle object to fetch resources and optional boolean
+ * values for the {@link TextBubble}.
+ *
+ * Use CENTER for centering the tip to the anchor view.
+ * UP_DOWN for drawing the bubble with tip pointing up or down.
+ * Up is true and Down is false.
+ * LAYOUT_WIDTH_ID Dimension resource Id for the width of the {@link TextView} inside the
+ * bubble. The height is set to half of this value.
+ *
+ * @param context
+ * @param res Bundle object that contains resource ids and optional flags.
+ */
+ public TextBubble(Context context, Bundle res) {
+ mAnchorBelow = (res.containsKey(UP_DOWN) ? res.getBoolean(UP_DOWN) : true);
+ mCenterView = (res.containsKey(CENTER) ? res.getBoolean(CENTER) : true);
+ mTooltipEdgeMargin =
+ context.getResources().getDimensionPixelSize(R.dimen.tooltip_min_edge_margin);
+ mTooltipTopMargin =
+ context.getResources().getDimensionPixelSize(R.dimen.tooltip_top_margin);
+ mBubbleTipXMargin = context.getResources().getDimensionPixelSize(R.dimen.bubble_tip_margin);
+
+ setBackgroundDrawable(new BubbleBackgroundDrawable(context, res));
+ setAnimationStyle(res.containsKey(ANIM_STYLE_ID) ? res.getInt(ANIM_STYLE_ID)
+ : android.R.style.Animation);
+
+ mTooltipText = new TextView(context);
+ mTooltipText.setTextAppearance(context,
+ (res.containsKey(TEXT_STYLE_ID) ? res.getInt(TEXT_STYLE_ID) : R.style.info_bubble));
+
+ setContentView(mTooltipText);
+ setWindowLayoutMode(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ }
+
+ /**
+ * @return The textview for the bubble text.
+ */
+ public TextView getBubbleTextView() {
+ return mTooltipText;
+ }
+
+ /**
+ * Shows a text bubble anchored to the given view.
+ *
+ * @param text The text to be shown.
+ * @param anchorView The view that the bubble should be anchored to.
+ * @param maxWidth The maximum width of the text bubble.
+ * @param maxHeight The maximum height of the text bubble.
+ */
+ public void showTextBubble(String text, View anchorView, int maxWidth, int maxHeight) {
+ mTooltipText.setText(text);
+ mTooltipText.measure(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST));
+ mAnchorView = anchorView;
+ calculateNewPosition();
+ showAtCalculatedPosition();
+ }
+
+ /**
+ * Calculates the new position for the bubble, updating mXPosition, mYPosition, mYOffset and
+ * the bubble arrow offset information without updating the UI. To see the changes,
+ * showAtCalculatedPosition should be called explicitly.
+ */
+ private void calculateNewPosition() {
+ View offsetView = mAnchorView;
+ int xOffset = 0;
+ int yOffset = 0;
+ if (mAnchorBelow) yOffset = mAnchorView.getHeight();
+
+ while (offsetView != null) {
+ xOffset += offsetView.getLeft();
+ yOffset += offsetView.getTop();
+ if (!(offsetView.getParent() instanceof View)) break;
+ offsetView = (View) offsetView.getParent();
+ }
+
+ if (mCenterView) {
+ // Center the tooltip over the view (calculating the width of the tooltip text).
+ xOffset += mAnchorView.getWidth() / 2;
+ } else if (LocalizationUtils.isLayoutRtl()) {
+ xOffset += mAnchorView.getWidth();
+ }
+
+ int tooltipWidth = mTooltipText.getMeasuredWidth();
+ xOffset -= tooltipWidth / 2;
+
+ // Account for the padding of the bubble background to ensure it is centered properly.
+ getBackground().getPadding(mCachedPaddingRect);
+ tooltipWidth += mCachedPaddingRect.left + mCachedPaddingRect.right;
+ xOffset -= mCachedPaddingRect.left;
+
+ int defaultXOffset = xOffset;
+
+ View rootView = mAnchorView.getRootView();
+ // Make sure the tooltip does not get rendered off the screen.
+ if (xOffset + tooltipWidth > rootView.getWidth()) {
+ xOffset = rootView.getWidth() - tooltipWidth - mTooltipEdgeMargin;
+ } else if (xOffset < 0) {
+ xOffset = mTooltipEdgeMargin;
+ }
+
+ // Move the bubble arrow to be centered over the anchor view.
+ int newOffset = -(xOffset - defaultXOffset);
+ if (Math.abs(newOffset) > mTooltipText.getMeasuredWidth() / 2 - mBubbleTipXMargin) {
+ newOffset = (mTooltipText.getMeasuredWidth() / 2 - mBubbleTipXMargin)
+ * (int) Math.signum(newOffset);
+ }
+ ((BubbleBackgroundDrawable) getBackground()).setBubbleArrowXOffset(newOffset);
+
+ if (mAnchorBelow) {
+ mXPosition = xOffset;
+ mYPosition = yOffset - mTooltipTopMargin;
+ } else {
+ mXPosition = xOffset;
+ mYPosition = mAnchorView.getRootView().getHeight() - yOffset + mTooltipTopMargin;
+ }
+ }
+
+ /**
+ * Shows the TextBubble in the precalculated position. Should be called after mXPosition
+ * and MYPosition has been set.
+ */
+ private void showAtCalculatedPosition() {
+ if (mAnchorBelow) {
+ showAtLocation(
+ mAnchorView.getRootView(), Gravity.TOP | Gravity.START, mXPosition, mYPosition);
+ } else {
+ showAtLocation(mAnchorView.getRootView(), Gravity.BOTTOM | Gravity.START, mXPosition,
+ mYPosition);
+ }
+ }
+
+ // The two functions below are used for the floating animation.
+
+ /**
+ * Updates the y offset of the popup bubble (applied in addition to
+ * the default calculated offset).
+ * @param yoffset The new mYOffset to be used.
+ */
+ public void setOffsetY(int yoffset) {
+ update(mXPosition, mYPosition + yoffset, -1, -1);
+ }
+
+ /**
+ * Updates the position information and checks whether any positional change will occur. This
+ * method doesn't change the {@link TextBubble} if it is showing.
+ * @return Whether the TextBubble needs to be redrawn.
+ */
+ private boolean updatePosition() {
+ int previousX = mXPosition;
+ int previousY = mYPosition;
+ int previousOffset = ((BubbleBackgroundDrawable) getBackground()).getBubbleArrowOffset();
+ calculateNewPosition();
+ if (previousX != mXPosition || previousY != mYPosition
+ || previousOffset
+ != ((BubbleBackgroundDrawable) getBackground()).getBubbleArrowOffset()) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
+ int oldTop, int oldRight, int oldBottom) {
+ boolean willDisappear = !mAnchorView.isShown();
+ boolean changePosition = updatePosition();
+ if (willDisappear) {
+ dismiss();
+ } else if (changePosition) {
+ dismiss();
+ showAtCalculatedPosition();
+ }
+ }
+
+ @Override
+ public void onViewAttachedToWindow(View v) {}
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ dismiss();
+ }
+
+ /**
+ * Drawable for rendering the background for a popup bubble.
+ *
+ * <p>Using a custom class as the LayerDrawable handles padding oddly and did not allow the
+ * bubble arrow to be rendered below the content portion of the bubble if you specified
+ * padding, which is required to make it look nice.
+ */
+ static class BubbleBackgroundDrawable extends Drawable {
+ private final int mTooltipBorderWidth;
+ private final Rect mTooltipContentPadding;
+
+ private final Drawable mBubbleContentsDrawable;
+ private final BitmapDrawable mBubbleArrowDrawable;
+ private boolean mUp = false;
+ private int mBubbleArrowXOffset;
+
+ BubbleBackgroundDrawable(Context context, Bundle res) {
+ mUp = (res.containsKey(UP_DOWN) ? res.getBoolean(UP_DOWN) : true);
+ mBubbleContentsDrawable = context.getResources().getDrawable(R.drawable.bubble_white);
+ mBubbleArrowDrawable = (BitmapDrawable) context.getResources().getDrawable(
+ R.drawable.bubble_point_white);
+ mTooltipBorderWidth =
+ context.getResources().getDimensionPixelSize(R.dimen.tooltip_border_width);
+
+ if (res.getBoolean(BACKGROUND_INTRINSIC_PADDING, false)) {
+ mTooltipContentPadding = new Rect();
+ mBubbleContentsDrawable.getPadding(mTooltipContentPadding);
+ } else {
+ int padding = context.getResources().getDimensionPixelSize(
+ R.dimen.tooltip_content_padding);
+ mTooltipContentPadding = new Rect(padding, padding, padding, padding);
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ mBubbleContentsDrawable.draw(canvas);
+ mBubbleArrowDrawable.draw(canvas);
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ if (bounds == null) return;
+
+ super.onBoundsChange(bounds);
+ int halfArrowWidth = mBubbleArrowDrawable.getIntrinsicWidth() / 2;
+ int halfBoundsWidth = bounds.width() / 2;
+ if (mUp) {
+ int contentsTop = bounds.top + mBubbleArrowDrawable.getIntrinsicHeight()
+ - mTooltipBorderWidth;
+ mBubbleContentsDrawable.setBounds(
+ bounds.left, contentsTop, bounds.right, bounds.bottom);
+ mBubbleArrowDrawable.setBounds(
+ mBubbleArrowXOffset + halfBoundsWidth - halfArrowWidth, bounds.top,
+ mBubbleArrowXOffset + halfBoundsWidth + halfArrowWidth,
+ bounds.top + mBubbleArrowDrawable.getIntrinsicHeight());
+ } else {
+ int contentsBottom = bounds.bottom - mBubbleArrowDrawable.getIntrinsicHeight();
+ mBubbleContentsDrawable.setBounds(
+ bounds.left, bounds.left, bounds.right, contentsBottom);
+ mBubbleArrowDrawable.setBounds(
+ mBubbleArrowXOffset + halfBoundsWidth - halfArrowWidth,
+ contentsBottom - mTooltipBorderWidth,
+ mBubbleArrowXOffset + halfBoundsWidth + halfArrowWidth, contentsBottom
+ + mBubbleArrowDrawable.getIntrinsicHeight() - mTooltipBorderWidth);
+ }
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ mBubbleContentsDrawable.setAlpha(alpha);
+ mBubbleArrowDrawable.setAlpha(alpha);
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ // Not supported.
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ @Override
+ public boolean getPadding(Rect padding) {
+ padding.set(mTooltipContentPadding);
+ if (mUp) {
+ padding.set(padding.left, padding.top + mBubbleArrowDrawable.getIntrinsicHeight(),
+ padding.right, padding.bottom);
+ } else {
+ padding.set(padding.left, padding.top, padding.right,
+ padding.bottom + mBubbleArrowDrawable.getIntrinsicHeight());
+ }
+
+ return true;
+ }
+
+ /**
+ * Updates the additional X Offset for the bubble arrow. The arrow defaults to being
+ * centered in the bubble, so this is delta from the center.
+ *
+ * @param xOffset The offset of the bubble arrow.
+ */
+ public void setBubbleArrowXOffset(int xOffset) {
+ mBubbleArrowXOffset = xOffset;
+ onBoundsChange(getBounds());
+ }
+
+ /**
+ * @return the current x offset for the bubble arrow.
+ */
+ public int getBubbleArrowOffset() {
+ return mBubbleArrowXOffset;
+ }
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698