首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >我如何让Android识别正在播放的歌曲?

我如何让Android识别正在播放的歌曲?
EN

Stack Overflow用户
提问于 2022-11-19 10:52:54
回答 1查看 30关注 0票数 1

我有一个音乐播放器,但我注意到android和其他设备不知道在播放什么歌曲。

例如,在mi乐队4,我可以控制音乐,如果我有它的youtube音乐,但不是在我的应用程序,甚至在我的发射器,一目了然的小部件识别它在播放。

示例:

既然你已经知道了上下文,我需要什么才能让它发挥作用,或者我缺少什么?

这是我的服务,播放音乐:

代码语言:javascript
复制
class SimpleMPService: Service() {


    private val mBinder = LocalBinder()
    private lateinit var notification: Notification
    private lateinit var notificationManager: NotificationManager
    var playList = ArrayList<Song>()
    private var shuffledPlaylist = ArrayList<Song>()
    var currentSongPosition: Int = 0
    private var currentSongPath: String = ""
    var onRepeatMode = false
    private lateinit var audioManager: AudioManager


    //Listeners
    var onMusicSelectedListener: OnMusicSelectedListener? = null
    var onMusicSelectedListenerToQueue: OnMusicSelectedListenerToQueue? = null //Since there is no way to have two listeners at same time it needs another listener to the queue list
    var onMusicPausedListener: OnMusicPausedListener? = null
    var onPlaylistAdded: OnPlaylistsAdded? = null
    var onMusicResumedListener: OnMusicResumedListener? = null
    var onMusicSecondPassedListener: OnSecondPassedListener? = null
    var onMusicShuffleToggledListener: OnMusicShuffleToggledListener? = null
    var onMediaPlayerStoppedListener: OnMediaPlayerStoppedListener? = null


    //Player States
    private var serviceStarted = false
    var musicShuffled = false
    private var musicStarted = false


    //Others
    private lateinit var mediaButtonReceiver: ComponentName
    private lateinit var mediaSession: MediaSessionCompat


    inner class LocalBinder : Binder() {
        fun getService(): SimpleMPService = this@SimpleMPService
    }


    companion object {

        private val mediaPlayer = MediaPlayer()

        fun startService(context: Context) {
            val startIntent = Intent(context, SimpleMPService::class.java)
            context.startForegroundService(startIntent)

        }
    }


    override fun onBind(intent: Intent?): IBinder {

        val context = this
        mediaButtonReceiver = ComponentName(context, ReceiverPlayPause::class.java)
        mediaSession = MediaSessionCompat(context, "SessionTag")
        mediaSession.setCallback(object : MediaSessionCompat.Callback(){

            override fun onMediaButtonEvent(mediaButtonIntent: Intent): Boolean {

                val ke = mediaButtonIntent.getParcelableExtra<KeyEvent>(Intent.EXTRA_KEY_EVENT)

                if( ke?.action == KeyEvent.ACTION_DOWN ){

                    if( ke.keyCode == KeyEvent.KEYCODE_MEDIA_PREVIOUS )
                        previousSong( context )

                    if( ke.keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE )
                        pauseResumeMusic( context )

                    if( ke.keyCode == KeyEvent.KEYCODE_MEDIA_PLAY )
                        pauseResumeMusic( context )

                    if( ke.keyCode == KeyEvent.KEYCODE_MEDIA_NEXT )
                        skipSong( context )
                }

                return super.onMediaButtonEvent(mediaButtonIntent)
            }
        })

        return mBinder
    }


    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

        return START_STICKY
    }


    override fun onCreate() {
        super.onCreate()

        notificationManager = this.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    }


    fun getCurrentPlaylist(): ArrayList<Song>{

        return if(!musicShuffled) playList else shuffledPlaylist
    }



    fun isMusicPlayingOrPaused(): Boolean{ return musicStarted }


    fun toggleShuffle(){

        if( !musicShuffled ){

            musicShuffled = true

            shuffledPlaylist = ArrayList()
            val tempShuffledPlaylist = ArrayList<Song>()


            //Adds the current song to first position
            playList.forEach { song ->

                if (song.path != currentSongPath)
                    tempShuffledPlaylist.add(song)

                else
                    shuffledPlaylist.add( song )
            }

            //Shuffles the temp playlist and adds it to the one with just the current song
            tempShuffledPlaylist.shuffle()

            for( song in tempShuffledPlaylist )
                shuffledPlaylist.add( song )


            currentSongPosition = 0
        }
        else{

            musicShuffled = false


            for( i in playList.indices ){

                if( playList[i].path == currentSongPath ){

                    currentSongPosition = i
                    break
                }
            }
        }

        onMusicShuffleToggledListener?.onMusicShuffleToggled(musicShuffled)
    }


    fun enableShuffle(){

        musicShuffled = true

        shuffledPlaylist = ArrayList(playList)
        shuffledPlaylist.shuffle()

        onMusicShuffleToggledListener?.onMusicShuffleToggled(true)


        currentSongPosition = 0
    }


    fun setPlaylist( newPlaylist: ArrayList<Song> ){ playList = newPlaylist }


    fun playSongAndEnableShuffle(context: Context, position: Int){


        val selectedSong = playList[position]
        shuffledPlaylist = ArrayList(playList)


        shuffledPlaylist.shuffle()
        shuffledPlaylist.removeIf{ it.path == selectedSong.path }
        shuffledPlaylist.add(0, selectedSong )

        currentSongPosition = 0
        playSong(context)


        musicShuffled = true
    }


    fun isMusicPlaying(): Boolean{

        return mediaPlayer.isPlaying
    }


    fun getCurrentSongPath(): String{ return currentSongPath }


    private val audioFocusChangeListener = OnAudioFocusChangeListener { focusChange ->
        when (focusChange) {
            AudioManager.AUDIOFOCUS_GAIN -> {}

            AudioManager.AUDIOFOCUS_GAIN_TRANSIENT->{}

            AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {

                if( mediaPlayer.isPlaying )
                    pauseMusic(this )
            }

            AudioManager.AUDIOFOCUS_LOSS -> {

                if( mediaPlayer.isPlaying )
                    pauseMusic(this )
            }
        }
    }


    private val focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).run {
        setAudioAttributes(AudioAttributes.Builder().run {
            setUsage(AudioAttributes.USAGE_MEDIA)
            setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            build()
        })
        setAcceptsDelayedFocusGain(true)
        setOnAudioFocusChangeListener(audioFocusChangeListener)
        build()
    }


    fun playSong(context: Context){

        serviceStarted = true
        musicStarted = true

        val songPath: String
        val songTitle: String
        val songArtist: String
        val songID: Long
        val songAlbumID: Long
        val songAlbumArt: Bitmap
        val songDuration: Int


        if( !musicShuffled ) {

            songPath = playList[currentSongPosition].path
            songTitle = playList[currentSongPosition].title
            songArtist = playList[currentSongPosition].artistName
            songID = playList[currentSongPosition].id
            songAlbumID = playList[currentSongPosition].albumID
            songAlbumArt = GetSongs.getSongAlbumArt(context, songID, songAlbumID)
            songDuration = playList[currentSongPosition].duration
        }
        else{

            songPath = shuffledPlaylist[currentSongPosition].path
            songTitle = shuffledPlaylist[currentSongPosition].title
            songArtist = shuffledPlaylist[currentSongPosition].artistName
            songID = shuffledPlaylist[currentSongPosition].id
            songAlbumID = shuffledPlaylist[currentSongPosition].albumID
            songAlbumArt = GetSongs.getSongAlbumArt(context, songID, songAlbumID)
            songDuration = shuffledPlaylist[currentSongPosition].duration
        }

        currentSongPath = songPath

        val isAudioLimited = PreferenceManager.getDefaultSharedPreferences(context).getBoolean("setting_limitAudioVolume", true)

        mediaPlayer.reset()
        mediaPlayer.setDataSource(songPath)

        when(isAudioLimited){

            true-> mediaPlayer.setVolume(0.08F, 0.08F)
            false-> mediaPlayer.setVolume(0.1F, 0.1F)
        }


        mediaPlayer.prepareAsync()
        mediaPlayer.setOnPreparedListener {

            audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager


            requestPlayWithFocus()
            mediaSession.isActive = true

            //Open App
            val openAppIntent = Intent( context, ActivityMain::class.java )
            val pendingOpenAppIntent = TaskStackBuilder.create( context ).run{

                addNextIntentWithParentStack(openAppIntent)
                getPendingIntent( 0, PendingIntent.FLAG_IMMUTABLE )
            }

            //Stop Service
            val stopIntent = Intent(context, ReceiverStop::class.java )
            val pendingStopIntent = PendingIntent.getBroadcast( context, 1, stopIntent, PendingIntent.FLAG_IMMUTABLE )


            //Previous Music
            val previousSongIntent = Intent(context, ReceiverPreviousSong::class.java )
            val pendingPreviousSongIntent = PendingIntent.getBroadcast( context, 1, previousSongIntent, PendingIntent.FLAG_IMMUTABLE )


            //Pauses/Plays music
            val playPauseIntent = Intent(context, ReceiverPlayPause::class.java )
            val pendingPlayPauseIntent = PendingIntent.getBroadcast( context, 1, playPauseIntent, PendingIntent.FLAG_IMMUTABLE )


            //Skips to next music
            val skipSongIntent = Intent(context, ReceiverSkipSong::class.java )
            val pendingSkipSongIntent = PendingIntent.getBroadcast( context, 1, skipSongIntent, PendingIntent.FLAG_IMMUTABLE )



            notification = NotificationCompat.Builder(context, "Playback")
                .setContentIntent( pendingOpenAppIntent )
                .setStyle( androidx.media.app.NotificationCompat.MediaStyle()
                    .setMediaSession(mediaSession.sessionToken)
                    .setShowActionsInCompactView(1, 2, 3)
                )
                .setSmallIcon(R.drawable.icon)
                .addAction(R.drawable.icon_x, "Stop Player", pendingStopIntent )
                .addAction(R.drawable.icon_previous_notification, "Previous Music", pendingPreviousSongIntent )
                .addAction(R.drawable.icon_pause_notification, "Play Pause Music", pendingPlayPauseIntent )
                .addAction(R.drawable.icon_next_notification, "Next Music", pendingSkipSongIntent )
                .build()


            mediaSession.setMetadata(
                MediaMetadataCompat.Builder()

                    .putString(MediaMetadata.METADATA_KEY_TITLE, songTitle)
                    .putString(MediaMetadata.METADATA_KEY_ARTIST, songArtist)
                    .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, songAlbumArt)
                    .putLong(MediaMetadata.METADATA_KEY_DURATION, songDuration.toLong())
                    .build()
            )


            startForeground( 2, notification )
            notificationManager.notify( 2, notification )
        }


        handleSongFinished( context )


        if( !musicShuffled ) {
            onMusicSelectedListener?.onMusicSelected(playList, currentSongPosition)
            onMusicSelectedListenerToQueue?.onMusicSelected(playList, currentSongPosition)
        }

        else {
            onMusicSelectedListener?.onMusicSelected(shuffledPlaylist, currentSongPosition)
            onMusicSelectedListenerToQueue?.onMusicSelected(shuffledPlaylist, currentSongPosition)
        }


        val bluetoothReceiver = IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY)
        context.registerReceiver(bluetoothBroadcastReceiver, bluetoothReceiver )


        val mainHandler = Handler(Looper.getMainLooper())
        mainHandler.post( object : Runnable{
            override fun run() {

                if( onMusicSecondPassedListener != null )
                    onMusicSecondPassedListener?.onSecondPassed( mediaPlayer.currentPosition )
                    mainHandler.postDelayed( this,1000)
            }
        })
    }


    private val bluetoothBroadcastReceiver = object : BroadcastReceiver(){

        override fun onReceive(p0: Context?, p1: Intent?) {

            if(isMusicPlaying()) pauseMusic(p0!!)
        }
    }



    fun seekTo( position: Int){

        val newSongPosition = position * 1000

        mediaPlayer.seekTo(newSongPosition)

        if( !mediaPlayer.isPlaying ) mediaPlayer.start()
    }


    private fun handleSongFinished(context: Context) {

        mediaPlayer.setOnCompletionListener{

            //If loop mode is activated
            if( onRepeatMode ){

                playSong( context )
            }

            //Is it's the last song
            else if( (currentSongPosition + 1) == playList.size ){

                stopMediaPlayer()
            }
            else{

                currentSongPosition++

                playSong( context )
            }
        }
    }


    fun toggleLoop(){

        onRepeatMode = !onRepeatMode
    }



    fun stopMediaPlayer(){

        onMediaPlayerStoppedListener?.onMediaPlayerStopped()
        mediaPlayer.stop()
        currentSongPosition = -1
        currentSongPath = ""
        stopForeground(true)
        stopSelf()
    }


    fun skipSong(context: Context){

        if( (currentSongPosition + 1) < playList.size ){

            currentSongPosition ++
            playSong( context )
        }
    }


    fun previousSong(context: Context){

        if( (currentSongPosition - 1) >= 0 ){

            currentSongPosition--
            playSong( context )
        }

    }


    @Suppress("DEPRECATION")
    fun pauseMusic(context: Context ){


        val playPauseIcon = R.drawable.icon_play_notification
        mediaPlayer.pause()
        mediaSession.isActive = false


        if( onMusicPausedListener != null)
            onMusicPausedListener?.onMusicPaused()



        //Updates the notification
        val playPauseIntent = Intent(context, ReceiverPlayPause::class.java )
        playPauseIntent.putExtra( "action", "playPause" )
        val pendingPlayPauseIntent = PendingIntent.getBroadcast( context, 1, playPauseIntent, PendingIntent.FLAG_IMMUTABLE )


        notification.actions[2] = Notification.Action( playPauseIcon, "Play Music", pendingPlayPauseIntent )


        startForeground( 2, notification )
        notificationManager.notify( 2, notification )
    }


    @Suppress("DEPRECATION")
    fun pauseResumeMusic(context: Context ){

        val playPauseIcon: Int

        if( mediaPlayer.isPlaying ) {

            playPauseIcon = R.drawable.icon_play_notification


            mediaPlayer.pause()

            if( onMusicPausedListener != null) onMusicPausedListener?.onMusicPaused()
        }
        else {

            playPauseIcon = R.drawable.icon_pause_notification


            if( onMusicResumedListener != null ) onMusicResumedListener?.onMusicResumed()

            requestPlayWithFocus()
        }


        //Updates the notification
        val playPauseIntent = Intent(context, ReceiverPlayPause::class.java )
        playPauseIntent.putExtra( "action", "playPause" )
        val pendingPlayPauseIntent = PendingIntent.getBroadcast( context, 1, playPauseIntent, PendingIntent.FLAG_IMMUTABLE )


        notification.actions[2] = Notification.Action( playPauseIcon, "Play Music", pendingPlayPauseIntent )


        startForeground( 2, notification )
        notificationManager.notify( 2, notification )
    }


    private fun requestPlayWithFocus(){

        val focusLock = Any()
        val res = audioManager.requestAudioFocus(focusRequest)


        synchronized(focusLock) {
            when (res) {
                AudioManager.AUDIOFOCUS_REQUEST_GRANTED -> {

                    mediaPlayer.start()
                    onMusicResumedListener?.onMusicResumed()

                    true
                }
                else -> false
            }
        }
    }


    fun updatePlaylists(){ onPlaylistAdded?.onPlaylistAdded() }

    //////////////////////////////////////////////////////////////////////////////////////////////////////

    interface OnMusicSelectedListener{ fun onMusicSelected(playList: ArrayList<Song>, position: Int ) }


    interface OnMusicSelectedListenerToQueue{ fun onMusicSelected(playList: ArrayList<Song>, position: Int ) }


    interface OnPlaylistsAdded{ fun onPlaylistAdded() }


    interface OnMusicPausedListener{ fun onMusicPaused() }


    interface OnMusicResumedListener{ fun onMusicResumed() }


    interface OnSecondPassedListener{ fun onSecondPassed(position: Int ) }


    interface OnMusicShuffleToggledListener{ fun onMusicShuffleToggled(state: Boolean) }


    interface OnMediaPlayerStoppedListener{ fun onMediaPlayerStopped() }
}

我想让其他设备识别出正在播放的歌曲,并映射相应的按钮,就像之前的“暂停播放下一步”一样。我试图搜索,但没有发现任何有用的东西。我不知道具体要找什么。

预先感谢:D

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-11-25 16:51:11

我终于发现了问题。我想念回放状态和回调

我正在分享我所做的,以防有人在这里和在同样的情况下结束。

首先,我向媒体会话回调添加了其他回调。主要是这些:

代码语言:javascript
复制
            override fun onPlay() {
                super.onPlay()

                pauseResumeMusic(context)
            }

            override fun onStop() {
                super.onStop()

                stopMediaPlayer()
            }

            override fun onPause() {
                super.onPause()

                pauseResumeMusic(context)
            }

            override fun onSkipToNext() {
                super.onSkipToNext()

                selectNextSong(context)
            }

            override fun onSkipToPrevious() {
                super.onSkipToPrevious()

                selectPreviousSong(context)
            }

在此之后,我需要设置这些操作发生时的状态。为此,我做了一个函数来设置它。

代码语言:javascript
复制
fun setPlaybackState(state: Int){

        val stateBuilder = PlaybackStateCompat.Builder()
            .setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE
                    or PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
                    or PlaybackStateCompat.ACTION_SKIP_TO_NEXT)
            .apply {
                setState(state, mediaPlayer.currentPosition.toLong(), 1.0f)
            }

        mediaSession.setPlaybackState(stateBuilder.build())
    }

现在,每次需要更新时,我只需调用函数并将状态作为参数发送。

示例:setPlaybackState(PlaybackStateCompat.STATE_PLAYING)

希望这能帮到一个人,以防它像我一样被困了几个月

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74499371

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档