1b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira/* 2b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * Copyright (C) 2011 The Android Open Source Project 3b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * 4b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * Licensed under the Apache License, Version 2.0 (the "License"); 5b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * you may not use this file except in compliance with the License. 6b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * You may obtain a copy of the License at 7b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * 8b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * http://www.apache.org/licenses/LICENSE-2.0 9b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * 10b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * Unless required by applicable law or agreed to in writing, software 11b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * distributed under the License is distributed on an "AS IS" BASIS, 12b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * See the License for the specific language governing permissions and 14b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * limitations under the License. 15b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira */ 16b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 17b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveirapackage com.example.android.musicplayer; 18b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 19b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveiraimport android.app.Notification; 20b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveiraimport android.app.NotificationManager; 21b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveiraimport android.app.PendingIntent; 22b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveiraimport android.app.Service; 235986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurikimport android.content.ComponentName; 24b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveiraimport android.content.Context; 25b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveiraimport android.content.Intent; 265986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurikimport android.graphics.Bitmap; 275986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurikimport android.graphics.BitmapFactory; 28b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveiraimport android.media.AudioManager; 295986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurikimport android.media.MediaMetadataRetriever; 30b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveiraimport android.media.MediaPlayer; 31b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveiraimport android.media.MediaPlayer.OnCompletionListener; 32b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveiraimport android.media.MediaPlayer.OnErrorListener; 33b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveiraimport android.media.MediaPlayer.OnPreparedListener; 345986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurikimport android.media.RemoteControlClient; 35b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveiraimport android.net.Uri; 36b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveiraimport android.net.wifi.WifiManager; 37b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveiraimport android.net.wifi.WifiManager.WifiLock; 38b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveiraimport android.os.IBinder; 39b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveiraimport android.os.PowerManager; 40b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveiraimport android.util.Log; 41b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveiraimport android.widget.Toast; 42b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 435986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurikimport java.io.IOException; 445986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 45b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira/** 46b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * Service that handles media playback. This is the Service through which we perform all the media 475986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik * handling in our application. Upon initialization, it starts a {@link MusicRetriever} to scan 48b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * the user's media. Then, it waits for Intents (which come from our main activity, 49b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * {@link MainActivity}, which signal the service to perform specific operations: Play, Pause, 50b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * Rewind, Skip, etc. 51b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira */ 52b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveirapublic class MusicService extends Service implements OnCompletionListener, OnPreparedListener, 53b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira OnErrorListener, MusicFocusable, 54b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira PrepareMusicRetrieverTask.MusicRetrieverPreparedListener { 55b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 565986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik // The tag we put on debug messages 575986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik final static String TAG = "RandomMusicPlayer"; 585986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 595986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik // These are the Intent actions that we are prepared to handle. Notice that the fact these 605986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik // constants exist in our class is a mere convenience: what really defines the actions our 615986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik // service can handle are the <action> tags in the <intent-filters> tag for our service in 625986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik // AndroidManifest.xml. 635986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik public static final String ACTION_TOGGLE_PLAYBACK = 645986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik "com.example.android.musicplayer.action.TOGGLE_PLAYBACK"; 655986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik public static final String ACTION_PLAY = "com.example.android.musicplayer.action.PLAY"; 665986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik public static final String ACTION_PAUSE = "com.example.android.musicplayer.action.PAUSE"; 675986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik public static final String ACTION_STOP = "com.example.android.musicplayer.action.STOP"; 685986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik public static final String ACTION_SKIP = "com.example.android.musicplayer.action.SKIP"; 695986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik public static final String ACTION_REWIND = "com.example.android.musicplayer.action.REWIND"; 705986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik public static final String ACTION_URL = "com.example.android.musicplayer.action.URL"; 715986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 725986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik // The volume we set the media player to when we lose audio focus, but are allowed to reduce 735986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik // the volume instead of stopping playback. 745986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik public static final float DUCK_VOLUME = 0.1f; 75b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 76b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // our media player 77b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira MediaPlayer mPlayer = null; 78b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 79b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // our AudioFocusHelper object, if it's available (it's available on SDK level >= 8) 80b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // If not available, this will be null. Always check for null before using! 81b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira AudioFocusHelper mAudioFocusHelper = null; 82b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 83b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // indicates the state our service: 84b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira enum State { 85b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira Retrieving, // the MediaRetriever is retrieving music 86b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira Stopped, // media player is stopped and not prepared to play 87b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira Preparing, // media player is preparing... 88b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira Playing, // playback active (media player ready!). (but the media player may actually be 89b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // paused in this state if we don't have audio focus. But we stay in this state 90b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // so that we know we have to resume playback once we get focus back) 91b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira Paused // playback paused (media player ready!) 92b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira }; 93b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 94b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira State mState = State.Retrieving; 95b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 96b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // if in Retrieving mode, this flag indicates whether we should start playing immediately 97b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // when we are ready or not. 98b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira boolean mStartPlayingAfterRetrieve = false; 99b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 100b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // if mStartPlayingAfterRetrieve is true, this variable indicates the URL that we should 101b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // start playing when we are ready. If null, we should play a random song from the device 102b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira Uri mWhatToPlayAfterRetrieve = null; 103b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 104b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira enum PauseReason { 105b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira UserRequest, // paused by user request 106b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira FocusLoss, // paused because of audio focus loss 107b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira }; 108b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 109b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // why did we pause? (only relevant if mState == State.Paused) 110b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira PauseReason mPauseReason = PauseReason.UserRequest; 111b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 112b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // do we have audio focus? 113b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira enum AudioFocus { 114b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira NoFocusNoDuck, // we don't have audio focus, and can't duck 115b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira NoFocusCanDuck, // we don't have focus, but can play at a low volume ("ducking") 116b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira Focused // we have full audio focus 117b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 118b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira AudioFocus mAudioFocus = AudioFocus.NoFocusNoDuck; 119b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 120b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // title of the song we are currently playing 121b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira String mSongTitle = ""; 122b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 123b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // whether the song we are playing is streaming from the network 124b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira boolean mIsStreaming = false; 125b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 126b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // Wifi lock that we hold when streaming files from the internet, in order to prevent the 127b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // device from shutting off the Wifi radio 128b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira WifiLock mWifiLock; 129b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 130b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // The ID we use for the notification (the onscreen alert that appears at the notification 131b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // area at the top of the screen as an icon -- and as text as well if the user expands the 132b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // notification area). 133b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira final int NOTIFICATION_ID = 1; 134b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 135b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // Our instance of our MusicRetriever, which handles scanning for media and 136b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // providing titles and URIs as we need. 137b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira MusicRetriever mRetriever; 138b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 1395986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik // our RemoteControlClient object, which will use remote control APIs available in 1405986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik // SDK level >= 14, if they're available. 1415986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik RemoteControlClientCompat mRemoteControlClientCompat; 1425986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 1435986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik // Dummy album art we will pass to the remote control (if the APIs are available). 1445986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik Bitmap mDummyAlbumArt; 1455986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 1465986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik // The component name of MusicIntentReceiver, for use with media button and remote control 1475986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik // APIs 1485986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik ComponentName mMediaButtonReceiverComponent; 1495986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 1505986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik AudioManager mAudioManager; 1515986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik NotificationManager mNotificationManager; 1525986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 153b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira Notification mNotification = null; 154b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 155b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira /** 156b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * Makes sure the media player exists and has been reset. This will create the media player 157b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * if needed, or reset the existing media player if one already exists. 158b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira */ 159b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira void createMediaPlayerIfNeeded() { 160b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira if (mPlayer == null) { 161b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mPlayer = new MediaPlayer(); 162b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 163b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // Make sure the media player will acquire a wake-lock while playing. If we don't do 164b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // that, the CPU might go to sleep while the song is playing, causing playback to stop. 165b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // 166b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // Remember that to use this, we have to declare the android.permission.WAKE_LOCK 167b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // permission in AndroidManifest.xml. 168b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK); 169b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 170b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // we want the media player to notify us when it's ready preparing, and when it's done 171b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // playing: 172b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mPlayer.setOnPreparedListener(this); 173b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mPlayer.setOnCompletionListener(this); 174b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mPlayer.setOnErrorListener(this); 175b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 176b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira else 177b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mPlayer.reset(); 178b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 179b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 180b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira @Override 181b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira public void onCreate() { 182b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira Log.i(TAG, "debug: Creating service"); 183b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 184b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // Create the Wifi lock (this does not acquire the lock, this just creates it) 185b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mWifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE)) 186b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock"); 187b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 188b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 1895986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE); 190b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 191b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // Create the retriever and start an asynchronous task that will prepare it. 192b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mRetriever = new MusicRetriever(getContentResolver()); 193b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira (new PrepareMusicRetrieverTask(mRetriever,this)).execute(); 194b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 195b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // create the Audio Focus Helper, if the Audio Focus feature is available (SDK 8 or above) 196b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira if (android.os.Build.VERSION.SDK_INT >= 8) 197b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mAudioFocusHelper = new AudioFocusHelper(getApplicationContext(), this); 198b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira else 199b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mAudioFocus = AudioFocus.Focused; // no focus feature, so we always "have" audio focus 2005986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 2015986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik mDummyAlbumArt = BitmapFactory.decodeResource(getResources(), R.drawable.dummy_album_art); 2025986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 2035986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik mMediaButtonReceiverComponent = new ComponentName(this, MusicIntentReceiver.class); 204b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 205b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 206b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira /** 207b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * Called when we receive an Intent. When we receive an intent sent to us via startService(), 208b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * this is the method that gets called. So here we react appropriately depending on the 209b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * Intent's action, which specifies what is being requested of us. 210b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira */ 211b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira @Override 212b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira public int onStartCommand(Intent intent, int flags, int startId) { 213b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira String action = intent.getAction(); 2145986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik if (action.equals(ACTION_TOGGLE_PLAYBACK)) processTogglePlaybackRequest(); 2155986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik else if (action.equals(ACTION_PLAY)) processPlayRequest(); 216b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira else if (action.equals(ACTION_PAUSE)) processPauseRequest(); 217b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira else if (action.equals(ACTION_SKIP)) processSkipRequest(); 218b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira else if (action.equals(ACTION_STOP)) processStopRequest(); 219b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira else if (action.equals(ACTION_REWIND)) processRewindRequest(); 220b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira else if (action.equals(ACTION_URL)) processAddRequest(intent); 221b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 222b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira return START_NOT_STICKY; // Means we started the service, but don't want it to 223b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // restart in case it's killed. 224b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 225b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 2265986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik void processTogglePlaybackRequest() { 2275986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik if (mState == State.Paused || mState == State.Stopped) { 2285986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik processPlayRequest(); 2295986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik } else { 2305986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik processPauseRequest(); 2315986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik } 2325986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik } 2335986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 234b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira void processPlayRequest() { 235b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira if (mState == State.Retrieving) { 236b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // If we are still retrieving media, just set the flag to start playing when we're 237b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // ready 238b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mWhatToPlayAfterRetrieve = null; // play a random song 239b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mStartPlayingAfterRetrieve = true; 240b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira return; 241b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 242b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 243b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira tryToGetAudioFocus(); 244b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 2455986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik // actually play the song 2465986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 247b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira if (mState == State.Stopped) { 248b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // If we're stopped, just go ahead to the next song and start playing 249b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira playNextSong(null); 250b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 251b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira else if (mState == State.Paused) { 252b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // If we're paused, just continue playback and restore the 'foreground service' state. 253b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mState = State.Playing; 254b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira setUpAsForeground(mSongTitle + " (playing)"); 255b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira configAndStartMediaPlayer(); 256b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 2575986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 2585986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik // Tell any remote controls that our playback state is 'playing'. 2595986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik if (mRemoteControlClientCompat != null) { 2605986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik mRemoteControlClientCompat 2615986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik .setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING); 2625986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik } 263b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 264b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 265b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira void processPauseRequest() { 266b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira if (mState == State.Retrieving) { 267b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // If we are still retrieving media, clear the flag that indicates we should start 268b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // playing when we're ready 269b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mStartPlayingAfterRetrieve = false; 270b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira return; 271b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 272b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 273b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira if (mState == State.Playing) { 274b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // Pause media player and cancel the 'foreground service' state. 275b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mState = State.Paused; 276b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mPlayer.pause(); 277b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira relaxResources(false); // while paused, we always retain the MediaPlayer 2785986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik // do not give up audio focus 2795986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik } 2805986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 2815986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik // Tell any remote controls that our playback state is 'paused'. 2825986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik if (mRemoteControlClientCompat != null) { 2835986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik mRemoteControlClientCompat 2845986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik .setPlaybackState(RemoteControlClient.PLAYSTATE_PAUSED); 285b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 286b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 287b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 288b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira void processRewindRequest() { 289b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira if (mState == State.Playing || mState == State.Paused) 290b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mPlayer.seekTo(0); 291b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 292b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 293b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira void processSkipRequest() { 294b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira if (mState == State.Playing || mState == State.Paused) { 295b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira tryToGetAudioFocus(); 296b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira playNextSong(null); 297b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 298b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 299b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 300b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira void processStopRequest() { 3015986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik processStopRequest(false); 3025986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik } 3035986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 3045986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik void processStopRequest(boolean force) { 3055986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik if (mState == State.Playing || mState == State.Paused || force) { 306b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mState = State.Stopped; 307b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 308b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // let go of all resources... 309b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira relaxResources(true); 310b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira giveUpAudioFocus(); 311b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 3125986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik // Tell any remote controls that our playback state is 'paused'. 3135986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik if (mRemoteControlClientCompat != null) { 3145986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik mRemoteControlClientCompat 3155986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik .setPlaybackState(RemoteControlClient.PLAYSTATE_STOPPED); 3165986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik } 3175986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 318b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // service is no longer necessary. Will be started again if needed. 319b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira stopSelf(); 320b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 321b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 322b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 323b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira /** 324b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * Releases resources used by the service for playback. This includes the "foreground service" 325b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * status and notification, the wake locks and possibly the MediaPlayer. 326b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * 327b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * @param releaseMediaPlayer Indicates whether the Media Player should also be released or not 328b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira */ 329b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira void relaxResources(boolean releaseMediaPlayer) { 330b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // stop being a foreground service 331b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira stopForeground(true); 332b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 333b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // stop and release the Media Player, if it's available 334b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira if (releaseMediaPlayer && mPlayer != null) { 335b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mPlayer.reset(); 336b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mPlayer.release(); 337b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mPlayer = null; 338b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 339b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 340b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // we can also release the Wifi lock, if we're holding it 341b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira if (mWifiLock.isHeld()) mWifiLock.release(); 342b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 343b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 344b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira void giveUpAudioFocus() { 345b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira if (mAudioFocus == AudioFocus.Focused && mAudioFocusHelper != null 346b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira && mAudioFocusHelper.abandonFocus()) 347b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mAudioFocus = AudioFocus.NoFocusNoDuck; 348b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 349b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 350b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira /** 351b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * Reconfigures MediaPlayer according to audio focus settings and starts/restarts it. This 352b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * method starts/restarts the MediaPlayer respecting the current audio focus state. So if 353b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * we have focus, it will play normally; if we don't have focus, it will either leave the 354b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * MediaPlayer paused or set it to a low volume, depending on what is allowed by the 355b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * current focus settings. This method assumes mPlayer != null, so if you are calling it, 356b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * you have to do so from a context where you are sure this is the case. 357b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira */ 358b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira void configAndStartMediaPlayer() { 359b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira if (mAudioFocus == AudioFocus.NoFocusNoDuck) { 360b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // If we don't have audio focus and can't duck, we have to pause, even if mState 361b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // is State.Playing. But we stay in the Playing state so that we know we have to resume 362b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // playback once we get the focus back. 363b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira if (mPlayer.isPlaying()) mPlayer.pause(); 364b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira return; 365b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 366b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira else if (mAudioFocus == AudioFocus.NoFocusCanDuck) 367b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mPlayer.setVolume(DUCK_VOLUME, DUCK_VOLUME); // we'll be relatively quiet 368b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira else 369b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mPlayer.setVolume(1.0f, 1.0f); // we can be loud 370b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 371b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira if (!mPlayer.isPlaying()) mPlayer.start(); 372b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 373b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 374b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira void processAddRequest(Intent intent) { 375b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // user wants to play a song directly by URL or path. The URL or path comes in the "data" 376b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // part of the Intent. This Intent is sent by {@link MainActivity} after the user 377b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // specifies the URL/path via an alert box. 378b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira if (mState == State.Retrieving) { 379b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // we'll play the requested URL right after we finish retrieving 380b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mWhatToPlayAfterRetrieve = intent.getData(); 381b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mStartPlayingAfterRetrieve = true; 382b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 383b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira else if (mState == State.Playing || mState == State.Paused || mState == State.Stopped) { 384b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira Log.i(TAG, "Playing from URL/path: " + intent.getData().toString()); 385b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira tryToGetAudioFocus(); 386b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira playNextSong(intent.getData().toString()); 387b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 388b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 389b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 390b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira void tryToGetAudioFocus() { 391b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira if (mAudioFocus != AudioFocus.Focused && mAudioFocusHelper != null 392b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira && mAudioFocusHelper.requestFocus()) 393b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mAudioFocus = AudioFocus.Focused; 394b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 395b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 396b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira /** 397b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * Starts playing the next song. If manualUrl is null, the next song will be randomly selected 398b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * from our Media Retriever (that is, it will be a random song in the user's device). If 399b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * manualUrl is non-null, then it specifies the URL or path to the song that will be played 400b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * next. 401b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira */ 402b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira void playNextSong(String manualUrl) { 403b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mState = State.Stopped; 404b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira relaxResources(false); // release everything except MediaPlayer 405b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 406b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira try { 4075986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik MusicRetriever.Item playingItem = null; 408b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira if (manualUrl != null) { 409b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // set the source of the media player to a manual URL or path 410b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira createMediaPlayerIfNeeded(); 411b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); 412b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mPlayer.setDataSource(manualUrl); 413b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mIsStreaming = manualUrl.startsWith("http:") || manualUrl.startsWith("https:"); 4145986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 4155986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik playingItem = new MusicRetriever.Item(0, null, manualUrl, null, 0); 416b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 417b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira else { 418b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mIsStreaming = false; // playing a locally available song 419b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 4205986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik playingItem = mRetriever.getRandomItem(); 4215986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik if (playingItem == null) { 4225986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik Toast.makeText(this, 4235986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik "No available music to play. Place some music on your external storage " 4245986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik + "device (e.g. your SD card) and try again.", 4255986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik Toast.LENGTH_LONG).show(); 4265986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik processStopRequest(true); // stop everything! 427b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira return; 428b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 429b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 430b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // set the source of the media player a a content URI 431b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira createMediaPlayerIfNeeded(); 432b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); 4335986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik mPlayer.setDataSource(getApplicationContext(), playingItem.getURI()); 434b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 435b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 4365986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik mSongTitle = playingItem.getTitle(); 437b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 438b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mState = State.Preparing; 439b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira setUpAsForeground(mSongTitle + " (loading)"); 440b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 4415986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik // Use the media button APIs (if available) to register ourselves for media button 4425986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik // events 4435986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 4445986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik MediaButtonHelper.registerMediaButtonEventReceiverCompat( 4455986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik mAudioManager, mMediaButtonReceiverComponent); 4465986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 4475986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik // Use the remote control APIs (if available) to set the playback state 4485986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 4495986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik if (mRemoteControlClientCompat == null) { 4505986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON); 4515986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik intent.setComponent(mMediaButtonReceiverComponent); 4525986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik mRemoteControlClientCompat = new RemoteControlClientCompat( 4535986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik PendingIntent.getBroadcast(this /*context*/, 4545986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 0 /*requestCode, ignored*/, intent /*intent*/, 0 /*flags*/)); 4555986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik RemoteControlHelper.registerRemoteControlClient(mAudioManager, 4565986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik mRemoteControlClientCompat); 4575986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik } 4585986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 4595986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik mRemoteControlClientCompat.setPlaybackState( 4605986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik RemoteControlClient.PLAYSTATE_PLAYING); 4615986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 4625986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik mRemoteControlClientCompat.setTransportControlFlags( 4635986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik RemoteControlClient.FLAG_KEY_MEDIA_PLAY | 4645986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik RemoteControlClient.FLAG_KEY_MEDIA_PAUSE | 4655986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik RemoteControlClient.FLAG_KEY_MEDIA_NEXT | 4665986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik RemoteControlClient.FLAG_KEY_MEDIA_STOP); 4675986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 4685986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik // Update the remote controls 4695986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik mRemoteControlClientCompat.editMetadata(true) 4705986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik .putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, playingItem.getArtist()) 4715986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik .putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, playingItem.getAlbum()) 4725986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik .putString(MediaMetadataRetriever.METADATA_KEY_TITLE, playingItem.getTitle()) 4735986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik .putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, 4745986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik playingItem.getDuration()) 4755986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik // TODO: fetch real item artwork 4765986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik .putBitmap( 4775986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik RemoteControlClientCompat.MetadataEditorCompat.METADATA_KEY_ARTWORK, 4785986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik mDummyAlbumArt) 4795986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik .apply(); 4805986e12034e38fd165d2f100c7e404bbf61b2649Roman Nurik 481b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // starts preparing the media player in the background. When it's done, it will call 482b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // our OnPreparedListener (that is, the onPrepared() method on this class, since we set 483b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // the listener to 'this'). 484b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // 485b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // Until the media player is prepared, we *cannot* call start() on it! 486b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mPlayer.prepareAsync(); 487b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 488b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // If we are streaming from the internet, we want to hold a Wifi lock, which prevents 489b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // the Wifi radio from going to sleep while the song is playing. If, on the other hand, 490b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // we are *not* streaming, we want to release the lock if we were holding it before. 491b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira if (mIsStreaming) mWifiLock.acquire(); 492b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira else if (mWifiLock.isHeld()) mWifiLock.release(); 493b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 494b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira catch (IOException ex) { 495b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira Log.e("MusicService", "IOException playing next song: " + ex.getMessage()); 496b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira ex.printStackTrace(); 497b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 498b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 499b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 500b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira /** Called when media player is done playing current song. */ 501b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira public void onCompletion(MediaPlayer player) { 502b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // The media player finished playing the current song, so we go ahead and start the next. 503b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira playNextSong(null); 504b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 505b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 506b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira /** Called when media player is done preparing. */ 507b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira public void onPrepared(MediaPlayer player) { 508b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // The media player is done preparing. That means we can start playing! 509b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mState = State.Playing; 510b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira updateNotification(mSongTitle + " (playing)"); 511b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira configAndStartMediaPlayer(); 512b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 513b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 514b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira /** Updates the notification. */ 515b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira void updateNotification(String text) { 516b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0, 517b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira new Intent(getApplicationContext(), MainActivity.class), 518b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira PendingIntent.FLAG_UPDATE_CURRENT); 519b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mNotification.setLatestEventInfo(getApplicationContext(), "RandomMusicPlayer", text, pi); 520b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mNotificationManager.notify(NOTIFICATION_ID, mNotification); 521b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 522b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 523b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira /** 524b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * Configures service as a foreground service. A foreground service is a service that's doing 525b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * something the user is actively aware of (such as playing music), and must appear to the 526b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * user as a notification. That's why we create the notification here. 527b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira */ 528b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira void setUpAsForeground(String text) { 529b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0, 530b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira new Intent(getApplicationContext(), MainActivity.class), 531b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira PendingIntent.FLAG_UPDATE_CURRENT); 532b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mNotification = new Notification(); 533b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mNotification.tickerText = text; 534b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mNotification.icon = R.drawable.ic_stat_playing; 535b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mNotification.flags |= Notification.FLAG_ONGOING_EVENT; 536b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mNotification.setLatestEventInfo(getApplicationContext(), "RandomMusicPlayer", 537b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira text, pi); 538b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira startForeground(NOTIFICATION_ID, mNotification); 539b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 540b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 541b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira /** 542b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * Called when there's an error playing media. When this happens, the media player goes to 543b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira * the Error state. We warn the user about the error and reset the media player. 544b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira */ 545b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira public boolean onError(MediaPlayer mp, int what, int extra) { 546b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira Toast.makeText(getApplicationContext(), "Media player error! Resetting.", 547b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira Toast.LENGTH_SHORT).show(); 548b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira Log.e(TAG, "Error: what=" + String.valueOf(what) + ", extra=" + String.valueOf(extra)); 549b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 550b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mState = State.Stopped; 551b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira relaxResources(true); 552b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira giveUpAudioFocus(); 553b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira return true; // true indicates we handled the error 554b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 555b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 556b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira public void onGainedAudioFocus() { 557b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira Toast.makeText(getApplicationContext(), "gained audio focus.", Toast.LENGTH_SHORT).show(); 558b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mAudioFocus = AudioFocus.Focused; 559b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 560b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // restart media player with new focus settings 561b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira if (mState == State.Playing) 562b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira configAndStartMediaPlayer(); 563b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 564b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 565b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira public void onLostAudioFocus(boolean canDuck) { 566b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira Toast.makeText(getApplicationContext(), "lost audio focus." + (canDuck ? "can duck" : 567b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira "no duck"), Toast.LENGTH_SHORT).show(); 568b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mAudioFocus = canDuck ? AudioFocus.NoFocusCanDuck : AudioFocus.NoFocusNoDuck; 569b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 570b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // start/restart/pause media player with new focus settings 571b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira if (mPlayer != null && mPlayer.isPlaying()) 572b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira configAndStartMediaPlayer(); 573b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 574b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 575b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira public void onMusicRetrieverPrepared() { 576b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // Done retrieving! 577b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mState = State.Stopped; 578b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 579b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // If the flag indicates we should start playing after retrieving, let's do that now. 580b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira if (mStartPlayingAfterRetrieve) { 581b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira tryToGetAudioFocus(); 582b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira playNextSong(mWhatToPlayAfterRetrieve == null ? 583b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira null : mWhatToPlayAfterRetrieve.toString()); 584b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 585b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 586b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 587b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 588b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira @Override 589b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira public void onDestroy() { 590b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira // Service is being killed, so make sure we release our resources 591b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira mState = State.Stopped; 592b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira relaxResources(true); 593b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira giveUpAudioFocus(); 594b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 595b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira 596b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira @Override 597b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira public IBinder onBind(Intent arg0) { 598b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira return null; 599b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira } 600b30a3010f51cb3631011d84ac948496f044fc7f5Bruno Oliveira} 601