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

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

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

יצירת CastPlayer

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

הוספת Composable MediaRouteButton לנגן

אתם יכולים להוסיף את ה-Composable‏ 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 כדי להציג את הכפתור להפעלת 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 בקובץ 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);
}

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);
}

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