- add sources.
[platform/framework/web/crosswalk.git] / src / ui / android / java / src / org / chromium / ui / autofill / AutofillPopup.java
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.ui.autofill;
6
7 import android.content.Context;
8 import android.graphics.Paint;
9 import android.graphics.Rect;
10 import android.text.TextUtils;
11 import android.view.LayoutInflater;
12 import android.view.View;
13 import android.view.View.OnLayoutChangeListener;
14 import android.widget.AdapterView;
15 import android.widget.ListPopupWindow;
16 import android.widget.TextView;
17
18 import java.util.ArrayList;
19
20 import org.chromium.ui.R;
21 import org.chromium.ui.ViewAndroidDelegate;
22
23 /**
24  * The Autofill suggestion popup that lists relevant suggestions.
25  */
26 public class AutofillPopup extends ListPopupWindow implements AdapterView.OnItemClickListener {
27
28     /**
29      * Constants defining types of Autofill suggestion entries.
30      * Has to be kept in sync with enum in WebAutofillClient.h
31      *
32      * Not supported: MenuItemIDWarningMessage, MenuItemIDSeparator, MenuItemIDClearForm, and
33      * MenuItemIDAutofillOptions.
34      */
35     private static final int ITEM_ID_AUTOCOMPLETE_ENTRY = 0;
36     private static final int ITEM_ID_PASSWORD_ENTRY = -2;
37     private static final int ITEM_ID_DATA_LIST_ENTRY = -6;
38
39     private static final int TEXT_PADDING_DP = 30;
40
41     private final AutofillPopupDelegate mAutofillCallback;
42     private final Context mContext;
43     private final ViewAndroidDelegate mViewAndroidDelegate;
44     private View mAnchorView;
45     private float mAnchorWidth;
46     private float mAnchorHeight;
47     private float mAnchorX;
48     private float mAnchorY;
49     private Paint mLabelViewPaint;
50     private Paint mSublabelViewPaint;
51     private OnLayoutChangeListener mLayoutChangeListener;
52
53     /**
54      * An interface to handle the touch interaction with an AutofillPopup object.
55      */
56     public interface AutofillPopupDelegate {
57         /**
58          * Requests the controller to hide AutofillPopup.
59          */
60         public void requestHide();
61
62         /**
63          * Handles the selection of an Autofill suggestion from an AutofillPopup.
64          * @param listIndex The index of the selected Autofill suggestion.
65          */
66         public void suggestionSelected(int listIndex);
67     }
68
69     /**
70      * Creates an AutofillWindow with specified parameters.
71      * @param context Application context.
72      * @param viewAndroidDelegate View delegate used to add and remove views.
73      * @param autofillCallback A object that handles the calls to the native AutofillPopupView.
74      */
75     public AutofillPopup(Context context, ViewAndroidDelegate viewAndroidDelegate,
76             AutofillPopupDelegate autofillCallback) {
77         super(context, null, 0, R.style.AutofillPopupWindow);
78         mContext = context;
79         mViewAndroidDelegate = viewAndroidDelegate ;
80         mAutofillCallback = autofillCallback;
81
82         setOnItemClickListener(this);
83
84         mAnchorView = mViewAndroidDelegate.acquireAnchorView();
85         mAnchorView.setId(R.id.autofill_popup_window);
86         mAnchorView.setTag(this);
87
88         mViewAndroidDelegate.setAnchorViewPosition(mAnchorView, mAnchorX, mAnchorY, mAnchorWidth,
89                 mAnchorHeight);
90
91         mLayoutChangeListener = new OnLayoutChangeListener() {
92             @Override
93             public void onLayoutChange(View v, int left, int top, int right, int bottom,
94                     int oldLeft, int oldTop, int oldRight, int oldBottom) {
95                 if (v == mAnchorView) AutofillPopup.this.show();
96             }
97         };
98
99         mAnchorView.addOnLayoutChangeListener(mLayoutChangeListener);
100         setAnchorView(mAnchorView);
101     }
102
103     @Override
104     public void show() {
105         // An ugly hack to keep the popup from expanding on top of the keyboard.
106         setInputMethodMode(INPUT_METHOD_NEEDED);
107         super.show();
108     }
109
110     /**
111      * Sets the location and the size of the anchor view that the AutofillPopup will use to attach
112      * itself.
113      * @param x X coordinate of the top left corner of the anchor view.
114      * @param y Y coordinate of the top left corner of the anchor view.
115      * @param width The width of the anchor view.
116      * @param height The height of the anchor view.
117      */
118     public void setAnchorRect(float x, float y, float width, float height) {
119         mAnchorWidth = width;
120         mAnchorHeight = height;
121         mAnchorX = x;
122         mAnchorY = y;
123         if (mAnchorView != null) {
124             mViewAndroidDelegate.setAnchorViewPosition(mAnchorView, mAnchorX, mAnchorY,
125                     mAnchorWidth, mAnchorHeight);
126         }
127     }
128
129     /**
130      * Sets the Autofill suggestions to display in the popup and shows the popup.
131      * @param suggestions Autofill suggestion data.
132      */
133     public void show(AutofillSuggestion[] suggestions) {
134         // Remove the AutofillSuggestions with IDs that are not supported by Android
135         ArrayList<AutofillSuggestion> cleanedData = new ArrayList<AutofillSuggestion>();
136         for (int i = 0; i < suggestions.length; i++) {
137             int itemId = suggestions[i].mUniqueId;
138             if (itemId > 0 || itemId == ITEM_ID_AUTOCOMPLETE_ENTRY ||
139                     itemId == ITEM_ID_PASSWORD_ENTRY || itemId == ITEM_ID_DATA_LIST_ENTRY) {
140                 cleanedData.add(suggestions[i]);
141             }
142         }
143         setAdapter(new AutofillListAdapter(mContext, cleanedData));
144         // Once the mAnchorRect is resized and placed correctly, it will show the Autofill popup.
145         mAnchorWidth = Math.max(getDesiredWidth(cleanedData), mAnchorWidth);
146         mViewAndroidDelegate.setAnchorViewPosition(mAnchorView, mAnchorX, mAnchorY, mAnchorWidth,
147                 mAnchorHeight);
148     }
149
150     /**
151      * Overrides the default dismiss behavior to request the controller to dismiss the view.
152      */
153     @Override
154     public void dismiss() {
155         mAutofillCallback.requestHide();
156     }
157
158     /**
159      * Hides the popup and removes the anchor view from the ContainerView.
160      */
161     public void hide() {
162         super.dismiss();
163         mAnchorView.removeOnLayoutChangeListener(mLayoutChangeListener);
164         mAnchorView.setTag(null);
165         mViewAndroidDelegate.releaseAnchorView(mAnchorView);
166     }
167
168     /**
169      * Get desired popup window width by calculating the maximum text length from Autofill data.
170      * @param data Autofill suggestion data.
171      * @return The popup window width in DIP.
172      */
173     private float getDesiredWidth(ArrayList<AutofillSuggestion> data) {
174         if (mLabelViewPaint == null || mSublabelViewPaint == null) {
175             LayoutInflater inflater =
176                     (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
177             View layout = inflater.inflate(R.layout.autofill_text, null);
178             TextView labelView = (TextView) layout.findViewById(R.id.autofill_label);
179             mLabelViewPaint = labelView.getPaint();
180             TextView sublabelView = (TextView) layout.findViewById(R.id.autofill_sublabel);
181             mSublabelViewPaint = sublabelView.getPaint();
182         }
183
184         float maxTextWidth = 0;
185         Rect bounds = new Rect();
186         for (int i = 0; i < data.size(); ++i) {
187             bounds.setEmpty();
188             String label = data.get(i).mLabel;
189             if (!TextUtils.isEmpty(label)) {
190                 mLabelViewPaint.getTextBounds(label, 0, label.length(), bounds);
191             }
192             float labelWidth = bounds.width();
193
194             bounds.setEmpty();
195             String sublabel = data.get(i).mSublabel;
196             if (!TextUtils.isEmpty(sublabel)) {
197                 mSublabelViewPaint.getTextBounds(sublabel, 0, sublabel.length(), bounds);
198             }
199
200             float localMax = Math.max(labelWidth, bounds.width());
201             maxTextWidth = Math.max(maxTextWidth, localMax);
202         }
203         // Scale it down to make it unscaled by screen density.
204         maxTextWidth = maxTextWidth / mContext.getResources().getDisplayMetrics().density;
205         // Adding padding.
206         return maxTextWidth + TEXT_PADDING_DP;
207     }
208
209     @Override
210     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
211         mAutofillCallback.suggestionSelected(position);
212     }
213
214 }