Home | History | Annotate | Download | only in autofill
      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.annotation.SuppressLint;
      8 import android.content.Context;
      9 import android.util.Log;
     10 import android.view.View;
     11 import android.widget.AdapterView;
     12 import android.widget.ListPopupWindow;
     13 import android.widget.PopupWindow;
     14 
     15 import org.chromium.base.ApiCompatibilityUtils;
     16 import org.chromium.ui.DropdownAdapter;
     17 import org.chromium.ui.DropdownItem;
     18 import org.chromium.ui.DropdownPopupWindow;
     19 import org.chromium.ui.base.ViewAndroidDelegate;
     20 
     21 import java.lang.reflect.Method;
     22 import java.util.ArrayList;
     23 import java.util.Arrays;
     24 import java.util.HashSet;
     25 import java.util.List;
     26 
     27 /**
     28  * The Autofill suggestion popup that lists relevant suggestions.
     29  */
     30 public class AutofillPopup extends DropdownPopupWindow implements AdapterView.OnItemClickListener,
     31         PopupWindow.OnDismissListener {
     32 
     33     /**
     34      * The constant used to specify a separator in a list of Autofill suggestions.
     35      * Has to be kept in sync with enum in WebAutofillClient.h
     36      */
     37     private static final int ITEM_ID_SEPARATOR_ENTRY = -3;
     38 
     39     private final Context mContext;
     40     private final AutofillPopupDelegate mAutofillCallback;
     41     private List<AutofillSuggestion> mSuggestions;
     42 
     43 
     44     /**
     45      * An interface to handle the touch interaction with an AutofillPopup object.
     46      */
     47     public interface AutofillPopupDelegate {
     48         /**
     49          * Informs the controller the AutofillPopup was hidden.
     50          */
     51         public void dismissed();
     52 
     53         /**
     54          * Handles the selection of an Autofill suggestion from an AutofillPopup.
     55          * @param listIndex The index of the selected Autofill suggestion.
     56          */
     57         public void suggestionSelected(int listIndex);
     58     }
     59 
     60     /**
     61      * Creates an AutofillWindow with specified parameters.
     62      * @param context Application context.
     63      * @param viewAndroidDelegate View delegate used to add and remove views.
     64      * @param autofillCallback A object that handles the calls to the native AutofillPopupView.
     65      */
     66     public AutofillPopup(Context context, ViewAndroidDelegate viewAndroidDelegate,
     67             AutofillPopupDelegate autofillCallback) {
     68         super(context, viewAndroidDelegate);
     69         mContext = context;
     70         mAutofillCallback = autofillCallback;
     71 
     72         setOnItemClickListener(this);
     73         setOnDismissListener(this);
     74     }
     75 
     76     /**
     77      * Filters the Autofill suggestions to the ones that we support and shows the popup.
     78      * @param suggestions Autofill suggestion data.
     79      */
     80     @SuppressLint("InlinedApi")
     81     public void filterAndShow(AutofillSuggestion[] suggestions, boolean isRtl) {
     82         mSuggestions = new ArrayList<AutofillSuggestion>(Arrays.asList(suggestions));
     83         // Remove the AutofillSuggestions with IDs that are not supported by Android
     84         ArrayList<DropdownItem> cleanedData = new ArrayList<DropdownItem>();
     85         HashSet<Integer> separators = new HashSet<Integer>();
     86         for (int i = 0; i < suggestions.length; i++) {
     87             int itemId = suggestions[i].getSuggestionId();
     88             if (itemId == ITEM_ID_SEPARATOR_ENTRY) {
     89                 separators.add(cleanedData.size());
     90             } else {
     91                 cleanedData.add(suggestions[i]);
     92             }
     93         }
     94 
     95         setAdapter(new DropdownAdapter(mContext, cleanedData, separators));
     96         show();
     97         ApiCompatibilityUtils.setLayoutDirection(getListView(),
     98                 isRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
     99 
    100         // HACK: The ListPopupWindow's mPopup automatically dismisses on an outside tap. There's
    101         // no way to override it or prevent it, except reaching into ListPopupWindow's hidden
    102         // API. This allows the C++ controller to completely control showing/hiding the popup.
    103         // See http://crbug.com/400601
    104         try {
    105             Method setForceIgnoreOutsideTouch = ListPopupWindow.class.getMethod(
    106                     "setForceIgnoreOutsideTouch", new Class[] { boolean.class });
    107             setForceIgnoreOutsideTouch.invoke(this, new Object[] { true });
    108         } catch (Exception e) {
    109             Log.e("AutofillPopup",
    110                     "ListPopupWindow.setForceIgnoreOutsideTouch not found",
    111                     e);
    112         }
    113     }
    114 
    115     /**
    116      * Hides the popup.
    117      */
    118     public void hide() {
    119         dismiss();
    120     }
    121 
    122     @Override
    123     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    124         DropdownAdapter adapter = (DropdownAdapter) parent.getAdapter();
    125         int listIndex = mSuggestions.indexOf(adapter.getItem(position));
    126         assert listIndex > -1;
    127         mAutofillCallback.suggestionSelected(listIndex);
    128     }
    129 
    130     @Override
    131     public void onDismiss() {
    132         mAutofillCallback.dismissed();
    133     }
    134 }
    135