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

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

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

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

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

جریان برنامه

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

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

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

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

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

uses-sdk

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

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

مقداردهی اولیه «بافت Cast»

این چارچوب دارای یک شیء تک‌قلو جهانی، 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
    }
}
جاوا
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)
    }
}
جاوا
public class MyActivity extends FragmentActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        CastContext castContext = CastContext.getSharedInstance(this);
    }
}

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

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

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

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

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

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

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

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

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

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

‫Android MediaRouter API-ها برای فعال کردن نمایش و بازپخش رسانه در دستگاه‌های ثانویه طراحی شده‌اند. برنامه‌های Android که از 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" />
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، برنامه فرستنده شناسه برنامه «گیرنده وب» را مشخص می‌کند و می‌تواند به‌طور اختیاری با تنظیم 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
    }
}
جاوا
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 ایجاد و بسته می‌شوند.

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

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

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

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

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

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

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

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

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

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

چارچوب Cast کلاس 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))))
جاوا
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())
جاوا
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 فعلی فراهم می‌کند. با تک‌ضرب زدن روی کنترل‌کننده کوچک، کاربر می‌تواند به نمای کنترل‌کننده گسترده تمام‌صفحه «ارسال محتوا» برگردد.

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

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()
جاوا
// ... 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 به‌طور خودکار ازطرف شما درخواست تمرکز صوتی می‌کند.

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

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