blob: 4e4b438db00f39a3ab7e94d44bd052562cd0f7e0 [file] [log] [blame]
// 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);
}
}