Home | History | Annotate | Download | only in web_contents_delegate_android
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 package org.chromium.components.web_contents_delegate_android;
      6 
      7 import android.graphics.Point;
      8 import android.graphics.RectF;
      9 import android.text.TextUtils;
     10 import android.view.Gravity;
     11 import android.view.View;
     12 import android.view.ViewGroup;
     13 import android.widget.PopupWindow;
     14 import android.widget.RelativeLayout;
     15 import android.widget.TextView;
     16 
     17 import org.chromium.base.ApiCompatibilityUtils;
     18 import org.chromium.base.CalledByNative;
     19 import org.chromium.content.R;
     20 import org.chromium.content.browser.ContentViewCore;
     21 import org.chromium.content.browser.RenderCoordinates;
     22 
     23 /**
     24  * This class is an implementation of validation message bubble UI.
     25  */
     26 class ValidationMessageBubble {
     27     private PopupWindow mPopup;
     28 
     29     /**
     30      * Creates a popup window to show the specified messages, and show it on
     31      * the specified anchor rectangle.
     32      *
     33      * @param contentViewCore The ContentViewCore object to provide various
     34      *                        information.
     35      * @param anchorX Anchor position in the CSS unit.
     36      * @param anchorY Anchor position in the CSS unit.
     37      * @param anchorWidth Anchor size in the CSS unit.
     38      * @param anchorHeight Anchor size in the CSS unit.
     39      * @param mainText The main message. It will shown at the top of the popup
     40      *                 window, and its font size is larger.
     41      * @param subText The sub message. It will shown below the main message, and
     42      *                its font size is smaller.
     43      */
     44     @CalledByNative
     45     private static ValidationMessageBubble createAndShow(
     46             ContentViewCore contentViewCore, int anchorX, int anchorY,
     47             int anchorWidth, int anchorHeight, String mainText, String subText) {
     48         final RectF anchorPixInScreen = makePixRectInScreen(
     49                 contentViewCore, anchorX, anchorY, anchorWidth, anchorHeight);
     50         return new ValidationMessageBubble(contentViewCore, anchorPixInScreen, mainText, subText);
     51     }
     52 
     53     private ValidationMessageBubble(
     54             ContentViewCore contentViewCore, RectF anchor, String mainText, String subText) {
     55         final ViewGroup root = (ViewGroup) View.inflate(contentViewCore.getContext(),
     56                 R.layout.validation_message_bubble, null);
     57         mPopup = new PopupWindow(root);
     58         updateTextViews(root, mainText, subText);
     59         measure(contentViewCore.getRenderCoordinates());
     60         Point origin = adjustWindowPosition(
     61                 contentViewCore, (int) (anchor.centerX() - getAnchorOffset()), (int) anchor.bottom);
     62         mPopup.showAtLocation(
     63                 contentViewCore.getContainerView(), Gravity.NO_GRAVITY, origin.x, origin.y);
     64     }
     65 
     66     @CalledByNative
     67     private void close() {
     68         if (mPopup == null) return;
     69         mPopup.dismiss();
     70         mPopup = null;
     71     }
     72 
     73     /**
     74      * Moves the popup window on the specified anchor rectangle.
     75      *
     76      * @param contentViewCore The ContentViewCore object to provide various
     77      *                        information.
     78      * @param anchorX Anchor position in the CSS unit.
     79      * @param anchorY Anchor position in the CSS unit.
     80      * @param anchorWidth Anchor size in the CSS unit.
     81      * @param anchorHeight Anchor size in the CSS unit.
     82      */
     83     @CalledByNative
     84     private void setPositionRelativeToAnchor(ContentViewCore contentViewCore,
     85             int anchorX, int anchorY, int anchorWidth, int anchorHeight) {
     86         RectF anchor = makePixRectInScreen(
     87                 contentViewCore, anchorX, anchorY, anchorWidth, anchorHeight);
     88         Point origin = adjustWindowPosition(
     89                 contentViewCore, (int) (anchor.centerX() - getAnchorOffset()), (int) anchor.bottom);
     90         mPopup.update(origin.x, origin.y, mPopup.getWidth(), mPopup.getHeight());
     91     }
     92 
     93     private static RectF makePixRectInScreen(ContentViewCore contentViewCore,
     94             int anchorX, int anchorY, int anchorWidth, int anchorHeight) {
     95         final RenderCoordinates coordinates = contentViewCore.getRenderCoordinates();
     96         final float yOffset = getWebViewOffsetYPixInScreen(contentViewCore);
     97         return new RectF(
     98                 coordinates.fromLocalCssToPix(anchorX),
     99                 coordinates.fromLocalCssToPix(anchorY) + yOffset,
    100                 coordinates.fromLocalCssToPix(anchorX + anchorWidth),
    101                 coordinates.fromLocalCssToPix(anchorY + anchorHeight) + yOffset);
    102     }
    103 
    104     private static float getWebViewOffsetYPixInScreen(ContentViewCore contentViewCore) {
    105         int[] location = new int[2];
    106         contentViewCore.getContainerView().getLocationOnScreen(location);
    107         return location[1] + contentViewCore.getRenderCoordinates().getContentOffsetYPix();
    108     }
    109 
    110     private static void updateTextViews(ViewGroup root, String mainText, String subText) {
    111         ((TextView) root.findViewById(R.id.main_text)).setText(mainText);
    112         final TextView subTextView = (TextView) root.findViewById(R.id.sub_text);
    113         if (!TextUtils.isEmpty(subText)) {
    114             subTextView.setText(subText);
    115         } else {
    116             ((ViewGroup) subTextView.getParent()).removeView(subTextView);
    117         }
    118     }
    119 
    120     private void measure(RenderCoordinates coordinates) {
    121         mPopup.setWindowLayoutMode(
    122                 ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    123         mPopup.getContentView().setLayoutParams(
    124                 new RelativeLayout.LayoutParams(
    125                         RelativeLayout.LayoutParams.WRAP_CONTENT,
    126                         RelativeLayout.LayoutParams.WRAP_CONTENT));
    127         mPopup.getContentView().measure(
    128                 View.MeasureSpec.makeMeasureSpec(coordinates.getLastFrameViewportWidthPixInt(),
    129                         View.MeasureSpec.AT_MOST),
    130                 View.MeasureSpec.makeMeasureSpec(coordinates.getLastFrameViewportHeightPixInt(),
    131                         View.MeasureSpec.AT_MOST));
    132     }
    133 
    134     private float getAnchorOffset() {
    135         final View root = mPopup.getContentView();
    136         final int width = root.getMeasuredWidth();
    137         final int arrowWidth = root.findViewById(R.id.arrow_image).getMeasuredWidth();
    138         return ApiCompatibilityUtils.isLayoutRtl(root) ?
    139                 (width * 3 / 4 - arrowWidth / 2) : (width / 4 + arrowWidth / 2);
    140     }
    141 
    142     /**
    143      * This adjusts the position if the popup protrudes the web view.
    144      */
    145     private Point adjustWindowPosition(ContentViewCore contentViewCore, int x, int y) {
    146         final RenderCoordinates coordinates = contentViewCore.getRenderCoordinates();
    147         final int viewWidth = coordinates.getLastFrameViewportWidthPixInt();
    148         final int viewBottom = (int) getWebViewOffsetYPixInScreen(contentViewCore) +
    149                 coordinates.getLastFrameViewportHeightPixInt();
    150         final int width = mPopup.getContentView().getMeasuredWidth();
    151         final int height = mPopup.getContentView().getMeasuredHeight();
    152         if (x < 0) {
    153             x = 0;
    154         } else if (x + width > viewWidth) {
    155             x = viewWidth - width;
    156         }
    157         if (y + height > viewBottom) {
    158             y = viewBottom - height;
    159         }
    160         return new Point(x, y);
    161     }
    162 }
    163