|
| 1 | +/* |
| 2 | + * ============================================================================ |
| 3 | + * Name : CodeAssetsDownloadService.java |
| 4 | + * Author : AppDevForAll |
| 5 | + * Copyright : Copyright (c) 2026 AppDevForAll |
| 6 | + * Description : K2GO-443 / K2GO-449. Foreground shell for the Code on the Go build-assets download on |
| 7 | + * the durable job engine (a single-item session, type "code-assets"). The generic service |
| 8 | + * behavior (onStartCommand dispatch, notification, Host) lives in ContentDownloadServiceBase; |
| 9 | + * this subclass only provides the module bits: the session singleton, the action strings, |
| 10 | + * the notification text, and the START item (a sentinel, since the box runner reads the |
| 11 | + * manifest itself). The heavy work (aria2, resume, verify, swap) runs on the box. |
| 12 | + * ============================================================================ |
| 13 | + */ |
| 14 | +package org.appdevforall.k2go.codeassets.presentation; |
| 15 | + |
| 16 | +import android.content.Context; |
| 17 | +import android.content.Intent; |
| 18 | + |
| 19 | +import androidx.core.content.ContextCompat; |
| 20 | + |
| 21 | +import org.appdevforall.k2go.R; |
| 22 | +import org.appdevforall.k2go.redesign.ContentDownloadServiceBase; |
| 23 | +import org.appdevforall.k2go.redesign.ContentDownloadSession; |
| 24 | +import org.json.JSONArray; |
| 25 | +import org.json.JSONObject; |
| 26 | + |
| 27 | +public final class CodeAssetsDownloadService extends ContentDownloadServiceBase { |
| 28 | + |
| 29 | + private static final String CHANNEL_ID = "code_assets_download_channel"; |
| 30 | + private static final int NOTIFICATION_ID = 7; |
| 31 | + |
| 32 | + public static final String ACTION_START = "org.appdevforall.k2go.CODE_ASSETS_DOWNLOAD_START"; |
| 33 | + public static final String ACTION_PAUSE = "org.appdevforall.k2go.CODE_ASSETS_DOWNLOAD_PAUSE"; |
| 34 | + public static final String ACTION_RESUME = "org.appdevforall.k2go.CODE_ASSETS_DOWNLOAD_RESUME"; |
| 35 | + public static final String ACTION_CANCEL = "org.appdevforall.k2go.CODE_ASSETS_DOWNLOAD_CANCEL"; |
| 36 | + public static final String ACTION_RETRY = "org.appdevforall.k2go.CODE_ASSETS_DOWNLOAD_RETRY"; |
| 37 | + |
| 38 | + // The box runner reads the manifest itself and ignores the job items, so a single sentinel |
| 39 | + // satisfies POST /code-assets/download (which requires a non-empty items/ids). |
| 40 | + private static final String SENTINEL = "build-assets"; |
| 41 | + |
| 42 | + private static final ContentDownloadSession SESSION = new ContentDownloadSession("code-assets"); |
| 43 | + |
| 44 | + /** Adapts to the session listener; the UI passes {@code this::render}. */ |
| 45 | + public interface Listener { void onUpdate(); } |
| 46 | + |
| 47 | + // ---- static API the UI observes (delegates to the shared session) -------------------------- |
| 48 | + public static boolean isRunning() { return SESSION.isRunning(); } |
| 49 | + public static boolean isPaused() { return SESSION.isPaused(); } |
| 50 | + public static boolean hasSession() { return SESSION.hasSession(); } |
| 51 | + public static boolean isComplete() { return SESSION.isComplete(); } |
| 52 | + public static boolean hasFailed() { return SESSION.hasFailed(); } |
| 53 | + public static int percent() { return SESSION.percent(); } |
| 54 | + public static long speed() { return SESSION.speed(); } |
| 55 | + public static int reconnectAttempt() { return SESSION.reconnectAttempt(); } |
| 56 | + public static int reconnectTotal() { return SESSION.reconnectTotal(); } |
| 57 | + public static void setListener(Listener l) { SESSION.setListener(l == null ? null : l::onUpdate); } |
| 58 | + |
| 59 | + public static void start(Context ctx) { send(ctx, ACTION_START); } |
| 60 | + public static void pause(Context ctx) { if (SESSION.isRunning()) send(ctx, ACTION_PAUSE); } |
| 61 | + public static void resume(Context ctx) { send(ctx, ACTION_RESUME); } |
| 62 | + public static void cancel(Context ctx) { send(ctx, ACTION_CANCEL); } |
| 63 | + |
| 64 | + /** Re-queue the failed item and resume (the box resumes the partial via aria2 --continue). */ |
| 65 | + public static void retry(Context ctx) { |
| 66 | + if (SESSION.requeueFailed() && !SESSION.isRunning()) send(ctx, ACTION_RETRY); |
| 67 | + } |
| 68 | + |
| 69 | + public static void finishSession() { SESSION.purge(); } |
| 70 | + |
| 71 | + private static void send(Context ctx, String action) { |
| 72 | + ContextCompat.startForegroundService(ctx, |
| 73 | + new Intent(ctx, CodeAssetsDownloadService.class).setAction(action)); |
| 74 | + } |
| 75 | + |
| 76 | + // ---- per-module hooks for ContentDownloadServiceBase --------------------------------------- |
| 77 | + @Override protected ContentDownloadSession session() { return SESSION; } |
| 78 | + @Override protected int notificationId() { return NOTIFICATION_ID; } |
| 79 | + @Override protected String channelId() { return CHANNEL_ID; } |
| 80 | + @Override protected String channelName() { return getString(R.string.k2go_card_code_assets); } |
| 81 | + @Override protected String notifTitle() { return getString(R.string.k2go_code_assets_updating); } |
| 82 | + @Override protected String notifText() { return getString(R.string.k2go_card_code_assets); } |
| 83 | + @Override protected String actionPause() { return ACTION_PAUSE; } |
| 84 | + @Override protected String actionResume() { return ACTION_RESUME; } |
| 85 | + @Override protected String actionCancel() { return ACTION_CANCEL; } |
| 86 | + @Override protected String actionRetry() { return ACTION_RETRY; } |
| 87 | + |
| 88 | + @Override |
| 89 | + protected void beginFromIntent(Intent intent) { |
| 90 | + String[] keys = { "code-assets" }; |
| 91 | + String[] labels = { getString(R.string.k2go_card_code_assets) }; |
| 92 | + long[] sizes = { 0L }; // count-based: the UI shows the item percent directly |
| 93 | + JSONObject[] bodies = new JSONObject[1]; |
| 94 | + try { bodies[0] = new JSONObject().put("ids", new JSONArray().put(SENTINEL)); } |
| 95 | + catch (Exception e) { bodies[0] = new JSONObject(); } |
| 96 | + SESSION.begin(keys, labels, sizes, bodies); |
| 97 | + } |
| 98 | +} |
0 commit comments