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

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

إنشاء تطبيقات باستخدام Kotlin وJava

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

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

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

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

    Groovy

    // 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 التطبيق الخاص بالمشروع، أضِف اسم كل حزمة مواد عرض في مشروعك كما هو موضّح أدناه:

    Groovy

    // 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 الخاص بالمشروع، أدرِج جميع حِزم مواد العرض في مشروعك كما هو موضّح أدناه:

    Groovy

    // 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 Asset Delivery إذا كنت تخطّط لاستخدام ميزة "التنزيل السريع" والتسليم عند الطلب.

    Groovy

    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

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

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

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

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

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

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

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

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

إلغاء الطلب

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

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

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

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

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

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

اختبار ميزة "عرض المواد في Play" على الجهاز ومن Google Play