بدء استخدام CastPlayer

CastPlayer هو تنفيذ مشغّل Jetpack Media3 الذي يتيح التشغيل على الجهاز والبث إلى جهاز بعيد يتيح استخدام Google Cast. تسهّل حزمة CastPlayer إضافة وظيفة البث إلى تطبيقك وتوفّر ميزات غنية للتبديل بسلاسة بين التشغيل المحلي والتشغيل عن بُعد. يوضّح لك هذا الدليل كيفية دمج CastPlayer في تطبيق الوسائط.

لدمج Cast مع منصات أخرى، يُرجى الاطّلاع على حزمة تطوير البرامج (SDK) لتطبيق Cast.

الحصول على جهاز يتيح استخدام Google Cast

لاختبار "CastPlayer"، يجب أن يكون لديك جهاز يتيح استخدام Google Cast. وتشمل الخيارات Android TV وChromecast ومكبّرات الصوت الذكية والشاشات الذكية. تأكَّد من إعداد جهازك واتصاله بشبكة Wi-Fi نفسها التي يتصل بها جهازك الجوّال المخصّص للتطوير من أجل اكتشاف الأجهزة.

إضافة اعتماديات الإصدار

لبدء استخدام CastPlayer، أضِف تبعيات AndroidX Media3 وCastPlayer إلى ملف build.gradle الخاص بوحدة تطبيقك.

Kotlin

implementation("androidx.media3:media3-exoplayer:1.11.1")
implementation("androidx.media3:media3-ui:1.11.1")
implementation("androidx.media3:media3-session:1.11.1")
implementation("androidx.media3:media3-cast:1.11.1")

أنيق

implementation "androidx.media3:media3-exoplayer:1.11.1"
implementation "androidx.media3:media3-ui:1.11.1"
implementation "androidx.media3:media3-session:1.11.1"
implementation "androidx.media3:media3-cast:1.11.1"

ضبط CastPlayer

لضبط CastPlayer، عليك تعديل ملف AndroidManifest.xml باستخدام موفّر خيارات.

مقدّم خيارات

يتطلّب CastPlayer موفّر خيارات لضبط سلوكه. لإجراء إعداد أساسي، يمكنك استخدام DefaultCastOptionsProvider من خلال إضافته إلى ملف AndroidManifest.xml. يستخدم هذا الإعدادات التلقائية، بما في ذلك تطبيق المستقبل التلقائي.

<application>
  ...
  <meta-data
    android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
    android:value="androidx.media3.cast.DefaultCastOptionsProvider" />
  ...
</application>

لتخصيص الإعدادات، نفِّذ OptionsProvider المخصّص الخاص بك. يُرجى الاطّلاع على دليل CastOptions لمعرفة كيفية إجراء ذلك.

إضافة جهاز استقبال لعمليات نقل الوسائط

تتيح إضافة MediaTransferReceiver إلى ملف البيان لواجهة مستخدم النظام اكتشاف الأجهزة التي تتيح استخدام Google Cast على الشبكة وإعادة توجيه الوسائط بدون فتح نشاط التطبيق. على سبيل المثال، يمكن للمستخدم تغيير الجهاز الذي يشغّل الوسائط في تطبيقك من خلال إشعار الوسائط.

<application>
  ...
  <receiver android:name="androidx.mediarouter.media.MediaTransferReceiver" />
  ...
</application>

إنشاء CastPlayer

بالنسبة إلى التشغيل عن بُعد باستخدام Cast، يجب أن يتمكّن تطبيقك من إدارة التشغيل حتى عندما لا يتفاعل المستخدم مع نشاط من تطبيقك، مثل إشعار الوسائط في النظام. لهذا السبب، عليك إنشاء مثيلات ExoPlayer (للتشغيل على الجهاز) وCastPlayer (للتشغيل عن بُعد) في خدمة، مثل MediaSessionService أو MediaLibraryService. أولاً، أنشئ مثيل ExoPlayer، ثم عند إنشاء مثيل CastPlayer، اضبط ExoPlayer كمثيل المشغّل المحلي. يمكنك بعد ذلك التبديل بين تشغيل الوسائط على جهازك الجوّال والجهاز الذي يتيح استخدام Google Cast من إشعار الوسائط أو إشعار شاشة القفل. تستخدم Media3 ميزة أداة التبديل بين أجهزة التشغيل للتعامل مع عمليات نقل المشغّل عندما يتغيّر مسار الإخراج من محلي إلى عن بُعد أو من عن بُعد إلى محلي.

لقطة شاشة تعرض واجهة مستخدم &quot;أداة تبديل المخرجات&quot; في الإشعارات
الشكل 1: (أ) شريحة الجهاز في إشعار الوسائط (ب) الأجهزة التي تتيح استخدام Google Cast تظهر عند النقر على شريحة الجهاز (ج) شريحة الجهاز في إشعار شاشة القفل

Kotlin

override fun onCreate() {
  super.onCreate()

  val exoPlayer = ExoPlayer.Builder(context).build()
  val castPlayer = CastPlayer.Builder(context).setLocalPlayer(exoPlayer).build()

  mediaSession = MediaSession.Builder(context, castPlayer).build()
}

Java

@Override
public void onCreate() {
  super.onCreate();

  ExoPlayer exoPlayer = new ExoPlayer.Builder(context).build();
  CastPlayer castPlayer = new CastPlayer.Builder(context).setLocalPlayer(exoPlayer).build();

  mediaSession =
      new MediaSession.Builder(/* context= */ context, /* player= */ castPlayer).build();
}

إضافة عناصر في واجهة المستخدم

أضِف MediaRouteButton إلى واجهة مستخدم تطبيقك. يؤدي النقر على MediaRouteButton إلى فتح مربّع حوار يعرض قائمة بالأجهزة المتاحة التي تتيح استخدام Google Cast على الشبكة. عندما يختار المستخدم جهازًا، يتم نقل تشغيل الوسائط من الجهاز الجوّال إلى جهاز الاستقبال المحدّد. يوضّح لك هذا القسم كيفية إضافة الزر والاستماع إلى الأحداث لتعديل واجهة المستخدم عند تبديل التشغيل بين الأجهزة المحلية والأجهزة البعيدة.

ضبط MediaRouteButton

هناك أربع طرق لإضافة MediaRouteButton إلى واجهة المستخدم الخاصة بنشاطك. يعتمد الخيار الأفضل على تصميم تطبيقك ومتطلباته.

  • واجهة مستخدم Compose: أضِف دالة مركّبة للزر.
  • واجهة مستخدم "المشاهدات":
    • أضِف الزر إلى قائمة شريط التطبيق.
    • أضِف الزر داخل PlayerView.
    • أضِف الزر كعنصر View عادي.
لقطة شاشة تعرض MediaRouteButton في واجهة المستخدم
الشكل 2: (أ) MediaRouteButton في شريط القوائم، (ب) كطريقة عرض، (ج) في PlayerView، و (د) مربّع حوار للأجهزة التي تتيح استخدام Google Cast.

إضافة دالة مركّبة MediaRouteButton إلى المشغّل

يمكنك إضافة الدالة المركّبة MediaRouteButton إلى واجهة مستخدم المشغّل. لمزيد من المعلومات، يُرجى الاطّلاع على دليل Compose.

@Composable
fun PlayerComposeView(player: Player, modifier: Modifier = Modifier) {
  var controlsVisible by remember { mutableStateOf(false) }

  Box(
    modifier = modifier.clickable { controlsVisible = true },
    contentAlignment = Alignment.Center,
  ) {
    PlayerSurface(player = player, modifier = modifier)
    AnimatedVisibility(visible = controlsVisible, enter = fadeIn(), exit = fadeOut()) {
      Box(modifier = Modifier.fillMaxSize()) {
        MediaRouteButton(modifier = Modifier.align(Alignment.TopEnd))
        PrimaryControls(player = player, modifier = Modifier.align(Alignment.Center))
      }
    }
  }
}

@Composable
fun PrimaryControls(player: Player, modifier: Modifier = Modifier) {
  // ...
}

إضافة MediaRouteButton إلى PlayerView

يمكنك إضافة MediaRouteButton مباشرةً ضمن عناصر التحكّم في واجهة المستخدم في PlayerView. بعد ضبط MediaController كمشغّل لـ PlayerView، قدِّم MediaRouteButtonViewProvider لعرض زر البث على المشغّل.

Kotlin

override fun onStart() {
  super.onStart()

  playerView.player = mediaController
  playerView.setMediaRouteButtonViewProvider(MediaRouteButtonViewProvider())
}

Java

@Override
public void onStart() {
  super.onStart();

  playerView.setPlayer(mediaController);
  playerView.setMediaRouteButtonViewProvider(new MediaRouteButtonViewProvider());
}

إضافة MediaRouteButton إلى قائمة شريط التطبيقات

لإعداد MediaRouteButton في قائمة شريط التطبيقات، أنشِئ قائمة XML وتجاوز onCreateOptionsMenu في Activity.

<menu xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:app="http://schemas.android.com/apk/res-auto">
  <item android:id="@+id/media_route_menu_item"
    android:title="@string/media_route_menu_title"
    app:showAsAction="always"
    app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"/>
</menu>

Kotlin

override fun onCreateOptionsMenu(menu: Menu): Boolean {
  // ...
  menuInflater.inflate(R.menu.sample_media_route_button_menu, menu)
  val menuItemFuture: ListenableFuture<MenuItem> =
    MediaRouteButtonFactory.setUpMediaRouteButton(context, menu, R.id.media_route_menu_item)
  Futures.addCallback(
    menuItemFuture,
    object : FutureCallback<MenuItem> {
      override fun onSuccess(menuItem: MenuItem) {
        // Do something with the menu item.
      }

      override fun onFailure(t: Throwable) {
        // Handle the failure.
      }
    },
    executor,
  )
  // ...
  return true
}

Java

@Override
public boolean onCreateOptionsMenu(Menu menu) {
  // ...
  getMenuInflater().inflate(R.menu.sample_media_route_button_menu, menu);
  ListenableFuture<MenuItem> menuItemFuture =
      MediaRouteButtonFactory.setUpMediaRouteButton(context, menu, R.id.media_route_menu_item);
  Futures.addCallback(
      menuItemFuture,
      new FutureCallback<MenuItem>() {
        @Override
        public void onSuccess(MenuItem menuItem) {
          // Do something with the menu item.
        }

        @Override
        public void onFailure(Throwable t) {
          // Handle the failure.
        }
      },
      executor);
  // ...
  return true;
}

إضافة MediaRouteButton كطريقة عرض

يمكنك إعداد MediaRouteButton في ملف activity layout.xml.

  <androidx.mediarouter.app.MediaRouteButton
      android:id="@+id/media_route_button"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      app:mediaRouteButtonTint="@android:color/white" />

لإكمال عملية إعداد MediaRouteButton، استخدِم Media3 Cast MediaRouteButtonFactory في رمز Activity.

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)

  findViewById<MediaRouteButton>(R.id.media_route_button)?.also {
    val unused = MediaRouteButtonFactory.setUpMediaRouteButton(context, it)
  }
}

Java

@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  // ...
  MediaRouteButton button = findViewById(R.id.media_route_button);
  ListenableFuture<Void> setUpFuture =
      MediaRouteButtonFactory.setUpMediaRouteButton(context, button);
}

أداة معالجة النشاط

أنشئ Player.Listener في Activity للاستماع إلى التغييرات في موقع تشغيل الوسائط. عندما يتغيّر playbackType بين PLAYBACK_TYPE_LOCAL وPLAYBACK_TYPE_REMOTE، يمكنك تعديل واجهة المستخدم حسب الحاجة. لمنع تسرُّب الذاكرة وحصر نشاط المستمع في الحالات التي يكون فيها تطبيقك مرئيًا فقط، يمكنك تسجيل المستمع في onStart وإلغاء تسجيله في onStop:

Kotlin

private val playerListener: Player.Listener =
  object : Player.Listener {
    override fun onDeviceInfoChanged(deviceInfo: DeviceInfo) {
      if (deviceInfo.playbackType == DeviceInfo.PLAYBACK_TYPE_LOCAL) {
        // Add UI changes for local playback.
      } else if (deviceInfo.playbackType == DeviceInfo.PLAYBACK_TYPE_REMOTE) {
        // Add UI changes for remote playback.
      }
    }
  }

override fun onStart() {
  super.onStart()
  mediaController.addListener(playerListener)
}

override fun onStop() {
  super.onStop()
  mediaController.removeListener(playerListener)
}

Java

private final Player.Listener playerListener =
    new Player.Listener() {
      @Override
      public void onDeviceInfoChanged(DeviceInfo deviceInfo) {
        if (deviceInfo.playbackType == DeviceInfo.PLAYBACK_TYPE_LOCAL) {
          // Add UI changes for local playback.
        } else if (deviceInfo.playbackType == DeviceInfo.PLAYBACK_TYPE_REMOTE) {
          // Add UI changes for remote playback.
        }
      }
    };

@Override
protected void onStart() {
  super.onStart();
  mediaController.addListener(playerListener);
}

@Override
protected void onStop() {
  super.onStop();
  mediaController.removeListener(playerListener);
}

لمزيد من المعلومات حول الاستماع إلى أحداث التشغيل والاستجابة لها، يُرجى الاطّلاع على دليل أحداث المشغّل.