অ্যান্ড্রয়েডে মিডিয়া কন্ট্রোলগুলো কুইক সেটিংসের কাছেই অবস্থিত। একাধিক অ্যাপের সেশনগুলো একটি সোয়াইপযোগ্য ক্যারোসেলে সাজানো থাকে। ক্যারোসেলটি সেশনগুলোকে এই ক্রমে তালিকাভুক্ত করে:
- ফোনে স্থানীয়ভাবে স্ট্রিম চলছে
- দূরবর্তী স্ট্রিম, যেমন বাহ্যিক ডিভাইসে শনাক্ত করা স্ট্রিম বা কাস্ট সেশন
- পূর্ববর্তী পুনরায় শুরুযোগ্য সেশনগুলো, সর্বশেষ খেলার ক্রম অনুসারে।
অ্যান্ড্রয়েড ১৩ (এপিআই লেভেল ৩৩) থেকে, যে অ্যাপগুলো মিডিয়া প্লে করে সেগুলোর জন্য ব্যবহারকারীরা যেন মিডিয়া কন্ট্রোলের একটি সমৃদ্ধ সেট অ্যাক্সেস করতে পারে, তা নিশ্চিত করার জন্য মিডিয়া কন্ট্রোলের অ্যাকশন বাটনগুলো Player স্টেট থেকে ডিরাইভ করা হয়।
এর মাধ্যমে, আপনি বিভিন্ন ডিভাইসে মিডিয়া কন্ট্রোলের একটি সামঞ্জস্যপূর্ণ সেট এবং আরও পরিশীলিত অভিজ্ঞতা প্রদান করতে পারবেন।
চিত্র ১-এ যথাক্রমে একটি ফোন এবং ট্যাবলেট ডিভাইসে এটি দেখতে কেমন লাগে তার একটি উদাহরণ দেখানো হয়েছে।
সিস্টেমটি Player অবস্থার উপর ভিত্তি করে নিম্নলিখিত সারণীতে বর্ণিত সর্বোচ্চ পাঁচটি অ্যাকশন বাটন প্রদর্শন করে। কম্প্যাক্ট মোডে, শুধুমাত্র প্রথম তিনটি অ্যাকশন স্লট প্রদর্শিত হয়। এটি অটো, অ্যাসিস্ট্যান্ট এবং ওয়্যার ওএস-এর মতো অন্যান্য অ্যান্ড্রয়েড প্ল্যাটফর্মে মিডিয়া কন্ট্রোলগুলো যেভাবে রেন্ডার করা হয়, তার সাথে সামঞ্জস্যপূর্ণ।
| স্লট | মানদণ্ড | পদক্ষেপ |
|---|---|---|
| ১ | playWhenReady এর মান false অথবা বর্তমান প্লেব্যাক অবস্থা STATE_ENDED । | খেলা |
playWhenReady এর মান true এবং বর্তমান প্লেব্যাক স্টেট হলো STATE_BUFFERING । | লোডিং স্পিনার | |
playWhenReady এর মান true এবং বর্তমান প্লেব্যাক স্টেট হলো STATE_READY । | বিরতি | |
| ২ | মিডিয়া বাটন প্রেফারেন্সে CommandButton.SLOT_BACK এর জন্য একটি কাস্টম বাটন রয়েছে। | কাস্টম |
প্লেয়ার কমান্ড COMMAND_SEEK_TO_PREVIOUS অথবা COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM উপলব্ধ আছে। | পূর্ববর্তী | |
| কাস্টম বাটন বা তালিকাভুক্ত কমান্ডগুলোর কোনোটিই উপলব্ধ নেই। | খালি | |
| ৩ | মিডিয়া বাটন প্রেফারেন্সে CommandButton.SLOT_FORWARD এর জন্য একটি কাস্টম বাটন রয়েছে। | কাস্টম |
প্লেয়ার কমান্ড COMMAND_SEEK_TO_NEXT অথবা COMMAND_SEEK_TO_NEXT_MEDIA_ITEM উপলব্ধ আছে। | পরবর্তী | |
| কাস্টম বাটন বা তালিকাভুক্ত কমান্ডগুলোর কোনোটিই উপলব্ধ নেই। | খালি | |
| ৪ | মিডিয়া বাটন প্রেফারেন্সে CommandButton.SLOT_OVERFLOW এর জন্য একটি কাস্টম বাটন রয়েছে যা এখনও স্থাপন করা হয়নি। | কাস্টম |
| ৫ | মিডিয়া বাটন প্রেফারেন্সে CommandButton.SLOT_OVERFLOW এর জন্য একটি কাস্টম বাটন রয়েছে যা এখনও স্থাপন করা হয়নি। | কাস্টম |
কাস্টম ওভারফ্লো বাটনগুলো মিডিয়া বাটন প্রেফারেন্সে যে ক্রমে যোগ করা হয়েছিল, সেই ক্রমেই স্থাপন করা হয়।
কমান্ড বোতামগুলি কাস্টমাইজ করুন
Jetpack Media3 ব্যবহার করে সিস্টেম মিডিয়া কন্ট্রোলগুলো কাস্টমাইজ করতে, আপনি সেশনের মিডিয়া বাটন প্রেফারেন্স এবং কন্ট্রোলারগুলোর উপলব্ধ কমান্ডগুলো সেই অনুযায়ী সেট করতে পারেন:
একটি
MediaSessionতৈরি করুন এবং কাস্টম কমান্ড বাটনগুলির জন্য মিডিয়া বাটনের পছন্দগুলি নির্ধারণ করুন ।MediaSession.Callback.onConnect()-এর মধ্যে,ConnectionResultএ কাস্টম কমান্ড সহ কন্ট্রোলারদের উপলব্ধ কমান্ডগুলো সংজ্ঞায়িত করে তাদের অনুমোদন দিন।MediaSession.Callback.onCustomCommand()-এ, ব্যবহারকারীর নির্বাচিত কাস্টম কমান্ডে সাড়া দিন।
কোটলিন
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(CommandButton.ICON_HEART_UNFILLED) .setDisplayName("Save to favorites") .setSessionCommand(customCommandFavorites) .build() val player = ExoPlayer.Builder(this).build() // Build the session with a custom layout. mediaSession = MediaSession.Builder(this, player) .setCallback(MyCallback()) .setMediaButtonPreferences(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) .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) } } }
জাভা
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(CommandButton.ICON_HEART_UNFILLED) .setDisplayName("Save to favorites") .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()) .setMediaButtonPreferences(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) .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 সেই কমান্ডগুলো প্লেয়ারের কাছে হস্তান্তর করে। Media3-এর Player ইন্টারফেসে সংজ্ঞায়িত কমান্ডগুলো মিডিয়া সেশন দ্বারা স্বয়ংক্রিয়ভাবে পরিচালিত হয়।
কাস্টম কমান্ডের প্রতিক্রিয়া জানানোর নির্দেশনার জন্য "কাস্টম কমান্ড যোগ করুন" অংশটি দেখুন।
গণমাধ্যম পুনরায় চালুর সমর্থন
মিডিয়া রিজাম্পশন ব্যবহারকারীদের অ্যাপটি চালু না করেই ক্যারোসেল থেকে পূর্ববর্তী সেশনগুলো পুনরায় শুরু করার সুযোগ দেয়। প্লেব্যাক শুরু হলে, ব্যবহারকারী স্বাভাবিক উপায়ে মিডিয়া কন্ট্রোলগুলো ব্যবহার করেন।
সেটিংস অ্যাপের সাউন্ড > মিডিয়া অপশন থেকে প্লেব্যাক পুনরায় শুরু করার ফিচারটি চালু বা বন্ধ করা যায়। এছাড়াও, প্রসারিত ক্যারোসেলে সোয়াইপ করার পর যে গিয়ার আইকনটি দেখা যায়, সেটিতে ট্যাপ করেও ব্যবহারকারী সেটিংসে প্রবেশ করতে পারেন।
মিডিয়া পুনরায় চালু করার সুবিধাটি সহজ করার জন্য Media3-তে এপিআই (API) রয়েছে। এই বৈশিষ্ট্যটি বাস্তবায়নের নির্দেশনার জন্য Media3 ডকুমেন্টেশনের 'Playback resumption with Media3' অংশটি দেখুন।
লিগ্যাসি মিডিয়া এপিআই ব্যবহার করে
এই অংশে লিগ্যাসি MediaCompat API ব্যবহার করে সিস্টেম মিডিয়া কন্ট্রোলের সাথে কীভাবে ইন্টিগ্রেট করতে হয় তা ব্যাখ্যা করা হয়েছে।
সিস্টেমটি MediaSession এর MediaMetadata থেকে নিম্নলিখিত তথ্যগুলো সংগ্রহ করে এবং উপলব্ধ হলে তা প্রদর্শন করে:
-
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 এর সাথে ম্যাপ করা থাকে।
মিডিয়া প্লেয়ারটি বর্তমানে প্লে হওয়া মিডিয়ার অগ্রগতি দেখায়, সাথে একটি সিক বারও থাকে যা MediaSession PlaybackState সাথে ম্যাপ করা থাকে। সিক বারটি ব্যবহারকারীদের অবস্থান পরিবর্তন করার সুযোগ দেয় এবং মিডিয়া আইটেমটির অতিবাহিত সময় প্রদর্শন করে। সিক বারটি সক্রিয় করার জন্য, আপনাকে অবশ্যই PlaybackState.Builder#setActions ইমপ্লিমেন্ট করতে হবে এবং ACTION_SEEK_TO অন্তর্ভুক্ত করতে হবে।
| স্লট | পদক্ষেপ | মানদণ্ড |
|---|---|---|
| ১ | খেলা | PlaybackState এর বর্তমান অবস্থা নিম্নলিখিতগুলির মধ্যে একটি:
|
| লোডিং স্পিনার | PlaybackState এর বর্তমান অবস্থা নিম্নলিখিতগুলির মধ্যে একটি:
| |
| বিরতি | PlaybackState এর বর্তমান অবস্থা উপরের কোনোটিই নয়। | |
| ২ | পূর্ববর্তী | PlaybackState অ্যাকশনগুলোর মধ্যে ACTION_SKIP_TO_PREVIOUS অন্তর্ভুক্ত। |
| কাস্টম | PlaybackState অ্যাকশনগুলোতে ACTION_SKIP_TO_PREVIOUS অন্তর্ভুক্ত থাকে না এবং PlaybackState কাস্টম অ্যাকশনগুলোতে এমন একটি কাস্টম অ্যাকশন অন্তর্ভুক্ত থাকে যা এখনও স্থাপন করা হয়নি। | |
| খালি | PlaybackState এক্সট্রাগুলোর মধ্যে SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV কী-টির জন্য একটি true বুলিয়ান মান অন্তর্ভুক্ত রয়েছে। | |
| ৩ | পরবর্তী | PlaybackState অ্যাকশনগুলোর মধ্যে ACTION_SKIP_TO_NEXT অন্তর্ভুক্ত। |
| কাস্টম | PlaybackState অ্যাকশনগুলোর মধ্যে ACTION_SKIP_TO_NEXT অন্তর্ভুক্ত নয় এবং PlaybackState কাস্টম অ্যাকশনগুলোর মধ্যে এমন একটি কাস্টম অ্যাকশন অন্তর্ভুক্ত রয়েছে যা এখনও স্থাপন করা হয়নি। | |
| খালি | PlaybackState এক্সট্রাগুলোর মধ্যে SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT কী-টির জন্য একটি true বুলিয়ান মান অন্তর্ভুক্ত রয়েছে। | |
| ৪ | কাস্টম | PlaybackState কাস্টম অ্যাকশনগুলোর মধ্যে এমন একটি কাস্টম অ্যাকশন অন্তর্ভুক্ত রয়েছে যা এখনও স্থাপন করা হয়নি। |
| ৫ | কাস্টম | PlaybackState কাস্টম অ্যাকশনগুলোর মধ্যে এমন একটি কাস্টম অ্যাকশন অন্তর্ভুক্ত রয়েছে যা এখনও স্থাপন করা হয়নি। |
স্ট্যান্ডার্ড অ্যাকশন যোগ করুন
নিম্নলিখিত কোড উদাহরণগুলিতে দেখানো হয়েছে কিভাবে PlaybackState স্ট্যান্ডার্ড এবং কাস্টম অ্যাকশন যোগ করতে হয়।
প্লে, পজ, আগের এবং পরের অ্যাকশনগুলো মিডিয়া সেশনের PlaybackState এ সেট করুন।
কোটলিন
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)
জাভা
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 যোগ করবেন না, এবং এর পরিবর্তে সেশনে এক্সট্রা যোগ করুন:
কোটলিন
session.setExtras(Bundle().apply { putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV, true) putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT, true) })
জাভা
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 এ যোগ করতে পারেন। এই অ্যাকশনগুলি যে ক্রমে যোগ করা হয়েছে, সেই ক্রমেই দেখানো হয়।
কোটলিন
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)
জাভা
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);
প্লেব্যাকস্টেট অ্যাকশনগুলির প্রতিক্রিয়া জানানো
যখন কোনো ব্যবহারকারী একটি বাটনে ট্যাপ করেন, তখন SystemUI, MediaController.TransportControls ব্যবহার করে MediaSession এ একটি কমান্ড ফেরত পাঠায়। আপনাকে এমন একটি কলব্যাক রেজিস্টার করতে হবে যা এই ইভেন্টগুলোতে সঠিকভাবে সাড়া দিতে পারে।
কোটলিন
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)
জাভা
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); } } };
গণমাধ্যম পুনরায় চালু
আপনার প্লেয়ার অ্যাপটিকে কুইক সেটিং এরিয়াতে দেখানোর জন্য, আপনাকে অবশ্যই একটি বৈধ MediaSession টোকেন সহ একটি MediaStyle নোটিফিকেশন তৈরি করতে হবে।
MediaStyle নোটিফিকেশনের শিরোনাম প্রদর্শন করতে, NotificationBuilder.setContentTitle() ব্যবহার করুন।
মিডিয়া প্লেয়ারের জন্য ব্র্যান্ড আইকন প্রদর্শন করতে NotificationBuilder.setSmallIcon() ব্যবহার করুন।
প্লেব্যাক পুনরায় শুরু করার জন্য, অ্যাপগুলিকে অবশ্যই একটি MediaBrowserService এবং MediaSession ইমপ্লিমেন্ট করতে হবে। আপনার MediaSession অবশ্যই onPlay() কলব্যাকটি ইমপ্লিমেন্ট করতে হবে।
MediaBrowserService বাস্তবায়ন
ডিভাইসটি চালু হওয়ার পর, সিস্টেমটি সম্প্রতি ব্যবহৃত পাঁচটি মিডিয়া অ্যাপ খুঁজে বের করে এবং প্রতিটি অ্যাপ থেকে প্লে করা পুনরায় শুরু করার জন্য কন্ট্রোল প্রদান করে।
সিস্টেমটি SystemUI থেকে একটি সংযোগের মাধ্যমে আপনার MediaBrowserService সাথে যোগাযোগ করার চেষ্টা করে। আপনার অ্যাপকে অবশ্যই এই ধরনের সংযোগের অনুমতি দিতে হবে, অন্যথায় এটি প্লেব্যাক পুনরায় শুরু করতে পারবে না।
SystemUI থেকে সংযোগগুলি com.android.systemui প্যাকেজ নাম এবং সিগনেচার ব্যবহার করে শনাক্ত ও যাচাই করা যায়। SystemUI প্ল্যাটফর্ম সিগনেচার দ্বারা স্বাক্ষরিত। প্ল্যাটফর্ম সিগনেচারের সাথে কীভাবে যাচাই করতে হয় তার একটি উদাহরণ UAMP অ্যাপে পাওয়া যাবে।
প্লেব্যাক পুনরায় শুরু করা সমর্থন করার জন্য, আপনার MediaBrowserService অবশ্যই এই আচরণগুলি বাস্তবায়ন করতে হবে:
onGetRoot()অবশ্যই দ্রুত একটি নন-নাল রুট রিটার্ন করবে। অন্যান্য জটিল লজিকonLoadChildren()-এ পরিচালনা করা উচিত।যখন রুট মিডিয়া আইডিতে
onLoadChildren()কল করা হয়, তখন ফলাফলে অবশ্যই একটি FLAG_PLAYABLE চাইল্ড থাকতে হবে।যখন
MediaBrowserServiceএকটি EXTRA_RECENT কোয়েরি গ্রহণ করে, তখন এটির সর্বশেষ প্লে করা মিডিয়া আইটেমটি ফেরত দেওয়া উচিত। ফেরত আসা মানটি কোনো জেনেরিক ফাংশন না হয়ে একটি প্রকৃত মিডিয়া আইটেম হওয়া উচিত।MediaBrowserServiceঅবশ্যই একটি উপযুক্ত MediaDescription প্রদান করতে হবে, যার একটি অ-খালি শিরোনাম এবং উপ-শিরোনাম থাকবে । এছাড়াও, এটির একটি আইকন URI অথবা একটি আইকন বিটম্যাপ সেট করা উচিত।
নিম্নলিখিত কোড উদাহরণগুলি দেখায় কিভাবে onGetRoot() প্রয়োগ করতে হয়।
কোটলিন
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)
জাভা
@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); }
অ্যান্ড্রয়েড ১৩-এর পূর্ববর্তী আচরণ
ব্যাকওয়ার্ড কম্প্যাটিবিলিটির জন্য, যে অ্যাপগুলো অ্যান্ড্রয়েড ১৩ টার্গেট করে আপডেট হয় না, অথবা যেগুলোতে PlaybackState তথ্য অন্তর্ভুক্ত থাকে না, সেগুলোর জন্য সিস্টেম UI একটি বিকল্প লেআউট প্রদান করে, যা নোটিফিকেশন অ্যাকশন ব্যবহার করে। অ্যাকশন বাটনগুলো MediaStyle নোটিফিকেশনের সাথে সংযুক্ত Notification.Action তালিকা থেকে নেওয়া হয়। সিস্টেম সর্বোচ্চ পাঁচটি অ্যাকশনকে সেগুলো যোগ করার ক্রমানুসারে প্রদর্শন করে। কম্প্যাক্ট মোডে, সর্বোচ্চ তিনটি বাটন দেখানো হয়, যা setShowActionsInCompactView() ফাংশনে দেওয়া মান দ্বারা নির্ধারিত হয়।
কাস্টম অ্যাকশনগুলো PlaybackState এ যে ক্রমে যোগ করা হয়েছিল, সেই ক্রমেই স্থাপন করা হয়।
নিম্নলিখিত কোড উদাহরণটি দেখায় কিভাবে MediaStyle নোটিফিকেশনে অ্যাকশন যোগ করতে হয়:
কোটলিন
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()
জাভা
import androidx.core.app.NotificationCompat; import androidx.media3.session.MediaStyleNotificationHelper; NotificationCompat.Builder notification = new 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(new MediaStyleNotificationHelper.MediaStyle(mediaSession) .setShowActionsInCompactView(1 /* #1: pause button */)) .setContentTitle("Wonderful music") .setContentText("My Awesome Band") .setLargeIcon(albumArtBitmap) .build();