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