Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(21)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/ItemChooserDialog.java

Issue 1315093002: Implement a bluetooth picker dialog for Android (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Remove unused member Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.chrome.browser; 5 package org.chromium.chrome.browser;
6 6
7 import android.app.Dialog; 7 import android.app.Dialog;
8 import android.content.Context; 8 import android.content.Context;
9 import android.content.DialogInterface;
9 import android.graphics.Color; 10 import android.graphics.Color;
10 import android.graphics.drawable.ColorDrawable; 11 import android.graphics.drawable.ColorDrawable;
11 import android.text.SpannableString; 12 import android.text.SpannableString;
12 import android.text.method.LinkMovementMethod; 13 import android.text.method.LinkMovementMethod;
13 import android.util.DisplayMetrics; 14 import android.util.DisplayMetrics;
14 import android.view.Gravity; 15 import android.view.Gravity;
15 import android.view.LayoutInflater; 16 import android.view.LayoutInflater;
16 import android.view.View; 17 import android.view.View;
17 import android.view.ViewGroup; 18 import android.view.ViewGroup;
18 import android.view.ViewGroup.LayoutParams; 19 import android.view.ViewGroup.LayoutParams;
19 import android.view.Window; 20 import android.view.Window;
20 import android.view.WindowManager; 21 import android.view.WindowManager;
21 import android.widget.AdapterView; 22 import android.widget.AdapterView;
22 import android.widget.ArrayAdapter; 23 import android.widget.ArrayAdapter;
23 import android.widget.Button; 24 import android.widget.Button;
24 import android.widget.LinearLayout; 25 import android.widget.LinearLayout;
25 import android.widget.ListView; 26 import android.widget.ListView;
26 import android.widget.ProgressBar; 27 import android.widget.ProgressBar;
27 import android.widget.TextView; 28 import android.widget.TextView;
28 29
29 import org.chromium.chrome.R; 30 import org.chromium.chrome.R;
30 import org.chromium.ui.base.DeviceFormFactor; 31 import org.chromium.ui.base.DeviceFormFactor;
31 import org.chromium.ui.widget.TextViewWithClickableSpans; 32 import org.chromium.ui.widget.TextViewWithClickableSpans;
32 33
34 import java.util.HashSet;
33 import java.util.List; 35 import java.util.List;
36 import java.util.Set;
34 37
35 /** 38 /**
36 * A general-purpose dialog for presenting a list of things to pick from. 39 * A general-purpose dialog for presenting a list of things to pick from.
37 */ 40 */
38 public class ItemChooserDialog { 41 public class ItemChooserDialog {
39 /** 42 /**
40 * An interface to implement to get a callback when something has been 43 * An interface to implement to get a callback when something has been
41 * selected. 44 * selected.
42 */ 45 */
43 public interface ItemSelectedCallback { 46 public interface ItemSelectedCallback {
(...skipping 24 matching lines...) Expand all
68 */ 71 */
69 public static class ItemChooserLabels { 72 public static class ItemChooserLabels {
70 // The title at the top of the dialog. 73 // The title at the top of the dialog.
71 public final SpannableString mTitle; 74 public final SpannableString mTitle;
72 // The message to show while results are trickling in. 75 // The message to show while results are trickling in.
73 public final String mSearching; 76 public final String mSearching;
74 // The message to show when no results were produced. 77 // The message to show when no results were produced.
75 public final SpannableString mNoneFound; 78 public final SpannableString mNoneFound;
76 // A status message to show above the button row. 79 // A status message to show above the button row.
77 public final SpannableString mStatus; 80 public final SpannableString mStatus;
81 // An error state.
82 public final SpannableString mErrorMessage;
83 // A status message to go with the error state.
84 public final SpannableString mErrorStatus;
78 // The label for the positive button (e.g. Select/Pair). 85 // The label for the positive button (e.g. Select/Pair).
79 public final String mPositiveButton; 86 public final String mPositiveButton;
80 87
81 public ItemChooserLabels(SpannableString title, String searching, Spanna bleString noneFound, 88 public ItemChooserLabels(SpannableString title, String searching, Spanna bleString noneFound,
82 SpannableString status, String positiveButton) { 89 SpannableString status, SpannableString errorMessage, SpannableS tring errorStatus,
90 String positiveButton) {
83 mTitle = title; 91 mTitle = title;
84 mSearching = searching; 92 mSearching = searching;
85 mNoneFound = noneFound; 93 mNoneFound = noneFound;
86 mStatus = status; 94 mStatus = status;
95 mErrorMessage = errorMessage;
96 mErrorStatus = errorStatus;
87 mPositiveButton = positiveButton; 97 mPositiveButton = positiveButton;
88 } 98 }
89 } 99 }
90 100
91 /** 101 /**
102 * The various states the dialog can represent.
103 */
104 private enum State {
105 STARTING,
106 PROGRESS_UPDATE_AVAILABLE,
107 SHOWING_ERROR,
108 }
109
110 /**
92 * An adapter for keeping track of which items to show in the dialog. 111 * An adapter for keeping track of which items to show in the dialog.
93 */ 112 */
94 private class ItemAdapter extends ArrayAdapter<ItemChooserRow> 113 private class ItemAdapter extends ArrayAdapter<ItemChooserRow>
95 implements AdapterView.OnItemClickListener { 114 implements AdapterView.OnItemClickListener {
96 private final LayoutInflater mInflater; 115 private final LayoutInflater mInflater;
97 116
98 // The background color of the highlighted item. 117 // The background color of the highlighted item.
99 private final int mBackgroundHighlightColor; 118 private final int mBackgroundHighlightColor;
100 119
101 // The color of the non-highlighted text. 120 // The color of the non-highlighted text.
102 private final int mDefaultTextColor; 121 private final int mDefaultTextColor;
103 122
104 // The zero-based index of the item currently selected in the dialog, 123 // The zero-based index of the item currently selected in the dialog,
105 // or -1 (INVALID_POSITION) if nothing is selected. 124 // or -1 (INVALID_POSITION) if nothing is selected.
106 private int mSelectedItem = ListView.INVALID_POSITION; 125 private int mSelectedItem = ListView.INVALID_POSITION;
107 126
127 // A set of keys that are marked as disabled in the dialog.
128 private Set<String> mDisabledEntries = new HashSet<String>();
129
108 public ItemAdapter(Context context, int resource) { 130 public ItemAdapter(Context context, int resource) {
109 super(context, resource); 131 super(context, resource);
110 132
111 mInflater = LayoutInflater.from(context); 133 mInflater = LayoutInflater.from(context);
112 134
113 mBackgroundHighlightColor = getContext().getResources().getColor( 135 mBackgroundHighlightColor = getContext().getResources().getColor(
114 R.color.light_active_color); 136 R.color.light_active_color);
115 mDefaultTextColor = getContext().getResources().getColor( 137 mDefaultTextColor = getContext().getResources().getColor(
116 R.color.default_text_color); 138 R.color.default_text_color);
117 } 139 }
118 140
119 @Override 141 @Override
120 public void clear() { 142 public void clear() {
121 mSelectedItem = ListView.INVALID_POSITION; 143 mSelectedItem = ListView.INVALID_POSITION;
122 mConfirmButton.setEnabled(false); 144 mConfirmButton.setEnabled(false);
123 super.clear(); 145 super.clear();
124 } 146 }
125 147
126 /** 148 /**
127 * Returns the key of the currently selected item or blank if nothing is 149 * Returns the key of the currently selected item or blank if nothing is
128 * selected. 150 * selected.
129 */ 151 */
130 public String getSelectedItemKey() { 152 public String getSelectedItemKey() {
131 ItemChooserRow row = getItem(mSelectedItem); 153 ItemChooserRow row = getItem(mSelectedItem);
132 if (row == null) return ""; 154 if (row == null) return "";
133 return row.mKey; 155 return row.mKey;
134 } 156 }
135 157
158 /**
159 * Sets whether the itam is enabled. Disabled items are grayed out.
160 * @param id The id of the item to affect.
161 * @param enabled Whether the item should be enabled or not.
162 */
163 public void setEnabled(String id, boolean enabled) {
164 if (enabled) {
165 mDisabledEntries.remove(id);
166 } else {
167 mDisabledEntries.add(id);
168 }
169 notifyDataSetChanged();
170 }
171
172 @Override
173 public boolean isEnabled(int position) {
174 ItemChooserRow item = getItem(position);
175 return !mDisabledEntries.contains(item.mKey);
176 }
177
136 @Override 178 @Override
137 public int getViewTypeCount() { 179 public int getViewTypeCount() {
138 return 1; 180 return 1;
139 } 181 }
140 182
141 @Override 183 @Override
142 public long getItemId(int position) { 184 public long getItemId(int position) {
143 return position; 185 return position;
144 } 186 }
145 187
146 @Override 188 @Override
147 public View getView(int position, View convertView, ViewGroup parent) { 189 public View getView(int position, View convertView, ViewGroup parent) {
148 TextView view; 190 TextView view;
149 if (convertView instanceof TextView) { 191 if (convertView instanceof TextView) {
150 view = (TextView) convertView; 192 view = (TextView) convertView;
151 } else { 193 } else {
152 view = (TextView) mInflater.inflate( 194 view = (TextView) mInflater.inflate(
153 R.layout.item_chooser_dialog_row, parent, false); 195 R.layout.item_chooser_dialog_row, parent, false);
154 } 196 }
155 197
156 // Set highlighting for currently selected item. 198 // Set highlighting for currently selected item.
157 if (position == mSelectedItem) { 199 if (position == mSelectedItem) {
158 view.setBackgroundColor(mBackgroundHighlightColor); 200 view.setBackgroundColor(mBackgroundHighlightColor);
159 view.setTextColor(Color.WHITE); 201 view.setTextColor(Color.WHITE);
160 } else { 202 } else {
161 view.setBackground(null); 203 view.setBackground(null);
162 view.setTextColor(mDefaultTextColor); 204 if (!isEnabled(position)) {
205 view.setTextColor(getContext().getResources().getColor(
206 R.color.primary_text_disabled_material_light));
207 } else {
208 view.setTextColor(mDefaultTextColor);
209 }
163 } 210 }
164 211
165 ItemChooserRow item = getItem(position); 212 ItemChooserRow item = getItem(position);
166 view.setText(item.mDescription); 213 view.setText(item.mDescription);
167 return view; 214 return view;
168 } 215 }
169 216
170 @Override 217 @Override
171 public void onItemClick(AdapterView<?> adapter, View view, int position, long id) { 218 public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
172 mSelectedItem = position; 219 mSelectedItem = position;
173 mConfirmButton.setEnabled(true); 220 mConfirmButton.setEnabled(true);
174 mItemAdapter.notifyDataSetChanged(); 221 mItemAdapter.notifyDataSetChanged();
175 } 222 }
176 } 223 }
177 224
178 private Context mContext; 225 private Context mContext;
179 226
180 // The dialog this class encapsulates. 227 // The dialog this class encapsulates.
181 private Dialog mDialog; 228 private Dialog mDialog;
182 229
183 // The callback to notify when the user selected an item. 230 // The callback to notify when the user selected an item.
184 private ItemSelectedCallback mItemSelectedCallback; 231 private ItemSelectedCallback mItemSelectedCallback;
185 232
186 // Individual UI elements. 233 // Individual UI elements.
187 private TextViewWithClickableSpans mTitle; 234 private TextViewWithClickableSpans mTitle;
188 private TextViewWithClickableSpans mNotFoundMessage; 235 private TextViewWithClickableSpans mEmptyMessage;
189 private ProgressBar mProgressBar; 236 private ProgressBar mProgressBar;
190 private ListView mListView; 237 private ListView mListView;
191 private TextView mStatus; 238 private TextView mStatus;
192 private Button mConfirmButton; 239 private Button mConfirmButton;
193 240
194 // The labels to display in the dialog. 241 // The labels to display in the dialog.
195 private ItemChooserLabels mLabels; 242 private ItemChooserLabels mLabels;
196 243
197 // The adapter containing the items to show in the dialog. 244 // The adapter containing the items to show in the dialog.
198 private ItemAdapter mItemAdapter; 245 private ItemAdapter mItemAdapter;
(...skipping 19 matching lines...) Expand all
218 mLabels = labels; 265 mLabels = labels;
219 266
220 LinearLayout dialogContainer = (LinearLayout) LayoutInflater.from( 267 LinearLayout dialogContainer = (LinearLayout) LayoutInflater.from(
221 mContext).inflate(R.layout.item_chooser_dialog, null); 268 mContext).inflate(R.layout.item_chooser_dialog, null);
222 269
223 mListView = (ListView) dialogContainer.findViewById(R.id.items); 270 mListView = (ListView) dialogContainer.findViewById(R.id.items);
224 mProgressBar = (ProgressBar) dialogContainer.findViewById(R.id.progress) ; 271 mProgressBar = (ProgressBar) dialogContainer.findViewById(R.id.progress) ;
225 mStatus = (TextView) dialogContainer.findViewById(R.id.status); 272 mStatus = (TextView) dialogContainer.findViewById(R.id.status);
226 mTitle = (TextViewWithClickableSpans) dialogContainer.findViewById( 273 mTitle = (TextViewWithClickableSpans) dialogContainer.findViewById(
227 R.id.dialog_title); 274 R.id.dialog_title);
228 mNotFoundMessage = 275 mEmptyMessage =
229 (TextViewWithClickableSpans) dialogContainer.findViewById( 276 (TextViewWithClickableSpans) dialogContainer.findViewById(R.id.n ot_found_message);
230 R.id.not_found_message);
231 277
232 mTitle.setText(labels.mTitle); 278 mTitle.setText(labels.mTitle);
233 mTitle.setMovementMethod(LinkMovementMethod.getInstance()); 279 mTitle.setMovementMethod(LinkMovementMethod.getInstance());
234 280
235 mNotFoundMessage.setText(labels.mNoneFound); 281 mEmptyMessage.setMovementMethod(LinkMovementMethod.getInstance());
236 mNotFoundMessage.setMovementMethod(LinkMovementMethod.getInstance());
237
238 mStatus.setText(labels.mSearching);
239 mStatus.setMovementMethod(LinkMovementMethod.getInstance()); 282 mStatus.setMovementMethod(LinkMovementMethod.getInstance());
240 283
241 mConfirmButton = (Button) dialogContainer.findViewById(R.id.positive); 284 mConfirmButton = (Button) dialogContainer.findViewById(R.id.positive);
242 mConfirmButton.setText(labels.mPositiveButton); 285 mConfirmButton.setText(labels.mPositiveButton);
243 mConfirmButton.setEnabled(false); 286 mConfirmButton.setEnabled(false);
244 mConfirmButton.setOnClickListener(new View.OnClickListener() { 287 mConfirmButton.setOnClickListener(new View.OnClickListener() {
245 @Override 288 @Override
246 public void onClick(View v) { 289 public void onClick(View v) {
247 mItemSelectedCallback.onItemSelected( 290 mItemSelectedCallback.onItemSelected(
248 mItemAdapter.getSelectedItemKey()); 291 mItemAdapter.getSelectedItemKey());
249 mDialog.dismiss(); 292 mDialog.dismiss();
250 } 293 }
251 }); 294 });
252 295
253 mItemAdapter = new ItemAdapter(mContext, R.layout.item_chooser_dialog_ro w); 296 mItemAdapter = new ItemAdapter(mContext, R.layout.item_chooser_dialog_ro w);
254 mListView.setAdapter(mItemAdapter); 297 mListView.setAdapter(mItemAdapter);
255 mListView.setEmptyView(mNotFoundMessage); 298 mListView.setEmptyView(mEmptyMessage);
256 mNotFoundMessage.setVisibility(View.GONE);
257 mListView.setOnItemClickListener(mItemAdapter); 299 mListView.setOnItemClickListener(mItemAdapter);
258 mListView.setDivider(null); 300 mListView.setDivider(null);
301 setState(State.STARTING);
259 302
260 // The list is the main element in the dialog and it should grow and 303 // The list is the main element in the dialog and it should grow and
261 // shrink according to the size of the screen available (clamped to a 304 // shrink according to the size of the screen available (clamped to a
262 // min and a max). 305 // min and a max).
263 View listViewContainer = dialogContainer.findViewById(R.id.container); 306 View listViewContainer = dialogContainer.findViewById(R.id.container);
264 DisplayMetrics metrics = new DisplayMetrics(); 307 DisplayMetrics metrics = new DisplayMetrics();
265 WindowManager manager = (WindowManager) mContext.getSystemService( 308 WindowManager manager = (WindowManager) mContext.getSystemService(
266 Context.WINDOW_SERVICE); 309 Context.WINDOW_SERVICE);
267 manager.getDefaultDisplay().getMetrics(metrics); 310 manager.getDefaultDisplay().getMetrics(metrics);
268 311
269 float density = context.getResources().getDisplayMetrics().density; 312 float density = context.getResources().getDisplayMetrics().density;
270 int height = (int) (metrics.heightPixels * LISTVIEW_HEIGHT_PERCENT); 313 int height = (int) (metrics.heightPixels * LISTVIEW_HEIGHT_PERCENT);
271 height = Math.min(height, Math.round(MAX_HEIGHT_DP * density)); 314 height = Math.min(height, Math.round(MAX_HEIGHT_DP * density));
272 height = Math.max(height, Math.round(MIN_HEIGHT_DP * density)); 315 height = Math.max(height, Math.round(MIN_HEIGHT_DP * density));
273 listViewContainer.setLayoutParams( 316 listViewContainer.setLayoutParams(
274 new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, height) ); 317 new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, height) );
275 318
276 showDialogForView(dialogContainer); 319 showDialogForView(dialogContainer);
277 } 320 }
278 321
279 private void showDialogForView(View view) { 322 private void showDialogForView(View view) {
280 mDialog = new Dialog(mContext); 323 mDialog = new Dialog(mContext);
281 mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); 324 mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
282 mDialog.addContentView(view, 325 mDialog.addContentView(view,
283 new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PA RENT, 326 new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PA RENT,
284 LinearLayout.LayoutParams.MATCH_PA RENT)); 327 LinearLayout.LayoutParams.MATCH_PA RENT));
328 mDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
329 @Override
330 public void onDismiss(DialogInterface dialog) {
331 mItemSelectedCallback.onItemSelected("");
newt (away) 2015/09/02 17:48:42 Seems like you'll end up calling onItemSelected()
Finnur 2015/09/03 11:51:45 Both handlers do, indeed, fire, but the second mes
332 }
333 });
285 334
286 Window window = mDialog.getWindow(); 335 Window window = mDialog.getWindow();
287 if (!DeviceFormFactor.isTablet(mContext)) { 336 if (!DeviceFormFactor.isTablet(mContext)) {
288 // On smaller screens, make the dialog fill the width of the screen, 337 // On smaller screens, make the dialog fill the width of the screen,
289 // and appear at the top. 338 // and appear at the top.
290 window.setBackgroundDrawable(new ColorDrawable(Color.WHITE)); 339 window.setBackgroundDrawable(new ColorDrawable(Color.WHITE));
291 window.setGravity(Gravity.TOP); 340 window.setGravity(Gravity.TOP);
292 window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, 341 window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
293 ViewGroup.LayoutParams.WRAP_CONTENT); 342 ViewGroup.LayoutParams.WRAP_CONTENT);
294 } 343 }
295 344
296 mDialog.show(); 345 mDialog.show();
297 } 346 }
298 347
348 public void dismiss() {
349 mDialog.dismiss();
350 }
351
299 /** 352 /**
300 * Add items to show in the dialog. 353 * Add items to show in the dialog.
301 * 354 *
302 * @param list The list of items to show. This function can be called 355 * @param list The list of items to show. This function can be called
303 * multiple times to add more items and new items will be appended to 356 * multiple times to add more items and new items will be appended to
304 * the end of the list. An empty list should be used if there are no 357 * the end of the list. An empty list should be used if there are no
305 * items to show. 358 * items to show.
306 */ 359 */
307 public void showList(List<ItemChooserRow> list) { 360 public void showList(List<ItemChooserRow> list) {
308 mProgressBar.setVisibility(View.GONE); 361 mProgressBar.setVisibility(View.GONE);
309 mStatus.setText(mLabels.mStatus);
310 362
311 if (list.isEmpty()) { 363 if (!list.isEmpty()) {
312 boolean showEmptyMessage = mItemAdapter.isEmpty();
313 mNotFoundMessage.setVisibility(
314 showEmptyMessage ? View.VISIBLE : View.GONE);
315 mListView.setVisibility(showEmptyMessage ? View.GONE : View.VISIBLE) ;
316 } else {
317 mListView.setVisibility(View.VISIBLE);
318 mItemAdapter.addAll(list); 364 mItemAdapter.addAll(list);
319 } 365 }
366 setState(State.PROGRESS_UPDATE_AVAILABLE);
320 } 367 }
321 368
322 /** 369 /**
370 * Sets whether the item is enabled.
371 * @param id The id of the item to affect.
372 * @param enabled Whether the item should be enabled or not.
373 */
374 public void setEnabled(String id, boolean enabled) {
375 mItemAdapter.setEnabled(id, enabled);
376 }
377
378 /**
323 * Clear all items from the dialog. 379 * Clear all items from the dialog.
324 */ 380 */
325 public void clear() { 381 public void clear() {
326 mItemAdapter.clear(); 382 mItemAdapter.clear();
383 setState(State.STARTING);
384 }
385
386 /**
387 * Set the error state for the dialog.
388 */
389 public void setErrorState() {
390 setState(State.SHOWING_ERROR);
391 }
392
393 private void setState(State state) {
394 switch (state) {
395 case STARTING:
396 mStatus.setText(mLabels.mSearching);
397 mListView.setVisibility(View.GONE);
398 mProgressBar.setVisibility(View.VISIBLE);
399 break;
400 case SHOWING_ERROR:
401 mProgressBar.setVisibility(View.GONE);
402 mEmptyMessage.setText(mLabels.mErrorMessage);
403 mStatus.setText(mLabels.mErrorStatus);
404 break;
405 case PROGRESS_UPDATE_AVAILABLE:
406 mStatus.setText(mLabels.mStatus);
407 mProgressBar.setVisibility(View.GONE);
408 mListView.setVisibility(View.VISIBLE);
409
410 boolean showEmptyMessage = mItemAdapter.isEmpty();
411 mEmptyMessage.setText(mLabels.mNoneFound);
412 mEmptyMessage.setVisibility(showEmptyMessage ? View.VISIBLE : Vi ew.GONE);
413 break;
414 }
327 } 415 }
328 } 416 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698