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