Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / public / android / java / src / org / chromium / content / browser / input / PastePopupMenu.java
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.
4
5 package org.chromium.content.browser.input;
6
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;
17
18 /**
19  * Paste popup implementation based on TextView.PastePopupMenu.
20  */
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;
35
36     /**
37      * Provider of paste functionality for the given popup.
38      */
39     public interface PastePopupMenuDelegate {
40         /**
41          * Called to initiate a paste after the popup has been tapped.
42          */
43         void paste();
44     }
45
46     public PastePopupMenu(View parent, PastePopupMenuDelegate delegate) {
47         mParent = parent;
48         mDelegate = 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);
55
56         mContainer.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
57         mContainer.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
58
59         final int[] POPUP_LAYOUT_ATTRS = { android.R.attr.textEditPasteWindowLayout, };
60
61         mPasteView = null;
62         TypedArray attrs = mContext.getTheme().obtainStyledAttributes(POPUP_LAYOUT_ATTRS);
63         mPasteViewLayout = attrs.getResourceId(attrs.getIndex(0), 0);
64
65         attrs.recycle();
66
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());
72
73         final int statusBarHeightResourceId =
74                 mContext.getResources().getIdentifier("status_bar_height", "dimen", "android");
75         if (statusBarHeightResourceId > 0) {
76             mStatusBarHeight =
77                     mContext.getResources().getDimensionPixelSize(statusBarHeightResourceId);
78         }
79     }
80
81     /**
82      * Shows the paste popup at an appropriate location relative to the specified position.
83      */
84     public void showAt(int x, int y) {
85         updateContent();
86         positionAt(x, y);
87     }
88
89     /**
90      * Hides the paste popup.
91      */
92     public void hide() {
93         mContainer.dismiss();
94     }
95
96     /**
97      * @return Whether the popup is active and showing.
98      */
99     public boolean isShowing() {
100         return mContainer.isShowing();
101     }
102
103     @Override
104     public void onClick(View v) {
105         paste();
106         hide();
107     }
108
109     private void positionAt(int x, int y) {
110         if (mRawPositionX == x && mRawPositionY == y && isShowing()) return;
111         mRawPositionX = x;
112         mRawPositionY = y;
113
114         View contentView = mContainer.getContentView();
115         int width = contentView.getMeasuredWidth();
116         int height = contentView.getMeasuredHeight();
117
118         mPositionX = (int) (x - width / 2.0f);
119         mPositionY = y - height - mLineOffsetY;
120
121         final int[] coords = new int[2];
122         mParent.getLocationInWindow(coords);
123         coords[0] += mPositionX;
124         coords[1] += mPositionY;
125
126         int minOffsetY = 0;
127         if (mParent.getSystemUiVisibility() == View.SYSTEM_UI_FLAG_VISIBLE) {
128             minOffsetY = mStatusBarHeight;
129         }
130
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();
137
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
140             coords[1] += height;
141             coords[1] += mLineOffsetY;
142
143             // Move to right hand side of insertion cursor by default. TODO RTL text.
144             final int handleHalfWidth = mWidthOffsetX / 2;
145
146             if (x + width < screenWidth) {
147                 coords[0] += handleHalfWidth + width / 2;
148             } else {
149                 coords[0] -= handleHalfWidth + width / 2;
150             }
151         } else {
152             // Horizontal clipping
153             coords[0] = Math.max(0, coords[0]);
154             coords[0] = Math.min(screenWidth - width, coords[0]);
155         }
156
157         if (!isShowing()) {
158             mContainer.showAtLocation(mParent, Gravity.NO_GRAVITY, coords[0], coords[1]);
159         } else {
160             mContainer.update(coords[0], coords[1], -1, -1);
161         }
162     }
163
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);
171             }
172
173             if (mPasteView == null) {
174                 throw new IllegalArgumentException("Unable to inflate TextEdit paste window");
175             }
176
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);
182
183             mPasteView.setOnClickListener(this);
184         }
185
186         mContainer.setContentView(mPasteView);
187     }
188
189     private void paste() {
190         mDelegate.paste();
191     }
192 }