Skip to content

Commit 9ddbdff

Browse files
committed
working on music service
1 parent 404cb56 commit 9ddbdff

7 files changed

Lines changed: 127 additions & 54 deletions

File tree

app/src/main/java/com/codeboy/mediafacerkotlin/PlayerActivity.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import androidx.media3.common.Player
1515
import androidx.media3.common.util.Util
1616
import androidx.media3.exoplayer.ExoPlayer
1717
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
18+
import androidx.media3.ui.PlayerNotificationManager
1819
import com.codeboy.mediafacer.models.VideoContent
1920
import com.codeboy.mediafacerkotlin.databinding.ActivityPlayerBinding
2021

@@ -49,6 +50,7 @@ class PlayerActivity : AppCompatActivity() {
4950
for (item: VideoContent in rawVideos){
5051
mediaItems.add(MediaItem.fromUri(item.videoUri))
5152
}
53+
5254
initializePlayer()
5355
}
5456

app/src/main/java/com/codeboy/mediafacerkotlin/fragments/AudiosFragment.kt

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.codeboy.mediafacerkotlin.fragments
22

33
import android.content.ComponentName
4+
import android.content.Intent
45
import android.graphics.drawable.AnimationDrawable
56
import android.media.session.PlaybackState
67
import android.os.Bundle
@@ -64,10 +65,10 @@ class AudiosFragment() : Fragment() {
6465
showMenu(it)
6566
}
6667

67-
animationDrawable = bindings.player.background as AnimationDrawable
68+
/* animationDrawable = bindings.player.background as AnimationDrawable
6869
animationDrawable.setEnterFadeDuration(5000)
6970
animationDrawable.setExitFadeDuration(2000)
70-
animationDrawable.start()
71+
animationDrawable.start()*/
7172

7273
bindings.audiosList.hasFixedSize()
7374
bindings.audiosList.setHasFixedSize(true)
@@ -80,16 +81,7 @@ class AudiosFragment() : Fragment() {
8081
override fun onStart() {
8182
super.onStart()
8283
//check connection to music service and proceed
83-
if(!PlaybackProtocol.isMusicServiceRunning){
84-
//start or bind the service here
85-
MediaBrowserCompat(requireActivity(),
86-
ComponentName(requireActivity(), MusicService::class.java), mMediaBrowserConnectionCallback, requireActivity().intent.extras).apply {
87-
connect()
88-
musicServiceBrowserCompat = this
89-
}
90-
}else{
91-
//other setups
92-
}
84+
startAndBindMusicService()
9385
}
9486

9587
private fun initAudios(){
@@ -418,6 +410,18 @@ class AudiosFragment() : Fragment() {
418410

419411
//MusicService Section, functions for music playback with media session and exoplayer -------------------------------------------------------------------------------------------------------------------------------------------------------------
420412

413+
private fun startAndBindMusicService(){
414+
// Create a new intent with the ACTION_MEDIA_BROWSER_SERVICE action
415+
val intent = Intent(requireActivity(), MusicService::class.java)
416+
requireActivity().startService(intent)
417+
418+
MediaBrowserCompat(requireActivity(),
419+
ComponentName(requireActivity(), MusicService::class.java), mMediaBrowserConnectionCallback, requireActivity().intent.extras).apply {
420+
connect()
421+
musicServiceBrowserCompat = this
422+
}
423+
}
424+
421425
private val mMediaControllerCallback: MediaControllerCompat.Callback =
422426
object : MediaControllerCompat.Callback() {
423427
override fun onPlaybackStateChanged(state: PlaybackStateCompat) {

app/src/main/java/com/codeboy/mediafacerkotlin/musicSession/MusicPlaybackButtonReceiver.kt

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,43 +20,46 @@ class MusicPlaybackButtonReceiver: MediaButtonReceiver() {
2020
intent.getParcelableExtra<KeyEvent>(Intent.EXTRA_KEY_EVENT)
2121
}
2222
if (keyEvent != null) {
23-
if (keyEvent.action == KeyEvent.ACTION_UP) {
24-
val audioManager = context?.getSystemService(Context.AUDIO_SERVICE) as AudioManager
25-
when (keyEvent.keyCode) {
26-
KeyEvent.KEYCODE_VOLUME_UP -> audioManager.adjustStreamVolume(
27-
AudioManager.STREAM_MUSIC,
28-
AudioManager.ADJUST_RAISE,
29-
AudioManager.FLAG_PLAY_SOUND
30-
)
31-
KeyEvent.KEYCODE_VOLUME_DOWN -> {
32-
23+
when (keyEvent.action) {
24+
KeyEvent.ACTION_UP -> {
25+
val audioManager = context?.getSystemService(Context.AUDIO_SERVICE) as AudioManager
26+
when (keyEvent.keyCode) {
27+
KeyEvent.KEYCODE_VOLUME_UP -> audioManager.adjustStreamVolume(
28+
AudioManager.STREAM_MUSIC,
29+
AudioManager.ADJUST_RAISE,
30+
AudioManager.FLAG_PLAY_SOUND
31+
)
32+
33+
KeyEvent.KEYCODE_VOLUME_DOWN -> {
34+
35+
}
3336
}
37+
}
3438

35-
KeyEvent.KEYCODE_MEDIA_PLAY -> {
36-
37-
}
39+
KeyEvent.KEYCODE_MEDIA_PLAY -> {
3840

39-
KeyEvent.KEYCODE_MEDIA_PAUSE -> {
41+
}
4042

41-
}
43+
KeyEvent.KEYCODE_MEDIA_PAUSE -> {
4244

43-
KeyEvent.KEYCODE_MEDIA_NEXT -> {
45+
}
4446

45-
}
47+
KeyEvent.KEYCODE_MEDIA_NEXT -> {
4648

47-
KeyEvent.KEYCODE_MEDIA_PREVIOUS -> {
49+
}
4850

49-
}
51+
KeyEvent.KEYCODE_MEDIA_PREVIOUS -> {
5052

51-
KeyEvent.KEYCODE_MEDIA_FAST_FORWARD -> {
53+
}
5254

53-
}
55+
KeyEvent.KEYCODE_MEDIA_FAST_FORWARD -> {
5456

55-
KeyEvent.KEYCODE_MEDIA_REWIND -> {
57+
}
5658

57-
}
59+
KeyEvent.KEYCODE_MEDIA_REWIND -> {
5860

5961
}
62+
6063
}
6164
}
6265
}

app/src/main/java/com/codeboy/mediafacerkotlin/musicSession/MusicService.kt

Lines changed: 78 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
package com.codeboy.mediafacerkotlin.musicSession
22

3+
import android.app.Notification
34
import android.app.PendingIntent
45
import android.content.*
6+
import android.content.pm.ServiceInfo
7+
import android.graphics.Bitmap
8+
import android.graphics.BitmapFactory
59
import android.media.AudioFocusRequest
610
import android.media.AudioManager
711
import android.media.AudioManager.OnAudioFocusChangeListener
12+
import android.net.Uri
813
import android.os.Build
914
import android.os.Bundle
1015
import android.os.SystemClock
@@ -20,10 +25,16 @@ import androidx.media3.common.MediaItem
2025
import androidx.media3.common.Player
2126
import androidx.media3.exoplayer.ExoPlayer
2227
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
28+
import androidx.media3.ui.PlayerNotificationManager
29+
import androidx.media3.ui.PlayerNotificationManager.MediaDescriptionAdapter
30+
import androidx.media3.ui.PlayerNotificationManager.NotificationListener
2331
import com.codeboy.mediafacer.models.AudioContent
32+
import com.codeboy.mediafacerkotlin.MainActivity
2433
import com.codeboy.mediafacerkotlin.R
2534
import com.codeboy.mediafacerkotlin.utils.MusicDataUtil
2635
import com.codeboy.mediafacerkotlin.viewModels.AudioViewModel
36+
import java.io.FileNotFoundException
37+
import java.io.InputStream
2738

2839
class MusicService : MediaBrowserServiceCompat(), OnAudioFocusChangeListener, Player.Listener{
2940

@@ -33,6 +44,9 @@ class MusicService : MediaBrowserServiceCompat(), OnAudioFocusChangeListener, Pl
3344
private lateinit var player: ExoPlayer
3445
private var musicList: ArrayList<AudioContent> = ArrayList()
3546
private var musicMetaDataList: ArrayList<MediaMetadataCompat> = ArrayList()
47+
private lateinit var currentTrack: MediaMetadataCompat
48+
private var trackPosition = 0
49+
3650

3751
private val model = AudioViewModel()
3852
private val observer = Observer<ArrayList<AudioContent>> { it ->
@@ -41,12 +55,12 @@ class MusicService : MediaBrowserServiceCompat(), OnAudioFocusChangeListener, Pl
4155
PlaybackProtocol.setCurrentMusic(musicList[0])
4256
setupUpMusicList(musicList)
4357
}
58+
private lateinit var playerNotification: PlayerNotificationManager
4459

4560
private val mNoisyReceiver: BroadcastReceiver = object : BroadcastReceiver() {
4661
override fun onReceive(context: Context, intent: Intent) {
4762
//handles playback when earphones are plugged and unplugged
4863
//pause exo player when unplugged and play when plugged
49-
5064
}
5165
}
5266

@@ -81,7 +95,7 @@ class MusicService : MediaBrowserServiceCompat(), OnAudioFocusChangeListener, Pl
8195
model.audios.observeForever(observer)
8296
model.loadNewItems(this,0,150,false)
8397
}else{
84-
PlaybackProtocol.setCurrentMusic(musicList[0])
98+
PlaybackProtocol.setCurrentMusic(musicList[trackPosition])
8599
setupUpMusicList(musicList)
86100
}
87101

@@ -92,13 +106,10 @@ class MusicService : MediaBrowserServiceCompat(), OnAudioFocusChangeListener, Pl
92106

93107
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
94108
MediaButtonReceiver.handleIntent(mMediaSessionCompat, intent)
95-
return super.onStartCommand(intent, flags, startId)
109+
//return super.onStartCommand(intent, flags, startId)
110+
return START_STICKY
96111
}
97112

98-
/*override fun onBind(intent: Intent): IBinder {
99-
TODO("Return the communication channel to the service.")
100-
}*/
101-
102113
override fun onGetRoot(clientPackageName: String, clientUid: Int, rootHints: Bundle?): BrowserRoot? {
103114
return if (TextUtils.equals(clientPackageName, packageName)) {
104115
BrowserRoot(getString(R.string.app_name), null)
@@ -109,7 +120,9 @@ class MusicService : MediaBrowserServiceCompat(), OnAudioFocusChangeListener, Pl
109120

110121
}
111122

112-
override fun onDestroy() {
123+
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
124+
override fun onDestroy() {
125+
playerNotification.setPlayer(null)
113126
//tell activity components that service is stopping
114127
PlaybackProtocol.isMusicServiceRunning = false
115128
//save the last playlist so next time it reads from preferences
@@ -118,20 +131,22 @@ class MusicService : MediaBrowserServiceCompat(), OnAudioFocusChangeListener, Pl
118131
if (model.audios.hasActiveObservers()){
119132
model.audios.removeObserver(observer)
120133
}
134+
player.removeListener(this)
135+
player.release()
121136
super.onDestroy()
122137
}
123138

124139
//service setup methods______________________________________________________________________________________________________________________
125140

126141
//build media items for media playback
127142
private fun setupUpMusicList(musicList: ArrayList<AudioContent>){
128-
129143
/* val imageUri = Uri.parse("https://example.com/image.jpg")
130144
val bitmap = MediaStore.Images.Media.getBitmap(contentResolver, imageUri)*/
131145

132146
for (musicItem in musicList){
133147
musicMetaDataList.add(musicItem.getMediaMetaDataCompat())
134148
}
149+
currentTrack = musicMetaDataList[trackPosition]
135150
}
136151

137152
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
@@ -142,7 +157,7 @@ class MusicService : MediaBrowserServiceCompat(), OnAudioFocusChangeListener, Pl
142157
mediaItems.add(MediaItem.fromUri(item.musicUri))
143158
}
144159
val trackSelector = DefaultTrackSelector(this).apply {
145-
setParameters(buildUponParameters().setMaxVideoSizeSd())
160+
setParameters(buildUponParameters())
146161
}
147162
val currentItem = 0
148163
val playbackPosition = 0L
@@ -156,16 +171,61 @@ class MusicService : MediaBrowserServiceCompat(), OnAudioFocusChangeListener, Pl
156171
exoPlayer.addListener(this)
157172
exoPlayer.prepare()
158173
}
174+
175+
val playerNotificationBuilder = PlayerNotificationManager.Builder(
176+
this,1010,"exoChannel"
177+
).setMediaDescriptionAdapter(object: MediaDescriptionAdapter{
178+
override fun getCurrentContentTitle(player: Player): CharSequence {
179+
return musicList[trackPosition].title
180+
}
181+
182+
override fun createCurrentContentIntent(player: Player): PendingIntent? {
183+
val playerIntent = Intent(this@MusicService, MainActivity::class.java)
184+
return PendingIntent.getActivity(this@MusicService, 0,playerIntent, PendingIntent.FLAG_UPDATE_CURRENT)
185+
}
186+
187+
override fun getCurrentContentText(player: Player): CharSequence? {
188+
return mMediaSessionCompat.controller.metadata.description.description
189+
}
190+
191+
override fun getCurrentLargeIcon(player: Player, callback: PlayerNotificationManager.BitmapCallback): Bitmap? {
192+
val imageUri: Uri = musicList[trackPosition].artUri
193+
var inputStream: InputStream? = null
194+
try {
195+
inputStream = contentResolver.openInputStream(imageUri)
196+
} catch (e: FileNotFoundException) {
197+
e.printStackTrace()
198+
}
199+
return BitmapFactory.decodeStream(inputStream)
200+
}
201+
}).setNotificationListener(object: NotificationListener{
202+
override fun onNotificationCancelled(notificationId: Int, dismissedByUser: Boolean) {
203+
stopSelf()
204+
super.onNotificationCancelled(notificationId, dismissedByUser)
205+
}
206+
207+
override fun onNotificationPosted(notificationId: Int, notification: Notification, ongoing: Boolean) {
208+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
209+
startForeground(1010,notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK)
210+
}else{
211+
startForeground(1010, notification)
212+
}
213+
super.onNotificationPosted(notificationId, notification, ongoing)
214+
}
215+
})
216+
217+
playerNotification = playerNotificationBuilder.build()
218+
playerNotification.setPlayer(player)
219+
159220
}
160221

161222
private fun setupMediaSession(){
162-
val mediaButtonReceiverName = ComponentName(applicationContext, MusicPlaybackButtonReceiver::class.java)
223+
val mediaButtonReceiverName = ComponentName(applicationContext, MediaButtonReceiver::class.java)
163224
mMediaSessionCompat = MediaSessionCompat(applicationContext, LOG_TAG, mediaButtonReceiverName, null)
164225
mMediaSessionCompat.setFlags(MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS)
165226

166227
val mediaButtonIntent = Intent(Intent.ACTION_MEDIA_BUTTON)
167-
//mediaButtonIntent.setClass(this, MediaButtonReceiver::class.java)
168-
mediaButtonIntent.setClass(this, MusicPlaybackButtonReceiver::class.java)
228+
mediaButtonIntent.setClass(this, MediaButtonReceiver::class.java)
169229

170230
val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
171231
PendingIntent.getBroadcast(this,0, mediaButtonIntent, PendingIntent.FLAG_IMMUTABLE.and(PendingIntent.FLAG_UPDATE_CURRENT))
@@ -210,9 +270,9 @@ class MusicService : MediaBrowserServiceCompat(), OnAudioFocusChangeListener, Pl
210270
}
211271
})
212272

273+
mMediaSessionCompat.setMetadata(currentTrack)
213274
mMediaSessionCompat.isActive = true
214275
sessionToken = mMediaSessionCompat.sessionToken
215-
216276
}
217277

218278
private fun buildState(state: Long): PlaybackStateCompat? {
@@ -275,6 +335,10 @@ class MusicService : MediaBrowserServiceCompat(), OnAudioFocusChangeListener, Pl
275335
}
276336
ExoPlayer.STATE_ENDED -> {
277337
//playWhenReady = false
338+
trackPosition += 1
339+
currentTrack = musicMetaDataList[trackPosition]
340+
mMediaSessionCompat.setMetadata(currentTrack)
341+
player.seekToNextMediaItem()
278342
}
279343
ExoPlayer.STATE_IDLE -> {
280344
//here you can set items and prepare

app/src/main/res/layout/fragment_audios.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,11 @@
113113
app:layout_constraintVertical_bias="0.6" />
114114
</androidx.constraintlayout.widget.ConstraintLayout>
115115

116+
<!-- android:background="@drawable/gradient_animation"-->
116117
<androidx.constraintlayout.widget.ConstraintLayout
117118
android:id="@+id/player"
118119
android:layout_width="0dp"
119120
android:layout_height="55dp"
120-
android:background="@drawable/gradient_animation"
121121
app:layout_constraintBottom_toBottomOf="parent"
122122
app:layout_constraintEnd_toEndOf="parent"
123123
app:layout_constraintStart_toStartOf="parent"

build.gradle

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
buildscript {
22
dependencies {
33
classpath 'com.google.gms:google-services:4.3.14'
4-
classpath 'com.android.tools.build:gradle:7.3.0'
4+
classpath 'com.android.tools.build:gradle:7.4.0'
55
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2'
66
classpath 'com.google.firebase:perf-plugin:1.4.2'
77
}
88
}// Top-level build file where you can add configuration options common to all sub-projects/modules.
99
plugins {
10-
id 'com.android.application' version '7.3.0' apply false
11-
id 'com.android.library' version '7.3.0' apply false
10+
id 'com.android.application' version '7.4.0' apply false
11+
id 'com.android.library' version '7.4.0' apply false
1212
id 'org.jetbrains.kotlin.android' version '1.7.10' apply false
1313
}
1414

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#Wed Apr 20 14:24:55 WAT 2022
22
distributionBase=GRADLE_USER_HOME
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
44
distributionPath=wrapper/dists
55
zipStorePath=wrapper/dists
66
zipStoreBase=GRADLE_USER_HOME

0 commit comments

Comments
 (0)