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[] POPUP_LAYOUT_ATTRS = { android.R.attr.textEditPasteWindowLayout, };
62 TypedArray attrs = mContext.getTheme().obtainStyledAttributes(POPUP_LAYOUT_ATTRS);
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 View contentView = mContainer.getContentView();
115 int width = contentView.getMeasuredWidth();
116 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 // Update dimensions from new view
134 contentView = mContainer.getContentView();
135 width = contentView.getMeasuredWidth();
136 height = contentView.getMeasuredHeight();
138 // Vertical clipping, move under edited line and to the side of insertion cursor
139 // TODO bottom clipping in case there is no system bar
141 coords[1] += mLineOffsetY;
143 // Move to right hand side of insertion cursor by default. TODO RTL text.
144 final int handleHalfWidth = mWidthOffsetX / 2;
146 if (x + width < screenWidth) {
147 coords[0] += handleHalfWidth + width / 2;
149 coords[0] -= handleHalfWidth + width / 2;
152 // Horizontal clipping
153 coords[0] = Math.max(0, coords[0]);
154 coords[0] = Math.min(screenWidth - width, coords[0]);
158 mContainer.showAtLocation(mParent, Gravity.NO_GRAVITY, coords[0], coords[1]);
160 mContainer.update(coords[0], coords[1], -1, -1);
164 private void updateContent() {
165 if (mPasteView == null) {
166 final int layout = mPasteViewLayout;
167 LayoutInflater inflater = (LayoutInflater) mContext.
168 getSystemService(Context.LAYOUT_INFLATER_SERVICE);
169 if (inflater != null) {
170 mPasteView = inflater.inflate(layout, null);
173 if (mPasteView == null) {
174 throw new IllegalArgumentException("Unable to inflate TextEdit paste window");
177 final int size = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
178 mPasteView.setLayoutParams(
179 new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
180 ViewGroup.LayoutParams.WRAP_CONTENT));
181 mPasteView.measure(size, size);
183 mPasteView.setOnClickListener(this);
186 mContainer.setContentView(mPasteView);
189 private void paste() {