Cast را در برنامه اندروید خود ادغام کنید

این راهنمای توسعه‌دهنده نحوه‌ی افزودن پشتیبانی گوگل کست به برنامه‌ی فرستنده‌ی اندروید شما را با استفاده از SDK فرستنده‌ی اندروید شرح می‌دهد.

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

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

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

جریان برنامه

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

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

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

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

فایل AndroidManifest.xml برنامه شما از شما می‌خواهد که عناصر زیر را برای Cast SDK پیکربندی کنید:

از SDK استفاده می‌کند

حداقل و سطح API اندروید مورد پشتیبانی Cast SDK را تعیین کنید. در حال حاضر حداقل سطح API 23 و سطح API هدف 34 است.

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

اندروید:تم

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

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

مقداردهی اولیه‌ی زمینه‌ی تبدیل (cast context)

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

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

کاتلین
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
    }
}
جاوا
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() به صورت تنبل مقداردهی اولیه می‌شود.

کاتلین
class MyActivity : FragmentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        val castContext = CastContext.getSharedInstance(this)
    }
}
جاوا
public class MyActivity extends FragmentActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        CastContext castContext = CastContext.getSharedInstance(this);
    }
}

ابزارک‌های تجربه کاربری کست

چارچوب Cast ویجت‌هایی را ارائه می‌دهد که با چک‌لیست طراحی Cast مطابقت دارند:

  • پوشش مقدماتی : این چارچوب یک نمای سفارشی به نام IntroductoryOverlay ارائه می‌دهد که برای جلب توجه کاربر به دکمه‌ی ارسال (Cast) در اولین باری که گیرنده در دسترس قرار می‌گیرد، به او نشان داده می‌شود. برنامه‌ی فرستنده می‌تواند متن و موقعیت متن عنوان را سفارشی کند .

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

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

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

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

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

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

دکمه‌ی ارسال محتوا (Cast) اضافه کنید

رابط‌های برنامه‌نویسی کاربردی (API) اندروید MediaRouter برای فعال کردن نمایش و پخش رسانه در دستگاه‌های ثانویه طراحی شده‌اند. برنامه‌های اندرویدی که از رابط برنامه‌نویسی کاربردی MediaRouter استفاده می‌کنند، باید یک دکمه‌ی Cast را به عنوان بخشی از رابط کاربری خود داشته باشند تا به کاربران امکان انتخاب مسیر رسانه برای پخش رسانه در دستگاه ثانویه مانند دستگاه 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" />
کاتلین
// 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>
کاتلین
// 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);
}

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

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

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

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

فرآیند کشف با بسته شدن کادر محاوره‌ای Cast یا ورود برنامه فرستنده به پس‌زمینه متوقف خواهد شد.

کاتلین
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
    }
}
جاوا
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 مفهوم یک جلسه Cast را معرفی می‌کند که ایجاد آن مراحل اتصال به یک دستگاه، راه‌اندازی (یا پیوستن) به یک برنامه گیرنده وب، اتصال به آن برنامه و مقداردهی اولیه یک کانال کنترل رسانه را ترکیب می‌کند. برای اطلاعات بیشتر در مورد جلسات Cast و چرخه عمر گیرنده وب، به راهنمای چرخه عمر برنامه گیرنده وب مراجعه کنید.

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

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

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

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

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

کاتلین
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)
    }
}
جاوا
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 با جدیدترین سیستم عامل می‌تواند به عنوان منبع یا مقصد در انتقال جریان عمل کند.

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

برای اطلاعات بیشتر به بخش انتقال جریان در گیرنده وب مراجعه کنید.

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

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

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

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

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

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

نحوه کار کنترل رسانه

چارچوب Cast کلاس RemoteMediaPlayer را از Cast 2.x منسوخ کرده و کلاس جدید RemoteMediaClient جایگزین آن کرده است که همان عملکرد را در مجموعه‌ای از APIهای راحت‌تر ارائه می‌دهد و از ارسال GoogleApiClient جلوگیری می‌کند.

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

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

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

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

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

کاتلین
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))))
جاوا
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 برای پخش، مکث و کنترل برنامه پخش رسانه‌ای که روی گیرنده وب اجرا می‌شود، استفاده کنید.

کاتلین
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())
جاوا
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_* نشان داده می‌شوند.

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

وقتی کاربری در حال پخش محتوا است، سایر دستگاه‌های اندروید در همان شبکه نیز اعلانی دریافت می‌کنند که به آنها اجازه می‌دهد پخش را کنترل کنند. هر کسی که دستگاهش چنین اعلان‌هایی را دریافت می‌کند، می‌تواند آنها را برای آن دستگاه در برنامه تنظیمات در Google > Google Cast > Show remote control notifications غیرفعال کند. (این اعلان‌ها شامل یک میانبر به برنامه تنظیمات هستند.) برای جزئیات بیشتر، به اعلان‌های Cast remote control مراجعه کنید.

اضافه کردن مینی کنترلر

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

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

<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 به طور خودکار یک دکمه پخش/توقف را به جای دکمه پخش/مکث در مینی کنترلر نمایش می‌دهد.

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

اضافه کردن کنترلر توسعه‌یافته

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

کیت توسعه نرم‌افزار Cast یک ویجت برای کنترلر توسعه‌یافته به نام ExpandedControllerActivity ارائه می‌دهد. این یک کلاس انتزاعی است که برای افزودن دکمه Cast باید از آن زیرکلاس بسازید.

ابتدا، یک فایل منبع منو جدید برای کنترلر توسعه‌یافته ایجاد کنید تا دکمه Cast را ارائه دهد:

<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 ارث‌بری کند.

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

کاتلین
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()
}
جاوا
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();
}

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

کاتلین
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 به طور خودکار یک دکمه پخش/توقف را به جای دکمه پخش/مکث در کنترلر گسترش‌یافته نمایش می‌دهد.

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

کنترل صدا

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

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

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

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

برای استفاده از کلیدهای فیزیکی تنظیم صدا برای کنترل میزان صدای دستگاه گیرنده وب در دستگاه‌های اندروید قدیمی‌تر از Jelly Bean، برنامه فرستنده باید dispatchKeyEvent در Activityهای خود override کند و CastContext.onDispatchVolumeKeyEventBeforeJellyBean() را فراخوانی کند:

کاتلین
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);
    }
}

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

فقط در اندروید، چک لیست طراحی گوگل کست (Google Cast Design Checklist) از یک برنامه فرستنده می‌خواهد که کنترل‌های رسانه‌ای را در یک اعلان و در صفحه قفل ، جایی که فرستنده در حال ارسال است اما برنامه فرستنده فوکوس ندارد، پیاده‌سازی کند. این چارچوب MediaNotificationService و MediaIntentReceiver را برای کمک به برنامه فرستنده در ساخت کنترل‌های رسانه‌ای در یک اعلان و در صفحه قفل ارائه می‌دهد.

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

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

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

کاتلین
// 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()
جاوا
// 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 با مقدار null در CastMediaOptions.Builder آن‌ها را غیرفعال کرد. در حال حاضر، ویژگی صفحه قفل تا زمانی که اعلان روشن باشد، فعال است.

کاتلین
// ... 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()
جاوا
// ... 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();

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

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

مدیریت خطاها

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