Cast'i Android Uygulamanıza Entegre Edin

Bu geliştirici kılavuzunda, Android Sender SDK'sını kullanarak Android gönderen uygulamanıza Google Cast desteğinin nasıl ekleneceği açıklanmaktadır.

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

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

Gönderen çerçevesi, gönderen uygulamayı etkinlikler hakkında bilgilendirmek ve Cast uygulama 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 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 cihaz keşfini otomatik olarak başlatır MediaRouter.
  • Kullanıcı, Yayınla düğmesini tıkladığında çerçeve, keşfedilen yayın cihazlarının listesini içeren Yayınla iletişim kutusunu 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 ve Web Alıcı uygulamaları arasında bir iletişim kanalı oluşturur.
  • Çerçeve, Web Alıcı'da medya oynatmayı yüklemek ve kontrol etmek için iletişim kanalını kullanır.
  • Çerçeve, medya oynatma durumunu gönderen ile Web Alıcısı arasında senkronize eder: Kullanıcı, gönderen kullanıcı arayüzü işlemlerini yaptığında çerçeve, bu medya kontrol isteklerini Web Alıcısı'na iletir ve Web Alıcısı medya durumu güncellemelerini gönderdiğinde çerçeve, gönderen kullanıcı arayüzünün durumunu günceller.
  • Kullanıcı, yayın cihazıyla bağlantıyı kesmek için yayın düğmesini tıkladığında çerçeve, gönderen uygulamanın web alıcısıyla 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 ekleme adımları açıklanmaktadır.

Android manifest dosyasını yapılandırma

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

uses-sdk

Cast SDK'sının desteklediği minimum ve hedef Android API düzeylerini ayarlayın. Şu anda minimum API düzeyi 24, hedef API düzeyi ise 35'tir.

<uses-sdk
        android:minSdkVersion="24"
        android:targetSdkVersion="35" />

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 ve Lollipop öncesi bir minimum Android SDK sürümünü hedefliyorsanız Theme.AppCompat varyantını kullanmanız gerekir.

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

Cast bağlamını başlatma

Çerçevede, çerçevenin tüm etkileşimlerini koordine eden global bir tekil nesne (CastContext) bulunur.

Uygulamanız, CastContext singleton'ı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çenekler içeren bir CastOptions örneği sağlar. Bunlardan en önemlisi, keşif sonuçlarını filtrelemek ve bir yayın oturumu başlatıldığında Web Alıcısı 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 nitelikli adını, gönderen uygulamanı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, CastContext.getSharedInstance() çağrıldığında 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, Cast Tasarım Kontrol Listesi'ne uygun widget'lar sağlar:

  • Tanıtım amaçlı yer paylaşımı: Çerçeve, bir alıcı ilk kez kullanılabilir olduğunda Yayınla düğmesine dikkat çekmek için kullanıcıya gösterilen özel bir görünüm olan IntroductoryOverlay sağlar. Gönderen 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, keşfedilen cihazların listelendiği bir Yayınla iletişim kutusu gösterilir. Cihaz bağlıyken kullanıcı Yayınla düğmesini tıkladığında mevcut medya meta verileri (ör. başlık, kayıt stüdyosunun adı ve küçük resim) gösterilir veya kullanıcının yayın cihazıyla bağlantısını kesmesine izin verilir. "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ş kumandadan gönderen uygulamadaki başka bir ekrana gittiğinde, kullanıcının o anda 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österilir.

  • Genişletilmiş Kumanda: Kullanıcı içerik yayınlarken medya bildirimini veya mini kumandayı tıklarsa genişletilmiş kumanda açılır. Bu kumanda, o anda oynatılan medya meta verilerini gösterir ve medya oynatmayı kontrol etmek için çeşitli düğmeler sunar.

  • Bildirim: Yalnızca Android. Kullanıcı içerik yayınlarken gönderen uygulamadan ayrıldığında, yayınlanmakta olan 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 kilit ekranına gittiğinde (veya cihazın zaman aşımı süresi dolduğunda) medya kilit ekranı kontrolü gösterilir. Bu kontrol, yayınlanan medyanın meta verilerini ve oynatma kontrollerini gösterir.

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

Yayınla düğmesi ekleme

Android MediaRouter API'leri, ikincil cihazlarda medya görüntüleme ve oynatmayı etkinleştirmek için tasarlanmıştır. MediaRouter API'sini kullanan Android uygulamalarının kullanıcı arayüzünde bir Yayınla düğmesi bulunmalıdır. Bu düğme, kullanıcıların Yayınla cihazı gibi ikincil bir cihazda medya oynatmak için medya rotası seçmesine olanak tanır.

Bu çerçeve, MediaRouteButton öğesini Cast button olarak eklemeyi çok kolaylaştırır. Öncelikle menünüzü tanımlayan XML dosyasına bir menü öğesi veya MediaRouteButton eklemeniz ve bunu çerçeveye 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 öğeniz FragmentActivity öğesinden devralıyorsa düzeninize bir 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);
}

Yayınla düğmesinin görünümünü bir tema kullanarak ayarlamak için Yayınla Düğmesini Özelleştirme başlıklı makaleyi inceleyin.

Cihaz keşfini yapılandırma

Cihaz keşfi tamamen CastContext tarafından yönetilir. CastContext başlatılırken gönderen uygulama, Web Alıcı uygulama kimliğini belirtir ve isteğe bağlı olarak CastOptions içinde supportedNamespaces değerini ayarlayarak ad alanı filtreleme isteğinde bulunabilir. CastContext, dahili olarak MediaRouter referansını barındırır ve aşağıdaki koşullarda keşif sürecini başlatır:

  • Cihaz keşif gecikmesi ile pil kullanımı arasında denge kurmak için tasarlanmış bir algoritmaya dayalı olarak, gönderen uygulama ön plana girdiğinde keşif işlemi zaman zaman otomatik olarak başlatılır.
  • Yayınlama iletişim kutusu açık.
  • Cast SDK'sı, bir 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ı, Cast oturumu kavramını sunar. Cast oturumu oluşturulurken bir cihaza bağlanma, Web Alıcı uygulamasını başlatma (veya uygulamaya katılma), bu uygulamaya bağlanma ve medya kontrol kanalı başlatma adımları birleştirilir. Yayın 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() aracılığıyla 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 Cast cihazlarıyla yapılan oturumları temsil eder. Uygulamanız, SessionManager.getCurrentCastSession() aracılığıyla o anda etkin olan Cast oturumuna erişebilir.

Uygulamanız, oturum etkinliklerini (ör. oluşturma, askıya alma, devam ettirme ve sonlandırma) izlemek için SessionManagerListener sınıfını kullanabilir. Çerçeve, bir oturum etkinken anormal/ani bir sonlandırmadan sonra otomatik olarak devam etmeye çalışır.

Oturumlar, MediaRouter iletişim kutularındaki kullanıcı hareketlerine yanıt olarak otomatik olarak oluşturulur ve kaldırılır.

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

Oturumun durum değişikliklerinden haberdar olmanız gerekiyorsa SessionManagerListener uygulayabilirsiniz. Bu örnekte, bir Activity içindeki CastSession öğesinin kullanılabilirliği dinlenir.

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
        mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession::class.java)
    }

    override fun onResume() {
        super.onResume()
        mCastSession = mSessionManager.currentCastSession
    }

    override fun onDestroy() {
        super.onDestroy()
        mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession::class.java)
    }
}
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();
        mSessionManager.addSessionManagerListener(mSessionManagerListener, CastSession.class);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mCastSession = mSessionManager.getCurrentCastSession();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mSessionManager.removeSessionManagerListener(mSessionManagerListener, CastSession.class);
    }
}

Akış aktarma

Oturum durumunu koruma, akış aktarmanın temelidir. Kullanıcılar sesli komutlar, Google Home uygulaması veya akıllı ekranlar aracılığıyla mevcut ses ve video akışlarını cihazlar arasında taşıyabilir. Medya bir cihazda (kaynak) durur ve başka bir cihazda (hedef) devam eder. En yeni donanım yazılımına sahip tüm Cast cihazları, akış aktarma işleminde kaynak veya hedef olarak kullanılabilir.

Akış aktarma veya genişletme sırasında yeni hedef cihazı almak için CastSession#addCastListener kullanarak bir Cast.Listener kaydedin. Ardından onDeviceNameChanged geri çağırma sırasında CastSession#getCastDevice() öğesini çağırın.

Daha fazla bilgi için Web Alıcısı'nda akış aktarma başlıklı makaleyi inceleyin.

Otomatik yeniden bağlanma

Çerçeve, gönderen uygulamanın birçok küçük uç durumda yeniden bağlanmayı işlemek için etkinleştirebileceği bir ReconnectionService sağlar. Örneğin:

  • Geçici kablosuz bağlantı kaybından kurtarma
  • Cihazın uyku modundan çıkması
  • Uygulamayı arka plana alma işleminden kurtarma
  • Uygulama kilitlendiyse kurtarma

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

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

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

Medya Kontrolü'nün işleyiş şekli

Cast çerçevesi, Cast 2.x'teki RemoteMediaPlayer sınıfını kullanımdan kaldırarak yerine daha kullanışlı API'lerde aynı işlevi sağlayan ve GoogleApiClient'ın iletilmesini gerektirmeyen yeni bir sınıf olan RemoteMediaClient sınıfını kullanır.

Uygulamanız, medya ad alanını destekleyen bir web alıcısı uygulamasıyla CastSession oluşturduğunda çerçeve tarafından otomatik olarak bir RemoteMediaClient örneği oluşturulur. Uygulamanız, CastSession örneğinde getRemoteMediaClient() yöntemini çağırarak bu örneğe 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 çerçevenin bazı dahili bileşenleri (ör. kalıcı mini kumandalar ve bildirim hizmeti) tarafından paylaşılması beklenir. Bu amaçla, bu örnek birden fazla RemoteMediaClient.Listener örneğinin kaydını destekler.

Medya meta verilerini ayarlama

MediaMetadata sınıfı, yayınlamak istediğiniz medya öğesiyle ilgili bilgileri temsil eder. Aşağıdaki örnekte, bir filmin yeni bir MediaMetadata örneği oluşturulur ve başlık, alt yazı ve iki resim ayarlanır.

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 verileriyle resimlerin kullanımı hakkında bilgi edinmek için Resim Seçimi bölümüne bakın.

Medyayı yükle

Uygulamanız, aşağıdaki kodda gösterildiği gibi bir medya öğesi yükleyebilir. Önce medyanın meta verileriyle MediaInfo.Builder kullanarak bir MediaInfo örneği oluşturun. 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 şekillerde 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());

Medya parçalarını kullanma ile ilgili bölüme de göz atın.

4K video biçimi

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

Birden fazla cihaza uzaktan kumanda bildirimleri gönderme

Bir kullanıcı içerik yayınlarken aynı ağdaki diğer Android cihazlar da oynatmayı kontrol etmelerine olanak tanıyan bir bildirim alır. Cihazına bu tür bildirimler gelen herkes, Ayarlar uygulamasında Google > Google Cast > Uzaktan kumanda bildirimlerini göster bölümünden bu bildirimleri kapatabilir. (Bildirimlerde Ayarlar uygulamasına giden bir kısayol bulunur.) Daha fazla bilgi için Yayınlama uzaktan kumanda bildirimleri başlıklı makaleyi inceleyin.

Mini kumanda ekleme

Cast Tasarım Kontrol Listesi'ne göre, gönderen uygulaması, kullanıcı mevcut içerik sayfasından gönderen uygulamasının başka bir bölümüne gittiğinde görünmesi gereken mini kumanda olarak bilinen kalıcı bir kontrol sağlamalıdır. Mini kumanda, kullanıcıya mevcut Cast oturumunu görünür bir şekilde hatırlatır. Kullanıcı, mini kumandaya dokunarak tam ekran genişletilmiş Cast 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 görünüm olan MiniControllerFragment'ı sağlar.

<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 bir video veya ses canlı yayını oynatırken SDK, mini kumandadaki oynat/duraklat düğmesinin yerine otomatik olarak bir oynat/durdur düğmesi gösterir.

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

Genişletilmiş kumanda ekleme

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

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

İlk olarak, genişletilmiş kumanda için yeni bir menü kaynak dosyası oluşturarak Yayınla düğmesini sağlayın:

<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 sınıfı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 uygulama manifestinde application etiketi içinde yeni etkinliğinizi tanımlayın:

<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>

Hedef etkinliği yeni etkinliğinize ayarlamak için CastOptionsProvider öğesini düzenleyin ve NotificationOptions ile 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üğmesinin yerine otomatik olarak bir oynat/durdur düğmesi gösterir.

Temaları kullanarak görünümü ayarlamak, hangi düğmelerin gösterileceğini seçmek ve özel düğmeler eklemek için Genişletilmiş Kumandayı Özelleştirme başlıklı makaleyi inceleyin.

Ses düzeyi kontrolü

Çerçeve, gönderen uygulamanın ses seviyesini otomatik olarak yönetir. Çerçeve, gönderen ve Web Alıcısı uygulamalarını otomatik olarak senkronize eder. Böylece gönderen kullanıcı arayüzü, her zaman Web Alıcısı tarafından belirtilen ses seviyesini bildirir.

Fiziksel düğmeyle ses seviyesi kontrolü

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

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

Jelly Bean'den eski Android cihazlarda Web Alıcısı cihazının ses seviyesini kontrol etmek için fiziksel ses seviyesi tuşlarını kullanmak istiyorsanız gönderen uygulamanın, Etkinliklerinde dispatchKeyEvent yöntemini geçersiz kılması ve CastContext.onDispatchVolumeKeyEventBeforeJellyBean() yöntemini çağırması gerekir:

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 kontrolleri ekleme

Yalnızca Android'de, Google Cast Tasarım Kontrol Listesi, gönderen uygulamanın bildirimde ve kilit ekranında medya kontrollerini uygulamasını gerektirir. Bu durumda gönderen yayın yapar ancak gönderen uygulama odakta değildir. Çerçeve, gönderen uygulamanın bildirimde ve kilit ekranında medya kontrolleri oluşturmasına yardımcı olmak için MediaNotificationService ve MediaIntentReceiver sağlar.

MediaNotificationService, gönderen yayın yaparken çalışır ve resim küçük resmi ile mevcut yayın öğesi hakkında bilgi, oynat/duraklat düğmesi ve durdur düğmesi içeren bir bildirim gösterir.

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

Uygulamanız, NotificationOptions aracılığıyla kilit ekranından 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 öğesinin açılacağını yapılandırabilir. İşlemler açıkça belirtilmezse varsayılan değerler (MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK ve MediaIntentReceiver.ACTION_STOP_CASTING) 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 kontrollerinin bildirim ve kilit ekranında gösterilmesi varsayılan olarak etkindir. CastMediaOptions.Builder içinde null ile setNotificationOptions çağrılarak devre dışı bırakılabilir. Şu anda kilit ekranı özelliği, bildirim etkin olduğu sürece etkin durumda.

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ünde oynat/duraklat düğmesi yerine otomatik olarak bir oynat/durdur düğmesi gösterir ancak kilit ekranı kontrolünde göstermez.

Not: RemoteMediaClient, Lollipop öncesi cihazlarda kilit ekranı kontrollerini göstermek için sizin adınıza otomatik olarak ses odağı isteğinde bulunur.

Hataları işleme

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