Skip to content

Commit 8b0685b

Browse files
StaehliJMGaetan89
andauthored
1052 remove playbackservice from library (#1078)
Co-authored-by: Gaëtan Muller <[email protected]>
1 parent 04b3edb commit 8b0685b

File tree

5 files changed

+68
-241
lines changed

5 files changed

+68
-241
lines changed

pillarbox-demo/src/main/AndroidManifest.xml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,6 @@
6666
android:launchMode="singleTask"
6767
android:supportsPictureInPicture="true" />
6868

69-
<service
70-
android:name=".service.DemoPlaybackService"
71-
android:foregroundServiceType="mediaPlayback" />
72-
7369
<service
7470
android:name=".service.DemoMediaSessionService"
7571
android:exported="true"

pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/service/DemoPlaybackService.kt

Lines changed: 0 additions & 32 deletions
This file was deleted.

pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/ui/player/SimplePlayerActivity.kt

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,12 @@
55
package ch.srgssr.pillarbox.demo.ui.player
66

77
import android.app.PictureInPictureParams
8-
import android.content.ComponentName
98
import android.content.Context
109
import android.content.Intent
11-
import android.content.ServiceConnection
1210
import android.content.pm.PackageManager
1311
import android.content.res.Configuration
1412
import android.os.Build
1513
import android.os.Bundle
16-
import android.os.IBinder
1714
import androidx.activity.ComponentActivity
1815
import androidx.activity.compose.setContent
1916
import androidx.activity.enableEdgeToEdge
@@ -34,25 +31,23 @@ import androidx.lifecycle.lifecycleScope
3431
import androidx.media3.common.Player
3532
import ch.srgssr.pillarbox.analytics.SRGAnalytics
3633
import ch.srgssr.pillarbox.demo.DemoPageView
37-
import ch.srgssr.pillarbox.demo.service.DemoPlaybackService
3834
import ch.srgssr.pillarbox.demo.shared.data.DemoItem
3935
import ch.srgssr.pillarbox.demo.shared.data.Playlist
4036
import ch.srgssr.pillarbox.demo.trackPagView
4137
import ch.srgssr.pillarbox.demo.ui.theme.PillarboxTheme
42-
import ch.srgssr.pillarbox.player.service.PlaybackService
4338
import kotlinx.coroutines.flow.collectLatest
4439
import kotlinx.coroutines.launch
4540

4641
/**
4742
* Simple player activity that can handle picture in picture.
4843
*
49-
* It handles basic background playback, as it will stop playback when the Activity is destroyed!
44+
* This implementation doesn't support background playback, it doesn't stop when the `Activity` starts picture in picture.
5045
* To have pure background playback with good integration from other devices like Auto, Wear, etc... we need *MediaSessionService*
5146
*
5247
* For this demo, only the picture in picture button can enable picture in picture.
5348
* But feel free to call [startPictureInPicture] whenever you decide, for example, when [onUserLeaveHint]
5449
*/
55-
class SimplePlayerActivity : ComponentActivity(), ServiceConnection {
50+
class SimplePlayerActivity : ComponentActivity() {
5651

5752
private val playerViewModel by viewModels<SimplePlayerViewModel>()
5853
private val layoutStyle by lazy { intent.getIntExtra(ARG_LAYOUT, LAYOUT_PLAYLIST) }
@@ -79,8 +74,6 @@ class SimplePlayerActivity : ComponentActivity(), ServiceConnection {
7974
}
8075
}
8176

82-
// Bind PlaybackService to allow background playback and MediaNotification.
83-
bindPlaybackService()
8477
setContent {
8578
PillarboxTheme {
8679
Scaffold(containerColor = Color.Black) { innerPadding ->
@@ -116,15 +109,6 @@ class SimplePlayerActivity : ComponentActivity(), ServiceConnection {
116109
readIntent(intent)
117110
}
118111

119-
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
120-
val binder = service as PlaybackService.ServiceBinder
121-
binder.setPlayer(playerViewModel.player)
122-
}
123-
124-
override fun onServiceDisconnected(name: ComponentName?) {
125-
// Nothing
126-
}
127-
128112
private fun startPictureInPicture() {
129113
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
130114
val params = PictureInPictureParams.Builder()
@@ -169,22 +153,12 @@ class SimplePlayerActivity : ComponentActivity(), ServiceConnection {
169153

170154
override fun onStart() {
171155
super.onStart()
172-
playerViewModel.player.play()
156+
playerViewModel.resumePlayback()
173157
}
174158

175159
override fun onStop() {
176160
super.onStop()
177-
playerViewModel.player.pause()
178-
}
179-
180-
override fun onDestroy() {
181-
super.onDestroy()
182-
unbindService(this)
183-
}
184-
185-
private fun bindPlaybackService() {
186-
val intent = Intent(this, DemoPlaybackService::class.java)
187-
bindService(intent, this, BIND_AUTO_CREATE)
161+
playerViewModel.stopPlayback()
188162
}
189163

190164
companion object {

pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/ui/player/SimplePlayerViewModel.kt

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,53 @@
55
package ch.srgssr.pillarbox.demo.ui.player
66

77
import android.app.Application
8+
import android.app.PendingIntent
9+
import android.content.Intent
810
import android.util.Log
911
import android.util.Rational
1012
import androidx.lifecycle.AndroidViewModel
13+
import androidx.lifecycle.application
1114
import androidx.media3.common.C
1215
import androidx.media3.common.MediaMetadata
1316
import androidx.media3.common.PlaybackException
1417
import androidx.media3.common.PlaybackParameters
1518
import androidx.media3.common.Player
1619
import androidx.media3.common.Timeline
1720
import androidx.media3.common.VideoSize
21+
import androidx.media3.common.util.NotificationUtil
22+
import androidx.media3.session.R
23+
import androidx.media3.ui.PlayerNotificationManager
1824
import ch.srgssr.pillarbox.demo.shared.data.DemoItem
1925
import ch.srgssr.pillarbox.demo.shared.di.PlayerModule
2026
import ch.srgssr.pillarbox.player.PillarboxPlayer
2127
import ch.srgssr.pillarbox.player.asset.timeRange.Chapter
2228
import ch.srgssr.pillarbox.player.asset.timeRange.Credit
2329
import ch.srgssr.pillarbox.player.extension.setHandleAudioFocus
2430
import ch.srgssr.pillarbox.player.extension.toRational
31+
import ch.srgssr.pillarbox.player.notification.PillarboxMediaDescriptionAdapter
32+
import ch.srgssr.pillarbox.player.session.PillarboxMediaSession
33+
import ch.srgssr.pillarbox.player.utils.PendingIntentUtils
2534
import ch.srgssr.pillarbox.player.utils.StringUtil
2635
import kotlinx.coroutines.flow.MutableStateFlow
2736

37+
private const val NotificationId = 2025
38+
2839
/**
29-
* Simple player view model than handle a PillarboxPlayer [player]
40+
* Simple player view model that handles a PillarboxPlayer [player].
41+
* Playback notification is displayed when the player is in the foreground.
42+
* When the Activity goes to the background, the playback is stopped and the notification removed.
3043
*/
3144
class SimplePlayerViewModel(application: Application) : AndroidViewModel(application), PillarboxPlayer.Listener {
3245
/**
3346
* Player as PillarboxPlayer
3447
*/
3548
val player = PlayerModule.provideDefaultPlayer(application)
3649

50+
private val mediaSession = PillarboxMediaSession.Builder(application, player)
51+
.setSessionActivity(pendingIntent())
52+
.build()
53+
private val notificationManager: PlayerNotificationManager
54+
3755
/**
3856
* Picture in picture enabled
3957
*/
@@ -45,6 +63,12 @@ class SimplePlayerViewModel(application: Application) : AndroidViewModel(applica
4563
var pictureInPictureRatio = MutableStateFlow(Rational(1, 1))
4664

4765
init {
66+
notificationManager = PlayerNotificationManager.Builder(application, NotificationId, "Pillarbox now playing")
67+
.setChannelImportance(NotificationUtil.IMPORTANCE_LOW)
68+
.setChannelNameResourceId(R.string.default_notification_channel_name)
69+
.setMediaDescriptionAdapter(PillarboxMediaDescriptionAdapter(context = application, pendingIntent = pendingIntent()))
70+
.build()
71+
notificationManager.setUseChronometer(false)
4872
player.addListener(this)
4973
/*
5074
* Seems to have no effect if not use with a foreground service to handle background playback.
@@ -62,6 +86,32 @@ class SimplePlayerViewModel(application: Application) : AndroidViewModel(applica
6286
* Playback will resume depending on the "importance" of the interruption (call, playback)
6387
*/
6488
player.setHandleAudioFocus(true)
89+
notificationManager.setMediaSessionToken(mediaSession.token)
90+
}
91+
92+
/**
93+
* Resume playback.
94+
*/
95+
fun resumePlayback() {
96+
displayNotification()
97+
player.prepare()
98+
player.play()
99+
}
100+
101+
/**
102+
* Stop playback.
103+
*/
104+
fun stopPlayback() {
105+
player.stop()
106+
hideNotification()
107+
}
108+
109+
private fun displayNotification() {
110+
notificationManager.setPlayer(player)
111+
}
112+
113+
private fun hideNotification() {
114+
notificationManager.setPlayer(null)
65115
}
66116

67117
/**
@@ -79,6 +129,8 @@ class SimplePlayerViewModel(application: Application) : AndroidViewModel(applica
79129
override fun onCleared() {
80130
super.onCleared()
81131
Log.d(TAG, "onCleared => releasing the player")
132+
notificationManager.setPlayer(null)
133+
mediaSession.release()
82134
player.release()
83135
player.removeListener(this)
84136
}
@@ -143,6 +195,17 @@ class SimplePlayerViewModel(application: Application) : AndroidViewModel(applica
143195
Log.i(TAG, "onCreditChanged $credit")
144196
}
145197

198+
private fun pendingIntent(): PendingIntent {
199+
val intent = Intent(application, SimplePlayerActivity::class.java)
200+
val flags = PendingIntentUtils.appendImmutableFlagIfNeeded(PendingIntent.FLAG_UPDATE_CURRENT)
201+
return PendingIntent.getActivity(
202+
application,
203+
0,
204+
intent,
205+
flags
206+
)
207+
}
208+
146209
private companion object {
147210
private const val TAG = "PillarboxDemo"
148211
}

0 commit comments

Comments
 (0)