Kotlin

[android : kotlin] 코틀린 Notification MediaStyle 사용시 앨범 자켓(이미지) setLargeIcon 설정 방법 :Bitmap , MediaMetadataCompat

Notificaiton 구현시 미디어 스타일(MediaStyle) 사용하는 경우 아래 스크린샷과 같이 해당 노래의 앨범자켓을 표시할 수 있다. 그런데 이 방법에는 제약이 있다. 무조건 Bitmap 데이터로 가져와야한다는 것이다.


출처: 안드로이드 개발자 문서 : 미디어 컨트롤로 알림 만들기

코틀린 mediaMetadata 설정하는 방법

NotificationCompat 빌더 코드이다.  빌더 생성시 mediaMetadata에서 description 데이터를 가져와서 설정해주고 있다. 그럼으로 mediaMetadata 설정하는 방법을 먼저 알아보자.

private fun getNotification(playbackState: Int) : NotificationCompat.Builder {

        // Get the session's metadata
        val controller = mediaSession.controller
        val mediaMetadata = controller.metadata
        val description = mediaMetadata.description


        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

            val builder =  NotificationCompat.Builder(this, CHANNEL_ID).apply {
                setContentTitle(description.title)
                setContentText(description.subtitle)
                setSubText(description.description)
                setLargeIcon(description.iconBitmap)  // 반드시 bitmap호출

                // Show controls on lock screen even when user hides sensitive content.
                setVisibility(NotificationCompat.VISIBILITY_PUBLIC)

                // Enable launching the player by clicking the notification
                setContentIntent(controller.sessionActivity)

                // Add an app icon and set its accent color
                // Be careful about the color
                //color = ContextCompat.getColor(context, R.color.primaryDark)
                setSmallIcon(R.mipmap.ic_launcher_round)

                setShowWhen(false)
                priority = NotificationCompat.PRIORITY_HIGH
                //setOnlyAlertOnce(true)
                //setChannelId(CHANNEL_ID)

                setDeleteIntent(
                    MediaButtonReceiver.buildMediaButtonPendingIntent(
                        applicationContext,
                        PlaybackStateCompat.ACTION_STOP
                    )
                )

                addAction(
                    R.drawable.baseline_skip_previous_24,
                    getString(R.string.previous),
                    MediaButtonReceiver.buildMediaButtonPendingIntent(
                        applicationContext,
                        PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
                    )
                ).build()




                // Add a pause button
                if(playbackState == PlaybackStateCompat.STATE_PLAYING) {
                    addAction(
                        NotificationCompat.Action(
                            R.drawable.baseline_pause_circle_outline_24,
                            getString(R.string.pause),
                            MediaButtonReceiver.buildMediaButtonPendingIntent(
                                applicationContext,
                                PlaybackStateCompat.ACTION_PLAY_PAUSE
                            )
                        )
                    )
                }else{
                    addAction(
                        R.drawable.baseline_play_circle_outline_24,
                        getString(R.string.play),
                        MediaButtonReceiver.buildMediaButtonPendingIntent(
                            applicationContext,
                            PlaybackStateCompat.ACTION_PLAY_PAUSE
                        )
                    ).build()
                }


                addAction(
                    R.drawable.baseline_skip_next_24,
                    getString(R.string.next),
                    MediaButtonReceiver.buildMediaButtonPendingIntent(
                        applicationContext,
                        PlaybackStateCompat.ACTION_SKIP_TO_NEXT
                    )
                ).build()

                // Take advantage of MediaStyle features
                setStyle(
                    androidx.media.app.NotificationCompat.MediaStyle()
                        .setMediaSession(sessionToken)
                        .setShowActionsInCompactView(0, 1, 2)

                        // Add a cancel button
                        .setShowCancelButton(true)
                        .setCancelButtonIntent(
                            MediaButtonReceiver.buildMediaButtonPendingIntent(
                                applicationContext, PlaybackStateCompat.ACTION_STOP
                            )
                        )
                )

            }
            return builder
        }
        return NotificationCompat.Builder(this, CHANNEL_ID)

    }

아래 코드스니펫을 보자. MyMediaSessionCallback메서드를 구현하였다. onPlay 메서드를 오버라이드 하였는데, 그 안에서 updateMetaData()메서드를 호출해주었다.

    inner class MyMediaSessionCallback : MediaSessionCompat.Callback() {
        var currentState = PlaybackStateCompat.STATE_STOPPED
 
        override fun onPlay() { 
            if(!exoPlayer.playWhenReady) {


                var item = SongLib.getCurrent()
                updateMetadata(item)

                prepareToPlay(item.contentUri)

                if (!audioFocusRequested) {
                    audioFocusRequested = true
                    val audioFocusResult: Int
                    audioFocusResult = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                        audioManager.requestAudioFocus(audioFocusRequest)
                    } else {
                        audioManager.requestAudioFocus(
                            audioFocusChangeListener,
                            AudioManager.STREAM_MUSIC,
                            AudioManager.AUDIOFOCUS_GAIN
                        )
                    }

                    if (audioFocusResult != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) return
                }


                mediaSession.isActive = true  

                exoPlayer.playWhenReady = true

             
                registerReceiver(
                    becomingNoisyReceiver,
                    IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY)
                )

                mediaSession.setPlaybackState(
                    stateBuilder.setState(
                        PlaybackStateCompat.STATE_PLAYING,
                        PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1F
                    ).build()
                )
                currentState = PlaybackStateCompat.STATE_PLAYING
  
                refreshNotifications(currentState)

            }
 

            super.onPlay()
        }

updateMetadata메서드는 아래 코드 스니펫을 참고 하자. 그 어떤 방법도 먹히지 않았다. 무조건 bitmap 데이터로 전달해야한다.

    private fun updateMetadata(item: MusicViewModel) {


        //android.util.Log.i("jinsu", "vo.getImgUri() = " + vo.getImgUri() + "]");

        //album.setImageURI(vo.getImgUri());

        //java.io.FileNotFoundException: No entry for content://media/external/audio/albumart/291  오류 해결법
        val bm: Bitmap? = SongLib.getAlbumArt(applicationContext, item.albumArtUri)
        Log.e(TAG, "######### item.Bitmap :${bm.toString()}")
        var metadataBuilder = MediaMetadataCompat.Builder()
//        metadataBuilder.putBitmap(
//            MediaMetadataCompat.METADATA_KEY_ART_URI, BitmapFactory.decodeResource(
//                resources, track.getBitmapResId()
//            )
//        )

        //iconBitmap
        // "content://media/external/audio/media/" + Long.toString(songId) + "/albumart"
        Log.e(TAG, "item.albumArtUri :${item.albumArtUri.toString()}")
       // metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, "content://media/external/audio/media/393/albumart")
        metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, bm)
        //metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, item.albumArtUri.toString())
        //metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, item.albumArtUri.toString())
        metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, item.title)
        metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, "dsfdsfdsfdsfdsdfsdf")
        metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, item.artist)
        metadataBuilder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, item.duration)
        mediaSession.setMetadata(metadataBuilder.build())

    }

[getAlbumArt 메서드]

        fun getAlbumArt(context: Context, uri: Uri?): Bitmap? {
            val cr: ContentResolver = context.contentResolver
            var inputStream: InputStream? = null
            inputStream  = try {
                cr.openInputStream(uri!!)
            } catch (e: FileNotFoundException) {
                //e.printStackTrace();
                return null
            } catch (ee: java.lang.Exception) {
                return null
            }
            return if (inputStream != null) {
                BitmapFactory.decodeStream(inputStream)
            } else null
        }

[build.gradle(:app)]

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 29

    defaultConfig {
        applicationId "edu.kotlin.study"
        minSdkVersion 23
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    // For Java compilers:
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.2'
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'

    implementation "androidx.media:media:1.2.0"
    implementation "androidx.recyclerview:recyclerview:1.1.0"
    //implementation "com.android.support:support-media-compat:29.+"

    // full exoplayer library
    //implementation 'com.google.android.exoplayer:exoplayer:2.11.5'
    implementation 'com.google.android.exoplayer:exoplayer-core:2.11.5'

    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

}

[REFERENCE]

stackoverflow.com/questions/35647821/android-notification-addaction-deprecated-in-api-23

미디어 컨트롤로 알림 만들기

[연관 글 더보기]

[android : kotlin] 코틀린 Notification MediaStyle 사용시 SeekBar 설정 및 해제 하는 방법

[android : kotlin] 코틀린 RecyclerView 클릭시 미디어 재생 하는 방법 : MediaController ,SimpleExoPlayer

[android : kotlin] 코틀린 Notification setShowActionsInCompactView 사용 예제 : MediaStyle

[프로그래밍/Kotlin] – [android : kotlin] 코틀린 Notification addAction 추가하는 방법 : Notification.Action.Builder

[프로그래밍/Kotlin] – [android : kotlin] 코틀린 Notification 사용 예제

[프로그래밍/Android] – 잠금화면에 알림내용(NotificationCompat) 노출하기 (to show content in lock screen

Leave a Reply

error: Content is protected !!