Skip to content

Commit dbdcf8d

Browse files
Merge pull request #626 from appdevforall/feat/K2GO-443-updater-progress
K2GO-443 feat(code-assets): build-assets updater on the durable job engine
2 parents 0a78727 + c95ca81 commit dbdcf8d

18 files changed

Lines changed: 957 additions & 268 deletions

‎controller/app/src/main/AndroidManifest.xml‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,15 @@
194194
android:value="Downloads offline library content through the co-located server REST engine." />
195195
</service>
196196

197+
<service
198+
android:name=".codeassets.presentation.CodeAssetsDownloadService"
199+
android:exported="false"
200+
android:foregroundServiceType="specialUse">
201+
<property
202+
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
203+
android:value="Downloads the Code on the Go build assets through the co-located server REST engine." />
204+
</service>
205+
197206
<service
198207
android:name=".redesign.BooksDownloadService"
199208
android:exported="false"

‎controller/app/src/main/java/org/appdevforall/k2go/codeassets/data/CodeAssetsRefreshClient.java‎

Lines changed: 0 additions & 170 deletions
This file was deleted.
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
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

Comments
 (0)