دمج عرض مواد العرض (Kotlin وJava)

اتّبِع الخطوات الواردة في هذا الدليل للوصول إلى حزم مواد العرض في تطبيقك من خلال رمز Java.

إنشاء تطبيقات بلغتَي Kotlin وJava

اتّبِع الخطوات التالية لدمج ميزة "عرض المواد في Play" في مجموعة حزمات تطبيق Android الخاصة بمشروعك. ليس عليك استخدام Android Studio لتنفيذ هذه الخطوات.

  1. يُرجى تحديث إصدار المكوّن الإضافي لنظام Gradle المتوافق مع Android في ملف build.gradle الخاص بمشروعك إلى الإصدار 4.0.0 أو إصدار أحدث.

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

  3. في دليل حزمة مواد العرض، أنشئ ملف build.gradle وأضِف الرمز التالي. تأكَّد من تحديد اسم حزمة مواد العرض ونوع واحد فقط من أنواع التسليم:

    أنيق

    // In the asset pack's build.gradle file:
    plugins {
      id 'com.android.asset-pack'
    }
    
    assetPack {
        packName = "asset-pack-name" // Directory name for the asset pack
        dynamicDelivery {
            deliveryType = "[ install-time | fast-follow | on-demand ]"
        }
    }

    Kotlin

    // In the asset pack's build.gradle.kts file:
    plugins {
      id("com.android.asset-pack")
    }
    
    assetPack {
      packName.set("asset-pack-name") // Directory name for the asset pack
      dynamicDelivery {
        deliveryType.set("[ install-time | fast-follow | on-demand ]")
      }
    }
  4. في ملف build.gradle الخاص بتطبيق المشروع، أضِف اسم كل حزمة مواد عرض في مشروعك كما هو موضّح أدناه:

    أنيق

    // In the app build.gradle file:
    android {
        ...
        assetPacks = [":asset-pack-name", ":asset-pack2-name"]
    }

    Kotlin

    // In the app build.gradle.kts file:
    android {
        ...
        assetPacks += listOf(":asset-pack-name", ":asset-pack2-name")
    }
  5. في ملف settings.gradle الخاص بالمشروع، أدرِج جميع حزم مواد العرض في مشروعك كما هو موضّح أدناه:

    أنيق

    // In the settings.gradle file:
    include ':app'
    include ':asset-pack-name'
    include ':asset-pack2-name'

    Kotlin

    // In the settings.gradle.kts file:
    include(":app")
    include(":asset-pack-name")
    include(":asset-pack2-name")
  6. في دليل حزمة مواد العرض، أنشئ الدليل الفرعي التالي: src/main/assets.

  7. ضَع مواد العرض في الدليل src/main/assets. يمكنك إنشاء أدلة فرعية هنا أيضًا. يجب أن تبدو بنية الدليل لتطبيقك الآن على النحو التالي:

    • build.gradle
    • settings.gradle
    • app/
    • asset-pack-name/build.gradle
    • asset-pack-name/src/main/assets/your-asset-directories
  8. إنشاء مجموعة حزمات تطبيق Android باستخدام Gradle في حزمة التطبيق التي تم إنشاؤها، يتضمّن الدليل على مستوى الجذر الآن ما يلي:

    • ‫asset-pack-name/manifest/AndroidManifest.xml: لضبط معرّف حزمة مواد العرض ووضع العرض
    • ‫asset-pack-name/assets/your-asset-directories: المجلد الذي يحتوي على جميع مواد العرض التي يتم تسليمها كجزء من حزمة مواد العرض

    ينشئ Gradle ملف البيان لكل حزمة مواد عرض ويُخرج الدليل assets/ لك.

  9. (اختياري) يمكنك تضمين مكتبة عرض المواد في Play إذا كنت تخطّط لاستخدام ميزتَي "التنزيل فور اكتمال التثبيت" و"العرض عند الطلب".

    أنيق

    implementation "com.google.android.play:asset-delivery:2.3.0"
    // For Kotlin use asset-delivery-ktx
    implementation "com.google.android.play:asset-delivery-ktx:2.3.0"

    Kotlin

    implementation("com.google.android.play:asset-delivery:2.3.0")
    // For Kotlin use core-ktx
    implementation("com.google.android.play:asset-delivery-ktx:2.3.0")

  10. (اختياري) يمكنك ضبط حِزمة تطبيقك لتتوافق مع تنسيقات ضغط الزخرفة المختلفة.

الدمج مع واجهة Play Asset Delivery API

توفّر واجهة برمجة التطبيقات Java لميزة "عرض المواد في Play" فئة AssetPackManager لطلب حِزم مواد العرض وإدارة عمليات التنزيل والوصول إلى مواد العرض. تأكَّد من إضافة مكتبة عرض المواد في Play إلى مشروعك أولاً.

يمكنك تنفيذ واجهة برمجة التطبيقات هذه وفقًا لنوع عرض حزمة مواد العرض التي تريد الوصول إليها. تظهر هذه الخطوات في المخطط الانسيابي التالي.

مخطّط بياني لتدفق حزمة مواد العرض في لغة البرمجة Java

الشكل 1: مخطّط انسيابي للوصول إلى حزم مواد العرض

العرض في وقت التثبيت

تتوفّر حزم مواد العرض التي تم ضبطها على install-time فور تشغيل التطبيق. استخدِم Java AssetManager API للوصول إلى مواد العرض المقدَّمة في هذا الوضع:

Kotlin

import android.content.res.AssetManager
...
val context: Context = createPackageContext("com.example.app", 0)
val assetManager: AssetManager = context.assets
val stream: InputStream = assetManager.open("asset-name")

Java

import android.content.res.AssetManager;
...
Context context = createPackageContext("com.example.app", 0);
AssetManager assetManager = context.getAssets();
InputStream is = assetManager.open("asset-name");

المتابعة السريعة والتسليم عند الطلب

توضّح الأقسام التالية كيفية الحصول على معلومات عن حزم مواد العرض قبل تنزيلها، وكيفية استدعاء واجهة برمجة التطبيقات لبدء عملية التنزيل، ثم كيفية الوصول إلى الحزم التي تم تنزيلها. تنطبق هذه الأقسام على حِزم مواد العرض fast-follow وon-demand.

التحقّق من الحالة

يتم تخزين كل حزمة مواد عرض في مجلد منفصل في مساحة التخزين الداخلية للتطبيق. استخدِم الطريقة getPackLocation() لتحديد المجلد الجذر لحزمة مواد العرض. تعرض هذه الطريقة القيم التالية:

عرض القيمة الحالة
عنصر AssetPackLocation صالح المجلد الرئيسي لحزمة مواد العرض جاهز للوصول الفوري إليه على assetsPath()
null حزمة مواد العرض غير معروفة أو مواد العرض غير متاحة

الحصول على معلومات تنزيل حِزم مواد العرض

يجب أن تفصح التطبيقات عن حجم التنزيل قبل جلب حزمة مواد العرض. استخدِم الطريقة requestPackStates() أو getPackStates() لتحديد حجم عملية التنزيل وما إذا كان قد بدأ تنزيل الحزمة بالفعل.

Kotlin

suspend fun requestPackStates(packNames: List<String>): AssetPackStates

Java

Task<AssetPackStates> getPackStates(List<String> packNames)

‫requestPackStates() هي دالة تعليق تعرض عنصر AssetPackStates، بينما getPackStates() هي طريقة غير متزامنة تعرض Task<AssetPackStates>. يعرض الإجراء packStates() لعنصر AssetPackStates قيمة Map<String, AssetPackState>. تحتوي هذه الخريطة على حالة كل حزمة مواد عرض مطلوبة، ويتم تحديدها حسب اسمها:

Kotlin

AssetPackStates#packStates(): Map<String, AssetPackState>

Java

Map<String, AssetPackState> AssetPackStates#packStates()

يظهر الطلب النهائي على النحو التالي:

Kotlin

const val assetPackName = "assetPackName"
coroutineScope.launch {
  try {
    val assetPackStates: AssetPackStates =
      manager.requestPackStates(listOf(assetPackName))
    val assetPackState: AssetPackState =
      assetPackStates.packStates()[assetPackName]
  } catch (e: RuntimeExecutionException) {
    Log.d("MainActivity", e.message)
  }
}

Java

final String assetPackName = "myasset";

assetPackManager
    .getPackStates(Collections.singletonList(assetPackName))
    .addOnCompleteListener(new OnCompleteListener<AssetPackStates>() {
        @Override
        public void onComplete(Task<AssetPackStates> task) {
            AssetPackStates assetPackStates;
            try {
                assetPackStates = task.getResult();
                AssetPackState assetPackState =
                    assetPackStates.packStates().get(assetPackName);
            } catch (RuntimeExecutionException e) {
                Log.d("MainActivity", e.getMessage());
                return;
            })

توفّر الطُرق التالية في AssetPackState حجم حزمة مواد العرض والكمية التي تم تنزيلها حتى الآن (إذا تم طلبها) والكمية التي تم نقلها إلى التطبيق:

لمعرفة حالة حزمة مواد عرض معيّنة، استخدِم الطريقة status() التي تعرض الحالة كعدد صحيح يتوافق مع حقل ثابت في الفئة AssetPackStatus. تظهر الحالة AssetPackStatus.NOT_INSTALLED لحزمة مواد العرض التي لم يتم تثبيتها بعد.

إذا تعذّر تنفيذ طلب، استخدِم الطريقة errorCode() التي تتوافق قيمة الإرجاع الخاصة بها مع حقل ثابت في الفئة AssetPackErrorCode.

تثبيت

استخدِم الطريقة requestFetch() أو fetch() لتنزيل حزمة مواد عرض لأول مرة أو طلب تحديث حزمة مواد عرض لإكمالها:

Kotlin

suspend fun AssetPackManager.requestFetch(packs: List<String>): AssetPackStates

Java

Task<AssetPackStates> fetch(List<String> packNames)

تعرض هذه الطريقة عنصر AssetPackStates يحتوي على قائمة بالحزم وحالات وأحجام التنزيل الأولية. إذا كانت حزمة مواد العرض المطلوبة من خلال requestFetch() أو fetch() قيد التنزيل، يتم عرض حالة التنزيل ولا يبدأ أي تنزيل إضافي.

مراقبة حالات التنزيل

يجب تنفيذ AssetPackStateUpdatedListener لتتبُّع مستوى تقدّم تثبيت حزم مواد العرض. يتم تقسيم تحديثات الحالة حسب الحزمة لتسهيل تتبُّع حالة حِزم مواد العرض الفردية. يمكنك البدء في استخدام حزم مواد العرض المتاحة قبل اكتمال جميع عمليات التنزيل الأخرى لطلبك.

Kotlin

fun registerListener(listener: AssetPackStateUpdatedListener)
fun unregisterListener(listener: AssetPackStateUpdatedListener)

Java

void registerListener(AssetPackStateUpdatedListener listener)
void unregisterListener(AssetPackStateUpdatedListener listener)

عمليات تنزيل كبيرة

إذا كان حجم الملف الذي يتم تنزيله أكبر من 200 ميغابايت ولم يكن المستخدم متصلاً بشبكة Wi-Fi، لن يبدأ التنزيل إلا بعد أن يمنح المستخدم موافقته الصريحة على المتابعة باستخدام اتصال بيانات الجوّال. وبالمثل، إذا كان حجم عملية التنزيل كبيرًا وفقد المستخدم الاتصال بشبكة Wi-Fi، يتم إيقاف عملية التنزيل مؤقتًا ويجب الحصول على موافقة صريحة للمتابعة باستخدام اتصال بيانات الجوّال. تكون حالة الحزمة المتوقفة مؤقتًا WAITING_FOR_WIFI. لتشغيل مسار واجهة المستخدم من أجل طلب موافقة المستخدم، استخدِم الطريقة showConfirmationDialog().

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

تأكيد المستخدم مطلوب

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

في ما يلي مثال على تنفيذ أداة استماع:

Kotlin

private val activityResultLauncher = registerForActivityResult(
    ActivityResultContracts.StartIntentSenderForResult()
) { result ->
    if (result.resultCode == RESULT_OK) {
        Log.d(TAG, "Confirmation dialog has been accepted.")
    } else if (result.resultCode == RESULT_CANCELED) {
        Log.d(TAG, "Confirmation dialog has been denied by the user.")
    }
}

assetPackManager.registerListener { assetPackState ->
  when(assetPackState.status()) {
    AssetPackStatus.PENDING -> {
      Log.i(TAG, "Pending")
    }
    AssetPackStatus.DOWNLOADING -> {
      val downloaded = assetPackState.bytesDownloaded()
      val totalSize = assetPackState.totalBytesToDownload()
      val percent = 100.0 * downloaded / totalSize

      Log.i(TAG, "PercentDone=" + String.format("%.2f", percent))
    }
    AssetPackStatus.TRANSFERRING -> {
      // 100% downloaded and assets are being transferred.
      // Notify user to wait until transfer is complete.
    }
    AssetPackStatus.COMPLETED -> {
      // Asset pack is ready to use. Start the game.
    }
    AssetPackStatus.FAILED -> {
      // Request failed. Notify user.
      Log.e(TAG, assetPackState.errorCode())
    }
    AssetPackStatus.CANCELED -> {
      // Request canceled. Notify user.
    }
    AssetPackStatus.WAITING_FOR_WIFI,
    AssetPackStatus.REQUIRES_USER_CONFIRMATION -> {
      if (!confirmationDialogShown) {
        assetPackManager.showConfirmationDialog(activityResultLauncher);
        confirmationDialogShown = true
      }
    }
    AssetPackStatus.NOT_INSTALLED -> {
      // Asset pack is not downloaded yet.
    }
    AssetPackStatus.UNKNOWN -> {
      Log.wtf(TAG, "Asset pack status unknown")
    }
  }
}

Java

assetPackStateUpdateListener = new AssetPackStateUpdateListener() {
    private final ActivityResultLauncher<IntentSenderRequest> activityResultLauncher =
      registerForActivityResult(
          new ActivityResultContracts.StartIntentSenderForResult(),
          new ActivityResultCallback<ActivityResult>() {
            @Override
            public void onActivityResult(ActivityResult result) {
              if (result.getResultCode() == RESULT_OK) {
                Log.d(TAG, "Confirmation dialog has been accepted.");
              } else if (result.getResultCode() == RESULT_CANCELED) {
                Log.d(TAG, "Confirmation dialog has been denied by the user.");
              }
            }
          });

    @Override
    public void onStateUpdate(AssetPackState assetPackState) {
      switch (assetPackState.status()) {
        case AssetPackStatus.PENDING:
          Log.i(TAG, "Pending");
          break;

        case AssetPackStatus.DOWNLOADING:
          long downloaded = assetPackState.bytesDownloaded();
          long totalSize = assetPackState.totalBytesToDownload();
          double percent = 100.0 * downloaded / totalSize;

          Log.i(TAG, "PercentDone=" + String.format("%.2f", percent));
          break;

        case AssetPackStatus.TRANSFERRING:
          // 100% downloaded and assets are being transferred.
          // Notify user to wait until transfer is complete.
          break;

        case AssetPackStatus.COMPLETED:
          // Asset pack is ready to use. Start the game.
          break;

        case AssetPackStatus.FAILED:
          // Request failed. Notify user.
          Log.e(TAG, assetPackState.errorCode());
          break;

        case AssetPackStatus.CANCELED:
          // Request canceled. Notify user.
          break;

        case AssetPackStatus.WAITING_FOR_WIFI:
        case AssetPackStatus.REQUIRES_USER_CONFIRMATION:
          if (!confirmationDialogShown) {
            assetPackManager.showConfirmationDialog(activityResultLauncher);
            confirmationDialogShown = true;
          }
          break;

        case AssetPackStatus.NOT_INSTALLED:
          // Asset pack is not downloaded yet.
          break;
        case AssetPackStatus.UNKNOWN:
          Log.wtf(TAG, "Asset pack status unknown")
          break;
      }
    }
}

يمكنك بدلاً من ذلك استخدام الطريقة getPackStates() للحصول على حالة عمليات التنزيل الحالية. يحتوي AssetPackStates على مستوى تقدُّم عملية التنزيل وحالتها وأي رموز خطأ في حال تعذّر التنزيل.

الوصول إلى حِزم مواد العرض

يمكنك الوصول إلى حزمة مواد عرض باستخدام طلبات نظام الملفات بعد أن يصل طلب التنزيل إلى الحالة COMPLETED. استخدِم طريقة getPackLocation() للحصول على المجلد الجذر لحزمة مواد العرض.

يتم تخزين مواد العرض في الدليل assets ضمن الدليل الجذري لحزمة مواد العرض. يمكنك الحصول على المسار إلى دليل assets باستخدام طريقة الراحة assetsPath(). استخدِم الطريقة التالية للحصول على مسار مادة عرض معيّنة:

Kotlin

private fun getAbsoluteAssetPath(assetPack: String, relativeAssetPath: String): String? {
    val assetPackPath: AssetPackLocation =
      assetPackManager.getPackLocation(assetPack)
      // asset pack is not ready
      ?: return null

    val assetsFolderPath = assetPackPath.assetsPath()
    // equivalent to: FilenameUtils.concat(assetPackPath.path(), "assets")
    return FilenameUtils.concat(assetsFolderPath, relativeAssetPath)
}

Java

private String getAbsoluteAssetPath(String assetPack, String relativeAssetPath) {
    AssetPackLocation assetPackPath = assetPackManager.getPackLocation(assetPack);

    if (assetPackPath == null) {
        // asset pack is not ready
        return null;
    }

    String assetsFolderPath = assetPackPath.assetsPath();
    // equivalent to: FilenameUtils.concat(assetPackPath.path(), "assets");
    String assetPath = FilenameUtils.concat(assetsFolderPath, relativeAssetPath);
    return assetPath;
}

طرق أخرى لواجهة برمجة التطبيقات Play Asset Delivery API

في ما يلي بعض الطرق الإضافية لواجهة برمجة التطبيقات التي قد تحتاج إلى استخدامها في تطبيقك.

إلغاء الطلب

استخدِم cancel() لإلغاء طلب حزمة مواد عرض نشط. يُرجى العِلم أنّ هذا الطلب هو عملية تعتمد على بذل أقصى جهد.

إزالة حزمة مواد عرض

استخدِم requestRemovePack() أو removePack() لجدولة إزالة حزمة مواد عرض.

الحصول على مواقع جغرافية لحزم مواد عرض متعددة

استخدِم getPackLocations() لطلب حالة حِزم مواد عرض متعددة بشكل مجمّع، ما يؤدي إلى عرض خريطة لحِزم مواد العرض ومواقعها الجغرافية. تحتوي الخريطة التي تعرضها الدالة getPackLocations() على إدخال لكل حزمة تم تنزيلها حاليًا ومحدَّثة.

الخطوة التالية

اختبِر ميزة "عرض المواد في Play" محليًا ومن Google Play.