MediaSessionService-এর মাধ্যমে ব্যাকগ্রাউন্ড প্লেব্যাক

কোনও অ্যাপ ফোরগ্রাউন্ডে না থাকাকালীন মিডিয়া চালানো প্রায়ই প্রয়োজন হয়। যেমন, ব্যবহারকারী ডিভাইস লক করে রাখলে বা অন্য অ্যাপ ব্যবহার করলেও মিউজিক প্লেয়ার সাধারণত মিউজিক চালিয়ে যেতে থাকে। Media3 লাইব্রেরি এমন ইন্টারফেসের একটি সিরিজ প্রদান করে যা আপনাকে ব্যাকগ্রাউন্ড প্লেব্যাক সাপোর্ট করতে দেয়।

MediaSessionService ব্যবহার করা

ব্যাকগ্রাউন্ড প্লেব্যাক চালু করতে, আপনাকে আলাদা পরিষেবার মধ্যে Player এবং MediaSession রাখতে হবে। এর ফলে, আপনার অ্যাপ ফোরগ্রাউন্ডে না থাকলেও ডিভাইস মিডিয়া পরিবেশন করা চালিয়ে যেতে পারে।

MediaSessionService, অ্যাপের অ্যাক্টিভিটি থেকে আলাদাভাবে
  মিডিয়া সেশন চালানোর অনুমতি দেয়
ছবি ১: MediaSessionService মিডিয়া সেশনকে অ্যাপের অ্যাক্টিভিটি থেকে আলাদাভাবে রান করার অনুমতি দেয়

কোনও পরিষেবার মধ্যে প্লেয়ার হোস্ট করার সময়, আপনাকে MediaSessionService ব্যবহার করতে হবে। এটি করতে, MediaSessionService এক্সটেন্ড করে এমন একটি ক্লাস তৈরি করুন এবং এর মধ্যে আপনার মিডিয়া সেশন তৈরি করুন।

MediaSessionService ব্যবহার করার ফলে Google Assistant, সিস্টেম মিডিয়া কন্ট্রোল, পেরিফেরাল ডিভাইসে থাকা মিডিয়া বোতাম বা Wear OS-এর মতো কম্প্যানিয়ন ডিভাইসের মতো এক্সটার্নাল ক্লায়েন্ট আপনার পরিষেবা খুঁজে পেতে, সেটির সাথে কানেক্ট করতে এবং প্লেব্যাক কন্ট্রোল করতে পারে, এর জন্য আপনার অ্যাপের UI অ্যাক্টিভিটি অ্যাক্সেস করার প্রয়োজন হয় না। আসলে, একই MediaSessionService-এর সাথে একই সময়ে একাধিক ক্লায়েন্ট অ্যাপ কানেক্ট করা থাকতে পারে, প্রতিটি অ্যাপের নিজস্ব MediaController থাকে।

পরিষেবা লাইফসাইকেল প্রয়োগ করা

আপনাকে পরিষেবার দুটি লাইফসাইকেল পদ্ধতি প্রয়োগ করতে হবে:

  • প্রথম কন্ট্রোলার কানেক্ট হতে চললে এবং সার্ভিস ইনস্ট্যানশিয়েট ও চালু হলে onCreate() কল করা হয়। Player ও MediaSession তৈরি করার জন্য এটিই সেরা জায়গা।
  • পরিষেবা বন্ধ করার সময় onDestroy() কল করা হয়। প্লেয়ার ও সেশন সহ সব রিসোর্স রিলিজ করতে হবে।

ব্যবহারকারী সাম্প্রতিক টাস্ক থেকে অ্যাপটি সরিয়ে দিলে কী হবে তা কাস্টমাইজ করতে, আপনি চাইলে onTaskRemoved(Intent) ওভাররাইড করতে পারেন। ডিফল্ট হিসেবে, প্লেব্যাক চলতে থাকলে পরিষেবা চালু থাকে এবং অন্যথায় বন্ধ হয়ে যায়।

Kotlin

class PlaybackService : MediaSessionService() {
  private var mediaSession: MediaSession? = null

  // Create your Player and MediaSession in the onCreate lifecycle event
  override fun onCreate() {
    super.onCreate()
    val player = ExoPlayer.Builder(this).build()
    mediaSession = MediaSession.Builder(this, player).build()
  }

  // Remember to release the player and media session in onDestroy
  override fun onDestroy() {
    mediaSession?.run {
      player.release()
      release()
      mediaSession = null
    }
    super.onDestroy()
  }

  override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? =
    mediaSession
}

জাভা

class PlaybackService extends MediaSessionService {
  private MediaSession mediaSession = null;

  // Create your Player and MediaSession in the onCreate lifecycle event
  @Override
  public void onCreate() {
    super.onCreate();
    ExoPlayer player = new ExoPlayer.Builder(this).build();
    mediaSession = new MediaSession.Builder(this, player).build();
  }

  // Remember to release the player and media session in onDestroy
  @Override
  public void onDestroy() {
    mediaSession.getPlayer().release();
    mediaSession.release();
    mediaSession = null;
    super.onDestroy();
  }

  @Override
  public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) {
    return mediaSession;
  }
}

ব্যাকগ্রাউন্ডে প্লেব্যাক চালিয়ে যাওয়ার বিকল্প হিসেবে, ব্যবহারকারী অ্যাপ বাতিল করে দিলে আপনি যেকোনও ক্ষেত্রে পরিষেবা বন্ধ করে দিতে পারেন:

Kotlin

@OptIn(UnstableApi::class)
override fun onTaskRemoved(rootIntent: Intent?) {
  pauseAllPlayersAndStopSelf()
}

জাভা

@OptIn(markerClass = UnstableApi.class)
@Override
public void onTaskRemoved(@Nullable Intent rootIntent) {
  pauseAllPlayersAndStopSelf();
}

onTaskRemoved-এর অন্য যেকোনও ম্যানুয়াল প্রয়োগের জন্য, প্লেব্যাক চালু আছে কিনা এবং ফোরগ্রাউন্ড পরিষেবা চালু হয়েছে কিনা তা চেক করতে আপনি isPlaybackOngoing() ব্যবহার করতে পারবেন।

মিডিয়া সেশনে অ্যাক্সেস দিন

পরিষেবা তৈরি করার সময় তৈরি হওয়া আপনার মিডিয়া সেশনে অন্যান্য ক্লায়েন্টদের অ্যাক্সেস দিতে onGetSession() পদ্ধতি ওভাররাইড করুন।

Kotlin

class PlaybackService : MediaSessionService() {

  // [...] lifecycle methods omitted

  override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? =
    mediaSession
}

জাভা

class PlaybackService extends MediaSessionService {

  // [...] lifecycle methods omitted

  @Override
  public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) {
    return mediaSession;
  }
}

ম্যানিফেস্টে পরিষেবা ঘোষণা করা

প্লেব্যাক ফোরগ্রাউন্ড পরিষেবা চালানোর জন্য কোনও অ্যাপের FOREGROUND_SERVICE ও FOREGROUND_SERVICE_MEDIA_PLAYBACK অনুমতি প্রয়োজন:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />

আপনাকে অবশ্যই ম্যানিফেস্টে Service ক্লাস ঘোষণা করতে হবে, যার মধ্যে MediaSessionService ইনটেন্ট ফিল্টার এবং foregroundServiceType থাকতে হবে, যার মধ্যে mediaPlayback অন্তর্ভুক্ত।

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaSessionService"/>
        <action android:name="android.media.browse.MediaBrowserService"/>
    </intent-filter>
</service>

MediaController ব্যবহার করে প্লেব্যাক কন্ট্রোল করা

আপনার প্লেয়ার UI থাকা অ্যাক্টিভিটি বা ফ্র্যাগমেন্টে, আপনি MediaController ব্যবহার করে UI ও মিডিয়া সেশনের মধ্যে লিঙ্ক স্থাপন করতে পারবেন। আপনার UI, সেশনের মধ্যে প্লেয়ারকে কমান্ড পাঠানোর জন্য মিডিয়া কন্ট্রোলার ব্যবহার করে। MediaController তৈরি করাMediaController ও ব্যবহার করা সম্পর্কে বিস্তারিত জানতে গাইড দেখুন।

MediaController কমান্ড ম্যানেজ করা

MediaSession কন্ট্রোলারের থেকে এর MediaSession.Callback-এর মাধ্যমে কমান্ড পায়। MediaSession ইনিশিয়ালাইজ করলে MediaSession.Callback-এর একটি ডিফল্ট ইমপ্লিমেন্টেশন তৈরি হয় যা MediaController-এর পাঠানো সব কমান্ড অটোমেটিক হ্যান্ডেল করে।

বিজ্ঞপ্তি

MediaSessionService অটোমেটিক আপনার জন্য একটি MediaNotification তৈরি করে যা বেশিরভাগ ক্ষেত্রে কাজ করা উচিত। ডিফল্ট হিসেবে, প্রকাশিত বিজ্ঞপ্তি হল একটি MediaStyle বিজ্ঞপ্তি যা আপনার মিডিয়া সেশন থেকে পাওয়া লেটেস্ট তথ্য দিয়ে আপডেট করা হয় এবং প্লেব্যাক কন্ট্রোল দেখায়। MediaNotification আপনার সেশন সম্পর্কে সচেতন এবং একই সেশনের সাথে কানেক্ট করা যেকোনও অ্যাপের প্লেব্যাক কন্ট্রোল করতে এটি ব্যবহার করা যেতে পারে।

যেমন, MediaSessionService ব্যবহার করা কোনও মিউজিক স্ট্রিমিং অ্যাপ একটি MediaNotification তৈরি করবে যা আপনার MediaSession কনফিগারেশনের উপর ভিত্তি করে প্লেব্যাক কন্ট্রোলের পাশাপাশি বর্তমানে চালানো মিডিয়া আইটেমের জন্য শিরোনাম, আর্টিস্ট ও অ্যালবাম আর্ট দেখাবে।

প্রয়োজনীয় মেটাডেটা মিডিয়াতে প্রদান করা যেতে পারে অথবা নিম্নলিখিত স্নিপেটের মতো মিডিয়া আইটেমের অংশ হিসেবে ঘোষণা করা যেতে পারে:

Kotlin

val mediaItem =
  MediaItem.Builder()
    .setMediaId("media-1")
    .setUri(mediaUri)
    .setMediaMetadata(
      MediaMetadata.Builder()
        .setArtist("David Bowie")
        .setTitle("Heroes")
        .setArtworkUri(artworkUri)
        .build()
    )
    .build()

mediaController.setMediaItem(mediaItem)
mediaController.prepare()
mediaController.play()

জাভা

MediaItem mediaItem =
    new MediaItem.Builder()
        .setMediaId("media-1")
        .setUri(mediaUri)
        .setMediaMetadata(
            new MediaMetadata.Builder()
                .setArtist("David Bowie")
                .setTitle("Heroes")
                .setArtworkUri(artworkUri)
                .build())
        .build();

mediaController.setMediaItem(mediaItem);
mediaController.prepare();
mediaController.play();

বিজ্ঞপ্তির লাইফ-সাইকেল

Player-এর প্লেলিস্টে MediaItem ইনস্ট্যান্স থাকলেই বিজ্ঞপ্তি তৈরি করা হয়।

Player ও MediaSession স্ট্যাটাসের ভিত্তিতে সব বিজ্ঞপ্তি আপডেট অটোমেটিক হয়।

ফোরগ্রাউন্ড পরিষেবা চলাকালীন বিজ্ঞপ্তি সরানো যাবে না। বিজ্ঞপ্তিটি অবিলম্বে সরাতে, আপনাকে অবশ্যই Player.release() কল করতে হবে অথবা Player.clearMediaItems() ব্যবহার করে প্লেলিস্ট পরিষ্কার করতে হবে।

প্লেয়ার ১০ মিনিটের বেশি সময় ধরে পজ, বন্ধ বা কাজ না করলে এবং ব্যবহারকারী আর কোনও ইন্টার‍্যাকশন না করলে, পরিষেবা অটোমেটিক ফোরগ্রাউন্ড পরিষেবা স্টেট থেকে বেরিয়ে আসে যাতে সিস্টেম এটিকে ধ্বংস করতে পারে। আপনি প্লেব্যাক আবার শুরু করার সুবিধা প্রয়োগ করতে পারেন যাতে কোনও ব্যবহারকারী সার্ভিস লাইফসাইকেল রিস্টার্ট করতে পারেন এবং পরে কোনও সময়ে প্লেব্যাক আবার শুরু করতে পারেন।

বিজ্ঞপ্তি কাস্টমাইজ করা

বর্তমানে চালানো হচ্ছে এমন আইটেমের মেটাডেটা MediaItem.MediaMetadata পরিবর্তন করে কাস্টমাইজ করা যেতে পারে। আপনি যদি কোনও আগে থেকে থাকা আইটেমের মেটাডেটা আপডেট করতে চান, তাহলে প্লেব্যাক বিনা বাধায় চালিয়ে গিয়ে মেটাডেটা আপডেট করতে Player.replaceMediaItem ব্যবহার করতে পারেন।

এছাড়াও, Android মিডিয়া কন্ট্রোলের জন্য কাস্টম মিডিয়া বোতাম সংক্রান্ত পছন্দ সেট করে, বিজ্ঞপ্তিতে দেখানো বোতামগুলির মধ্যে কিছু কাস্টমাইজ করতে পারবেন। Android মিডিয়া কন্ট্রোল কাস্টমাইজ করা সম্পর্কে আরও জানুন।

বিজ্ঞপ্তি আরও কাস্টমাইজ করতে, MediaNotification.Provider DefaultMediaNotificationProvider.Builder ব্যবহার করে অথবা প্রোভাইডার ইন্টারফেসের কাস্টম ইমপ্লিমেন্টেশন তৈরি করে তৈরি করুন। setMediaNotificationProvider-এর মাধ্যমে আপনার MediaSessionService-এ প্রদানকারীকে যোগ করুন।

প্লেব্যাক আবার চালু করা

MediaSessionService বন্ধ হয়ে যাওয়ার পরে এবং ডিভাইস রিবুট করা হলেও, ব্যবহারকারীকে প্লেব্যাক চালু করার সুবিধা দেওয়া যেতে পারে যাতে তিনি পরিষেবা রিস্টার্ট করতে পারেন এবং যেখানে ছেড়ে গেছিলেন সেখান থেকে প্লেব্যাক আবার শুরু করতে পারেন। ডিফল্ট হিসেবে, প্লেব্যাক আবার শুরু করার সুবিধা বন্ধ করা থাকে। এর অর্থ হল, আপনার পরিষেবা চালু না থাকলে ব্যবহারকারী প্লেব্যাক আবার শুরু করতে পারবেন না। এই ফিচারে অপ্ট-ইন করতে, আপনাকে একটি মিডিয়া বোতাম রিসিভার ঘোষণা করতে হবে এবং onPlaybackResumption পদ্ধতি প্রয়োগ করতে হবে।

Media3 মিডিয়া বোতাম রিসিভার ঘোষণা করুন

আপনার ম্যানিফেস্টে MediaButtonReceiver ঘোষণা করে শুরু করুন:

<receiver android:name="androidx.media3.session.MediaButtonReceiver"
  android:exported="true">
  <intent-filter>
    <action android:name="android.intent.action.MEDIA_BUTTON" />
  </intent-filter>
</receiver>

প্লেব্যাক আবার শুরু করার কলব্যাক প্রয়োগ করা

কোনও ব্লুটুথ ডিভাইস বা Android System UI রিজিউম ফিচার প্লেব্যাক আবার চালু করার অনুরোধ করলে, onPlaybackResumption() কলব্যাক পদ্ধতিকে কল করা হয়।

Kotlin

override fun onPlaybackResumption(
  mediaSession: MediaSession,
  controller: MediaSession.ControllerInfo,
  isForPlayback: Boolean,
): ListenableFuture<MediaSession.MediaItemsWithStartPosition> {
  val settableFuture = SettableFuture.create<MediaSession.MediaItemsWithStartPosition>()
  settableFuture.addListener(
    {
      // Your app is responsible for storing the playlist, metadata (like title
      // and artwork) of the current item and the start position to use here.
      val resumptionPlaylist = restorePlaylist()
      settableFuture.set(resumptionPlaylist)
    },
    MoreExecutors.directExecutor(),
  )
  return settableFuture
}

জাভা

@Override
public ListenableFuture<MediaItemsWithStartPosition> onPlaybackResumption(
    MediaSession mediaSession, ControllerInfo controller, boolean isForPlayback) {
  SettableFuture<MediaItemsWithStartPosition> settableFuture = SettableFuture.create();
  settableFuture.addListener(
      () -> {
        // Your app is responsible for storing the playlist, metadata (like title
        // and artwork) of the current item and the start position to use here.
        MediaItemsWithStartPosition resumptionPlaylist = restorePlaylist();
        settableFuture.set(resumptionPlaylist);
      },
      MoreExecutors.directExecutor());
  return settableFuture;
}

আপনি যদি প্লেব্যাক স্পিড, রিপিট মোড বা শাফেল মোডের মতো অন্যান্য প্যারামিটার সেভ করে থাকেন, তাহলে Media3 প্লেয়ার প্রস্তুত করার আগে এবং কলব্যাক সম্পূর্ণ হলে প্লেব্যাক শুরু করার আগে, onPlaybackResumption() হল এইসব প্যারামিটার দিয়ে প্লেয়ার কনফিগার করার একটি ভাল জায়গা।

ডিভাইস রিবুট করার পরে Android System UI রেজিউম বিজ্ঞপ্তি তৈরি করতে বুট করার সময় এই পদ্ধতি কল করা হয়, যেখানে isForPlayback-এর মান false হিসেবে সেট করা থাকে। রিচ বিজ্ঞপ্তির ক্ষেত্রে, MediaMetadata title এবং artworkData বা artworkUri-এর মতো ফিল্ডে স্থানীয়ভাবে উপলভ্য ভ্যালু পূরণ করার সাজেশন দেওয়া হয়, কারণ নেটওয়ার্ক অ্যাক্সেস এখনও উপলভ্য নাও থাকতে পারে। এছাড়াও, আপনি MediaMetadata.extras-এ MediaConstants.EXTRAS_KEY_COMPLETION_STATUS এবং MediaConstants.EXTRAS_KEY_COMPLETION_PERCENTAGE যোগ করতে পারেন যাতে প্লেব্যাক পজিশন আবার শুরু করা যায়।

উন্নত কন্ট্রোলার কনফিগারেশন ও ব্যাকওয়ার্ড কম্প্যাটিবিলিটি

প্লেব্যাক কন্ট্রোল করতে এবং প্লেলিস্ট দেখাতে অ্যাপ UI-তে MediaController ব্যবহার করা হল একটি সাধারণ দৃশ্য। একই সাথে, সেশনটি Android মিডিয়া কন্ট্রোল এবং মোবাইল বা টিভিতে Assistant-এর মতো এক্সটার্নাল ক্লায়েন্ট, ওয়াচের জন্য Wear OS এবং গাড়িতে Android Auto-র কাছে উপলভ্য থাকে। Media3 সেশন ডেমো অ্যাপ হল এমন একটি অ্যাপের উদাহরণ যা এই ধরনের পরিস্থিতি প্রয়োগ করে।

এইসব এক্সটার্নাল ক্লায়েন্ট, লিগ্যাসি AndroidX লাইব্রেরির MediaControllerCompat API বা Android প্ল্যাটফর্মের android.media.session.MediaController API ব্যবহার করতে পারে। Media3, পুরনো লাইব্রেরির সাথে সম্পূর্ণ ব্যাকওয়ার্ড মানানসই এবং Android প্ল্যাটফর্ম API-এর সাথে ইন্টারঅপারেবিলিটি প্রদান করে।

বিশ্বস্ত কন্ট্রোলার শনাক্ত করা

যেকোনও অ্যাপ আপনার মিডিয়া সেশন বা লাইব্রেরির সাথে কানেক্ট করার চেষ্টা করতে পারে। আপনি যদি সিস্টেম কন্ট্রোলার, মিডিয়া কন্টেন্ট কন্ট্রোল অনুমতি সহ কন্ট্রোলার এবং আপনার নিজের অ্যাপে অ্যাক্সেস সীমাবদ্ধ করতে চান, তাহলে আপনি একটি প্রাথমিক অ্যাক্সেস চেকের জন্য ControllerInfo.isTrusted() ব্যবহার করতে পারেন। অথবা, আপনি আরও নির্দিষ্ট কন্ট্রোলার শনাক্ত করতে পারেন যেমন, মিডিয়া বিজ্ঞপ্তি কন্ট্রোলার বা Android Auto কন্ট্রোলার, যা নিম্নলিখিত বিভাগে বর্ণনা করা হয়েছে।

মিডিয়া বিজ্ঞপ্তি কন্ট্রোলার ব্যবহার করা

এটি বোঝা গুরুত্বপূর্ণ যে এইসব লিগ্যাসি ও প্ল্যাটফর্ম কন্ট্রোলার একই স্টেট শেয়ার করে এবং কন্ট্রোলার (যেমন, উপলভ্য PlaybackState.getActions() ও PlaybackState.getCustomActions()) অনুযায়ী ভিজিবিলিটি কাস্টমাইজ করা যায় না। এইসব লিগ্যাসি ও প্ল্যাটফর্ম কন্ট্রোলারের সাথে কম্প্যাটিবিলিটির জন্য প্ল্যাটফর্ম মিডিয়া সেশনে সেট করা স্টেট কনফিগার করতে আপনি মিডিয়া বিজ্ঞপ্তি কন্ট্রোলার ব্যবহার করতে পারবেন।

যেমন, কোনও অ্যাপ উপলভ্য কমান্ড সেট করতে এবং MediaSession.Callback.onConnect() প্ল্যাটফর্ম সেশনের জন্য নির্দিষ্টভাবে মিডিয়া বোতামের পছন্দ সেট করতে নিম্নলিখিতভাবে প্রয়োগ করতে পারে:

Kotlin

override fun onConnectAsync(
  session: MediaSession,
  controller: MediaSession.ControllerInfo,
): ListenableFuture<ConnectionResult> {
  if (session.isMediaNotificationController(controller)) {
    val playerCommands =
      ConnectionResult.DEFAULT_PLAYER_COMMANDS.buildUpon()
        .remove(COMMAND_SEEK_TO_PREVIOUS)
        .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)
        .remove(COMMAND_SEEK_TO_NEXT)
        .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)
        .build()
    // Custom button preferences and commands to configure the platform session.
    return immediateFuture(
      AcceptedResultBuilder(session, controller)
        .setMediaButtonPreferences(listOf(seekBackButton, seekForwardButton))
        .setAvailablePlayerCommands(playerCommands)
        .build()
    )
  }
  // Default commands with default button preferences for all other controllers.
  return immediateFuture(AcceptedResultBuilder(session, controller).build())
}

জাভা

@Override
public ListenableFuture<ConnectionResult> onConnectAsync(
    MediaSession session, MediaSession.ControllerInfo controller) {
  if (session.isMediaNotificationController(controller)) {
    Player.Commands playerCommands =
        ConnectionResult.DEFAULT_PLAYER_COMMANDS
            .buildUpon()
            .remove(COMMAND_SEEK_TO_PREVIOUS)
            .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)
            .remove(COMMAND_SEEK_TO_NEXT)
            .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)
            .build();
    // Custom button preferences and commands to configure the platform session.
    return immediateFuture(
        new AcceptedResultBuilder(session, controller)
            .setMediaButtonPreferences(ImmutableList.of(seekBackButton, seekForwardButton))
            .setAvailablePlayerCommands(playerCommands)
            .build());
  }
  // Default commands with default button preferences for all other controllers.
  return immediateFuture(new AcceptedResultBuilder(session, controller).build());
}

কাস্টম কমান্ড পাঠানোর জন্য Android Auto-কে অনুমোদন দিন

MediaLibraryService ব্যবহার করার সময় এবং মোবাইল অ্যাপের সাথে Android Auto কাজ করানোর জন্য, Android Auto কন্ট্রোলারের উপযুক্ত উপলভ্য কমান্ড প্রয়োজন, অন্যথায় Media3 সেই কন্ট্রোলারের থেকে আসা কাস্টম কমান্ড প্রত্যাখ্যান করবে:

Kotlin

override fun onConnectAsync(
  session: MediaSession,
  controller: MediaSession.ControllerInfo,
): ListenableFuture<ConnectionResult> {
  val sessionCommands =
    ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon().add(customCommand).build()
  if (session.isMediaNotificationController(controller)) {
    // ... See above.
  } else if (session.isAutoCompanionController(controller)) {
    // Available commands to accept incoming custom commands from Auto.
    return immediateFuture(
      AcceptedResultBuilder(session, controller)
        .setAvailableSessionCommands(sessionCommands)
        .build()
    )
  }
  // Default commands for all other controllers.
  return immediateFuture(AcceptedResultBuilder(session, controller).build())
}

জাভা

@Override
public ListenableFuture<ConnectionResult> onConnectAsync(
    MediaSession session, MediaSession.ControllerInfo controller) {
  SessionCommands sessionCommands =
      ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon().add(customCommand).build();
  if (session.isMediaNotificationController(controller)) {
    // ... See above.
  } else if (session.isAutoCompanionController(controller)) {
    // Available commands to accept incoming custom commands from Auto.
    return immediateFuture(
        new AcceptedResultBuilder(session, controller)
            .setAvailableSessionCommands(sessionCommands)
            .build());
  }
  // Default commands for all other controllers.
  return immediateFuture(new AcceptedResultBuilder(session, controller).build());
}

সেশন ডেমো অ্যাপে একটি অটোমোটিভ মডিউল আছে, যা আলাদা APK প্রয়োজন এমন অটোমোটিভ OS-এর জন্য কাজ করে।