Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,24 +46,27 @@ private BooksCatalogAsset() {}
// Parsed once, kept in memory (already popularity-ordered by the generator).
private static volatile List<JSONObject> CACHE;

/** Search the offline catalog. lang: ""=all or an ISO code; q empty => popularity order. */
public static void search(Context ctx, String q, String lang, int limit, BooksClient.ArrayCb cb) {
/** Search the offline catalog. lang: ""=all or an ISO code; q empty => popularity order.
* ADFA-5329: offset skips earlier batches so the caller can page through with "Load more". */
public static void search(Context ctx, String q, String lang, int offset, int limit, BooksClient.ArrayCb cb) {
final Context app = ctx.getApplicationContext();
AppExecutors.get().io().execute(() -> {
try {
List<JSONObject> all = ensureLoaded(app);
String term = q == null ? "" : q.trim().toLowerCase(Locale.ROOT);
String lc = lang == null ? "" : lang.trim();
int skip = Math.max(0, offset);
JSONArray out = new JSONArray();
int n = 0;
int matched = 0, taken = 0;
for (JSONObject b : all) {
if (!lc.isEmpty() && !lc.equalsIgnoreCase(b.optString("language"))) continue;
if (!term.isEmpty()) {
String hay = (b.optString("title") + " " + b.optString("author")).toLowerCase(Locale.ROOT);
if (!hay.contains(term)) continue;
}
if (matched++ < skip) continue; // already shown in an earlier batch
out.put(b);
if (++n >= Math.max(1, limit)) break;
if (++taken >= Math.max(1, limit)) break;
}
MAIN.post(() -> cb.onOk(out));
} catch (Exception e) {
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,7 +4,7 @@
* Author : AppDevForAll
* Copyright : Copyright (c) 2026 AppDevForAll
* Description : ADFA-4850. App-side client for the dashboard's Books REST endpoints:
* GET /api/books/search?q=&filter=&limit= -> catalog rows (offline FTS)
* GET /api/books/search?q=&filter=&lang=&offset=&limit= -> catalog rows (offline FTS)
* GET /api/books/library -> Calibre-Web library rows
* POST /api/books/library/:id/remove -> delete
* (The actual download is a durable job driven by BooksDownloadService.)
Expand DownExpand Up@@ -38,11 +38,13 @@ public interface ArrayCb { void onOk(JSONArray rows); void onErr(String message)
public interface OkCb { void onOk(); void onErr(String message); }

/** Search the offline Gutenberg catalog. filter: ""|"educational"; lang: ""=all or an ISO code;
* q empty => top-by-downloads. */
public static void search(String q, String filter, String lang, int limit, ArrayCb cb) {
* q empty => top-by-downloads. ADFA-5329: offset pages through the results ("Load more").
* The server must apply a stable order (popularity, then id) so batches don't overlap. */
public static void search(String q, String filter, String lang, int offset, int limit, ArrayCb cb) {
AppExecutors.get().io().execute(() -> {
try {
String url = BASE + "/search?limit=" + limit
+ "&offset=" + Math.max(0, offset)
+ "&filter=" + enc(filter == null ? "" : filter)
+ "&lang=" + enc(lang == null ? "" : lang)
+ "&q=" + enc(q == null ? "" : q);
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -68,6 +68,11 @@ public class BooksLandingFragment extends Fragment {
// live download service. "In your books" here means "already in your setup order".
private boolean wizard = false;

// ADFA-5329: incremental "Load more" — BATCH titles per tap; hasMore/loading drive the footer.
private static final int BATCH = 40;
private boolean hasMore = false;
private boolean loading = false;

/** Open the Books screen in wizard (pre-install, offline) mode. */
public static BooksLandingFragment newInstance(boolean wizard) {
BooksLandingFragment f = new BooksLandingFragment();
Expand DownExpand Up@@ -229,27 +234,51 @@ private void loadLibrary() {
});
}

/** (Re)load the first batch — called on open and whenever the filter, search or language changes. */
private void loadBooks() {
android.util.Log.d("K2Go-Books", "loadBooks wizard=" + wizard + " filter=" + filter + " lang=" + lang + " q=" + query);
status.setVisibility(View.VISIBLE);
status.setText(getString(R.string.k2go_books_loading));
grid.removeAllViews();
hasMore = true;
fetchBatch(0, false);
}

/** ADFA-5329: append the next batch on demand ("Load more"). */
private void loadMore() {
if (loading || !hasMore) return;
fetchBatch(books.size(), true);
}

private void fetchBatch(int offset, boolean append) {
android.util.Log.d("K2Go-Books", "fetchBatch off=" + offset + " append=" + append
+ " wizard=" + wizard + " filter=" + filter + " lang=" + lang + " q=" + query);
loading = true;
if (!append) {
books.clear();
status.setVisibility(View.VISIBLE);
status.setText(getString(R.string.k2go_books_loading));
grid.removeAllViews();
} else {
render(); // show the "Loading…" footer while the next batch arrives
}
BooksClient.ArrayCb cb = new BooksClient.ArrayCb() {
@Override public void onOk(JSONArray rows) {
if (!isAdded()) return;
books.clear();
loading = false;
if (!append) books.clear();
for (int i = 0; i < rows.length(); i++) { JSONObject b = rows.optJSONObject(i); if (b != null) books.add(b); }
// The local library returns everything at once; a short batch means we hit the end.
hasMore = !isLocal() && rows.length() >= BATCH;
render();
}
@Override public void onErr(String m) {
if (!isAdded()) return;
loading = false;
if (append) { render(); return; } // keep what we have; the Load more stays for a retry
status.setVisibility(View.VISIBLE);
status.setText(getString(wizard ? R.string.k2go_books_offline_error : R.string.k2go_books_unavailable));
}
};
if (wizard) BooksCatalogAsset.search(requireContext(), query, lang, 40, cb);
if (wizard) BooksCatalogAsset.search(requireContext(), query, lang, offset, BATCH, cb);
else if (isLocal()) BooksClient.library(cb);
else BooksClient.search(query, filter, lang, 40, cb);
else BooksClient.search(query, filter, lang, offset, BATCH, cb);
}

private boolean inLibrary(JSONObject b) {
Expand DownExpand Up@@ -279,9 +308,51 @@ private void render() {
row.addView(pad, new LinearLayout.LayoutParams(0, 1, 1f));
}
}
appendPaginationFooter(); // ADFA-5329: Load more / loading / end-of-list
refreshFooter();
}

/** ADFA-5329: the "Load more" / loading / end-of-list footer, appended under the grid (inside the
* scroll, above the fixed Add bar). Not shown for the local library, which returns all rows. */
private void appendPaginationFooter() {
if (isLocal() || books.isEmpty()) return;
if (loading) {
grid.addView(footerText(getString(R.string.k2go_books_loading), false));
return;
}
if (hasMore) {
grid.addView(footerText(getString(R.string.k2go_books_showing_fmt, books.size()), false));
TextView more = footerText(getString(R.string.k2go_books_load_more), true);
more.setOnClickListener(v -> loadMore());
grid.addView(more);
} else {
grid.addView(footerText(getString(R.string.k2go_books_all_fmt, books.size()), false));
}
}

private TextView footerText(String text, boolean asButton) {
TextView t = new TextView(requireContext());
t.setText(text);
t.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
lp.setMargins(px(8), px(10), px(8), px(6));
t.setLayoutParams(lp);
if (asButton) {
t.setPadding(px(10), px(12), px(10), px(12));
t.setBackgroundResource(R.drawable.k2go_getmore_bg);
t.setTextAppearance(com.google.android.material.R.style.TextAppearance_Material3_BodyLarge);
t.setTextColor(ContextCompat.getColor(requireContext(), R.color.k2go_teal)); // after appearance
t.setClickable(true);
t.setFocusable(true);
} else {
t.setPadding(px(10), px(6), px(10), px(6));
t.setTextAppearance(com.google.android.material.R.style.TextAppearance_Material3_BodySmall);
t.setTextColor(ContextCompat.getColor(requireContext(), R.color.k2go_muted)); // after appearance
}
return t;
}

/** Cover colors chosen so a card never repeats its left neighbor's or the one above it (2-col
* grid). Seeded from the title hash for variety, then nudged off any collision — no meaning,
* just fewer same-color blocks. */
Expand Down
3 changes: 3 additions & 0 deletions controller/app/src/main/res/values-ar/strings.xml
Original file line numberDiff line numberDiff line change
Expand Up@@ -1286,4 +1286,7 @@
<string name="k2go_kolibri_failed_fmt">%1$d دورة لم تكتمل.</string>
<string name="k2go_kolibri_retry_before_leaving">أعد محاولة ما فشل قبل المغادرة — المغادرة تمسح هذه القائمة.</string>
<string name="k2go_kolibri_catalog_updated">تم تحديث الكتالوج في %1$s</string>
<string name="k2go_books_load_more">تحميل المزيد</string>
<string name="k2go_books_showing_fmt">عرض %1$d كتاب</string>
<string name="k2go_books_all_fmt">هذا كل شيء · %1$d كتاب</string>
</resources>
3 changes: 3 additions & 0 deletions controller/app/src/main/res/values-az/strings.xml
Original file line numberDiff line numberDiff line change
Expand Up@@ -1306,4 +1306,7 @@
<string name="k2go_kolibri_failed_fmt">%1$d kurs tamamlanmadı.</string>
<string name="k2go_kolibri_retry_before_leaving">Getməzdən əvvəl uğursuzları yenidən yoxlayın — çıxış bu siyahını silir.</string>
<string name="k2go_kolibri_catalog_updated">Kataloq %1$s tarixində yeniləndi</string>
<string name="k2go_books_load_more">Daha çox yüklə</string>
<string name="k2go_books_showing_fmt">%1$d kitab göstərilir</string>
<string name="k2go_books_all_fmt">Hamısı budur · %1$d kitab</string>
</resources>
3 changes: 3 additions & 0 deletions controller/app/src/main/res/values-bg/strings.xml
Original file line numberDiff line numberDiff line change
Expand Up@@ -1293,4 +1293,7 @@
<string name="k2go_kolibri_failed_fmt">%1$d курса не завършиха.</string>
<string name="k2go_kolibri_retry_before_leaving">Опитайте отново неуспешните, преди да излезете — излизането изчиства този списък.</string>
<string name="k2go_kolibri_catalog_updated">Каталогът е обновен на %1$s</string>
<string name="k2go_books_load_more">Зареди още</string>
<string name="k2go_books_showing_fmt">Показани %1$d книги</string>
<string name="k2go_books_all_fmt">Това е всичко · %1$d книги</string>
</resources>
3 changes: 3 additions & 0 deletions controller/app/src/main/res/values-bn/strings.xml
Original file line numberDiff line numberDiff line change
Expand Up@@ -1299,4 +1299,7 @@
<string name="k2go_kolibri_failed_fmt">%1$dটি কোর্স শেষ হয়নি।</string>
<string name="k2go_kolibri_retry_before_leaving">চলে যাওয়ার আগে ব্যর্থগুলো আবার চেষ্টা করুন — চলে গেলে এই তালিকা মুছে যাবে।</string>
<string name="k2go_kolibri_catalog_updated">ক্যাটালগ %1$s তারিখে আপডেট হয়েছে</string>
<string name="k2go_books_load_more">আরও লোড করুন</string>
<string name="k2go_books_showing_fmt">%1$d টি বই দেখানো হচ্ছে</string>
<string name="k2go_books_all_fmt">এটুকুই · %1$d টি বই</string>
</resources>
3 changes: 3 additions & 0 deletions controller/app/src/main/res/values-cs/strings.xml
Original file line numberDiff line numberDiff line change
Expand Up@@ -1293,4 +1293,7 @@
<string name="k2go_kolibri_failed_fmt">%1$d kurzů se nedokončilo.</string>
<string name="k2go_kolibri_retry_before_leaving">Než odejdete, zkuste neúspěšné znovu — odchodem se tento seznam smaže.</string>
<string name="k2go_kolibri_catalog_updated">Katalog aktualizován %1$s</string>
<string name="k2go_books_load_more">Načíst další</string>
<string name="k2go_books_showing_fmt">Zobrazeno %1$d knih</string>
<string name="k2go_books_all_fmt">To je vše · %1$d knih</string>
</resources>
3 changes: 3 additions & 0 deletions controller/app/src/main/res/values-de/strings.xml
Original file line numberDiff line numberDiff line change
Expand Up@@ -1286,4 +1286,7 @@
<string name="k2go_kolibri_failed_fmt">%1$d Kurse wurden nicht fertig.</string>
<string name="k2go_kolibri_retry_before_leaving">Wiederholen Sie die fehlgeschlagenen, bevor Sie gehen — beim Verlassen wird diese Liste gelöscht.</string>
<string name="k2go_kolibri_catalog_updated">Katalog aktualisiert am %1$s</string>
<string name="k2go_books_load_more">Mehr laden</string>
<string name="k2go_books_showing_fmt">%1$d Bücher angezeigt</string>
<string name="k2go_books_all_fmt">Das ist alles · %1$d Bücher</string>
</resources>
3 changes: 3 additions & 0 deletions controller/app/src/main/res/values-el/strings.xml
Original file line numberDiff line numberDiff line change
Expand Up@@ -1293,4 +1293,7 @@
<string name="k2go_kolibri_failed_fmt">%1$d μαθήματα δεν ολοκληρώθηκαν.</string>
<string name="k2go_kolibri_retry_before_leaving">Δοκιμάστε ξανά όσα απέτυχαν πριν φύγετε — η έξοδος διαγράφει αυτή τη λίστα.</string>
<string name="k2go_kolibri_catalog_updated">Ο κατάλογος ενημερώθηκε στις %1$s</string>
<string name="k2go_books_load_more">Φόρτωση περισσότερων</string>
<string name="k2go_books_showing_fmt">Εμφανίζονται %1$d βιβλία</string>
<string name="k2go_books_all_fmt">Αυτά ήταν όλα · %1$d βιβλία</string>
</resources>
3 changes: 3 additions & 0 deletions controller/app/src/main/res/values-es/strings.xml
Original file line numberDiff line numberDiff line change
Expand Up@@ -1362,4 +1362,7 @@
<string name="k2go_kolibri_failed_fmt">%1$d curso(s) no terminaron.</string>
<string name="k2go_kolibri_retry_before_leaving">Reintenta los que fallaron antes de salir: al salir se borra esta lista.</string>
<string name="k2go_kolibri_catalog_updated">Catálogo actualizado el %1$s</string>
<string name="k2go_books_load_more">Cargar más</string>
<string name="k2go_books_showing_fmt">Mostrando %1$d libros</string>
<string name="k2go_books_all_fmt">Eso es todo · %1$d libros</string>
</resources>
3 changes: 3 additions & 0 deletions controller/app/src/main/res/values-fa/strings.xml
Original file line numberDiff line numberDiff line change
Expand Up@@ -1286,4 +1286,7 @@
<string name="k2go_kolibri_failed_fmt">%1$d دوره کامل نشد.</string>
<string name="k2go_kolibri_retry_before_leaving">پیش از خروج، موارد ناموفق را دوباره تلاش کنید — خروج این فهرست را پاک می‌کند.</string>
<string name="k2go_kolibri_catalog_updated">کاتالوگ در %1$s به‌روزرسانی شد</string>
<string name="k2go_books_load_more">بارگذاری بیشتر</string>
<string name="k2go_books_showing_fmt">نمایش %1$d کتاب</string>
<string name="k2go_books_all_fmt">همین · %1$d کتاب</string>
</resources>
3 changes: 3 additions & 0 deletions controller/app/src/main/res/values-fr/strings.xml
Original file line numberDiff line numberDiff line change
Expand Up@@ -1373,4 +1373,7 @@
<string name="k2go_kolibri_failed_fmt">%1$d cours ne se sont pas terminés.</string>
<string name="k2go_kolibri_retry_before_leaving">Réessayez ceux qui ont échoué avant de partir : quitter efface cette liste.</string>
<string name="k2go_kolibri_catalog_updated">Catalogue mis à jour le %1$s</string>
<string name="k2go_books_load_more">Charger plus</string>
<string name="k2go_books_showing_fmt">%1$d livres affichés</string>
<string name="k2go_books_all_fmt">C\'est tout · %1$d livres</string>
</resources>
3 changes: 3 additions & 0 deletions controller/app/src/main/res/values-gu/strings.xml
Original file line numberDiff line numberDiff line change
Expand Up@@ -1299,4 +1299,7 @@
<string name="k2go_kolibri_failed_fmt">%1$d કોર્સ પૂરા થયા નથી.</string>
<string name="k2go_kolibri_retry_before_leaving">જતાં પહેલાં નિષ્ફળ થયેલા ફરી પ્રયાસ કરો — જવાથી આ યાદી ભૂંસાઈ જશે.</string>
<string name="k2go_kolibri_catalog_updated">કૅટલૉગ %1$s ના રોજ અપડેટ થયું</string>
<string name="k2go_books_load_more">વધુ લોડ કરો</string>
<string name="k2go_books_showing_fmt">%1$d પુસ્તકો બતાવ્યાં</string>
<string name="k2go_books_all_fmt">એટલું જ · %1$d પુસ્તકો</string>
</resources>
3 changes: 3 additions & 0 deletions controller/app/src/main/res/values-hi/strings.xml
Original file line numberDiff line numberDiff line change
Expand Up@@ -1363,4 +1363,7 @@
<string name="k2go_kolibri_failed_fmt">%1$d कोर्स पूरे नहीं हुए।</string>
<string name="k2go_kolibri_retry_before_leaving">जाने से पहले विफल हुए फिर से आज़माएँ — जाने पर यह सूची मिट जाएगी।</string>
<string name="k2go_kolibri_catalog_updated">कैटलॉग %1$s को अपडेट हुआ</string>
<string name="k2go_books_load_more">और लोड करें</string>
<string name="k2go_books_showing_fmt">%1$d किताबें दिखाई जा रही हैं</string>
<string name="k2go_books_all_fmt">बस इतना ही · %1$d किताबें</string>
</resources>
3 changes: 3 additions & 0 deletions controller/app/src/main/res/values-hu/strings.xml
Original file line numberDiff line numberDiff line change
Expand Up@@ -1286,4 +1286,7 @@
<string name="k2go_kolibri_failed_fmt">%1$d tanfolyam nem fejeződött be.</string>
<string name="k2go_kolibri_retry_before_leaving">Távozás előtt próbálja újra a sikerteleneket — a kilépés törli ezt a listát.</string>
<string name="k2go_kolibri_catalog_updated">Katalógus frissítve: %1$s</string>
<string name="k2go_books_load_more">Továbbiak betöltése</string>
<string name="k2go_books_showing_fmt">%1$d könyv megjelenítve</string>
<string name="k2go_books_all_fmt">Ennyi · %1$d könyv</string>
</resources>
3 changes: 3 additions & 0 deletions controller/app/src/main/res/values-in/strings.xml
Original file line numberDiff line numberDiff line change
Expand Up@@ -1293,4 +1293,7 @@
<string name="k2go_kolibri_failed_fmt">%1$d kursus tidak selesai.</string>
<string name="k2go_kolibri_retry_before_leaving">Coba lagi yang gagal sebelum keluar — keluar akan menghapus daftar ini.</string>
<string name="k2go_kolibri_catalog_updated">Katalog diperbarui pada %1$s</string>
<string name="k2go_books_load_more">Muat lebih banyak</string>
<string name="k2go_books_showing_fmt">Menampilkan %1$d buku</string>
<string name="k2go_books_all_fmt">Itu saja · %1$d buku</string>
</resources>
3 changes: 3 additions & 0 deletions controller/app/src/main/res/values-it/strings.xml
Original file line numberDiff line numberDiff line change
Expand Up@@ -1286,4 +1286,7 @@
<string name="k2go_kolibri_failed_fmt">%1$d corsi non sono stati completati.</string>
<string name="k2go_kolibri_retry_before_leaving">Riprova quelli falliti prima di uscire: uscendo questa lista viene cancellata.</string>
<string name="k2go_kolibri_catalog_updated">Catalogo aggiornato il %1$s</string>
<string name="k2go_books_load_more">Carica altro</string>
<string name="k2go_books_showing_fmt">%1$d libri mostrati</string>
<string name="k2go_books_all_fmt">È tutto · %1$d libri</string>
</resources>
3 changes: 3 additions & 0 deletions controller/app/src/main/res/values-ja/strings.xml
Original file line numberDiff line numberDiff line change
Expand Up@@ -1287,4 +1287,7 @@
<string name="k2go_kolibri_failed_fmt">%1$d 件のコースが完了しませんでした。</string>
<string name="k2go_kolibri_retry_before_leaving">退出する前に失敗したものを再試行してください。退出するとこの一覧は消えます。</string>
<string name="k2go_kolibri_catalog_updated">カタログの更新日: %1$s</string>
<string name="k2go_books_load_more">もっと読み込む</string>
<string name="k2go_books_showing_fmt">%1$d 冊を表示中</string>
<string name="k2go_books_all_fmt">以上です · %1$d 冊</string>
</resources>
Loading
Loading