/* * Copyright (C) 2012 Andrew Neal Licensed under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law * or agreed to in writing, software distributed under the License is * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package com.andrew.apollo.utils; import java.io.File; import java.util.Arrays; import java.util.WeakHashMap; import android.app.Activity; import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; import android.content.ServiceConnection; import android.database.Cursor; import android.net.Uri; import android.os.IBinder; import android.os.RemoteException; import android.provider.BaseColumns; import android.provider.MediaStore; import android.provider.MediaStore.Audio.AlbumColumns; import android.provider.MediaStore.Audio.ArtistColumns; import android.provider.MediaStore.Audio.AudioColumns; import android.provider.MediaStore.Audio.Playlists; import android.provider.MediaStore.Audio.PlaylistsColumns; import android.provider.MediaStore.MediaColumns; import android.provider.Settings; import android.util.Log; import com.andrew.apollo.IApolloService; import com.andrew.apollo.MusicPlaybackService; //import com.andrew.apollo.R; //import com.andrew.apollo.loaders.FavoritesLoader; //import com.andrew.apollo.loaders.LastAddedLoader; //import com.andrew.apollo.loaders.PlaylistLoader; //import com.andrew.apollo.loaders.SongLoader; //import com.andrew.apollo.menu.FragmentMenuItems; import com.andrew.apollo.provider.FavoritesStore; import com.andrew.apollo.provider.FavoritesStore.FavoriteColumns; import com.andrew.apollo.provider.RecentStore; //import com.devspark.appmsg.AppMsg; import com.bt.download.android.R; /** * A collection of helpers directly related to music or Apollo's service. * * @author Andrew Neal (andrewdneal@gmail.com) */ public final class MusicUtils { public static IApolloService mService = null; private static int sForegroundActivities = 0; private static final WeakHashMap<Context, ServiceBinder> mConnectionMap; private static final long[] sEmptyList; private static ContentValues[] mContentValuesCache = null; static { mConnectionMap = new WeakHashMap<Context, ServiceBinder>(); sEmptyList = new long[0]; } /* This class is never initiated */ public MusicUtils() { } /** * @param context The {@link Context} to use * @param callback The {@link ServiceConnection} to use * @return The new instance of {@link ServiceToken} */ public static final ServiceToken bindToService(final Context context, final ServiceConnection callback) { Activity realActivity = ((Activity)context).getParent(); if (realActivity == null) { realActivity = (Activity)context; } final ContextWrapper contextWrapper = new ContextWrapper(realActivity); contextWrapper.startService(new Intent(contextWrapper, MusicPlaybackService.class)); final ServiceBinder binder = new ServiceBinder(callback); if (contextWrapper.bindService( new Intent().setClass(contextWrapper, MusicPlaybackService.class), binder, 0)) { mConnectionMap.put(contextWrapper, binder); return new ServiceToken(contextWrapper); } return null; } /** * @param token The {@link ServiceToken} to unbind from */ public static void unbindFromService(final ServiceToken token) { if (token == null) { return; } final ContextWrapper mContextWrapper = token.mWrappedContext; final ServiceBinder mBinder = mConnectionMap.remove(mContextWrapper); if (mBinder == null) { return; } mContextWrapper.unbindService(mBinder); if (mConnectionMap.isEmpty()) { mService = null; } } public static final class ServiceBinder implements ServiceConnection { private final ServiceConnection mCallback; /** * Constructor of <code>ServiceBinder</code> * * @param context The {@link ServiceConnection} to use */ public ServiceBinder(final ServiceConnection callback) { mCallback = callback; } @Override public void onServiceConnected(final ComponentName className, final IBinder service) { mService = IApolloService.Stub.asInterface(service); if (mCallback != null) { mCallback.onServiceConnected(className, service); } } @Override public void onServiceDisconnected(final ComponentName className) { if (mCallback != null) { mCallback.onServiceDisconnected(className); } mService = null; } } public static final class ServiceToken { public ContextWrapper mWrappedContext; /** * Constructor of <code>ServiceToken</code> * * @param context The {@link ContextWrapper} to use */ public ServiceToken(final ContextWrapper context) { mWrappedContext = context; } } /** * Used to make number of labels for the number of artists, albums, songs, * genres, and playlists. * * @param context The {@link Context} to use. * @param pluralInt The ID of the plural string to use. * @param number The number of artists, albums, songs, genres, or playlists. * @return A {@link String} used as a label for the number of artists, * albums, songs, genres, and playlists. */ public static final String makeLabel(final Context context, final int pluralInt, final int number) { return context.getResources().getQuantityString(pluralInt, number, number); } /** * * Used to create a formatted time string for the duration of tracks. * * @param context The {@link Context} to use. * @param secs The track in seconds. * @return Duration of a track that's properly formatted. */ public static final String makeTimeString(final Context context, long secs) { long hours, mins; hours = secs / 3600; secs -= hours * 3600; mins = secs / 60; secs -= mins * 60; final String durationFormat = context.getResources().getString( hours == 0 ? R.string.durationformatshort : R.string.durationformatlong); return String.format(durationFormat, hours, mins, secs); } /** * Changes to the next track */ public static void next() { try { if (mService != null) { mService.next(); } } catch (final RemoteException ignored) { } } /** * Changes to the previous track. * * @NOTE The AIDL isn't used here in order to properly use the previous * action. When the user is shuffling, because {@link * MusicPlaybackService.#openCurrentAndNext()} is used, the user won't * be able to travel to the previously skipped track. To remedy this, * {@link MusicPlaybackService.#openCurrent()} is called in {@link * MusicPlaybackService.#prev()}. {@code #startService(Intent intent)} * is called here to specifically invoke the onStartCommand used by * {@link MusicPlaybackService}, which states if the current position * less than 2000 ms, start the track over, otherwise move to the * previously listened track. */ public static void previous(final Context context) { final Intent previous = new Intent(context, MusicPlaybackService.class); previous.setAction(MusicPlaybackService.PREVIOUS_ACTION); context.startService(previous); } /** * Plays or pauses the music. */ public static void playOrPause() { try { if (mService != null) { if (mService.isPlaying()) { mService.pause(); } else { mService.play(); } } } catch (final Exception ignored) { } } /** * Cycles through the repeat options. */ public static void cycleRepeat() { try { if (mService != null) { switch (mService.getRepeatMode()) { case MusicPlaybackService.REPEAT_NONE: mService.setRepeatMode(MusicPlaybackService.REPEAT_ALL); break; case MusicPlaybackService.REPEAT_ALL: mService.setRepeatMode(MusicPlaybackService.REPEAT_CURRENT); if (mService.getShuffleMode() != MusicPlaybackService.SHUFFLE_NONE) { mService.setShuffleMode(MusicPlaybackService.SHUFFLE_NONE); } break; default: mService.setRepeatMode(MusicPlaybackService.REPEAT_NONE); break; } } } catch (final RemoteException ignored) { } } /** * Cycles through the shuffle options. */ public static void cycleShuffle() { try { if (mService != null) { switch (mService.getShuffleMode()) { case MusicPlaybackService.SHUFFLE_NONE: mService.setShuffleMode(MusicPlaybackService.SHUFFLE_NORMAL); if (mService.getRepeatMode() == MusicPlaybackService.REPEAT_CURRENT) { mService.setRepeatMode(MusicPlaybackService.REPEAT_ALL); } break; case MusicPlaybackService.SHUFFLE_NORMAL: mService.setShuffleMode(MusicPlaybackService.SHUFFLE_NONE); break; case MusicPlaybackService.SHUFFLE_AUTO: mService.setShuffleMode(MusicPlaybackService.SHUFFLE_NONE); break; default: break; } } } catch (final RemoteException ignored) { } } /** * @return True if we're playing music, false otherwise. */ public static final boolean isPlaying() { if (mService != null) { try { return mService.isPlaying(); } catch (final RemoteException ignored) { } } return false; } /** * @return The current shuffle mode. */ public static final int getShuffleMode() { if (mService != null) { try { return mService.getShuffleMode(); } catch (final RemoteException ignored) { } } return 0; } /** * @return The current repeat mode. */ public static final int getRepeatMode() { if (mService != null) { try { return mService.getRepeatMode(); } catch (final RemoteException ignored) { } } return 0; } /** * @return The current track name. */ public static final String getTrackName() { if (mService != null) { try { return mService.getTrackName(); } catch (final RemoteException ignored) { } } return null; } /** * @return The current artist name. */ public static final String getArtistName() { if (mService != null) { try { return mService.getArtistName(); } catch (final RemoteException ignored) { } } return null; } /** * @return The current album name. */ public static final String getAlbumName() { if (mService != null) { try { return mService.getAlbumName(); } catch (final RemoteException ignored) { } } return null; } /** * @return The current album Id. */ public static final long getCurrentAlbumId() { if (mService != null) { try { return mService.getAlbumId(); } catch (final RemoteException ignored) { } } return -1; } /** * @return The current song Id. */ public static final long getCurrentAudioId() { if (mService != null) { try { return mService.getAudioId(); } catch (final RemoteException ignored) { } } return -1; } /** * @return The current artist Id. */ public static final long getCurrentArtistId() { if (mService != null) { try { return mService.getArtistId(); } catch (final RemoteException ignored) { } } return -1; } /** * @return The audio session Id. */ public static final int getAudioSessionId() { if (mService != null) { try { return mService.getAudioSessionId(); } catch (final RemoteException ignored) { } } return -1; } /** * @return The queue. */ public static final long[] getQueue() { try { if (mService != null) { return mService.getQueue(); } else { } } catch (final RemoteException ignored) { } return sEmptyList; } /** * @param id The ID of the track to remove. * @return removes track from a playlist or the queue. */ public static final int removeTrack(final long id) { try { if (mService != null) { return mService.removeTrack(id); } } catch (final RemoteException ingored) { } return 0; } /** * @return The position of the current track in the queue. */ public static final int getQueuePosition() { try { if (mService != null) { return mService.getQueuePosition(); } } catch (final RemoteException ignored) { } return 0; } /** * @param cursor The {@link Cursor} used to perform our query. * @return The song list for a MIME type. */ public static final long[] getSongListForCursor(Cursor cursor) { if (cursor == null) { return sEmptyList; } final int len = cursor.getCount(); final long[] list = new long[len]; cursor.moveToFirst(); int columnIndex = -1; try { columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.Members.AUDIO_ID); } catch (final IllegalArgumentException notaplaylist) { columnIndex = cursor.getColumnIndexOrThrow(BaseColumns._ID); } for (int i = 0; i < len; i++) { list[i] = cursor.getLong(columnIndex); cursor.moveToNext(); } cursor.close(); cursor = null; return list; } /** * @param context The {@link Context} to use. * @param id The ID of the artist. * @return The song list for an artist. */ public static final long[] getSongListForArtist(final Context context, final long id) { final String[] projection = new String[] { BaseColumns._ID }; final String selection = AudioColumns.ARTIST_ID + "=" + id + " AND " + AudioColumns.IS_MUSIC + "=1"; Cursor cursor = context.getContentResolver().query( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection, null, AudioColumns.ALBUM_KEY + "," + AudioColumns.TRACK); if (cursor != null) { final long[] mList = getSongListForCursor(cursor); cursor.close(); cursor = null; return mList; } return sEmptyList; } /** * @param context The {@link Context} to use. * @param id The ID of the album. * @return The song list for an album. */ public static final long[] getSongListForAlbum(final Context context, final long id) { final String[] projection = new String[] { BaseColumns._ID }; final String selection = AudioColumns.ALBUM_ID + "=" + id + " AND " + AudioColumns.IS_MUSIC + "=1"; Cursor cursor = context.getContentResolver().query( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection, null, AudioColumns.TRACK + ", " + MediaStore.Audio.Media.DEFAULT_SORT_ORDER); if (cursor != null) { final long[] mList = getSongListForCursor(cursor); cursor.close(); cursor = null; return mList; } return sEmptyList; } /** * Plays songs by an artist. * * @param context The {@link Context} to use. * @param artistId The artist Id. * @param position Specify where to start. */ public static void playArtist(final Context context, final long artistId, int position) { final long[] artistList = getSongListForArtist(context, artistId); if (artistList != null) { playAll(context, artistList, position, false); } } /** * @param context The {@link Context} to use. * @param id The ID of the genre. * @return The song list for an genre. */ public static final long[] getSongListForGenre(final Context context, final long id) { final String[] projection = new String[] { BaseColumns._ID }; final StringBuilder selection = new StringBuilder(); selection.append(AudioColumns.IS_MUSIC + "=1"); selection.append(" AND " + MediaColumns.TITLE + "!=''"); final Uri uri = MediaStore.Audio.Genres.Members.getContentUri("external", Long.valueOf(id)); Cursor cursor = context.getContentResolver().query(uri, projection, selection.toString(), null, null); if (cursor != null) { final long[] mList = getSongListForCursor(cursor); cursor.close(); cursor = null; return mList; } return sEmptyList; } /** * @param context The {@link Context} to use * @param uri The source of the file */ public static void playFile(final Context context, final Uri uri) { if (uri == null || mService == null) { return; } // If this is a file:// URI, just use the path directly instead // of going through the open-from-filedescriptor codepath. String filename; String scheme = uri.getScheme(); if ("file".equals(scheme)) { filename = uri.getPath(); } else { filename = uri.toString(); } try { mService.stop(); mService.openFile(filename); mService.play(); } catch (final RemoteException ignored) { } } /** * @param context The {@link Context} to use. * @param list The list of songs to play. * @param position Specify where to start. * @param forceShuffle True to force a shuffle, false otherwise. */ public static void playAll(final Context context, final long[] list, int position, final boolean forceShuffle) { if (list.length == 0 || mService == null) { return; } try { if (forceShuffle) { mService.setShuffleMode(MusicPlaybackService.SHUFFLE_NORMAL); } else { mService.setShuffleMode(MusicPlaybackService.SHUFFLE_NONE); } final long currentId = mService.getAudioId(); final int currentQueuePosition = getQueuePosition(); if (position != -1 && currentQueuePosition == position && currentId == list[position]) { final long[] playlist = getQueue(); if (Arrays.equals(list, playlist)) { mService.play(); return; } } if (position < 0) { position = 0; } mService.open(list, forceShuffle ? -1 : position); mService.play(); } catch (final RemoteException ignored) { } } /** * @param list The list to enqueue. */ public static void playNext(final long[] list) { if (mService == null) { return; } try { mService.enqueue(list, MusicPlaybackService.NEXT); } catch (final RemoteException ignored) { } } /** * @param context The {@link Context} to use. */ public static void shuffleAll(final Context context) { Cursor cursor = null;//SongLoader.makeSongCursor(context); final long[] mTrackList = getSongListForCursor(cursor); final int position = 0; if (mTrackList.length == 0 || mService == null) { return; } try { mService.setShuffleMode(MusicPlaybackService.SHUFFLE_NORMAL); final long mCurrentId = mService.getAudioId(); final int mCurrentQueuePosition = getQueuePosition(); if (position != -1 && mCurrentQueuePosition == position && mCurrentId == mTrackList[position]) { final long[] mPlaylist = getQueue(); if (Arrays.equals(mTrackList, mPlaylist)) { mService.play(); return; } } mService.open(mTrackList, -1); mService.play(); cursor.close(); cursor = null; } catch (final RemoteException ignored) { } } /** * Returns The ID for a playlist. * * @param context The {@link Context} to use. * @param name The name of the playlist. * @return The ID for a playlist. */ public static final long getIdForPlaylist(final Context context, final String name) { Cursor cursor = context.getContentResolver().query( MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, new String[] { BaseColumns._ID }, PlaylistsColumns.NAME + "=?", new String[] { name }, PlaylistsColumns.NAME); int id = -1; if (cursor != null) { cursor.moveToFirst(); if (!cursor.isAfterLast()) { id = cursor.getInt(0); } cursor.close(); cursor = null; } return id; } /** * Returns the Id for an artist. * * @param context The {@link Context} to use. * @param name The name of the artist. * @return The ID for an artist. */ public static final long getIdForArtist(final Context context, final String name) { Cursor cursor = context.getContentResolver().query( MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI, new String[] { BaseColumns._ID }, ArtistColumns.ARTIST + "=?", new String[] { name }, ArtistColumns.ARTIST); int id = -1; if (cursor != null) { cursor.moveToFirst(); if (!cursor.isAfterLast()) { id = cursor.getInt(0); } cursor.close(); cursor = null; } return id; } /** * Returns the ID for an album. * * @param context The {@link Context} to use. * @param albumName The name of the album. * @param artistName The name of the artist * @return The ID for an album. */ public static final long getIdForAlbum(final Context context, final String albumName, final String artistName) { Cursor cursor = context.getContentResolver().query( MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, new String[] { BaseColumns._ID }, AlbumColumns.ALBUM + "=? AND " + AlbumColumns.ARTIST + "=?", new String[] { albumName, artistName }, AlbumColumns.ALBUM); int id = -1; if (cursor != null) { cursor.moveToFirst(); if (!cursor.isAfterLast()) { id = cursor.getInt(0); } cursor.close(); cursor = null; } return id; } /** * Plays songs from an album. * * @param context The {@link Context} to use. * @param albumId The album Id. * @param position Specify where to start. */ public static void playAlbum(final Context context, final long albumId, int position) { final long[] albumList = getSongListForAlbum(context, albumId); if (albumList != null) { playAll(context, albumList, position, false); } } /* */ public static void makeInsertItems(final long[] ids, final int offset, int len, final int base) { if (offset + len > ids.length) { len = ids.length - offset; } if (mContentValuesCache == null || mContentValuesCache.length != len) { mContentValuesCache = new ContentValues[len]; } for (int i = 0; i < len; i++) { if (mContentValuesCache[i] == null) { mContentValuesCache[i] = new ContentValues(); } mContentValuesCache[i].put(Playlists.Members.PLAY_ORDER, base + offset + i); mContentValuesCache[i].put(Playlists.Members.AUDIO_ID, ids[offset + i]); } } /** * @param context The {@link Context} to use. * @param name The name of the new playlist. * @return A new playlist ID. */ public static final long createPlaylist(final Context context, final String name) { if (name != null && name.length() > 0) { final ContentResolver resolver = context.getContentResolver(); final String[] projection = new String[] { PlaylistsColumns.NAME }; final String selection = PlaylistsColumns.NAME + " = '" + name + "'"; Cursor cursor = resolver.query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, projection, selection, null, null); if (cursor.getCount() <= 0) { final ContentValues values = new ContentValues(1); values.put(PlaylistsColumns.NAME, name); final Uri uri = resolver.insert(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, values); return Long.parseLong(uri.getLastPathSegment()); } if (cursor != null) { cursor.close(); cursor = null; } return -1; } return -1; } /** * @param context The {@link Context} to use. * @param playlistId The playlist ID. */ public static void clearPlaylist(final Context context, final int playlistId) { final Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId); context.getContentResolver().delete(uri, null, null); return; } /** * @param context The {@link Context} to use. * @param ids The id of the song(s) to add. * @param playlistid The id of the playlist being added to. */ public static void addToPlaylist(final Context context, final long[] ids, final long playlistid) { final int size = ids.length; final ContentResolver resolver = context.getContentResolver(); final String[] projection = new String[] { "count(*)" }; final Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlistid); Cursor cursor = resolver.query(uri, projection, null, null, null); cursor.moveToFirst(); final int base = cursor.getInt(0); cursor.close(); cursor = null; int numinserted = 0; for (int offSet = 0; offSet < size; offSet += 1000) { makeInsertItems(ids, offSet, 1000, base); numinserted += resolver.bulkInsert(uri, mContentValuesCache); } // final String message = context.getResources().getQuantityString( // R.plurals.NNNtrackstoplaylist, numinserted, numinserted); // AppMsg.makeText((Activity)context, message, AppMsg.STYLE_CONFIRM).show(); } /** * Removes a single track from a given playlist * @param context The {@link Context} to use. * @param id The id of the song to remove. * @param playlistId The id of the playlist being removed from. */ public static void removeFromPlaylist(final Context context, final long id, final long playlistId) { final Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId); final ContentResolver resolver = context.getContentResolver(); resolver.delete(uri, Playlists.Members.AUDIO_ID + " = ? ", new String[] { Long.toString(id) }); // final String message = context.getResources().getQuantityString( // R.plurals.NNNtracksfromplaylist, 1, 1); // AppMsg.makeText((Activity)context, message, AppMsg.STYLE_CONFIRM).show(); } /** * @param context The {@link Context} to use. * @param list The list to enqueue. */ public static void addToQueue(final Context context, final long[] list) { if (mService == null) { return; } try { mService.enqueue(list, MusicPlaybackService.LAST); //final String message = makeLabel(context, R.plurals.NNNtrackstoqueue, list.length); //AppMsg.makeText((Activity)context, message, AppMsg.STYLE_CONFIRM).show(); } catch (final RemoteException ignored) { } } /** * @param context The {@link Context} to use * @param id The song ID. */ public static void setRingtone(final Context context, final long id) { final ContentResolver resolver = context.getContentResolver(); final Uri uri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id); try { final ContentValues values = new ContentValues(2); values.put(AudioColumns.IS_RINGTONE, "1"); values.put(AudioColumns.IS_ALARM, "1"); resolver.update(uri, values, null, null); } catch (final UnsupportedOperationException ingored) { return; } final String[] projection = new String[] { BaseColumns._ID, MediaColumns.DATA, MediaColumns.TITLE }; final String selection = BaseColumns._ID + "=" + id; Cursor cursor = resolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection, null, null); try { if (cursor != null && cursor.getCount() == 1) { cursor.moveToFirst(); Settings.System.putString(resolver, Settings.System.RINGTONE, uri.toString()); // final String message = context.getString(R.string.set_as_ringtone, // cursor.getString(2)); // AppMsg.makeText((Activity)context, message, AppMsg.STYLE_CONFIRM).show(); } } finally { if (cursor != null) { cursor.close(); cursor = null; } } } /** * @param context The {@link Context} to use. * @param id The id of the album. * @return The song count for an album. */ public static final String getSongCountForAlbum(final Context context, final long id) { if (id == -1) { return null; } Uri uri = ContentUris.withAppendedId(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, id); Cursor cursor = context.getContentResolver().query(uri, new String[] { AlbumColumns.NUMBER_OF_SONGS }, null, null, null); String songCount = null; if (cursor != null) { cursor.moveToFirst(); if (!cursor.isAfterLast()) { songCount = cursor.getString(0); } cursor.close(); cursor = null; } return songCount; } /** * @param context The {@link Context} to use. * @param id The id of the album. * @return The release date for an album. */ public static final String getReleaseDateForAlbum(final Context context, final long id) { if (id == -1) { return null; } Uri uri = ContentUris.withAppendedId(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, id); Cursor cursor = context.getContentResolver().query(uri, new String[] { AlbumColumns.FIRST_YEAR }, null, null, null); String releaseDate = null; if (cursor != null) { cursor.moveToFirst(); if (!cursor.isAfterLast()) { releaseDate = cursor.getString(0); } cursor.close(); cursor = null; } return releaseDate; } /** * @return The path to the currently playing file as {@link String} */ public static final String getFilePath() { try { if (mService != null) { return mService.getPath(); } } catch (final RemoteException ignored) { } return null; } /** * @param from The index the item is currently at. * @param to The index the item is moving to. */ public static void moveQueueItem(final int from, final int to) { try { if (mService != null) { mService.moveQueueItem(from, to); } else { } } catch (final RemoteException ignored) { } } /** * Toggles the current song as a favorite. */ public static void toggleFavorite() { try { if (mService != null) { mService.toggleFavorite(); } } catch (final RemoteException ignored) { } } /** * @return True if the current song is a favorite, false otherwise. */ public static final boolean isFavorite() { try { if (mService != null) { return mService.isFavorite(); } } catch (final RemoteException ignored) { } return false; } /** * @param context The {@link Context} to sue * @param playlistId The playlist Id * @return The track list for a playlist */ public static final long[] getSongListForPlaylist(final Context context, final long playlistId) { final String[] projection = new String[] { MediaStore.Audio.Playlists.Members.AUDIO_ID }; Cursor cursor = context.getContentResolver().query( MediaStore.Audio.Playlists.Members.getContentUri("external", Long.valueOf(playlistId)), projection, null, null, MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER); if (cursor != null) { final long[] list = getSongListForCursor(cursor); cursor.close(); cursor = null; return list; } return sEmptyList; } /** * Plays a user created playlist. * * @param context The {@link Context} to use. * @param playlistId The playlist Id. */ public static void playPlaylist(final Context context, final long playlistId) { final long[] playlistList = getSongListForPlaylist(context, playlistId); if (playlistList != null) { playAll(context, playlistList, -1, false); } } /** * @param cursor The {@link Cursor} used to gather the list in our favorites * database * @return The song list for the favorite playlist */ public final static long[] getSongListForFavoritesCursor(Cursor cursor) { if (cursor == null) { return sEmptyList; } final int len = cursor.getCount(); final long[] list = new long[len]; cursor.moveToFirst(); int colidx = -1; try { colidx = cursor.getColumnIndexOrThrow(FavoriteColumns.ID); } catch (final Exception ignored) { } for (int i = 0; i < len; i++) { list[i] = cursor.getLong(colidx); cursor.moveToNext(); } cursor.close(); cursor = null; return list; } /** * @param context The {@link Context} to use * @return The song list from our favorites database */ public final static long[] getSongListForFavorites(final Context context) { Cursor cursor = null;//FavoritesLoader.makeFavoritesCursor(context); if (cursor != null) { final long[] list = getSongListForFavoritesCursor(cursor); cursor.close(); cursor = null; return list; } return sEmptyList; } /** * Play the songs that have been marked as favorites. * * @param context The {@link Context} to use */ public static void playFavorites(final Context context) { playAll(context, getSongListForFavorites(context), 0, false); } /** * @param context The {@link Context} to use * @return The song list for the last added playlist */ public static final long[] getSongListForLastAdded(final Context context) { final Cursor cursor = null;//LastAddedLoader.makeLastAddedCursor(context); if (cursor != null) { final int count = cursor.getCount(); final long[] list = new long[count]; for (int i = 0; i < count; i++) { cursor.moveToNext(); list[i] = cursor.getLong(0); } return list; } return sEmptyList; } /** * Plays the last added songs from the past two weeks. * * @param context The {@link Context} to use */ public static void playLastAdded(final Context context) { playAll(context, getSongListForLastAdded(context), 0, false); } /** * Creates a sub menu used to add items to a new playlist or an existsing * one. * * @param context The {@link Context} to use. * @param groupId The group Id of the menu. * @param subMenu The {@link SubMenu} to add to. * @param showFavorites True if we should show the option to add to the * Favorites cache. */ // public static void makePlaylistMenu(final Context context, final int groupId, // final SubMenu subMenu, final boolean showFavorites) { // subMenu.clear(); // if (showFavorites) { // subMenu.add(groupId, FragmentMenuItems.ADD_TO_FAVORITES, Menu.NONE, // R.string.add_to_favorites); // } // subMenu.add(groupId, FragmentMenuItems.NEW_PLAYLIST, Menu.NONE, R.string.new_playlist); // Cursor cursor = PlaylistLoader.makePlaylistCursor(context); // if (cursor != null && cursor.getCount() > 0 && cursor.moveToFirst()) { // while (!cursor.isAfterLast()) { // final Intent intent = new Intent(); // String name = cursor.getString(1); // if (name != null) { // intent.putExtra("playlist", getIdForPlaylist(context, name)); // subMenu.add(groupId, FragmentMenuItems.PLAYLIST_SELECTED, Menu.NONE, // name).setIntent(intent); // } // cursor.moveToNext(); // } // } // if (cursor != null) { // cursor.close(); // cursor = null; // } // } /** * Called when one of the lists should refresh or requery. */ public static void refresh() { try { if (mService != null) { mService.refresh(); } } catch (final RemoteException ignored) { } } /** * Queries {@link RecentStore} for the last album played by an artist * * @param context The {@link Context} to use * @param artistName The artist name * @return The last album name played by an artist */ public static final String getLastAlbumForArtist(final Context context, final String artistName) { return RecentStore.getInstance(context).getAlbumName(artistName); } /** * Seeks the current track to a desired position * * @param position The position to seek to */ public static void seek(final long position) { if (mService != null) { try { mService.seek(position); } catch (final RemoteException ignored) { } } } /** * @return The current position time of the track */ public static final long position() { if (mService != null) { try { return mService.position(); } catch (final RemoteException ignored) { } } return 0; } /** * @return The total length of the current track */ public static final long duration() { if (mService != null) { try { return mService.duration(); } catch (final RemoteException ignored) { } } return 0; } /** * @param position The position to move the queue to */ public static void setQueuePosition(final int position) { if (mService != null) { try { mService.setQueuePosition(position); } catch (final RemoteException ignored) { } } } /** * Clears the qeueue */ public static void clearQueue() { try { mService.removeTracks(0, Integer.MAX_VALUE); } catch (final RemoteException ignored) { } } /** * Used to build and show a notification when Apollo is sent into the * background * * @param context The {@link Context} to use. */ public static void notifyForegroundStateChanged(final Context context, boolean inForeground) { int old = sForegroundActivities; if (inForeground) { sForegroundActivities++; } else { sForegroundActivities--; } if (old == 0 || sForegroundActivities == 0) { final Intent intent = new Intent(context, MusicPlaybackService.class); intent.setAction(MusicPlaybackService.FOREGROUND_STATE_CHANGED); intent.putExtra(MusicPlaybackService.NOW_IN_FOREGROUND, sForegroundActivities != 0); context.startService(intent); } } /** * Perminately deletes item(s) from the user's device * * @param context The {@link Context} to use. * @param list The item(s) to delete. */ public static void deleteTracks(final Context context, final long[] list) { final String[] projection = new String[] { BaseColumns._ID, MediaColumns.DATA, AudioColumns.ALBUM_ID }; final StringBuilder selection = new StringBuilder(); selection.append(BaseColumns._ID + " IN ("); for (int i = 0; i < list.length; i++) { selection.append(list[i]); if (i < list.length - 1) { selection.append(","); } } selection.append(")"); final Cursor c = context.getContentResolver().query( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection.toString(), null, null); if (c != null) { // Step 1: Remove selected tracks from the current playlist, as well // as from the album art cache c.moveToFirst(); while (!c.isAfterLast()) { // Remove from current playlist final long id = c.getLong(0); removeTrack(id); // Remove from the favorites playlist FavoritesStore.getInstance(context).removeItem(id); // Remove any items in the recents database RecentStore.getInstance(context).removeItem(c.getLong(2)); c.moveToNext(); } // Step 2: Remove selected tracks from the database context.getContentResolver().delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, selection.toString(), null); // Step 3: Remove files from card c.moveToFirst(); while (!c.isAfterLast()) { final String name = c.getString(1); final File f = new File(name); try { // File.delete can throw a security exception if (!f.delete()) { // I'm not sure if we'd ever get here (deletion would // have to fail, but no exception thrown) Log.e("MusicUtils", "Failed to delete file " + name); } c.moveToNext(); } catch (final SecurityException ex) { c.moveToNext(); } } c.close(); } // final String message = makeLabel(context, R.plurals.NNNtracksdeleted, list.length); // // AppMsg.makeText((Activity)context, message, AppMsg.STYLE_CONFIRM).show(); // We deleted a number of tracks, which could affect any number of // things // in the media content domain, so update everything. context.getContentResolver().notifyChange(Uri.parse("content://media"), null); // Notify the lists to update refresh(); } }