Cast'i Android Uygulamanıza Entegre Edin

Bu geliştirici kılavuzunda, Android Sender SDK'yı kullanarak Android gönderen uygulamanıza Google Cast desteğini nasıl ekleyeceğiniz açıklanmaktadır.

Mobil cihaz veya dizüstü bilgisayar, oynatmayı kontrol eden gönderen, Google Cast cihazı ise TV'de içeriği gösteren Alıcı'dır.

Gönderen çerçevesi, Cast sınıf kitaplığı ikili programını ve gönderenin çalışma zamanında mevcut olan ilişkili kaynakları ifade eder. Gönderen uygulaması veya Cast uygulaması, gönderen üzerinde de çalışan bir uygulamayı belirtir. Web Alıcı uygulaması, Cast uyumlu cihazda çalışan HTML uygulamasını belirtir.

Gönderen çerçevesi, gönderen uygulamasına etkinlikler hakkında bilgi vermek ve Cast uygulaması yaşam döngüsünün çeşitli durumları arasında geçiş yapmak için eşzamansız bir geri çağırma tasarımı kullanır.

Uygulama akışı

Aşağıdaki adımlarda, gönderen bir Android uygulaması için tipik üst düzey yürütme akışı açıklanmaktadır:

  • Cast çerçevesi, Activity yaşam döngüsüne göre MediaRouter cihaz keşfini otomatik olarak başlatır.
  • Kullanıcı Yayınla düğmesini tıkladığında çerçeve, Yayınla iletişim kutusunu, bulunan Yayın cihazlarının listesini gösterir.
  • Kullanıcı bir Yayın cihazı seçtiğinde çerçeve, Yayın cihazında Web Alıcı uygulamasını başlatmaya çalışır.
  • Çerçeve, Web Alıcı uygulamasının başlatıldığını onaylamak için gönderen uygulamasında geri çağırmaları çağırır.
  • Çerçeve, gönderen ile Web Alıcı uygulamaları arasında bir iletişim kanalı oluşturur.
  • Çerçeve, Web Alıcısında medya oynatmayı yüklemek ve kontrol etmek için iletişim kanalını kullanır.
  • Çerçeve, medya oynatma durumunu gönderen ve Web Alıcı arasında senkronize eder: Kullanıcı, gönderenin kullanıcı arayüzü işlemleri yaptığında çerçeve bu medya kontrol isteklerini Web Alıcısı'na iletir. Web Alıcısı medya durumu güncellemeleri gönderdiğinde ise çerçeve, gönderen kullanıcı arayüzünün durumunu günceller.
  • Kullanıcı, yayın cihazı bağlantısını kesmek için Yayın düğmesini tıkladığında çerçeve, gönderen uygulamasının Web Alıcısı ile bağlantısını keser.

Google Cast Android SDK'sındaki tüm sınıfların, yöntemlerin ve etkinliklerin kapsamlı bir listesi için Android için Google Cast Sender API Referansı'na bakın. Aşağıdaki bölümlerde, Android uygulamanıza Cast'i eklemeniz için uygulanacak adımlar ele alınmaktadır.

Android manifestini yapılandırma

Uygulamanızın AndroidManifest.xml dosyası, Cast SDK'sı için aşağıdaki öğeleri yapılandırmanız gerektirir:

uses-sdk

Cast SDK'sının desteklediği minimum ve hedef Android API düzeylerini belirleyin. Şu anda minimum değer API düzeyi 23, hedef ise API düzeyi 34'tür.

<uses-sdk
        android:minSdkVersion="23"
        android:targetSdkVersion="34" />

android:theme

Uygulamanızın temasını minimum Android SDK sürümüne göre ayarlayın. Örneğin, kendi temanızı uygulamıyorsanız minimum Android SDK sürümünü Lollipop öncesi bir Android SDK sürümünü hedeflerken Theme.AppCompat varyantını kullanmalısınız.

<application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.AppCompat" >
       ...
</application>

Yayınlama Bağlamını Başlat

Çerçeve, çerçevenin tüm etkileşimlerini koordine eden CastContext adlı global bir tekil nesneye sahiptir.

Uygulamanız, CastContext single'ını başlatmak için gereken seçenekleri sağlamak üzere OptionsProvider arayüzünü uygulamalıdır. OptionsProvider, çerçevenin davranışını etkileyen seçeneklerin bulunduğu CastOptions örneğini sunar. Bunlardan en önemlisi, keşif sonuçlarını filtrelemek ve bir Yayınlama oturumu başlatıldığında Web Alıcı uygulamasını başlatmak için kullanılan Web Alıcısı uygulama kimliğidir.

Kotlin
class CastOptionsProvider : OptionsProvider {
    override fun getCastOptions(context: Context): CastOptions {
        return Builder()
            .setReceiverApplicationId(context.getString(R.string.app_id))
            .build()
    }

    override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? {
        return null
    }
}
Java
public class CastOptionsProvider implements OptionsProvider {
    @Override
    public CastOptions getCastOptions(Context context) {
        CastOptions castOptions = new CastOptions.Builder()
            .setReceiverApplicationId(context.getString(R.string.app_id))
            .build();
        return castOptions;
    }
    @Override
    public List<SessionProvider> getAdditionalSessionProviders(Context context) {
        return null;
    }
}

Uygulanan OptionsProvider öğesinin tam adını, gönderen uygulamasının AndroidManifest.xml dosyasında meta veri alanı olarak beyan etmeniz gerekir:

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

CastContext.getSharedInstance() çağrıldığında CastContext geç başlatılır.

Kotlin
class MyActivity : FragmentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        val castContext = CastContext.getSharedInstance(this)
    }
}
Java
public class MyActivity extends FragmentActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        CastContext castContext = CastContext.getSharedInstance(this);
    }
}

Cast UX Widget'ları

Cast çerçevesi, Yayın Tasarımı Kontrol Listesi'ne uygun widget'ları sağlar:

  • Tanıtım Yer Paylaşımı: Çerçeve, bir alıcı ilk kez kullanılabilir olduğunda Yayınla düğmesine dikkat çekmesi için kullanıcıya gösterilen özel bir Görünüm IntroductoryOverlay sağlar. Sender uygulaması, metni ve başlık metninin konumunu özelleştirebilir.

  • Yayınla Düğmesi: Yayınla düğmesi, yayın cihazlarının kullanılabilirliğinden bağımsız olarak görünür. Kullanıcı Yayınla düğmesini ilk kez tıkladığında, bulunan cihazların listelendiği bir Yayınla iletişim kutusu gösterilir. Kullanıcı, cihaz bağlıyken Yayın düğmesini tıkladığında mevcut medya meta verilerini (başlık, kayıt stüdyosunun adı ve küçük resim gibi) gösterir veya kullanıcının Yayın cihazıyla olan bağlantısını kesmesine olanak tanır. "Yayınla düğmesi" bazen "Yayınla simgesi" olarak da adlandırılır.

  • Mini Kumanda: Kullanıcı içerik yayınlarken mevcut içerik sayfasından veya genişletilmiş denetleyiciden gönderen uygulamasında başka bir ekrana geçerse kullanıcının o an yayınlanan medya meta verilerini görmesine ve oynatmayı kontrol etmesine olanak tanımak için ekranın alt kısmında mini kumanda görüntülenir.

  • Genişletilmiş Denetleyici: Kullanıcı içerik yayınlarken medya bildirimini veya mini denetleyiciyi tıkladığında genişletilmiş kumanda devreye girer. Bu durumda, şu anda oynatılan medya meta verileri gösterilir ve medya oynatmayı kontrol etmek için birkaç düğme sağlanır.

  • Bildirim: Yalnızca Android. Kullanıcı içerik yayınlarken gönderen uygulamasından ayrıldığında, o anda yayınlanan medya meta verilerini ve oynatma kontrollerini gösteren bir medya bildirimi görüntülenir.

  • Kilit Ekranı: Yalnızca Android. Kullanıcı içerik yayınlarken ve kilit ekranına gittiğinde (veya cihaz zaman aşımına uğradığında) o anda yayınlanan medya meta verilerini ve oynatma kontrollerini gösteren bir medya kilit ekranı kontrolü görüntülenir.

Aşağıdaki kılavuzda, bu widget'ları uygulamanıza nasıl ekleyeceğinize dair açıklamalar yer almaktadır.

Yayınla Düğmesi Ekle

Android MediaRouter API'leri, ikincil cihazlarda medya görüntüleme ve oynatma için tasarlanmıştır. MediaRouter API kullanan Android uygulamaları, kullanıcıların yayın cihazı gibi ikincil bir cihazda medya oynatmak üzere bir medya yolu seçmelerine olanak tanımak için kullanıcı arayüzünün parçası olarak bir Yayınla düğmesi içermelidir.

Çerçeve, bir MediaRouteButton öğesinin Cast button olarak eklenmesini çok kolay hale getirir. Önce menünüzü tanımlayan xml dosyasına bir menü öğesi veya MediaRouteButton eklemeniz ve çerçeve ile bağlamak için CastButtonFactory kullanmanız gerekir.

// To add a Cast button, add the following snippet.
// menu.xml
<item
    android:id="@+id/media_route_menu_item"
    android:title="@string/media_route_menu_title"
    app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
    app:showAsAction="always" />
Kotlin
// Then override the onCreateOptionMenu() for each of your activities.
// MyActivity.kt
override fun onCreateOptionsMenu(menu: Menu): Boolean {
    super.onCreateOptionsMenu(menu)
    menuInflater.inflate(R.menu.main, menu)
    CastButtonFactory.setUpMediaRouteButton(
        applicationContext,
        menu,
        R.id.media_route_menu_item
    )
    return true
}
Java
// Then override the onCreateOptionMenu() for each of your activities.
// MyActivity.java
@Override public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    getMenuInflater().inflate(R.menu.main, menu);
    CastButtonFactory.setUpMediaRouteButton(getApplicationContext(),
                                            menu,
                                            R.id.media_route_menu_item);
    return true;
}

Ardından, Activity öğesi FragmentActivity öğesinden devralıyorsa düzeninize MediaRouteButton ekleyebilirsiniz.

// activity_layout.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:gravity="center_vertical"
   android:orientation="horizontal" >

   <androidx.mediarouter.app.MediaRouteButton
       android:id="@+id/media_route_button"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_weight="1"
       android:mediaRouteTypes="user"
       android:visibility="gone" />

</LinearLayout>
Kotlin
// MyActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_layout)

    mMediaRouteButton = findViewById<View>(R.id.media_route_button) as MediaRouteButton
    CastButtonFactory.setUpMediaRouteButton(applicationContext, mMediaRouteButton)

    mCastContext = CastContext.getSharedInstance(this)
}
Java
// MyActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_layout);

   mMediaRouteButton = (MediaRouteButton) findViewById(R.id.media_route_button);
   CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), mMediaRouteButton);

   mCastContext = CastContext.getSharedInstance(this);
}

Tema kullanarak Yayınla düğmesinin görünümünü ayarlamak için Yayınla Düğmesini Özelleştirme konusuna bakın.

Cihaz bulmayı yapılandırın

Cihaz bulma tamamen CastContext tarafından yönetilir. CastContext başlatılırken gönderen uygulama, Web Alıcısı uygulama kimliğini belirtir ve isteğe bağlı olarak CastOptions içinde supportedNamespaces ayarını yaparak ad alanı filtrelemesi isteyebilir. CastContext, dahili olarak MediaRouter öğesine referans verir ve keşif sürecini aşağıdaki koşullar altında başlatır:

  • Cihazın keşif gecikmesi ile pil kullanımını dengelemek için tasarlanmış bir algoritmaya göre, gönderen uygulaması ön plana girdiğinde keşif zaman zaman otomatik olarak başlatılır.
  • Yayınla iletişim kutusu açık.
  • Cast SDK'sı, Cast oturumunu kurtarmaya çalışıyor.

Yayınlama iletişim kutusu kapatıldığında veya gönderen uygulama arka plana girdiğinde keşif işlemi durdurulur.

Kotlin
class CastOptionsProvider : OptionsProvider {
    companion object {
        const val CUSTOM_NAMESPACE = "urn:x-cast:custom_namespace"
    }

    override fun getCastOptions(appContext: Context): CastOptions {
        val supportedNamespaces: MutableList<String> = ArrayList()
        supportedNamespaces.add(CUSTOM_NAMESPACE)

        return CastOptions.Builder()
            .setReceiverApplicationId(context.getString(R.string.app_id))
            .setSupportedNamespaces(supportedNamespaces)
            .build()
    }

    override fun getAdditionalSessionProviders(context: Context): List<SessionProvider>? {
        return null
    }
}
Java
class CastOptionsProvider implements OptionsProvider {
    public static final String CUSTOM_NAMESPACE = "urn:x-cast:custom_namespace";

    @Override
    public CastOptions getCastOptions(Context appContext) {
        List<String> supportedNamespaces = new ArrayList<>();
        supportedNamespaces.add(CUSTOM_NAMESPACE);

        CastOptions castOptions = new CastOptions.Builder()
            .setReceiverApplicationId(context.getString(R.string.app_id))
            .setSupportedNamespaces(supportedNamespaces)
            .build();
        return castOptions;
    }

    @Override
    public List<SessionProvider> getAdditionalSessionProviders(Context context) {
        return null;
    }
}

Oturum yönetiminin işleyiş şekli

Cast SDK'sı, bir cihaza bağlanma, bir Web Alıcısı uygulamasını başlatma (veya katılma) adımlarını birleştiren, bu uygulamaya bağlanma ve medya kontrol kanalını başlatma adımlarını birleştiren Cast oturumu kavramını tanıtır. Yayınlama oturumları ve Web Alıcısı yaşam döngüsü hakkında daha fazla bilgi için Web Alıcısı Uygulama yaşam döngüsü kılavuzuna bakın.

Oturumlar, uygulamanızın CastContext.getSessionManager() üzerinden erişebileceği SessionManager sınıfı tarafından yönetilir. Bağımsız oturumlar, Session sınıfının alt sınıflarıyla temsil edilir. Örneğin, CastSession, yayın cihazları olan oturumları temsil eder. Uygulamanız şu anda etkin olan Cast oturumuna SessionManager.getCurrentCastSession() üzerinden erişebilir.

Uygulamanız; oluşturma, askıya alma, devam ettirme ve sonlandırma gibi oturum etkinliklerini izlemek için SessionManagerListener sınıfını kullanabilir. Çerçeve, bir oturum etkin durumdayken otomatik olarak anormal/ani bir sonlandırma durumundan devam etmeye çalışır.

Oturumlar, MediaRouter iletişim kutularındaki kullanıcı hareketlerine göre otomatik olarak oluşturulur ve bölünür.

Uygulamalar, yayınlama başlatma hatalarını daha iyi anlamak için CastContext#getCastReasonCodeForCastStatusCode(int) kullanarak oturum başlatma hatasını CastReasonCodes olarak dönüştürebilir. Bazı oturum başlatma hatalarının (ör. CastReasonCodes#CAST_CANCELLED) amaçlanan davranış olduğunu ve hata olarak günlüğe kaydedilmemesi gerektiğini unutmayın.

Oturumla ilgili durum değişikliklerini bilmeniz gerekiyorsa bir SessionManagerListener uygulayabilirsiniz. Bu örnekte, Activity içindeki bir CastSession kullanılabilirliği dinlenmektedir.

Kotlin
class MyActivity : Activity() {
    private var mCastSession: CastSession? = null
    private lateinit var mCastContext: CastContext
    private lateinit var mSessionManager: SessionManager
    private val mSessionManagerListener: SessionManagerListener<CastSession> =
        SessionManagerListenerImpl()

    private inner class SessionManagerListenerImpl : SessionManagerListener<CastSession?> {
        override fun onSessionStarting(session: CastSession?) {}

        override fun onSessionStarted(session: CastSession?, sessionId: String) {
            invalidateOptionsMenu()
        }

        override fun onSessionStartFailed(session: CastSession?, error: Int) {
            val castReasonCode = mCastContext.getCastReasonCodeForCastStatusCode(error)
            // Handle error
        }

        override fun onSessionSuspended(session: CastSession?, reason Int) {}

        override fun onSessionResuming(session: CastSession?, sessionId: String) {}

        override fun onSessionResumed(session: CastSession?, wasSuspended: Boolean) {
            invalidateOptionsMenu()
        }

        override fun onSessionResumeFailed(session: CastSession?, error: Int) {}

        override fun onSessionEnding(session: CastSession?) {}

        override fun onSessionEnded(session: CastSession?, error: Int) {
            finish()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mCastContext = CastContext.getSharedInstance(this)
        mSessionManager = mCastContext.sessionManager
    }

    override fun onResume() {
        super.onResume()
        mCastSession = mSessionManager.currentCastSession
        mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession::class.java)
    }

    override fun onPause() {
        super.onPause()
        mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession::class.java)
        mCastSession = null
    }
}
Java
public class MyActivity extends Activity {
    private CastContext mCastContext;
    private CastSession mCastSession;
    private SessionManager mSessionManager;
    private SessionManagerListener<CastSession> mSessionManagerListener =
            new SessionManagerListenerImpl();

    private class SessionManagerListenerImpl implements SessionManagerListener<CastSession> {
        @Override
        public void onSessionStarting(CastSession session) {}
        @Override
        public void onSessionStarted(CastSession session, String sessionId) {
            invalidateOptionsMenu();
        }
        @Override
        public void onSessionStartFailed(CastSession session, int error) {
            int castReasonCode = mCastContext.getCastReasonCodeForCastStatusCode(error);
            // Handle error
        }
        @Override
        public void onSessionSuspended(CastSession session, int reason) {}
        @Override
        public void onSessionResuming(CastSession session, String sessionId) {}
        @Override
        public void onSessionResumed(CastSession session, boolean wasSuspended) {
            invalidateOptionsMenu();
        }
        @Override
        public void onSessionResumeFailed(CastSession session, int error) {}
        @Override
        public void onSessionEnding(CastSession session) {}
        @Override
        public void onSessionEnded(CastSession session, int error) {
            finish();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mCastContext = CastContext.getSharedInstance(this);
        mSessionManager = mCastContext.getSessionManager();
    }
    @Override
    protected void onResume() {
        super.onResume();
        mCastSession = mSessionManager.getCurrentCastSession();
        mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession.class);
    }
    @Override
    protected void onPause() {
        super.onPause();
        mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession.class);
        mCastSession = null;
    }
}

Akış aktarma

Akış aktarımının temeli oturum durumunu korumaktır. Kullanıcılar burada sesli komutları, Google Home uygulamasını veya akıllı ekranları kullanarak mevcut ses ve video akışlarını cihazlar arasında taşıyabilir. Medya, bir cihazda (kaynak) durdurulur ve başka bir cihazda (hedef) devam eder. En yeni donanım yazılımına sahip yayın cihazları, akış aktarımında kaynak veya hedef olarak kullanılabilir.

Akış aktarımı veya genişletme sırasında yeni hedef cihazı almak için CastSession#addCastListener kullanarak bir Cast.Listener kaydedin. Ardından, onDeviceNameChanged geri araması sırasında CastSession#getCastDevice() numaralı telefonu arayın.

Daha fazla bilgi için Web Alıcısında akış aktarımı bölümüne bakın.

Otomatik yeniden bağlanma

Çerçeve, aşağıdakiler gibi birçok küçük köşe durumunda yeniden bağlantıyı işlemek için gönderen uygulaması tarafından etkinleştirilebilen bir ReconnectionService sağlar:

  • Geçici kablosuz ağ kaybından kurtulma
  • Cihazı uyku modundan kurtar
  • Uygulamayı arka plana alma durumundan kurtar
  • Uygulama kilitlenirse kurtar

Bu hizmet varsayılan olarak etkindir ve CastOptions.Builder'te devre dışı bırakılabilir.

Gradle dosyanızda otomatik birleştirme etkinse bu hizmet otomatik olarak uygulamanızın manifest dosyasıyla birleştirilebilir.

Çerçeve, medya oturumu olduğunda hizmeti başlatır ve medya oturumu sona erdiğinde durdurur.

Medya Denetimi nasıl çalışır?

Cast çerçevesi, Cast 2.x'teki RemoteMediaPlayer sınıfını kullanımdan kaldırarak onun yerine yeni RemoteMediaClient sınıfını kullanıma sunacaktır. Bu sınıf, aynı işlevi daha kullanışlı bir dizi API'de sunarak GoogleApiClient'ı aktarmak zorunda kalmamaktadır.

Uygulamanız medya ad alanını destekleyen bir Web Alıcı uygulamasıyla CastSession kurduğunda çerçeve tarafından otomatik olarak RemoteMediaClient örneği oluşturulur. Uygulamanız, CastSession örneğinde getRemoteMediaClient() yöntemini çağırarak buna erişebilir.

Web Alıcısına istek gönderen tüm RemoteMediaClient yöntemleri, bu isteği izlemek için kullanılabilecek bir PendingResult nesnesi döndürür.

RemoteMediaClient örneğinin uygulamanızın birden fazla bölümü ve hatta kalıcı mini kumandalar ile bildirim hizmeti gibi çerçevenin bazı dahili bileşenleri tarafından paylaşılmış olması beklenir. Bu doğrultuda, bu örnek birden fazla RemoteMediaClient.Listener örneğinin kaydedilmesini destekler.

Medya meta verilerini ayarla

MediaMetadata sınıfı, Yayınlamak istediğiniz medya öğesiyle ilgili bilgileri temsil eder. Aşağıdaki örnek, bir filmin yeni MediaMetadata örneğini oluşturur ve başlığı, alt başlığı ve iki resmi ayarlar.

Kotlin
val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE)

movieMetadata.putString(MediaMetadata.KEY_TITLE, mSelectedMedia.getTitle())
movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, mSelectedMedia.getStudio())
movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia.getImage(0))))
movieMetadata.addImage(WebImage(Uri.parse(mSelectedMedia.getImage(1))))
Java
MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE);

movieMetadata.putString(MediaMetadata.KEY_TITLE, mSelectedMedia.getTitle());
movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, mSelectedMedia.getStudio());
movieMetadata.addImage(new WebImage(Uri.parse(mSelectedMedia.getImage(0))));
movieMetadata.addImage(new WebImage(Uri.parse(mSelectedMedia.getImage(1))));

Medya meta verileri içeren resimlerin kullanımı için Resim Seçimi bölümüne bakın.

Medya yükle

Uygulamanız, aşağıdaki kodda gösterildiği gibi bir medya öğesi yükleyebilir. İlk olarak, bir MediaInfo örneği oluşturmak için medyanın meta verileriyle MediaInfo.Builder'ı kullanın. Mevcut CastSession öğesinden RemoteMediaClient öğesini alın, ardından MediaInfo öğesini bu RemoteMediaClient öğesine yükleyin. Web Alıcısında çalışan bir medya oynatıcı uygulamasını oynatmak, duraklatmak ve başka bir şekilde kontrol etmek için RemoteMediaClient kullanın.

Kotlin
val mediaInfo = MediaInfo.Builder(mSelectedMedia.getUrl())
    .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
    .setContentType("videos/mp4")
    .setMetadata(movieMetadata)
    .setStreamDuration(mSelectedMedia.getDuration() * 1000)
    .build()
val remoteMediaClient = mCastSession.getRemoteMediaClient()
remoteMediaClient.load(MediaLoadRequestData.Builder().setMediaInfo(mediaInfo).build())
Java
MediaInfo mediaInfo = new MediaInfo.Builder(mSelectedMedia.getUrl())
        .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
        .setContentType("videos/mp4")
        .setMetadata(movieMetadata)
        .setStreamDuration(mSelectedMedia.getDuration() * 1000)
        .build();
RemoteMediaClient remoteMediaClient = mCastSession.getRemoteMediaClient();
remoteMediaClient.load(new MediaLoadRequestData.Builder().setMediaInfo(mediaInfo).build());

Ayrıca medya kanallarını kullanma ile ilgili bölüme de bakın.

4K video biçimi

Medyanızın hangi video biçimini kullandığını kontrol etmek için MediaStatus'ta getVideoInfo() değerini kullanarak VideoInfo öğesinin mevcut örneğini alın. Bu örnek, HDR TV biçiminin türünü ve görüntü yüksekliği ile genişliğini piksel cinsinden içerir. 4K biçiminin varyantları sabit değerlerle gösterilir HDR_TYPE_*.

Birden fazla cihaza uzaktan bildirim gönderme

Bir kullanıcı yayın yaparken aynı ağdaki diğer Android cihazlar da oynatmayı kontrol etmelerine olanak tanıyan bir bildirim alır. Cihazı bu tür bildirimleri alan herkes, Google > Google Cast > Uzaktan kumanda bildirimlerini göster bölümündeki Ayarlar uygulaması üzerinden söz konusu cihaz için bildirimleri kapatabilir. (Bildirimler, Ayarlar uygulamasına giden bir kısayol içerir.) Daha fazla bilgi için Uzaktan kumanda bildirimlerini yayınlama bölümüne bakın.

Mini kumanda ekle

Yayın Tasarımı Kontrol Listesi'ne göre bir gönderen uygulaması, kullanıcı mevcut içerik sayfasından gönderen uygulamasının başka bir bölümüne gittiğinde mini denetleyici olarak bilinen kalıcı bir denetim sağlamalıdır. Mini kumanda, mevcut Cast oturumunun kullanıcısına görünür bir hatırlatma sağlar. Kullanıcı mini kumandaya dokunarak Cast'in tam ekran genişletilmiş kumanda görünümüne dönebilir.

Çerçeve, mini kumandayı göstermek istediğiniz her etkinliğin düzen dosyasının alt kısmına ekleyebileceğiniz özel bir MiniControllerFragment görünümü sunar.

<fragment
    android:id="@+id/castMiniController"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:visibility="gone"
    class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment" />

Gönderen uygulamanız video veya ses canlı yayını oynatırken SDK, mini kumandadaki oynat/duraklat düğmesi yerine otomatik olarak bir oynat/durdur düğmesi görüntüler.

Bu özel görünümün başlığı ve alt başlığının metin görünümünü ayarlamak ve düğmeleri seçmek için Mini Kumanda'yı Özelleştirme bölümüne bakın.

Genişletilmiş denetleyici ekle

Google Cast Tasarım Kontrol Listesi için bir gönderen uygulamasının, yayınlanan medya için genişletilmiş bir denetleyici sağlaması gerekir. Genişletilmiş kumanda, mini kumandanın tam ekran sürümüdür.

Cast SDK'sı, genişletilmiş denetleyici için ExpandedControllerActivity adlı bir widget sağlar. Bu, Yayınla düğmesi eklemek için alt sınıfa girmeniz gereken soyut bir sınıftır.

İlk olarak, genişletilmiş denetleyicide yayın düğmesini sağlaması için yeni bir menü kaynak dosyası oluşturun:

<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:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
            app:showAsAction="always"/>

</menu>

ExpandedControllerActivity kapsamını genişleten yeni bir sınıf oluşturun.

Kotlin
class ExpandedControlsActivity : ExpandedControllerActivity() {
    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        super.onCreateOptionsMenu(menu)
        menuInflater.inflate(R.menu.expanded_controller, menu)
        CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item)
        return true
    }
}
Java
public class ExpandedControlsActivity extends ExpandedControllerActivity {
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        getMenuInflater().inflate(R.menu.expanded_controller, menu);
        CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item);
        return true;
    }
}

Şimdi yeni etkinliğinizi application etiketinin içindeki uygulama manifest'inde bildirin:

<application>
...
<activity
        android:name=".expandedcontrols.ExpandedControlsActivity"
        android:label="@string/app_name"
        android:launchMode="singleTask"
        android:theme="@style/Theme.CastVideosDark"
        android:screenOrientation="portrait"
        android:parentActivityName="com.google.sample.cast.refplayer.VideoBrowserActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
    </intent-filter>
</activity>
...
</application>

CastOptionsProvider'yi düzenleyin ve hedef etkinliği yeni etkinliğiniz olarak ayarlamak için NotificationOptions ve CastMediaOptions değerlerini değiştirin:

Kotlin
override fun getCastOptions(context: Context): CastOptions? {
    val notificationOptions = NotificationOptions.Builder()
        .setTargetActivityClassName(ExpandedControlsActivity::class.java.name)
        .build()
    val mediaOptions = CastMediaOptions.Builder()
        .setNotificationOptions(notificationOptions)
        .setExpandedControllerActivityClassName(ExpandedControlsActivity::class.java.name)
        .build()

    return CastOptions.Builder()
        .setReceiverApplicationId(context.getString(R.string.app_id))
        .setCastMediaOptions(mediaOptions)
        .build()
}
Java
public CastOptions getCastOptions(Context context) {
    NotificationOptions notificationOptions = new NotificationOptions.Builder()
            .setTargetActivityClassName(ExpandedControlsActivity.class.getName())
            .build();
    CastMediaOptions mediaOptions = new CastMediaOptions.Builder()
            .setNotificationOptions(notificationOptions)
            .setExpandedControllerActivityClassName(ExpandedControlsActivity.class.getName())
            .build();

    return new CastOptions.Builder()
            .setReceiverApplicationId(context.getString(R.string.app_id))
            .setCastMediaOptions(mediaOptions)
            .build();
}

Uzak medya yüklendiğinde yeni etkinliğinizi görüntülemek için LocalPlayerActivity loadRemoteMedia yöntemini güncelleyin:

Kotlin
private fun loadRemoteMedia(position: Int, autoPlay: Boolean) {
    val remoteMediaClient = mCastSession?.remoteMediaClient ?: return

    remoteMediaClient.registerCallback(object : RemoteMediaClient.Callback() {
        override fun onStatusUpdated() {
            val intent = Intent(this@LocalPlayerActivity, ExpandedControlsActivity::class.java)
            startActivity(intent)
            remoteMediaClient.unregisterCallback(this)
        }
    })

    remoteMediaClient.load(
        MediaLoadRequestData.Builder()
            .setMediaInfo(mSelectedMedia)
            .setAutoplay(autoPlay)
            .setCurrentTime(position.toLong()).build()
    )
}
Java
private void loadRemoteMedia(int position, boolean autoPlay) {
    if (mCastSession == null) {
        return;
    }
    final RemoteMediaClient remoteMediaClient = mCastSession.getRemoteMediaClient();
    if (remoteMediaClient == null) {
        return;
    }
    remoteMediaClient.registerCallback(new RemoteMediaClient.Callback() {
        @Override
        public void onStatusUpdated() {
            Intent intent = new Intent(LocalPlayerActivity.this, ExpandedControlsActivity.class);
            startActivity(intent);
            remoteMediaClient.unregisterCallback(this);
        }
    });
    remoteMediaClient.load(new MediaLoadRequestData.Builder()
            .setMediaInfo(mSelectedMedia)
            .setAutoplay(autoPlay)
            .setCurrentTime(position).build());
}

Gönderen uygulamanız bir video veya ses canlı yayını oynatırken SDK, genişletilmiş kumandadaki oynat/duraklat düğmesi yerine otomatik olarak bir oynat/durdur düğmesi görüntüler.

Görünümü temalar kullanarak ayarlamak, hangi düğmelerin gösterileceğini seçmek ve özel düğmeler eklemek için Genişletilmiş Denetleyiciyi Özelleştirme bölümüne bakın.

Ses düzeyi kontrolü

Çerçeve, gönderen uygulamasının hacmini otomatik olarak yönetir. Çerçeve, gönderen uygulamasını ve Web Alıcı uygulamasını otomatik olarak senkronize eder. Böylece, gönderen kullanıcı arayüzü her zaman Web Alıcısı tarafından belirtilen ses düzeyini raporlar.

Fiziksel düğme ses düzeyi kontrolü

Android'de, Jelly Bean veya daha yeni bir sürümü kullanan tüm cihazlarda Web Alıcısı'ndaki yayın oturumunun ses düzeyini varsayılan olarak değiştirmek için gönderen cihazın üzerindeki fiziksel düğmeler kullanılabilir.

Jelly Bean'den önce fiziksel düğme ses düzeyi kontrolü

Jelly Bean'den eski Android cihazlarda Web Alıcısı cihazın ses düzeyini kontrol etmek üzere fiziksel ses tuşlarını kullanmak için gönderen uygulaması, Etkinliklerinde dispatchKeyEvent özelliğini geçersiz kılmalı ve CastContext.onDispatchVolumeKeyEventBeforeJellyBean() araması yapmalıdır:

Kotlin
class MyActivity : FragmentActivity() {
    override fun dispatchKeyEvent(event: KeyEvent): Boolean {
        return (CastContext.getSharedInstance(this)
            .onDispatchVolumeKeyEventBeforeJellyBean(event)
                || super.dispatchKeyEvent(event))
    }
}
Java
class MyActivity extends FragmentActivity {
    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        return CastContext.getSharedInstance(this)
            .onDispatchVolumeKeyEventBeforeJellyBean(event)
            || super.dispatchKeyEvent(event);
    }
}

Bildirim ve kilit ekranına medya denetimleri ekleyin

Yalnızca Android'de, Google Cast Tasarım Kontrol Listesi için bir gönderen uygulamasının medya kontrollerini bildirimde uygulaması ve gönderenin yayın yaptığı ancak gönderen uygulamanın odaklanamadığı kilit ekranında uygulaması gerekir. Çerçeve, gönderen uygulamasının bildirim ve kilit ekranında medya kontrolleri oluşturmasına yardımcı olmak için MediaNotificationService ve MediaIntentReceiver özelliklerini sunar.

MediaNotificationService, gönderen yayın yaparken çalışır ve küçük resim ile mevcut yayın öğesiyle ilgili bilgilerin yanı sıra bir oynat/duraklat düğmesi ve bir durdurma düğmesi içeren bir bildirim gösterir.

MediaIntentReceiver, bildirimdeki kullanıcı işlemlerini işleyen bir BroadcastReceiver öğesidir.

Uygulamanız, kilit ekranından NotificationOptions aracılığıyla bildirim ve medya kontrolünü yapılandırabilir. Uygulamanız, bildirimde hangi kontrol düğmelerinin gösterileceğini ve kullanıcı bildirime dokunduğunda hangi Activity düğmesinin açılacağını yapılandırabilir. İşlemler açıkça sağlanmazsa MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK ve MediaIntentReceiver.ACTION_STOP_CASTING varsayılan değerleri kullanılır.

Kotlin
// Example showing 4 buttons: "rewind", "play/pause", "forward" and "stop casting".
val buttonActions: MutableList<String> = ArrayList()
buttonActions.add(MediaIntentReceiver.ACTION_REWIND)
buttonActions.add(MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK)
buttonActions.add(MediaIntentReceiver.ACTION_FORWARD)
buttonActions.add(MediaIntentReceiver.ACTION_STOP_CASTING)

// Showing "play/pause" and "stop casting" in the compat view of the notification.
val compatButtonActionsIndices = intArrayOf(1, 3)

// Builds a notification with the above actions. Each tap on the "rewind" and "forward" buttons skips 30 seconds.
// Tapping on the notification opens an Activity with class VideoBrowserActivity.
val notificationOptions = NotificationOptions.Builder()
    .setActions(buttonActions, compatButtonActionsIndices)
    .setSkipStepMs(30 * DateUtils.SECOND_IN_MILLIS)
    .setTargetActivityClassName(VideoBrowserActivity::class.java.name)
    .build()
Java
// Example showing 4 buttons: "rewind", "play/pause", "forward" and "stop casting".
List<String> buttonActions = new ArrayList<>();
buttonActions.add(MediaIntentReceiver.ACTION_REWIND);
buttonActions.add(MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK);
buttonActions.add(MediaIntentReceiver.ACTION_FORWARD);
buttonActions.add(MediaIntentReceiver.ACTION_STOP_CASTING);

// Showing "play/pause" and "stop casting" in the compat view of the notification.
int[] compatButtonActionsIndices = new int[]{1, 3};

// Builds a notification with the above actions. Each tap on the "rewind" and "forward" buttons skips 30 seconds.
// Tapping on the notification opens an Activity with class VideoBrowserActivity.
NotificationOptions notificationOptions = new NotificationOptions.Builder()
    .setActions(buttonActions, compatButtonActionsIndices)
    .setSkipStepMs(30 * DateUtils.SECOND_IN_MILLIS)
    .setTargetActivityClassName(VideoBrowserActivity.class.getName())
    .build();

Medya denetimlerinin bildirimden ve kilit ekranından gösterilmesi varsayılan olarak açıktır ve CastMediaOptions.Builder içinde null ile setNotificationOptions çağrısı yapılarak devre dışı bırakılabilir. Şu anda, bildirim açık olduğu sürece kilit ekranı özelliği etkindir.

Kotlin
// ... continue with the NotificationOptions built above
val mediaOptions = CastMediaOptions.Builder()
    .setNotificationOptions(notificationOptions)
    .build()
val castOptions: CastOptions = Builder()
    .setReceiverApplicationId(context.getString(R.string.app_id))
    .setCastMediaOptions(mediaOptions)
    .build()
Java
// ... continue with the NotificationOptions built above
CastMediaOptions mediaOptions = new CastMediaOptions.Builder()
        .setNotificationOptions(notificationOptions)
        .build();
CastOptions castOptions = new CastOptions.Builder()
        .setReceiverApplicationId(context.getString(R.string.app_id))
        .setCastMediaOptions(mediaOptions)
        .build();

Gönderen uygulamanız bir video veya ses canlı yayını oynatırken SDK, bildirim kontrolündeki oynat/duraklat düğmesi yerine kilit ekranı kontrolü yerine otomatik olarak bir oynat/durdur düğmesi görüntüler.

Not: Lollipop öncesi cihazlarda kilit ekranı kontrollerini görüntülemek için RemoteMediaClient sizin adınıza otomatik olarak ses odağı ister.

Hataları işleme

Gönderen uygulamalarının tüm hata geri çağırmalarını ele alması ve Cast yaşam döngüsünün her aşaması için en iyi yanıta karar vermesi çok önemlidir. Uygulama, kullanıcıya hata iletişim kutuları gösterebilir veya Web Alıcısı ile bağlantıyı kesmeye karar verebilir.