| /* |
| * Copyright 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.example.android.mediasession.service; |
| |
| import android.app.Notification; |
| import android.content.Intent; |
| import android.os.Bundle; |
| import android.support.annotation.NonNull; |
| import android.support.v4.content.ContextCompat; |
| import android.support.v4.media.MediaBrowserCompat; |
| import android.support.v4.media.MediaBrowserServiceCompat; |
| import android.support.v4.media.MediaDescriptionCompat; |
| import android.support.v4.media.MediaMetadataCompat; |
| import android.support.v4.media.session.MediaSessionCompat; |
| import android.support.v4.media.session.PlaybackStateCompat; |
| import android.util.Log; |
| |
| import com.example.android.mediasession.service.contentcatalogs.MusicLibrary; |
| import com.example.android.mediasession.service.notifications.MediaNotificationManager; |
| import com.example.android.mediasession.service.players.MediaPlayerAdapter; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| public class MusicService extends MediaBrowserServiceCompat { |
| |
| private static final String TAG = MusicService.class.getSimpleName(); |
| |
| private MediaSessionCompat mSession; |
| private PlayerAdapter mPlayback; |
| private MediaNotificationManager mMediaNotificationManager; |
| private MediaSessionCallback mCallback; |
| private boolean mServiceInStartedState; |
| |
| @Override |
| public void onCreate() { |
| super.onCreate(); |
| |
| // Create a new MediaSession. |
| mSession = new MediaSessionCompat(this, "MusicService"); |
| mCallback = new MediaSessionCallback(); |
| mSession.setCallback(mCallback); |
| mSession.setFlags( |
| MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | |
| MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS | |
| MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS); |
| setSessionToken(mSession.getSessionToken()); |
| |
| mMediaNotificationManager = new MediaNotificationManager(this); |
| |
| mPlayback = new MediaPlayerAdapter(this, new MediaPlayerListener()); |
| Log.d(TAG, "onCreate: MusicService creating MediaSession, and MediaNotificationManager"); |
| } |
| |
| @Override |
| public void onTaskRemoved(Intent rootIntent) { |
| super.onTaskRemoved(rootIntent); |
| stopSelf(); |
| } |
| |
| @Override |
| public void onDestroy() { |
| mMediaNotificationManager.onDestroy(); |
| mPlayback.stop(); |
| mSession.release(); |
| Log.d(TAG, "onDestroy: MediaPlayerAdapter stopped, and MediaSession released"); |
| } |
| |
| @Override |
| public BrowserRoot onGetRoot(@NonNull String clientPackageName, |
| int clientUid, |
| Bundle rootHints) { |
| return new BrowserRoot(MusicLibrary.getRoot(), null); |
| } |
| |
| @Override |
| public void onLoadChildren( |
| @NonNull final String parentMediaId, |
| @NonNull final Result<List<MediaBrowserCompat.MediaItem>> result) { |
| result.sendResult(MusicLibrary.getMediaItems()); |
| } |
| |
| // MediaSession Callback: Transport Controls -> MediaPlayerAdapter |
| public class MediaSessionCallback extends MediaSessionCompat.Callback { |
| private final List<MediaSessionCompat.QueueItem> mPlaylist = new ArrayList<>(); |
| private int mQueueIndex = -1; |
| private MediaMetadataCompat mPreparedMedia; |
| |
| @Override |
| public void onAddQueueItem(MediaDescriptionCompat description) { |
| mPlaylist.add(new MediaSessionCompat.QueueItem(description, description.hashCode())); |
| mQueueIndex = (mQueueIndex == -1) ? 0 : mQueueIndex; |
| } |
| |
| @Override |
| public void onRemoveQueueItem(MediaDescriptionCompat description) { |
| mPlaylist.remove(new MediaSessionCompat.QueueItem(description, description.hashCode())); |
| mQueueIndex = (mPlaylist.isEmpty()) ? -1 : mQueueIndex; |
| } |
| |
| @Override |
| public void onPrepare() { |
| if (mQueueIndex < 0 && mPlaylist.isEmpty()) { |
| // Nothing to play. |
| return; |
| } |
| |
| final String mediaId = mPlaylist.get(mQueueIndex).getDescription().getMediaId(); |
| mPreparedMedia = MusicLibrary.getMetadata(MusicService.this, mediaId); |
| mSession.setMetadata(mPreparedMedia); |
| |
| if (!mSession.isActive()) { |
| mSession.setActive(true); |
| } |
| } |
| |
| @Override |
| public void onPlay() { |
| if (!isReadyToPlay()) { |
| // Nothing to play. |
| return; |
| } |
| |
| if (mPreparedMedia == null) { |
| onPrepare(); |
| } |
| |
| mPlayback.playFromMedia(mPreparedMedia); |
| Log.d(TAG, "onPlayFromMediaId: MediaSession active"); |
| } |
| |
| @Override |
| public void onPause() { |
| mPlayback.pause(); |
| } |
| |
| @Override |
| public void onStop() { |
| mPlayback.stop(); |
| mSession.setActive(false); |
| } |
| |
| @Override |
| public void onSkipToNext() { |
| mQueueIndex = (++mQueueIndex % mPlaylist.size()); |
| mPreparedMedia = null; |
| onPlay(); |
| } |
| |
| @Override |
| public void onSkipToPrevious() { |
| mQueueIndex = mQueueIndex > 0 ? mQueueIndex - 1 : mPlaylist.size() - 1; |
| mPreparedMedia = null; |
| onPlay(); |
| } |
| |
| @Override |
| public void onSeekTo(long pos) { |
| mPlayback.seekTo(pos); |
| } |
| |
| private boolean isReadyToPlay() { |
| return (!mPlaylist.isEmpty()); |
| } |
| } |
| |
| // MediaPlayerAdapter Callback: MediaPlayerAdapter state -> MusicService. |
| public class MediaPlayerListener extends PlaybackInfoListener { |
| |
| private final ServiceManager mServiceManager; |
| |
| MediaPlayerListener() { |
| mServiceManager = new ServiceManager(); |
| } |
| |
| @Override |
| public void onPlaybackStateChange(PlaybackStateCompat state) { |
| // Report the state to the MediaSession. |
| mSession.setPlaybackState(state); |
| |
| // Manage the started state of this service. |
| switch (state.getState()) { |
| case PlaybackStateCompat.STATE_PLAYING: |
| mServiceManager.moveServiceToStartedState(state); |
| break; |
| case PlaybackStateCompat.STATE_PAUSED: |
| mServiceManager.updateNotificationForPause(state); |
| break; |
| case PlaybackStateCompat.STATE_STOPPED: |
| mServiceManager.moveServiceOutOfStartedState(state); |
| break; |
| } |
| } |
| |
| class ServiceManager { |
| |
| private void moveServiceToStartedState(PlaybackStateCompat state) { |
| Notification notification = |
| mMediaNotificationManager.getNotification( |
| mPlayback.getCurrentMedia(), state, getSessionToken()); |
| |
| if (!mServiceInStartedState) { |
| ContextCompat.startForegroundService( |
| MusicService.this, |
| new Intent(MusicService.this, MusicService.class)); |
| mServiceInStartedState = true; |
| } |
| |
| startForeground(MediaNotificationManager.NOTIFICATION_ID, notification); |
| } |
| |
| private void updateNotificationForPause(PlaybackStateCompat state) { |
| stopForeground(false); |
| Notification notification = |
| mMediaNotificationManager.getNotification( |
| mPlayback.getCurrentMedia(), state, getSessionToken()); |
| mMediaNotificationManager.getNotificationManager() |
| .notify(MediaNotificationManager.NOTIFICATION_ID, notification); |
| } |
| |
| private void moveServiceOutOfStartedState(PlaybackStateCompat state) { |
| stopForeground(true); |
| stopSelf(); |
| mServiceInStartedState = false; |
| } |
| } |
| |
| } |
| |
| } |