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

این راهنمای برنامه‌نویس نحوه افزودن پشتیبانی Google Cast را با استفاده از Android Sender SDK به برنامه فرستنده Android خود توضیح می‌دهد.

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

چارچوب فرستنده به کتابخانه باینری کلاس Cast و منابع مرتبط موجود در زمان اجرا بر روی فرستنده اشاره دارد. برنامه فرستنده یا برنامه Cast به برنامه ای اشاره دارد که روی فرستنده نیز اجرا می شود. برنامه Web Receiver به برنامه HTML در حال اجرا در دستگاه دارای Cast-enabled اشاره دارد.

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

جریان برنامه

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

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

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

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

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

use-sdk

حداقل سطوح Android API را که Cast SDK پشتیبانی می‌کند، تنظیم کنید. در حال حاضر حداقل سطح API 21 و هدف سطح API 28 است.

<uses-sdk
        android:minSdkVersion="21"
        android:targetSdkVersion="28" />

android:theme

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

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

قالب Cast را مقداردهی اولیه کنید

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

برنامه شما باید رابط OptionsProvider را برای ارائه گزینه های مورد نیاز برای مقداردهی اولیه CastContext singleton پیاده سازی کند. OptionsProvider نمونه ای از CastOptions را ارائه می دهد که حاوی گزینه هایی است که بر رفتار چارچوب تأثیر می گذارد. مهمترین آنها شناسه برنامه Web Receiver است که برای فیلتر کردن نتایج کشف و راه اندازی برنامه Web Receiver هنگام شروع جلسه 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.getSharedInstance() فراخوانی می شود، CastContext به طرز تنبلی مقدار دهی اولیه می شود.

کاتلین
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 UX

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

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

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

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

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

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

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

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

یک دکمه Cast اضافه کنید

API های Android MediaRouter برای فعال کردن نمایش و پخش رسانه در دستگاه های ثانویه طراحی شده اند. برنامه‌های اندرویدی که از MediaRouter API استفاده می‌کنند باید یک دکمه 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، برنامه فرستنده شناسه برنامه Web Receiver را مشخص می کند و می تواند به صورت اختیاری با تنظیم supportedNamespaces در CastOptions ، فیلتر فضای نام را درخواست کند. CastContext به طور داخلی یک مرجع به MediaRouter دارد و فرآیند کشف را تحت شرایط زیر آغاز می کند:

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

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

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

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

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

برای درک بهتر خطاهای شروع Cast، برنامه‌ها می‌توانند از CastContext#getCastReasonCodeForCastStatusCode(int) برای تبدیل خطای شروع جلسه به CastReasonCodes استفاده کنند. لطفاً توجه داشته باشید که برخی از خطاهای شروع جلسه (مثلاً 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
    }

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

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

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

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

انتقال جریان

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

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

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

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

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

  • بهبودی از از دست دادن موقت WiFi
  • بازیابی از خواب دستگاه
  • بازیابی از پس‌زمینه برنامه
  • در صورت خراب شدن برنامه بازیابی کنید

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

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

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

کنترل رسانه چگونه کار می کند

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

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

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

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

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

کلاس MediaMetadata اطلاعاتی را در مورد یک آیتم رسانه ای که می خواهید ارسال کنید نشان می دهد. مثال زیر یک نمونه 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_* نشان داده می شود.

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

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

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

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

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

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

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

فهرست بررسی طراحی Google Cast مستلزم آن است که یک برنامه فرستنده یک کنترلر گسترده برای رسانه در حال Cast ارائه دهد. کنترلر توسعه یافته یک نسخه تمام صفحه از مینی کنترلر است.

Cast SDK ویجتی را برای کنترلر توسعه یافته به نام 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();
}

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

کاتلین
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، دکمه‌های فیزیکی دستگاه فرستنده را می‌توان برای تغییر صدای جلسه Cast در گیرنده وب به‌طور پیش‌فرض برای هر دستگاهی که از Jelly Bean یا جدیدتر استفاده می‌کند، استفاده کرد.

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

برای استفاده از کلیدهای حجم فیزیکی برای کنترل صدای دستگاه Web Receiver در دستگاه‌های Android قدیمی‌تر از Jelly Bean، برنامه فرستنده باید dispatchKeyEvent در فعالیت‌های خود لغو کند و 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);
    }
}

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

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

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

رسیدگی به خطاها

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