root/components/web_contents_delegate_android/android/java/src/org/chromium/components/web_contents_delegate_android/ValidationMessageBubble.java

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. createAndShow
  2. close
  3. setPositionRelativeToAnchor
  4. makePixRectInScreen
  5. getWebViewOffsetYPixInScreen
  6. updateTextViews
  7. measure
  8. getAnchorOffset
  9. adjustWindowPosition

// 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.components.web_contents_delegate_android;

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.content.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);
    }
}

/* [<][>][^][v][top][bottom][index][help] */