إنشاء مقدّم وسائط في السحابة الإلكترونية لنظام التشغيل Android

يوفّر مقدّم خدمات الوسائط في السحابة الإلكترونية محتوًى إضافيًا من الوسائط في السحابة الإلكترونية إلى أداة اختيار الصور على Android. ويمكن للمستخدمين اختيار الصور أو الفيديوهات التي يوفّرها مقدّم خدمات الوسائط في السحابة الإلكترونية عندما يستخدم التطبيق ACTION_PICK_IMAGES أو ACTION_GET_CONTENT لطلب ملفات الوسائط من المستخدم. كما يمكن لمقدّم وسائط السحابة الإلكترونية تقديم معلومات حول الألبومات التي يمكن تصفّحها في "أداة اختيار الصور" على Android.

قبل البدء

ضع العناصر التالية في الاعتبار قبل البدء في إنشاء مزود وسائط السحابة.

الأهلية

يشغِّل Android برنامجًا تجريبيًا للسماح للتطبيقات التي تم ترشيحها من قِبل المصنّع الأصلي للجهاز بأن تصبح مقدّمي خدمات وسائط على السحابة الإلكترونية. التطبيقات التي يرشّحها المصنّعون الأصليون للأجهزة فقط مؤهّلة للمشاركة في هذا البرنامج لتصبح مقدّم وسائط سحابية لنظام Android في الوقت الحالي. ويمكن لكل مصنّع أصلي للجهاز ترشيح ما يصل إلى 3 تطبيقات. بعد الموافقة على هذه التطبيقات، سيصبح بإمكانها الوصول إليها كمزوّدي وسائط سحابة إلكترونية على أي جهاز "خدمات Google للأجهزة الجوّالة" (GMS) تم تثبيت هذه التطبيقات عليه.

يحتفظ Android بقائمة من جانب الخادم تضم جميع مقدّمي خدمات السحابة الإلكترونية المؤهَّلين. ويمكن لكل مصنّع أصلي للجهاز اختيار مقدّم خدمات سحابة إلكترونية تلقائي باستخدام تراكب قابل للتهيئة. ويجب أن تستوفي التطبيقات المرشَّحة جميع المتطلبات الفنية وأن تجتاز جميع اختبارات الجودة. لمعرفة المزيد حول عملية البرنامج التجريبي لمزوّد خدمات الوسائط السحابية لدى المصنّع الأصلي للجهاز ومتطلباته، يُرجى ملء نموذج الاستفسار.

تحديد ما إذا كنت بحاجة إلى إنشاء مقدّم خدمات وسائط في السحابة الإلكترونية

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

مقدّم خدمات سحابة إلكترونية نشط واحد لكل ملف تجاري

يمكن أن يكون هناك مقدّم وسائط نشط واحد على الأكثر في السحابة الإلكترونية في كل مرة لكل ملف شخصي على Android. يمكن للمستخدمين إزالة أو تغيير التطبيق المحدّد لمقدّم خدمة الوسائط السحابية في أي وقت من خلال إعدادات "أداة اختيار الصور".

سيحاول "أداة اختيار الصور" على Android اختيار مقدّم خدمات السحابة الإلكترونية تلقائيًا.

  • إذا كان هناك مقدّم خدمات سحابية مؤهل واحد فقط على الجهاز، سيتم تلقائيًا اختيار هذا التطبيق كمقدّم خدمات حالي.
  • إذا كان هناك أكثر من مقدّم خدمات سحابة إلكترونية مؤهَّل واحد على الجهاز وكان أحدهم يتطابق مع الخيار التلقائي الذي اختاره المصنّع الأصلي للجهاز، سيتم اختيار التطبيق الذي يختاره المصنّع الأصلي للجهاز.

  • إذا كان هناك أكثر من مقدّم خدمات سحابة إلكترونية مؤهَّل واحد على الجهاز، ولم يتطابق أي منهم مع الخيار التلقائي الذي اختاره المصنّع الأصلي للجهاز، لن يتم اختيار أي تطبيق.

إنشاء مقدّم خدمات الوسائط في السحابة الإلكترونية

يوضِّح المخطّط التالي تسلسل الأحداث قبل وأثناء جلسة اختيار الصور بين تطبيق Android وأداة اختيار الصور من Android وMediaProvider على الجهاز المحلي وCloudMediaProvider.

رسم بياني للتسلسل يعرض التدفق من أداة اختيار الصور إلى مقدّم خدمات وسائط في السحابة الإلكترونية
الشكل 1: رسم تخطيطي لتسلسل الأحداث أثناء جلسة اختيار صور.
  1. يبدأ النظام إعداد السحابة الإلكترونية المفضّل لدى المستخدم ويزامن بشكل دوري البيانات الوصفية للوسائط مع الواجهة الخلفية لأداة اختيار الصور على Android.
  2. عندما يشغِّل أحد تطبيقات Android "أداة اختيار الصور"، قبل عرض شبكة عناصر محلية أو مدمجة على السحابة الإلكترونية للمستخدم، تُجري "أداة اختيار الصور" مزامنة متزايدة حساسة لوقت الاستجابة مع مقدّم خدمات السحابة الإلكترونية لضمان عرض أحدث النتائج قدر الإمكان. بعد تلقّي رد أو عند انقضاء الموعد النهائي، تعرض شبكة أداة اختيار الصور الآن جميع الصور التي يمكن الوصول إليها، حيث تجمع تلك الصور المخزنة محليًا على جهازك مع الصور التي تمت مزامنتها من السحابة الإلكترونية.
  3. أثناء قيام المستخدم بالتمرير، يجلب "أداة اختيار الصور" الصور المصغّرة للوسائط من مقدّم خدمة الوسائط في السحابة الإلكترونية لعرضها في واجهة المستخدم.
  4. عندما يُكمل المستخدم الجلسة وتكون النتائج تتضمّن عنصر وسائط سحابية، يطلب منتقي الصور واصفات الملفات للمحتوى، وينشئ معرّف موارد منتظمًا، ويمنح تطبيق الاتصال إمكانية الوصول إلى الملف.
  5. يمكن للتطبيق الآن فتح معرّف الموارد المنتظم (URI) والوصول إلى محتوى الوسائط بالقراءة فقط. يتم تلقائيًا إخفاء البيانات الوصفية الحسّاسة. تستفيد أداة اختيار الصور من نظام ملفات FUSE لتنسيق تبادل البيانات بين تطبيق Android وموفِّر خدمة الوسائط على السحابة الإلكترونية.

المشاكل الشائعة

فيما يلي بعض الاعتبارات المهمة التي يجب وضعها في الاعتبار عند النظر في التنفيذ الخاص بك:

تجنب الملفات المكررة

بما أنّ "أداة اختيار الصور" على Android لا تتوفّر لها أي طريقة لفحص حالة الوسائط على السحابة الإلكترونية، يحتاج CloudMediaProvider إلى توفير MEDIA_STORE_URI في صف المؤشر لأي ملف متوفّر على السحابة الإلكترونية وعلى الجهاز المحلي، وإلا سيرى المستخدم ملفات مكرّرة في "أداة اختيار الصور".

تحسين أحجام الصور لعرض المعاينة

من المهم جدًا ألا يكون الملف الذي يتم عرضه من onOpenPreview بالدقة الكاملة، وأن يتوافق مع Size المطلوبة. ستستغرق الصورة الكبيرة جدًا أوقات تحميل في واجهة المستخدم، وقد تتقطّع الصورة الصغيرة جدًا أو تكون غير واضحة بناءً على حجم شاشة الجهاز.

التعامل مع الاتجاه الصحيح

إذا كانت الصور المصغّرة التي تم عرضها في onOpenPreview لا تحتوي على بيانات EXIF، يجب عرضها بالاتجاه الصحيح لتجنُّب تدوير الصور المصغّرة على نحو غير صحيح في شبكة المعاينة.

منع الوصول غير المصرّح به

ابحث عن MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION قبل عرض البيانات للمتصل من ContentProvider. سيؤدي ذلك إلى منع التطبيقات غير المُصرَّح لها من الوصول إلى البيانات السحابية.

فئة CloudMediaProvider

تشتمل الفئة CloudMediaProvider المشتقة من android.content.ContentProvider، على طُرق مثل تلك المعروضة في المثال التالي:

Kotlin

abstract class CloudMediaProvider : ContentProvider() {

    @NonNull
    abstract override fun onGetMediaCollectionInfo(@NonNull bundle: Bundle): Bundle

    @NonNull
    override fun onQueryAlbums(@NonNull bundle: Bundle): Cursor = TODO("Implement onQueryAlbums")

    @NonNull
    abstract override fun onQueryDeletedMedia(@NonNull bundle: Bundle): Cursor

    @NonNull
    abstract override fun onQueryMedia(@NonNull bundle: Bundle): Cursor

    @NonNull
    abstract override fun onOpenMedia(
        @NonNull string: String,
        @Nullable bundle: Bundle?,
        @Nullable cancellationSignal: CancellationSignal?
    ): ParcelFileDescriptor

    @NonNull
    abstract override fun onOpenPreview(
        @NonNull string: String,
        @NonNull point: Point,
        @Nullable bundle: Bundle?,
        @Nullable cancellationSignal: CancellationSignal?
    ): AssetFileDescriptor

    @Nullable
    override fun onCreateCloudMediaSurfaceController(
        @NonNull bundle: Bundle,
        @NonNull callback: CloudMediaSurfaceStateChangedCallback
    ): CloudMediaSurfaceController? = null
}

Java

public abstract class CloudMediaProvider extends android.content.ContentProvider {

  @NonNull
  public abstract android.os.Bundle onGetMediaCollectionInfo(@NonNull android.os.Bundle);

  @NonNull
  public android.database.Cursor onQueryAlbums(@NonNull android.os.Bundle);

  @NonNull
  public abstract android.database.Cursor onQueryDeletedMedia(@NonNull android.os.Bundle);

  @NonNull
  public abstract android.database.Cursor onQueryMedia(@NonNull android.os.Bundle);

  @NonNull
  public abstract android.os.ParcelFileDescriptor onOpenMedia(@NonNull String, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException;

  @NonNull
  public abstract android.content.res.AssetFileDescriptor onOpenPreview(@NonNull String, @NonNull android.graphics.Point, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException;

  @Nullable
  public android.provider.CloudMediaProvider.CloudMediaSurfaceController onCreateCloudMediaSurfaceController(@NonNull android.os.Bundle, @NonNull android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback);
}

فئة CloudMediaProviderContract

بالإضافة إلى فئة تنفيذ CloudMediaProvider الأساسية، تتضمّن أداة اختيار الصور على Android فئة CloudMediaProviderContract. توضّح هذه الفئة إمكانية التشغيل التفاعلي بين "أداة اختيار الصور" ومقدِّم خدمات الوسائط في السحابة الإلكترونية، بما في ذلك جوانب مثل MediaCollectionInfo لعمليات المزامنة وأعمدة Cursor المتوقّعة وBundle من العناصر الإضافية.

Kotlin

object CloudMediaProviderContract {

    const val EXTRA_ALBUM_ID = "android.provider.extra.ALBUM_ID"
    const val EXTRA_LOOPING_PLAYBACK_ENABLED = "android.provider.extra.LOOPING_PLAYBACK_ENABLED"
    const val EXTRA_MEDIA_COLLECTION_ID = "android.provider.extra.MEDIA_COLLECTION_ID"
    const val EXTRA_PAGE_SIZE = "android.provider.extra.PAGE_SIZE"
    const val EXTRA_PAGE_TOKEN = "android.provider.extra.PAGE_TOKEN"
    const val EXTRA_PREVIEW_THUMBNAIL = "android.provider.extra.PREVIEW_THUMBNAIL"
    const val EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED = "android.provider.extra.SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED"
    const val EXTRA_SYNC_GENERATION = "android.provider.extra.SYNC_GENERATION"
    const val MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION = "com.android.providers.media.permission.MANAGE_CLOUD_MEDIA_PROVIDERS"
    const val PROVIDER_INTERFACE = "android.content.action.CLOUD_MEDIA_PROVIDER"

    object MediaColumns {
        const val DATE_TAKEN_MILLIS = "date_taken_millis"
        const val DURATION_MILLIS = "duration_millis"
        const val HEIGHT = "height"
        const val ID = "id"
        const val IS_FAVORITE = "is_favorite"
        const val MEDIA_STORE_URI = "media_store_uri"
        const val MIME_TYPE = "mime_type"
        const val ORIENTATION = "orientation"
        const val SIZE_BYTES = "size_bytes"
        const val STANDARD_MIME_TYPE_EXTENSION = "standard_mime_type_extension"
        const val STANDARD_MIME_TYPE_EXTENSION_ANIMATED_WEBP = 3 // 0x3
        const val STANDARD_MIME_TYPE_EXTENSION_GIF = 1 // 0x1
        const val STANDARD_MIME_TYPE_EXTENSION_MOTION_PHOTO = 2 // 0x2
        const val STANDARD_MIME_TYPE_EXTENSION_NONE = 0 // 0x0
        const val SYNC_GENERATION = "sync_generation"
        const val WIDTH = "width"
    }

    object AlbumColumns {
        const val DATE_TAKEN_MILLIS = "date_taken_millis"
        const val DISPLAY_NAME = "display_name"
        const val ID = "id"
        const val MEDIA_COUNT = "album_media_count"
        const val MEDIA_COVER_ID = "album_media_cover_id"
    }

    object MediaCollectionInfo {
        const val ACCOUNT_CONFIGURATION_INTENT = "account_configuration_intent"
        const val ACCOUNT_NAME = "account_name"
        const val LAST_MEDIA_SYNC_GENERATION = "last_media_sync_generation"
        const val MEDIA_COLLECTION_ID = "media_collection_id"
    }
}

Java

public final class CloudMediaProviderContract {

  public static final String EXTRA_ALBUM_ID = "android.provider.extra.ALBUM_ID";
  public static final String EXTRA_LOOPING_PLAYBACK_ENABLED = "android.provider.extra.LOOPING_PLAYBACK_ENABLED";
  public static final String EXTRA_MEDIA_COLLECTION_ID = "android.provider.extra.MEDIA_COLLECTION_ID";
  public static final String EXTRA_PAGE_SIZE = "android.provider.extra.PAGE_SIZE";
  public static final String EXTRA_PAGE_TOKEN = "android.provider.extra.PAGE_TOKEN";
  public static final String EXTRA_PREVIEW_THUMBNAIL = "android.provider.extra.PREVIEW_THUMBNAIL";
  public static final String EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED = "android.provider.extra.SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED";
  public static final String EXTRA_SYNC_GENERATION = "android.provider.extra.SYNC_GENERATION";
  public static final String MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION = "com.android.providers.media.permission.MANAGE_CLOUD_MEDIA_PROVIDERS";
  public static final String PROVIDER_INTERFACE = "android.content.action.CLOUD_MEDIA_PROVIDER";
}

// Columns available for every media item
public static final class CloudMediaProviderContract.MediaColumns {

  public static final String DATE_TAKEN_MILLIS = "date_taken_millis";
  public static final String DURATION_MILLIS = "duration_millis";
  public static final String HEIGHT = "height";
  public static final String ID = "id";
  public static final String IS_FAVORITE = "is_favorite";
  public static final String MEDIA_STORE_URI = "media_store_uri";
  public static final String MIME_TYPE = "mime_type";
  public static final String ORIENTATION = "orientation";
  public static final String SIZE_BYTES = "size_bytes";
  public static final String STANDARD_MIME_TYPE_EXTENSION = "standard_mime_type_extension";
  public static final int STANDARD_MIME_TYPE_EXTENSION_ANIMATED_WEBP = 3; // 0x3
  public static final int STANDARD_MIME_TYPE_EXTENSION_GIF = 1; // 0x1 
  public static final int STANDARD_MIME_TYPE_EXTENSION_MOTION_PHOTO = 2; // 0x2 
  public static final int STANDARD_MIME_TYPE_EXTENSION_NONE = 0; // 0x0 
  public static final String SYNC_GENERATION = "sync_generation";
  public static final String WIDTH = "width";
}

// Columns available for every album item
public static final class CloudMediaProviderContract.AlbumColumns {

  public static final String DATE_TAKEN_MILLIS = "date_taken_millis";
  public static final String DISPLAY_NAME = "display_name";
  public static final String ID = "id";
  public static final String MEDIA_COUNT = "album_media_count";
  public static final String MEDIA_COVER_ID = "album_media_cover_id";
}

// Media Collection metadata that is cached by the OS to compare sync states.
public static final class CloudMediaProviderContract.MediaCollectionInfo {

  public static final String ACCOUNT_CONFIGURATION_INTENT = "account_configuration_intent";
  public static final String ACCOUNT_NAME = "account_name";
  public static final String LAST_MEDIA_SYNC_GENERATION = "last_media_sync_generation";
  public static final String MEDIA_COLLECTION_ID = "media_collection_id";
}

onGetMediaCollectionInfo

يستخدم نظام التشغيل الطريقة onGetMediaCollectionInfo() لتقييم صلاحية عناصر الوسائط في السحابة الإلكترونية المخزَّنة مؤقتًا وتحديد المزامنة اللازمة مع مقدّم خدمة الوسائط في السحابة الإلكترونية. نظرًا لاحتمالية تلقّي طلبات متكررة من نظام التشغيل، يُعتبر onGetMediaCollectionInfo() أمرًا بالغ الأهمية للأداء، ومن الضروري تجنُّب العمليات الطويلة الأمد أو الآثار الجانبية التي قد تؤثر سلبًا على الأداء. يخزّن نظام التشغيل الردود السابقة من هذه الطريقة في ذاكرة التخزين المؤقت ويقارنها بالاستجابات اللاحقة لتحديد الإجراءات المناسبة.

Kotlin

abstract fun onGetMediaCollectionInfo(extras: Bundle): Bundle

Java

@NonNull
public abstract Bundle onGetMediaCollectionInfo(@NonNull Bundle extras);

تشتمل حزمة MediaCollectionInfo التي تم إرجاعها على الثوابت التالية:

onQueryMedia

تُستخدَم طريقة onQueryMedia() لتعبئة شبكة الصور الرئيسية في أداة اختيار الصور ضمن مجموعة متنوعة من طرق العرض. قد تكون هذه الطلبات حساسة لوقت الاستجابة، ويمكن استدعاؤها كجزء من المزامنة الاستباقية في الخلفية أو أثناء جلسات "أداة اختيار الصور" عندما تكون حالة المزامنة الكاملة أو التزايدية مطلوبة. ولن تنتظر واجهة مستخدم "أداة اختيار الصور" ردًّا لعرض النتائج إلى أجل غير مسمى، وقد تنتهي مهلة هذه الطلبات لأغراض متعلقة بواجهة المستخدم. ستستمر محاولة المؤشر الذي تم إرجاعه في قاعدة بيانات منتقي الصور للجلسات المستقبلية.

تعرض هذه الطريقة Cursor الذي يمثّل جميع عناصر الوسائط في مجموعة الوسائط، وتتم فلترتها اختياريًا حسب الميزات الإضافية المتوفّرة، ويتم ترتيبها بترتيب زمني عكسي حسب MediaColumns#DATE_TAKEN_MILLIS (أحدث العناصر أولاً).

تشتمل حزمة CloudMediaProviderContract التي تم إرجاعها على الثبات التالية:

على مقدّم خدمات الوسائط السحابية ضبط CloudMediaProviderContract#EXTRA_MEDIA_COLLECTION_ID كجزء من Bundle التي يتم عرضها. يُعد عدم ضبط ذلك خطأً ويؤدي إلى إلغاء صلاحية Cursor التي تم عرضها. إذا تعامل مقدّم خدمات الوسائط السحابية مع أي فلاتر في العناصر الإضافية المتوفرة، عليه إضافة المفتاح إلى ContentResolver#EXTRA_HONORED_ARGS كجزء من Cursor#setExtras التي يتم عرضها.

onQueryDeletedMedia

يتم استخدام طريقة onQueryDeletedMedia() لضمان إزالة العناصر المحذوفة في الحساب السحابي بشكل صحيح من واجهة مستخدم "أداة اختيار الصور". وبسبب حساسية وقت الاستجابة المحتملة، يمكن بدء هذه الطلبات كجزء من:

  • المزامنة الاستباقية في الخلفية
  • جلسات أداة اختيار الصور (عندما تكون حالة المزامنة الكاملة أو التزايدية مطلوبة)

تمنح واجهة المستخدم في "أداة اختيار الصور" الأولوية لتجربة المستخدم المتجاوبة مع مختلف الأجهزة، ولن يتم الانتظار إلى أجل غير مسمى لتلقّي رد. للحفاظ على تفاعلات سلسة، قد تحدث مهلات. وستحاول معالجة أي قيمة Cursor تم إرجاعها في قاعدة بيانات أداة اختيار الصور خلال الجلسات المستقبلية.

تعرض هذه الطريقة Cursor الذي يمثّل جميع ملفات الوسائط المحذوفة في مجموعة الوسائط بالكامل ضمن إصدار مقدّم الخدمة الحالي كما يعرضها onGetMediaCollectionInfo(). يمكن فلترة هذه العناصر حسب الميزات الإضافية اختياريًا. على مقدّم خدمة الوسائط السحابية ضبط CloudMediaProviderContract#EXTRA_MEDIA_COLLECTION_ID كجزء من البيانات التي يتم عرضها. Cursor#setExtras في حال عدم ضبط هذا، يتم اعتبار أنّ هذا خطأ يؤدي إلى إلغاء صلاحية Cursor. إذا تعامل موفّر الخدمة مع أي فلاتر في الإضافات المتوفرة، عليه إضافة المفتاح إلى ContentResolver#EXTRA_HONORED_ARGS.

onQueryAlbums

يتم استخدام طريقة onQueryAlbums() لجلب قائمة بألبومات السحابة الإلكترونية المتاحة لدى مقدّم خدمات السحابة الإلكترونية والبيانات الوصفية المرتبطة بها. يمكنك الاطّلاع على CloudMediaProviderContract.AlbumColumns للحصول على تفاصيل إضافية.

تعرض هذه الطريقة علامة Cursor التي تمثّل جميع عناصر الألبوم في مجموعة الوسائط التي تتم فلترتها بشكل اختياري حسب الميزات الإضافية المتوفّرة ويتم ترتيبها بترتيب زمني عكسي حسب AlbumColumns#DATE_TAKEN_MILLIS، وهي أحدث العناصر أولاً. على مقدّم خدمات الوسائط السحابية ضبط CloudMediaProviderContract#EXTRA_MEDIA_COLLECTION_ID كجزء من Cursor التي يتم عرضها. يُعد عدم ضبط ذلك خطأً ويؤدي إلى إلغاء صلاحية Cursor التي تم عرضها. إذا تعامل المزوّد مع أي فلاتر في الإضافات المتوفرة، عليه إضافة المفتاح إلى ContentResolver#EXTRA_HONORED_ARGS كجزء من Cursor التي يتم عرضها.

على OpenMedia

من المفترض أن تعرض الطريقة onOpenMedia() الوسائط بالحجم الكامل المحدّدة من خلال السمة mediaId المتوفرة. إذا تم حظر هذه الطريقة أثناء تنزيل المحتوى على الجهاز، يجب التحقق بشكل دوري من علامة CancellationSignal المتوفرة لإلغاء الطلبات التي تم التخلي عنها.

onOpenPreview

من المفترض أن تعرض الطريقة onOpenPreview() صورة مصغّرة من size المتوفرة لعنصر رقم تعريف الوسائط المقدَّم. يجب أن تكون الصورة المصغّرة بتنسيق CloudMediaProviderContract.MediaColumns#MIME_TYPE الأصلي، ومن المتوقّع أن تكون درجة دقة الصورة المصغّرة أقل بكثير من جودة الصورة التي يعرضها onOpenMedia. إذا تم حظر هذه الطريقة أثناء تنزيل المحتوى على الجهاز، عليك التحقق بشكل دوري من علامة CancellationSignal المتوفرة لإلغاء الطلبات التي تم إلغاؤها.

وحدة تحكم onCreateCloudMediaSurfaceController

من المفترض أن تعرض طريقة onCreateCloudMediaSurfaceController() القيمة CloudMediaSurfaceController المستخدمة لعرض معاينة عناصر الوسائط أو null إذا كان عرض المعاينة غير متوافق.

يدير CloudMediaSurfaceController عرض معاينة عناصر الوسائط على مثيلات معيّنة من Surface. ومن المفترض أن تكون الطرق ضمن هذه الفئة غير متزامنة، ويجب ألا يتم حظرها من خلال تنفيذ أي عملية صعبة. ويكون مثيل CloudMediaSurfaceController الواحد مسؤولاً عن عرض عناصر وسائط متعددة مرتبطة بمساحات عرض متعددة.

تتوافق "CloudMediaSurfaceController" مع القائمة التالية من عمليات معاودة الاتصال بمراحل النشاط: