今天來實作 Widget 顯示資訊的部分,因為 Widget 元件要透過 BroadcastReceiver 來接收事件,因此要透過 intent 發送。下面的 MediaControllerCallback 在實作播放功能時有介紹到,當播放資訊和狀態有改變時,會收到 callback,然後再將資訊送出。
private inner class MediaControllerCallback : MediaControllerCompat.Callback() {
override fun onPlaybackStateChanged(state: PlaybackStateCompat?) {
Log.d(TAG, "MediaControllerCallback onPlaybackStateChanged:$state")
playbackState.postValue(state ?: EMPTY_PLAYBACK_STATE)
}
override fun onMetadataChanged(metadata: MediaMetadataCompat?) {
Log.d(TAG, "MediaControllerCallback onMetadataChanged:$metadata")
nowPlaying.postValue(
if (metadata?.id == null) {
NOTHING_PLAYING
} else {
sendWidgetIntent(metadata) // Add this to send ingo through intent
metadata
}
)
}
}
將歌曲名稱、歌手名稱、專輯封面透過 intent,由 Broadcast 的方式送出。
private fun sendWidgetIntent(metadata: MediaMetadataCompat) {
val intent = Intent(context, MusicAppWidget::class.java).apply {
action = WidgetConstants.METADATA_CHANGED
putExtra(WidgetConstants.ARGUMENT_SONG_ID, metadata.id)
putExtra(WidgetConstants.ARGUMENT_TITLE, metadata.title)
putExtra(WidgetConstants.ARGUMENT_SUBTITLE, metadata.displaySubtitle)
putExtra(WidgetConstants.ARGUMENT_COVER_URI, metadata.displayIconUri.toString())
}
context.sendBroadcast(intent)
}
在 MusicAppWidget 內,就可以透過 onReceive 收對應的 action,還有對應的 key,再將資訊取出,就類似 activity 傳資料到另外的 activity 的方式。
class MusicAppWidget : AppWidgetProvider() {
......
override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent)
val appWidgetManager =
context.getSystemService(Context.APPWIDGET_SERVICE) as AppWidgetManager
val appWidgetIds =
appWidgetManager.getAppWidgetIds(ComponentName(context, MusicAppWidget::class.java))
when (intent.action) {
WidgetConstants.METADATA_CHANGED -> {
val id = intent.getLongExtra(WidgetConstants.ARGUMENT_SONG_ID, 0)
val title = intent.getStringExtra(WidgetConstants.ARGUMENT_TITLE)!!
val subtitle = intent.getStringExtra(WidgetConstants.ARGUMENT_SUBTITLE)!!
val coverPath = intent.getStringExtra(WidgetConstants.ARGUMENT_COVER_URI)!!
val metadata = WidgetMetadata(id, title, subtitle, coverPath)
for (appWidgetId in appWidgetIds) {
metaChanged(context, metadata, appWidgetManager, appWidgetId)
}
}
}
}
}
可以看到這邊是透過 RemoteViews 的方式來更新,先選擇要更新的類型(字的內容、字體顏色、圖片內容),然後再填入對應的 View Id,再拿出相對應的內容填入。
private fun metaChanged(
context: Context,
widgetMetadata: WidgetMetadata,
appWidgetManager: AppWidgetManager,
appWidgetId: Int
) {
val views = RemoteViews(context.packageName, R.layout.music_app_widget)
views.setTextViewText(R.id.title, widgetMetadata.title)
views.setTextViewText(R.id.subtitle, widgetMetadata.subtitle)
//Use Glide to load cover
appWidgetManager.updateAppWidget(appWidgetId, views)
}
關於 Widget 的設定,主要是參考這個音樂 App 的寫法:canaree-music-player
成果圖:
放大縮小:
程式碼在這,分支名稱(day21_widget_ui_information): Fancy/day21_widget_ui_information