2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.music;
19 import java.util.Arrays;
21 import android.app.ActionBar;
22 import android.app.ListActivity;
23 import android.app.SearchManager;
24 import android.content.AsyncQueryHandler;
25 import android.content.BroadcastReceiver;
26 import android.content.ComponentName;
27 import android.content.ContentResolver;
28 import android.content.ContentUris;
29 import android.content.ContentValues;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.IntentFilter;
33 import android.content.ServiceConnection;
34 import android.content.SharedPreferences;
35 import android.content.pm.PackageManager;
36 import android.content.pm.PackageManager.NameNotFoundException;
37 import android.content.res.Configuration;
38 import android.content.res.Resources;
39 import android.database.AbstractCursor;
40 import android.database.CharArrayBuffer;
41 import android.database.Cursor;
42 import android.database.sqlite.SQLiteException;
43 import android.graphics.Bitmap;
44 import android.graphics.drawable.AnimationDrawable;
45 import android.media.AudioManager;
46 import android.net.Uri;
47 import android.os.Bundle;
48 import android.os.Handler;
49 import android.os.IBinder;
50 import android.os.Message;
51 import android.os.RemoteException;
52 import android.provider.BaseColumns;
53 import android.provider.MediaStore;
54 import android.provider.MediaStore.Audio.AudioColumns;
55 import android.provider.MediaStore.Audio.Playlists;
56 import android.text.TextUtils;
57 import android.util.Log;
58 import android.view.ContextMenu;
59 import android.view.ContextMenu.ContextMenuInfo;
60 import android.view.GestureDetector;
61 import android.view.GestureDetector.SimpleOnGestureListener;
62 import android.view.KeyEvent;
63 import android.view.Menu;
64 import android.view.MenuItem;
65 import android.view.MotionEvent;
66 import android.view.SubMenu;
67 import android.view.View;
68 import android.view.ViewGroup;
69 import android.widget.AdapterView.AdapterContextMenuInfo;
70 import android.widget.AlphabetIndexer;
71 import android.widget.FrameLayout;
72 import android.widget.ImageButton;
73 import android.widget.ImageView;
74 import android.widget.LinearLayout;
75 import android.widget.ListView;
76 import android.widget.ProgressBar;
77 import android.widget.RelativeLayout;
78 import android.widget.SectionIndexer;
79 import android.widget.SimpleCursorAdapter;
80 import android.widget.TabWidget;
81 import android.widget.TextView;
83 import com.android.music.MusicUtils.ServiceToken;
85 public class TrackBrowserActivity extends ListActivity implements
86 View.OnCreateContextMenuListener, MusicUtils.Defs, ServiceConnection {
87 private static final int Q_SELECTED = CHILD_MENU_BASE;
88 private static final int Q_ALL = CHILD_MENU_BASE + 1;
89 private static final int SAVE_AS_PLAYLIST = CHILD_MENU_BASE + 2;
90 private static final int PLAY_ALL = CHILD_MENU_BASE + 3;
91 private static final int CLEAR_PLAYLIST = CHILD_MENU_BASE + 4;
92 private static final int REMOVE = CHILD_MENU_BASE + 5;
93 private static final int SEARCH = CHILD_MENU_BASE + 6;
95 private static final String LOGTAG = "TrackBrowser";
97 private String[] mCursorCols;
98 private String[] mPlaylistMemberCols;
99 private boolean mDeletedOneRow = false;
100 private boolean mEditMode = false;
101 private String mCurrentTrackName;
102 private String mCurrentAlbumName;
103 private String mCurrentArtistNameForAlbum;
104 private ListView mTrackList;
105 private Cursor mTrackCursor;
106 private TrackListAdapter mAdapter;
107 private boolean mAdapterSent = false;
108 private String mAlbumId;
109 private String mArtistId;
110 private String mPlaylist;
111 private String mGenre;
112 private String mSortOrder;
113 private int mSelectedPosition;
114 private long mSelectedId;
115 private static int mLastListPosCourse = -1;
116 private static int mLastListPosFine = -1;
117 private boolean mUseLastListPos = false;
118 private ServiceToken mToken;
119 private ServiceToken mOSC;
120 private IMediaPlaybackService mService = null;
121 private SharedPreferences mPreferences;
123 public LinearLayout mSongTab;
124 public TabWidget mButtonBar;
125 public TextView mButtonBarArtist;
126 public TextView mButtonBarAlbum;
127 public TextView mButtonBarSong;
128 public TextView mButtonBarPlaylist;
129 public TextView mButtonBarNP;
130 public RelativeLayout mGroup;
131 public RelativeLayout mChild;
132 // Smaller now playing window buttons
133 private LinearLayout mNowPlaying;
134 private ImageView mInfoDivider;
135 private ProgressBar mProgress;
136 private ImageButton mDoSearch;
137 private ImageButton mMarket;
138 private ImageButton mNext;
139 private ImageButton mPlay;
140 private ImageButton mPlusPlaylist;
141 private ImageButton mPrev;
142 private ImageButton mShare;
143 private ImageButton mFlow;
144 // Back button long press
145 public String back_button_db;
146 // Smaller now playing window swipe gesture
147 public GestureDetector gestureDetector;
148 private static final int SWIPE_MIN_DISTANCE = 120;
149 private static final int SWIPE_MAX_OFF_PATH = 250;
150 private static final int SWIPE_THRESHOLD_VELOCITY = 200;
151 private String np_swipe_gesture_db;
153 private GestureDetector swipetabs;
154 // ADW Theme constants
155 public static final int THEME_ITEM_BACKGROUND = 0;
156 public static final int THEME_ITEM_FOREGROUND = 1;
157 public static final int THEME_ITEM_TEXT_DRAWABLE = 2;
159 public TrackBrowserActivity() {
162 /** Called when the activity is first created. */
164 public void onCreate(Bundle icicle) {
165 super.onCreate(icicle);
166 ActionBar bar = getActionBar();
168 bar.setTitle(mService.getArtistName());
169 bar.setBackgroundDrawable(getResources().getDrawable(
170 R.drawable.solid_dark_pressed));
171 } catch (Exception e) {
172 // TODO Auto-generated catch block
175 bar.setDisplayHomeAsUpEnabled(true);
177 mPreferences = getSharedPreferences(
178 MusicSettingsActivity.PREFERENCES_FILE, MODE_PRIVATE);
179 Intent intent = getIntent();
180 if (intent != null) {
181 if (intent.getBooleanExtra("withtabs", false)) {
185 setVolumeControlStream(AudioManager.STREAM_MUSIC);
186 if (icicle != null) {
187 mSelectedId = icicle.getLong("selectedtrack");
188 mAlbumId = icicle.getString("album");
189 mArtistId = icicle.getString("artist");
190 mPlaylist = icicle.getString("playlist");
191 mGenre = icicle.getString("genre");
192 mEditMode = icicle.getBoolean("editmode", false);
194 mAlbumId = intent.getStringExtra("album");
195 // If we have an album, show everything on the album, not just stuff
196 // by a particular artist.
197 mArtistId = intent.getStringExtra("artist");
198 mPlaylist = intent.getStringExtra("playlist");
199 mGenre = intent.getStringExtra("genre");
200 mEditMode = intent.getAction().equals(Intent.ACTION_EDIT);
203 mCursorCols = new String[] { MediaStore.Audio.Media._ID,
204 MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.DATA,
205 MediaStore.Audio.Media.ALBUM, MediaStore.Audio.Media.ARTIST,
206 MediaStore.Audio.Media.ARTIST_ID,
207 MediaStore.Audio.Media.DURATION };
208 mPlaylistMemberCols = new String[] {
209 MediaStore.Audio.Playlists.Members._ID,
210 MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.DATA,
211 MediaStore.Audio.Media.ALBUM, MediaStore.Audio.Media.ARTIST,
212 MediaStore.Audio.Media.ARTIST_ID,
213 MediaStore.Audio.Media.DURATION,
214 MediaStore.Audio.Playlists.Members.PLAY_ORDER,
215 MediaStore.Audio.Playlists.Members.AUDIO_ID,
216 MediaStore.Audio.Media.IS_MUSIC };
218 setContentView(R.layout.media_picker_activity);
220 mToken = MusicUtils.bindToService(this, this);
221 // We have to bind to osc to control the music, but we need to keep the
222 // previous line to load the list view.
223 mOSC = MusicUtils.bindToService(this, osc);
225 mUseLastListPos = MusicUtils.updateButtonBar(this, R.id.songtab);
226 mTrackList = getListView();
227 mTrackList.setOnCreateContextMenuListener(this);
228 mTrackList.setCacheColorHint(0);
230 LinearLayout nowPlaying = (LinearLayout) findViewById(R.id.nowplaying);
231 if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
232 nowPlaying.setVisibility(View.GONE);
234 ((TouchInterceptor) mTrackList).setDropListener(mDropListener);
235 ((TouchInterceptor) mTrackList).setRemoveListener(mRemoveListener);
236 mTrackList.setDivider(null);
237 mTrackList.setSelector(R.drawable.list_selector_background);
238 // ADW: Load the specified theme
239 String themePackage = MusicUtils.getThemePackageName(this,
240 MusicSettingsActivity.THEME_DEFAULT);
241 PackageManager pm = getPackageManager();
242 Resources themeResources = null;
243 if (!themePackage.equals(MusicSettingsActivity.THEME_DEFAULT)) {
246 .getResourcesForApplication(themePackage);
247 } catch (NameNotFoundException e) {
248 // ADW The saved theme was uninstalled so we save the
250 MusicUtils.setThemePackageName(this,
251 MusicSettingsActivity.THEME_DEFAULT);
254 // Set Views for themes
255 if (themeResources != null) {
256 int lS = themeResources.getIdentifier("queue_selector",
257 "drawable", themePackage);
259 mTrackList.setSelector(themeResources.getDrawable(lS));
263 mTrackList.setTextFilterEnabled(true);
265 mAdapter = (TrackListAdapter) getLastNonConfigurationInstance();
267 if (mAdapter != null) {
268 mAdapter.setActivity(this);
269 setListAdapter(mAdapter);
273 gestureDetector = new GestureDetector(new NowPlayingGesture());
274 View nowplayingview = (View) findViewById(R.id.nowplaying);
276 swipetabs = new GestureDetector(new TabSwipe());
277 View mainview = (View) findViewById(android.R.id.list);
279 // Set the touch listener for the main view to be our custom gesture
282 mainview.setOnTouchListener(new View.OnTouchListener() {
283 public boolean onTouch(View v, MotionEvent event) {
284 if (swipetabs.onTouchEvent(event)) {
293 // Smaller now playing window buttons
294 mShare = (ImageButton) findViewById(R.id.share_song);
295 mPlay = (ImageButton) findViewById(R.id.media_play);
296 mNext = (ImageButton) findViewById(R.id.media_next);
297 mPrev = (ImageButton) findViewById(R.id.media_prev);
298 mMarket = (ImageButton) findViewById(R.id.market_music);
299 mDoSearch = (ImageButton) findViewById(R.id.doSearch);
300 mPlusPlaylist = (ImageButton) findViewById(R.id.plus_playlist);
301 mFlow = (ImageButton) findViewById(R.id.overFlow);
302 // I found that without setting the onClickListeners in Portrait mode
303 // only, flipping into Landscape force closes.
304 if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
305 // Smaller now playing window actions
306 mDoSearch.setOnClickListener(mDoSearchListener);
307 mMarket.setOnClickListener(mMarketSearch);
308 mPlay.setOnClickListener(mMediaPlay);
309 mNext.setOnClickListener(mMediaNext);
310 mPrev.setOnClickListener(mMediaPrev);
311 mShare.setOnClickListener(mShareSong);
312 mFlow.setOnClickListener(mOverFlow);
313 // I only want this to show in the Playlist activity
314 mPlusPlaylist.setVisibility(View.GONE);
316 nowplayingview.setOnTouchListener(new View.OnTouchListener() {
317 public boolean onTouch(View vee, MotionEvent nowplayingevent) {
318 if (gestureDetector.onTouchEvent(nowplayingevent)) {
325 // ADW: Load the specified theme
326 String themePackage = MusicUtils.getThemePackageName(this,
327 MusicSettingsActivity.THEME_DEFAULT);
328 PackageManager pm = getPackageManager();
329 Resources themeResources = null;
330 if (!themePackage.equals(MusicSettingsActivity.THEME_DEFAULT)) {
332 themeResources = pm.getResourcesForApplication(themePackage);
333 } catch (NameNotFoundException e) {
334 // ADW The saved theme was uninstalled so we save the
336 MusicUtils.setThemePackageName(this,
337 MusicSettingsActivity.THEME_DEFAULT);
340 // Set Views for themes
341 if (themeResources != null) {
343 mSongTab = (LinearLayout) findViewById(R.id.album_tab);
344 mButtonBar = (TabWidget) findViewById(R.id.buttonbar);
345 mButtonBarArtist = (TextView) findViewById(R.id.artisttab);
346 mButtonBarAlbum = (TextView) findViewById(R.id.albumtab);
347 mButtonBarSong = (TextView) findViewById(R.id.songtab);
348 mButtonBarPlaylist = (TextView) findViewById(R.id.playlisttab);
349 mButtonBarNP = (TextView) findViewById(R.id.nowplayingtab);
350 ArtistAlbumBrowserActivity.loadThemeResource(themeResources,
351 themePackage, "tab_song", mSongTab, THEME_ITEM_BACKGROUND);
352 ArtistAlbumBrowserActivity.loadThemeResource(themeResources,
353 themePackage, "buttonbar", mButtonBar,
354 THEME_ITEM_BACKGROUND);
355 ArtistAlbumBrowserActivity.loadThemeResource(themeResources,
356 themePackage, "tab_bg_artist", mButtonBarArtist,
357 THEME_ITEM_BACKGROUND);
358 ArtistAlbumBrowserActivity.loadThemeResource(themeResources,
359 themePackage, "tab_bg_album", mButtonBarAlbum,
360 THEME_ITEM_BACKGROUND);
361 ArtistAlbumBrowserActivity.loadThemeResource(themeResources,
362 themePackage, "tab_bg_song", mButtonBarSong,
363 THEME_ITEM_BACKGROUND);
364 ArtistAlbumBrowserActivity.loadThemeResource(themeResources,
365 themePackage, "tab_bg_playlist", mButtonBarPlaylist,
366 THEME_ITEM_BACKGROUND);
367 ArtistAlbumBrowserActivity.loadThemeResource(themeResources,
368 themePackage, "tab_bg_nowplaying", mButtonBarNP,
369 THEME_ITEM_BACKGROUND);
370 // Small now playing window
371 mNowPlaying = (LinearLayout) findViewById(R.id.nowplaying);
372 mInfoDivider = (ImageView) findViewById(R.id.info_divider);
373 mProgress = (ProgressBar) findViewById(R.id.progress);
374 mShare = (ImageButton) findViewById(R.id.share_song);
375 mPlay = (ImageButton) findViewById(R.id.media_play);
376 mNext = (ImageButton) findViewById(R.id.media_next);
377 mPrev = (ImageButton) findViewById(R.id.media_prev);
378 mMarket = (ImageButton) findViewById(R.id.market_music);
379 mDoSearch = (ImageButton) findViewById(R.id.doSearch);
380 mFlow = (ImageButton) findViewById(R.id.overFlow);
381 if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
382 ArtistAlbumBrowserActivity.loadThemeResource(themeResources,
383 themePackage, "snp_background", mNowPlaying,
384 THEME_ITEM_BACKGROUND);
385 ArtistAlbumBrowserActivity.loadThemeResource(themeResources,
386 themePackage, "snp_progress", mProgress,
387 THEME_ITEM_BACKGROUND);
388 ArtistAlbumBrowserActivity.loadThemeResource(themeResources,
389 themePackage, "snp_info_divider", mInfoDivider,
390 THEME_ITEM_BACKGROUND);
391 ArtistAlbumBrowserActivity.loadThemeResource(themeResources,
392 themePackage, "snp_share", mShare,
393 THEME_ITEM_FOREGROUND);
394 ArtistAlbumBrowserActivity.loadThemeResource(themeResources,
395 themePackage, "snp_play", mPlay, THEME_ITEM_FOREGROUND);
396 ArtistAlbumBrowserActivity.loadThemeResource(themeResources,
397 themePackage, "snp_next", mNext, THEME_ITEM_FOREGROUND);
398 ArtistAlbumBrowserActivity.loadThemeResource(themeResources,
399 themePackage, "snp_prev", mPrev, THEME_ITEM_FOREGROUND);
400 ArtistAlbumBrowserActivity.loadThemeResource(themeResources,
401 themePackage, "snp_market", mMarket,
402 THEME_ITEM_FOREGROUND);
403 ArtistAlbumBrowserActivity.loadThemeResource(themeResources,
404 themePackage, "snp_search", mDoSearch,
405 THEME_ITEM_FOREGROUND);
406 ArtistAlbumBrowserActivity.loadThemeResource(themeResources,
407 themePackage, "snp_flow", mFlow, THEME_ITEM_FOREGROUND);
410 // don't set the album art until after the view has been layed out
411 mTrackList.post(new Runnable() {
414 setAlbumArtBackground();
419 public void onServiceConnected(ComponentName name, IBinder service) {
420 IntentFilter f = new IntentFilter();
421 f.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
422 f.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
423 f.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
424 f.addDataScheme("file");
425 registerReceiver(mScanListener, f);
427 if (mAdapter == null) {
428 // Log.i("@@@", "starting query");
429 mAdapter = new TrackListAdapter(
430 getApplication(), // need to use application context to
433 mEditMode ? R.layout.edit_track_list_item
434 : R.layout.track_list_item,
436 new String[] {}, new int[] {},
437 "nowplaying".equals(mPlaylist),
439 && !(mPlaylist.equals("podcasts") || mPlaylist
440 .equals("recentlyadded")));
441 setListAdapter(mAdapter);
442 setTitle(R.string.working_songs);
443 getTrackCursor(mAdapter.getQueryHandler(), null, true);
445 mTrackCursor = mAdapter.getCursor();
446 // If mTrackCursor is null, this can be because it doesn't have
447 // a cursor yet (because the initial query that sets its cursor
448 // is still in progress), or because the query failed.
449 // In order to not flash the error dialog at the user for the
450 // first case, simply retry the query when the cursor is null.
451 // Worst case, we end up doing the same query twice.
452 if (mTrackCursor != null) {
453 init(mTrackCursor, false);
455 setTitle(R.string.working_songs);
456 getTrackCursor(mAdapter.getQueryHandler(), null, true);
460 MusicUtils.updateNowPlaying(this);
464 public void onServiceDisconnected(ComponentName name) {
465 // we can't really function without the service, so don't
470 public Object onRetainNonConfigurationInstance() {
471 TrackListAdapter a = mAdapter;
477 class TabSwipe extends SimpleOnGestureListener {
479 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
481 Intent intent = new Intent(Intent.ACTION_PICK);
484 if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH)
486 if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE
487 && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
488 intent.setDataAndType(Uri.EMPTY,
489 MediaStore.Audio.Playlists.CONTENT_TYPE);
490 intent.putExtra("withtabs", true);
491 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
492 startActivity(intent);
494 TrackBrowserActivity.this.overridePendingTransition(
495 R.anim.slide_in_right, R.anim.slide_out_left);
497 } else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE
498 && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
499 intent.setDataAndType(Uri.EMPTY,
500 "vnd.android.cursor.dir/album");
501 intent.putExtra("withtabs", true);
502 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
503 startActivity(intent);
505 TrackBrowserActivity.this.overridePendingTransition(
506 R.anim.slide_in_left, R.anim.slide_out_right);
511 } catch (Exception e) {
512 Log.e("Fling", "There was an error processing the Fling event:"
518 // It is necessary to return true from onDown for the onFling event to
521 public boolean onDown(MotionEvent e) {
527 class NowPlayingGesture extends SimpleOnGestureListener {
529 public boolean onFling(MotionEvent e3, MotionEvent e4, float vX,
531 np_swipe_gesture_db = mPreferences.getString("np_swipe_gesture",
533 if (np_swipe_gesture_db.equals("0")) {
534 if (e3.getY() - e4.getY() > SWIPE_MIN_DISTANCE
535 && Math.abs(vY) > SWIPE_THRESHOLD_VELOCITY) {
540 np_swipe_gesture_db = mPreferences.getString("np_swipe_gesture",
542 if (np_swipe_gesture_db.equals("1")) {
543 if (e3.getY() - e4.getY() > SWIPE_MIN_DISTANCE
544 && Math.abs(vY) > SWIPE_THRESHOLD_VELOCITY) {
549 np_swipe_gesture_db = mPreferences.getString("np_swipe_gesture",
551 if (np_swipe_gesture_db.equals("2")) {
552 if (e3.getY() - e4.getY() > SWIPE_MIN_DISTANCE
553 && Math.abs(vY) > SWIPE_THRESHOLD_VELOCITY) {
557 np_swipe_gesture_db = mPreferences.getString("np_swipe_gesture",
559 if (np_swipe_gesture_db.equals("3")) {
560 if (e3.getY() - e4.getY() > SWIPE_MIN_DISTANCE
561 && Math.abs(vY) > SWIPE_THRESHOLD_VELOCITY) {
570 public boolean onDown(MotionEvent f) {
575 // Smaller now playing window OnClickListerners
576 private View.OnClickListener mMarketSearch = new View.OnClickListener() {
577 public void onClick(View v) {
578 Uri marketUri = null;
580 marketUri = Uri.parse("market://search?q= "
581 + mService.getArtistName());
582 } catch (RemoteException e) {
583 // TODO Auto-generated catch block
586 Intent marketIntent = new Intent(Intent.ACTION_VIEW)
588 startActivity(marketIntent);
593 private View.OnClickListener mShareSong = new View.OnClickListener() {
596 public void onClick(View v) {
597 // TODO Auto-generated method stub
600 } catch (RemoteException e) {
601 // TODO Auto-generated catch block
609 private String shareCurrentTrack() throws RemoteException {
610 if (mService.getTrackName() == null || mService.getArtistName() == null) {
614 Intent shareIntent = new Intent();
615 String currentTrackMessage = "Now listening to: "
616 + mService.getTrackName() + " by " + mService.getArtistName();
618 shareIntent.setAction(Intent.ACTION_SEND);
619 shareIntent.setType("text/plain");
620 shareIntent.putExtra(Intent.EXTRA_TEXT, currentTrackMessage);
622 startActivity(Intent.createChooser(shareIntent, "Share track using"));
623 return currentTrackMessage;
626 private View.OnClickListener mOverFlow = new View.OnClickListener() {
627 public void onClick(View v) {
631 private View.OnClickListener mDoSearchListener = new View.OnClickListener() {
632 public void onClick(View v) {
636 private View.OnClickListener mMediaPlay = new View.OnClickListener() {
637 public void onClick(View v) {
641 private View.OnClickListener mMediaNext = new View.OnClickListener() {
642 public void onClick(View v) {
646 private View.OnClickListener mMediaPrev = new View.OnClickListener() {
647 public void onClick(View v) {
653 public void onDestroy() {
654 ListView lv = getListView();
656 if (mUseLastListPos) {
657 mLastListPosCourse = lv.getFirstVisiblePosition();
658 View cv = lv.getChildAt(0);
660 mLastListPosFine = cv.getTop();
664 // clear the listeners so we won't get any more callbacks
665 ((TouchInterceptor) lv).setDropListener(null);
666 ((TouchInterceptor) lv).setRemoveListener(null);
670 MusicUtils.unbindFromService(mToken);
671 MusicUtils.unbindFromService(mOSC);
673 if ("nowplaying".equals(mPlaylist)) {
674 unregisterReceiverSafe(mNowPlayingListener);
676 unregisterReceiverSafe(mTrackListListener);
678 } catch (IllegalArgumentException ex) {
679 // we end up here in case we never registered the listeners
682 // If we have an adapter and didn't send it off to another activity yet,
684 // close its cursor, which we do by assigning a null cursor to it. Doing
686 // instead of closing the cursor directly keeps the framework from
688 // the closed cursor later.
689 if (!mAdapterSent && mAdapter != null) {
690 mAdapter.changeCursor(null);
692 // Because we pass the adapter to the next activity, we need to make
693 // sure it doesn't keep a reference to this activity. We can do this
694 // by clearing its DatasetObservers, which setListAdapter(null) does.
695 setListAdapter(null);
697 unregisterReceiverSafe(mScanListener);
702 * Unregister a receiver, but eat the exception that is thrown if the
703 * receiver was never registered to begin with. This is a little easier than
704 * keeping track of whether the receivers have actually been registered by
705 * the time onDestroy() is called.
707 private void unregisterReceiverSafe(BroadcastReceiver receiver) {
709 unregisterReceiver(receiver);
710 } catch (IllegalArgumentException e) {
715 private void startPlayback() {
717 if (mService == null)
719 Intent intent = getIntent();
720 String filename = "";
721 Uri uri = intent.getData();
722 if (uri != null && uri.toString().length() > 0) {
723 // If this is a file:// URI, just use the path directly instead
724 // of going through the open-from-filedescriptor codepath.
725 String scheme = uri.getScheme();
726 if ("file".equals(scheme)) {
727 filename = uri.getPath();
729 filename = uri.toString();
734 mService.openFile(filename);
736 setIntent(new Intent());
737 } catch (Exception ex) {
738 Log.d("MediaPlaybackActivity", "couldn't start playback: " + ex);
743 private ServiceConnection osc = new ServiceConnection() {
744 public void onServiceConnected(ComponentName classname, IBinder obj) {
745 mService = IMediaPlaybackService.Stub.asInterface(obj);
747 // This is for orientation changes or entering the tab from a
748 // different activity
749 if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
750 setPauseButtonImage();
751 // This is so the controls will load correctly according to the
753 MusicUtils.updateNowPlaying(TrackBrowserActivity.this);
756 if (mService.getAudioId() >= 0 || mService.isPlaying()
757 || mService.getPath() != null) {
760 } catch (RemoteException ex) {
762 if (getIntent().getData() == null) {
763 Intent intent = new Intent(Intent.ACTION_MAIN);
764 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
765 intent.setClass(TrackBrowserActivity.this,
766 MusicBrowserActivity.class);
767 startActivity(intent);
771 public void onServiceDisconnected(ComponentName classname) {
777 public void onResume() {
779 if (mTrackCursor != null) {
780 getListView().invalidateViews();
782 MusicUtils.setSpinnerState(this);
786 public void onPause() {
787 mReScanHandler.removeCallbacksAndMessages(null);
792 * This listener gets called when the media scanner starts up or finishes,
793 * and when the sd card is unmounted.
795 private BroadcastReceiver mScanListener = new BroadcastReceiver() {
797 public void onReceive(Context context, Intent intent) {
798 String action = intent.getAction();
799 if (Intent.ACTION_MEDIA_SCANNER_STARTED.equals(action)
800 || Intent.ACTION_MEDIA_SCANNER_FINISHED.equals(action)) {
801 MusicUtils.setSpinnerState(TrackBrowserActivity.this);
803 mReScanHandler.sendEmptyMessage(0);
807 private Handler mReScanHandler = new Handler() {
809 public void handleMessage(Message msg) {
810 if (mAdapter != null) {
811 getTrackCursor(mAdapter.getQueryHandler(), null, true);
813 // if the query results in a null cursor, onQueryComplete() will
814 // call init(), which will post a delayed message to this handler
815 // in order to try again.
819 public void onSaveInstanceState(Bundle outcicle) {
820 // need to store the selected item so we don't lose it in case
821 // of an orientation switch. Otherwise we could lose it while
822 // in the middle of specifying a playlist to add the item to.
823 outcicle.putLong("selectedtrack", mSelectedId);
824 outcicle.putString("artist", mArtistId);
825 outcicle.putString("album", mAlbumId);
826 outcicle.putString("playlist", mPlaylist);
827 outcicle.putString("genre", mGenre);
828 outcicle.putBoolean("editmode", mEditMode);
829 super.onSaveInstanceState(outcicle);
832 public void init(Cursor newCursor, boolean isLimited) {
834 if (mAdapter == null) {
837 mAdapter.changeCursor(newCursor); // also sets mTrackCursor
839 if (mTrackCursor == null) {
840 MusicUtils.displayDatabaseError(this);
842 mReScanHandler.sendEmptyMessageDelayed(0, 1000);
846 MusicUtils.hideDatabaseError(this);
847 mUseLastListPos = MusicUtils.updateButtonBar(this, R.id.songtab);
850 // Restore previous position
851 if (mLastListPosCourse >= 0 && mUseLastListPos) {
852 ListView lv = getListView();
853 // this hack is needed because otherwise the position doesn't change
854 // for the 2nd (non-limited) cursor
855 lv.setAdapter(lv.getAdapter());
856 lv.setSelectionFromTop(mLastListPosCourse, mLastListPosFine);
858 mLastListPosCourse = -1;
862 // When showing the queue, position the selection on the currently
864 // Otherwise, position the selection on the first matching artist, if
866 IntentFilter f = new IntentFilter();
867 f.addAction(MediaPlaybackService.META_CHANGED);
868 f.addAction(MediaPlaybackService.QUEUE_CHANGED);
869 f.addAction(MediaPlaybackService.PLAYSTATE_CHANGED);
870 f.addAction(MediaPlaybackService.PROGRESSBAR_CHANGED);
871 if ("nowplaying".equals(mPlaylist)) {
873 int cur = MusicUtils.sService.getQueuePosition();
875 registerReceiver(mNowPlayingListener, new IntentFilter(f));
876 mNowPlayingListener.onReceive(this, new Intent(
877 MediaPlaybackService.META_CHANGED));
878 } catch (RemoteException ex) {
881 String key = getIntent().getStringExtra("artist");
883 int keyidx = mTrackCursor
884 .getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST_ID);
885 mTrackCursor.moveToFirst();
886 while (!mTrackCursor.isAfterLast()) {
887 String artist = mTrackCursor.getString(keyidx);
888 if (artist.equals(key)) {
889 setSelection(mTrackCursor.getPosition());
892 mTrackCursor.moveToNext();
895 registerReceiver(mTrackListListener, new IntentFilter(f));
896 mTrackListListener.onReceive(this, new Intent(
897 MediaPlaybackService.META_CHANGED));
901 private void setAlbumArtBackground() {
904 long albumid = Long.valueOf(mAlbumId);
905 Bitmap bm = MusicUtils.getArtwork(TrackBrowserActivity.this,
908 MusicUtils.setBackground(mTrackList, bm);
909 mTrackList.setCacheColorHint(0);
912 } catch (Exception ex) {
915 mTrackList.setCacheColorHint(0);
918 private void setTitle() {
920 CharSequence fancyName = null;
921 if (mAlbumId != null) {
922 int numresults = mTrackCursor != null ? mTrackCursor.getCount() : 0;
923 if (numresults > 0) {
924 mTrackCursor.moveToFirst();
925 int idx = mTrackCursor
926 .getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM);
927 fancyName = mTrackCursor.getString(idx);
928 // For compilation albums show only the album title,
929 // but for regular albums show "artist - album".
930 // To determine whether something is a compilation
931 // album, do a query for the artist + album of the
932 // first item, and see if it returns the same number
933 // of results as the album query.
934 String where = MediaStore.Audio.Media.ALBUM_ID
938 + MediaStore.Audio.Media.ARTIST_ID
941 .getLong(mTrackCursor
942 .getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST_ID));
943 Cursor cursor = MusicUtils.query(this,
944 MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
945 new String[] { MediaStore.Audio.Media.ALBUM }, where,
947 if (cursor != null) {
948 if (cursor.getCount() != numresults) {
950 fancyName = mTrackCursor.getString(idx);
954 if (fancyName == null
955 || fancyName.equals(MediaStore.UNKNOWN_STRING)) {
956 fancyName = getString(R.string.unknown_album_name);
959 } else if (mPlaylist != null) {
960 if (mPlaylist.equals("nowplaying")) {
961 if (MusicUtils.getCurrentShuffleMode() == MediaPlaybackService.SHUFFLE_AUTO) {
962 fancyName = getText(R.string.party_shuffle);
964 fancyName = getText(R.string.nowplaying_title);
966 } else if (mPlaylist.equals("podcasts")) {
967 fancyName = getText(R.string.podcasts_title);
968 } else if (mPlaylist.equals("recentlyadded")) {
969 fancyName = getText(R.string.recentlyadded_title);
971 String[] cols = new String[] { MediaStore.Audio.Playlists.NAME };
972 Cursor cursor = MusicUtils.query(this, ContentUris
973 .withAppendedId(Playlists.EXTERNAL_CONTENT_URI,
974 Long.valueOf(mPlaylist)), cols, null, null,
976 if (cursor != null) {
977 if (cursor.getCount() != 0) {
978 cursor.moveToFirst();
979 fancyName = cursor.getString(0);
984 } else if (mGenre != null) {
985 String[] cols = new String[] { MediaStore.Audio.Genres.NAME };
986 Cursor cursor = MusicUtils.query(this, ContentUris.withAppendedId(
987 MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI,
988 Long.valueOf(mGenre)), cols, null, null, null);
989 if (cursor != null) {
990 if (cursor.getCount() != 0) {
991 cursor.moveToFirst();
992 fancyName = cursor.getString(0);
998 if (fancyName != null) {
1003 setTitle(mService.getArtistName());
1004 } catch (Exception e) {
1005 // TODO Auto-generated catch block
1006 e.printStackTrace();
1011 private TouchInterceptor.DropListener mDropListener = new TouchInterceptor.DropListener() {
1012 public void drop(int from, int to) {
1013 if (mTrackCursor instanceof NowPlayingCursor) {
1014 // update the currently playing list
1015 NowPlayingCursor c = (NowPlayingCursor) mTrackCursor;
1016 c.moveItem(from, to);
1017 ((TrackListAdapter) getListAdapter()).notifyDataSetChanged();
1018 getListView().invalidateViews();
1019 mDeletedOneRow = true;
1021 // update a saved playlist
1022 Uri baseUri = MediaStore.Audio.Playlists.Members.getContentUri(
1023 "external", Long.valueOf(mPlaylist));
1024 ContentValues values = new ContentValues();
1025 String where = MediaStore.Audio.Playlists.Members._ID + "=?";
1026 String[] wherearg = new String[1];
1027 ContentResolver res = getContentResolver();
1028 int colidx = mTrackCursor
1029 .getColumnIndexOrThrow(MediaStore.Audio.Playlists.Members.PLAY_ORDER);
1031 // move the item to somewhere later in the list
1032 mTrackCursor.moveToPosition(to);
1033 long toidx = mTrackCursor.getLong(colidx);
1034 mTrackCursor.moveToPosition(from);
1035 values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER,
1037 wherearg[0] = mTrackCursor.getString(0);
1038 res.update(baseUri, values, where, wherearg);
1039 for (int i = from + 1; i <= to; i++) {
1040 mTrackCursor.moveToPosition(i);
1042 MediaStore.Audio.Playlists.Members.PLAY_ORDER,
1044 wherearg[0] = mTrackCursor.getString(0);
1045 res.update(baseUri, values, where, wherearg);
1047 } else if (from > to) {
1048 // move the item to somewhere earlier in the list
1049 mTrackCursor.moveToPosition(to);
1050 long toidx = mTrackCursor.getLong(colidx);
1051 mTrackCursor.moveToPosition(from);
1052 values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER,
1054 wherearg[0] = mTrackCursor.getString(0);
1055 res.update(baseUri, values, where, wherearg);
1056 for (int i = from - 1; i >= to; i--) {
1057 mTrackCursor.moveToPosition(i);
1059 MediaStore.Audio.Playlists.Members.PLAY_ORDER,
1061 wherearg[0] = mTrackCursor.getString(0);
1062 res.update(baseUri, values, where, wherearg);
1070 private TouchInterceptor.RemoveListener mRemoveListener = new TouchInterceptor.RemoveListener() {
1071 public void remove(int which) {
1072 removePlaylistItem(which);
1076 private void removePlaylistItem(int which) {
1077 View v = mTrackList.getChildAt(which
1078 - mTrackList.getFirstVisiblePosition());
1080 Log.d(LOGTAG, "No view when removing playlist item " + which);
1084 if (MusicUtils.sService != null
1085 && which != MusicUtils.sService.getQueuePosition()) {
1086 mDeletedOneRow = true;
1088 } catch (RemoteException e) {
1089 // Service died, so nothing playing.
1090 mDeletedOneRow = true;
1092 v.setVisibility(View.GONE);
1093 mTrackList.invalidateViews();
1094 if (mTrackCursor instanceof NowPlayingCursor) {
1095 ((NowPlayingCursor) mTrackCursor).removeItem(which);
1097 int colidx = mTrackCursor
1098 .getColumnIndexOrThrow(MediaStore.Audio.Playlists.Members._ID);
1099 mTrackCursor.moveToPosition(which);
1100 long id = mTrackCursor.getLong(colidx);
1101 Uri uri = MediaStore.Audio.Playlists.Members.getContentUri(
1102 "external", Long.valueOf(mPlaylist));
1103 getContentResolver().delete(ContentUris.withAppendedId(uri, id),
1106 v.setVisibility(View.VISIBLE);
1107 mTrackList.invalidateViews();
1110 private BroadcastReceiver mTrackListListener = new BroadcastReceiver() {
1112 public void onReceive(Context context, Intent intent) {
1113 String action = intent.getAction();
1114 getListView().invalidateViews();
1115 if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
1117 if (action.equals(MediaPlaybackService.PLAYSTATE_CHANGED)) {
1118 setPauseButtonImage();
1121 MusicUtils.updateNowPlaying(TrackBrowserActivity.this);
1127 private BroadcastReceiver mNowPlayingListener = new BroadcastReceiver() {
1129 public void onReceive(Context context, Intent intent) {
1130 if (intent.getAction().equals(MediaPlaybackService.META_CHANGED)) {
1131 getListView().invalidateViews();
1132 } else if (intent.getAction().equals(
1133 MediaPlaybackService.QUEUE_CHANGED)) {
1134 if (mDeletedOneRow) {
1135 // This is the notification for a single row that was
1136 // deleted previously, which is already reflected in
1138 mDeletedOneRow = false;
1141 // The service could disappear while the broadcast was in
1143 // so check to see if it's still valid
1144 if (MusicUtils.sService == null) {
1148 if (mAdapter != null) {
1149 Cursor c = new NowPlayingCursor(MusicUtils.sService,
1151 if (c.getCount() == 0) {
1155 mAdapter.changeCursor(c);
1161 // Cursor should be positioned on the entry to be checked
1162 // Returns false if the entry matches the naming pattern used for
1164 // or if it is marked as not music in the database.
1165 private boolean isMusic(Cursor c) {
1166 int titleidx = c.getColumnIndex(MediaStore.Audio.Media.TITLE);
1167 int albumidx = c.getColumnIndex(MediaStore.Audio.Media.ALBUM);
1168 int artistidx = c.getColumnIndex(MediaStore.Audio.Media.ARTIST);
1170 String title = c.getString(titleidx);
1171 String album = c.getString(albumidx);
1172 String artist = c.getString(artistidx);
1173 if (MediaStore.UNKNOWN_STRING.equals(album)
1174 && MediaStore.UNKNOWN_STRING.equals(artist) && title != null
1175 && title.startsWith("recording")) {
1180 int ismusic_idx = c.getColumnIndex(MediaStore.Audio.Media.IS_MUSIC);
1181 boolean ismusic = true;
1182 if (ismusic_idx >= 0) {
1183 ismusic = mTrackCursor.getInt(ismusic_idx) != 0;
1189 public void onCreateContextMenu(ContextMenu menu, View view,
1190 ContextMenuInfo menuInfoIn) {
1191 menu.add(0, PLAY_SELECTION, 0, R.string.play_selection);
1192 SubMenu sub = menu.addSubMenu(0, ADD_TO_PLAYLIST, 0,
1193 R.string.add_to_playlist);
1194 MusicUtils.makePlaylistMenu(this, sub);
1196 menu.add(0, REMOVE, 0, R.string.remove_from_playlist);
1198 menu.add(0, USE_AS_RINGTONE, 0, R.string.ringtone_menu);
1199 menu.add(0, DELETE_ITEM, 0, R.string.delete_item);
1200 AdapterContextMenuInfo mi = (AdapterContextMenuInfo) menuInfoIn;
1201 mSelectedPosition = mi.position;
1202 mTrackCursor.moveToPosition(mSelectedPosition);
1204 int id_idx = mTrackCursor
1205 .getColumnIndexOrThrow(MediaStore.Audio.Playlists.Members.AUDIO_ID);
1206 mSelectedId = mTrackCursor.getLong(id_idx);
1207 } catch (IllegalArgumentException ex) {
1208 mSelectedId = mi.id;
1210 // only add the 'search' menu if the selected item is music
1211 if (isMusic(mTrackCursor)) {
1212 menu.add(0, SEARCH, 0, R.string.search_title);
1214 mCurrentAlbumName = mTrackCursor.getString(mTrackCursor
1215 .getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM));
1216 mCurrentArtistNameForAlbum = mTrackCursor.getString(mTrackCursor
1217 .getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST));
1218 mCurrentTrackName = mTrackCursor.getString(mTrackCursor
1219 .getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));
1220 menu.setHeaderTitle(mCurrentTrackName);
1224 public boolean onContextItemSelected(MenuItem item) {
1225 switch (item.getItemId()) {
1226 case PLAY_SELECTION: {
1228 int position = mSelectedPosition;
1229 MusicUtils.playAll(this, mTrackCursor, position);
1234 long[] list = new long[] { mSelectedId };
1235 MusicUtils.addToCurrentPlaylist(this, list);
1239 case NEW_PLAYLIST: {
1240 Intent intent = new Intent();
1241 intent.setClass(this, CreatePlaylist.class);
1242 startActivityForResult(intent, NEW_PLAYLIST);
1246 case PLAYLIST_SELECTED: {
1247 long[] list = new long[] { mSelectedId };
1248 long playlist = item.getIntent().getLongExtra("playlist", 0);
1249 MusicUtils.addToPlaylist(this, list, playlist);
1253 case USE_AS_RINGTONE:
1254 // Set the system setting to make this the current ringtone
1255 MusicUtils.setRingtone(this, mSelectedId);
1259 long[] list = new long[1];
1260 list[0] = (int) mSelectedId;
1261 Bundle b = new Bundle();
1263 if (android.os.Environment.isExternalStorageRemovable()) {
1264 f = getString(R.string.delete_song_desc);
1266 f = getString(R.string.delete_song_desc_nosdcard);
1268 String desc = String.format(f, mCurrentTrackName);
1269 b.putString("description", desc);
1270 b.putLongArray("items", list);
1271 Intent intent = new Intent();
1272 intent.setClass(this, DeleteItems.class);
1273 intent.putExtras(b);
1274 startActivityForResult(intent, -1);
1279 removePlaylistItem(mSelectedPosition);
1286 return super.onContextItemSelected(item);
1290 CharSequence title = null;
1291 String query = null;
1293 Intent i = new Intent();
1294 i.setAction(MediaStore.INTENT_ACTION_MEDIA_SEARCH);
1295 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1297 title = mCurrentTrackName;
1298 if (MediaStore.UNKNOWN_STRING.equals(mCurrentArtistNameForAlbum)) {
1299 query = mCurrentTrackName;
1301 query = mCurrentArtistNameForAlbum + " " + mCurrentTrackName;
1302 i.putExtra(MediaStore.EXTRA_MEDIA_ARTIST,
1303 mCurrentArtistNameForAlbum);
1305 if (MediaStore.UNKNOWN_STRING.equals(mCurrentAlbumName)) {
1306 i.putExtra(MediaStore.EXTRA_MEDIA_ALBUM, mCurrentAlbumName);
1308 i.putExtra(MediaStore.EXTRA_MEDIA_FOCUS, "audio/*");
1309 title = getString(R.string.mediasearch, title);
1310 i.putExtra(SearchManager.QUERY, query);
1312 startActivity(Intent.createChooser(i, title));
1315 // In order to use alt-up/down as a shortcut for moving the selected item
1316 // in the list, we need to override dispatchKeyEvent, not onKeyDown.
1317 // (onKeyDown never sees these events, since they are handled by the list)
1319 public boolean dispatchKeyEvent(KeyEvent event) {
1320 if (mPlaylist != null && event.getMetaState() != 0
1321 && event.getAction() == KeyEvent.ACTION_DOWN) {
1322 switch (event.getKeyCode()) {
1323 case KeyEvent.KEYCODE_DPAD_UP:
1326 case KeyEvent.KEYCODE_DPAD_DOWN:
1329 case KeyEvent.KEYCODE_DEL:
1335 return super.dispatchKeyEvent(event);
1338 private void removeItem() {
1339 int curcount = mTrackCursor.getCount();
1340 int curpos = mTrackList.getSelectedItemPosition();
1341 if (curcount == 0 || curpos < 0) {
1345 if ("nowplaying".equals(mPlaylist)) {
1346 // remove track from queue
1348 // Work around bug 902971. To get quick visual feedback
1349 // of the deletion of the item, hide the selected view.
1351 if (curpos != MusicUtils.sService.getQueuePosition()) {
1352 mDeletedOneRow = true;
1354 } catch (RemoteException ex) {
1356 View v = mTrackList.getSelectedView();
1357 v.setVisibility(View.GONE);
1358 mTrackList.invalidateViews();
1359 ((NowPlayingCursor) mTrackCursor).removeItem(curpos);
1360 v.setVisibility(View.VISIBLE);
1361 mTrackList.invalidateViews();
1363 // remove track from playlist
1364 int colidx = mTrackCursor
1365 .getColumnIndexOrThrow(MediaStore.Audio.Playlists.Members._ID);
1366 mTrackCursor.moveToPosition(curpos);
1367 long id = mTrackCursor.getLong(colidx);
1368 Uri uri = MediaStore.Audio.Playlists.Members.getContentUri(
1369 "external", Long.valueOf(mPlaylist));
1370 getContentResolver().delete(ContentUris.withAppendedId(uri, id),
1373 if (curcount == 0) {
1376 mTrackList.setSelection(curpos < curcount ? curpos : curcount);
1381 private void moveItem(boolean up) {
1382 int curcount = mTrackCursor.getCount();
1383 int curpos = mTrackList.getSelectedItemPosition();
1384 if ((up && curpos < 1) || (!up && curpos >= curcount - 1)) {
1388 if (mTrackCursor instanceof NowPlayingCursor) {
1389 NowPlayingCursor c = (NowPlayingCursor) mTrackCursor;
1390 c.moveItem(curpos, up ? curpos - 1 : curpos + 1);
1391 ((TrackListAdapter) getListAdapter()).notifyDataSetChanged();
1392 getListView().invalidateViews();
1393 mDeletedOneRow = true;
1395 mTrackList.setSelection(curpos - 1);
1397 mTrackList.setSelection(curpos + 1);
1400 int colidx = mTrackCursor
1401 .getColumnIndexOrThrow(MediaStore.Audio.Playlists.Members.PLAY_ORDER);
1402 mTrackCursor.moveToPosition(curpos);
1403 int currentplayidx = mTrackCursor.getInt(colidx);
1404 Uri baseUri = MediaStore.Audio.Playlists.Members.getContentUri(
1405 "external", Long.valueOf(mPlaylist));
1406 ContentValues values = new ContentValues();
1407 String where = MediaStore.Audio.Playlists.Members._ID + "=?";
1408 String[] wherearg = new String[1];
1409 ContentResolver res = getContentResolver();
1411 values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER,
1412 currentplayidx - 1);
1413 wherearg[0] = mTrackCursor.getString(0);
1414 res.update(baseUri, values, where, wherearg);
1415 mTrackCursor.moveToPrevious();
1417 values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER,
1418 currentplayidx + 1);
1419 wherearg[0] = mTrackCursor.getString(0);
1420 res.update(baseUri, values, where, wherearg);
1421 mTrackCursor.moveToNext();
1423 values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER,
1425 wherearg[0] = mTrackCursor.getString(0);
1426 res.update(baseUri, values, where, wherearg);
1431 protected void onListItemClick(ListView l, View v, int position, long id) {
1432 if (mTrackCursor.getCount() == 0) {
1435 // When selecting a track from the queue, just jump there instead of
1436 // reloading the queue. This is both faster, and prevents accidentally
1437 // dropping out of party shuffle.
1438 if (mTrackCursor instanceof NowPlayingCursor) {
1439 if (MusicUtils.sService != null) {
1441 MusicUtils.sService.setQueuePosition(position);
1443 } catch (RemoteException ex) {
1447 MusicUtils.playAll(this, mTrackCursor, position);
1448 if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
1449 setPauseButtonImage();
1454 public boolean onCreateOptionsMenu(Menu menu) {
1456 * This activity is used for a number of different browsing modes, and
1457 * the menu can be different for each of them: - all tracks, optionally
1458 * restricted to an album, artist or playlist - the list of currently
1461 super.onCreateOptionsMenu(menu);
1462 if (mPlaylist == null) {
1463 menu.add(0, PLAY_ALL, 0, R.string.play_all);
1465 menu.add(0, PARTY_SHUFFLE, 0, R.string.party_shuffle);
1466 menu.add(0, SHUFFLE_ALL, 0, R.string.shuffle_all);
1467 menu.add(0, SETTINGS, 0, R.string.settings);
1469 if (mPlaylist != null) {
1470 menu.add(0, SAVE_AS_PLAYLIST, 0, R.string.save_as_playlist)
1471 .setIcon(android.R.drawable.ic_menu_save);
1472 if (mPlaylist.equals("nowplaying")) {
1473 menu.add(0, CLEAR_PLAYLIST, 0, R.string.clear_playlist);
1480 public boolean onPrepareOptionsMenu(Menu menu) {
1481 MusicUtils.setPartyShuffleMenuIcon(menu);
1482 return super.onPrepareOptionsMenu(menu);
1486 public boolean onOptionsItemSelected(MenuItem item) {
1489 switch (item.getItemId()) {
1490 case android.R.id.home:
1491 super.onBackPressed();
1494 MusicUtils.playAll(this, mTrackCursor);
1499 MusicUtils.togglePartyShuffle();
1503 // Should 'shuffle all' shuffle ALL, or only the tracks shown?
1504 cursor = MusicUtils.query(this,
1505 MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
1506 new String[] { MediaStore.Audio.Media._ID },
1507 MediaStore.Audio.Media.IS_MUSIC + "=1", null,
1508 MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
1509 if (cursor != null) {
1510 MusicUtils.shuffleAll(this, cursor);
1515 case SAVE_AS_PLAYLIST:
1516 intent = new Intent();
1517 intent.setClass(this, CreatePlaylist.class);
1518 startActivityForResult(intent, SAVE_AS_PLAYLIST);
1521 case CLEAR_PLAYLIST:
1522 // We only clear the current playlist
1523 MusicUtils.clearQueue();
1526 intent = new Intent();
1527 intent.setClass(this, MusicSettingsActivity.class);
1528 startActivityForResult(intent, SETTINGS);
1531 return super.onOptionsItemSelected(item);
1535 protected void onActivityResult(int requestCode, int resultCode,
1537 switch (requestCode) {
1539 if (resultCode == RESULT_CANCELED) {
1542 getTrackCursor(mAdapter.getQueryHandler(), null, true);
1547 if (resultCode == RESULT_OK) {
1548 Uri uri = intent.getData();
1550 long[] list = new long[] { mSelectedId };
1551 MusicUtils.addToPlaylist(this, list,
1552 Integer.valueOf(uri.getLastPathSegment()));
1557 case SAVE_AS_PLAYLIST:
1558 if (resultCode == RESULT_OK) {
1559 Uri uri = intent.getData();
1561 long[] list = MusicUtils.getSongListForCursor(mTrackCursor);
1562 int plid = Integer.parseInt(uri.getLastPathSegment());
1563 MusicUtils.addToPlaylist(this, list, plid);
1570 private Cursor getTrackCursor(
1571 TrackListAdapter.TrackQueryHandler queryhandler, String filter,
1574 if (queryhandler == null) {
1575 throw new IllegalArgumentException();
1579 mSortOrder = MediaStore.Audio.Media.TITLE_KEY;
1580 StringBuilder where = new StringBuilder();
1581 where.append(MediaStore.Audio.Media.TITLE + " != ''");
1583 if (mGenre != null) {
1584 Uri uri = MediaStore.Audio.Genres.Members.getContentUri("external",
1585 Integer.valueOf(mGenre));
1586 if (!TextUtils.isEmpty(filter)) {
1587 uri = uri.buildUpon()
1588 .appendQueryParameter("filter", Uri.encode(filter))
1591 mSortOrder = MediaStore.Audio.Genres.Members.DEFAULT_SORT_ORDER;
1592 ret = queryhandler.doQuery(uri, mCursorCols, where.toString(),
1593 null, mSortOrder, async);
1594 } else if (mPlaylist != null) {
1595 if (mPlaylist.equals("nowplaying")) {
1596 if (MusicUtils.sService != null) {
1597 ret = new NowPlayingCursor(MusicUtils.sService, mCursorCols);
1598 if (ret.getCount() == 0) {
1602 // Nothing is playing.
1604 } else if (mPlaylist.equals("podcasts")) {
1605 where.append(" AND " + MediaStore.Audio.Media.IS_PODCAST + "=1");
1606 Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
1607 if (!TextUtils.isEmpty(filter)) {
1608 uri = uri.buildUpon()
1609 .appendQueryParameter("filter", Uri.encode(filter))
1612 ret = queryhandler.doQuery(uri, mCursorCols, where.toString(),
1613 null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER, async);
1614 } else if (mPlaylist.equals("recentlyadded")) {
1615 // do a query for all songs added in the last X weeks
1616 Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
1617 if (!TextUtils.isEmpty(filter)) {
1618 uri = uri.buildUpon()
1619 .appendQueryParameter("filter", Uri.encode(filter))
1622 int X = MusicUtils.getIntPref(this, "numweeks", 2)
1624 where.append(" AND " + MediaStore.MediaColumns.DATE_ADDED + ">");
1625 where.append(System.currentTimeMillis() / 1000 - X);
1626 ret = queryhandler.doQuery(uri, mCursorCols, where.toString(),
1627 null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER, async);
1629 Uri uri = MediaStore.Audio.Playlists.Members.getContentUri(
1630 "external", Long.valueOf(mPlaylist));
1631 if (!TextUtils.isEmpty(filter)) {
1632 uri = uri.buildUpon()
1633 .appendQueryParameter("filter", Uri.encode(filter))
1636 mSortOrder = MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER;
1637 ret = queryhandler.doQuery(uri, mPlaylistMemberCols,
1638 where.toString(), null, mSortOrder, async);
1641 if (mAlbumId != null) {
1642 where.append(" AND " + MediaStore.Audio.Media.ALBUM_ID + "="
1644 mSortOrder = MediaStore.Audio.Media.TRACK + ", " + mSortOrder;
1646 if (mArtistId != null) {
1647 where.append(" AND " + MediaStore.Audio.Media.ARTIST_ID + "="
1650 where.append(" AND " + MediaStore.Audio.Media.IS_MUSIC + "=1");
1651 Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
1652 if (!TextUtils.isEmpty(filter)) {
1653 uri = uri.buildUpon()
1654 .appendQueryParameter("filter", Uri.encode(filter))
1657 ret = queryhandler.doQuery(uri, mCursorCols, where.toString(),
1658 null, mSortOrder, async);
1661 // This special case is for the "nowplaying" cursor, which cannot be
1663 // asynchronously using AsyncQueryHandler, so we do some extra
1664 // initialization here.
1665 if (ret != null && async) {
1672 private class NowPlayingCursor extends AbstractCursor {
1673 public NowPlayingCursor(IMediaPlaybackService service, String[] cols) {
1676 makeNowPlayingCursor();
1679 private void makeNowPlayingCursor() {
1680 mCurrentPlaylistCursor = null;
1682 mNowPlaying = mService.getQueue();
1683 } catch (RemoteException ex) {
1684 mNowPlaying = new long[0];
1686 mSize = mNowPlaying.length;
1691 StringBuilder where = new StringBuilder();
1692 where.append(MediaStore.Audio.Media._ID + " IN (");
1693 for (int i = 0; i < mSize; i++) {
1694 where.append(mNowPlaying[i]);
1695 if (i < mSize - 1) {
1701 mCurrentPlaylistCursor = MusicUtils.query(
1702 TrackBrowserActivity.this,
1703 MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, mCols,
1704 where.toString(), null, MediaStore.Audio.Media._ID);
1706 if (mCurrentPlaylistCursor == null) {
1711 int size = mCurrentPlaylistCursor.getCount();
1712 mCursorIdxs = new long[size];
1713 mCurrentPlaylistCursor.moveToFirst();
1714 int colidx = mCurrentPlaylistCursor
1715 .getColumnIndexOrThrow(MediaStore.Audio.Media._ID);
1716 for (int i = 0; i < size; i++) {
1717 mCursorIdxs[i] = mCurrentPlaylistCursor.getLong(colidx);
1718 mCurrentPlaylistCursor.moveToNext();
1720 mCurrentPlaylistCursor.moveToFirst();
1723 // At this point we can verify the 'now playing' list we got
1724 // earlier to make sure that all the items in there still exist
1725 // in the database, and remove those that aren't. This way we
1726 // don't get any blank items in the list.
1729 for (int i = mNowPlaying.length - 1; i >= 0; i--) {
1730 long trackid = mNowPlaying[i];
1731 int crsridx = Arrays.binarySearch(mCursorIdxs, trackid);
1733 // Log.i("@@@@@", "item no longer exists in db: " +
1735 removed += mService.removeTrack(trackid);
1739 mNowPlaying = mService.getQueue();
1740 mSize = mNowPlaying.length;
1746 } catch (RemoteException ex) {
1747 mNowPlaying = new long[0];
1752 public int getCount() {
1757 public boolean onMove(int oldPosition, int newPosition) {
1758 if (oldPosition == newPosition)
1761 if (mNowPlaying == null || mCursorIdxs == null
1762 || newPosition >= mNowPlaying.length) {
1766 // The cursor doesn't have any duplicates in it, and is not ordered
1767 // in queue-order, so we need to figure out where in the cursor we
1770 long newid = mNowPlaying[newPosition];
1771 int crsridx = Arrays.binarySearch(mCursorIdxs, newid);
1772 mCurrentPlaylistCursor.moveToPosition(crsridx);
1773 mCurPos = newPosition;
1778 public boolean removeItem(int which) {
1780 if (mService.removeTracks(which, which) == 0) {
1781 return false; // delete failed
1783 int i = (int) which;
1786 mNowPlaying[i] = mNowPlaying[i + 1];
1789 onMove(-1, (int) mCurPos);
1790 } catch (RemoteException ex) {
1795 public void moveItem(int from, int to) {
1797 mService.moveQueueItem(from, to);
1798 mNowPlaying = mService.getQueue();
1799 onMove(-1, mCurPos); // update the underlying cursor
1800 } catch (RemoteException ex) {
1804 private void dump() {
1806 for (int i = 0; i < mSize; i++) {
1807 where += mNowPlaying[i];
1808 if (i < mSize - 1) {
1813 Log.i("NowPlayingCursor: ", where);
1817 public String getString(int column) {
1819 return mCurrentPlaylistCursor.getString(column);
1820 } catch (Exception ex) {
1827 public short getShort(int column) {
1828 return mCurrentPlaylistCursor.getShort(column);
1832 public int getInt(int column) {
1834 return mCurrentPlaylistCursor.getInt(column);
1835 } catch (Exception ex) {
1842 public long getLong(int column) {
1844 return mCurrentPlaylistCursor.getLong(column);
1845 } catch (Exception ex) {
1852 public float getFloat(int column) {
1853 return mCurrentPlaylistCursor.getFloat(column);
1857 public double getDouble(int column) {
1858 return mCurrentPlaylistCursor.getDouble(column);
1862 public int getType(int column) {
1863 return mCurrentPlaylistCursor.getType(column);
1867 public boolean isNull(int column) {
1868 return mCurrentPlaylistCursor.isNull(column);
1872 public String[] getColumnNames() {
1877 public void deactivate() {
1878 if (mCurrentPlaylistCursor != null)
1879 mCurrentPlaylistCursor.deactivate();
1883 public boolean requery() {
1884 makeNowPlayingCursor();
1888 private String[] mCols;
1889 private Cursor mCurrentPlaylistCursor; // updated in onMove
1890 private int mSize; // size of the queue
1891 private long[] mNowPlaying;
1892 private long[] mCursorIdxs;
1893 private int mCurPos;
1894 private IMediaPlaybackService mService;
1897 static class TrackListAdapter extends SimpleCursorAdapter implements
1899 boolean mIsNowPlaying;
1900 boolean mDisableNowPlayingIndicator;
1908 private final StringBuilder mBuilder = new StringBuilder();
1909 private final String mUnknownArtist;
1910 private final String mUnknownAlbum;
1912 private AlphabetIndexer mIndexer;
1914 private TrackBrowserActivity mActivity = null;
1915 private TrackQueryHandler mQueryHandler;
1916 private String mConstraint = null;
1917 private boolean mConstraintIsValid = false;
1919 static class ViewHolder {
1923 ImageView play_indicator;
1924 CharArrayBuffer buffer1;
1927 public FrameLayout mContextMenu;
1930 class TrackQueryHandler extends AsyncQueryHandler {
1934 public String[] projection;
1935 public String selection;
1936 public String[] selectionArgs;
1937 public String orderBy;
1940 TrackQueryHandler(ContentResolver res) {
1944 public Cursor doQuery(Uri uri, String[] projection,
1945 String selection, String[] selectionArgs, String orderBy,
1948 // Get 100 results first, which is enough to allow the user
1949 // to start scrolling,
1950 // while still being very fast.
1951 Uri limituri = uri.buildUpon()
1952 .appendQueryParameter("limit", "100").build();
1953 QueryArgs args = new QueryArgs();
1955 args.projection = projection;
1956 args.selection = selection;
1957 args.selectionArgs = selectionArgs;
1958 args.orderBy = orderBy;
1960 startQuery(0, args, limituri, projection, selection,
1961 selectionArgs, orderBy);
1964 return MusicUtils.query(mActivity, uri, projection, selection,
1965 selectionArgs, orderBy);
1969 protected void onQueryComplete(int token, Object cookie,
1971 // Log.i("@@@", "query complete: " + cursor.getCount() + " " +
1973 mActivity.init(cursor, cookie != null);
1974 if (token == 0 && cookie != null && cursor != null
1975 && cursor.getCount() >= 100) {
1976 QueryArgs args = (QueryArgs) cookie;
1977 startQuery(1, null, args.uri, args.projection,
1978 args.selection, args.selectionArgs, args.orderBy);
1983 TrackListAdapter(Context context, TrackBrowserActivity currentactivity,
1984 int layout, Cursor cursor, String[] from, int[] to,
1985 boolean isnowplaying, boolean disablenowplayingindicator) {
1986 super(context, layout, cursor, from, to);
1987 mActivity = currentactivity;
1988 getColumnIndices(cursor);
1989 mIsNowPlaying = isnowplaying;
1990 mDisableNowPlayingIndicator = disablenowplayingindicator;
1991 mUnknownArtist = context.getString(R.string.unknown_artist_name);
1992 mUnknownAlbum = context.getString(R.string.unknown_album_name);
1994 mQueryHandler = new TrackQueryHandler(context.getContentResolver());
1997 public void setActivity(TrackBrowserActivity newactivity) {
1998 mActivity = newactivity;
2001 public TrackQueryHandler getQueryHandler() {
2002 return mQueryHandler;
2005 private void getColumnIndices(Cursor cursor) {
2006 if (cursor != null) {
2008 .getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE);
2010 .getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST);
2011 mDurationIdx = cursor
2012 .getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION);
2014 .getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM);
2017 mAudioIdIdx = cursor
2018 .getColumnIndexOrThrow(MediaStore.Audio.Playlists.Members.AUDIO_ID);
2019 } catch (IllegalArgumentException ex) {
2020 mAudioIdIdx = cursor
2021 .getColumnIndexOrThrow(MediaStore.Audio.Media._ID);
2024 if (mIndexer != null) {
2025 mIndexer.setCursor(cursor);
2026 } else if (!mActivity.mEditMode && mActivity.mAlbumId == null) {
2027 String alpha = mActivity
2028 .getString(R.string.fast_scroll_alphabet);
2030 mIndexer = new MusicAlphabetIndexer(cursor, mTitleIdx,
2036 private View.OnClickListener mCML = new View.OnClickListener() {
2037 public void onClick(View v) {
2038 v.showContextMenu();
2044 public View newView(Context context, Cursor cursor, ViewGroup parent) {
2045 View v = super.newView(context, cursor, parent);
2046 ImageView iv = (ImageView) v.findViewById(R.id.icon);
2047 iv.setVisibility(View.GONE);
2049 ViewHolder vh = new ViewHolder();
2050 vh.line1 = (TextView) v.findViewById(R.id.line1);
2051 vh.line2 = (TextView) v.findViewById(R.id.line2);
2052 vh.duration = (TextView) v.findViewById(R.id.duration);
2053 vh.play_indicator = (ImageView) v.findViewById(R.id.play_indicator);
2054 vh.buffer1 = new CharArrayBuffer(100);
2055 vh.buffer2 = new char[200];
2057 vh.mCM = (ImageView) v.findViewById(R.id.CM);
2058 vh.mContextMenu = (FrameLayout) v
2059 .findViewById(R.id.second_column_icon);
2060 vh.mContextMenu.setOnClickListener(mCML);
2061 // ADW: Load the specified theme
2062 String themePackage = MusicUtils.getThemePackageName(context,
2063 MusicSettingsActivity.THEME_DEFAULT);
2064 PackageManager pm = context.getPackageManager();
2065 Resources themeResources = null;
2066 if (!themePackage.equals(MusicSettingsActivity.THEME_DEFAULT)) {
2069 .getResourcesForApplication(themePackage);
2070 } catch (NameNotFoundException e) {
2071 // ADW The saved theme was uninstalled so we save the
2073 MusicUtils.setThemePackageName(context,
2074 MusicSettingsActivity.THEME_DEFAULT);
2077 if (themeResources != null) {
2078 int line1 = themeResources.getIdentifier(
2079 "song_tab_track_name_color", "color", themePackage);
2081 vh.line1.setTextColor(themeResources.getColor(line1));
2083 int line2 = themeResources.getIdentifier(
2084 "song_tab_album_name_color", "color", themePackage);
2086 vh.line2.setTextColor(themeResources.getColor(line2));
2088 int duration = themeResources.getIdentifier(
2089 "song_tab_duration_color", "color", themePackage);
2090 if (duration != 0) {
2091 vh.duration.setTextColor(themeResources.getColor(duration));
2093 ArtistAlbumBrowserActivity.loadThemeResource(themeResources,
2094 themePackage, "bt_context_menu", vh.mCM,
2095 THEME_ITEM_FOREGROUND);
2101 public void bindView(View view, Context context, Cursor cursor) {
2102 // ADW: Load the specified theme
2103 String themePackage = MusicUtils.getThemePackageName(context,
2104 MusicSettingsActivity.THEME_DEFAULT);
2105 PackageManager pm = context.getPackageManager();
2106 Resources themeResources = null;
2107 if (!themePackage.equals(MusicSettingsActivity.THEME_DEFAULT)) {
2110 .getResourcesForApplication(themePackage);
2111 } catch (NameNotFoundException e) {
2112 // ADW The saved theme was uninstalled so we save the
2114 MusicUtils.setThemePackageName(context,
2115 MusicSettingsActivity.THEME_DEFAULT);
2119 ViewHolder vh = (ViewHolder) view.getTag();
2121 cursor.copyStringToBuffer(mTitleIdx, vh.buffer1);
2122 vh.line1.setText(vh.buffer1.data, 0, vh.buffer1.sizeCopied);
2124 int secs = cursor.getInt(mDurationIdx) / 1000;
2126 vh.duration.setText("");
2128 vh.duration.setText(MusicUtils.makeTimeString(context, secs));
2131 final StringBuilder builder = mBuilder;
2132 builder.delete(0, builder.length());
2134 String name = cursor.getString(mAlbumIdx);
2135 if (name == null || name.equals(MediaStore.UNKNOWN_STRING)) {
2136 builder.append(mUnknownArtist);
2138 builder.append(name);
2140 int len = builder.length();
2141 if (vh.buffer2.length < len) {
2142 vh.buffer2 = new char[len];
2144 builder.getChars(0, len, vh.buffer2, 0);
2145 vh.line2.setText(vh.buffer2, 0, len);
2147 ImageView iv = vh.play_indicator;
2149 if (MusicUtils.sService != null) {
2150 // TODO: IPC call on each bind??
2152 if (mIsNowPlaying) {
2153 id = MusicUtils.sService.getQueuePosition();
2155 id = MusicUtils.sService.getAudioId();
2157 } catch (RemoteException ex) {
2161 // Determining whether and where to show the "now playing indicator
2162 // is tricky, because we don't actually keep track of where the
2164 // in the current playlist came from after they've started playing.
2166 // If the "current playlists" is shown, then we can simply match by
2168 // otherwise, we need to match by id. Match-by-id gets a little
2170 // a song appears in a playlist more than once, and you're in
2172 // mode. In that case, both items will have the "now playing"
2174 // For this reason, we don't show the play indicator at all when in
2176 // playlist mode (except when you're viewing the "current playlist",
2177 // which is not really a playlist)
2178 if ((mIsNowPlaying && cursor.getPosition() == id)
2179 || (!mIsNowPlaying && !mDisableNowPlayingIndicator && cursor
2180 .getLong(mAudioIdIdx) == id)) {
2181 iv.setBackgroundResource(R.anim.peak_meter);
2182 AnimationDrawable frameAnimation = (AnimationDrawable) iv
2184 if (themeResources != null) {
2185 int peak = themeResources.getIdentifier("peak_meter",
2186 "anim", themePackage);
2188 iv.setBackgroundDrawable(themeResources
2189 .getDrawable(peak));
2190 frameAnimation.start();
2193 // Start the animation (looped playback by default).
2194 frameAnimation.start();
2195 iv.setVisibility(View.VISIBLE);
2197 iv.setVisibility(View.GONE);
2202 public void changeCursor(Cursor cursor) {
2203 if (mActivity.isFinishing() && cursor != null) {
2207 if (cursor != mActivity.mTrackCursor) {
2208 mActivity.mTrackCursor = cursor;
2209 super.changeCursor(cursor);
2210 getColumnIndices(cursor);
2215 public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
2216 String s = constraint.toString();
2217 if (mConstraintIsValid
2218 && ((s == null && mConstraint == null) || (s != null && s
2219 .equals(mConstraint)))) {
2222 Cursor c = mActivity.getTrackCursor(mQueryHandler, s, false);
2224 mConstraintIsValid = true;
2228 // SectionIndexer methods
2230 public Object[] getSections() {
2231 if (mIndexer != null) {
2232 return mIndexer.getSections();
2234 return new String[] { " " };
2238 public int getPositionForSection(int section) {
2239 if (mIndexer != null) {
2240 return mIndexer.getPositionForSection(section);
2245 public int getSectionForPosition(int position) {
2250 // Methods for media control
2251 private void doPauseResume() {
2253 if (mService != null) {
2254 if (mService.isPlaying()) {
2259 if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
2260 setPauseButtonImage();
2263 } catch (RemoteException ex) {
2267 private void setPauseButtonImage() {
2268 // ADW: Load the specified theme
2269 String themePackage = MusicUtils.getThemePackageName(this,
2270 MusicSettingsActivity.THEME_DEFAULT);
2271 PackageManager pm = getPackageManager();
2272 Resources themeResources = null;
2273 if (!themePackage.equals(MusicSettingsActivity.THEME_DEFAULT)) {
2275 themeResources = pm.getResourcesForApplication(themePackage);
2276 } catch (NameNotFoundException e) {
2277 // ADW The saved theme was uninstalled so we save the
2279 MusicUtils.setThemePackageName(this,
2280 MusicSettingsActivity.THEME_DEFAULT);
2284 if (mService != null && mService.isPlaying()) {
2285 mPlay.setImageResource(R.drawable.ic_media_pause);
2286 ArtistAlbumBrowserActivity
2287 .loadThemeResource(themeResources, themePackage,
2288 "snp_pause", mPlay, THEME_ITEM_FOREGROUND);
2290 mPlay.setImageResource(R.drawable.ic_appwidget_music_play);
2291 ArtistAlbumBrowserActivity.loadThemeResource(themeResources,
2292 themePackage, "snp_play", mPlay, THEME_ITEM_FOREGROUND);
2294 } catch (RemoteException ex) {
2298 private void refreshProgress() {
2299 ProgressBar mProgress = (ProgressBar) findViewById(R.id.progress);
2300 mProgress.setMax(1000);
2302 if ((MusicUtils.sService.position() >= 0)
2303 && (MusicUtils.sService.duration() > 0)) {
2304 mProgress.setProgress((int) (1000 * MusicUtils.sService
2305 .position() / MusicUtils.sService.duration()));
2307 mProgress.setProgress(1000);
2309 } catch (Exception e) {
2310 // TODO Auto-generated catch block
2311 e.printStackTrace();
2316 private void doPrev() {
2317 if (mService == null)
2320 if (mService.position() < 2000) {
2326 setPauseButtonImage();
2327 } catch (RemoteException ex) {
2331 private void doNext() {
2332 if (mService == null)
2336 setPauseButtonImage();
2337 } catch (RemoteException ex) {
2341 private void playRecentlyAdded() {
2342 // do a query for all songs added in the last X weeks
2343 int X = MusicUtils.getIntPref(this, "numweeks", 2) * (3600 * 24 * 7);
2344 final String[] ccols = new String[] { MediaStore.Audio.Media._ID };
2345 String where = MediaStore.MediaColumns.DATE_ADDED + ">"
2346 + (System.currentTimeMillis() / 1000 - X);
2347 Cursor cursor = MusicUtils.query(this,
2348 MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, ccols, where,
2349 null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
2351 if (cursor == null) {
2352 // Todo: show a message
2356 int len = cursor.getCount();
2357 long[] list = new long[len];
2358 for (int i = 0; i < len; i++) {
2359 cursor.moveToNext();
2360 list[i] = cursor.getLong(0);
2362 MusicUtils.playAll(this, list, 0);
2363 } catch (SQLiteException ex) {
2370 public boolean onKeyLongPress(int keyCode, KeyEvent event) {
2371 if (keyCode == KeyEvent.KEYCODE_BACK) {
2372 back_button_db = mPreferences.getString("back_button_db", "0");
2373 if (back_button_db.equals("0")) {
2374 MusicUtils.togglePartyShuffle();
2377 if (keyCode == KeyEvent.KEYCODE_BACK) {
2378 back_button_db = mPreferences.getString("back_button_db", "1");
2379 if (back_button_db.equals("1")) {
2380 playRecentlyAdded();
2385 if (keyCode == KeyEvent.KEYCODE_BACK) {
2386 back_button_db = mPreferences.getString("back_button_db", "2");
2387 if (back_button_db.equals("2")) {
2389 cursor = MusicUtils.query(this,
2390 MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
2391 new String[] { BaseColumns._ID },
2392 AudioColumns.IS_MUSIC + "=1", null,
2393 MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
2394 if (cursor != null) {
2395 MusicUtils.shuffleAll(this, cursor);
2402 return super.onKeyLongPress(keyCode, event);