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

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

إنشاء تطبيقات متوافقة مع Kotlin وJava

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

  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.2.1"
    // For Kotlin use asset-delivery-ktx
    implementation "com.google.android.play:asset-delivery-ktx:2.2.1"
    

    Kotlin

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

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

الدمج مع واجهة برمجة التطبيقات Play Asset Delivery API

توفّر Play Asset Delivery Java API الفئة 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 API

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

إلغاء الطلب

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

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

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

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

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

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

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