Skip to content

Commit 8bd356f

Browse files
Merge pull request #555 from appdevforall/refactor/K2GO-385-maps-m3
K2GO-385 refactor(ui): Maps M3, download-control ladder, and dialogs on paper
2 parents c578a04 + bba8a49 commit 8bd356f

13 files changed

Lines changed: 273 additions & 128 deletions

File tree

‎controller/app/src/main/java/org/appdevforall/k2go/redesign/FqrController.java‎

Lines changed: 72 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@
2020
* building them through a ContextThemeWrapper(Theme.K2Go) — a Material3.DayNight theme
2121
* carrying the app palette — without touching PortalActivity's global theme. Colors come
2222
* from theme attributes (colorSurface/onSurface/primary/error/…) so they follow light/dark;
23-
* dialogs use MaterialAlertDialogBuilder, buttons MaterialButton, and the progress bars
24-
* the Material progress indicators. All user-facing text lives in string resources.
23+
* confirm/error dialogs use the shared BrandDialog (K2GO-385), buttons MaterialButton, and
24+
* the progress bars the Material progress indicators. All user-facing text lives in string
25+
* resources.
2526
*
2627
* ADFA-5062: in the operation model (ADR-5061) this FQR region fetch is
2728
* Operation.content("maps") — a CONTENT / LIVE operation. Unlike the banked ContentType
@@ -55,11 +56,11 @@
5556

5657
import com.google.android.material.button.MaterialButton;
5758
import com.google.android.material.color.MaterialColors;
58-
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
5959
import com.google.android.material.progressindicator.CircularProgressIndicator;
6060
import com.google.android.material.progressindicator.LinearProgressIndicator;
6161

6262
import org.appdevforall.k2go.R;
63+
import org.appdevforall.k2go.ui.dialog.BrandDialog;
6364
import org.appdevforall.k2go.util.M3Text;
6465
import org.json.JSONArray;
6566
import org.json.JSONObject;
@@ -81,6 +82,9 @@ public final class FqrController {
8182
// theme: build every view/dialog through this wrapper so ?attr colors + the app font resolve.
8283
private final Context themed;
8384
private final int cSurface, cSurfaceContainer, cSurfaceHighest, cOnSurface, cOnSurfaceVariant, cPrimary, cError;
85+
// K2GO-385 (C1): state colours for the download bar -- amber = stopped (halted, not live), leaf = done.
86+
// Fixed tokens (same in day/night), so a halted or finished job reads the same on either theme.
87+
private final int cAmber, cLeaf;
8488

8589
private volatile boolean active = false; // written on UI thread, read on the WebView binder thread
8690
private AlertDialog dialog; // "calculating" / consent (one at a time)
@@ -132,6 +136,8 @@ public FqrController(Activity activity, WebView webView) {
132136
// Material3-specific surface roles above; read them from the appcompat namespace.
133137
this.cPrimary = attr(androidx.appcompat.R.attr.colorPrimary, 0xFF4CAF7D);
134138
this.cError = attr(androidx.appcompat.R.attr.colorError, 0xFFE05353);
139+
this.cAmber = androidx.core.content.ContextCompat.getColor(themed, R.color.k2go_amber);
140+
this.cLeaf = androidx.core.content.ContextCompat.getColor(themed, R.color.k2go_leaf);
135141
}
136142

137143
private int attr(int attrId, int fallback) {
@@ -235,11 +241,7 @@ private void handleExtract(String name, String box) {
235241
@Override public void onError(String message) {
236242
if (estimateCanceled) return;
237243
dismissDialog();
238-
new MaterialAlertDialogBuilder(themed)
239-
.setTitle(R.string.k2go_fqr_estimate_error_title)
240-
.setMessage(message)
241-
.setPositiveButton(android.R.string.ok, null)
242-
.show();
244+
errorDialog(R.string.k2go_fqr_estimate_error_title, message);
243245
}
244246
});
245247
}
@@ -270,7 +272,9 @@ private void resetMapSelection() {
270272
// ---- Consent -----------------------------------------------------------------------------
271273
private void showCalculating() {
272274
dismissDialog();
273-
LinearLayout row = dialogContent(dp(24)); // ADFA-5027: M3 dialog inset (4dp grid)
275+
// hpad 0: BrandDialog's content host already sits at the card's horizontal inset (double-inset
276+
// otherwise). Vertical 8dp keeps the 4dp grid (ADFA-5027).
277+
LinearLayout row = dialogContent(0, dp(8));
274278
row.setOrientation(LinearLayout.HORIZONTAL);
275279
row.setGravity(Gravity.CENTER_VERTICAL);
276280
CircularProgressIndicator spin = new CircularProgressIndicator(themed);
@@ -285,17 +289,18 @@ private void showCalculating() {
285289
row.addView(t);
286290
// ADFA-5043: canceling while estimating must drop the pending estimate AND clear the map's
287291
// selection — otherwise the crosshair lingers and a late estimate could still pop the consent.
288-
dialog = new MaterialAlertDialogBuilder(themed)
289-
.setView(row)
292+
dialog = new BrandDialog(themed)
293+
.setContentView(row)
290294
.setCancelable(true)
291-
.setOnCancelListener(d -> { estimateCanceled = true; resetMapSelection(); })
292-
.show();
295+
.setOnCancel(() -> { estimateCanceled = true; resetMapSelection(); })
296+
.show()
297+
.getDialog();
293298
}
294299

295300
private void showConsent(String name, String box, long transfer, long archive, long free, long freeAfter) {
296-
// ADFA-5027: M3 dialog spacing — content aligned to the title (24dp) with 4dp-grid vertical
297-
// breathing room, so it isn't cramped against the edges/title/buttons.
298-
LinearLayout body = dialogContent(dp(24), dp(8));
301+
// ADFA-5027: 4dp-grid vertical breathing room. hpad 0: BrandDialog's content host already
302+
// carries the card's horizontal inset (aligned to the title), so no inset is added here.
303+
LinearLayout body = dialogContent(0, dp(8));
299304

300305
TextView sub = new TextView(themed);
301306
sub.setText(str(R.string.k2go_fqr_consent_sub, name, human(transfer), human(archive)));
@@ -337,17 +342,18 @@ private void showConsent(String name, String box, long transfer, long archive, l
337342
body.addView(warn);
338343
}
339344

340-
dialog = new MaterialAlertDialogBuilder(themed)
345+
// ADFA-5043: bailing out here (Not now / tap-outside) must also clear the map's FQR selection
346+
// + tool -- the same reset the name dialog's Cancel does -- or the crosshair and the drawn area
347+
// linger with a stale "download this region" button on top.
348+
dialog = new BrandDialog(themed)
341349
.setTitle(R.string.k2go_fqr_consent_title)
342-
.setView(body)
343-
// ADFA-5043: bailing out here (Not now / tap-outside) must also clear the map's FQR
344-
// selection + tool — the same reset the name dialog's Cancel does — or the crosshair and
345-
// the drawn area linger with a stale "download this region" button on top.
346-
.setNegativeButton(R.string.k2go_fqr_not_now, (d, w) -> { d.dismiss(); resetMapSelection(); })
347-
.setPositiveButton(R.string.k2go_fqr_download, (d, w) -> startDownload(name))
348-
.setOnCancelListener(d -> resetMapSelection())
350+
.setContentView(body)
351+
.setPositive(R.string.k2go_fqr_download, () -> startDownload(name))
352+
.setNegative(R.string.k2go_fqr_not_now, () -> resetMapSelection())
353+
.setOnCancel(() -> resetMapSelection())
349354
.setCancelable(true)
350-
.show();
355+
.show()
356+
.getDialog();
351357

352358
// Kick off the actual download only after consent; the box already re-validates.
353359
// (startDownload is invoked from the positive button above.)
@@ -364,9 +370,10 @@ private void startDownload(String name) {
364370
showOverlay(name, pendingArchive);
365371
client.download(name, box, new MapsRegionClient.DownloadListener() {
366372
@Override public void onProgress(int percent, long speed) {
367-
if (overlayStopped) { // retried/running again: back to Stop
373+
if (overlayStopped) { // retried/running again: back to Stop, live teal bar
368374
overlayStopped = false;
369375
if (overlayStop != null) overlayStop.setText(R.string.k2go_clone_stop_confirm);
376+
setBarState(cPrimary, true);
370377
}
371378
if (overlayStop != null) overlayStop.setEnabled(true); // re-enable after a Stop/Retry tap
372379
updateOverlay(percent, speed);
@@ -381,18 +388,18 @@ private void startDownload(String name) {
381388
// #2: freeze the bar (determinate, no animation) so it doesn't keep animating under "Stopped".
382389
if (overlayBar != null && overlayBar.isIndeterminate()) setBarMode(false);
383390
if (overlayBar != null && percent >= 0) overlayBar.setProgressCompat(percent, false);
391+
setBarState(cAmber, false); // amber + dimmed: a halted job must not look live
384392
}
385393
@Override public void onDone() {
394+
setBarState(cLeaf, true); // done: a full leaf bar reads as success
386395
updateOverlay(100, 0);
387396
if (overlayTitle != null) overlayTitle.setText(R.string.k2go_fqr_region_added); // may be gone if hidden
388397
webView.postDelayed(() -> { hideOverlay(); webView.reload(); }, 1200);
389398
}
390399
@Override public void onError(String message) {
391400
hideOverlay();
392401
if (!"canceled".equals(message)) {
393-
new MaterialAlertDialogBuilder(themed)
394-
.setTitle(R.string.k2go_fqr_download_failed).setMessage(message)
395-
.setPositiveButton(android.R.string.ok, null).show();
402+
errorDialog(R.string.k2go_fqr_download_failed, message);
396403
}
397404
}
398405
});
@@ -443,15 +450,18 @@ private void showOverlay(String name, long sizeBytes) {
443450
// ADFA-4896: Stop/Retry beside Cancel. The label follows the reported state; the tap fires the
444451
// matching verb and the poll (onPaused/onProgress) is the source of truth.
445452
overlayStopped = false;
446-
// K2GO-385 (PR3): the download controls use the app button system (K2Go outlined stadium) via the
447-
// shared overlay, not a bare Material3 outlined button. FQR's overlay is a themed (day/night)
448-
// surface, so the K2Go outlined style's theme teal is right here -- not the fixed boot tokens.
449-
ContextThemeWrapper btnCtx = new ContextThemeWrapper(themed, R.style.ThemeOverlay_K2Go_Button_Outlined);
450-
overlayStop = new MaterialButton(btnCtx, null);
453+
// K2GO-385: download-controls role ladder (design k2go-download-card-style-sizes-v1) -- the
454+
// keep-the-download primary (Stop/Retry) is the FILLED teal button; Cancel, which discards the
455+
// transfer, is a TEXT button in clay. Never two equal outline pills. FQR's overlay is a themed
456+
// (day/night) surface, so the app button styles' theme teal/clay are right here -- not the fixed
457+
// boot tokens. The role look lives only in the styles; no colour is set in Java.
458+
ContextThemeWrapper filledCtx = new ContextThemeWrapper(themed, R.style.ThemeOverlay_K2Go_Button_Filled);
459+
overlayStop = new MaterialButton(filledCtx, null);
451460
overlayStop.setText(R.string.k2go_clone_stop_confirm);
452461
overlayStop.setOnClickListener(v -> toggleStop());
453462
row.addView(overlayStop);
454-
MaterialButton cancel = new MaterialButton(btnCtx, null);
463+
ContextThemeWrapper textDangerCtx = new ContextThemeWrapper(themed, R.style.ThemeOverlay_K2Go_Button_Text_Destructive);
464+
MaterialButton cancel = new MaterialButton(textDangerCtx, null);
455465
cancel.setText(R.string.k2go_cancel);
456466
cancel.setOnClickListener(v -> { client.cancel(); hideOverlay(); });
457467
row.addView(cancel);
@@ -509,6 +519,14 @@ private void setBarMode(boolean indeterminate) {
509519
overlayBar.setVisibility(vis == View.GONE ? View.VISIBLE : vis);
510520
}
511521

522+
/** K2GO-385 (C1): colour the download bar by state and dim it when the job is not live, so a stopped
523+
* job never looks like a running one (teal = downloading, amber = stopped, leaf = done). */
524+
private void setBarState(int color, boolean live) {
525+
if (overlayBar == null) return;
526+
overlayBar.setIndicatorColor(color);
527+
overlayBar.setAlpha(live ? 1f : 0.55f);
528+
}
529+
512530
/** bytes/sec -> "2.4 MB/s". Empty for non-positive (speed not reported yet). */
513531
private static String humanRate(long bps) {
514532
if (bps <= 0) return "";
@@ -674,22 +692,25 @@ private void flyTo(Region r) {
674692
}
675693

676694
private void confirmDelete(String name) {
677-
new MaterialAlertDialogBuilder(themed)
695+
// K2GO-385: a destructive region delete uses the shared BrandDialog (clay outlined destructive +
696+
// text Cancel), like the other from-scratch/erase confirms, instead of a flat MaterialAlertDialog
697+
// with two equal text buttons. themed (not the host activity) carries Theme_K2Go, which the dialog
698+
// frame needs -- PortalActivity's own theme is not K2Go.
699+
new BrandDialog(themed)
678700
.setTitle(str(R.string.k2go_fqr_delete_confirm_title, name))
679701
.setMessage(R.string.k2go_fqr_delete_confirm_msg)
680-
.setNegativeButton(R.string.k2go_cancel, null)
681-
.setPositiveButton(R.string.k2go_fqr_delete, (d, w) -> client.deleteRegion(name, new MapsRegionClient.DeleteListener() {
702+
.setDestructive(R.string.k2go_fqr_delete, () -> client.deleteRegion(name, new MapsRegionClient.DeleteListener() {
682703
@Override public void onOk() {
683704
toast(str(R.string.k2go_fqr_deleted, name));
684705
highlight = null;
685706
webView.reload(); // map redraws without the region
686707
refreshRegions(); // and the list drops it
687708
}
688709
@Override public void onError(String m) {
689-
new MaterialAlertDialogBuilder(themed).setTitle(R.string.k2go_fqr_delete_failed).setMessage(m)
690-
.setPositiveButton(android.R.string.ok, null).show();
710+
errorDialog(R.string.k2go_fqr_delete_failed, m);
691711
}
692712
}))
713+
.setNegative(R.string.k2go_cancel, null)
693714
.show();
694715
}
695716

@@ -712,7 +733,17 @@ private void closeDeleteSheetAndTool() {
712733
}
713734

714735
// ---- helpers -----------------------------------------------------------------------------
715-
/** Padded, transparent container for MaterialAlertDialog setView (the dialog paints the surface). */
736+
/** K2GO-385: a single-button (OK) error dialog on the shared BrandDialog, so the three FQR error
737+
* cases (estimate / download / delete) share one recipe. */
738+
private void errorDialog(int titleRes, CharSequence message) {
739+
new BrandDialog(themed)
740+
.setTitle(titleRes)
741+
.setMessage(message)
742+
.setPositive(android.R.string.ok, (BrandDialog.OnClick) null)
743+
.show();
744+
}
745+
746+
/** Padded, transparent container for the dialog body (BrandDialog paints the surface). */
716747
private LinearLayout dialogContent(int pad) { return dialogContent(pad, pad); }
717748

718749
/** Vertical content holder with horizontal/vertical insets (4dp grid). */

0 commit comments

Comments
 (0)