Home | History | Annotate | Download | only in service
      1 /*
      2  * Copyright 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.example.android.mediasession.service;
     18 
     19 import android.app.Notification;
     20 import android.content.Intent;
     21 import android.os.Bundle;
     22 import android.support.annotation.NonNull;
     23 import android.support.v4.content.ContextCompat;
     24 import android.support.v4.media.MediaBrowserCompat;
     25 import android.support.v4.media.MediaBrowserServiceCompat;
     26 import android.support.v4.media.MediaDescriptionCompat;
     27 import android.support.v4.media.MediaMetadataCompat;
     28 import android.support.v4.media.session.MediaSessionCompat;
     29 import android.support.v4.media.session.PlaybackStateCompat;
     30 import android.util.Log;
     31 
     32 import com.example.android.mediasession.service.contentcatalogs.MusicLibrary;
     33 import com.example.android.mediasession.service.notifications.MediaNotificationManager;
     34 import com.example.android.mediasession.service.players.MediaPlayerAdapter;
     35 
     36 import java.util.ArrayList;
     37 import java.util.List;
     38 
     39 public class MusicService extends MediaBrowserServiceCompat {
     40 
     41     private static final String TAG = MusicService.class.getSimpleName();
     42 
     43     private MediaSessionCompat mSession;
     44     private PlayerAdapter mPlayback;
     45     private MediaNotificationManager mMediaNotificationManager;
     46     private MediaSessionCallback mCallback;
     47     private boolean mServiceInStartedState;
     48 
     49     @Override
     50     public void onCreate() {
     51         super.onCreate();
     52 
     53         // Create a new MediaSession.
     54         mSession = new MediaSessionCompat(this, "MusicService");
     55         mCallback = new MediaSessionCallback();
     56         mSession.setCallback(mCallback);
     57         mSession.setFlags(
     58                 MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
     59                 MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS |
     60                 MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
     61         setSessionToken(mSession.getSessionToken());
     62 
     63         mMediaNotificationManager = new MediaNotificationManager(this);
     64 
     65         mPlayback = new MediaPlayerAdapter(this, new MediaPlayerListener());
     66         Log.d(TAG, "onCreate: MusicService creating MediaSession, and MediaNotificationManager");
     67     }
     68 
     69     @Override
     70     public void onTaskRemoved(Intent rootIntent) {
     71         super.onTaskRemoved(rootIntent);
     72         stopSelf();
     73     }
     74 
     75     @Override
     76     public void onDestroy() {
     77         mMediaNotificationManager.onDestroy();
     78         mPlayback.stop();
     79         mSession.release();
     80         Log.d(TAG, "onDestroy: MediaPlayerAdapter stopped, and MediaSession released");
     81     }
     82 
     83     @Override
     84     public BrowserRoot onGetRoot(@NonNull String clientPackageName,
     85                                  int clientUid,
     86                                  Bundle rootHints) {
     87         return new BrowserRoot(MusicLibrary.getRoot(), null);
     88     }
     89 
     90     @Override
     91     public void onLoadChildren(
     92             @NonNull final String parentMediaId,
     93             @NonNull final Result<List<MediaBrowserCompat.MediaItem>> result) {
     94         result.sendResult(MusicLibrary.getMediaItems());
     95     }
     96 
     97     // MediaSession Callback: Transport Controls -> MediaPlayerAdapter
     98     public class MediaSessionCallback extends MediaSessionCompat.Callback {
     99         private final List<MediaSessionCompat.QueueItem> mPlaylist = new ArrayList<>();
    100         private int mQueueIndex = -1;
    101         private MediaMetadataCompat mPreparedMedia;
    102 
    103         @Override
    104         public void onAddQueueItem(MediaDescriptionCompat description) {
    105             mPlaylist.add(new MediaSessionCompat.QueueItem(description, description.hashCode()));
    106             mQueueIndex = (mQueueIndex == -1) ? 0 : mQueueIndex;
    107             mSession.setQueue(mPlaylist);
    108         }
    109 
    110         @Override
    111         public void onRemoveQueueItem(MediaDescriptionCompat description) {
    112             mPlaylist.remove(new MediaSessionCompat.QueueItem(description, description.hashCode()));
    113             mQueueIndex = (mPlaylist.isEmpty()) ? -1 : mQueueIndex;
    114             mSession.setQueue(mPlaylist);
    115         }
    116 
    117         @Override
    118         public void onPrepare() {
    119             if (mQueueIndex < 0 && mPlaylist.isEmpty()) {
    120                 // Nothing to play.
    121                 return;
    122             }
    123 
    124             final String mediaId = mPlaylist.get(mQueueIndex).getDescription().getMediaId();
    125             mPreparedMedia = MusicLibrary.getMetadata(MusicService.this, mediaId);
    126             mSession.setMetadata(mPreparedMedia);
    127 
    128             if (!mSession.isActive()) {
    129                 mSession.setActive(true);
    130             }
    131         }
    132 
    133         @Override
    134         public void onPlay() {
    135             if (!isReadyToPlay()) {
    136                 // Nothing to play.
    137                 return;
    138             }
    139 
    140             if (mPreparedMedia == null) {
    141                 onPrepare();
    142             }
    143 
    144             mPlayback.playFromMedia(mPreparedMedia);
    145             Log.d(TAG, "onPlayFromMediaId: MediaSession active");
    146         }
    147 
    148         @Override
    149         public void onPause() {
    150             mPlayback.pause();
    151         }
    152 
    153         @Override
    154         public void onStop() {
    155             mPlayback.stop();
    156             mSession.setActive(false);
    157         }
    158 
    159         @Override
    160         public void onSkipToNext() {
    161             mQueueIndex = (++mQueueIndex % mPlaylist.size());
    162             mPreparedMedia = null;
    163             onPlay();
    164         }
    165 
    166         @Override
    167         public void onSkipToPrevious() {
    168             mQueueIndex = mQueueIndex > 0 ? mQueueIndex - 1 : mPlaylist.size() - 1;
    169             mPreparedMedia = null;
    170             onPlay();
    171         }
    172 
    173         @Override
    174         public void onSeekTo(long pos) {
    175             mPlayback.seekTo(pos);
    176         }
    177 
    178         private boolean isReadyToPlay() {
    179             return (!mPlaylist.isEmpty());
    180         }
    181     }
    182 
    183     // MediaPlayerAdapter Callback: MediaPlayerAdapter state -> MusicService.
    184     public class MediaPlayerListener extends PlaybackInfoListener {
    185 
    186         private final ServiceManager mServiceManager;
    187 
    188         MediaPlayerListener() {
    189             mServiceManager = new ServiceManager();
    190         }
    191 
    192         @Override
    193         public void onPlaybackStateChange(PlaybackStateCompat state) {
    194             // Report the state to the MediaSession.
    195             mSession.setPlaybackState(state);
    196 
    197             // Manage the started state of this service.
    198             switch (state.getState()) {
    199                 case PlaybackStateCompat.STATE_PLAYING:
    200                     mServiceManager.moveServiceToStartedState(state);
    201                     break;
    202                 case PlaybackStateCompat.STATE_PAUSED:
    203                     mServiceManager.updateNotificationForPause(state);
    204                     break;
    205                 case PlaybackStateCompat.STATE_STOPPED:
    206                     mServiceManager.moveServiceOutOfStartedState(state);
    207                     break;
    208             }
    209         }
    210 
    211         class ServiceManager {
    212 
    213             private void moveServiceToStartedState(PlaybackStateCompat state) {
    214                 Notification notification =
    215                         mMediaNotificationManager.getNotification(
    216                                 mPlayback.getCurrentMedia(), state, getSessionToken());
    217 
    218                 if (!mServiceInStartedState) {
    219                     ContextCompat.startForegroundService(
    220                             MusicService.this,
    221                             new Intent(MusicService.this, MusicService.class));
    222                     mServiceInStartedState = true;
    223                 }
    224 
    225                 startForeground(MediaNotificationManager.NOTIFICATION_ID, notification);
    226             }
    227 
    228             private void updateNotificationForPause(PlaybackStateCompat state) {
    229                 stopForeground(false);
    230                 Notification notification =
    231                         mMediaNotificationManager.getNotification(
    232                                 mPlayback.getCurrentMedia(), state, getSessionToken());
    233                 mMediaNotificationManager.getNotificationManager()
    234                         .notify(MediaNotificationManager.NOTIFICATION_ID, notification);
    235             }
    236 
    237             private void moveServiceOutOfStartedState(PlaybackStateCompat state) {
    238                 stopForeground(true);
    239                 stopSelf();
    240                 mServiceInStartedState = false;
    241             }
    242         }
    243 
    244     }
    245 
    246 }