ตัวควบคุมสื่อใน Android จะอยู่ใกล้กับการตั้งค่าด่วน เซสชันจาก แอปหลายรายการมีการจัดเรียงเป็นภาพหมุนแบบปัดดู ภาพสไลด์แสดงรายการเซสชัน โดยเรียงตามลำดับดังนี้
- สตรีมที่เล่นในโทรศัพท์ในเครื่อง
- สตรีมระยะไกล เช่น สตรีมที่ตรวจพบในอุปกรณ์ภายนอกหรือเซสชันการแคสต์
- เซสชันที่กลับมาเล่นต่อได้ก่อนหน้า จะเรียงลำดับตามการเล่นล่าสุด
เริ่มตั้งแต่ Android 13 (API ระดับ 33) เพื่อไม่ให้ผู้ใช้เข้าถึง
ชุดตัวควบคุมสื่อสำหรับแอปที่กำลังเล่นสื่อ ปุ่มการทำงานบนตัวควบคุมสื่อ
ได้มาจากสถานะ Player
ด้วยวิธีนี้ คุณจะสามารถนำเสนอชุดการควบคุมสื่อที่สอดคล้องกันและ ประสบการณ์การควบคุมสื่อในอุปกรณ์ต่างๆ
รูปที่ 1 แสดงตัวอย่างว่าข้อมูลจะมีลักษณะอย่างไรบนโทรศัพท์และแท็บเล็ต ตามลำดับ
ระบบจะแสดงปุ่มดำเนินการสูงสุด 5 ปุ่มตามสถานะของ Player
ดังต่อไปนี้
ดังที่อธิบายในตารางต่อไปนี้ ในโหมดกะทัดรัด การดำเนินการ 3 รายการแรกเท่านั้น
จะปรากฏขึ้น การดำเนินการนี้สอดคล้องกับวิธีแสดงผลตัวควบคุมสื่อใน
แพลตฟอร์ม Android เช่น Auto, Assistant และ Wear OS
สล็อต | เกณฑ์ | การทำงาน |
---|---|---|
1 |
playWhenReady
เป็นเท็จหรือการเล่นปัจจุบัน
สถานะคือ STATE_ENDED
|
เล่น |
playWhenReady เป็นจริงและสถานะการเล่นปัจจุบันคือ STATE_BUFFERING
|
ไอคอนหมุนแสดงการโหลด | |
playWhenReady เป็นจริงและสถานะการเล่นปัจจุบันคือ STATE_READY |
หยุดชั่วคราว | |
2 | ใช้คําสั่งโปรแกรมเล่น COMMAND_SEEK_TO_PREVIOUS หรือ COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM ได้ |
ก่อนหน้า |
ไม่มีทั้งคำสั่งของโปรแกรมเล่น COMMAND_SEEK_TO_PREVIOUS และ COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM และจะใช้คำสั่งที่กำหนดเองจากเลย์เอาต์ที่กำหนดเองที่ยังไม่ได้วางเพื่อเติมช่องโฆษณาได้ |
กำหนดเอง | |
(ยังไม่รองรับ Media3) PlaybackState ส่วนเพิ่มเติมจะมีค่าบูลีน true สำหรับคีย์ EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV |
ว่าง | |
3 | ใช้คําสั่งโปรแกรมเล่น COMMAND_SEEK_TO_NEXT หรือ COMMAND_SEEK_TO_NEXT_MEDIA_ITEM ได้ |
ถัดไป |
ไม่มีทั้งคำสั่งของโปรแกรมเล่น COMMAND_SEEK_TO_NEXT และ COMMAND_SEEK_TO_NEXT_MEDIA_ITEM และจะใช้คำสั่งที่กำหนดเองจากเลย์เอาต์ที่กำหนดเองที่ยังไม่ได้วางเพื่อเติมช่องโฆษณาได้ |
กำหนดเอง | |
(ยังไม่รองรับ Media3) PlaybackState ส่วนเพิ่มเติมจะมีค่าบูลีน true สำหรับคีย์ EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT |
ว่าง | |
4 | คำสั่งที่กำหนดเองจากเลย์เอาต์ที่กำหนดเองที่ยังไม่ได้วางพร้อมให้ใช้งานเพื่อเติมช่องโฆษณา | กำหนดเอง |
5 | คำสั่งที่กำหนดเองจากเลย์เอาต์ที่กำหนดเองที่ยังไม่ได้วางพร้อมให้ใช้งานเพื่อเติมช่องโฆษณา | กำหนดเอง |
ระบบจะวางคำสั่งที่กำหนดเองตามลำดับที่เพิ่มใน เลย์เอาต์ที่กำหนดเอง
ปรับแต่งปุ่มคำสั่ง
วิธีปรับแต่งการควบคุมสื่อของระบบด้วย Jetpack Media3
คุณจะตั้งค่าเลย์เอาต์ที่กำหนดเองของเซสชันและคำสั่งที่ใช้ได้
ตัวควบคุมให้สอดคล้องกัน เมื่อใช้ MediaSessionService
มีดังนี้
ใน
onCreate()
ให้สร้างMediaSession
และกำหนดเลย์เอาต์ที่กำหนดเอง ปุ่มคำสั่งใน
MediaSession.Callback.onConnect()
ให้สิทธิ์ตัวควบคุมโดยกำหนดคำสั่งที่ใช้ได้ ซึ่งรวมถึง คำสั่งที่กำหนดเอง ในConnectionResult
ใน
MediaSession.Callback.onCustomCommand()
ตอบสนองต่อคำสั่งที่กำหนดเองที่ผู้ใช้เลือก
Kotlin
class PlaybackService : MediaSessionService() { private val customCommandFavorites = SessionCommand(ACTION_FAVORITES, Bundle.EMPTY) private var mediaSession: MediaSession? = null override fun onCreate() { super.onCreate() val favoriteButton = CommandButton.Builder() .setDisplayName("Save to favorites") .setIconResId(R.drawable.favorite_icon) .setSessionCommand(customCommandFavorites) .build() val player = ExoPlayer.Builder(this).build() // Build the session with a custom layout. mediaSession = MediaSession.Builder(this, player) .setCallback(MyCallback()) .setCustomLayout(ImmutableList.of(favoriteButton)) .build() } private inner class MyCallback : MediaSession.Callback { override fun onConnect( session: MediaSession, controller: MediaSession.ControllerInfo ): ConnectionResult { // Set available player and session commands. return AcceptedResultBuilder(session) .setAvailablePlayerCommands( ConnectionResult.DEFAULT_PLAYER_COMMANDS.buildUpon() .remove(COMMAND_SEEK_TO_NEXT) .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM) .remove(COMMAND_SEEK_TO_PREVIOUS) .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM) .build() ) .setAvailableSessionCommands( ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(customCommandFavorites) .build() ) .build() } override fun onCustomCommand( session: MediaSession, controller: MediaSession.ControllerInfo, customCommand: SessionCommand, args: Bundle ): ListenableFuture{ if (customCommand.customAction == ACTION_FAVORITES) { // Do custom logic here saveToFavorites(session.player.currentMediaItem) return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS)) } return super.onCustomCommand(session, controller, customCommand, args) } } }
Java
public class PlaybackService extends MediaSessionService { private static final SessionCommand CUSTOM_COMMAND_FAVORITES = new SessionCommand("ACTION_FAVORITES", Bundle.EMPTY); @Nullable private MediaSession mediaSession; public void onCreate() { super.onCreate(); CommandButton favoriteButton = new CommandButton.Builder() .setDisplayName("Save to favorites") .setIconResId(R.drawable.favorite_icon) .setSessionCommand(CUSTOM_COMMAND_FAVORITES) .build(); Player player = new ExoPlayer.Builder(this).build(); // Build the session with a custom layout. mediaSession = new MediaSession.Builder(this, player) .setCallback(new MyCallback()) .setCustomLayout(ImmutableList.of(favoriteButton)) .build(); } private static class MyCallback implements MediaSession.Callback { @Override public ConnectionResult onConnect( MediaSession session, MediaSession.ControllerInfo controller) { // Set available player and session commands. return new AcceptedResultBuilder(session) .setAvailablePlayerCommands( ConnectionResult.DEFAULT_PLAYER_COMMANDS.buildUpon() .remove(COMMAND_SEEK_TO_NEXT) .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM) .remove(COMMAND_SEEK_TO_PREVIOUS) .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM) .build()) .setAvailableSessionCommands( ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(CUSTOM_COMMAND_FAVORITES) .build()) .build(); } public ListenableFutureonCustomCommand( MediaSession session, MediaSession.ControllerInfo controller, SessionCommand customCommand, Bundle args) { if (customCommand.customAction.equals(CUSTOM_COMMAND_FAVORITES.customAction)) { // Do custom logic here saveToFavorites(session.getPlayer().getCurrentMediaItem()); return Futures.immediateFuture(new SessionResult(SessionResult.RESULT_SUCCESS)); } return MediaSession.Callback.super.onCustomCommand( session, controller, customCommand, args); } } }
หากต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับการกำหนดค่า MediaSession
เพื่อให้ลูกค้าอย่าง
ระบบสามารถเชื่อมต่อกับแอปสื่อของคุณได้ โปรดดู
ให้สิทธิ์ควบคุมแก่ลูกค้ารายอื่น
เมื่อใช้ Jetpack Media3 เมื่อคุณใช้ MediaSession
PlaybackState
ได้รับการอัปเดตโดยอัตโนมัติด้วยมีเดียเพลเยอร์ ในทำนองเดียวกัน เมื่อคุณ
ใช้ MediaSessionService
ไลบรารีจะเผยแพร่
MediaStyle
การแจ้งเตือน
ให้กับคุณและคอยอัปเดตให้เป็นปัจจุบันอยู่เสมอ
ตอบสนองต่อปุ่มการทำงาน
เมื่อผู้ใช้แตะปุ่มการทำงานในตัวควบคุมสื่อของระบบ
MediaController
ส่งคำสั่งการเล่นไปยัง MediaSession
จากนั้น MediaSession
จะมอบสิทธิ์คำสั่งเหล่านั้นให้โปรแกรมเล่น คำสั่ง
ที่กำหนดไว้ใน Player
ของ Media3
ระบบจะจัดการอินเทอร์เฟซ
โดยสื่อโดยอัตโนมัติ
เซสชัน
โปรดดูหัวข้อเพิ่มคำสั่งที่กำหนดเอง เพื่อดูคำแนะนำเกี่ยวกับวิธีตอบสนองต่อคำสั่งที่กำหนดเอง
พฤติกรรมก่อน Android 13
สำหรับความเข้ากันได้แบบย้อนหลัง UI ของระบบจะยังคงมีเลย์เอาต์สำรอง
ซึ่งใช้การดำเนินการแจ้งเตือนสำหรับแอปที่ไม่ได้อัปเดตให้กำหนดเป้าหมายเป็น Android 13
หรือไม่มีข้อมูล PlaybackState
ปุ่มดำเนินการ
มาจากรายการ Notification.Action
ที่แนบมากับ MediaStyle
การแจ้งเตือน ระบบจะแสดงการดำเนินการสูงสุด 5 รายการตามลำดับ
เพิ่ม แล้ว ในโหมดกะทัดรัด ปุ่มจะแสดงถึงสามปุ่ม ซึ่งขึ้นอยู่กับ
ค่าที่ส่งให้ setShowActionsInCompactView()
การดำเนินการที่กำหนดเองจะวางตามลำดับที่เพิ่มใน
PlaybackState
ตัวอย่างโค้ดต่อไปนี้แสดงวิธีเพิ่มการทำงานให้กับ MediaStyle การแจ้งเตือน :
Kotlin
import androidx.core.app.NotificationCompat import androidx.media3.session.MediaStyleNotificationHelper var notification = NotificationCompat.Builder(context, CHANNEL_ID) // Show controls on lock screen even when user hides sensitive content. .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setSmallIcon(R.drawable.ic_stat_player) // Add media control buttons that invoke intents in your media service .addAction(R.drawable.ic_prev, "Previous", prevPendingIntent) // #0 .addAction(R.drawable.ic_pause, "Pause", pausePendingIntent) // #1 .addAction(R.drawable.ic_next, "Next", nextPendingIntent) // #2 // Apply the media style template .setStyle(MediaStyleNotificationHelper.MediaStyle(mediaSession) .setShowActionsInCompactView(1 /* #1: pause button */)) .setContentTitle("Wonderful music") .setContentText("My Awesome Band") .setLargeIcon(albumArtBitmap) .build()
Java
import androidx.core.app.NotificationCompat; import androidx.media3.session.MediaStyleNotificationHelper; NotificationCompat.Builder notification = new NotificationCompat.Builder(context, CHANNEL_ID) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setSmallIcon(R.drawable.ic_stat_player) .addAction(R.drawable.ic_prev, "Previous", prevPendingIntent) .addAction(R.drawable.ic_pause, "Pause", pausePendingIntent) .addAction(R.drawable.ic_next, "Next", nextPendingIntent) .setStyle(new MediaStyleNotificationHelper.MediaStyle(mediaSession) .setShowActionsInCompactView(1 /* #1: pause button */)) .setContentTitle("Wonderful music") .setContentText("My Awesome Band") .setLargeIcon(albumArtBitmap) .build();
รองรับการกลับมาเล่นสื่ออีกครั้ง
การกลับมาเล่นสื่ออีกครั้งช่วยให้ผู้ใช้รีสตาร์ทเซสชันก่อนหน้าจากภาพสไลด์ได้ โดยไม่ต้องเริ่มแอป เมื่อเริ่มเล่น ผู้ใช้จะโต้ตอบกับ ควบคุมสื่อตามปกติ
คุณสามารถเปิดและปิดฟีเจอร์การกลับมาเล่นต่อได้โดยใช้แอปการตั้งค่า ภายใต้ส่วน เสียง > สื่อ ผู้ใช้ยังเข้าถึงการตั้งค่าได้โดย แตะไอคอนรูปเฟืองที่ปรากฏขึ้นหลังจากเลื่อนบนภาพสไลด์ที่ขยาย
Media3 มี API ที่ช่วยให้รองรับการกลับมาเล่นสื่อต่อได้ง่ายขึ้น โปรดดู การกลับมาเล่นอีกครั้งด้วย Media3 เพื่อดูคำแนะนำในการใช้ฟีเจอร์นี้ได้
การใช้ API สื่อแบบเดิม
ส่วนนี้จะอธิบายวิธีผสานรวมกับการควบคุมสื่อของระบบโดยใช้ MediaCompat API เดิม
ระบบจะดึงข้อมูลต่อไปนี้จาก
MediaMetadata
ของ MediaSession
และจะแสดงเมื่อพร้อมให้บริการ:
METADATA_KEY_ALBUM_ART_URI
METADATA_KEY_TITLE
METADATA_KEY_DISPLAY_TITLE
METADATA_KEY_ARTIST
METADATA_KEY_DURATION
(หากไม่ได้ตั้งค่าระยะเวลา แถบค้นหาไม่มีการกำหนดค่า แสดงความคืบหน้า)
เพื่อให้คุณมีการแจ้งเตือนการควบคุมสื่อที่ถูกต้องและแม่นยำ
กำหนดค่าของ METADATA_KEY_TITLE
หรือ METADATA_KEY_DISPLAY_TITLE
ลงในชื่อของสื่อที่กำลังเล่นอยู่
มีเดียเพลเยอร์จะแสดงเวลาที่ผ่านไปของวิดีโอที่กำลังเล่นอยู่
สื่อ พร้อมด้วยแถบค้นหาซึ่งแมปกับ MediaSession
PlaybackState
มีเดียเพลเยอร์จะแสดงความคืบหน้าของสื่อที่เล่นอยู่ พร้อมกับ
แถบเลื่อนที่แมปกับ PlaybackState
ของ MediaSession
แถบเลื่อน
ช่วยให้ผู้ใช้เปลี่ยนตำแหน่งและแสดงเวลาที่ผ่านไปของสื่อได้
รายการ หากต้องการเปิดใช้งานแถบค้นหา คุณจะต้องติดตั้งใช้งาน
PlaybackState.Builder#setActions
และรวม ACTION_SEEK_TO
สล็อต | การทำงาน | เกณฑ์ |
---|---|---|
1 | เล่น |
สถานะปัจจุบันของ PlaybackState คืออย่างใดอย่างหนึ่งต่อไปนี้
|
ไอคอนหมุนแสดงการโหลด |
สถานะปัจจุบันของ PlaybackState เป็นอย่างใดอย่างหนึ่งต่อไปนี้
|
|
หยุดชั่วคราว | สถานะปัจจุบันของ PlaybackState ไม่ตรงกับข้อใดข้างต้น |
|
2 | ก่อนหน้า | PlaybackState การกระทำ ได้แก่ ACTION_SKIP_TO_PREVIOUS |
กำหนดเอง | PlaybackState การกระทำไม่รวม ACTION_SKIP_TO_PREVIOUS และ PlaybackState การกระทำที่กำหนดเองรวมการกระทำที่กำหนดเองที่ยังไม่ได้วาง |
|
ว่าง | PlaybackState ส่วนเพิ่มเติมมีค่าบูลีน true สำหรับคีย์ SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV |
|
3 | ถัดไป | PlaybackState การกระทำ ได้แก่ ACTION_SKIP_TO_NEXT |
กำหนดเอง | PlaybackState การกระทำไม่รวม ACTION_SKIP_TO_NEXT และ PlaybackState การกระทำที่กำหนดเองรวมการกระทำที่กำหนดเองที่ยังไม่ได้วาง |
|
ว่าง | PlaybackState ส่วนเพิ่มเติมมีค่าบูลีน true สำหรับคีย์ SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT |
|
4 | กำหนดเอง | PlaybackState การกระทำที่กำหนดเองจะรวมการกระทำที่กำหนดเองที่ยังไม่ได้วาง |
5 | กำหนดเอง | PlaybackState การกระทำที่กำหนดเองจะรวมการกระทำที่กำหนดเองที่ยังไม่ได้วาง |
เพิ่มการดำเนินการมาตรฐาน
ตัวอย่างโค้ดต่อไปนี้แสดงวิธีเพิ่ม PlaybackState
มาตรฐานและ
การกระทำที่กำหนดเอง
สำหรับการเล่น หยุดชั่วคราว ก่อนหน้า และถัดไป ให้ตั้งค่าการดำเนินการเหล่านี้ใน
PlaybackState
สำหรับเซสชันสื่อ
Kotlin
val session = MediaSessionCompat(context, TAG) val playbackStateBuilder = PlaybackStateCompat.Builder() val style = NotificationCompat.MediaStyle() // For this example, the media is currently paused: val state = PlaybackStateCompat.STATE_PAUSED val position = 0L val playbackSpeed = 1f playbackStateBuilder.setState(state, position, playbackSpeed) // And the user can play, skip to next or previous, and seek val stateActions = PlaybackStateCompat.ACTION_PLAY or PlaybackStateCompat.ACTION_PLAY_PAUSE or PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS or PlaybackStateCompat.ACTION_SKIP_TO_NEXT or PlaybackStateCompat.ACTION_SEEK_TO // adding the seek action enables seeking with the seekbar playbackStateBuilder.setActions(stateActions) // ... do more setup here ... session.setPlaybackState(playbackStateBuilder.build()) style.setMediaSession(session.sessionToken) notificationBuilder.setStyle(style)
Java
MediaSessionCompat session = new MediaSessionCompat(context, TAG); PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder(); NotificationCompat.MediaStyle style = new NotificationCompat.MediaStyle(); // For this example, the media is currently paused: int state = PlaybackStateCompat.STATE_PAUSED; long position = 0L; float playbackSpeed = 1f; playbackStateBuilder.setState(state, position, playbackSpeed); // And the user can play, skip to next or previous, and seek long stateActions = PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS | PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SEEK_TO; // adding this enables the seekbar thumb playbackStateBuilder.setActions(stateActions); // ... do more setup here ... session.setPlaybackState(playbackStateBuilder.build()); style.setMediaSession(session.getSessionToken()); notificationBuilder.setStyle(style);
หากคุณไม่ต้องการปุ่มใดๆ ในช่องก่อนหน้าหรือถัดไป ไม่ต้องเพิ่ม
ACTION_SKIP_TO_PREVIOUS
หรือ ACTION_SKIP_TO_NEXT
และเพิ่มรายการพิเศษลงใน
เซสชัน
Kotlin
session.setExtras(Bundle().apply { putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV, true) putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT, true) })
Java
Bundle extras = new Bundle(); extras.putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV, true); extras.putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT, true); session.setExtras(extras);
เพิ่มการกระทำที่กำหนดเอง
สำหรับการดำเนินการอื่นๆ ที่ต้องการให้แสดงในตัวควบคุมสื่อ คุณสามารถสร้าง
PlaybackStateCompat.CustomAction
แล้วเพิ่มลงใน PlaybackState
แทน การดำเนินการเหล่านี้จะปรากฏใน
ลำดับที่เพิ่มเข้ามา
Kotlin
val customAction = PlaybackStateCompat.CustomAction.Builder( "com.example.MY_CUSTOM_ACTION", // action ID "Custom Action", // title - used as content description for the button R.drawable.ic_custom_action ).build() playbackStateBuilder.addCustomAction(customAction)
Java
PlaybackStateCompat.CustomAction customAction = new PlaybackStateCompat.CustomAction.Builder( "com.example.MY_CUSTOM_ACTION", // action ID "Custom Action", // title - used as content description for the button R.drawable.ic_custom_action ).build(); playbackStateBuilder.addCustomAction(customAction);
การตอบสนองต่อการทำงาน PlaybackState
เมื่อผู้ใช้แตะปุ่ม SystemUI จะใช้
MediaController.TransportControls
เพื่อส่งคำสั่งกลับไปยัง MediaSession
คุณต้องลงทะเบียนการติดต่อกลับ
ที่สามารถตอบสนองต่อเหตุการณ์เหล่านี้ได้อย่างเหมาะสม
Kotlin
val callback = object: MediaSession.Callback() { override fun onPlay() { // start playback } override fun onPause() { // pause playback } override fun onSkipToPrevious() { // skip to previous } override fun onSkipToNext() { // skip to next } override fun onSeekTo(pos: Long) { // jump to position in track } override fun onCustomAction(action: String, extras: Bundle?) { when (action) { CUSTOM_ACTION_1 -> doCustomAction1(extras) CUSTOM_ACTION_2 -> doCustomAction2(extras) else -> { Log.w(TAG, "Unknown custom action $action") } } } } session.setCallback(callback)
Java
MediaSession.Callback callback = new MediaSession.Callback() { @Override public void onPlay() { // start playback } @Override public void onPause() { // pause playback } @Override public void onSkipToPrevious() { // skip to previous } @Override public void onSkipToNext() { // skip to next } @Override public void onSeekTo(long pos) { // jump to position in track } @Override public void onCustomAction(String action, Bundle extras) { if (action.equals(CUSTOM_ACTION_1)) { doCustomAction1(extras); } else if (action.equals(CUSTOM_ACTION_2)) { doCustomAction2(extras); } else { Log.w(TAG, "Unknown custom action " + action); } } };
การกลับมาเล่นสื่ออีกครั้ง
หากต้องการให้แอปโปรแกรมเล่นปรากฏในพื้นที่การตั้งค่าด่วน ให้ทำดังนี้
คุณต้องสร้างการแจ้งเตือน MediaStyle
ด้วยโทเค็น MediaSession
ที่ถูกต้อง
หากต้องการแสดงชื่อของการแจ้งเตือน MediaStyle ให้ใช้
NotificationBuilder.setContentTitle()
หากต้องการแสดงไอคอนแบรนด์สำหรับมีเดียเพลเยอร์ ให้ใช้
NotificationBuilder.setSmallIcon()
แอปต้องใช้ MediaBrowserService
เพื่อรองรับการกลับมาเล่นอีกครั้ง
และ MediaSession
MediaSession
ต้องใช้ Callback onPlay()
การใช้งาน MediaBrowserService
หลังจากเปิดเครื่อง ระบบจะมองหาสื่อที่ใช้งานล่าสุด 5 รายการ และให้การควบคุมที่สามารถใช้เพื่อเริ่มเล่นอีกครั้งจากแต่ละแอป
ระบบพยายามติดต่อ MediaBrowserService
ด้วยการเชื่อมต่อจาก
SystemUI แอปของคุณต้องอนุญาตการเชื่อมต่อดังกล่าว มิฉะนั้นจะไม่รองรับ
การกลับมาเล่นต่อ
ระบุและยืนยันการเชื่อมต่อจาก SystemUI ได้โดยใช้ชื่อแพ็กเกจ
com.android.systemui
และลายเซ็น SystemUI ลงนามกับแพลตฟอร์มแล้ว
ลายเซ็น ตัวอย่างวิธีตรวจสอบกับลายเซ็นของแพลตฟอร์มมีดังนี้
ที่พบในแอป UAMP
MediaBrowserService
ต้อง
ใช้ลักษณะการทำงานเหล่านี้
onGetRoot()
ต้องแสดงผลรากที่ไม่เป็นค่าว่างอย่างรวดเร็ว ตรรกะที่ซับซ้อนอื่นๆ ควร ได้รับการจัดการในonLoadChildren()
วันและเวลา มีการเรียก
onLoadChildren()
ในรหัสสื่อราก ผลลัพธ์ต้องมี FLAG_PLAYABLE ของบุตรหลานMediaBrowserService
ควรแสดงรายการสื่อที่เล่นล่าสุดเมื่อ พวกเขาจะได้รับ แสดงเพิ่มเติมล่าสุด คำถาม ค่าที่แสดงผลควรเป็นรายการสื่อจริงแทนที่จะเป็นรายการทั่วไปMediaBrowserService
จะต้องระบุ MediaDescription พร้อมค่าที่ไม่ว่างเปล่า ชื่อ และ คำบรรยาย และยังควรตั้งค่า URL ของไอคอน หรือแท็ก บิตแมปของไอคอน
ตัวอย่างโค้ดต่อไปนี้แสดงวิธีใช้งาน onGetRoot()
Kotlin
override fun onGetRoot( clientPackageName: String, clientUid: Int, rootHints: Bundle? ): BrowserRoot? { ... // Verify that the specified package is SystemUI. You'll need to write your // own logic to do this. if (isSystem(clientPackageName, clientUid)) { rootHints?.let { if (it.getBoolean(BrowserRoot.EXTRA_RECENT)) { // Return a tree with a single playable media item for resumption. val extras = Bundle().apply { putBoolean(BrowserRoot.EXTRA_RECENT, true) } return BrowserRoot(MY_RECENTS_ROOT_ID, extras) } } // You can return your normal tree if the EXTRA_RECENT flag is not present. return BrowserRoot(MY_MEDIA_ROOT_ID, null) } // Return an empty tree to disallow browsing. return BrowserRoot(MY_EMPTY_ROOT_ID, null)
Java
@Override public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) { ... // Verify that the specified package is SystemUI. You'll need to write your // own logic to do this. if (isSystem(clientPackageName, clientUid)) { if (rootHints != null) { if (rootHints.getBoolean(BrowserRoot.EXTRA_RECENT)) { // Return a tree with a single playable media item for resumption. Bundle extras = new Bundle(); extras.putBoolean(BrowserRoot.EXTRA_RECENT, true); return new BrowserRoot(MY_RECENTS_ROOT_ID, extras); } } // You can return your normal tree if the EXTRA_RECENT flag is not present. return new BrowserRoot(MY_MEDIA_ROOT_ID, null); } // Return an empty tree to disallow browsing. return new BrowserRoot(MY_EMPTY_ROOT_ID, null); }