Added Android code
[wl-app.git] / Android / app / src / main / java / com / moiseum / wolnelektury / view / player / service / AudiobookService.java
diff --git a/Android/app/src/main/java/com/moiseum/wolnelektury/view/player/service/AudiobookService.java b/Android/app/src/main/java/com/moiseum/wolnelektury/view/player/service/AudiobookService.java
new file mode 100755 (executable)
index 0000000..a95f53f
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * 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.moiseum.wolnelektury.view.player.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 java.util.ArrayList;
+import java.util.List;
+
+public class AudiobookService extends MediaBrowserServiceCompat {
+
+    private static final String TAG = AudiobookService.class.getSimpleName();
+
+       public static final String ACTION_CLEAR_PLAYLIST = "CommandClear";
+       public static final String EXTRA_PLAYBACK_CURRENT = "PlaybackCurrent";
+       public static final String EXTRA_PLAYBACK_TOTAL = "PlaybackTotal";
+
+    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, "AudiobookService");
+        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: AudiobookService 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(AudiobookLibrary.getRoot(), null);
+    }
+
+    @Override
+    public void onLoadChildren(
+            @NonNull final String parentMediaId,
+            @NonNull final Result<List<MediaBrowserCompat.MediaItem>> result) {
+        result.sendResult(AudiobookLibrary.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 onCustomAction(String action, Bundle extras) {
+                   if (ACTION_CLEAR_PLAYLIST.equals(action)) {
+                           mPlaylist.clear();
+                           mQueueIndex = 0;
+                           mSession.setQueue(mPlaylist);
+                   }
+           }
+
+           @Override
+        public void onAddQueueItem(MediaDescriptionCompat description) {
+            mPlaylist.add(new MediaSessionCompat.QueueItem(description, description.hashCode()));
+            mQueueIndex = (mQueueIndex == -1) ? 0 : mQueueIndex;
+            mSession.setQueue(mPlaylist);
+        }
+
+        @Override
+        public void onRemoveQueueItem(MediaDescriptionCompat description) {
+            mPlaylist.remove(new MediaSessionCompat.QueueItem(description, description.hashCode()));
+            mQueueIndex = (mPlaylist.isEmpty()) ? -1 : mQueueIndex;
+            mSession.setQueue(mPlaylist);
+        }
+
+        @Override
+        public void onPrepare() {
+            if (mQueueIndex < 0 && mPlaylist.isEmpty()) {
+                // Nothing to play.
+                return;
+            }
+
+            final String mediaId = mPlaylist.get(mQueueIndex).getDescription().getMediaId();
+            mPreparedMedia = AudiobookLibrary.getMetadata(AudiobookService.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 onSkipToQueueItem(long id) {
+            mQueueIndex = (int) id;
+            mPreparedMedia = null;
+            onPlay();
+        }
+
+        @Override
+        public void onSeekTo(long pos) {
+            mPlayback.seekTo(pos);
+        }
+
+           @Override
+           public void onFastForward() {
+                   mPlayback.fastForward();
+           }
+
+           @Override
+           public void onRewind() {
+                   mPlayback.rewind();
+           }
+
+           private boolean isReadyToPlay() {
+            return (!mPlaylist.isEmpty());
+        }
+    }
+
+    // MediaPlayerAdapter Callback: MediaPlayerAdapter state -> AudiobookService.
+    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;
+            }
+        }
+
+           @Override
+           public void onPlaybackProgress(int current) {
+                   Bundle bundle = new Bundle();
+                   bundle.putInt(EXTRA_PLAYBACK_CURRENT, current);
+                   mSession.setExtras(bundle);
+           }
+
+           @Override
+           public void onPlaybackPrepared(int duration) {
+                   Bundle bundle = new Bundle();
+                   bundle.putInt(EXTRA_PLAYBACK_TOTAL, duration);
+                   mSession.setExtras(bundle);
+           }
+
+           class ServiceManager {
+
+            private void moveServiceToStartedState(PlaybackStateCompat state) {
+                Notification notification =
+                        mMediaNotificationManager.getNotification(
+                                mPlayback.getCurrentMedia(), state, getSessionToken());
+
+                if (!mServiceInStartedState) {
+                    ContextCompat.startForegroundService(
+                            AudiobookService.this,
+                            new Intent(AudiobookService.this, AudiobookService.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;
+            }
+        }
+
+    }
+
+}
\ No newline at end of file