| // Copyright 2013 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; |
| |
| import android.content.res.Resources; |
| import android.graphics.Point; |
| import android.graphics.RectF; |
| import android.text.TextUtils; |
| import android.view.Gravity; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.widget.PopupWindow; |
| import android.widget.RelativeLayout; |
| import android.widget.TextView; |
| import org.chromium.base.ApiCompatibilityUtils; |
| import org.chromium.base.CalledByNative; |
| import org.chromium.chrome.R; |
| import org.chromium.content.browser.ContentViewCore; |
| import org.chromium.content.browser.RenderCoordinates; |
| |
| /** |
| * This class is an implementation of validation message bubble UI. |
| */ |
| class ValidationMessageBubble { |
| private PopupWindow mPopup; |
| |
| /** |
| * Creates a popup window to show the specified messages, and show it on |
| * the specified anchor rectangle. |
| * |
| * @param contentViewCore The ContentViewCore object to provide various |
| * information. |
| * @param anchorX Anchor position in the CSS unit. |
| * @param anchorY Anchor position in the CSS unit. |
| * @param anchorWidth Anchor size in the CSS unit. |
| * @param anchorHeight Anchor size in the CSS unit. |
| * @param mainText The main message. It will shown at the top of the popup |
| * window, and its font size is larger. |
| * @param subText The sub message. It will shown below the main message, and |
| * its font size is smaller. |
| */ |
| @CalledByNative |
| private static ValidationMessageBubble createAndShow( |
| ContentViewCore contentViewCore, int anchorX, int anchorY, |
| int anchorWidth, int anchorHeight, String mainText, String subText) { |
| final RectF anchorPixInScreen = makePixRectInScreen( |
| contentViewCore, anchorX, anchorY, anchorWidth, anchorHeight); |
| return new ValidationMessageBubble(contentViewCore, anchorPixInScreen, mainText, subText); |
| } |
| |
| private ValidationMessageBubble( |
| ContentViewCore contentViewCore, RectF anchor, String mainText, String subText) { |
| final ViewGroup root = (ViewGroup) View.inflate(contentViewCore.getContext(), |
| R.layout.validation_message_bubble, null); |
| mPopup = new PopupWindow(root); |
| updateTextViews(root, mainText, subText); |
| measure(contentViewCore.getRenderCoordinates()); |
| Point origin = adjustWindowPosition( |
| contentViewCore, (int) (anchor.centerX() - getAnchorOffset()), (int) anchor.bottom); |
| mPopup.showAtLocation( |
| contentViewCore.getContainerView(), Gravity.NO_GRAVITY, origin.x, origin.y); |
| } |
| |
| @CalledByNative |
| private void close() { |
| if (mPopup == null) return; |
| mPopup.dismiss(); |
| mPopup = null; |
| } |
| |
| /** |
| * Moves the popup window on the specified anchor rectangle. |
| * |
| * @param contentViewCore The ContentViewCore object to provide various |
| * information. |
| * @param anchorX Anchor position in the CSS unit. |
| * @param anchorY Anchor position in the CSS unit. |
| * @param anchorWidth Anchor size in the CSS unit. |
| * @param anchorHeight Anchor size in the CSS unit. |
| */ |
| @CalledByNative |
| private void setPositionRelativeToAnchor(ContentViewCore contentViewCore, |
| int anchorX, int anchorY, int anchorWidth, int anchorHeight) { |
| RectF anchor = makePixRectInScreen( |
| contentViewCore, anchorX, anchorY, anchorWidth, anchorHeight); |
| Point origin = adjustWindowPosition( |
| contentViewCore, (int) (anchor.centerX() - getAnchorOffset()), (int) anchor.bottom); |
| mPopup.update(origin.x, origin.y, mPopup.getWidth(), mPopup.getHeight()); |
| } |
| |
| private static RectF makePixRectInScreen(ContentViewCore contentViewCore, |
| int anchorX, int anchorY, int anchorWidth, int anchorHeight) { |
| final RenderCoordinates coordinates = contentViewCore.getRenderCoordinates(); |
| final float yOffset = getWebViewOffsetYPixInScreen(contentViewCore); |
| return new RectF( |
| coordinates.fromLocalCssToPix(anchorX), |
| coordinates.fromLocalCssToPix(anchorY) + yOffset, |
| coordinates.fromLocalCssToPix(anchorX + anchorWidth), |
| coordinates.fromLocalCssToPix(anchorY + anchorHeight) + yOffset); |
| } |
| |
| private static float getWebViewOffsetYPixInScreen(ContentViewCore contentViewCore) { |
| int[] location = new int[2]; |
| contentViewCore.getContainerView().getLocationOnScreen(location); |
| return location[1] + contentViewCore.getRenderCoordinates().getContentOffsetYPix(); |
| } |
| |
| private static void updateTextViews(ViewGroup root, String mainText, String subText) { |
| ((TextView) root.findViewById(R.id.main_text)).setText(mainText); |
| final TextView subTextView = (TextView) root.findViewById(R.id.sub_text); |
| if (!TextUtils.isEmpty(subText)) { |
| subTextView.setText(subText); |
| } else { |
| ((ViewGroup) subTextView.getParent()).removeView(subTextView); |
| } |
| } |
| |
| private void measure(RenderCoordinates coordinates) { |
| mPopup.setWindowLayoutMode( |
| ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); |
| mPopup.getContentView().setLayoutParams( |
| new RelativeLayout.LayoutParams( |
| RelativeLayout.LayoutParams.WRAP_CONTENT, |
| RelativeLayout.LayoutParams.WRAP_CONTENT)); |
| mPopup.getContentView().measure( |
| View.MeasureSpec.makeMeasureSpec(coordinates.getLastFrameViewportWidthPixInt(), |
| View.MeasureSpec.AT_MOST), |
| View.MeasureSpec.makeMeasureSpec(coordinates.getLastFrameViewportHeightPixInt(), |
| View.MeasureSpec.AT_MOST)); |
| } |
| |
| private float getAnchorOffset() { |
| final View root = mPopup.getContentView(); |
| final int width = root.getMeasuredWidth(); |
| final int arrowWidth = root.findViewById(R.id.arrow_image).getMeasuredWidth(); |
| return ApiCompatibilityUtils.isLayoutRtl(root) ? |
| (width * 3 / 4 - arrowWidth / 2) : (width / 4 + arrowWidth / 2); |
| } |
| |
| /** |
| * This adjusts the position if the popup protrudes the web view. |
| */ |
| private Point adjustWindowPosition(ContentViewCore contentViewCore, int x, int y) { |
| final RenderCoordinates coordinates = contentViewCore.getRenderCoordinates(); |
| final int viewWidth = coordinates.getLastFrameViewportWidthPixInt(); |
| final int viewBottom = (int) getWebViewOffsetYPixInScreen(contentViewCore) + |
| coordinates.getLastFrameViewportHeightPixInt(); |
| final int width = mPopup.getContentView().getMeasuredWidth(); |
| final int height = mPopup.getContentView().getMeasuredHeight(); |
| if (x < 0) { |
| x = 0; |
| } else if (x + width > viewWidth) { |
| x = viewWidth - width; |
| } |
| if (y + height > viewBottom) { |
| y = viewBottom - height; |
| } |
| return new Point(x, y); |
| } |
| } |