SelectPopupDialog.java revision f2477e01787aa58f445919b809d89e252beef54f
1// Copyright (c) 2012 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 5package org.chromium.content.browser.input; 6 7import android.app.AlertDialog; 8import android.content.DialogInterface; 9import android.content.res.TypedArray; 10import android.util.SparseBooleanArray; 11import android.view.View; 12import android.view.ViewGroup; 13import android.widget.AdapterView; 14import android.widget.AdapterView.OnItemClickListener; 15import android.widget.ArrayAdapter; 16import android.widget.CheckedTextView; 17import android.widget.ListView; 18 19import org.chromium.content.R; 20import org.chromium.content.browser.ContentViewCore; 21 22/** 23 * Handles the popup dialog for the <select> HTML tag support. 24 */ 25public class SelectPopupDialog { 26 // The currently showing popup dialog, null if none is showing. 27 private static SelectPopupDialog sShownDialog; 28 29 private static final int[] SELECT_DIALOG_ATTRS = { 30 R.attr.select_dialog_multichoice, 31 R.attr.select_dialog_singlechoice 32 }; 33 34 // The dialog hosting the popup list view. 35 private AlertDialog mListBoxPopup = null; 36 37 private final ContentViewCore mContentViewCore; 38 39 /** 40 * Subclass ArrayAdapter so we can disable OPTION_GROUP items. 41 */ 42 private class SelectPopupArrayAdapter extends ArrayAdapter<String> { 43 /** 44 * Possible values for mItemEnabled. 45 * Keep in sync with the value passed from content_view_core_impl.cc 46 */ 47 static final int POPUP_ITEM_TYPE_GROUP = 0; 48 static final int POPUP_ITEM_TYPE_DISABLED = 1; 49 static final int POPUP_ITEM_TYPE_ENABLED = 2; 50 51 // Indicates the POPUP_ITEM_TYPE of each item. 52 private final int[] mItemEnabled; 53 54 // True if all items are POPUP_ITEM_TYPE_ENABLED. 55 private boolean mAreAllItemsEnabled; 56 57 public SelectPopupArrayAdapter(String[] labels, int[] enabled, boolean multiple) { 58 super(mContentViewCore.getContext(), getSelectDialogLayout(multiple), labels); 59 mItemEnabled = enabled; 60 mAreAllItemsEnabled = true; 61 for (int item : mItemEnabled) { 62 if (item != POPUP_ITEM_TYPE_ENABLED) { 63 mAreAllItemsEnabled = false; 64 break; 65 } 66 } 67 } 68 69 @Override 70 public View getView(int position, View convertView, ViewGroup parent) { 71 if (position < 0 || position >= getCount()) { 72 return null; 73 } 74 75 // Always pass in null so that we will get a new CheckedTextView. Otherwise, an item 76 // which was previously used as an <optgroup> element (i.e. has no check), could get 77 // used as an <option> element, which needs a checkbox/radio, but it would not have 78 // one. 79 convertView = super.getView(position, null, parent); 80 if (mItemEnabled[position] != POPUP_ITEM_TYPE_ENABLED) { 81 if (mItemEnabled[position] == POPUP_ITEM_TYPE_GROUP) { 82 // Currently select_dialog_multichoice & select_dialog_multichoice use 83 // CheckedTextViews in chrome but not in WebView. 84 if (convertView instanceof CheckedTextView) { 85 ((CheckedTextView) convertView).setCheckMarkDrawable(null); 86 } 87 } else { 88 // Draw the disabled element in a disabled state. 89 convertView.setEnabled(false); 90 } 91 } 92 return convertView; 93 } 94 95 @Override 96 public boolean areAllItemsEnabled() { 97 return mAreAllItemsEnabled; 98 } 99 100 @Override 101 public boolean isEnabled(int position) { 102 if (position < 0 || position >= getCount()) { 103 return false; 104 } 105 return mItemEnabled[position] == POPUP_ITEM_TYPE_ENABLED; 106 } 107 } 108 109 private int getSelectDialogLayout(boolean isMultiChoice) { 110 int resource_id; 111 TypedArray styledAttributes = mContentViewCore.getContext().obtainStyledAttributes( 112 R.style.SelectPopupDialog, SELECT_DIALOG_ATTRS); 113 resource_id = styledAttributes.getResourceId(isMultiChoice ? 0 : 1, 0); 114 styledAttributes.recycle(); 115 return resource_id; 116 } 117 118 private SelectPopupDialog(ContentViewCore contentViewCore, String[] labels, int[] enabled, 119 boolean multiple, int[] selected) { 120 mContentViewCore = contentViewCore; 121 122 final ListView listView = new ListView(mContentViewCore.getContext()); 123 listView.setCacheColorHint(0); 124 AlertDialog.Builder b = new AlertDialog.Builder(mContentViewCore.getContext()) 125 .setView(listView) 126 .setCancelable(true) 127 .setInverseBackgroundForced(true); 128 129 if (multiple) { 130 b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 131 @Override 132 public void onClick(DialogInterface dialog, int which) { 133 mContentViewCore.selectPopupMenuItems(getSelectedIndices(listView)); 134 } 135 }); 136 b.setNegativeButton(android.R.string.cancel, 137 new DialogInterface.OnClickListener() { 138 @Override 139 public void onClick(DialogInterface dialog, int which) { 140 mContentViewCore.selectPopupMenuItems(null); 141 } 142 }); 143 } 144 mListBoxPopup = b.create(); 145 final SelectPopupArrayAdapter adapter = new SelectPopupArrayAdapter(labels, enabled, 146 multiple); 147 listView.setAdapter(adapter); 148 listView.setFocusableInTouchMode(true); 149 150 if (multiple) { 151 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); 152 for (int i = 0; i < selected.length; ++i) { 153 listView.setItemChecked(selected[i], true); 154 } 155 } else { 156 listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); 157 listView.setOnItemClickListener(new OnItemClickListener() { 158 @Override 159 public void onItemClick(AdapterView<?> parent, View v, 160 int position, long id) { 161 mContentViewCore.selectPopupMenuItems(getSelectedIndices(listView)); 162 mListBoxPopup.dismiss(); 163 } 164 }); 165 if (selected.length > 0) { 166 listView.setSelection(selected[0]); 167 listView.setItemChecked(selected[0], true); 168 } 169 } 170 mListBoxPopup.setOnCancelListener(new DialogInterface.OnCancelListener() { 171 @Override 172 public void onCancel(DialogInterface dialog) { 173 mContentViewCore.selectPopupMenuItems(null); 174 } 175 }); 176 mListBoxPopup.setOnDismissListener(new DialogInterface.OnDismissListener() { 177 @Override 178 public void onDismiss(DialogInterface dialog) { 179 mListBoxPopup = null; 180 sShownDialog = null; 181 } 182 }); 183 } 184 185 private int[] getSelectedIndices(ListView listView) { 186 SparseBooleanArray sparseArray = listView.getCheckedItemPositions(); 187 int selectedCount = 0; 188 for (int i = 0; i < sparseArray.size(); ++i) { 189 if (sparseArray.valueAt(i)) { 190 selectedCount++; 191 } 192 } 193 int[] indices = new int[selectedCount]; 194 for (int i = 0, j = 0; i < sparseArray.size(); ++i) { 195 if (sparseArray.valueAt(i)) { 196 indices[j++] = sparseArray.keyAt(i); 197 } 198 } 199 return indices; 200 } 201 202 /** 203 * Shows the popup menu triggered by the passed ContentView. 204 * Hides any currently shown popup. 205 * @param items Items to show. 206 * @param enabled POPUP_ITEM_TYPEs for items. 207 * @param multiple Whether the popup menu should support multi-select. 208 * @param selectedIndices Indices of selected items. 209 */ 210 public static void show(ContentViewCore contentViewCore, String[] items, int[] enabled, 211 boolean multiple, int[] selectedIndices) { 212 // Hide the popup currently showing if any. This could happen if the user pressed a select 213 // and pressed it again before the popup was shown. In that case, the previous popup is 214 // irrelevant and can be hidden. 215 hide(null); 216 sShownDialog = new SelectPopupDialog(contentViewCore, items, enabled, multiple, 217 selectedIndices); 218 sShownDialog.mListBoxPopup.show(); 219 } 220 221 /** 222 * Hides the showing popup menu if any it was triggered by the passed ContentView. If 223 * contentView is null, hides it regardless of which ContentView triggered it. 224 * @param contentView 225 */ 226 public static void hide(ContentViewCore contentView) { 227 if (sShownDialog != null && 228 (contentView == null || sShownDialog.mContentViewCore == contentView)) { 229 if (contentView != null) contentView.selectPopupMenuItems(null); 230 sShownDialog.mListBoxPopup.dismiss(); 231 } 232 } 233 234 // The methods below are used by tests. 235 public static SelectPopupDialog getCurrent() { 236 return sShownDialog; 237 } 238} 239