blob: 58f9bee1302479f8230346950ce32e3c09f0fc99 [file] [log] [blame]
The Android Open Source Project792a2202009-03-03 19:32:30 -08001/*
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
The Android Open Source Project792a2202009-03-03 19:32:30 -080019import android.app.Activity;
The Android Open Source Project792a2202009-03-03 19:32:30 -080020import android.content.ComponentName;
21import android.content.ContentResolver;
22import android.content.ContentUris;
23import android.content.ContentValues;
24import android.content.Context;
25import android.content.Intent;
26import android.content.ServiceConnection;
27import android.content.SharedPreferences;
28import android.content.SharedPreferences.Editor;
29import android.content.res.Resources;
30import android.database.Cursor;
31import android.graphics.Bitmap;
32import android.graphics.BitmapFactory;
33import android.graphics.Canvas;
34import android.graphics.ColorFilter;
35import android.graphics.PixelFormat;
36import android.graphics.drawable.BitmapDrawable;
37import android.graphics.drawable.Drawable;
Marco Nelissenec0c57a2009-12-12 12:27:11 -080038import android.media.MediaFile;
The Android Open Source Project792a2202009-03-03 19:32:30 -080039import android.net.Uri;
The Android Open Source Project792a2202009-03-03 19:32:30 -080040import android.os.Environment;
41import android.os.ParcelFileDescriptor;
Marco Nelissen7a16cc72009-06-16 08:54:17 -070042import android.os.RemoteException;
The Android Open Source Project792a2202009-03-03 19:32:30 -080043import android.provider.MediaStore;
44import android.provider.Settings;
Marco Nelissen7a052982009-09-09 10:34:11 -070045import android.text.TextUtils;
The Android Open Source Project792a2202009-03-03 19:32:30 -080046import android.util.Log;
Marco Nelissenec0c57a2009-12-12 12:27:11 -080047import android.view.Menu;
48import android.view.MenuItem;
The Android Open Source Project792a2202009-03-03 19:32:30 -080049import android.view.SubMenu;
50import android.view.View;
Marco Nelissenec0c57a2009-12-12 12:27:11 -080051import android.view.ViewGroup;
The Android Open Source Project792a2202009-03-03 19:32:30 -080052import android.view.Window;
Marco Nelissenec0c57a2009-12-12 12:27:11 -080053import android.widget.TabWidget;
The Android Open Source Project792a2202009-03-03 19:32:30 -080054import android.widget.TextView;
55import android.widget.Toast;
56
Marco Nelissen7a16cc72009-06-16 08:54:17 -070057import java.io.File;
58import java.io.FileDescriptor;
59import java.io.FileNotFoundException;
60import java.io.IOException;
61import java.io.InputStream;
62import java.util.Arrays;
63import java.util.Formatter;
64import java.util.HashMap;
65import java.util.Locale;
66
The Android Open Source Project792a2202009-03-03 19:32:30 -080067public class MusicUtils {
68
69 private static final String TAG = "MusicUtils";
70
71 public interface Defs {
72 public final static int OPEN_URL = 0;
73 public final static int ADD_TO_PLAYLIST = 1;
74 public final static int USE_AS_RINGTONE = 2;
75 public final static int PLAYLIST_SELECTED = 3;
76 public final static int NEW_PLAYLIST = 4;
77 public final static int PLAY_SELECTION = 5;
78 public final static int GOTO_START = 6;
79 public final static int GOTO_PLAYBACK = 7;
80 public final static int PARTY_SHUFFLE = 8;
81 public final static int SHUFFLE_ALL = 9;
82 public final static int DELETE_ITEM = 10;
83 public final static int SCAN_DONE = 11;
84 public final static int QUEUE = 12;
85 public final static int CHILD_MENU_BASE = 13; // this should be the last item
86 }
87
88 public static String makeAlbumsLabel(Context context, int numalbums, int numsongs, boolean isUnknown) {
89 // There are two formats for the albums/songs information:
90 // "N Song(s)" - used for unknown artist/album
91 // "N Album(s)" - used for known albums
92
93 StringBuilder songs_albums = new StringBuilder();
94
95 Resources r = context.getResources();
96 if (isUnknown) {
97 if (numsongs == 1) {
98 songs_albums.append(context.getString(R.string.onesong));
99 } else {
100 String f = r.getQuantityText(R.plurals.Nsongs, numsongs).toString();
101 sFormatBuilder.setLength(0);
102 sFormatter.format(f, Integer.valueOf(numsongs));
103 songs_albums.append(sFormatBuilder);
104 }
105 } else {
106 String f = r.getQuantityText(R.plurals.Nalbums, numalbums).toString();
107 sFormatBuilder.setLength(0);
108 sFormatter.format(f, Integer.valueOf(numalbums));
109 songs_albums.append(sFormatBuilder);
110 songs_albums.append(context.getString(R.string.albumsongseparator));
111 }
112 return songs_albums.toString();
113 }
114
115 /**
116 * This is now only used for the query screen
117 */
118 public static String makeAlbumsSongsLabel(Context context, int numalbums, int numsongs, boolean isUnknown) {
119 // There are several formats for the albums/songs information:
120 // "1 Song" - used if there is only 1 song
121 // "N Songs" - used for the "unknown artist" item
122 // "1 Album"/"N Songs"
123 // "N Album"/"M Songs"
124 // Depending on locale, these may need to be further subdivided
125
126 StringBuilder songs_albums = new StringBuilder();
127
128 if (numsongs == 1) {
129 songs_albums.append(context.getString(R.string.onesong));
130 } else {
131 Resources r = context.getResources();
132 if (! isUnknown) {
133 String f = r.getQuantityText(R.plurals.Nalbums, numalbums).toString();
134 sFormatBuilder.setLength(0);
135 sFormatter.format(f, Integer.valueOf(numalbums));
136 songs_albums.append(sFormatBuilder);
137 songs_albums.append(context.getString(R.string.albumsongseparator));
138 }
139 String f = r.getQuantityText(R.plurals.Nsongs, numsongs).toString();
140 sFormatBuilder.setLength(0);
141 sFormatter.format(f, Integer.valueOf(numsongs));
142 songs_albums.append(sFormatBuilder);
143 }
144 return songs_albums.toString();
145 }
146
147 public static IMediaPlaybackService sService = null;
148 private static HashMap<Context, ServiceBinder> sConnectionMap = new HashMap<Context, ServiceBinder>();
149
150 public static boolean bindToService(Context context) {
151 return bindToService(context, null);
152 }
153
154 public static boolean bindToService(Context context, ServiceConnection callback) {
155 context.startService(new Intent(context, MediaPlaybackService.class));
156 ServiceBinder sb = new ServiceBinder(callback);
157 sConnectionMap.put(context, sb);
158 return context.bindService((new Intent()).setClass(context,
159 MediaPlaybackService.class), sb, 0);
160 }
161
162 public static void unbindFromService(Context context) {
163 ServiceBinder sb = (ServiceBinder) sConnectionMap.remove(context);
164 if (sb == null) {
165 Log.e("MusicUtils", "Trying to unbind for unknown Context");
166 return;
167 }
168 context.unbindService(sb);
169 if (sConnectionMap.isEmpty()) {
170 // presumably there is nobody interested in the service at this point,
171 // so don't hang on to the ServiceConnection
172 sService = null;
173 }
174 }
175
176 private static class ServiceBinder implements ServiceConnection {
177 ServiceConnection mCallback;
178 ServiceBinder(ServiceConnection callback) {
179 mCallback = callback;
180 }
181
182 public void onServiceConnected(ComponentName className, android.os.IBinder service) {
183 sService = IMediaPlaybackService.Stub.asInterface(service);
184 initAlbumArtCache();
185 if (mCallback != null) {
186 mCallback.onServiceConnected(className, service);
187 }
188 }
189
190 public void onServiceDisconnected(ComponentName className) {
191 if (mCallback != null) {
192 mCallback.onServiceDisconnected(className);
193 }
194 sService = null;
195 }
196 }
197
Marco Nelissenbd447b62009-06-29 14:52:05 -0700198 public static long getCurrentAlbumId() {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800199 if (sService != null) {
200 try {
201 return sService.getAlbumId();
202 } catch (RemoteException ex) {
203 }
204 }
205 return -1;
206 }
207
Marco Nelissenbd447b62009-06-29 14:52:05 -0700208 public static long getCurrentArtistId() {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800209 if (MusicUtils.sService != null) {
210 try {
211 return sService.getArtistId();
212 } catch (RemoteException ex) {
213 }
214 }
215 return -1;
216 }
217
Marco Nelissenbd447b62009-06-29 14:52:05 -0700218 public static long getCurrentAudioId() {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800219 if (MusicUtils.sService != null) {
220 try {
221 return sService.getAudioId();
222 } catch (RemoteException ex) {
223 }
224 }
225 return -1;
226 }
227
228 public static int getCurrentShuffleMode() {
229 int mode = MediaPlaybackService.SHUFFLE_NONE;
230 if (sService != null) {
231 try {
232 mode = sService.getShuffleMode();
233 } catch (RemoteException ex) {
234 }
235 }
236 return mode;
237 }
238
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800239 public static void togglePartyShuffle() {
240 if (sService != null) {
241 int shuffle = getCurrentShuffleMode();
242 try {
243 if (shuffle == MediaPlaybackService.SHUFFLE_AUTO) {
244 sService.setShuffleMode(MediaPlaybackService.SHUFFLE_NONE);
245 } else {
246 sService.setShuffleMode(MediaPlaybackService.SHUFFLE_AUTO);
247 }
248 } catch (RemoteException ex) {
249 }
250 }
251 }
252
253 public static void setPartyShuffleMenuIcon(Menu menu) {
254 MenuItem item = menu.findItem(Defs.PARTY_SHUFFLE);
255 if (item != null) {
256 int shuffle = MusicUtils.getCurrentShuffleMode();
257 if (shuffle == MediaPlaybackService.SHUFFLE_AUTO) {
258 item.setIcon(R.drawable.ic_menu_party_shuffle);
259 item.setTitle(R.string.party_shuffle_off);
260 } else {
261 item.setIcon(R.drawable.ic_menu_party_shuffle);
262 item.setTitle(R.string.party_shuffle);
263 }
264 }
265 }
266
The Android Open Source Project792a2202009-03-03 19:32:30 -0800267 /*
268 * Returns true if a file is currently opened for playback (regardless
269 * of whether it's playing or paused).
270 */
271 public static boolean isMusicLoaded() {
272 if (MusicUtils.sService != null) {
273 try {
274 return sService.getPath() != null;
275 } catch (RemoteException ex) {
276 }
277 }
278 return false;
279 }
280
Marco Nelissenbd447b62009-06-29 14:52:05 -0700281 private final static long [] sEmptyList = new long[0];
The Android Open Source Project792a2202009-03-03 19:32:30 -0800282
Marco Nelissenbd447b62009-06-29 14:52:05 -0700283 public static long [] getSongListForCursor(Cursor cursor) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800284 if (cursor == null) {
285 return sEmptyList;
286 }
287 int len = cursor.getCount();
Marco Nelissenbd447b62009-06-29 14:52:05 -0700288 long [] list = new long[len];
The Android Open Source Project792a2202009-03-03 19:32:30 -0800289 cursor.moveToFirst();
290 int colidx = -1;
291 try {
292 colidx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.Members.AUDIO_ID);
293 } catch (IllegalArgumentException ex) {
294 colidx = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID);
295 }
296 for (int i = 0; i < len; i++) {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700297 list[i] = cursor.getLong(colidx);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800298 cursor.moveToNext();
299 }
300 return list;
301 }
302
Marco Nelissenbd447b62009-06-29 14:52:05 -0700303 public static long [] getSongListForArtist(Context context, long id) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800304 final String[] ccols = new String[] { MediaStore.Audio.Media._ID };
305 String where = MediaStore.Audio.Media.ARTIST_ID + "=" + id + " AND " +
306 MediaStore.Audio.Media.IS_MUSIC + "=1";
307 Cursor cursor = query(context, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
308 ccols, where, null,
309 MediaStore.Audio.Media.ALBUM_KEY + "," + MediaStore.Audio.Media.TRACK);
310
311 if (cursor != null) {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700312 long [] list = getSongListForCursor(cursor);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800313 cursor.close();
314 return list;
315 }
316 return sEmptyList;
317 }
318
Marco Nelissenbd447b62009-06-29 14:52:05 -0700319 public static long [] getSongListForAlbum(Context context, long id) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800320 final String[] ccols = new String[] { MediaStore.Audio.Media._ID };
321 String where = MediaStore.Audio.Media.ALBUM_ID + "=" + id + " AND " +
322 MediaStore.Audio.Media.IS_MUSIC + "=1";
323 Cursor cursor = query(context, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
324 ccols, where, null, MediaStore.Audio.Media.TRACK);
325
326 if (cursor != null) {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700327 long [] list = getSongListForCursor(cursor);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800328 cursor.close();
329 return list;
330 }
331 return sEmptyList;
332 }
333
Marco Nelissenbd447b62009-06-29 14:52:05 -0700334 public static long [] getSongListForPlaylist(Context context, long plid) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800335 final String[] ccols = new String[] { MediaStore.Audio.Playlists.Members.AUDIO_ID };
336 Cursor cursor = query(context, MediaStore.Audio.Playlists.Members.getContentUri("external", plid),
337 ccols, null, null, MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER);
338
339 if (cursor != null) {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700340 long [] list = getSongListForCursor(cursor);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800341 cursor.close();
342 return list;
343 }
344 return sEmptyList;
345 }
346
347 public static void playPlaylist(Context context, long plid) {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700348 long [] list = getSongListForPlaylist(context, plid);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800349 if (list != null) {
350 playAll(context, list, -1, false);
351 }
352 }
353
Marco Nelissenbd447b62009-06-29 14:52:05 -0700354 public static long [] getAllSongs(Context context) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800355 Cursor c = query(context, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
356 new String[] {MediaStore.Audio.Media._ID}, MediaStore.Audio.Media.IS_MUSIC + "=1",
357 null, null);
358 try {
359 if (c == null || c.getCount() == 0) {
360 return null;
361 }
362 int len = c.getCount();
Marco Nelissenbd447b62009-06-29 14:52:05 -0700363 long [] list = new long[len];
The Android Open Source Project792a2202009-03-03 19:32:30 -0800364 for (int i = 0; i < len; i++) {
365 c.moveToNext();
Marco Nelissenbd447b62009-06-29 14:52:05 -0700366 list[i] = c.getLong(0);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800367 }
368
369 return list;
370 } finally {
371 if (c != null) {
372 c.close();
373 }
374 }
375 }
376
377 /**
378 * Fills out the given submenu with items for "new playlist" and
379 * any existing playlists. When the user selects an item, the
380 * application will receive PLAYLIST_SELECTED with the Uri of
381 * the selected playlist, NEW_PLAYLIST if a new playlist
382 * should be created, and QUEUE if the "current playlist" was
383 * selected.
384 * @param context The context to use for creating the menu items
385 * @param sub The submenu to add the items to.
386 */
387 public static void makePlaylistMenu(Context context, SubMenu sub) {
388 String[] cols = new String[] {
389 MediaStore.Audio.Playlists._ID,
390 MediaStore.Audio.Playlists.NAME
391 };
392 ContentResolver resolver = context.getContentResolver();
393 if (resolver == null) {
394 System.out.println("resolver = null");
395 } else {
396 String whereclause = MediaStore.Audio.Playlists.NAME + " != ''";
397 Cursor cur = resolver.query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
398 cols, whereclause, null,
399 MediaStore.Audio.Playlists.NAME);
400 sub.clear();
401 sub.add(1, Defs.QUEUE, 0, R.string.queue);
402 sub.add(1, Defs.NEW_PLAYLIST, 0, R.string.new_playlist);
403 if (cur != null && cur.getCount() > 0) {
404 //sub.addSeparator(1, 0);
405 cur.moveToFirst();
406 while (! cur.isAfterLast()) {
407 Intent intent = new Intent();
Marco Nelissenbd447b62009-06-29 14:52:05 -0700408 intent.putExtra("playlist", cur.getLong(0));
The Android Open Source Project792a2202009-03-03 19:32:30 -0800409// if (cur.getInt(0) == mLastPlaylistSelected) {
410// sub.add(0, MusicBaseActivity.PLAYLIST_SELECTED, cur.getString(1)).setIntent(intent);
411// } else {
412 sub.add(1, Defs.PLAYLIST_SELECTED, 0, cur.getString(1)).setIntent(intent);
413// }
414 cur.moveToNext();
415 }
416 }
417 if (cur != null) {
418 cur.close();
419 }
420 }
421 }
422
423 public static void clearPlaylist(Context context, int plid) {
424
425 Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", plid);
426 context.getContentResolver().delete(uri, null, null);
427 return;
428 }
429
Marco Nelissenbd447b62009-06-29 14:52:05 -0700430 public static void deleteTracks(Context context, long [] list) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800431
432 String [] cols = new String [] { MediaStore.Audio.Media._ID,
433 MediaStore.Audio.Media.DATA, MediaStore.Audio.Media.ALBUM_ID };
434 StringBuilder where = new StringBuilder();
435 where.append(MediaStore.Audio.Media._ID + " IN (");
436 for (int i = 0; i < list.length; i++) {
437 where.append(list[i]);
438 if (i < list.length - 1) {
439 where.append(",");
440 }
441 }
442 where.append(")");
443 Cursor c = query(context, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, cols,
444 where.toString(), null, null);
445
446 if (c != null) {
447
448 // step 1: remove selected tracks from the current playlist, as well
449 // as from the album art cache
450 try {
451 c.moveToFirst();
452 while (! c.isAfterLast()) {
453 // remove from current playlist
Marco Nelissenbd447b62009-06-29 14:52:05 -0700454 long id = c.getLong(0);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800455 sService.removeTrack(id);
456 // remove from album art cache
Marco Nelissenbd447b62009-06-29 14:52:05 -0700457 long artIndex = c.getLong(2);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800458 synchronized(sArtCache) {
459 sArtCache.remove(artIndex);
460 }
461 c.moveToNext();
462 }
463 } catch (RemoteException ex) {
464 }
465
466 // step 2: remove selected tracks from the database
467 context.getContentResolver().delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, where.toString(), null);
468
469 // step 3: remove files from card
470 c.moveToFirst();
471 while (! c.isAfterLast()) {
472 String name = c.getString(1);
473 File f = new File(name);
474 try { // File.delete can throw a security exception
475 if (!f.delete()) {
476 // I'm not sure if we'd ever get here (deletion would
477 // have to fail, but no exception thrown)
478 Log.e("MusicUtils", "Failed to delete file " + name);
479 }
480 c.moveToNext();
481 } catch (SecurityException ex) {
482 c.moveToNext();
483 }
484 }
485 c.close();
486 }
487
488 String message = context.getResources().getQuantityString(
489 R.plurals.NNNtracksdeleted, list.length, Integer.valueOf(list.length));
490
491 Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
492 // We deleted a number of tracks, which could affect any number of things
493 // in the media content domain, so update everything.
494 context.getContentResolver().notifyChange(Uri.parse("content://media"), null);
495 }
496
Marco Nelissenbd447b62009-06-29 14:52:05 -0700497 public static void addToCurrentPlaylist(Context context, long [] list) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800498 if (sService == null) {
499 return;
500 }
501 try {
502 sService.enqueue(list, MediaPlaybackService.LAST);
503 String message = context.getResources().getQuantityString(
504 R.plurals.NNNtrackstoplaylist, list.length, Integer.valueOf(list.length));
505 Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
506 } catch (RemoteException ex) {
507 }
508 }
509
Marco Nelissenbd447b62009-06-29 14:52:05 -0700510 public static void addToPlaylist(Context context, long [] ids, long playlistid) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800511 if (ids == null) {
512 // this shouldn't happen (the menuitems shouldn't be visible
513 // unless the selected item represents something playable
514 Log.e("MusicBase", "ListSelection null");
515 } else {
516 int size = ids.length;
517 ContentValues values [] = new ContentValues[size];
518 ContentResolver resolver = context.getContentResolver();
519 // need to determine the number of items currently in the playlist,
520 // so the play_order field can be maintained.
521 String[] cols = new String[] {
522 "count(*)"
523 };
524 Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlistid);
525 Cursor cur = resolver.query(uri, cols, null, null, null);
526 cur.moveToFirst();
527 int base = cur.getInt(0);
528 cur.close();
529
530 for (int i = 0; i < size; i++) {
531 values[i] = new ContentValues();
532 values[i].put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, Integer.valueOf(base + i));
533 values[i].put(MediaStore.Audio.Playlists.Members.AUDIO_ID, ids[i]);
534 }
535 resolver.bulkInsert(uri, values);
536 String message = context.getResources().getQuantityString(
537 R.plurals.NNNtrackstoplaylist, size, Integer.valueOf(size));
538 Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
539 //mLastPlaylistSelected = playlistid;
540 }
541 }
542
543 public static Cursor query(Context context, Uri uri, String[] projection,
Marco Nelissen4248ed22009-07-30 08:28:49 -0700544 String selection, String[] selectionArgs, String sortOrder, int limit) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800545 try {
546 ContentResolver resolver = context.getContentResolver();
547 if (resolver == null) {
548 return null;
549 }
Marco Nelissen4248ed22009-07-30 08:28:49 -0700550 if (limit > 0) {
551 uri = uri.buildUpon().appendQueryParameter("limit", "" + limit).build();
552 }
The Android Open Source Project792a2202009-03-03 19:32:30 -0800553 return resolver.query(uri, projection, selection, selectionArgs, sortOrder);
554 } catch (UnsupportedOperationException ex) {
555 return null;
556 }
557
558 }
Marco Nelissen4248ed22009-07-30 08:28:49 -0700559 public static Cursor query(Context context, Uri uri, String[] projection,
560 String selection, String[] selectionArgs, String sortOrder) {
561 return query(context, uri, projection, selection, selectionArgs, sortOrder, 0);
562 }
The Android Open Source Project792a2202009-03-03 19:32:30 -0800563
564 public static boolean isMediaScannerScanning(Context context) {
565 boolean result = false;
566 Cursor cursor = query(context, MediaStore.getMediaScannerUri(),
567 new String [] { MediaStore.MEDIA_SCANNER_VOLUME }, null, null, null);
568 if (cursor != null) {
569 if (cursor.getCount() == 1) {
570 cursor.moveToFirst();
571 result = "external".equals(cursor.getString(0));
572 }
573 cursor.close();
574 }
575
576 return result;
577 }
578
579 public static void setSpinnerState(Activity a) {
580 if (isMediaScannerScanning(a)) {
581 // start the progress spinner
582 a.getWindow().setFeatureInt(
583 Window.FEATURE_INDETERMINATE_PROGRESS,
584 Window.PROGRESS_INDETERMINATE_ON);
585
586 a.getWindow().setFeatureInt(
587 Window.FEATURE_INDETERMINATE_PROGRESS,
588 Window.PROGRESS_VISIBILITY_ON);
589 } else {
590 // stop the progress spinner
591 a.getWindow().setFeatureInt(
592 Window.FEATURE_INDETERMINATE_PROGRESS,
593 Window.PROGRESS_VISIBILITY_OFF);
594 }
595 }
596
Marco Nelissen7a052982009-09-09 10:34:11 -0700597 private static String mLastSdStatus;
598
The Android Open Source Project792a2202009-03-03 19:32:30 -0800599 public static void displayDatabaseError(Activity a) {
600 String status = Environment.getExternalStorageState();
601 int title = R.string.sdcard_error_title;
602 int message = R.string.sdcard_error_message;
603
604 if (status.equals(Environment.MEDIA_SHARED) ||
605 status.equals(Environment.MEDIA_UNMOUNTED)) {
606 title = R.string.sdcard_busy_title;
607 message = R.string.sdcard_busy_message;
608 } else if (status.equals(Environment.MEDIA_REMOVED)) {
609 title = R.string.sdcard_missing_title;
610 message = R.string.sdcard_missing_message;
611 } else if (status.equals(Environment.MEDIA_MOUNTED)){
612 // The card is mounted, but we didn't get a valid cursor.
613 // This probably means the mediascanner hasn't started scanning the
614 // card yet (there is a small window of time during boot where this
615 // will happen).
616 a.setTitle("");
617 Intent intent = new Intent();
618 intent.setClass(a, ScanningProgress.class);
619 a.startActivityForResult(intent, Defs.SCAN_DONE);
Marco Nelissen7a052982009-09-09 10:34:11 -0700620 } else if (!TextUtils.equals(mLastSdStatus, status)) {
621 mLastSdStatus = status;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800622 Log.d(TAG, "sd card: " + status);
623 }
624
625 a.setTitle(title);
626 View v = a.findViewById(R.id.sd_message);
627 if (v != null) {
628 v.setVisibility(View.VISIBLE);
629 }
630 v = a.findViewById(R.id.sd_icon);
631 if (v != null) {
632 v.setVisibility(View.VISIBLE);
633 }
634 v = a.findViewById(android.R.id.list);
635 if (v != null) {
636 v.setVisibility(View.GONE);
637 }
Marco Nelissen9e0f1fd2009-12-12 13:41:34 -0800638 v = a.findViewById(R.id.buttonbar);
639 if (v != null) {
640 v.setVisibility(View.GONE);
641 }
The Android Open Source Project792a2202009-03-03 19:32:30 -0800642 TextView tv = (TextView) a.findViewById(R.id.sd_message);
643 tv.setText(message);
644 }
645
646 public static void hideDatabaseError(Activity a) {
647 View v = a.findViewById(R.id.sd_message);
648 if (v != null) {
649 v.setVisibility(View.GONE);
650 }
651 v = a.findViewById(R.id.sd_icon);
652 if (v != null) {
653 v.setVisibility(View.GONE);
654 }
655 v = a.findViewById(android.R.id.list);
656 if (v != null) {
657 v.setVisibility(View.VISIBLE);
658 }
659 }
660
661 static protected Uri getContentURIForPath(String path) {
662 return Uri.fromFile(new File(path));
663 }
664
665
666 /* Try to use String.format() as little as possible, because it creates a
667 * new Formatter every time you call it, which is very inefficient.
668 * Reusing an existing Formatter more than tripled the speed of
669 * makeTimeString().
670 * This Formatter/StringBuilder are also used by makeAlbumSongsLabel()
671 */
672 private static StringBuilder sFormatBuilder = new StringBuilder();
673 private static Formatter sFormatter = new Formatter(sFormatBuilder, Locale.getDefault());
674 private static final Object[] sTimeArgs = new Object[5];
675
676 public static String makeTimeString(Context context, long secs) {
Marco Nelissen1e5a5672009-09-02 11:09:15 -0700677 String durationformat = context.getString(
678 secs < 3600 ? R.string.durationformatshort : R.string.durationformatlong);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800679
680 /* Provide multiple arguments so the format can be changed easily
681 * by modifying the xml.
682 */
683 sFormatBuilder.setLength(0);
684
685 final Object[] timeArgs = sTimeArgs;
686 timeArgs[0] = secs / 3600;
687 timeArgs[1] = secs / 60;
688 timeArgs[2] = (secs / 60) % 60;
689 timeArgs[3] = secs;
690 timeArgs[4] = secs % 60;
691
692 return sFormatter.format(durationformat, timeArgs).toString();
693 }
694
695 public static void shuffleAll(Context context, Cursor cursor) {
696 playAll(context, cursor, 0, true);
697 }
698
699 public static void playAll(Context context, Cursor cursor) {
700 playAll(context, cursor, 0, false);
701 }
702
703 public static void playAll(Context context, Cursor cursor, int position) {
704 playAll(context, cursor, position, false);
705 }
706
Marco Nelissenbd447b62009-06-29 14:52:05 -0700707 public static void playAll(Context context, long [] list, int position) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800708 playAll(context, list, position, false);
709 }
710
711 private static void playAll(Context context, Cursor cursor, int position, boolean force_shuffle) {
712
Marco Nelissenbd447b62009-06-29 14:52:05 -0700713 long [] list = getSongListForCursor(cursor);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800714 playAll(context, list, position, force_shuffle);
715 }
716
Marco Nelissenbd447b62009-06-29 14:52:05 -0700717 private static void playAll(Context context, long [] list, int position, boolean force_shuffle) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800718 if (list.length == 0 || sService == null) {
719 Log.d("MusicUtils", "attempt to play empty song list");
720 // Don't try to play empty playlists. Nothing good will come of it.
721 String message = context.getString(R.string.emptyplaylist, list.length);
722 Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
723 return;
724 }
725 try {
726 if (force_shuffle) {
727 sService.setShuffleMode(MediaPlaybackService.SHUFFLE_NORMAL);
728 }
Marco Nelissenbd447b62009-06-29 14:52:05 -0700729 long curid = sService.getAudioId();
The Android Open Source Project792a2202009-03-03 19:32:30 -0800730 int curpos = sService.getQueuePosition();
731 if (position != -1 && curpos == position && curid == list[position]) {
732 // The selected file is the file that's currently playing;
733 // figure out if we need to restart with a new playlist,
734 // or just launch the playback activity.
Marco Nelissenbd447b62009-06-29 14:52:05 -0700735 long [] playlist = sService.getQueue();
The Android Open Source Project792a2202009-03-03 19:32:30 -0800736 if (Arrays.equals(list, playlist)) {
737 // we don't need to set a new list, but we should resume playback if needed
738 sService.play();
739 return; // the 'finally' block will still run
740 }
741 }
742 if (position < 0) {
743 position = 0;
744 }
745 sService.open(list, force_shuffle ? -1 : position);
746 sService.play();
747 } catch (RemoteException ex) {
748 } finally {
Marco Nelissenec0c57a2009-12-12 12:27:11 -0800749 Intent intent = new Intent(context, MediaPlaybackActivity.class)
The Android Open Source Project792a2202009-03-03 19:32:30 -0800750 .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
751 context.startActivity(intent);
752 }
753 }
754
755 public static void clearQueue() {
756 try {
757 sService.removeTracks(0, Integer.MAX_VALUE);
758 } catch (RemoteException ex) {
759 }
760 }
761
762 // A really simple BitmapDrawable-like class, that doesn't do
763 // scaling, dithering or filtering.
764 private static class FastBitmapDrawable extends Drawable {
765 private Bitmap mBitmap;
766 public FastBitmapDrawable(Bitmap b) {
767 mBitmap = b;
768 }
769 @Override
770 public void draw(Canvas canvas) {
771 canvas.drawBitmap(mBitmap, 0, 0, null);
772 }
773 @Override
774 public int getOpacity() {
775 return PixelFormat.OPAQUE;
776 }
777 @Override
778 public void setAlpha(int alpha) {
779 }
780 @Override
781 public void setColorFilter(ColorFilter cf) {
782 }
783 }
784
785 private static int sArtId = -2;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800786 private static Bitmap mCachedBit = null;
787 private static final BitmapFactory.Options sBitmapOptionsCache = new BitmapFactory.Options();
788 private static final BitmapFactory.Options sBitmapOptions = new BitmapFactory.Options();
789 private static final Uri sArtworkUri = Uri.parse("content://media/external/audio/albumart");
Marco Nelissenbd447b62009-06-29 14:52:05 -0700790 private static final HashMap<Long, Drawable> sArtCache = new HashMap<Long, Drawable>();
The Android Open Source Project792a2202009-03-03 19:32:30 -0800791 private static int sArtCacheId = -1;
792
793 static {
794 // for the cache,
795 // 565 is faster to decode and display
796 // and we don't want to dither here because the image will be scaled down later
797 sBitmapOptionsCache.inPreferredConfig = Bitmap.Config.RGB_565;
798 sBitmapOptionsCache.inDither = false;
799
800 sBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
801 sBitmapOptions.inDither = false;
802 }
803
804 public static void initAlbumArtCache() {
805 try {
806 int id = sService.getMediaMountedCount();
807 if (id != sArtCacheId) {
808 clearAlbumArtCache();
809 sArtCacheId = id;
810 }
811 } catch (RemoteException e) {
812 e.printStackTrace();
813 }
814 }
815
816 public static void clearAlbumArtCache() {
817 synchronized(sArtCache) {
818 sArtCache.clear();
819 }
820 }
821
Marco Nelissenbd447b62009-06-29 14:52:05 -0700822 public static Drawable getCachedArtwork(Context context, long artIndex, BitmapDrawable defaultArtwork) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800823 Drawable d = null;
824 synchronized(sArtCache) {
825 d = sArtCache.get(artIndex);
826 }
827 if (d == null) {
828 d = defaultArtwork;
829 final Bitmap icon = defaultArtwork.getBitmap();
830 int w = icon.getWidth();
831 int h = icon.getHeight();
832 Bitmap b = MusicUtils.getArtworkQuick(context, artIndex, w, h);
833 if (b != null) {
834 d = new FastBitmapDrawable(b);
835 synchronized(sArtCache) {
836 // the cache may have changed since we checked
837 Drawable value = sArtCache.get(artIndex);
838 if (value == null) {
839 sArtCache.put(artIndex, d);
840 } else {
841 d = value;
842 }
843 }
844 }
845 }
846 return d;
847 }
848
849 // Get album art for specified album. This method will not try to
850 // fall back to getting artwork directly from the file, nor will
851 // it attempt to repair the database.
Marco Nelissenbd447b62009-06-29 14:52:05 -0700852 private static Bitmap getArtworkQuick(Context context, long album_id, int w, int h) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800853 // NOTE: There is in fact a 1 pixel border on the right side in the ImageView
854 // used to display this drawable. Take it into account now, so we don't have to
855 // scale later.
856 w -= 1;
857 ContentResolver res = context.getContentResolver();
858 Uri uri = ContentUris.withAppendedId(sArtworkUri, album_id);
859 if (uri != null) {
860 ParcelFileDescriptor fd = null;
861 try {
862 fd = res.openFileDescriptor(uri, "r");
863 int sampleSize = 1;
864
865 // Compute the closest power-of-two scale factor
866 // and pass that to sBitmapOptionsCache.inSampleSize, which will
867 // result in faster decoding and better quality
868 sBitmapOptionsCache.inJustDecodeBounds = true;
869 BitmapFactory.decodeFileDescriptor(
870 fd.getFileDescriptor(), null, sBitmapOptionsCache);
871 int nextWidth = sBitmapOptionsCache.outWidth >> 1;
872 int nextHeight = sBitmapOptionsCache.outHeight >> 1;
873 while (nextWidth>w && nextHeight>h) {
874 sampleSize <<= 1;
875 nextWidth >>= 1;
876 nextHeight >>= 1;
877 }
878
879 sBitmapOptionsCache.inSampleSize = sampleSize;
880 sBitmapOptionsCache.inJustDecodeBounds = false;
881 Bitmap b = BitmapFactory.decodeFileDescriptor(
882 fd.getFileDescriptor(), null, sBitmapOptionsCache);
883
884 if (b != null) {
885 // finally rescale to exactly the size we need
886 if (sBitmapOptionsCache.outWidth != w || sBitmapOptionsCache.outHeight != h) {
887 Bitmap tmp = Bitmap.createScaledBitmap(b, w, h, true);
888 // Bitmap.createScaledBitmap() can return the same bitmap
889 if (tmp != b) b.recycle();
890 b = tmp;
891 }
892 }
893
894 return b;
895 } catch (FileNotFoundException e) {
896 } finally {
897 try {
898 if (fd != null)
899 fd.close();
900 } catch (IOException e) {
901 }
902 }
903 }
904 return null;
905 }
906
907 /** Get album art for specified album. You should not pass in the album id
908 * for the "unknown" album here (use -1 instead)
909 */
Marco Nelissenbd447b62009-06-29 14:52:05 -0700910 public static Bitmap getArtwork(Context context, long song_id, long album_id) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800911
912 if (album_id < 0) {
913 // This is something that is not in the database, so get the album art directly
914 // from the file.
Marco Nelissen7a16cc72009-06-16 08:54:17 -0700915 if (song_id >= 0) {
916 Bitmap bm = getArtworkFromFile(context, song_id, -1);
917 if (bm != null) {
918 return bm;
919 }
The Android Open Source Project792a2202009-03-03 19:32:30 -0800920 }
Marco Nelissen7a16cc72009-06-16 08:54:17 -0700921 return getDefaultArtwork(context);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800922 }
923
924 ContentResolver res = context.getContentResolver();
925 Uri uri = ContentUris.withAppendedId(sArtworkUri, album_id);
926 if (uri != null) {
927 InputStream in = null;
928 try {
929 in = res.openInputStream(uri);
930 return BitmapFactory.decodeStream(in, null, sBitmapOptions);
931 } catch (FileNotFoundException ex) {
932 // The album art thumbnail does not actually exist. Maybe the user deleted it, or
933 // maybe it never existed to begin with.
Marco Nelissen7a16cc72009-06-16 08:54:17 -0700934 Bitmap bm = getArtworkFromFile(context, song_id, album_id);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800935 if (bm != null) {
Marco Nelissen756c3f52009-05-14 10:07:23 -0700936 if (bm.getConfig() == null) {
937 bm = bm.copy(Bitmap.Config.RGB_565, false);
938 if (bm == null) {
Marco Nelissen7a16cc72009-06-16 08:54:17 -0700939 return getDefaultArtwork(context);
Marco Nelissen756c3f52009-05-14 10:07:23 -0700940 }
941 }
The Android Open Source Project792a2202009-03-03 19:32:30 -0800942 } else {
Marco Nelissen7a16cc72009-06-16 08:54:17 -0700943 bm = getDefaultArtwork(context);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800944 }
945 return bm;
946 } finally {
947 try {
948 if (in != null) {
949 in.close();
950 }
951 } catch (IOException ex) {
952 }
953 }
954 }
955
956 return null;
957 }
The Android Open Source Project792a2202009-03-03 19:32:30 -0800958
959 // get album art for specified file
960 private static final String sExternalMediaUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI.toString();
Marco Nelissenbd447b62009-06-29 14:52:05 -0700961 private static Bitmap getArtworkFromFile(Context context, long songid, long albumid) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800962 Bitmap bm = null;
963 byte [] art = null;
964 String path = null;
965
Marco Nelissen7a16cc72009-06-16 08:54:17 -0700966 if (albumid < 0 && songid < 0) {
967 throw new IllegalArgumentException("Must specify an album or a song id");
The Android Open Source Project792a2202009-03-03 19:32:30 -0800968 }
Marco Nelissen7a16cc72009-06-16 08:54:17 -0700969
970 try {
971 if (albumid < 0) {
972 Uri uri = Uri.parse("content://media/external/audio/media/" + songid + "/albumart");
973 ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "r");
974 if (pfd != null) {
975 FileDescriptor fd = pfd.getFileDescriptor();
976 bm = BitmapFactory.decodeFileDescriptor(fd);
977 }
978 } else {
979 Uri uri = ContentUris.withAppendedId(sArtworkUri, albumid);
980 ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "r");
981 if (pfd != null) {
982 FileDescriptor fd = pfd.getFileDescriptor();
983 bm = BitmapFactory.decodeFileDescriptor(fd);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800984 }
985 }
Marco Nelissen7a16cc72009-06-16 08:54:17 -0700986 } catch (FileNotFoundException ex) {
987 //
The Android Open Source Project792a2202009-03-03 19:32:30 -0800988 }
Marco Nelissen7a16cc72009-06-16 08:54:17 -0700989 if (bm != null) {
990 mCachedBit = bm;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800991 }
992 return bm;
993 }
994
995 private static Bitmap getDefaultArtwork(Context context) {
996 BitmapFactory.Options opts = new BitmapFactory.Options();
997 opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
998 return BitmapFactory.decodeStream(
999 context.getResources().openRawResource(R.drawable.albumart_mp_unknown), null, opts);
1000 }
1001
1002 static int getIntPref(Context context, String name, int def) {
1003 SharedPreferences prefs =
1004 context.getSharedPreferences("com.android.music", Context.MODE_PRIVATE);
1005 return prefs.getInt(name, def);
1006 }
1007
1008 static void setIntPref(Context context, String name, int value) {
1009 SharedPreferences prefs =
1010 context.getSharedPreferences("com.android.music", Context.MODE_PRIVATE);
1011 Editor ed = prefs.edit();
1012 ed.putInt(name, value);
1013 ed.commit();
1014 }
1015
1016 static void setRingtone(Context context, long id) {
1017 ContentResolver resolver = context.getContentResolver();
1018 // Set the flag in the database to mark this as a ringtone
1019 Uri ringUri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id);
1020 try {
1021 ContentValues values = new ContentValues(2);
1022 values.put(MediaStore.Audio.Media.IS_RINGTONE, "1");
1023 values.put(MediaStore.Audio.Media.IS_ALARM, "1");
1024 resolver.update(ringUri, values, null, null);
1025 } catch (UnsupportedOperationException ex) {
1026 // most likely the card just got unmounted
1027 Log.e(TAG, "couldn't set ringtone flag for id " + id);
1028 return;
1029 }
1030
1031 String[] cols = new String[] {
1032 MediaStore.Audio.Media._ID,
1033 MediaStore.Audio.Media.DATA,
1034 MediaStore.Audio.Media.TITLE
1035 };
1036
1037 String where = MediaStore.Audio.Media._ID + "=" + id;
1038 Cursor cursor = query(context, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
1039 cols, where , null, null);
1040 try {
1041 if (cursor != null && cursor.getCount() == 1) {
1042 // Set the system setting to make this the current ringtone
1043 cursor.moveToFirst();
1044 Settings.System.putString(resolver, Settings.System.RINGTONE, ringUri.toString());
1045 String message = context.getString(R.string.ringtone_set, cursor.getString(2));
1046 Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
1047 }
1048 } finally {
1049 if (cursor != null) {
1050 cursor.close();
1051 }
1052 }
1053 }
Marco Nelissenec0c57a2009-12-12 12:27:11 -08001054
1055 static int sActiveTabIndex = -1;
1056
Marco Nelissen19cea9e2009-12-14 15:59:30 -08001057 static boolean updateButtonBar(Activity a, int highlight) {
Marco Nelissenec0c57a2009-12-12 12:27:11 -08001058 final TabWidget ll = (TabWidget) a.findViewById(R.id.buttonbar);
1059 boolean withtabs = false;
1060 Intent intent = a.getIntent();
1061 if (intent != null) {
1062 withtabs = intent.getBooleanExtra("withtabs", false);
1063 }
1064
1065 if (highlight == 0 || !withtabs) {
1066 ll.setVisibility(View.GONE);
Marco Nelissen19cea9e2009-12-14 15:59:30 -08001067 return withtabs;
Marco Nelissenec0c57a2009-12-12 12:27:11 -08001068 } else if (withtabs) {
1069 ll.setVisibility(View.VISIBLE);
1070 }
1071 for (int i = ll.getChildCount() - 1; i >= 0; i--) {
1072
1073 View v = ll.getChildAt(i);
1074 boolean isActive = (v.getId() == highlight);
1075 if (isActive) {
1076 ll.setCurrentTab(i);
1077 sActiveTabIndex = i;
1078 }
Jeff Hamilton333bf192009-12-14 00:48:30 -06001079 v.setTag(i);
Marco Nelissenec0c57a2009-12-12 12:27:11 -08001080 v.setOnFocusChangeListener(new View.OnFocusChangeListener() {
1081
Marco Nelissenec0c57a2009-12-12 12:27:11 -08001082 public void onFocusChange(View v, boolean hasFocus) {
1083 if (hasFocus) {
1084 for (int i = 0; i < ll.getTabCount(); i++) {
1085 if (ll.getChildTabViewAt(i) == v) {
1086 ll.setCurrentTab(i);
1087 processTabClick((Activity)ll.getContext(), v, ll.getChildAt(sActiveTabIndex).getId());
1088 break;
1089 }
1090 }
1091 }
1092 }});
1093
1094 v.setOnClickListener(new View.OnClickListener() {
1095
Marco Nelissenec0c57a2009-12-12 12:27:11 -08001096 public void onClick(View v) {
1097 processTabClick((Activity)ll.getContext(), v, ll.getChildAt(sActiveTabIndex).getId());
Marco Nelissenec0c57a2009-12-12 12:27:11 -08001098 }});
1099 }
Marco Nelissen19cea9e2009-12-14 15:59:30 -08001100 return withtabs;
Marco Nelissenec0c57a2009-12-12 12:27:11 -08001101 }
1102
1103 static void processTabClick(Activity a, View v, int current) {
1104 int id = v.getId();
1105 if (id == current) {
1106 return;
1107 }
Jeff Hamilton333bf192009-12-14 00:48:30 -06001108
1109 final TabWidget ll = (TabWidget) a.findViewById(R.id.buttonbar);
1110 ll.setCurrentTab((Integer) v.getTag());
1111
Marco Nelissenec0c57a2009-12-12 12:27:11 -08001112 activateTab(a, id);
1113 if (id != R.id.nowplayingtab) {
1114 setIntPref(a, "activetab", id);
1115 }
1116 }
1117
1118 static void activateTab(Activity a, int id) {
1119 Intent intent = new Intent(Intent.ACTION_PICK);
1120 switch (id) {
1121 case R.id.artisttab:
1122 intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/artistalbum");
1123 break;
1124 case R.id.albumtab:
1125 intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/album");
1126 break;
1127 case R.id.songtab:
1128 intent.setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track");
1129 break;
1130 case R.id.playlisttab:
1131 intent.setDataAndType(Uri.EMPTY, MediaStore.Audio.Playlists.CONTENT_TYPE);
1132 break;
1133 case R.id.nowplayingtab:
1134 intent = new Intent(a, MediaPlaybackActivity.class);
1135 a.startActivity(intent);
1136 // fall through and return
1137 default:
1138 return;
1139 }
1140 intent.putExtra("withtabs", true);
1141 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
1142 a.startActivity(intent);
1143 a.finish();
1144 a.overridePendingTransition(0, 0);
1145 }
1146
1147 static void updateNowPlaying(Activity a) {
1148 View nowPlayingView = a.findViewById(R.id.nowplaying);
1149 if (nowPlayingView == null) {
1150 return;
1151 }
1152 try {
1153 boolean withtabs = false;
1154 Intent intent = a.getIntent();
1155 if (intent != null) {
1156 withtabs = intent.getBooleanExtra("withtabs", false);
1157 }
1158 if (true && MusicUtils.sService != null && MusicUtils.sService.getAudioId() != -1) {
1159 TextView title = (TextView) nowPlayingView.findViewById(R.id.title);
1160 TextView artist = (TextView) nowPlayingView.findViewById(R.id.artist);
1161 title.setText(MusicUtils.sService.getTrackName());
1162 String artistName = MusicUtils.sService.getArtistName();
1163 if (MediaFile.UNKNOWN_STRING.equals(artistName)) {
1164 artistName = a.getString(R.string.unknown_artist_name);
1165 }
1166 artist.setText(artistName);
1167 //mNowPlayingView.setOnFocusChangeListener(mFocuser);
1168 //mNowPlayingView.setOnClickListener(this);
1169 nowPlayingView.setVisibility(View.VISIBLE);
1170 nowPlayingView.setOnClickListener(new View.OnClickListener() {
1171
Marco Nelissenec0c57a2009-12-12 12:27:11 -08001172 public void onClick(View v) {
1173 Context c = v.getContext();
1174 c.startActivity(new Intent(c, MediaPlaybackActivity.class));
1175 }});
1176 return;
1177 }
1178 } catch (RemoteException ex) {
1179 }
1180 nowPlayingView.setVisibility(View.GONE);
1181 }
1182
The Android Open Source Project792a2202009-03-03 19:32:30 -08001183}