Added Android code
[wl-app.git] / Android / app / src / main / java / com / moiseum / wolnelektury / view / player / PlayerPresenter.java
1 package com.moiseum.wolnelektury.view.player;
2
3 import android.content.Context;
4 import android.os.Bundle;
5 import android.support.annotation.NonNull;
6 import android.support.v4.media.MediaBrowserCompat;
7 import android.support.v4.media.MediaMetadataCompat;
8 import android.support.v4.media.session.MediaControllerCompat;
9 import android.support.v4.media.session.MediaSessionCompat;
10 import android.support.v4.media.session.PlaybackStateCompat;
11 import android.util.Log;
12
13 import com.moiseum.wolnelektury.base.WLApplication;
14 import com.moiseum.wolnelektury.base.mvp.FragmentPresenter;
15 import com.moiseum.wolnelektury.connection.models.BookDetailsModel;
16 import com.moiseum.wolnelektury.connection.models.BookModel;
17 import com.moiseum.wolnelektury.storage.BookStorage;
18 import com.moiseum.wolnelektury.view.player.service.AudiobookLibrary;
19 import com.moiseum.wolnelektury.view.player.service.AudiobookService;
20 import com.moiseum.wolnelektury.view.player.service.MediaBrowserHelper;
21
22 import java.util.List;
23 import java.util.Locale;
24
25 /**
26  * Created by Piotr Ostrowski on 22.05.2018.
27  */
28 public class PlayerPresenter extends FragmentPresenter<PlayerView> {
29
30         private final BookDetailsModel book;
31         private final BookModel storedBook;
32
33         private final BookStorage storage = WLApplication.getInstance().getBookStorage();
34         private MediaBrowserHelper mMediaBrowserHelper;
35         private boolean mIsPlaying;
36
37         /**
38          * Customize the connection to our {@link android.support.v4.media.MediaBrowserServiceCompat}
39          * and implement our app specific desires.
40          */
41         private class MediaBrowserConnection extends MediaBrowserHelper {
42                 private MediaBrowserConnection(Context context) {
43                         super(context, AudiobookService.class);
44                 }
45
46                 @Override
47                 protected void onChildrenLoaded(@NonNull String parentId,
48                                                 @NonNull List<MediaBrowserCompat.MediaItem> children) {
49                         super.onChildrenLoaded(parentId, children);
50
51                         final MediaControllerCompat mediaController = getMediaController();
52                         mediaController.getTransportControls().sendCustomAction(AudiobookService.ACTION_CLEAR_PLAYLIST, null);
53
54                         AudiobookLibrary.createAudiobookMetadata(book);
55                         for (final MediaBrowserCompat.MediaItem mediaItem : AudiobookLibrary.getMediaItems()) {
56                                 mediaController.addQueueItem(mediaItem.getDescription());
57                         }
58
59                         // Call prepare now so pressing play just works.
60                         mediaController.getTransportControls().prepare();
61                         if (storedBook != null) {
62                                 mediaController.getTransportControls().skipToQueueItem(storedBook.getCurrentAudioChapter());
63                                 mediaController.getTransportControls().pause();
64                         }
65                 }
66         }
67
68         /**
69          * Implementation of the {@link MediaControllerCompat.Callback} methods we're interested in.
70          * <p>
71          * Here would also be where one could override
72          * {@code onQueueChanged(List<MediaSessionCompat.QueueItem> queue)} to get informed when items
73          * are added or removed from the queue. We don't do this here in order to keep the UI
74          * simple.
75          */
76         private class PlayerMediaControllerCallback extends MediaControllerCompat.Callback {
77                 @Override
78                 public void onPlaybackStateChanged(PlaybackStateCompat playbackState) {
79                         if (playbackState != null) {
80                                 if (playbackState.getState() == PlaybackStateCompat.STATE_ERROR) {
81                                         getView().onPlayerError();
82                                 }
83
84                                 mIsPlaying = playbackState.getState() == PlaybackStateCompat.STATE_PLAYING;
85                                 getView().setPlayButtonState(mIsPlaying);
86
87                                 if (mIsPlaying && playbackState.getExtras() != null) {
88                                         int total = playbackState.getExtras().getInt(AudiobookService.EXTRA_PLAYBACK_TOTAL);
89                                         getView().setTrackDuration(total, getCurrentTimerText(total));
90                                 }
91                         }
92                 }
93
94                 @Override
95                 public void onMetadataChanged(MediaMetadataCompat mediaMetadata) {
96                         if (mediaMetadata == null) {
97                                 return;
98                         }
99                         if (!book.getAudiobookFilesUrls().contains(mediaMetadata.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID))) {
100                                 return;
101                         }
102
103                         int currentChapter = (int) mediaMetadata.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER);
104                         if (storedBook != null) {
105                                 storedBook.setCurrentAudioChapter(currentChapter);
106                                 storage.update(storedBook);
107                         }
108
109                         String chapterTitle = mediaMetadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE);
110                         getView().setTrackTexts(chapterTitle, currentChapter);
111                 }
112
113                 @Override
114                 public void onExtrasChanged(Bundle extras) {
115                         if (extras.containsKey(AudiobookService.EXTRA_PLAYBACK_CURRENT)) {
116                                 int position = extras.getInt(AudiobookService.EXTRA_PLAYBACK_CURRENT);
117                                 getView().setTrackPosition(position, getCurrentTimerText(position));
118                         }
119                         if (extras.containsKey(AudiobookService.EXTRA_PLAYBACK_TOTAL)) {
120                                 int total = extras.getInt(AudiobookService.EXTRA_PLAYBACK_TOTAL);
121                                 getView().setTrackDuration(total, getCurrentTimerText(total));
122                         }
123                 }
124
125                 @Override
126                 public void onSessionDestroyed() {
127                         super.onSessionDestroyed();
128                 }
129
130                 @Override
131                 public void onQueueChanged(List<MediaSessionCompat.QueueItem> queue) {
132                         super.onQueueChanged(queue);
133                 }
134         }
135
136         PlayerPresenter(BookDetailsModel book, String slug, PlayerView view, Context context) {
137                 super(view);
138                 this.book = book;
139                 this.storedBook = storage.find(slug);
140                 mMediaBrowserHelper = new MediaBrowserConnection(context);
141                 mMediaBrowserHelper.registerCallback(new PlayerMediaControllerCallback());
142         }
143
144         @Override
145         public void onStart() {
146                 super.onStart();
147                 mMediaBrowserHelper.onStart();
148         }
149
150         @Override
151         public void onStop() {
152                 super.onStop();
153                 mMediaBrowserHelper.onStop();
154         }
155
156         public void playOrPause(boolean pauseCall) {
157                 if (mIsPlaying) {
158                         mMediaBrowserHelper.getTransportControls().pause();
159                 } else {
160                         mMediaBrowserHelper.getTransportControls().play();
161                 }
162         }
163
164         public void changeChapter(boolean next) {
165                 if (next) {
166                         mMediaBrowserHelper.getTransportControls().skipToNext();
167                 } else {
168                         mMediaBrowserHelper.getTransportControls().skipToPrevious();
169                 }
170         }
171
172         public void seekToButton(boolean forward) {
173                 if (forward) {
174                         mMediaBrowserHelper.getTransportControls().fastForward();
175                 } else {
176                         mMediaBrowserHelper.getTransportControls().rewind();
177                 }
178         }
179
180         public void seekTo(int userSelectedPosition) {
181                 mMediaBrowserHelper.getTransportControls().seekTo(userSelectedPosition);
182         }
183
184         public String getCurrentTimerText(int currentPosition) {
185                 StringBuilder sb = new StringBuilder();
186                 int minutes = (currentPosition % (1000 * 60 * 60)) / (1000 * 60);
187                 int seconds = ((currentPosition % (1000 * 60 * 60)) % (1000 * 60) / 1000);
188                 if (currentPosition > (1000 * 60 * 60)) {
189                         int hours = (currentPosition / (1000 * 60 * 60));
190                         sb.append(String.format(Locale.getDefault(), "%01d", hours));
191                         sb.append(":");
192                 }
193                 sb.append(String.format(Locale.getDefault(), "%02d", minutes));
194                 sb.append(":");
195                 sb.append(String.format(Locale.getDefault(), "%02d", seconds));
196                 return sb.toString();
197         }
198 }