1792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project/* 2792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * Copyright (C) 2007 The Android Open Source Project 3792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * 4792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 5792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * you may not use this file except in compliance with the License. 6792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * You may obtain a copy of the License at 7792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * 8792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 9792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * 10792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software 11792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 12792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * See the License for the specific language governing permissions and 14792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * limitations under the License. 15792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project */ 16792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 17792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectpackage com.android.music; 18792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 19792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.app.PendingIntent; 20792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.content.Context; 21792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.content.Intent; 228717d3490e179e9591eac98a91a14995d98291adMarco Nelissenimport android.graphics.Bitmap; 23f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.media.MediaDescription; 24f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.media.MediaMetadata; 25f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.media.browse.MediaBrowser.MediaItem; 26f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.media.session.MediaSession; 27f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.media.session.PlaybackState; 28f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.os.Bundle; 29792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.os.Handler; 30792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.os.Message; 31792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.os.SystemClock; 32f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.service.media.MediaBrowserService; 33f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport android.text.TextUtils; 34792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.util.Log; 35f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport com.android.music.utils.*; 36792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 372b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissenimport java.lang.ref.WeakReference; 38f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport java.util.*; 39f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 40f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Heimport static com.android.music.utils.MediaIDHelper.*; 41792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 42792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project/** 43792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * Provides "background" audio playback capabilities, allowing the 44792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * user to switch between activities without stopping playback. 45792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project */ 46f02d3c605f5d1d091ca7e27170010ad23cd22134Jack Hepublic class MediaPlaybackService extends MediaBrowserService implements Playback.Callback { 47f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private static final String TAG = LogHelper.makeLogTag(MediaPlaybackService.class); 48f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 49f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Delay stopSelf by using a handler. 50f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private static final int STOP_DELAY = 30000; 51f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 52f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public static final String ACTION_CMD = "com.android.music.ACTION_CMD"; 53f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public static final String CMD_NAME = "CMD_NAME"; 54f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public static final String CMD_PAUSE = "CMD_PAUSE"; 55f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public static final String CMD_REPEAT = "CMD_PAUSE"; 56f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public static final String REPEAT_MODE = "REPEAT_MODE"; 57f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 58f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public enum RepeatMode { REPEAT_NONE, REPEAT_ALL, REPEAT_CURRENT } 59f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 60f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Music catalog manager 61f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private MusicProvider mMusicProvider; 62f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private MediaSession mSession; 63f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // "Now playing" queue: 64f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private List<MediaSession.QueueItem> mPlayingQueue = null; 65f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private int mCurrentIndexOnQueue = -1; 66f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private MediaNotificationManager mMediaNotificationManager; 67f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Indicates whether the service was started. 68f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private boolean mServiceStarted; 69f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private DelayedStopHandler mDelayedStopHandler = new DelayedStopHandler(this); 70f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private Playback mPlayback; 71f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Default mode is repeat none 72f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private RepeatMode mRepeatMode = RepeatMode.REPEAT_NONE; 73f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Extra information for this session 74f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private Bundle mExtras; 75085cbfd3da2c00f5eddf74f344802d16df151988Jean-Michel Trivi 76b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He public MediaPlaybackService() {} 77792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 78792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project @Override 79792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project public void onCreate() { 80f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.d(TAG, "onCreate()"); 81792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project super.onCreate(); 82f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.d(TAG, "Create MusicProvider"); 83f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mPlayingQueue = new ArrayList<>(); 84f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mMusicProvider = new MusicProvider(this); 85f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 86f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.d(TAG, "Create MediaSession"); 87f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Start a new MediaSession 88f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mSession = new MediaSession(this, "MediaPlaybackService"); 89f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Set extra information 90f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mExtras = new Bundle(); 91f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mExtras.putInt(REPEAT_MODE, mRepeatMode.ordinal()); 92f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mSession.setExtras(mExtras); 93f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Enable callbacks from MediaButtons and TransportControls 94f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS 95f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS); 96f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Set an initial PlaybackState with ACTION_PLAY, so media buttons can start the player 97f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He PlaybackState.Builder stateBuilder = new PlaybackState.Builder().setActions( 98f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PLAY_PAUSE); 99f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mSession.setPlaybackState(stateBuilder.build()); 100f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // MediaSessionCallback() has methods that handle callbacks from a media controller 101f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mSession.setCallback(new MediaSessionCallback()); 102f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Set the session's token so that client activities can communicate with it. 103f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He setSessionToken(mSession.getSessionToken()); 104f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 105f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mPlayback = new Playback(this, mMusicProvider); 106f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mPlayback.setState(PlaybackState.STATE_NONE); 107f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mPlayback.setCallback(this); 108f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mPlayback.start(); 109f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 110f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He Context context = getApplicationContext(); 111f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He Intent intent = new Intent(context, MusicBrowserActivity.class); 112f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He PendingIntent pi = PendingIntent.getActivity( 113f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He context, 99 /*request code*/, intent, PendingIntent.FLAG_UPDATE_CURRENT); 114f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mSession.setSessionActivity(pi); 115f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 116f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He updatePlaybackState(null); 117f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 118f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mMediaNotificationManager = new MediaNotificationManager(this); 119792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 120792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 121792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project @Override 122f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public int onStartCommand(Intent startIntent, int flags, int startId) { 123f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (startIntent != null) { 124f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He String action = startIntent.getAction(); 125f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He String command = startIntent.getStringExtra(CMD_NAME); 126f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (ACTION_CMD.equals(action)) { 127f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (CMD_PAUSE.equals(command)) { 128f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (mPlayback != null && mPlayback.isPlaying()) { 129f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He handlePauseRequest(); 130f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 131f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 132f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 133792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 134f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He return START_STICKY; 135f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 1362b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen 137f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He @Override 138f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public void onDestroy() { 139f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He Log.d(TAG, "onDestroy"); 140f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Service is being killed, so make sure we release our resources 141f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He handleStopRequest(null); 142792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 143f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mDelayedStopHandler.removeCallbacksAndMessages(null); 144f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Always release the MediaSession to clean up resources 145f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // and notify associated MediaController(s). 146f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mSession.release(); 147792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 148b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He 149f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He @Override 150f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) { 151f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He Log.d(TAG, 152f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He "OnGetRoot: clientPackageName=" + clientPackageName + "; clientUid=" + clientUid 153f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He + " ; rootHints=" + rootHints); 154f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Allow everyone to browse 155f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He return new BrowserRoot(MEDIA_ID_ROOT, null); 156f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 157792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 158f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He @Override 159f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public void onLoadChildren(final String parentMediaId, final Result<List<MediaItem>> result) { 160f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He Log.d(TAG, "OnLoadChildren: parentMediaId=" + parentMediaId); 161f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Browsing not allowed 162f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (parentMediaId == null) { 163f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He result.sendResult(null); 164792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project return; 165792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 166f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (!mMusicProvider.isInitialized()) { 167f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Use result.detach to allow calling result.sendResult from another thread: 168f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He result.detach(); 1698d08ec235831d71fdd7f7b6f7757c2bc19528faeMarco Nelissen 170f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mMusicProvider.retrieveMediaAsync(new MusicProvider.MusicProviderCallback() { 171f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He @Override 172f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public void onMusicCatalogReady(boolean success) { 173f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He Log.d(TAG, "Received catalog result, success: " + String.valueOf(success)); 174f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (success) { 175f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He onLoadChildren(parentMediaId, result); 17663dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen } else { 177f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He result.sendResult(Collections.emptyList()); 17863dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen } 17963dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen } 180f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He }); 181b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He 182f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } else { 183f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // If our music catalog is already loaded/cached, load them into result immediately 184f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He List<MediaItem> mediaItems = new ArrayList<>(); 185f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 186f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He switch (parentMediaId) { 187f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He case MEDIA_ID_ROOT: 188f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He Log.d(TAG, "OnLoadChildren.ROOT"); 189f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mediaItems.add(new MediaItem(new MediaDescription.Builder() 190f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .setMediaId(MEDIA_ID_MUSICS_BY_ARTIST) 191f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .setTitle("Artists") 192f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .build(), 193f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaItem.FLAG_BROWSABLE)); 194f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mediaItems.add(new MediaItem(new MediaDescription.Builder() 195f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .setMediaId(MEDIA_ID_MUSICS_BY_ALBUM) 196f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .setTitle("Albums") 197f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .build(), 198f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaItem.FLAG_BROWSABLE)); 199f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mediaItems.add(new MediaItem(new MediaDescription.Builder() 200f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .setMediaId(MEDIA_ID_MUSICS_BY_SONG) 201f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .setTitle("Songs") 202f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .build(), 203f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaItem.FLAG_BROWSABLE)); 204f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mediaItems.add(new MediaItem(new MediaDescription.Builder() 205f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .setMediaId(MEDIA_ID_MUSICS_BY_PLAYLIST) 206f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .setTitle("Playlists") 207f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .build(), 208f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaItem.FLAG_BROWSABLE)); 209f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He break; 210f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He case MEDIA_ID_MUSICS_BY_ARTIST: 211f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He Log.d(TAG, "OnLoadChildren.ARTIST"); 212f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He for (String artist : mMusicProvider.getArtists()) { 213f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaItem item = new MediaItem( 214f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He new MediaDescription.Builder() 215f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .setMediaId(MediaIDHelper.createBrowseCategoryMediaID( 216f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MEDIA_ID_MUSICS_BY_ARTIST, artist)) 217f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .setTitle(artist) 218f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .build(), 219f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaItem.FLAG_BROWSABLE); 220f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mediaItems.add(item); 221f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 222f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He break; 223f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He case MEDIA_ID_MUSICS_BY_PLAYLIST: 224f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.d(TAG, "OnLoadChildren.PLAYLIST"); 225f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He for (String playlist : mMusicProvider.getPlaylists()) { 226f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaItem item = new MediaItem( 227f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He new MediaDescription.Builder() 228f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .setMediaId(MediaIDHelper.createBrowseCategoryMediaID( 229f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MEDIA_ID_MUSICS_BY_PLAYLIST, playlist)) 230f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .setTitle(playlist) 231f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .build(), 232f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaItem.FLAG_BROWSABLE); 233f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mediaItems.add(item); 234f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 235f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He break; 236f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He case MEDIA_ID_MUSICS_BY_ALBUM: 237f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He Log.d(TAG, "OnLoadChildren.ALBUM"); 238f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He loadAlbum(mMusicProvider.getAlbums(), mediaItems); 239f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He break; 240f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He case MEDIA_ID_MUSICS_BY_SONG: 241f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He Log.d(TAG, "OnLoadChildren.SONG"); 242f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He String hierarchyAwareMediaID = MediaIDHelper.createBrowseCategoryMediaID( 243f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He parentMediaId, MEDIA_ID_MUSICS_BY_SONG); 244f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He loadSong(mMusicProvider.getMusicList(), mediaItems, hierarchyAwareMediaID); 245f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He break; 246f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He default: 247f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (parentMediaId.startsWith(MEDIA_ID_MUSICS_BY_ARTIST)) { 248f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He String artist = MediaIDHelper.getHierarchy(parentMediaId)[1]; 249f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He Log.d(TAG, "OnLoadChildren.SONGS_BY_ARTIST artist=" + artist); 250f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He loadAlbum(mMusicProvider.getAlbumByArtist(artist), mediaItems); 251f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } else if (parentMediaId.startsWith(MEDIA_ID_MUSICS_BY_ALBUM)) { 252f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He String album = MediaIDHelper.getHierarchy(parentMediaId)[1]; 253f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He Log.d(TAG, "OnLoadChildren.SONGS_BY_ALBUM album=" + album); 254f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He loadSong(mMusicProvider.getMusicsByAlbum(album), mediaItems, parentMediaId); 255f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } else if (parentMediaId.startsWith(MEDIA_ID_MUSICS_BY_PLAYLIST)) { 256f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He String playlist = MediaIDHelper.getHierarchy(parentMediaId)[1]; 257f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.d(TAG, "OnLoadChildren.SONGS_BY_PLAYLIST playlist=", playlist); 258f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (playlist.equals(MEDIA_ID_NOW_PLAYING) && mPlayingQueue != null 259f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He && mPlayingQueue.size() > 0) { 260f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He loadPlayingQueue(mediaItems, parentMediaId); 261f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } else { 262f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He loadSong(mMusicProvider.getMusicsByPlaylist(playlist), mediaItems, 263f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He parentMediaId); 264f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 265bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen } else { 266f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He Log.w(TAG, "Skipping unmatched parentMediaId: " + parentMediaId); 267bf0ea148eefcffd43e6f5023a6f8365d74fb829fMarco Nelissen } 268f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He break; 269792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 270f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He Log.d(TAG, 271f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He "OnLoadChildren sending " + mediaItems.size() + " results for " 272f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He + parentMediaId); 273f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He result.sendResult(mediaItems); 274f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 275f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 276f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 277f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private void loadPlayingQueue(List<MediaItem> mediaItems, String parentId) { 278f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He for (MediaSession.QueueItem queueItem : mPlayingQueue) { 279f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaItem mediaItem = 280f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He new MediaItem(queueItem.getDescription(), MediaItem.FLAG_PLAYABLE); 281f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mediaItems.add(mediaItem); 282f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 283f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 284f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 285f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private void loadSong( 286f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He Iterable<MediaMetadata> songList, List<MediaItem> mediaItems, String parentId) { 287f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He for (MediaMetadata metadata : songList) { 288f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He String hierarchyAwareMediaID = 289f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaIDHelper.createMediaID(metadata.getDescription().getMediaId(), parentId); 290f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He Bundle songExtra = new Bundle(); 291f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He songExtra.putLong(MediaMetadata.METADATA_KEY_DURATION, 292f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He metadata.getLong(MediaMetadata.METADATA_KEY_DURATION)); 293f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He String title = metadata.getString(MediaMetadata.METADATA_KEY_TITLE); 294f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He String artistName = metadata.getString(MediaMetadata.METADATA_KEY_ARTIST); 295f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaItem item = new MediaItem(new MediaDescription.Builder() 296f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .setMediaId(hierarchyAwareMediaID) 297f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .setTitle(title) 298f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .setSubtitle(artistName) 299f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .setExtras(songExtra) 300f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .build(), 301f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaItem.FLAG_PLAYABLE); 302f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mediaItems.add(item); 303f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 304f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 305f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 306f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private void loadAlbum(Iterable<MediaMetadata> albumList, List<MediaItem> mediaItems) { 307f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He for (MediaMetadata albumMetadata : albumList) { 308f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He String albumName = albumMetadata.getString(MediaMetadata.METADATA_KEY_ALBUM); 309f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He String artistName = albumMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST); 310f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He Bundle albumExtra = new Bundle(); 311f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He albumExtra.putLong(MediaMetadata.METADATA_KEY_NUM_TRACKS, 312f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He albumMetadata.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS)); 313f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaItem item = new MediaItem( 314f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He new MediaDescription.Builder() 315f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .setMediaId(MediaIDHelper.createBrowseCategoryMediaID( 316f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MEDIA_ID_MUSICS_BY_ALBUM, albumName)) 317f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .setTitle(albumName) 318f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .setSubtitle(artistName) 319f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .setIconBitmap( 320f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He albumMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART)) 321f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .setExtras(albumExtra) 322f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .build(), 323f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaItem.FLAG_BROWSABLE); 324f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mediaItems.add(item); 325f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 326f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 327f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 328f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private final class MediaSessionCallback extends MediaSession.Callback { 329f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He @Override 330f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public void onPlay() { 331f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He Log.d(TAG, "play"); 332792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 333f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (mPlayingQueue == null || mPlayingQueue.isEmpty()) { 334f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mPlayingQueue = QueueHelper.getRandomQueue(mMusicProvider); 335f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mSession.setQueue(mPlayingQueue); 336f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mSession.setQueueTitle(getString(R.string.random_queue_title)); 337f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // start playing from the beginning of the queue 338f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mCurrentIndexOnQueue = 0; 339792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 340792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 341f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) { 342f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He handlePlayRequest(); 343792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 344f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 345b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He 346f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He @Override 347f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public void onSkipToQueueItem(long queueId) { 348f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.d(TAG, "OnSkipToQueueItem:", queueId); 349792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 350f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) { 351f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // set the current index on queue from the music Id: 352f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mCurrentIndexOnQueue = QueueHelper.getMusicIndexOnQueue(mPlayingQueue, queueId); 353f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // play the music 354f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He handlePlayRequest(); 35563dbbbb163b537f8447f5abdb44fe2966a525464Marco Nelissen } 356792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 357c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen 358f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He @Override 359f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public void onSeekTo(long position) { 360f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He Log.d(TAG, "onSeekTo:" + position); 361f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mPlayback.seekTo((int) position); 362f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 363c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen 364f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He @Override 365f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public void onPlayFromMediaId(String mediaId, Bundle extras) { 366f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.d(TAG, "playFromMediaId mediaId:", mediaId, " extras=", extras); 367f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 368f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // The mediaId used here is not the unique musicId. This one comes from the 369f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // MediaBrowser, and is actually a "hierarchy-aware mediaID": a concatenation of 370f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // the hierarchy in MediaBrowser and the actual unique musicID. This is necessary 371f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // so we can build the correct playing queue, based on where the track was 372f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // selected from. 373f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mPlayingQueue = QueueHelper.getPlayingQueue(mediaId, mMusicProvider); 374f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mSession.setQueue(mPlayingQueue); 375f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He String queueTitle = getString(R.string.browse_musics_by_genre_subtitle, 376f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaIDHelper.extractBrowseCategoryValueFromMediaID(mediaId)); 377f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mSession.setQueueTitle(queueTitle); 378f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 379f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) { 380f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // set the current index on queue from the media Id: 381f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mCurrentIndexOnQueue = QueueHelper.getMusicIndexOnQueue(mPlayingQueue, mediaId); 382f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 383f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (mCurrentIndexOnQueue < 0) { 384f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.e(TAG, "playFromMediaId: media ID ", mediaId, 385f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He " could not be found on queue. Ignoring."); 386c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen } else { 387f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // play the music 388f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He handlePlayRequest(); 389c1017e5fe69da3a4ffd2bf7d0890a1a216265a61Marco Nelissen } 390792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 391792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 392b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He 393f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He @Override 394f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public void onPause() { 395f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.d(TAG, "pause. current state=" + mPlayback.getState()); 396f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He handlePauseRequest(); 397792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 398b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He 399f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He @Override 400f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public void onStop() { 401f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.d(TAG, "stop. current state=" + mPlayback.getState()); 402f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He handleStopRequest(null); 403792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 404b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He 405792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project @Override 406f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public void onSkipToNext() { 407f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.d(TAG, "skipToNext"); 408f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mCurrentIndexOnQueue++; 409f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (mPlayingQueue != null && mCurrentIndexOnQueue >= mPlayingQueue.size()) { 410f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // This sample's behavior: skipping to next when in last song returns to the 411f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // first song. 412f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mCurrentIndexOnQueue = 0; 413f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 414f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) { 415f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He handlePlayRequest(); 416f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } else { 417f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.e(TAG, 418f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He "skipToNext: cannot skip to next. next Index=" + mCurrentIndexOnQueue 419f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He + " queue length=" 420f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He + (mPlayingQueue == null ? "null" : mPlayingQueue.size())); 421f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He handleStopRequest("Cannot skip"); 422792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 423792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 424792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 425f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He @Override 426f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public void onSkipToPrevious() { 427f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.d(TAG, "skipToPrevious"); 428f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mCurrentIndexOnQueue--; 429f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (mPlayingQueue != null && mCurrentIndexOnQueue < 0) { 430f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // This sample's behavior: skipping to previous when in first song restarts the 431f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // first song. 432f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mCurrentIndexOnQueue = 0; 433f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 434f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) { 435f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He handlePlayRequest(); 436f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } else { 437f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.e(TAG, 438f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He "skipToPrevious: cannot skip to previous. previous Index=" 439f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He + mCurrentIndexOnQueue + " queue length=" 440f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He + (mPlayingQueue == null ? "null" : mPlayingQueue.size())); 441f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He handleStopRequest("Cannot skip"); 442f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 443792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 444792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 445f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He @Override 446f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public void onPlayFromSearch(String query, Bundle extras) { 447f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.d(TAG, "playFromSearch query=", query); 448f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 449f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (TextUtils.isEmpty(query)) { 450f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // A generic search like "Play music" sends an empty query 451f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // and it's expected that we start playing something. What will be played depends 452f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // on the app: favorite playlist, "I'm feeling lucky", most recent, etc. 453f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mPlayingQueue = QueueHelper.getRandomQueue(mMusicProvider); 454f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } else { 455f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mPlayingQueue = QueueHelper.getPlayingQueueFromSearch(query, mMusicProvider); 4568ef367969a0fd1f678b1f119b67d0ee409ea0389Mike Lockwood } 4578717d3490e179e9591eac98a91a14995d98291adMarco Nelissen 458f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.d(TAG, "playFromSearch playqueue.length=" + mPlayingQueue.size()); 459f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mSession.setQueue(mPlayingQueue); 460b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He 461f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) { 462f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // immediately start playing from the beginning of the search results 463f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mCurrentIndexOnQueue = 0; 464792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 465f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He handlePlayRequest(); 466f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } else { 467f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // if nothing was found, we need to warn the user and stop playing 468f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He handleStopRequest(getString(R.string.no_search_results)); 469792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 470792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 471b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He 472f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He @Override 473f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public void onCustomAction(String action, Bundle extras) { 474f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.d(TAG, "onCustomAction action=", action, ", extras=", extras); 475f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He switch (action) { 476f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He case CMD_REPEAT: 477f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mRepeatMode = RepeatMode.values()[extras.getInt(REPEAT_MODE)]; 478f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mExtras.putInt(REPEAT_MODE, mRepeatMode.ordinal()); 479f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mSession.setExtras(mExtras); 480f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.d(TAG, "modified repeatMode=", mRepeatMode); 481f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He break; 482f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He default: 483f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.d(TAG, "Unkown action=", action); 484f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He break; 485f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 4863aa9ad029bc85da2e07bba019cec928ad5d74043Marco Nelissen } 487792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 488b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He 489792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project /** 490f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He * Handle a request to play music 491792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project */ 492f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private void handlePlayRequest() { 493f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.d(TAG, "handlePlayRequest: mState=" + mPlayback.getState()); 494f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 495f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mDelayedStopHandler.removeCallbacksAndMessages(null); 496f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (!mServiceStarted) { 497f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.v(TAG, "Starting service"); 498f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // The MusicService needs to keep running even after the calling MediaBrowser 499f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // is disconnected. Call startService(Intent) and then stopSelf(..) when we no longer 500f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // need to play media. 501f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He startService(new Intent(getApplicationContext(), MediaPlaybackService.class)); 502f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mServiceStarted = true; 503792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 504792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 505f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (!mSession.isActive()) { 506f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mSession.setActive(true); 507f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 508792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 509f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) { 510f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He updateMetadata(); 511f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mPlayback.play(mPlayingQueue.get(mCurrentIndexOnQueue)); 512792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 513792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 514b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He 515792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project /** 516f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He * Handle a request to pause music 517792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project */ 518f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private void handlePauseRequest() { 519f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.d(TAG, "handlePauseRequest: mState=" + mPlayback.getState()); 520f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mPlayback.pause(); 521f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // reset the delayed stop handler. 522f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mDelayedStopHandler.removeCallbacksAndMessages(null); 523f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mDelayedStopHandler.sendEmptyMessageDelayed(0, STOP_DELAY); 524792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 525792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 526792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project /** 527f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He * Handle a request to stop music 528792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project */ 529f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private void handleStopRequest(String withError) { 530f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.d( 531f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He TAG, "handleStopRequest: mState=" + mPlayback.getState() + " error=", withError); 532f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mPlayback.stop(true); 533f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // reset the delayed stop handler. 534f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mDelayedStopHandler.removeCallbacksAndMessages(null); 535f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mDelayedStopHandler.sendEmptyMessageDelayed(0, STOP_DELAY); 536792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 537f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He updatePlaybackState(withError); 538e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen 539f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // service is no longer necessary. Will be started again if needed. 540f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He stopSelf(); 541f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mServiceStarted = false; 542e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen } 543e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen 544f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private void updateMetadata() { 545f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (!QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) { 546f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.e(TAG, "Can't retrieve current metadata."); 547f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He updatePlaybackState(getResources().getString(R.string.error_no_metadata)); 548f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He return; 549f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 550f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaSession.QueueItem queueItem = mPlayingQueue.get(mCurrentIndexOnQueue); 551f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He String musicId = 552f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaIDHelper.extractMusicIDFromMediaID(queueItem.getDescription().getMediaId()); 553f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaMetadata track = mMusicProvider.getMusicByMediaId(musicId).getMetadata(); 554f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He final String trackId = track.getString(MediaMetadata.METADATA_KEY_MEDIA_ID); 555f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (!musicId.equals(trackId)) { 556f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He IllegalStateException e = new IllegalStateException("track ID should match musicId."); 557f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.e(TAG, "track ID should match musicId.", " musicId=", musicId, 558f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He " trackId=", trackId, 559f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He " mediaId from queueItem=", queueItem.getDescription().getMediaId(), 560f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He " title from queueItem=", queueItem.getDescription().getTitle(), 561f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He " mediaId from track=", track.getDescription().getMediaId(), 562f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He " title from track=", track.getDescription().getTitle(), 563f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He " source.hashcode from track=", 564f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He track.getString(MusicProvider.CUSTOM_METADATA_TRACK_SOURCE).hashCode(), e); 565f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He throw e; 566f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 567f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.d(TAG, "Updating metadata for MusicID= " + musicId); 568f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mSession.setMetadata(track); 569f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 570f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Set the proper album artwork on the media session, so it can be shown in the 571f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // locked screen and in other places. 572f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (track.getDescription().getIconBitmap() == null 573f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He && track.getDescription().getIconUri() != null) { 574f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He String albumUri = track.getDescription().getIconUri().toString(); 575f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He AlbumArtCache.getInstance().fetch(albumUri, new AlbumArtCache.FetchListener() { 576f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He @Override 577f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public void onFetched(String artUrl, Bitmap bitmap, Bitmap icon) { 578f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaSession.QueueItem queueItem = mPlayingQueue.get(mCurrentIndexOnQueue); 579f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaMetadata track = mMusicProvider.getMusicByMediaId(trackId).getMetadata(); 580f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He track = new MediaMetadata 581f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .Builder(track) 582f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 583f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // set high resolution bitmap in METADATA_KEY_ALBUM_ART. This is 584f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // used, for 585f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // example, on the lockscreen background when the media session 586f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // is active. 587f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, bitmap) 588f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 589f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // set small version of the album art in the DISPLAY_ICON. This 590f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // is used on 591f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // the MediaDescription and thus it should be small to be 592f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // serialized if 593f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // necessary.. 594f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .putBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON, icon) 595f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 596f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .build(); 597f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 598f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mMusicProvider.updateMusic(trackId, track); 599f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He 600f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // If we are still playing the same music 601f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He String currentPlayingId = MediaIDHelper.extractMusicIDFromMediaID( 602f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He queueItem.getDescription().getMediaId()); 603f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (trackId.equals(currentPlayingId)) { 604f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mSession.setMetadata(track); 605c37b200d0198deaf488d0bfe1b114d1fd9037382Marco Nelissen } 606e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen } 607f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He }); 608e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen } 609e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen } 610e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen 611792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project /** 612f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He * Update the current media player state, optionally showing an error message. 613792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * 614f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He * @param error if not null, error message to present to the user. 615792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project */ 616f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private void updatePlaybackState(String error) { 617f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.d(TAG, "updatePlaybackState, playback state=" + mPlayback.getState()); 618f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He long position = PlaybackState.PLAYBACK_POSITION_UNKNOWN; 619f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (mPlayback != null && mPlayback.isConnected()) { 620f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He position = mPlayback.getCurrentStreamPosition(); 621792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 622792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 623f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He PlaybackState.Builder stateBuilder = 624f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He new PlaybackState.Builder().setActions(getAvailableActions()); 625272eb78d8836f969f7480454167bdffc6a6f6dd7Thomas Tuttle 626f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He int state = mPlayback.getState(); 627792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 628f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // If there is an error message, send it to the playback state: 629f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (error != null) { 630f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Error states are really only supposed to be used for errors that cause playback to 631f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // stop unexpectedly and persist until the user takes action to fix it. 632f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He stateBuilder.setErrorMessage(error); 633f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He state = PlaybackState.STATE_ERROR; 634f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 635f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He stateBuilder.setState(state, position, 1.0f, SystemClock.elapsedRealtime()); 636347fe57e4c8a860dfbad6f0ec1991171bfa69d77Mike Cleron 637f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Set the activeQueueItemId if the current index is valid. 638f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) { 639f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaSession.QueueItem item = mPlayingQueue.get(mCurrentIndexOnQueue); 640f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He stateBuilder.setActiveQueueItemId(item.getQueueId()); 641792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 642e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen 643f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mSession.setPlaybackState(stateBuilder.build()); 644e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen 645f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (state == PlaybackState.STATE_PLAYING || state == PlaybackState.STATE_PAUSED) { 646f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mMediaNotificationManager.startNotification(); 647e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen } 648e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen } 649e41bd18d3351b494306b7d7f2e1f562fa3fa8f8cMarco Nelissen 650f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private long getAvailableActions() { 651f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He long actions = PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PLAY_FROM_MEDIA_ID 652f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He | PlaybackState.ACTION_PLAY_FROM_SEARCH; 653f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (mPlayingQueue == null || mPlayingQueue.isEmpty()) { 654f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He return actions; 655792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 656f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (mPlayback.isPlaying()) { 657f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He actions |= PlaybackState.ACTION_PAUSE; 658792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 659f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (mCurrentIndexOnQueue > 0) { 660f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He actions |= PlaybackState.ACTION_SKIP_TO_PREVIOUS; 661792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 662f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (mCurrentIndexOnQueue < mPlayingQueue.size() - 1) { 663f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He actions |= PlaybackState.ACTION_SKIP_TO_NEXT; 664792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 665f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He return actions; 666792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 667792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 668f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private MediaMetadata getCurrentPlayingMusic() { 669f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (QueueHelper.isIndexPlayable(mCurrentIndexOnQueue, mPlayingQueue)) { 670f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaSession.QueueItem item = mPlayingQueue.get(mCurrentIndexOnQueue); 671f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (item != null) { 672f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He LogHelper.d(TAG, 673f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He "getCurrentPlayingMusic for musicId=", item.getDescription().getMediaId()); 674f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He return mMusicProvider 675f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .getMusicByMediaId(MediaIDHelper.extractMusicIDFromMediaID( 676f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He item.getDescription().getMediaId())) 677f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He .getMetadata(); 678f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 679f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 680f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He return null; 681792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 682792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 683792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project /** 684f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He * Implementation of the Playback.Callback interface 685792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project */ 686f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He @Override 687f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public void onCompletion() { 688f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // The media player finished playing the current song, so we go ahead 689f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // and start the next. 690f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (mPlayingQueue != null && !mPlayingQueue.isEmpty()) { 691f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He switch (mRepeatMode) { 692f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He case REPEAT_ALL: 693f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Increase the index 694f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mCurrentIndexOnQueue++; 695f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Restart queue when reaching the end 696f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (mCurrentIndexOnQueue >= mPlayingQueue.size()) { 697f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mCurrentIndexOnQueue = 0; 698f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 699f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He break; 700f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He case REPEAT_CURRENT: 701f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Do not change the index 702f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He break; 703f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He case REPEAT_NONE: 704f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He default: 705f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Increase the index 706f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mCurrentIndexOnQueue++; 707f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // Stop the queue when reaching the end 708f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (mCurrentIndexOnQueue >= mPlayingQueue.size()) { 709f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He handleStopRequest(null); 710f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He return; 711f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 712f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He break; 713407cf91b661ceeb3ced16dc20af8243b9a0eb632Marco Nelissen } 714f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He handlePlayRequest(); 715f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } else { 716f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He // If there is nothing to play, we stop and release the resources: 717f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He handleStopRequest(null); 718792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 719792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 720792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 721f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He @Override 722f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public void onPlaybackStatusChanged(int state) { 723f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He updatePlaybackState(null); 724792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 725792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 726f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He @Override 727f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public void onError(String error) { 728f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He updatePlaybackState(error); 729f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He } 730b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He 731792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project /** 732f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He * A simple handler that stops the service if playback is not active (playing) 733792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project */ 734f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private static class DelayedStopHandler extends Handler { 735f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private final WeakReference<MediaPlaybackService> mWeakReference; 736b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He 737f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He private DelayedStopHandler(MediaPlaybackService service) { 738f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He mWeakReference = new WeakReference<>(service); 739792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 740b0fba8b212a649f41c7cad4c9e7c33e94ca29bb0Jack He 741f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He @Override 742f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He public void handleMessage(Message msg) { 743f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He MediaPlaybackService service = mWeakReference.get(); 744f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (service != null && service.mPlayback != null) { 745f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He if (service.mPlayback.isPlaying()) { 746f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He Log.d(TAG, "Ignoring delayed stop since the media player is in use."); 747792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project return; 748792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 749f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He Log.d(TAG, "Stopping service with delay handler."); 750f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He service.stopSelf(); 751f02d3c605f5d1d091ca7e27170010ad23cd22134Jack He service.mServiceStarted = false; 752308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen } 753308670290db0a553de41a21f1c07ca7d53b3fbf6Marco Nelissen } 7542b0b91376a7101fafdf8b007ae660d8b2a039530Marco Nelissen } 755792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project} 756