@@ -88,6 +88,7 @@ class AudioService : LifecycleService() {
8888
8989 var episodeId: String? = null
9090 private set
91+ private var episodeTitle: String? = null
9192
9293 private lateinit var exoPlayer: SimpleExoPlayer
9394
@@ -117,75 +118,18 @@ class AudioService : LifecycleService() {
117118 .build()
118119 exoPlayer.setAudioAttributes(audioAttributes, true )
119120
121+ // Monitor ExoPlayer events.
120122 exoPlayer.addListener(PlayerEventListener ())
121- }
122-
123- override fun onStartCommand (intent : Intent ? , flags : Int , startId : Int ): Int {
124- handleIntent(intent)
125-
126- return super .onStartCommand(intent, flags, startId)
127- }
128-
129- override fun onDestroy () {
130- cancelPlaybackMonitor()
131-
132- mediaSession?.release()
133- mediaSessionConnector?.setPlayer(null )
134- playerNotificationManager?.setPlayer(null )
135-
136- exoPlayer.release()
137-
138- super .onDestroy()
139- }
140-
141- @MainThread
142- fun play (uri : Uri , startPosition : Long , playbackSpeed : Float? = null) {
143- val userAgent = Util .getUserAgent(applicationContext, BuildConfig .APPLICATION_ID )
144- val mediaSource = ExtractorMediaSource (
145- uri,
146- DefaultDataSourceFactory (applicationContext, userAgent),
147- DefaultExtractorsFactory (),
148- null ,
149- null )
150-
151- val haveStartPosition = startPosition != C .POSITION_UNSET .toLong()
152- if (haveStartPosition) {
153- exoPlayer.seekTo(startPosition)
154- }
155-
156- playbackSpeed?.let { changePlaybackSpeed(playbackSpeed) }
157-
158- exoPlayer.prepare(mediaSource, ! haveStartPosition, false )
159- exoPlayer.playWhenReady = true
160- }
161-
162- @MainThread
163- fun resume () {
164- exoPlayer.playWhenReady = true
165- }
166-
167- @MainThread
168- fun pause () {
169- exoPlayer.playWhenReady = false
170- }
171-
172- @MainThread
173- fun changePlaybackSpeed (playbackSpeed : Float ) {
174- exoPlayer.playbackParameters = PlaybackParameters (playbackSpeed)
175- }
176-
177- @MainThread
178- private fun handleIntent (intent : Intent ? ) {
179- episodeId = intent?.getStringExtra(ARG_EPISODE_ID )
180123
124+ // Setup notification and media session.
181125 playerNotificationManager = PlayerNotificationManager .createWithNotificationChannel(
182126 applicationContext,
183127 PLAYBACK_CHANNEL_ID ,
184128 R .string.playback_channel_name,
185129 PLAYBACK_NOTIFICATION_ID ,
186130 object : PlayerNotificationManager .MediaDescriptionAdapter {
187131 override fun getCurrentContentTitle (player : Player ): String {
188- return intent?.getStringExtra( ARG_TITLE ) ? : getString(R .string.loading_dots)
132+ return episodeTitle ? : getString(R .string.loading_dots)
189133 }
190134
191135 @Nullable
@@ -211,13 +155,28 @@ class AudioService : LifecycleService() {
211155 }
212156
213157 override fun onNotificationCancelled (notificationId : Int ) {
158+ _playerStatusLiveData .value = PlayerStatus .Cancelled (episodeId)
159+
214160 stopSelf()
215161 }
162+
163+ override fun onNotificationPosted (notificationId : Int , notification : Notification ? , ongoing : Boolean ) {
164+ if (ongoing) {
165+ // Make sure the service will not get destroyed while playing media.
166+ startForeground(notificationId, notification)
167+ } else {
168+ // Make notification cancellable.
169+ stopForeground(false )
170+ }
171+ }
216172 }
217173 ).apply {
218- // omit skip previous and next actions
174+ // Omit skip previous and next actions.
219175 setUseNavigationActions(false )
220176
177+ // Add stop action.
178+ setUseStopAction(true )
179+
221180 val incrementMs = resources.getInteger(R .integer.increment_ms).toLong()
222181 setFastForwardIncrementMs(incrementMs)
223182 setRewindIncrementMs(incrementMs)
@@ -240,7 +199,7 @@ class AudioService : LifecycleService() {
240199 putParcelable(MediaMetadataCompat .METADATA_KEY_DISPLAY_ICON , bitmap)
241200 }
242201
243- val title = intent?.getStringExtra( ARG_TITLE ) ? : getString(R .string.loading_dots)
202+ val title = episodeTitle ? : getString(R .string.loading_dots)
244203
245204 return MediaDescriptionCompat .Builder ()
246205 .setIconBitmap(bitmap)
@@ -252,6 +211,30 @@ class AudioService : LifecycleService() {
252211
253212 setPlayer(exoPlayer)
254213 }
214+ }
215+
216+ override fun onStartCommand (intent : Intent ? , flags : Int , startId : Int ): Int {
217+ handleIntent(intent)
218+
219+ return super .onStartCommand(intent, flags, startId)
220+ }
221+
222+ override fun onDestroy () {
223+ cancelPlaybackMonitor()
224+
225+ mediaSession?.release()
226+ mediaSessionConnector?.setPlayer(null )
227+ playerNotificationManager?.setPlayer(null )
228+
229+ exoPlayer.release()
230+
231+ super .onDestroy()
232+ }
233+
234+ @MainThread
235+ private fun handleIntent (intent : Intent ? ) {
236+ episodeId = intent?.getStringExtra(ARG_EPISODE_ID )
237+ episodeTitle = intent?.getStringExtra(ARG_TITLE )
255238
256239 // Play
257240 intent?.let {
@@ -264,6 +247,42 @@ class AudioService : LifecycleService() {
264247 }
265248 }
266249
250+ @MainThread
251+ fun play (uri : Uri , startPosition : Long , playbackSpeed : Float? = null) {
252+ val userAgent = Util .getUserAgent(applicationContext, BuildConfig .APPLICATION_ID )
253+ val mediaSource = ExtractorMediaSource (
254+ uri,
255+ DefaultDataSourceFactory (applicationContext, userAgent),
256+ DefaultExtractorsFactory (),
257+ null ,
258+ null )
259+
260+ val haveStartPosition = startPosition != C .POSITION_UNSET .toLong()
261+ if (haveStartPosition) {
262+ exoPlayer.seekTo(startPosition)
263+ }
264+
265+ playbackSpeed?.let { changePlaybackSpeed(playbackSpeed) }
266+
267+ exoPlayer.prepare(mediaSource, ! haveStartPosition, false )
268+ exoPlayer.playWhenReady = true
269+ }
270+
271+ @MainThread
272+ fun resume () {
273+ exoPlayer.playWhenReady = true
274+ }
275+
276+ @MainThread
277+ fun pause () {
278+ exoPlayer.playWhenReady = false
279+ }
280+
281+ @MainThread
282+ fun changePlaybackSpeed (playbackSpeed : Float ) {
283+ exoPlayer.playbackParameters = PlaybackParameters (playbackSpeed)
284+ }
285+
267286 @MainThread
268287 private fun saveLastListeningPosition () = lifecycleScope.launch {
269288 episodeId?.let { appDatabase.listenedDao().insert(Listened (it, exoPlayer.contentPosition, exoPlayer.duration)) }
@@ -321,18 +340,21 @@ class AudioService : LifecycleService() {
321340 if (playbackState == Player .STATE_READY ) {
322341 if (exoPlayer.playWhenReady) {
323342 episodeId?.let { _playerStatusLiveData .value = PlayerStatus .Playing (it) }
324-
325- monitorPlaybackProgress()
326343 } else {// Paused
327344 episodeId?.let { _playerStatusLiveData .value = PlayerStatus .Paused (it) }
328-
329- cancelPlaybackMonitor()
330345 }
331346 } else if (playbackState == Player .STATE_ENDED ) {
332347 episodeId?.let { _playerStatusLiveData .value = PlayerStatus .Ended (it) }
333348 } else {
334349 episodeId?.let { _playerStatusLiveData .value = PlayerStatus .Other (it) }
335350 }
351+
352+ // Only monitor playback to record progress when playing.
353+ if (playbackState == Player .STATE_READY && exoPlayer.playWhenReady) {
354+ monitorPlaybackProgress()
355+ } else {
356+ cancelPlaybackMonitor()
357+ }
336358 }
337359
338360 override fun onPlayerError (e : ExoPlaybackException ? ) {
0 commit comments