نقل تطبيق CCL Sender إلى إطار عمل تطبيق الإرسال (CAF)

يمكّنك الإجراء التالي من تحويل تطبيق إرسال Android من Cast SDK v2 باستخدام CCL إلى CAF. تم تنفيذ جميع وظائف CCL في CAF، لذلك بعد الترحيل، لن تحتاج إلى استخدام CCL.

تستخدم حزمة تطوير البرامج (SDK) لمُرسِل CAF في Cast CastCast لإدارة GoogleAPIClient نيابةً عنك. يُدير CastContext دورات الحياة والأخطاء ومعاودة الاتصال، ما يسهِّل إلى حد كبير تطوير تطبيق Cast.

المقدمة

  • نظرًا لتأثر تصميم مرسل CAF بمكتبة Cast Companion Library، فإن الترحيل من CCL إلى مرسل CAF يتضمن في الغالب تعيينات فردية للفئات وأساليبها.
  • لا يزال مُرسل CAF موزعًا كجزء من خدمات Google Play باستخدام مدير Android SDK.
  • وتكون الحزم الجديدة (com.google.android.gms.cast.framework.*) التي تمت إضافتها إلى مرسِل CAF، ذات وظائف مشابهة لوظيفة CCL، مسؤولة عن الالتزام بقائمة التحقّق من تصميم Google Cast.
  • يوفر CAF Sender أدوات تتوافق مع متطلبات Cast UX، وتشبه هذه الأدوات الأدوات التي تقدمها CCL.
  • يوفر CAF Sender استدعاءات غير متزامنة تشبه حالات CCL، لتتبع الحالات والحصول على البيانات. بخلاف CCL، لا يوفر CAF Sender أي عمليات تنفيذ مستقلة لطرق الواجهة المتنوعة.

في الأقسام التالية، سنركز بشكل أساسي على التطبيقات التي تركز على الفيديو استنادًا إلى VideoCastManager في CCL، ولكن في كثير من الحالات، تنطبق المفاهيم نفسها على DataCastManager أيضًا.

العناصر التابعة

لدى CCL وCAF التبعيات نفسها في مكتبة دعم AppCompat ومكتبة دعم MediaRouter v7 وخدمات Google Play. ومع ذلك، فإن الفرق هو أن CAF يعتمد على إطار عمل الإرسال الجديد المتاح في خدمات Google Play الإصدار 9.2.0 أو إصدار أحدث.

في ملف build.gradle، أزل التبعيات على com.google.android.gms:play-services-cast و com.google.android.libraries.cast.companionlibrary:ccl، ثم أضف إطار عمل الإرسال الجديد:

dependencies {
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.android.support:mediarouter-v7:23.4.0'
    compile 'com.google.android.gms:play-services-cast-framework:9.4.0'
}

يمكنك أيضًا إزالة البيانات الوصفية لخدمة Google Play:

<meta‐data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version"/>

يتم تلقائيًا دمج أي خدمات وأنشطة وموارد تشكّل جزءًا من CAF في ملف بيان التطبيق وموارده.

الحد الأدنى لإصدار Android SDK الذي يدعمه CAF هو 9 (Gingerbread)؛ الحد الأدنى لإصدار SDK لنظام Android CCL هو 10.

يوفّر CCL طريقة سهلة، BaseCastManager.checkGooglePlayServices(activity)، للتحقق من توفر إصدار متوافق من خدمات Google Play على الجهاز. ولا يوفر CAF هذا كجزء من تطبيق Cast SDK. اتَّبِع الإجراء التأكُّد من أنّ الأجهزة تتضمّن حِزمة APK لخدمات Google Play للتأكُّد من تثبيت حِزمة APK الصحيحة لخدمات Google Play على جهاز المستخدم لأنّ التحديثات قد لا تصل فورًا إلى جميع المستخدمين.

لا يزال يتعين عليك استخدام متغير من Theme.AppCompat لمظهر التطبيق.

الإعداد

بالنسبة إلى CCL، كان مطلوب استدعاء VideoCastManager.initialize() في طريقة onCreate() من مثيلات التطبيقات. يجب إزالة هذا المنطق من رمز فئة التطبيق.

في CAF، يجب أيضًا إجراء خطوة تهيئة صريحة لإطار الإرسال. ويتضمن هذا إعداد CastContext سينغلتون، باستخدام OptionsProvider المناسب لتحديد معرّف تطبيق المستلم وأي خيارات عامة أخرى. يلعب CastContext دورًا مشابهًا لدور CCL VideoCastManager من خلال توفير أغنية فردية يتفاعل معها العملاء. إنّ OptionsProvider مشابه لـ CastConfiguration الخاصّ بـ CCL ليتيح لك ضبط ميزات إطار عمل Cast.

إذا كانت قيمة CCL CastConfiguration.Builder الحالية تبدو كما يلي:

VideoCastManager.initialize(
   getApplicationContext(),
   new CastConfiguration.Builder(context.getString(R.string.app_id))
       .enableWifiReconnection()
       .enableAutoReconnect()
       .build());

ثم في CAF، سيبدو CastOptionsProvider التالي باستخدام CastOptions.Builder مشابهًا:

public class CastOptionsProvider implements OptionsProvider {

    @Override
    public CastOptions getCastOptions(Context context) {
        return new CastOptions.Builder()
                .setReceiverApplicationId(context.getString(R.string.app_id))
                .build();
    }

    @Override
    public List<SessionProvider> getAdditionalSessionProviders(
            Context context) {
        return null;
    }
}

ألق نظرة على نموذج تطبيق للحصول على التنفيذ الكامل لخيارات الخيارات.

أعلن عن OptionsProvider ضمن العنصر "application" ضمن ملف AndroidManifest.xml:

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

إعداد CastContext ببطء في كل طريقة onCreate Activity (وليس مثيل Application):

private CastContext mCastContext;

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.video_browser);
    setupActionBar();

    mCastContext = CastContext.getSharedInstance(this);
}

للدخول إلى CastContext Singleton، استخدم:

mCastContext = CastContext.getSharedInstance(this);

رصد الأجهزة

يجب إزالة VideoCastManager incrementUiCounter وdecrementUiCounter من CCL من طريقتي onResume وonPause من Activities.

في CAF، تبدأ عملية الاكتشاف وتوقفها تلقائيًا إطار الصورة عندما يظهر التطبيق في المقدّمة وينتقل إلى الخلفية على التوالي.

زر الإرسال ومربع حوار الإرسال

وكما هو الحال مع CCL، يتم توفير هذه المكونات من خلال مكتبة دعم MediaRouter v7.

لا يزال زر الإرسال قيد التنفيذ بواسطة MediaRouteButton ويمكن إضافته إلى نشاطك (باستخدام ActionBar أو Toolbar)، كعنصر في القائمة.

إعلان MediaRouteActionProvider في قائمة XML هو نفسه مع CCL:

<item
    android:id="@+id/media_route_menu_item"
    android:title="@string/media_route_menu_title"
    app:actionProviderClass="android.support.v7.app.MediaRouteActionProvider"
    app:showAsAction="always"/>

كما هو الحال مع CCL، يمكنك إلغاء طريقة onCreateOptionMenu() لكل نشاط، ولكن بدلاً من استخدام CastManager.addMediaRouterButton، يمكنك استخدام CastButtonfactory التابع لـ CAF لتوصيل إطار عمل MediaRouteButton بإطار عمل Cast:

public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    getMenuInflater().inflate(R.menu.browse, menu);
    CastButtonFactory.setUpMediaRouteButton(getApplicationContext(),
                                                menu,
                                                R.id.media_route_menu_item);
    return true;
}

التحكّم في الجهاز

وكما هو الحال مع CCL، يتم التعامل مع التحكم في الأجهزة إلى حد كبير بواسطة إطار العمل في CAF. لا يحتاج تطبيق المُرسِل إلى الاتصال (وعدم محاولة المعالجة) الاتصال بالجهاز وإطلاق تطبيق المُستلِم باستخدام GoogleApiClient.

يتم الآن تمثيل التفاعل بين المُرسِل والمُستلِم على أنه "جلسة". يعالج الصف SessionManager دورة الحياة ويبدأ الجلسات ويوقفها تلقائيًا بناءً على إيماءات المستخدم: تبدأ الجلسة عندما يحدد المستخدم جهاز البث في مربع حوار البث، وتنتهي عندما ينقر المستخدم على الزر "إيقاف الإرسال" في مربّع الحوار "إرسال" أو عندما يُنهي تطبيق المُرسِل نفسه.

في CCL، يجب تمديد الفئة VideoCastConsumerImpl لتتبع حالة جلسة الإرسال.

private final VideoCastConsumer mCastConsumer = new VideoCastConsumerImpl() {
  public void onApplicationConnected(ApplicationMetadata appMetadata, 
                                     String sessionId,
                                     boolean wasLaunched) {}
  public void onDisconnectionReason(int reason) {}
  public void onDisconnected() {}
}

في CAF، يمكن إشعار تطبيق المُرسِل بأحداث دورة الحياة من خلال تسجيل SessionManagerListener في SessionManager. تحدد استدعاءات SessionManagerListener طرق معاودة الاتصال لجميع أحداث مراحل النشاط في الجلسة.

يتم تعيين الطرق SessionManagerListener التالية من واجهة VideoCastConsumerCCL:

  • VideoCastConsumer.onApplicationConnected -> SessionManagerListener.onSessionStarted
  • VideoCastConsumer.onDisconnected -> SessionManagerListener.onSessionEnded

حدِّد فئة تستخدم واجهة SessionManagerListener وانقل المنطق VideoCastConsumerImpl إلى طرق المطابقة:

private class CastSessionManagerListener implements SessionManagerListener<CastSession> {
  public void onSessionEnded(CastSession session, int error) {}
  public void onSessionStarted(CastSession session, String sessionId) {}
  public void onSessionEnding(CastSession session) {}
  ...
}

يمثل الصف CastSession جلسة مع جهاز بث. تتوفر في الصف طرق للتحكّم في مستوى صوت الجهاز وكتم الصوت، وهو ما يحدث في CCL في BaseCastManager.

بدلاً من استخدام CCL VideoCastManager لإضافة مستهلك:

VideoCastManager.getInstance().addVideoCastConsumer(mCastConsumer);

سجِّل الآن SessionManagerListener:

mCastSessionManager = 
    CastContext.getSharedInstance(this).getSessionManager();
mCastSessionManagerListener = new CastSessionManagerListener();
mCastSessionManager.addSessionManagerListener(mCastSessionManagerListener,
                  CastSession.class);

لإيقاف الاستماع إلى الأحداث في CCL:

VideoCastManager.getInstance().removeVideoCastConsumer(mCastConsumer);

يمكنك الآن استخدام SessionManager لإيقاف الاستماع إلى أحداث الجلسات:

mCastSessionManager.removeSessionManagerListener(mCastSessionManagerListener,
                    CastSession.class);

لقطع الاتصال بجهاز البث بشكل صريح، استعانت CCL بما يلي:

VideoCastManager.disconnectDevice(boolean stopAppOnExit, 
            boolean clearPersistedConnectionData,
            boolean setDefaultRoute)

بالنسبة إلى CAF، استخدم SessionManager:

CastContext.getSharedInstance(this).getSessionManager()
                                   .endCurrentSession(true);

لتحديد ما إذا كان المُرسِل متصلاً بالمستلِم، يقدّم CCL VideoCastManager.getInstance().isConnected()، ولكن في CAF استخدم SessionManager:

public boolean isConnected() {
    CastSession castSession = CastContext.getSharedInstance(mAppContext)
                                  .getSessionManager()
                                  .getCurrentCastSession();
    return (castSession != null && castSession.isConnected());
}

في CAF، يستمر إرسال إشعارات تغيير مستوى الصوت/كتم الصوت عبر طرق معاودة الاتصال في Cast.Listener، ويتم تسجيل هؤلاء المستمعين باستخدام CastSession. يتم تسليم جميع إشعارات حالة الجهاز المتبقية عن طريق CastStateListener معاودة الاتصال، ويتم تسجيل هؤلاء المستمعين باستخدام CastSession. تأكد من أنك لا تزال تلغي تسجيل المستمعين عند انتقال الأجزاء أو الأنشطة أو التطبيقات المرتبطة إلى الخلفية.

منطق إعادة الاتصال

يحاول CAF إعادة إنشاء اتصالات الشبكة المفقودة بسبب فقدان إشارة WiFi المؤقتة أو أخطاء الشبكة الأخرى. ويتم ذلك الآن على مستوى الجلسة، ويمكن أن تُدخل الجلسة حالة "معلّقة" عند فقد الاتصال، وتنتقل مرة أخرى إلى الحالة "مرتبط" عند استعادة الاتصال. ويتولى إطار العمل هذا إعادة الاتصال بتطبيق جهاز الاستقبال وإعادة ربط أي من قنوات الإرسال كجزء من هذه العملية.

يوفر CAF خدمة إعادة الاتصال الخاصة به، حتى تتمكن من إزالة CCL ReconnectionService من البيان:

<service android:name="com.google.android.libraries.cast.companionlibrary.cast.reconnection.ReconnectionService"/>

لا تحتاج أيضًا إلى الأذونات التالية في البيان لمنطق إعادة الاتصال:

<uses‐permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses‐permission android:name="android.permission.ACCESS_WIFI_STATE"/>

يتم تفعيل خدمة إعادة الاتصال في CAF تلقائيًا، ولكن يمكن إيقافها باستخدام CastOptions.

إضافة إلى ذلك، يضيف CAF أيضًا استئنافًا تلقائيًا للجلسة يتم تمكينه تلقائيًا (ويمكن إلغاء تنشيطه عبر CastOptions). إذا تم إرسال تطبيق المرسل إلى الخلفية أو تم إنهاؤه (عن طريق التمرير السريع بعيدًا أو بسبب حدوث عطل) أثناء كون جلسة الإرسال قيد التقدم، فسيحاول إطار العمل استئناف هذه الجلسة عندما يعود تطبيق المرسل إلى المقدمة أو يتم إعادة تشغيله، وسيتم التعامل مع هذا تلقائيًا من خلال رسالة SessionManager التي سيتم فيها تسجيل أي رد مناسب على SessionManagerListener.

تسجيل قناة مخصّصة

يوفر CCL طريقتين لإنشاء قناة رسالة مخصصة إلى المستلم:

  • يتيح لك CastConfiguration تحديد مساحات أسماء متعددة، وسينشئ برنامج CCL القناة بالنيابة عنك.
  • إنّ DataCastManager مشابه لـ VideoCastManager، ولكنّه يركّز على حالات الاستخدام غير الوسائط.

لا يدعم أي من هاتين الطريقتين لإنشاء قناة مخصصة بواسطة CAF -- يجب عليك بدلاً من ذلك اتباع الإجراء إضافة قناة مخصصة لتطبيق المرسل.

وكما هو الحال مع CCL، ليس من الضروري في تطبيقات الوسائط تسجيل قناة التحكم في الوسائط بشكل صريح.

التحكم في الوسائط

في CAF، تُعادل الفئة RemoteMediaClient طرق الوسائط VideoCastManager. تُعادل RemoteMediaClient.Listener طريقة VideoCastConsumer. على وجه التحديد، تعيّن طريقتا onRemoteMediaPlayerMetadataUpdated وonRemoteMediaPlayerStatusUpdated في VideoCastConsumer طريقتَي onMetadataUpdated وonStatusUpdated لـ RemoteMediaClient.Listener على التوالي:

private class CastMediaClientListener implements RemoteMediaClient.Listener {

    @Override
    public void onMetadataUpdated() {
        setMetadataFromRemote();
    }

    @Override
    public void onStatusUpdated() {
        updatePlaybackState();
    }

    @Override
    public void onSendingRemoteMediaRequest() {
    }

    @Override
    public void onQueueStatusUpdated() {
    }

    @Override
    public void onPreloadStatusUpdated() {
    }
}

ليس من الضروري إعداد كائن RemoteMediaClient أو تسجيله بشكل صريح، حيث سينشئ إطار العمل صورة للكائنات تلقائيًا ويسجِّل قناة الوسائط الأساسية في وقت بدء الجلسة إذا كان تطبيق المستقبِل الذي يتم الاتصال به يتوافق مع مساحة اسم الوسائط.

ويمكن الوصول إلى RemoteMediaClient كطريقة getRemoteMediaClient لكائن CastSession.

CastSession castSession = CastContext.getSharedInstance(mAppContext)
                                     .getSessionManager()
                                     .getCurrentCastSession();
mRemoteMediaClient = castSession.getRemoteMediaClient();
mRemoteMediaClientListener = new CastMediaClientListener();

بدلاً من CCL:

VideoCastManager.getInstance().addVideoCastConsumer(mCastConsumer);

يمكنك الآن استخدام CAF:

mRemoteMediaClient.addListener(mRemoteMediaClientListener);

يمكن تسجيل أي عدد من المستمعين مع RemoteMediaClient، ما يسمح للعديد من مكوّنات المُرسِل بمشاركة المثيل الوحيد لـ RemoteMediaClient المرتبط بالجلسة.

يوفر VideoCastManager في CCL طرقًا للتعامل مع تشغيل الوسائط:

VideoCastManager manager = VideoCastManager.getInstance();
if (manager.isRemoteMediaLoaded()) {
    manager.pause();
    mCurrentPosition = (int) manager.getCurrentMediaPosition();
}

يتم تنفيذ هذه الآن بواسطة RemoteMediaClient في CAF:

if (mRemoteMediaClient.hasMediaSession()) {
    mRemoteMediaClient.pause();
    mCurrentPosition = 
        (int)mRemoteMediaClient.getApproximateStreamPosition();
}

في CAF، تعرض جميع طلبات الوسائط التي تم إصدارها في RemoteMediaClient RemoteMediaClient.MediaChannelResult عبر معاودة الاتصال PendingResult والتي يمكن استخدامها لتتبع مدى تقدم الطلب والنتائج النهائية.

يستخدم كل من CCL وCAF فئتي MediaInfo وMediaMetadata لتمثيل عناصر الوسائط وتحميل الوسائط.

لتحميل الوسائط في CCL، يتم استخدام VideoCastManager:

VideoCastManager.getInstance().loadMedia(media, autoPlay, mCurrentPosition, customData);

في CAF، يتم استخدام RemoteMediaClient لتحميل الوسائط:

mRemoteMediaClient.load(media, autoPlay, mCurrentPosition, customData);

للحصول على معلومات Media وحالة جلسة الوسائط الحالية على جهاز الاستقبال، تستخدم ميزة CCL VideoCastManager:

MediaInfo mediaInfo = VideoCastManager.getInstance()
                                      .getRemoteMediaInformation();
int status = VideoCastManager.getInstance().getPlaybackStatus();
int idleReason = VideoCastManager.getInstance().getIdleReason();

في CAF، استخدم RemoteMediaClient للحصول على المعلومات نفسها:

MediaInfo mediaInfo = mRemoteMediaClient.getMediaInfo();
int status = mRemoteMediaClient.getPlayerState();
int idleReason = mRemoteMediaClient.getIdleReason();

تراكب تمهيدي

على غرار CCL، يوفر CAF عرضًا مخصصًا IntroductoryOverlay لإبراز زر الإرسال عند عرضه للمرة الأولى للمستخدمين.

بدلاً من استخدام طريقة VideoCastConsumer onCastAvailabilityChanged لـ CCL لمعرفة وقت عرض التراكب، أعلن عن CastStateListener لتحديد متى يصبح زر الإرسال مرئيًا بعد أن يكتشف MediaRouter أجهزة الإرسال على الشبكة المحلية:

private IntroductoryOverlay mIntroductoryOverlay;
private MenuItem mMediaRouteMenuItem;

protected void onCreate(Bundle savedInstanceState) {
    ...
    mCastStateListener = new CastStateListener() {
        @Override
        public void onCastStateChanged(int newState) {
            if (newState != CastState.NO_DEVICES_AVAILABLE) {
                showIntroductoryOverlay();
            }
        }
    };
    mCastContext = CastContext.getSharedInstance(this);
    mCastContext.registerLifecycleCallbacksBeforeIceCreamSandwich(this, 
        savedInstanceState);
}

protected void onResume() {
    mCastContext.addCastStateListener(mCastStateListener);
    ...
}

protected void onPause() {
    mCastContext.removeCastStateListener(mCastStateListener);
    ...
}

تتبُّع مثيل MediaRouteMenuItem:

public boolean onCreateOptionsMenu(Menu menu) {
   super.onCreateOptionsMenu(menu);
    getMenuInflater().inflate(R.menu.browse, menu);
    mMediaRouteMenuItem = CastButtonFactory.setUpMediaRouteButton(
            getApplicationContext(), menu,
            R.id.media_route_menu_item);
    showIntroductoryOverlay();
    return true;
}

تحقق مما إذا كان MediaRouteButton مرئيًا بحيث يمكن عرض التراكب التمهيدي:

private void showIntroductoryOverlay() {
    if (mIntroductoryOverlay != null) {
        mIntroductoryOverlay.remove();
    }
    if ((mMediaRouteMenuItem != null) && mMediaRouteMenuItem.isVisible()) {
        new Handler().post(new Runnable() {
            @Override
            public void run() {
                mIntroductoryOverlay = new IntroductoryOverlay.Builder(
                        VideoBrowserActivity.this, mMediaRouteMenuItem)
                        .setTitleText(getString(R.string.introducing_cast))
                        .setOverlayColor(R.color.primary)
                        .setSingleTime()
                        .setOnOverlayDismissedListener(
                                new IntroductoryOverlay
                                    .OnOverlayDismissedListener() {
                                        @Override
                                        public void onOverlayDismissed() {
                                            mIntroductoryOverlay = null;
                                        }
                                })
                        .build();
                mIntroductoryOverlay.show();
            }
        });
    }
}

ألق نظرة على نموذج التطبيق للحصول على شفرة العمل الكاملة لعرض التراكب التمهيدي.

لتخصيص نمط التراكب التمهيدي، اتبع الإجراء تخصيص التراكب التمهيدي.

وحدة تحكم صغيرة

بدلاً من استخدام CCL MiniController، استخدم MiniControllerFragment CAF في ملف تخطيط التطبيق للأنشطة التي تريد عرض وحدة التحكم المصغّرة فيها:

<fragment
        android:id="@+id/cast_mini_controller"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        app:castShowImageThumbnail="true"
        android:visibility="gone"
        class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment" />

لا يدعم CAF الضبط اليدوي الذي يدعمه MiniController CCL ولا يدعم ميزة Autoplay أيضًا.

لتخصيص النمط وأزرار وحدة التحكم المصغرة، اتبع إجراء تخصيص وحدة تحكم صغيرة.

الإشعار وشاشة القفل

على غرار VideoCastNotificationService في CCL، يوفر CAF MediaNotificationService لإدارة عرض إشعارات الوسائط عند الإرسال.

يجب إزالة ما يلي من البيان:

  • VideoIntentReceiver
  • VideoCastNotificationService

يتيح CCL توفير خدمة إشعارات مخصّصة مع CastConfiguration.Builder، وهو غير متوافق مع CAF.

يجب إعداد CastManager التالية باستخدام CCL:

VideoCastManager.initialize(
   getApplicationContext(),
   new CastConfiguration.Builder(
           context.getString(R.string.app_id))
       .addNotificationAction(
           CastConfiguration.NOTIFICATION_ACTION_PLAY_PAUSE,true)
       .addNotificationAction(
           CastConfiguration.NOTIFICATION_ACTION_DISCONNECT,true)
       .build());

بالنسبة إلى الإعدادات المكافئة في CAF، توفّر حزمة تطوير البرامج (SDK) NotificationsOptions.Builder لمساعدتك على إنشاء عناصر التحكّم في الوسائط للإشعار وشاشة القفل في تطبيق المُرسِل. ويمكن تفعيل الإشعارات وعناصر التحكّم في شاشة القفل باستخدام CastOptions عند إعداد CastContext.

public CastOptions getCastOptions(Context context) {
    NotificationOptions notificationOptions = 
        new NotificationOptions.Builder()
            .setActions(Arrays.asList(
                MediaIntentReceiver.ACTION_TOGGLE_PLAYBACK,
                MediaIntentReceiver.ACTION_STOP_CASTING), new int[]{0, 1})
            .build();
    CastMediaOptions mediaOptions = new CastMediaOptions.Builder()
             .setNotificationOptions(notificationOptions)
             .build();
    return new CastOptions.Builder()
             .setReceiverApplicationId(context.getString(R.string.app_id))
             .setCastMediaOptions(mediaOptions)
             .build();
}

تكون الإشعارات وعناصر التحكم في شاشة القفل مفعّلة دائمًا في CAF. لاحظ أيضًا أنه يتم توفير أزرار التشغيل/الإيقاف المؤقت وإيقاف الإرسال بشكل افتراضي. سيتتبع CAF مستوى رؤية الأنشطة تلقائيًا لتحديد وقت عرض إشعار الوسائط، باستثناء Gingerbread. (بالنسبة إلى Gingerbread، راجع الملاحظة السابقة حول استخدام registerLifecycleCallbacksBeforeIceCreamSandwich()، ومكالمات CCL VideoCastManager incrementUiCounter، وdecrementUiCounter يجب إزالتها.)

لتخصيص الأزرار التي يتم عرضها في الإشعارات، اتبع الإجراء إضافة عناصر تحكم في الوسائط إلى الإشعارات وشاشة القفل.

وحدة تحكم موسعة

توفّر CCL VideoCastControllerActivity وVideoCastControllerFragment لعرض وحدة تحكم موسّعة عند إرسال الوسائط.

يمكنك إزالة تعريف VideoCastControllerActivity في البيان.

في CAF، يجب توسيع قائمة التشغيل الموسّعة وإضافة زر الإرسال.

لتخصيص الأنماط والأزرار التي يتم عرضها في وحدة التحكم الموسَّعة، اتبع الإجراء تخصيص وحدة التحكم الموسعة.

التركيز على الصوت

كما هو الحال مع CCL، تتم إدارة تركيز الصوت تلقائيًا.

التحكم في مستوى الصوت

بالنسبة إلى Gingerbread، يجب توفير dispatchKeyEvent كما هو الحال مع CCL. في ICS والإصدارات الأحدث بالنسبة إلى التحكم في مستوى صوت CCL وCAF، يتم التعامل معهما تلقائيًا.

يتيح CAF التحكم في مستوى صوت الإرسال من خلال زر مستوى الصوت الثابت على الهاتف داخل أنشطة التطبيقات، كما يعرض شريط مستوى الصوت المرئي عند الإرسال إلى الإصدارات المتوافقة. يتعامل CAF أيضًا مع تغيير مستوى الصوت من خلال مستوى الصوت الثابت حتى إذا لم يكن تطبيقك في المقدمة، أو كان مغلقًا، أو حتى عندما تكون الشاشة مغلقة.

الترجمة والشرح

في نظام التشغيل Android KitKat وأحدث، يمكن تخصيص التسميات التوضيحية من خلال إعدادات التسميات التوضيحية، الموجودة ضمن إعدادات > إمكانية الوصول. إلا أن الإصدارات السابقة من Android لا تتضمن هذه الإمكانية. يتعامل CCL مع هذا من خلال توفير إعدادات مخصصة للإصدارات السابقة وتفويض إعدادات النظام على KitKat والإصدارات الأحدث.

لا يوفر CAF إعدادات مخصصة لتغيير تفضيلات التسميات التوضيحية. عليك إزالة المراجع CaptionsPreferenceActivity في ملف البيان وتفضيلات XML.

لم تعد قناة TracksChooserDialog التابعة لـ CCL مطلوبة لأن تغيير واجهة مقاطع الترجمة والشرح تتم معالجته بواسطة واجهة مستخدم وحدة التحكم الموسّعة.

إنّ واجهة برمجة التطبيقات للتسميات التوضيحية المغلقة في CAF مماثلة للإصدار 2.

تسجيل تصحيح الأخطاء

لا يوفر CAF إعدادات تسجيل تصحيح الأخطاء.

ميزات متنوّعة

لا تتوفّر ميزات CCL التالية في CAF:

  • جارٍ الحصول على تفويض قبل التشغيل من خلال تقديم MediaAuthService
  • رسائل واجهة المستخدم القابلة للتهيئة

نماذج تطبيقات

يمكنك الاطّلاع على الفارق في نقل نموذج التطبيق Universal Music Player لـ Android (Uamp) من CCL إلى CAF.

لدينا أيضًا برامج تعليمية حول الترميز ونماذج من التطبيقات التي تستخدم CAF.