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

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

Powered by Google App Engine
This is Rietveld 408576698