ادغام کردن Cast در برنامه Android

این راهنمای توسعه‌دهنده نحوه افزودن پشتیبانی Google Cast به برنامه فرستنده Android بااستفاده از کیت توسعه نرم‌افزار فرستنده Android را شرح می‌دهد.

دستگاه همراه یا رایانه کیفی فرستنده است که پخش را کنترل می‌کند و دستگاه Google Cast گیرنده است که محتوا را در تلویزیون نمایش می‌دهد.

چارچوب فرستنده به باینری کتابخانه کلاس Cast و منابع مرتبط موجود در زمان اجرا در فرستنده اشاره دارد. برنامه فرستنده یا برنامه «پخش محتوا» به برنامه‌ای اشاره دارد که در فرستنده نیز اجرا می‌شود. برنامه گیرنده وب به برنامه HTML درحال اجرا در دستگاه سازگار با Cast اشاره دارد.

چارچوب فرستنده از طراحی بازخوانی ناهمزمان برای اطلاع‌رسانی رویدادها به برنامه فرستنده و انتقال بین حالت‌های مختلف چرخه حیات برنامه «پخش محتوا» استفاده می‌کند.

جریان برنامه

مراحل زیر جریان اجرای سطح بالای معمول برای برنامه Android فرستنده را شرح می‌دهد:

  • چارچوب Cast به‌طور خودکار MediaRouter شناسایی دستگاه را براساس چرخه حیات Activity شروع می‌کند.
  • وقتی کاربر روی دکمه «پخش محتوا» کلیک می‌کند، چارچوب گفتگوی «پخش محتوا» را با فهرست دستگاه‌های «پخش محتوا» کشف‌شده ارائه می‌کند.
  • وقتی کاربر دستگاه Cast را انتخاب می‌کند، چارچوب تلاش می‌کند برنامه «گیرنده وب» را در دستگاه Cast راه‌اندازی کند.
  • چارچوب، فراخوانی‌های برگشتی را در برنامه فرستنده فراخوانی می‌کند تا تأیید کند که برنامه «گیرنده وب» راه‌اندازی شده است.
  • این چارچوب یک کانال ارتباطی بین برنامه‌های فرستنده و «گیرنده وب» ایجاد می‌کند.
  • این چارچوب از کانال ارتباطی برای بار کردن و کنترل پخش رسانه در «گیرنده وب» استفاده می‌کند.
  • این چارچوب وضعیت بازپخش رسانه را بین فرستنده و «گیرنده وب» همگام‌سازی می‌کند: وقتی کاربر کنش‌های میانای کاربر فرستنده را انجام می‌دهد، چارچوب آن درخواست‌های کنترل رسانه را به «گیرنده وب» ارسال می‌کند، و وقتی «گیرنده وب» به‌روزرسانی‌های وضعیت رسانه را ارسال می‌کند، چارچوب وضعیت میانای کاربر فرستنده را به‌روز می‌کند.
  • وقتی کاربر روی دکمه «پخش محتوا» کلیک می‌کند تا اتصال خود را از دستگاه «پخش محتوا» قطع کند، چارچوب اتصال برنامه فرستنده را از «گیرنده وب» قطع می‌کند.

برای فهرست کامل همه کلاس‌ها، روش‌ها، و رویدادها در Google Cast Android SDK، به مرجع API فرستنده Google Cast برای Android مراجعه کنید. بخش‌های زیر مراحل افزودن «پخش محتوا» به برنامه Android را پوشش می‌دهد.

پیکربندی مانیفست Android

فایل AndroidManifest.xml برنامه شما نیاز دارد عناصر زیر را برای «کیت توسعه نرم‌افزار Cast» پیکربندی کنید:

uses-sdk

حداقل و هدف سطح میانای برنامه‌سازی کاربردی Android را که «کیت توسعه نرم‌افزار Cast» پشتیبانی می‌کند تنظیم کنید. درحال‌حاضر حداقل سطح میانای برنامه‌سازی کاربردی ۲۴ و سطح هدف ۳۵ است.

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

android:theme

زمینه برنامه‌تان را براساس حداقل نسخه کیت توسعه نرم‌افزار Android تنظیم کنید. برای مثال، اگر زمینه خودتان را پیاده‌سازی نمی‌کنید، باید از نوعی از Theme.AppCompat هنگام هدف‌یابی حداقل نسخه کیت توسعه نرم‌افزار Android که پیش‌از Lollipop است استفاده کنید.

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

مقداردهی اولیه بافت «پخش محتوا»

این چارچوب دارای یک شیء تک‌نمونه جهانی، CastContext، است که همه تعاملات چارچوب را هماهنگ می‌کند.

برنامه شما باید OptionsProvider میانای لازم برای ارائه گزینه‌های موردنیاز برای مقداردهی اولیه CastContext تک‌نمونه را پیاده‌سازی کند. OptionsProvider نمونه‌ای از CastOptions ارائه می‌دهد که حاوی گزینه‌هایی است که بر رفتار چارچوب تأثیر می‌گذارد. مهم‌ترین آن‌ها شناسه برنامه «گیرنده وب» است که برای فیلتر کردن نتایج کاوش و راه‌اندازی برنامه «گیرنده وب» هنگام شروع جلسه Cast استفاده می‌شود.

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

باید نام کاملاً واجدشرایط OptionsProvider پیاده‌سازی‌شده را به‌عنوان فیلد فراداده در فایل AndroidManifest.xml برنامه فرستنده اعلام کنید:

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

CastContext به‌صورت تنبل مقداردهی اولیه می‌شود وقتی CastContext.getSharedInstance() فراخوانی می‌شود.

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 ابزارک‌هایی را ارائه می‌دهد که با «فهرست وارسی طراحی Cast» مطابقت دارند:

  • روپوش معرفی: این چارچوب یک «نمای» سفارشی ارائه می‌دهد که IntroductoryOverlay به کاربر نشان داده می‌شود تا توجه او را به دکمه «پخش محتوا» در اولین باری که گیرنده دردسترس قرار می‌گیرد جلب کند. برنامه «فرستنده» می‌تواند نوشتار و جایگاه نوشتار عنوان را سفارشی‌سازی کند.

  • دکمه «پخش محتوا»: دکمه «پخش محتوا» صرف‌نظر از دردسترس بودن دستگاه‌های Cast قابل‌مشاهده است. وقتی کاربر برای اولین‌بار روی دکمه «پخش محتوا» کلیک می‌کند، چارگوش گفتگوی «پخش محتوا» نمایش داده می‌شود که دستگاه‌های شناسایی‌شده را فهرست می‌کند. وقتی کاربر روی دکمه «پخش محتوا» کلیک می‌کند درحالی‌که دستگاه متصل است، فراداده رسانه فعلی (مثل عنوان، نام استودیو ضبط، و تصویر کوچک) را نمایش می‌دهد یا به کاربر اجازه می‌دهد از دستگاه «پخش محتوا» قطع اتصال کند. گاهی اوقات به «دکمه پخش محتوا» «نماد پخش محتوا» گفته می‌شود.

  • کنترل‌کننده کوچک: وقتی کاربر درحال پخش محتوا است و از صفحه محتوای فعلی یا کنترل‌کننده ازهم بازشده به صفحه دیگری در برنامه فرستنده پیمایش کرده است، کنترل‌کننده کوچک در پایین صفحه نمایش داده می‌شود تا به کاربر امکان دهد فراداده رسانه درحال پخش را ببیند و پخش را کنترل کند.

  • کنترل‌کننده گسترده: وقتی کاربر محتوا را ارسال می‌کند، اگر روی اعلان رسانه یا کنترل‌کننده کوچک کلیک کند، کنترل‌کننده گسترده راه‌اندازی می‌شود که فراداده رسانه درحال پخش را نمایش می‌دهد و چند دکمه برای کنترل بازپخش رسانه ارائه می‌دهد.

  • اعلان: فقط Android. وقتی کاربر درحال پخش محتوا است و از برنامه فرستنده خارج می‌شود، اعلان رسانه‌ای نمایش داده می‌شود که فراداده رسانه درحال پخش و کنترل‌های بازپخش را نشان می‌دهد.

  • صفحه قفل: فقط Android. وقتی کاربر درحال پخش محتوا است و به صفحه قفل پیمایش می‌کند (یا دستگاه زمان‌بندی می‌شود)، کنترل صفحه قفل رسانه‌ای نمایش داده می‌شود که فراداده رسانه درحال پخش و کنترل‌های بازپخش را نشان می‌دهد.

راهنمای زیر شامل شرح‌هایی درباره نحوه افزودن این ابزاره‌ها به برنامه شما است.

افزودن دکمه پخش محتوا

«میاناهای برنامه‌سازی کاربردی» Android MediaRouter برای فعال کردن نمایش و بازپخش رسانه در دستگاه‌های ثانویه طراحی شده‌اند. برنامه‌های Android که از میانای برنامه‌سازی کاربردی MediaRouter استفاده می‌کنند باید دکمه «پخش محتوا» را به‌عنوان بخشی از میانای کاربر خود داشته باشند تا کاربران بتوانند مسیر رسانه را برای پخش رسانه در دستگاه ثانویه، مثل دستگاه Cast، انتخاب کنند.

این چارچوب افزودن MediaRouteButton به‌عنوان Cast button را بسیار آسان می‌کند. ابتدا باید عنصر منو یا MediaRouteButton را در فایل xml که منو شما را تعریف می‌کند اضافه کنید، و از CastButtonFactory برای اتصال آن به چارچوب استفاده کنید.

// 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
}
جاوا
// 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;
}

سپس، اگر Activity شما از FragmentActivity ارث‌بری کند، می‌توانید MediaRouteButton به چیدمانتان اضافه کنید.

// 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)
}
جاوا
// 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);
}

برای تنظیم ظاهر دکمه «ارسال محتوا» بااستفاده از زمینه، به سفارشی‌سازی دکمه «ارسال محتوا» مراجعه کنید.

پیکربندی کاوش دستگاه

کشف دستگاه کاملاً توسط CastContext مدیریت می‌شود. هنگام مقداردهی اولیه CastContext، برنامه فرستنده شناسه برنامه Web Receiver را مشخص می‌کند و می‌تواند به‌صورت اختیاری با تنظیم supportedNamespaces در CastOptions، فیلتر فضای نام را درخواست کند. ‫CastContext به‌طور داخلی مرجعی برای MediaRouter دارد و فرایند کاوش را تحت شرایط زیر شروع می‌کند:

  • براساس الگوریتمی که برای ایجاد توازن بین تأخیر در شناسایی دستگاه و مصرف باتری طراحی شده است، وقتی برنامه فرستنده به پیش‌زمینه می‌رود، شناسایی گاهی به‌طور خودکار شروع می‌شود.
  • کادر گفتگوی Cast باز است.
  • «کیت توسعه نرم‌افزار Cast» درحال تلاش برای بازیابی جلسه Cast است.

وقتی کادر گفتگوی «پخش محتوا» بسته شود یا برنامه فرستنده به پس‌زمینه برود، فرایند شناسایی متوقف خواهد شد.

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

نحوه عملکرد مدیریت جلسه

Cast SDK مفهوم جلسه Cast را معرفی می‌کند که ایجاد آن ترکیبی از مراحل اتصال به دستگاه، راه‌اندازی (یا پیوستن به) برنامه Web Receiver، اتصال به آن برنامه، و مقداردهی اولیه کانال کنترل رسانه است. برای اطلاعات بیشتر درباره جلسه‌های Cast و چرخه عمر «گیرنده وب»، راهنمای چرخه عمر برنامه «گیرنده وب» را ببینید.

جلسه‌ها توسط کلاس SessionManager مدیریت می‌شوند که برنامه‌تان می‌تواند ازطریق CastContext.getSessionManager() به آن دسترسی داشته باشد. جلسات فردی با زیرکلاس‌های کلاس Session نشان داده می‌شوند. برای مثال، CastSession نشان‌دهنده جلسات با دستگاه‌های Cast است. برنامه شما می‌تواند ازطریق SessionManager.getCurrentCastSession() به جلسه «پخش محتوا» فعال فعلی دسترسی داشته باشد.

برنامه شما می‌تواند از کلاس SessionManagerListener برای نظارت بر رویدادهای جلسه، مانند ایجاد، تعلیق، ازسرگیری، و پایان استفاده کند. چارچوب به‌طور خودکار تلاش می‌کند از پایان غیرعادی/ناگهانی درحالی‌که جلسه فعال بوده است ازسر بگیرد.

جلسه‌ها به‌طور خودکار در پاسخ به اشاره‌های کاربر از کادرهای گفتگوی MediaRouter ایجاد و بسته می‌شوند.

برای درک بهتر خطاهای شروع «پخش محتوا»، برنامه‌ها می‌توانند از CastContext#getCastReasonCodeForCastStatusCode(int) برای تبدیل خطای شروع جلسه به CastReasonCodes استفاده کنند. لطفاً توجه داشته باشید که برخی‌از خطاهای شروع جلسه (مثلاً CastReasonCodes#CAST_CANCELLED) رفتار موردانتظار است و نباید به‌عنوان خطا ثبت شود.

اگر لازم است از تغییرات وضعیت جلسه مطلع شوید، می‌توانید SessionManagerListener را پیاده‌سازی کنید. این مثال به دردسترس بودن CastSession در Activity گوش می‌دهد.

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

انتقال جاری‌سازی

حفظ وضعیت جلسه اساس انتقال جاری‌سازی است، جایی که کاربران می‌توانند بااستفاده از فرمان‌های صوتی، برنامه Google Home، یا نمایشگرهای هوشمند، جاری‌سازی‌های صوتی و ویدیویی موجود را بین دستگاه‌ها منتقل کنند. پخش رسانه در یک دستگاه (منبع) متوقف می‌شود و در دستگاه دیگری (مقصد) ادامه می‌یابد. هر دستگاه «پخش محتوا» با جدیدترین سفت‌افزار می‌تواند به‌عنوان منبع یا مقصد در انتقال جاری‌سازی عمل کند.

برای دریافت دستگاه مقصد جدید درطول انتقال یا گسترش جاری‌سازی، Cast.Listener را بااستفاده از CastSession#addCastListener ثبت کنید. سپس درطول تماس برگشتی onDeviceNameChanged تماس بگیرید CastSession#getCastDevice().

برای اطلاعات بیشتر، انتقال جاری‌سازی در «گیرنده وب» را ببینید.

اتصال مجدد خودکار

این چارچوب ReconnectionService را ارائه می‌دهد که برنامه فرستنده می‌تواند آن را فعال کند تا اتصال مجدد را در بسیاری از موارد جزئی خاص، مانند موارد زیر، مدیریت کند:

  • بازیابی پس‌از قطع موقت Wi-Fi
  • بازیابی از حالت خواب دستگاه
  • بازیابی از پس‌زمینه‌ای کردن برنامه
  • بازیابی درصورت ازکارافتادن برنامه

این سرویس به‌طور پیش‌فرض روشن است و می‌توان آن را در CastOptions.Builder خاموش کرد.

اگر ادغام خودکار در فایل gradle فعال باشد، این سرویس می‌تواند به‌طور خودکار در مانیفست برنامه‌تان ادغام شود.

چارچوب سرویس را وقتی جلسه رسانه وجود دارد شروع می‌کند و وقتی جلسه رسانه تمام می‌شود آن را متوقف می‌کند.

نحوه عملکرد «کنترل رسانه»

چارچوب «پخش محتوا» کلاس RemoteMediaPlayer را از Cast 2.x به‌نفع کلاس جدید RemoteMediaClient منسوخ می‌کند، که همان قابلیت را در مجموعه‌ای از «میاناهای برنامه‌سازی کاربردی» راحت‌تر ارائه می‌دهد، و از نیاز به انتقال GoogleApiClient جلوگیری می‌کند.

وقتی برنامه‌تان با برنامه «گیرنده وب» که از فضای نام رسانه پشتیبانی می‌کند CastSession ایجاد می‌کند، چارچوب به‌طور خودکار نمونه‌ای از RemoteMediaClient ایجاد می‌کند؛ برنامه‌تان می‌تواند با فراخوانی روش getRemoteMediaClient() در نمونه CastSession به آن دسترسی پیدا کند.

همه روش‌های RemoteMediaClient که درخواست‌ها را برای «گیرنده وب» صادر می‌کنند، یک شیء PendingResult برمی‌گردانند که می‌توان از آن برای ردیابی آن درخواست استفاده کرد.

انتظار می‌رود که نمونه RemoteMediaClient ممکن است توسط چندین بخش از برنامه شما و درواقع برخی‌از مؤلفه‌های داخلی چارچوب، مانند کنترل‌کننده‌های کوچک دائمی و سرویس اعلان هم‌رسانی شود. به همین منظور، این نمونه از ثبت چندین نمونه از RemoteMediaClient.Listener پشتیبانی می‌کند.

تنظیم فراداده رسانه

کلاس MediaMetadata اطلاعات مربوط به مورد رسانه‌ای را که می‌خواهید «ارسال محتوا» کنید نشان می‌دهد. مثال زیر نمونه جدیدی از MediaMetadata برای فیلم ایجاد می‌کند و عنوان، زیرنویس، و دو تصویر را تنظیم می‌کند.

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

در بخش انتخاب تصویر درباره استفاده از تصاویر با فراداده رسانه بیشتر بدانید.

بار کردن رسانه

برنامه شما می‌تواند یک مورد رسانه‌ای را بارگیری کند، همان‌طور که در کد زیر نشان داده شده است. ابتدا از MediaInfo.Builder با فراداده رسانه برای ساختن نمونه MediaInfo استفاده کنید. RemoteMediaClient را از CastSession فعلی دریافت کنید، سپس MediaInfo را در آن RemoteMediaClient بار کنید. از RemoteMediaClient برای پخش، توقف موقت، و کنترل برنامه پخش‌کننده رسانه درحال اجرا در «گیرنده وب» استفاده کنید.

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

همچنین بخش مربوط به استفاده از قطعه‌های رسانه‌ای را ببینید.

قالب ویدیو 4K

برای بررسی قالب ویدیویی رسانه‌تان، از getVideoInfo() در MediaStatus استفاده کنید تا نمونه فعلی VideoInfo را دریافت کنید. این نمونه شامل نوع قالب تلویزیون HDR و ارتفاع و عرض نمایشگر برحسب پیکسل است. گونه‌های قالب 4K با ثابت‌های HDR_TYPE_* مشخص می‌شوند.

اعلان‌های کنترل از راه دور برای چند دستگاه

وقتی کاربری درحال پخش محتوا است، سایر دستگاه‌های Android در همان شبکه اعلانی دریافت می‌کنند که به آن‌ها نیز اجازه می‌دهد بازپخش را کنترل کنند. هرکسی که دستگاهش این اعلان‌ها را دریافت می‌کند می‌تواند آن‌ها را برای آن دستگاه در برنامه «تنظیمات» در Google > Google Cast > نمایش اعلان‌های کنترل از دور خاموش کند. (اعلان‌ها شامل میان‌بری به برنامه «تنظیمات» است.) برای جزئیات بیشتر، اعلان‌های کنترل از دور پخش محتوا را ببینید.

افزودن کنترل‌کننده کوچک

طبق فهرست وارسی طراحی پخش محتوا، برنامه فرستنده باید کنترل پایداری به نام کنترل‌کننده کوچک ارائه دهد که وقتی کاربر از صفحه محتوای فعلی به بخش دیگری از برنامه فرستنده پیمایش می‌کند باید ظاهر شود. کنترل‌کننده کوچک یادآوری نمایانی از جلسه پخش محتوای فعلی به کاربر ارائه می‌دهد. با تک‌ضرب زدن روی کنترل‌کننده کوچک، کاربر می‌تواند به نمای کنترل‌کننده تمام‌صفحه گسترده «پخش محتوا» برگردد.

این چارچوب یک View سفارشی، MiniControllerFragment، ارائه می‌دهد که می‌توانید آن را به پایین فایل چیدمان هر فعالیتی که می‌خواهید کنترل‌کننده کوچک را در آن نشان دهید اضافه کنید.

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

وقتی برنامه فرستنده شما درحال پخش جاری‌سازی زنده ویدیو یا صدا است، SDK به‌طور خودکار دکمه پخش/توقف را به‌جای دکمه پخش/مکث در کنترل‌کننده کوچک نمایش می‌دهد.

برای تنظیم ظاهر نوشتار عنوان و زیرنویس این نمای سفارشی، و برای انتخاب دکمه‌ها، به سفارشی‌سازی کنترل‌کننده کوچک مراجعه کنید.

افزودن کنترل‌کننده ازهم بازشده

«بازبینه طراحی Google Cast» ایجاب می‌کند که برنامه فرستنده یک کنترل‌کننده گسترده برای رسانه درحال «پخش محتوا» ارائه دهد. کنترل‌کننده ازهم بازشده نسخه تمام‌صفحه کنترل‌کننده کوچک است.

«کیت توسعه نرم‌افزار Cast» ابزاره‌ای برای کنترل‌کننده گسترده به نام ExpandedControllerActivity ارائه می‌دهد. این یک کلاس انتزاعی است که باید آن را زیرکلاس کنید تا دکمه «پخش محتوا» را اضافه کنید.

ابتدا، فایل منبع منو جدیدی برای کنترل‌کننده ازهم بازشده ایجاد کنید تا دکمه «پخش محتوا» را ارائه دهد:

<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 را گسترش دهد.

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

اکنون فعالیت جدیدتان را در مانیفست برنامه در برچسب application اعلام کنید:

<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 را ویرایش کنید و NotificationOptions و CastMediaOptions را تغییر دهید تا فعالیت هدف را روی فعالیت جدیدتان تنظیم کنید:

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

روش LocalPlayerActivity loadRemoteMedia را به‌روز کنید تا فعالیت جدیدتان هنگام بار شدن رسانه از دور نمایش داده شود:

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()
    )
}
جاوا
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());
}

وقتی برنامه فرستنده شما درحال پخش جاری‌سازی زنده ویدیو یا صدا است، SDK به‌طور خودکار دکمه پخش/توقف را به‌جای دکمه پخش/مکث در کنترل‌کننده گسترده نمایش می‌دهد.

برای تنظیم ظاهر بااستفاده از زمینه‌ها، انتخاب دکمه‌هایی که نمایش داده شود، و افزودن دکمه‌های سفارشی، به سفارشی‌سازی کنترل‌کننده گسترده مراجعه کنید.

کنترل میزان صدا

چارچوب به‌طور خودکار حجم را برای برنامه فرستنده مدیریت می‌کند. چارچوب به‌طور خودکار برنامه‌های فرستنده و «گیرنده وب» را همگام‌سازی می‌کند تا رابط کاربری فرستنده همیشه حجم مشخص‌شده توسط «گیرنده وب» را گزارش کند.

کنترل میزان صدا با دکمه فیزیکی

در Android، از دکمه‌های فیزیکی دستگاه فرستنده می‌توان برای تغییر دادن صدای جلسه «پخش محتوا» در «گیرنده وب» به‌طور پیش‌فرض برای هر دستگاهی که از Jelly Bean یا نسخه‌های جدیدتر استفاده می‌کند استفاده کرد.

کنترل میزان صدا با دکمه فیزیکی قبل‌از Jelly Bean

برای استفاده از کلیدهای فیزیکی میزان صدا برای کنترل کردن میزان صدای دستگاه «گیرنده وب» در دستگاه‌های Android قدیمی‌تر از Jelly Bean، برنامه فرستنده باید dispatchKeyEvent را در «فعالیت‌های» خود لغو کند و CastContext.onDispatchVolumeKeyEventBeforeJellyBean() را فراخوانی کند:

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

افزودن کنترل‌های رسانه به اعلان و صفحه قفل

فقط در Android، «فهرست بازبینی طراحی Google Cast» از برنامه فرستنده می‌خواهد که کنترل‌های رسانه را در اعلان و در صفحه قفل پیاده‌سازی کند، جایی که فرستنده درحال پخش محتوا است اما برنامه فرستنده در کانون توجه نیست. این چارچوب MediaNotificationService و MediaIntentReceiver را ارائه می‌دهد تا به برنامه فرستنده کمک کند کنترل‌های رسانه را در اعلان و در صفحه قفل بسازد.

MediaNotificationService زمانی اجرا می‌شود که فرستنده درحال پخش محتوا باشد و اعلانی با ریزعکس تصویر و اطلاعاتی درباره مورد درحال پخش، دکمه پخش/مکث، و دکمه توقف نشان می‌دهد.

‫MediaIntentReceiver یک BroadcastReceiver است که کنش‌های کاربر را از اعلان مدیریت می‌کند.

برنامه شما می‌تواند کنترل رسانه و اعلان را از صفحه قفل ازطریق NotificationOptions پیکربندی کند. برنامه شما می‌تواند دکمه‌های کنترلی را که در اعلان نشان داده می‌شود پیکربندی کند و وقتی کاربر روی اعلان ضربه می‌زند، کدام Activity باز شود. اگر کنش‌ها به‌طور صریح ارائه نشده باشند، از مقادیر پیش‌فرض، MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK و MediaIntentReceiver.ACTION_STOP_CASTING استفاده خواهد شد.

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

نمایش کنترل‌های رسانه از اعلان و صفحه قفل به‌طور پیش‌فرض روشن است و با فراخوانی setNotificationOptions با مقدار تهی در CastMediaOptions.Builder می‌توان آن را غیرفعال کرد. درحال‌حاضر، تا زمانی که اعلان روشن باشد، ویژگی صفحه قفل نیز روشن است.

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

وقتی برنامه فرستنده شما درحال پخش جاری‌سازی زنده ویدیو یا صدا است، «کیت توسعه نرم‌افزار» به‌طور خودکار دکمه پخش/توقف را به‌جای دکمه پخش/مکث در کنترل اعلان نمایش می‌دهد، اما در کنترل صفحه قفل نمایش نمی‌دهد.

توجه: برای نمایش کنترل‌های صفحه قفل در دستگاه‌های قبل‌از Lollipop، RemoteMediaClient به‌طور خودکار ازطرف شما درخواست تمرکز صوتی می‌کند.

مدیریت کردن خطاها

بسیار مهم است که برنامه‌های فرستنده همه برگشت‌های خطا را مدیریت کنند و بهترین پاسخ را برای هر مرحله از چرخه حیات «پخش محتوا» تعیین کنند. برنامه می‌تواند کادرهای گفتگوی خطا را به کاربر نمایش دهد یا می‌تواند تصمیم بگیرد که اتصال به «گیرنده وب» را قطع کند.