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

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

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

اتّبِع الخطوات التالية لإنشاء ميزة "إرسال مواد العرض في Play" في ملف APK لتطبيق 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.2.2"
    // For Kotlin use asset-delivery-ktx
    implementation "com.google.android.play:asset-delivery-ktx:2.2.2"
    

    Kotlin

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

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

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

توفّر واجهة برمجة التطبيقات Java API لميزة "عرض المواد في 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.