blob: ea32ec9ed0ef54fda6d75bfe6c6d4214732c1bd0 [file] [log] [blame]
Hansong Zhang80cfa562018-04-26 12:26:49 -07001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.music;
18
19import android.app.ListActivity;
20import android.content.AsyncQueryHandler;
21import android.content.ContentUris;
22import android.content.Context;
23import android.content.Intent;
24import android.database.CharArrayBuffer;
25import android.database.Cursor;
26import android.media.AudioManager;
27import android.media.MediaPlayer;
28import android.media.RingtoneManager;
29import android.net.Uri;
30import android.os.Bundle;
31import android.os.Parcelable;
32import android.provider.MediaStore;
33import android.text.TextUtils;
34import android.util.Log;
35import android.view.Menu;
36import android.view.MenuItem;
37import android.view.View;
38import android.view.ViewGroup;
39import android.view.Window;
40import android.view.animation.AnimationUtils;
41import android.widget.ImageView;
42import android.widget.ListView;
43import android.widget.RadioButton;
44import android.widget.SectionIndexer;
45import android.widget.SimpleCursorAdapter;
46import android.widget.TextView;
47
48import java.io.IOException;
49import java.text.Collator;
50import java.util.Formatter;
51import java.util.Locale;
52
53/**
54 * Activity allowing the user to select a music track on the device, and
55 * return it to its caller. The music picker user interface is fairly
56 * extensive, providing information about each track like the music
57 * application (title, author, album, duration), as well as the ability to
58 * previous tracks and sort them in different orders.
59 *
60 * <p>This class also illustrates how you can load data from a content
61 * provider asynchronously, providing a good UI while doing so, perform
62 * indexing of the content for use inside of a {@link FastScrollView}, and
63 * perform filtering of the data as the user presses keys.
64 */
65public class MusicPicker
66 extends ListActivity implements View.OnClickListener, MediaPlayer.OnCompletionListener {
67 static final boolean DBG = false;
68 static final String TAG = "MusicPicker";
69
70 /** Holds the previous state of the list, to restore after the async
71 * query has completed. */
72 static final String LIST_STATE_KEY = "liststate";
73 /** Remember whether the list last had focus for restoring its state. */
74 static final String FOCUS_KEY = "focused";
75 /** Remember the last ordering mode for restoring state. */
76 static final String SORT_MODE_KEY = "sortMode";
77
78 /** Arbitrary number, doesn't matter since we only do one query type. */
79 static final int MY_QUERY_TOKEN = 42;
80
81 /** Menu item to sort the music list by track title. */
82 static final int TRACK_MENU = Menu.FIRST;
83 /** Menu item to sort the music list by album title. */
84 static final int ALBUM_MENU = Menu.FIRST + 1;
85 /** Menu item to sort the music list by artist name. */
86 static final int ARTIST_MENU = Menu.FIRST + 2;
87
88 /** These are the columns in the music cursor that we are interested in. */
89 static final String[] CURSOR_COLS = new String[] {MediaStore.Audio.Media._ID,
90 MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.TITLE_KEY,
91 MediaStore.Audio.Media.DATA, MediaStore.Audio.Media.ALBUM,
92 MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.ARTIST_ID,
93 MediaStore.Audio.Media.DURATION, MediaStore.Audio.Media.TRACK};
94
95 /** Formatting optimization to avoid creating many temporary objects. */
96 static StringBuilder sFormatBuilder = new StringBuilder();
97 /** Formatting optimization to avoid creating many temporary objects. */
98 static Formatter sFormatter = new Formatter(sFormatBuilder, Locale.getDefault());
99 /** Formatting optimization to avoid creating many temporary objects. */
100 static final Object[] sTimeArgs = new Object[5];
101
102 /** Uri to the directory of all music being displayed. */
103 Uri mBaseUri;
104
105 /** This is the adapter used to display all of the tracks. */
106 TrackListAdapter mAdapter;
107 /** Our instance of QueryHandler used to perform async background queries. */
108 QueryHandler mQueryHandler;
109
110 /** Used to keep track of the last scroll state of the list. */
111 Parcelable mListState = null;
112 /** Used to keep track of whether the list last had focus. */
113 boolean mListHasFocus;
114
115 /** The current cursor on the music that is being displayed. */
116 Cursor mCursor;
117 /** The actual sort order the user has selected. */
118 int mSortMode = -1;
119 /** SQL order by string describing the currently selected sort order. */
120 String mSortOrder;
121
122 /** Container of the in-screen progress indicator, to be able to hide it
123 * when done loading the initial cursor. */
124 View mProgressContainer;
125 /** Container of the list view hierarchy, to be able to show it when done
126 * loading the initial cursor. */
127 View mListContainer;
128 /** Set to true when the list view has been shown for the first time. */
129 boolean mListShown;
130
131 /** View holding the okay button. */
132 View mOkayButton;
133 /** View holding the cancel button. */
134 View mCancelButton;
135
136 /** Which track row ID the user has last selected. */
137 long mSelectedId = -1;
138 /** Completel Uri that the user has last selected. */
139 Uri mSelectedUri;
140
141 /** If >= 0, we are currently playing a track for preview, and this is its
142 * row ID. */
143 long mPlayingId = -1;
144
145 /** This is used for playing previews of the music files. */
146 MediaPlayer mMediaPlayer;
147
148 /**
149 * A special implementation of SimpleCursorAdapter that knows how to bind
150 * our cursor data to our list item structure, and takes care of other
151 * advanced features such as indexing and filtering.
152 */
153 class TrackListAdapter extends SimpleCursorAdapter implements SectionIndexer {
154 final ListView mListView;
155
156 private final StringBuilder mBuilder = new StringBuilder();
157 private final String mUnknownArtist;
158 private final String mUnknownAlbum;
159
160 private int mIdIdx;
161 private int mTitleIdx;
162 private int mArtistIdx;
163 private int mAlbumIdx;
164 private int mDurationIdx;
165
166 private boolean mLoading = true;
167 private int mIndexerSortMode;
168 private MusicAlphabetIndexer mIndexer;
169
170 class ViewHolder {
171 TextView line1;
172 TextView line2;
173 TextView duration;
174 RadioButton radio;
175 ImageView play_indicator;
176 CharArrayBuffer buffer1;
177 char[] buffer2;
178 }
179
180 TrackListAdapter(Context context, ListView listView, int layout, String[] from, int[] to) {
181 super(context, layout, null, from, to);
182 mListView = listView;
183 mUnknownArtist = context.getString(R.string.unknown_artist_name);
184 mUnknownAlbum = context.getString(R.string.unknown_album_name);
185 }
186
187 /**
188 * The mLoading flag is set while we are performing a background
189 * query, to avoid displaying the "No music" empty view during
190 * this time.
191 */
192 public void setLoading(boolean loading) {
193 mLoading = loading;
194 }
195
196 @Override
197 public boolean isEmpty() {
198 if (mLoading) {
199 // We don't want the empty state to show when loading.
200 return false;
201 } else {
202 return super.isEmpty();
203 }
204 }
205
206 @Override
207 public View newView(Context context, Cursor cursor, ViewGroup parent) {
208 View v = super.newView(context, cursor, parent);
209 ViewHolder vh = new ViewHolder();
210 vh.line1 = (TextView) v.findViewById(R.id.line1);
211 vh.line2 = (TextView) v.findViewById(R.id.line2);
212 vh.duration = (TextView) v.findViewById(R.id.duration);
213 vh.radio = (RadioButton) v.findViewById(R.id.radio);
214 vh.play_indicator = (ImageView) v.findViewById(R.id.play_indicator);
215 vh.buffer1 = new CharArrayBuffer(100);
216 vh.buffer2 = new char[200];
217 v.setTag(vh);
218 return v;
219 }
220
221 @Override
222 public void bindView(View view, Context context, Cursor cursor) {
223 ViewHolder vh = (ViewHolder) view.getTag();
224
225 cursor.copyStringToBuffer(mTitleIdx, vh.buffer1);
226 vh.line1.setText(vh.buffer1.data, 0, vh.buffer1.sizeCopied);
227
228 int secs = cursor.getInt(mDurationIdx) / 1000;
229 if (secs == 0) {
230 vh.duration.setText("");
231 } else {
232 vh.duration.setText(MusicUtils.makeTimeString(context, secs));
233 }
234
235 final StringBuilder builder = mBuilder;
236 builder.delete(0, builder.length());
237
238 String name = cursor.getString(mAlbumIdx);
239 if (name == null || name.equals("<unknown>")) {
240 builder.append(mUnknownAlbum);
241 } else {
242 builder.append(name);
243 }
244 builder.append('\n');
245 name = cursor.getString(mArtistIdx);
246 if (name == null || name.equals("<unknown>")) {
247 builder.append(mUnknownArtist);
248 } else {
249 builder.append(name);
250 }
251 int len = builder.length();
252 if (vh.buffer2.length < len) {
253 vh.buffer2 = new char[len];
254 }
255 builder.getChars(0, len, vh.buffer2, 0);
256 vh.line2.setText(vh.buffer2, 0, len);
257
258 // Update the checkbox of the item, based on which the user last
259 // selected. Note that doing it this way means we must have the
260 // list view update all of its items when the selected item
261 // changes.
262 final long id = cursor.getLong(mIdIdx);
263 vh.radio.setChecked(id == mSelectedId);
264 if (DBG)
265 Log.v(TAG,
266 "Binding id=" + id + " sel=" + mSelectedId + " playing=" + mPlayingId
267 + " cursor=" + cursor);
268
269 // Likewise, display the "now playing" icon if this item is
270 // currently being previewed for the user.
271 ImageView iv = vh.play_indicator;
272 if (id == mPlayingId) {
273 iv.setImageResource(R.drawable.indicator_ic_mp_playing_list);
274 iv.setVisibility(View.VISIBLE);
275 } else {
276 iv.setVisibility(View.GONE);
277 }
278 }
279
280 /**
281 * This method is called whenever we receive a new cursor due to
282 * an async query, and must take care of plugging the new one in
283 * to the adapter.
284 */
285 @Override
286 public void changeCursor(Cursor cursor) {
287 super.changeCursor(cursor);
288 if (DBG)
289 Log.v(TAG, "Setting cursor to: " + cursor + " from: " + MusicPicker.this.mCursor);
290
291 MusicPicker.this.mCursor = cursor;
292
293 if (cursor != null) {
294 // Retrieve indices of the various columns we are interested in.
295 mIdIdx = cursor.getColumnIndex(MediaStore.Audio.Media._ID);
296 mTitleIdx = cursor.getColumnIndex(MediaStore.Audio.Media.TITLE);
297 mArtistIdx = cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST);
298 mAlbumIdx = cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM);
299 mDurationIdx = cursor.getColumnIndex(MediaStore.Audio.Media.DURATION);
300
301 // If the sort mode has changed, or we haven't yet created an
302 // indexer one, then create a new one that is indexing the
303 // appropriate column based on the sort mode.
304 if (mIndexerSortMode != mSortMode || mIndexer == null) {
305 mIndexerSortMode = mSortMode;
306 int idx = mTitleIdx;
307 switch (mIndexerSortMode) {
308 case ARTIST_MENU:
309 idx = mArtistIdx;
310 break;
311 case ALBUM_MENU:
312 idx = mAlbumIdx;
313 break;
314 }
315 mIndexer = new MusicAlphabetIndexer(
316 cursor, idx, getResources().getString(R.string.fast_scroll_alphabet));
317
318 // If we have a valid indexer, but the cursor has changed since
319 // its last use, then point it to the current cursor.
320 } else {
321 mIndexer.setCursor(cursor);
322 }
323 }
324
325 // Ensure that the list is shown (and initial progress indicator
326 // hidden) in case this is the first cursor we have gotten.
327 makeListShown();
328 }
329
330 /**
331 * This method is called from a background thread by the list view
332 * when the user has typed a letter that should result in a filtering
333 * of the displayed items. It returns a Cursor, when will then be
334 * handed to changeCursor.
335 */
336 @Override
337 public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
338 if (DBG) Log.v(TAG, "Getting new cursor...");
339 return doQuery(true, constraint.toString());
340 }
341
342 public int getPositionForSection(int section) {
343 Cursor cursor = getCursor();
344 if (cursor == null) {
345 // No cursor, the section doesn't exist so just return 0
346 return 0;
347 }
348
349 return mIndexer.getPositionForSection(section);
350 }
351
352 public int getSectionForPosition(int position) {
353 return 0;
354 }
355
356 public Object[] getSections() {
357 if (mIndexer != null) {
358 return mIndexer.getSections();
359 }
360 return null;
361 }
362 }
363
364 /**
365 * This is our specialization of AsyncQueryHandler applies new cursors
366 * to our state as they become available.
367 */
368 private final class QueryHandler extends AsyncQueryHandler {
369 public QueryHandler(Context context) {
370 super(context.getContentResolver());
371 }
372
373 @Override
374 protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
375 if (!isFinishing()) {
376 // Update the adapter: we are no longer loading, and have
377 // a new cursor for it.
378 mAdapter.setLoading(false);
379 mAdapter.changeCursor(cursor);
380 setProgressBarIndeterminateVisibility(false);
381
382 // Now that the cursor is populated again, it's possible to restore the list state
383 if (mListState != null) {
384 getListView().onRestoreInstanceState(mListState);
385 if (mListHasFocus) {
386 getListView().requestFocus();
387 }
388 mListHasFocus = false;
389 mListState = null;
390 }
391 } else {
392 cursor.close();
393 }
394 }
395 }
396
397 /** Called when the activity is first created. */
398 @Override
399 public void onCreate(Bundle icicle) {
400 super.onCreate(icicle);
401
402 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
403
404 int sortMode = TRACK_MENU;
405 if (icicle == null) {
406 mSelectedUri =
407 getIntent().getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI);
408 } else {
409 mSelectedUri = (Uri) icicle.getParcelable(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI);
410 // Retrieve list state. This will be applied after the
411 // QueryHandler has run
412 mListState = icicle.getParcelable(LIST_STATE_KEY);
413 mListHasFocus = icicle.getBoolean(FOCUS_KEY);
414 sortMode = icicle.getInt(SORT_MODE_KEY, sortMode);
415 }
416 if (Intent.ACTION_GET_CONTENT.equals(getIntent().getAction())) {
417 mBaseUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
418 } else {
419 mBaseUri = getIntent().getData();
420 if (mBaseUri == null) {
421 Log.w("MusicPicker", "No data URI given to PICK action");
422 finish();
423 return;
424 }
425 }
426
427 setContentView(R.layout.music_picker);
428
429 mSortOrder = MediaStore.Audio.Media.TITLE_KEY;
430
431 final ListView listView = getListView();
432
433 listView.setItemsCanFocus(false);
434
435 mAdapter = new TrackListAdapter(
436 this, listView, R.layout.music_picker_item, new String[] {}, new int[] {});
437
438 setListAdapter(mAdapter);
439
440 listView.setTextFilterEnabled(true);
441
442 // We manually save/restore the listview state
443 listView.setSaveEnabled(false);
444
445 mQueryHandler = new QueryHandler(this);
446
447 mProgressContainer = findViewById(R.id.progressContainer);
448 mListContainer = findViewById(R.id.listContainer);
449
450 mOkayButton = findViewById(R.id.okayButton);
451 mOkayButton.setOnClickListener(this);
452 mCancelButton = findViewById(R.id.cancelButton);
453 mCancelButton.setOnClickListener(this);
454
455 // If there is a currently selected Uri, then try to determine who
456 // it is.
457 if (mSelectedUri != null) {
458 Uri.Builder builder = mSelectedUri.buildUpon();
459 String path = mSelectedUri.getEncodedPath();
460 int idx = path.lastIndexOf('/');
461 if (idx >= 0) {
462 path = path.substring(0, idx);
463 }
464 builder.encodedPath(path);
465 Uri baseSelectedUri = builder.build();
466 if (DBG) Log.v(TAG, "Selected Uri: " + mSelectedUri);
467 if (DBG) Log.v(TAG, "Selected base Uri: " + baseSelectedUri);
468 if (DBG) Log.v(TAG, "Base Uri: " + mBaseUri);
469 if (baseSelectedUri.equals(mBaseUri)) {
470 // If the base Uri of the selected Uri is the same as our
471 // content's base Uri, then use the selection!
472 mSelectedId = ContentUris.parseId(mSelectedUri);
473 }
474 }
475
476 setSortMode(sortMode);
477 }
478
479 @Override
480 public void onRestart() {
481 super.onRestart();
482 doQuery(false, null);
483 }
484
485 @Override
486 public boolean onOptionsItemSelected(MenuItem item) {
487 if (setSortMode(item.getItemId())) {
488 return true;
489 }
490 return super.onOptionsItemSelected(item);
491 }
492
493 @Override
494 public boolean onCreateOptionsMenu(Menu menu) {
495 super.onCreateOptionsMenu(menu);
496 menu.add(Menu.NONE, TRACK_MENU, Menu.NONE, R.string.sort_by_track);
497 menu.add(Menu.NONE, ALBUM_MENU, Menu.NONE, R.string.sort_by_album);
498 menu.add(Menu.NONE, ARTIST_MENU, Menu.NONE, R.string.sort_by_artist);
499 return true;
500 }
501
502 @Override
503 protected void onSaveInstanceState(Bundle icicle) {
504 super.onSaveInstanceState(icicle);
505 // Save list state in the bundle so we can restore it after the
506 // QueryHandler has run
507 icicle.putParcelable(LIST_STATE_KEY, getListView().onSaveInstanceState());
508 icicle.putBoolean(FOCUS_KEY, getListView().hasFocus());
509 icicle.putInt(SORT_MODE_KEY, mSortMode);
510 }
511
512 @Override
513 public void onPause() {
514 super.onPause();
515 stopMediaPlayer();
516 }
517
518 @Override
519 public void onStop() {
520 super.onStop();
521
522 // We don't want the list to display the empty state, since when we
523 // resume it will still be there and show up while the new query is
524 // happening. After the async query finishes in response to onResume()
525 // setLoading(false) will be called.
526 mAdapter.setLoading(true);
527 mAdapter.changeCursor(null);
528 }
529
530 /**
531 * Changes the current sort order, building the appropriate query string
532 * for the selected order.
533 */
534 boolean setSortMode(int sortMode) {
535 if (sortMode != mSortMode) {
536 switch (sortMode) {
537 case TRACK_MENU:
538 mSortMode = sortMode;
539 mSortOrder = MediaStore.Audio.Media.TITLE_KEY;
540 doQuery(false, null);
541 return true;
542 case ALBUM_MENU:
543 mSortMode = sortMode;
544 mSortOrder = MediaStore.Audio.Media.ALBUM_KEY + " ASC, "
545 + MediaStore.Audio.Media.TRACK + " ASC, "
546 + MediaStore.Audio.Media.TITLE_KEY + " ASC";
547 doQuery(false, null);
548 return true;
549 case ARTIST_MENU:
550 mSortMode = sortMode;
551 mSortOrder = MediaStore.Audio.Media.ARTIST_KEY + " ASC, "
552 + MediaStore.Audio.Media.ALBUM_KEY + " ASC, "
553 + MediaStore.Audio.Media.TRACK + " ASC, "
554 + MediaStore.Audio.Media.TITLE_KEY + " ASC";
555 doQuery(false, null);
556 return true;
557 }
558 }
559 return false;
560 }
561
562 /**
563 * The first time this is called, we hide the large progress indicator
564 * and show the list view, doing fade animations between them.
565 */
566 void makeListShown() {
567 if (!mListShown) {
568 mListShown = true;
569 mProgressContainer.startAnimation(
570 AnimationUtils.loadAnimation(this, android.R.anim.fade_out));
571 mProgressContainer.setVisibility(View.GONE);
572 mListContainer.startAnimation(
573 AnimationUtils.loadAnimation(this, android.R.anim.fade_in));
574 mListContainer.setVisibility(View.VISIBLE);
575 }
576 }
577
578 /**
579 * Common method for performing a query of the music database, called for
580 * both top-level queries and filtering.
581 *
582 * @param sync If true, this query should be done synchronously and the
583 * resulting cursor returned. If false, it will be done asynchronously and
584 * null returned.
585 * @param filterstring If non-null, this is a filter to apply to the query.
586 */
587 Cursor doQuery(boolean sync, String filterstring) {
588 // Cancel any pending queries
589 mQueryHandler.cancelOperation(MY_QUERY_TOKEN);
590
591 StringBuilder where = new StringBuilder();
592 where.append(MediaStore.Audio.Media.TITLE + " != ''");
593
594 // We want to show all audio files, even recordings. Enforcing the
595 // following condition would hide recordings.
596 // where.append(" AND " + MediaStore.Audio.Media.IS_MUSIC + "=1");
597
598 Uri uri = mBaseUri;
599 if (!TextUtils.isEmpty(filterstring)) {
600 uri = uri.buildUpon().appendQueryParameter("filter", Uri.encode(filterstring)).build();
601 }
602
603 if (sync) {
604 try {
605 return getContentResolver().query(
606 uri, CURSOR_COLS, where.toString(), null, mSortOrder);
607 } catch (UnsupportedOperationException ex) {
608 }
609 } else {
610 mAdapter.setLoading(true);
611 setProgressBarIndeterminateVisibility(true);
612 mQueryHandler.startQuery(
613 MY_QUERY_TOKEN, null, uri, CURSOR_COLS, where.toString(), null, mSortOrder);
614 }
615 return null;
616 }
617
618 @Override
619 protected void onListItemClick(ListView l, View v, int position, long id) {
620 mCursor.moveToPosition(position);
621 if (DBG)
622 Log.v(TAG,
623 "Click on " + position + " (id=" + id + ", cursid="
624 + mCursor.getLong(mCursor.getColumnIndex(MediaStore.Audio.Media._ID))
625 + ") in cursor " + mCursor + " adapter=" + l.getAdapter());
626 setSelected(mCursor);
627 }
628
629 void setSelected(Cursor c) {
630 Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
631 long newId = mCursor.getLong(mCursor.getColumnIndex(MediaStore.Audio.Media._ID));
632 mSelectedUri = ContentUris.withAppendedId(uri, newId);
633
634 mSelectedId = newId;
635 if (newId != mPlayingId || mMediaPlayer == null) {
636 stopMediaPlayer();
637 mMediaPlayer = new MediaPlayer();
638 try {
639 mMediaPlayer.setDataSource(this, mSelectedUri);
640 mMediaPlayer.setOnCompletionListener(this);
641 mMediaPlayer.setAudioStreamType(AudioManager.STREAM_RING);
642 mMediaPlayer.prepare();
643 mMediaPlayer.start();
644 mPlayingId = newId;
645 getListView().invalidateViews();
646 } catch (IOException e) {
647 Log.w("MusicPicker", "Unable to play track", e);
648 }
649 } else if (mMediaPlayer != null) {
650 stopMediaPlayer();
651 getListView().invalidateViews();
652 }
653 }
654
655 public void onCompletion(MediaPlayer mp) {
656 if (mMediaPlayer == mp) {
657 mp.stop();
658 mp.release();
659 mMediaPlayer = null;
660 mPlayingId = -1;
661 getListView().invalidateViews();
662 }
663 }
664
665 void stopMediaPlayer() {
666 if (mMediaPlayer != null) {
667 mMediaPlayer.stop();
668 mMediaPlayer.release();
669 mMediaPlayer = null;
670 mPlayingId = -1;
671 }
672 }
673
674 public void onClick(View v) {
675 switch (v.getId()) {
676 case R.id.okayButton:
677 if (mSelectedId >= 0) {
678 setResult(RESULT_OK, new Intent().setData(mSelectedUri));
679 finish();
680 }
681 break;
682
683 case R.id.cancelButton:
684 finish();
685 break;
686 }
687 }
688}