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

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

Issue 2426573005: bluetooth-chooser: Refactor and clean up bluetooth chooser (Closed)
Patch Set: Address juncai's comments Created 4 years, 2 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.Activity; 7 import android.app.Activity;
8 import android.app.Dialog; 8 import android.app.Dialog;
9 import android.content.Context; 9 import android.content.Context;
10 import android.content.DialogInterface; 10 import android.content.DialogInterface;
11 import android.graphics.Color; 11 import android.graphics.Color;
12 import android.graphics.drawable.ColorDrawable; 12 import android.graphics.drawable.ColorDrawable;
13 import android.text.SpannableString; 13 import android.text.SpannableString;
14 import android.text.method.LinkMovementMethod; 14 import android.text.method.LinkMovementMethod;
15 import android.view.Gravity; 15 import android.view.Gravity;
16 import android.view.LayoutInflater; 16 import android.view.LayoutInflater;
17 import android.view.View; 17 import android.view.View;
18 import android.view.ViewGroup; 18 import android.view.ViewGroup;
19 import android.view.ViewGroup.LayoutParams; 19 import android.view.ViewGroup.LayoutParams;
20 import android.view.Window; 20 import android.view.Window;
21 import android.widget.AdapterView; 21 import android.widget.AdapterView;
22 import android.widget.ArrayAdapter; 22 import android.widget.ArrayAdapter;
23 import android.widget.Button; 23 import android.widget.Button;
24 import android.widget.LinearLayout; 24 import android.widget.LinearLayout;
25 import android.widget.ListView; 25 import android.widget.ListView;
26 import android.widget.ProgressBar; 26 import android.widget.ProgressBar;
27 import android.widget.TextView; 27 import android.widget.TextView;
28 28
29 import org.chromium.base.ApiCompatibilityUtils;
30 import org.chromium.base.VisibleForTesting; 29 import org.chromium.base.VisibleForTesting;
31 import org.chromium.chrome.R; 30 import org.chromium.chrome.R;
32 import org.chromium.chrome.browser.util.MathUtils; 31 import org.chromium.chrome.browser.util.MathUtils;
33 import org.chromium.ui.base.DeviceFormFactor; 32 import org.chromium.ui.base.DeviceFormFactor;
34 import org.chromium.ui.widget.TextViewWithClickableSpans; 33 import org.chromium.ui.widget.TextViewWithClickableSpans;
35 34
36 import java.util.HashMap; 35 import java.util.HashMap;
37 import java.util.HashSet; 36 import java.util.HashSet;
38 import java.util.Map; 37 import java.util.Map;
39 import java.util.Set; 38 import java.util.Set;
(...skipping 15 matching lines...) Expand all
55 * 54 *
56 * @param id The id of the item selected. Blank if the dialog was closed 55 * @param id The id of the item selected. Blank if the dialog was closed
57 * without selecting anything. 56 * without selecting anything.
58 */ 57 */
59 void onItemSelected(String id); 58 void onItemSelected(String id);
60 } 59 }
61 60
62 /** 61 /**
63 * A class representing one data row in the picker. 62 * A class representing one data row in the picker.
64 */ 63 */
65 public static class ItemChooserRow { 64 public static class ItemChooserRow {
Ted C 2016/10/20 21:50:18 should/can this class be private now?
ortuno 2016/10/21 02:14:07 Done.
66 private final String mKey; 65 private final String mKey;
67 private String mDescription; 66 private String mDescription;
68 67
69 public ItemChooserRow(String key, String description) { 68 public ItemChooserRow(String key, String description) {
70 mKey = key; 69 mKey = key;
71 mDescription = description; 70 mDescription = description;
72 } 71 }
73 72
74 @Override 73 public boolean hasSameContents(String key, String description) {
Ted C 2016/10/20 21:50:18 non-private methods should have javadoc
ortuno 2016/10/21 02:14:07 Tests use hasSameContents and a follow up patch wi
75 public boolean equals(Object obj) { 74 if (!mKey.equals(key)) return false;
Ted C 2016/10/20 21:50:18 use TextUtils.equals
ortuno 2016/10/21 02:14:07 Done.
76 if (!(obj instanceof ItemChooserRow)) return false; 75 if (!mDescription.equals(description)) return false;
77 if (this == obj) return true; 76 return true;
78 ItemChooserRow item = (ItemChooserRow) obj;
79 return mKey.equals(item.mKey) && mDescription.equals(item.mDescripti on);
80 }
81
82 @Override
83 public int hashCode() {
84 return mKey.hashCode() + mDescription.hashCode();
85 } 77 }
86 } 78 }
87 79
88 /** 80 /**
89 * The labels to show in the dialog. 81 * The labels to show in the dialog.
90 */ 82 */
91 public static class ItemChooserLabels { 83 public static class ItemChooserLabels {
92 // The title at the top of the dialog. 84 // The title at the top of the dialog.
93 public final CharSequence title; 85 public final CharSequence title;
94 // The message to show while there are no results. 86 // The message to show while there are no results.
(...skipping 19 matching lines...) Expand all
114 this.searching = searching; 106 this.searching = searching;
115 this.noneFound = noneFound; 107 this.noneFound = noneFound;
116 this.statusActive = statusActive; 108 this.statusActive = statusActive;
117 this.statusIdleNoneFound = statusIdleNoneFound; 109 this.statusIdleNoneFound = statusIdleNoneFound;
118 this.statusIdleSomeFound = statusIdleSomeFound; 110 this.statusIdleSomeFound = statusIdleSomeFound;
119 this.positiveButton = positiveButton; 111 this.positiveButton = positiveButton;
120 } 112 }
121 } 113 }
122 114
123 /** 115 /**
116 * Item holder for performance boost.
117 */
118 private static class ViewHolder {
119 private TextView mTextView;
120
121 public ViewHolder(View view) {
122 mTextView = (TextView) view.findViewById(R.id.description);
123 }
124 }
125
126 /**
124 * The various states the dialog can represent. 127 * The various states the dialog can represent.
125 */ 128 */
126 private enum State { STARTING, PROGRESS_UPDATE_AVAILABLE, DISCOVERY_IDLE } 129 private enum State { STARTING, PROGRESS_UPDATE_AVAILABLE, DISCOVERY_IDLE }
127 130
128 /** 131 /**
129 * An adapter for keeping track of which items to show in the dialog. 132 * An adapter for keeping track of which items to show in the dialog.
130 */ 133 */
131 public class ItemAdapter extends ArrayAdapter<ItemChooserRow> 134 public class ItemAdapter extends ArrayAdapter<ItemChooserRow>
132 implements AdapterView.OnItemClickListener { 135 implements AdapterView.OnItemClickListener {
133 private final LayoutInflater mInflater; 136 private final LayoutInflater mInflater;
134 137
135 // The background color of the highlighted item.
136 private final int mBackgroundHighlightColor;
137
138 // The color of the non-highlighted text.
139 private final int mDefaultTextColor;
140
141 // The zero-based index of the item currently selected in the dialog, 138 // The zero-based index of the item currently selected in the dialog,
142 // or -1 (INVALID_POSITION) if nothing is selected. 139 // or -1 (INVALID_POSITION) if nothing is selected.
143 private int mSelectedItem = ListView.INVALID_POSITION; 140 private int mSelectedItem = ListView.INVALID_POSITION;
144 141
145 // A set of keys that are marked as disabled in the dialog. 142 // A set of keys that are marked as disabled in the dialog.
146 private Set<String> mDisabledEntries = new HashSet<String>(); 143 private Set<String> mDisabledEntries = new HashSet<String>();
147 144
148 // Item descriptions are counted in a map. 145 // Item descriptions are counted in a map.
149 private Map<String, Integer> mItemDescriptionMap = new HashMap<>(); 146 private Map<String, Integer> mItemDescriptionMap = new HashMap<>();
150 147
151 // Map of keys to items so that we can access the items in O(1). 148 // Map of keys to items so that we can access the items in O(1).
152 private Map<String, ItemChooserRow> mKeyToItemMap = new HashMap<>(); 149 private Map<String, ItemChooserRow> mKeyToItemMap = new HashMap<>();
153 150
154 public ItemAdapter(Context context, int resource) { 151 public ItemAdapter(Context context, int resource) {
155 super(context, resource); 152 super(context, resource);
156 153
157 mInflater = LayoutInflater.from(context); 154 mInflater = LayoutInflater.from(context);
158
159 mBackgroundHighlightColor = ApiCompatibilityUtils.getColor(getContex t().getResources(),
160 R.color.light_active_color);
161 mDefaultTextColor = ApiCompatibilityUtils.getColor(getContext().getR esources(),
162 R.color.default_text_color);
163 } 155 }
164 156
165 @Override 157 @Override
166 public boolean isEmpty() { 158 public boolean isEmpty() {
167 boolean isEmpty = super.isEmpty(); 159 boolean isEmpty = super.isEmpty();
168 if (isEmpty) { 160 if (isEmpty) {
169 assert mKeyToItemMap.isEmpty(); 161 assert mKeyToItemMap.isEmpty();
170 assert mDisabledEntries.isEmpty(); 162 assert mDisabledEntries.isEmpty();
171 assert mItemDescriptionMap.isEmpty(); 163 assert mItemDescriptionMap.isEmpty();
172 } else { 164 } else {
173 assert !mKeyToItemMap.isEmpty(); 165 assert !mKeyToItemMap.isEmpty();
174 assert !mItemDescriptionMap.isEmpty(); 166 assert !mItemDescriptionMap.isEmpty();
175 } 167 }
176 return isEmpty; 168 return isEmpty;
177 } 169 }
178 170
179 public void addOrUpdate(ItemChooserRow item) { 171 public void addOrUpdate(String key, String description) {
180 ItemChooserRow oldItem = mKeyToItemMap.get(item.mKey); 172 ItemChooserRow oldItem = mKeyToItemMap.get(key);
181 if (oldItem != null) { 173 if (oldItem != null) {
182 if (oldItem.equals(item)) { 174 if (oldItem.hasSameContents(key, description)) {
183 // No need to update anything. 175 // No need to update anything.
184 return; 176 return;
185 } 177 }
186 if (!oldItem.mDescription.equals(item.mDescription)) { 178
179 if (!oldItem.mDescription.equals(description)) {
Ted C 2016/10/20 21:50:18 I would use TextUtils.equals here instead (incase
ortuno 2016/10/21 02:14:07 Ah good idea. I didn't know about TextUtils. Done.
187 removeFromDescriptionsMap(oldItem.mDescription); 180 removeFromDescriptionsMap(oldItem.mDescription);
188 oldItem.mDescription = item.mDescription; 181 oldItem.mDescription = description;
189 addToDescriptionsMap(oldItem.mDescription); 182 addToDescriptionsMap(oldItem.mDescription);
190 } 183 }
184
191 notifyDataSetChanged(); 185 notifyDataSetChanged();
192 return; 186 return;
193 } 187 }
194 ItemChooserRow result = mKeyToItemMap.put(item.mKey, item);
195 assert result == null;
196 188
197 addToDescriptionsMap(item.mDescription); 189 assert !mKeyToItemMap.containsKey(key);
198 add(item); 190 ItemChooserRow newItem = new ItemChooserRow(key, description);
191 mKeyToItemMap.put(key, newItem);
192
193 addToDescriptionsMap(newItem.mDescription);
194 add(newItem);
199 } 195 }
200 196
201 @Override 197 public void remove(String key) {
Ted C 2016/10/20 21:50:18 I would try to avoid overloads of names in the pub
ortuno 2016/10/21 02:14:07 Done.
202 public void remove(ItemChooserRow item) { 198 ItemChooserRow oldItem = mKeyToItemMap.remove(key);
203 ItemChooserRow oldItem = mKeyToItemMap.remove(item.mKey);
204 if (oldItem == null) return; 199 if (oldItem == null) return;
205 int oldItemPosition = getPosition(oldItem); 200 int oldItemPosition = getPosition(oldItem);
206 // If the removed item is the item that is currently selected, desel ect it 201 // If the removed item is the item that is currently selected, desel ect it
207 // and disable the confirm button. Otherwise if the removed item is before 202 // and disable the confirm button. Otherwise if the removed item is before
208 // the currently selected item, the currently selected item's index needs 203 // the currently selected item, the currently selected item's index needs
209 // to be adjusted by one. 204 // to be adjusted by one.
210 if (oldItemPosition == mSelectedItem) { 205 if (oldItemPosition == mSelectedItem) {
211 mSelectedItem = ListView.INVALID_POSITION; 206 mSelectedItem = ListView.INVALID_POSITION;
212 mConfirmButton.setEnabled(false); 207 mConfirmButton.setEnabled(false);
213 } else if (oldItemPosition < mSelectedItem) { 208 } else if (oldItemPosition < mSelectedItem) {
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
285 return 1; 280 return 1;
286 } 281 }
287 282
288 @Override 283 @Override
289 public long getItemId(int position) { 284 public long getItemId(int position) {
290 return position; 285 return position;
291 } 286 }
292 287
293 @Override 288 @Override
294 public View getView(int position, View convertView, ViewGroup parent) { 289 public View getView(int position, View convertView, ViewGroup parent) {
295 TextView view; 290 ViewHolder row;
296 if (convertView instanceof TextView) { 291 if (convertView == null) {
297 view = (TextView) convertView; 292 convertView = mInflater.inflate(R.layout.item_chooser_dialog_row , parent, false);
293 row = new ViewHolder(convertView);
294 convertView.setTag(row);
298 } else { 295 } else {
299 view = (TextView) mInflater.inflate( 296 row = (ViewHolder) convertView.getTag();
300 R.layout.item_chooser_dialog_row, parent, false);
301 } 297 }
302 298
303 // Set highlighting for currently selected item. 299 row.mTextView.setEnabled(isEnabled(position));
304 if (position == mSelectedItem) { 300 row.mTextView.setText(getDisplayText(position));
305 view.setBackgroundColor(mBackgroundHighlightColor);
306 view.setTextColor(Color.WHITE);
307 } else {
308 view.setBackground(null);
309 if (!isEnabled(position)) {
310 view.setTextColor(ApiCompatibilityUtils.getColor(getContext( ).getResources(),
311 R.color.primary_text_disabled_material_light));
312 } else {
313 view.setTextColor(mDefaultTextColor);
314 }
315 }
316 301
317 view.setText(getDisplayText(position)); 302 return convertView;
318 return view;
319 } 303 }
320 304
321 @Override 305 @Override
322 public void onItemClick(AdapterView<?> adapter, View view, int position, long id) { 306 public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
323 mSelectedItem = position; 307 mSelectedItem = position;
324 mConfirmButton.setEnabled(true); 308 mConfirmButton.setEnabled(true);
325 mItemAdapter.notifyDataSetChanged(); 309 notifyDataSetChanged();
326 } 310 }
327 311
328 private void addToDescriptionsMap(String description) { 312 private void addToDescriptionsMap(String description) {
329 int count = mItemDescriptionMap.containsKey(description) 313 int count = mItemDescriptionMap.containsKey(description)
330 ? mItemDescriptionMap.get(description) 314 ? mItemDescriptionMap.get(description)
331 : 0; 315 : 0;
332 mItemDescriptionMap.put(description, count + 1); 316 mItemDescriptionMap.put(description, count + 1);
333 } 317 }
334 318
335 private void removeFromDescriptionsMap(String description) { 319 private void removeFromDescriptionsMap(String description) {
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
414 public void onClick(View v) { 398 public void onClick(View v) {
415 mItemSelectedCallback.onItemSelected(mItemAdapter.getSelectedIte mKey()); 399 mItemSelectedCallback.onItemSelected(mItemAdapter.getSelectedIte mKey());
416 mDialog.setOnDismissListener(null); 400 mDialog.setOnDismissListener(null);
417 mDialog.dismiss(); 401 mDialog.dismiss();
418 } 402 }
419 }); 403 });
420 404
421 mItemAdapter = new ItemAdapter(mActivity, R.layout.item_chooser_dialog_r ow); 405 mItemAdapter = new ItemAdapter(mActivity, R.layout.item_chooser_dialog_r ow);
422 mItemAdapter.setNotifyOnChange(true); 406 mItemAdapter.setNotifyOnChange(true);
423 mListView.setAdapter(mItemAdapter); 407 mListView.setAdapter(mItemAdapter);
408 mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
424 mListView.setEmptyView(mEmptyMessage); 409 mListView.setEmptyView(mEmptyMessage);
425 mListView.setOnItemClickListener(mItemAdapter); 410 mListView.setOnItemClickListener(mItemAdapter);
426 mListView.setDivider(null); 411 mListView.setDivider(null);
427 setState(State.STARTING); 412 setState(State.STARTING);
428 413
429 // The list is the main element in the dialog and it should grow and 414 // The list is the main element in the dialog and it should grow and
430 // shrink according to the size of the screen available. 415 // shrink according to the size of the screen available.
431 View listViewContainer = dialogContainer.findViewById(R.id.container); 416 View listViewContainer = dialogContainer.findViewById(R.id.container);
432 listViewContainer.setLayoutParams(new LinearLayout.LayoutParams( 417 listViewContainer.setLayoutParams(new LinearLayout.LayoutParams(
433 LayoutParams.MATCH_PARENT, 418 LayoutParams.MATCH_PARENT,
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
479 } 464 }
480 465
481 mDialog.show(); 466 mDialog.show();
482 } 467 }
483 468
484 public void dismiss() { 469 public void dismiss() {
485 mDialog.dismiss(); 470 mDialog.dismiss();
486 } 471 }
487 472
488 /** 473 /**
489 * Add an item to the end of the list to show in the dialog if the item 474 * Adds an item to the end of the list to show in the dialog if the item
490 * was not in the chooser. Otherwise update the items description. 475 * was not in the chooser. Otherwise updates the items description.
491 * 476 *
492 * @param item The item to be added to the end of the chooser or updated. 477 * @param key Unique identifier for that item.
493 */ 478 * @param description Text in the row.
494 public void addOrUpdateItem(ItemChooserRow item) { 479 */
480 public void addOrUpdateItem(String key, String description) {
495 mProgressBar.setVisibility(View.GONE); 481 mProgressBar.setVisibility(View.GONE);
496 mItemAdapter.addOrUpdate(item); 482 mItemAdapter.addOrUpdate(key, description);
497 setState(State.PROGRESS_UPDATE_AVAILABLE); 483 setState(State.PROGRESS_UPDATE_AVAILABLE);
498 } 484 }
499 485
500 /** 486 /**
501 * Remove an item that is shown in the dialog. 487 * Removes an item that is shown in the dialog.
502 * 488 *
503 * @param item The item to be removed in the chooser. 489 * @param key Unique identifier for the item.
504 */ 490 */
505 public void removeItemFromList(ItemChooserRow item) { 491 public void removeItemFromList(String key) {
506 mItemAdapter.remove(item); 492 mItemAdapter.remove(key);
507 setState(State.DISCOVERY_IDLE); 493 setState(State.DISCOVERY_IDLE);
508 } 494 }
509 495
510 /** 496 /**
511 * Indicates the chooser that no more items will be added. 497 * Indicates the chooser that no more items will be added.
512 */ 498 */
513 public void setIdleState() { 499 public void setIdleState() {
514 mProgressBar.setVisibility(View.GONE); 500 mProgressBar.setVisibility(View.GONE);
515 setState(State.DISCOVERY_IDLE); 501 setState(State.DISCOVERY_IDLE);
516 } 502 }
517 503
518 /** 504 /**
519 * Sets whether the item is enabled. 505 * Sets whether the item is enabled.
520 * @param id The id of the item to affect. 506 * @param key Unique indetifier for the item.
521 * @param enabled Whether the item should be enabled or not. 507 * @param enabled Whether the item should be enabled or not.
522 */ 508 */
523 public void setEnabled(String id, boolean enabled) { 509 public void setEnabled(String key, boolean enabled) {
524 mItemAdapter.setEnabled(id, enabled); 510 mItemAdapter.setEnabled(key, enabled);
525 } 511 }
526 512
527 /** 513 /**
528 * Clear all items from the dialog. 514 * Clear all items from the dialog.
529 */ 515 */
530 public void clear() { 516 public void clear() {
531 mItemAdapter.clear(); 517 mItemAdapter.clear();
532 setState(State.STARTING); 518 setState(State.STARTING);
533 } 519 }
534 520
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
575 } 561 }
576 562
577 /** 563 /**
578 * Returns the ItemAdapter associated with this class. For use with tests on ly. 564 * Returns the ItemAdapter associated with this class. For use with tests on ly.
579 */ 565 */
580 @VisibleForTesting 566 @VisibleForTesting
581 public ItemAdapter getItemAdapterForTesting() { 567 public ItemAdapter getItemAdapterForTesting() {
582 return mItemAdapter; 568 return mItemAdapter;
583 } 569 }
584 } 570 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698