Skip to content

Commit 25f31aa

Browse files
Merge pull request #539 from appdevforall/feat/K2GO-385-dialog-consolidation
K2GO-385 feat(ui): finish the BrandDialog consolidation — checkbox slot, Stop-update collapse, app button system
2 parents e98cac0 + 905a1a4 commit 25f31aa

7 files changed

Lines changed: 206 additions & 118 deletions

File tree

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

Lines changed: 16 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -351,52 +351,22 @@ private void showBackupCancelDialog() {
351351
*/
352352
private void showDestructiveCancelDialog() {
353353
if (!isAdded()) return;
354-
final float density = getResources().getDisplayMetrics().density;
355-
// ADFA-5339 pattern: a MaterialCheckBox as a dialog's custom view sits flush-left; a holder padded by
356-
// dialogPreferredPadding (the title/message inset) with the checkbox's own left padding at 0 lines it
357-
// up with the text above.
358-
final com.google.android.material.checkbox.MaterialCheckBox box =
359-
new com.google.android.material.checkbox.MaterialCheckBox(requireContext());
360-
box.setText(R.string.k2go_br_cancel_extract_ack);
361-
box.setCompoundDrawablePadding(Math.round(8 * density));
362-
final int pad = dialogContentPadding();
363-
final android.widget.FrameLayout holder = new android.widget.FrameLayout(requireContext());
364-
holder.setPadding(pad, Math.round(8 * density), pad, 0);
365-
holder.addView(box);
366-
final androidx.appcompat.app.AlertDialog d =
367-
new com.google.android.material.dialog.MaterialAlertDialogBuilder(requireContext())
368-
.setTitle(R.string.k2go_br_cancel_title)
369-
.setMessage(R.string.k2go_br_cancel_extract_body)
370-
.setView(holder)
371-
.setPositiveButton(R.string.k2go_br_cancel_confirm, null) // click set below to gate dismiss
372-
.setNegativeButton(R.string.k2go_br_cancel_keep, null)
373-
.show();
374-
final android.widget.Button confirm = d.getButton(androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE);
375-
confirm.setTextColor(ContextCompat.getColor(requireContext(), R.color.btn_danger)); // same red as BrandDialog DESTRUCTIVE
376-
// K2GO-384: keep the button ENABLED and validate on click -- an inert disabled button gives no
377-
// feedback; a snackbar tells the user why nothing happened until they acknowledge.
378-
confirm.setOnClickListener(v -> {
379-
if (!box.isChecked()) {
380-
Snackbars.make(requireActivity().findViewById(android.R.id.content),
381-
R.string.k2go_br_cancel_extract_need_ack).show();
382-
return;
383-
}
384-
// K2GO-384: the destructive kill ends as FAILED with the "damaged" message (non-empty), so the
385-
// terminal shows -- the user sees "the next launch will start recovery" (no CANCELLED short-circuit).
386-
if (cancel != null) cancel.setEnabled(false);
387-
sendToService(DeepOpService.ACTION_FORCE_CANCEL);
388-
d.dismiss();
389-
});
390-
}
391-
392-
/** The dialog's horizontal content inset (title/message use it); a custom view must match it to line up
393-
* (ADFA-5339). */
394-
private int dialogContentPadding() {
395-
android.util.TypedValue tv = new android.util.TypedValue();
396-
if (requireContext().getTheme().resolveAttribute(androidx.appcompat.R.attr.dialogPreferredPadding, tv, true)) {
397-
return android.util.TypedValue.complexToDimensionPixelSize(tv.data, getResources().getDisplayMetrics());
398-
}
399-
return Math.round(24 * getResources().getDisplayMetrics().density); // Material default
354+
// K2GO-384: a checkbox-gated destructive confirm. The acknowledgement is required (the shared dialog
355+
// blocks the RED confirm and shows the inline "please tick" note until it is checked); the red comes
356+
// from BrandDialog's DESTRUCTIVE role.
357+
new BrandDialog(requireContext())
358+
.setTitle(R.string.k2go_br_cancel_title)
359+
.setMessage(R.string.k2go_br_cancel_extract_body)
360+
.setCheckbox(R.string.k2go_br_cancel_extract_ack, false)
361+
.requireCheckbox(R.string.k2go_br_cancel_extract_need_ack)
362+
.setDestructive(R.string.k2go_br_cancel_confirm, checked -> {
363+
// K2GO-384: the destructive kill ends as FAILED with the "damaged" message (non-empty), so
364+
// the terminal shows -- the user sees "the next launch will start recovery".
365+
if (cancel != null) cancel.setEnabled(false);
366+
sendToService(DeepOpService.ACTION_FORCE_CANCEL);
367+
})
368+
.setNegative(R.string.k2go_br_cancel_keep, null)
369+
.show();
400370
}
401371

402372
/** K2GO-384: Cancel is shown while the current pass is cancellable (PAUSABLE copy or ABORTABLE

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

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@
2020
import androidx.appcompat.app.AppCompatActivity;
2121
import androidx.core.content.ContextCompat;
2222

23-
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
24-
25-
import org.appdevforall.k2go.R;
23+
import org.appdevforall.k2go.ui.dialog.BrandDialog;
2624

2725
public final class DashboardCancelConfirmActivity extends AppCompatActivity {
2826

@@ -31,16 +29,11 @@ public final class DashboardCancelConfirmActivity extends AppCompatActivity {
3129
// If the update already ended between tapping the notification and getting here, there is
3230
// nothing to cancel — just close without a dialog.
3331
if (!DashboardRebuildService.isRunning()) { finish(); return; }
34-
new MaterialAlertDialogBuilder(this)
35-
.setTitle(R.string.k2go_dash_cancel_confirm_title)
36-
.setMessage(R.string.k2go_dash_cancel_confirm_msg)
37-
.setNegativeButton(R.string.k2go_dash_cancel_confirm_keep, (d, w) -> finish())
38-
.setPositiveButton(R.string.k2go_dash_cancel_confirm_stop, (d, w) -> {
39-
ContextCompat.startForegroundService(this, new Intent(this, DashboardRebuildService.class)
40-
.setAction(DashboardRebuildService.ACTION_CANCEL));
41-
finish();
42-
})
43-
.setOnCancelListener(d -> finish()) // tap-outside / back = keep updating
44-
.show();
32+
BrandDialog.Handle h = DashboardCancelDialog.show(this, () ->
33+
ContextCompat.startForegroundService(getApplicationContext(),
34+
new Intent(getApplicationContext(), DashboardRebuildService.class)
35+
.setAction(DashboardRebuildService.ACTION_CANCEL)));
36+
// Any dismissal — Stop, Keep updating, Back or a scrim tap — closes this windowless activity.
37+
h.getDialog().setOnDismissListener(d -> finish());
4538
}
4639
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* ============================================================================
3+
* Name : DashboardCancelDialog.java
4+
* Author : AppDevForAll
5+
* Copyright : Copyright (c) 2026 AppDevForAll
6+
* Description : K2GO-385. The one "Stop the update?" confirm, shared by the in-card Cancel
7+
* (DashboardDetailFragment) and the notification path (DashboardCancelConfirmActivity)
8+
* so the same dialog is not built twice. Only the stop action differs, so it is passed
9+
* in; the dialog itself lives here once.
10+
* ============================================================================
11+
*/
12+
package org.appdevforall.k2go.redesign;
13+
14+
import android.content.Context;
15+
16+
import org.appdevforall.k2go.R;
17+
import org.appdevforall.k2go.ui.dialog.BrandDialog;
18+
19+
public final class DashboardCancelDialog {
20+
21+
private DashboardCancelDialog() {}
22+
23+
/**
24+
* Show the shared "Stop the update?" confirm. {@code onStop} runs only on a positive answer;
25+
* "Keep updating", Back and a scrim tap all just dismiss. Returns the {@link BrandDialog.Handle}
26+
* so the notification activity can close itself on any dismissal.
27+
*/
28+
public static BrandDialog.Handle show(Context ctx, Runnable onStop) {
29+
return new BrandDialog(ctx)
30+
.setTitle(R.string.k2go_dash_cancel_confirm_title)
31+
.setMessage(R.string.k2go_dash_cancel_confirm_msg)
32+
.setDestructive(R.string.k2go_dash_cancel_confirm_stop, () -> onStop.run())
33+
.setNegative(R.string.k2go_dash_cancel_confirm_keep, null)
34+
.show();
35+
}
36+
}

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

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -332,12 +332,7 @@ private View buildUpdatingRow(Button rebuildBtn) {
332332
private void onCancelUpdate() {
333333
// ADFA-5339: stopping a live update is worth a confirm — the swap may be seconds away, and a
334334
// stray tap shouldn't abandon it. Only on a positive answer do we signal the server.
335-
new com.google.android.material.dialog.MaterialAlertDialogBuilder(requireContext())
336-
.setTitle(R.string.k2go_dash_cancel_confirm_title)
337-
.setMessage(R.string.k2go_dash_cancel_confirm_msg)
338-
.setNegativeButton(R.string.k2go_dash_cancel_confirm_keep, null)
339-
.setPositiveButton(R.string.k2go_dash_cancel_confirm_stop, (d, w) -> signalCancel())
340-
.show();
335+
DashboardCancelDialog.show(requireContext(), this::signalCancel);
341336
}
342337

343338
private void signalCancel() {

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

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,10 @@
3232
import androidx.annotation.NonNull;
3333
import androidx.fragment.app.Fragment;
3434

35-
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
36-
3735
import org.appdevforall.k2go.R;
3836
import org.appdevforall.k2go.install.presentation.InstallService;
3937
import org.appdevforall.k2go.system.domain.Operation;
38+
import org.appdevforall.k2go.ui.dialog.BrandDialog;
4039
import org.appdevforall.k2go.util.AppExecutors;
4140
import org.appdevforall.k2go.util.Snackbars;
4241

@@ -59,41 +58,20 @@ public static void confirmAndStart(@NonNull Fragment host, @NonNull View anchor,
5958
}
6059
// ADFA-5339: a versionless "Update the Website" checkbox (default on) rides on the confirm. It
6160
// refreshes the served landing page in the same run — a SEPARATE, unversioned artifact, so it
62-
// carries no version label. Built in code to leave the shared dialog usage untouched.
63-
final com.google.android.material.checkbox.MaterialCheckBox siteBox =
64-
new com.google.android.material.checkbox.MaterialCheckBox(ctx);
65-
siteBox.setText(R.string.k2go_dash_update_site);
66-
siteBox.setChecked(true);
67-
siteBox.setCompoundDrawablePadding(Math.round(8 * ctx.getResources().getDisplayMetrics().density));
68-
// Align the checkbox's left edge with the dialog's title/message, which are inset by
69-
// dialogPreferredPadding — the custom view otherwise sits flush left. A container carries that
70-
// inset so the checkbox's own left padding stays 0 and the box lines up with the text above.
71-
int pad = dialogPadding(ctx);
72-
android.widget.FrameLayout holder = new android.widget.FrameLayout(ctx);
73-
holder.setPadding(pad, Math.round(8 * ctx.getResources().getDisplayMetrics().density), pad, 0);
74-
holder.addView(siteBox);
61+
// carries no version label.
7562
// ADFA-5339: the confirm matches the primary action — "Update" when a newer build exists,
7663
// "Rebuild" for a manual re-apply — so the dialog can't say "Rebuild" over an "Update" button.
77-
new MaterialAlertDialogBuilder(ctx)
64+
new BrandDialog(ctx)
7865
.setTitle(updateAvailable ? R.string.k2go_dash_update_confirm_title
7966
: R.string.k2go_dash_rebuild_confirm_title)
8067
.setMessage(R.string.k2go_dash_rebuild_confirm_msg)
81-
.setView(holder)
82-
.setNegativeButton(android.R.string.cancel, null)
83-
.setPositiveButton(updateAvailable ? R.string.k2go_dash_update : R.string.k2go_dash_rebuild,
84-
(d, w) -> start(host, anchor, siteBox.isChecked()))
68+
.setCheckbox(R.string.k2go_dash_update_site, true)
69+
.setPositive(updateAvailable ? R.string.k2go_dash_update : R.string.k2go_dash_rebuild,
70+
BrandDialog.Role.PRIMARY, checked -> start(host, anchor, checked))
71+
.setNegative(android.R.string.cancel, null)
8572
.show();
8673
}
8774

88-
/** The dialog's horizontal content inset (title/message use it); the custom view must match it. */
89-
private static int dialogPadding(Context ctx) {
90-
android.util.TypedValue tv = new android.util.TypedValue();
91-
if (ctx.getTheme().resolveAttribute(androidx.appcompat.R.attr.dialogPreferredPadding, tv, true)) {
92-
return android.util.TypedValue.complexToDimensionPixelSize(tv.data, ctx.getResources().getDisplayMetrics());
93-
}
94-
return Math.round(24 * ctx.getResources().getDisplayMetrics().density); // Material default
95-
}
96-
9775
/** ADFA-5051: route by the installed dash-node version. >= 1.2.0 updates live over REST; older
9876
* installs take the proot rebuild once as a bridge to 1.2.0. The version read hits disk, so it
9977
* runs off the main thread; the routing itself is posted back to the UI. */

0 commit comments

Comments
 (0)