תחילת העבודה עם CastPlayer

CastPlayer הוא הטמעה של Player ב-Jetpack Media3 שתומכת בהפעלה מקומית ובהעברה למכשיר מרוחק שתומך ב-Cast. CastPlayerהספרייה הזו מפשטת את הוספת הפונקציונליות של Cast לאפליקציה ומספקת תכונות עשירות שמאפשרות מעבר חלק בין הפעלה מקומית להפעלה מרחוק. במדריך הזה מוסבר איך לשלב את CastPlayer באפליקציית המדיה שלכם.

כדי לשלב את Cast עם פלטפורמות אחרות, אפשר לעיין ב-Cast SDK.

קניית מכשיר שתומך ב-Cast

כדי לבדוק את CastPlayer, צריך מכשיר תומך ב-Cast. האפשרויות כוללות Android TV,‏ Chromecast, רמקולים חכמים ומסכים חכמים. מוודאים שהמכשיר מוגדר ומחובר לאותה רשת Wi-Fi שאליה מחובר הנייד שלכם לפיתוח, כדי שהמערכת תוכל לזהות אותו.

הוספת יחסי תלות ב-build

כדי להתחיל להשתמש ב-CastPlayer, מוסיפים את התלות של AndroidX Media3 ושל CastPlayer לקובץ build.gradle של מודול האפליקציה.

Kotlin

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

Groovy

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

הגדרת 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 למניפסט מאפשרת לממשק המשתמש של המערכת לגלות מכשירים תומכי Cast ברשת ולנתב מחדש מדיה בלי לפתוח את פעילות האפליקציה. לדוגמה, משתמש יכול לשנות את המכשיר שבו מוצג המדיה של האפליקציה שלכם מההתראה על המדיה.

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

יצירת CastPlayer

כדי להפעיל הפעלה מרחוק באמצעות Cast, האפליקציה צריכה להיות מסוגלת לנהל את ההפעלה גם כשהמשתמש לא מקיים אינטראקציה עם פעילות מהאפליקציה, למשל באמצעות ההתראה על מדיה במערכת. לכן, כדאי ליצור את המופעים של ExoPlayer (להפעלה מקומית) ושל CastPlayer (להפעלה מרחוק) בשירות כמו MediaSessionService או MediaLibraryService. קודם יוצרים מופע של ExoPlayer, ואז כשמפתחים מופע של CastPlayer, מגדירים את ExoPlayer כמופע של הנגן המקומי. אחרי מכן תוכלו להעביר את הפעלת המדיה בין הנייד לבין המכשיר תומך ב-Cast דרך התראת המדיה או התראות מסך הנעילה. ‫Media3 משתמש בתכונה Output Switcher כדי לטפל בהעברות של נגנים כשנתיב הפלט משתנה ממקומי למרוחק או ממרוחק למקומי.

צילום מסך שבו מוצג ממשק המשתמש של הכלי להחלפת פלט בהתראות.
תמונה 1: (א) צ'יפ של מכשיר בהתראה על מדיה (ב) מכשירים תומכי 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 פותחת תיבת דו-שיח עם רשימה של מכשירים שתומכים ב-Cast ברשת. כשהמשתמש בוחר מכשיר, הפעלת המדיה מועברת מהנייד למכשיר המקלט שנבחר. בסעיף הזה מוסבר איך להוסיף את הלחצן ולהאזין לאירועים כדי לעדכן את ממשק המשתמש כשמעבירים את ההפעלה בין מכשירים מקומיים ומרוחקים.

הגדרת MediaRouteButton

יש ארבע דרכים להוסיף את MediaRouteButton לממשק המשתמש של הפעילות. הבחירה הטובה ביותר תלויה בעיצוב ובדרישות של האפליקציה.

  • Compose UI: מוסיפים קוד קומפזבילי של כפתור.
  • ממשק המשתמש של התצוגות:
    • מוסיפים את הלחצן לתפריט של סרגל האפליקציות.
    • מוסיפים את הלחצן בתוך PlayerView.
    • מוסיפים את הכפתור כView.
צילום מסך שבו מוצג הלחצן MediaRouteButton בממשק המשתמש.
איור 2: (א) MediaRouteButton בסרגל התפריטים, (ב) כ-View,‏ (ג) ב-PlayerView, ‏ (ד) תיבת דו-שיח של מכשירים עם תמיכה ב-Cast.

הוספת רכיב Composable MediaRouteButton לנגן

אפשר להוסיף את רכיב ה-Composable‏ MediaRouteButton לממשק המשתמש של הנגן. מידע נוסף זמין במדריך בנושא כתיבת הודעות.

@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 כדי להציג את לחצן ה-Cast בנגן.

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 בפריסת הפעילות שלכם ב-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);
}

Activity Listener

יוצרים 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);
}

מידע נוסף על האזנה לאירועי הפעלה ועל תגובה להם זמין במדריך בנושא אירועים של נגן.