1 // Copyright 2014 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.
5 package org.chromium.content.browser.input;
7 import android.content.Context;
8 import android.content.res.TypedArray;
9 import android.util.TypedValue;
10 import android.view.Gravity;
11 import android.view.LayoutInflater;
12 import android.view.View;
13 import android.view.View.OnClickListener;
14 import android.view.ViewGroup;
15 import android.view.ViewGroup.LayoutParams;
16 import android.widget.PopupWindow;
19 * Paste popup implementation based on TextView.PastePopupMenu.
21 public class PastePopupMenu implements OnClickListener {
22 private final View mParent;
23 private final PastePopupMenuDelegate mDelegate;
24 private final Context mContext;
25 private final PopupWindow mContainer;
26 private int mRawPositionX;
27 private int mRawPositionY;
28 private int mPositionX;
29 private int mPositionY;
30 private int mStatusBarHeight;
31 private View mPasteView;
32 private final int mPasteViewLayout;
33 private final int mLineOffsetY;
34 private final int mWidthOffsetX;
37 * Provider of paste functionality for the given popup.
39 public interface PastePopupMenuDelegate {
41 * Called to initiate a paste after the popup has been tapped.
46 public PastePopupMenu(View parent, PastePopupMenuDelegate delegate) {
49 mContext = parent.getContext();
50 mContainer = new PopupWindow(mContext, null,
51 android.R.attr.textSelectHandleWindowStyle);
52 mContainer.setSplitTouchEnabled(true);
53 mContainer.setClippingEnabled(false);
54 mContainer.setAnimationStyle(0);
56 mContainer.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
57 mContainer.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
59 final int[] popupLayoutAttrs = { android.R.attr.textEditPasteWindowLayout, };
62 TypedArray attrs = mContext.getTheme().obtainStyledAttributes(popupLayoutAttrs);
63 mPasteViewLayout = attrs.getResourceId(attrs.getIndex(0), 0);
67 // Convert line offset dips to pixels.
68 mLineOffsetY = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
69 5.0f, mContext.getResources().getDisplayMetrics());
70 mWidthOffsetX = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
71 30.0f, mContext.getResources().getDisplayMetrics());
73 final int statusBarHeightResourceId =
74 mContext.getResources().getIdentifier("status_bar_height", "dimen", "android");
75 if (statusBarHeightResourceId > 0) {
77 mContext.getResources().getDimensionPixelSize(statusBarHeightResourceId);
82 * Shows the paste popup at an appropriate location relative to the specified position.
84 public void showAt(int x, int y) {
90 * Hides the paste popup.
97 * @return Whether the popup is active and showing.
99 public boolean isShowing() {
100 return mContainer.isShowing();
104 public void onClick(View v) {
109 private void positionAt(int x, int y) {
110 if (mRawPositionX == x && mRawPositionY == y && isShowing()) return;
114 final View contentView = mContainer.getContentView();
115 final int width = contentView.getMeasuredWidth();
116 final int height = contentView.getMeasuredHeight();
118 mPositionX = (int) (x - width / 2.0f);
119 mPositionY = y - height - mLineOffsetY;
121 final int[] coords = new int[2];
122 mParent.getLocationInWindow(coords);
123 coords[0] += mPositionX;
124 coords[1] += mPositionY;
127 if (mParent.getSystemUiVisibility() == View.SYSTEM_UI_FLAG_VISIBLE) {
128 minOffsetY = mStatusBarHeight;
131 final int screenWidth = mContext.getResources().getDisplayMetrics().widthPixels;
132 if (coords[1] < minOffsetY) {
133 // Vertical clipping, move under edited line and to the side of insertion cursor
134 // TODO bottom clipping in case there is no system bar
136 coords[1] += mLineOffsetY;
138 // Move to right hand side of insertion cursor by default. TODO RTL text.
139 final int handleHalfWidth = mWidthOffsetX / 2;
141 if (x + width < screenWidth) {
142 coords[0] += handleHalfWidth + width / 2;
144 coords[0] -= handleHalfWidth + width / 2;
147 // Horizontal clipping
148 coords[0] = Math.max(0, coords[0]);
149 coords[0] = Math.min(screenWidth - width, coords[0]);
153 mContainer.showAtLocation(mParent, Gravity.NO_GRAVITY, coords[0], coords[1]);
155 mContainer.update(coords[0], coords[1], -1, -1);
159 private void updateContent() {
160 if (mPasteView == null) {
161 final int layout = mPasteViewLayout;
162 LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
163 Context.LAYOUT_INFLATER_SERVICE);
164 if (inflater != null) {
165 mPasteView = inflater.inflate(layout, null);
168 if (mPasteView == null) {
169 throw new IllegalArgumentException("Unable to inflate TextEdit paste window");
172 final int size = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
173 mPasteView.setLayoutParams(
174 new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
175 ViewGroup.LayoutParams.WRAP_CONTENT));
176 mPasteView.measure(size, size);
178 mPasteView.setOnClickListener(this);
181 mContainer.setContentView(mPasteView);
184 private void paste() {