@@ -116,6 +116,10 @@ public class DeployFragment extends Fragment {
116116 // Native Engine Variables
117117 private static Aria2Manager aria2Manager ;
118118 private static boolean isDownloadingRootfs = false ;
119+ // State Variables (New control variables)
120+ private boolean isRestoring = false ;
121+ private boolean isDeleting = false ;
122+ private boolean isImporting = false ;
119123 private PRootEngine prootEngine ;
120124
121125 // Background Handlers
@@ -591,53 +595,62 @@ else if (isServerRunning)
591595 btnAdvancedForceStop .setOnClickListener (v -> openTermuxAppInfo ());
592596 }
593597
594- if (isServerRunning ) {
595- // CASE A: Server Running (Lock UI)
598+ boolean isBusy = isSystemBusy ();
599+
600+ // 1. ALWAYS link the buttons so Listeners can intercept and drop the Snackbar
601+ bindInstallButtonLogic (mainAct , debianRootfs , iiabRootDir );
602+ bindDeleteButtonLogic (mainAct , debianRootfs );
603+ bindBackupButtonLogic (mainAct , backupsDir , iiabRootDir );
604+ bindResetButtonLogic (mainAct , debianRootfs );
605+ bindBackupMenuLogic (backupsDir );
606+ refreshRestoreButtonLogic ();
607+
608+ if (isServerRunning || isBusy ) {
609+ // LOCK MODE: Server On or System Busy
596610 float lockAlpha = 0.5f ;
597- btnFastInstall .setAlpha (lockAlpha );
598- btnFastDelete .setAlpha (lockAlpha );
599- if (btnAdvancedBackup != null ) btnAdvancedBackup .setAlpha (lockAlpha );
600- if (btnAdvancedRestore != null ) btnAdvancedRestore .setAlpha (lockAlpha );
601- if (btnAdvancedReset != null ) btnAdvancedReset .setAlpha (lockAlpha );
602- if (txtSelectBackupTitle != null ) txtSelectBackupTitle .setAlpha (lockAlpha );
603611
604- btnFastInstall .setText (R .string .install_btn_reinstall );
605- View .OnClickListener serverHot = v -> Snackbar .make (v , R .string .install_msg_server_running_lock , Snackbar .LENGTH_LONG ).show ();
606- btnFastInstall .setOnClickListener (serverHot );
607- btnFastDelete .setOnClickListener (serverHot );
608- if (btnAdvancedBackup != null ) btnAdvancedBackup .setOnClickListener (serverHot );
609- if (btnAdvancedRestore != null ) btnAdvancedRestore .setOnClickListener (serverHot );
610- if (btnAdvancedReset != null ) btnAdvancedReset .setOnClickListener (serverHot );
611- if (txtSelectBackupTitle != null ) txtSelectBackupTitle .setOnClickListener (serverHot );
612+ // We keep the opacity at 80% only for the button that is currently working
613+ btnFastInstall .setAlpha ((isDownloadingRootfs && !isServerRunning ) ? 0.8f : lockAlpha );
614+ btnFastDelete .setAlpha (isDeleting ? 0.8f : lockAlpha );
615+ if (btnAdvancedBackup != null ) btnAdvancedBackup .setAlpha (isBackupInProgress ? 0.8f : lockAlpha );
616+ if (btnAdvancedRestore != null ) btnAdvancedRestore .setAlpha (isRestoring ? 0.8f : lockAlpha );
617+ if (btnAdvancedReset != null ) btnAdvancedReset .setAlpha ((isDownloadingRootfs && !isServerRunning ) ? 0.8f : lockAlpha );
618+ if (txtSelectBackupTitle != null ) txtSelectBackupTitle .setAlpha (lockAlpha );
619+ if (btnImportBackup != null ) btnImportBackup .setAlpha (isImporting ? 0.8f : lockAlpha );
620+
621+ // Lock module checkboxes in the grid
622+ for (CheckBox cb : newInstallCheckboxes ) {
623+ cb .setEnabled (false );
624+ View card = (View ) cb .getParent ().getParent ();
625+ card .setAlpha (0.6f );
626+ card .setOnClickListener (v -> {
627+ String msg = isServerRunning ? getString (R .string .install_msg_server_running_lock ) : getSystemBusyMessage ();
628+ Snackbar .make (v , msg , Snackbar .LENGTH_LONG ).show ();
629+ });
630+ }
612631
613632 } else {
614- // CASE B: Server Offline (Enable UI)
633+ // FREE MODE: All off and ready to operate
615634 if (selectedTier == null || !isStorageSafe ) btnFastInstall .setAlpha (0.4f );
616635 else btnFastInstall .setAlpha (1.0f );
617636
618637 btnFastDelete .setAlpha (1.0f );
619638 if (btnAdvancedBackup != null ) btnAdvancedBackup .setAlpha (1.0f );
620639 if (btnAdvancedReset != null ) btnAdvancedReset .setAlpha (1.0f );
621640 if (txtSelectBackupTitle != null ) txtSelectBackupTitle .setAlpha (1.0f );
622- refreshRestoreButtonLogic ();
623-
624- if (!isDownloadingRootfs ) {
625- btnFastInstall .setEnabled (true );
626- btnFastInstall .setTextSize (14f );
627- if (isProotInstalled ) btnFastInstall .setText (R .string .install_btn_reinstall );
628- else btnFastInstall .setText (R .string .install_btn_install );
629- } else {
630- btnFastInstall .setEnabled (true );
631- btnFastInstall .setTextSize (12f );
632- btnFastInstall .setAlpha (0.8f );
641+ if (btnImportBackup != null ) btnImportBackup .setAlpha (1.0f );
642+
643+ btnFastInstall .setEnabled (true );
644+ btnFastInstall .setTextSize (14f );
645+ btnFastInstall .setText (isProotInstalled ? R .string .install_btn_reinstall : R .string .install_btn_install );
646+
647+ // Unlock checkboxes
648+ for (CheckBox cb : newInstallCheckboxes ) {
649+ cb .setEnabled (true );
650+ View card = (View ) cb .getParent ().getParent ();
651+ card .setAlpha (1.0f );
652+ card .setOnClickListener (v -> cb .toggle ());
633653 }
634-
635- // Bind native buttons
636- bindInstallButtonLogic (mainAct , debianRootfs , iiabRootDir );
637- bindDeleteButtonLogic (mainAct , debianRootfs );
638- bindBackupButtonLogic (mainAct , backupsDir , iiabRootDir );
639- bindResetButtonLogic (mainAct , debianRootfs );
640- bindBackupMenuLogic (backupsDir );
641654 }
642655 }
643656
@@ -916,14 +929,13 @@ public void onError(String error) {
916929
917930 private void bindInstallButtonLogic (MainActivity mainAct , File debianRootfs , File iiabRootDir ) {
918931 btnFastInstall .setOnClickListener (v -> {
919- if (selectedTier == null ) {
920- Snackbar .make (v , R .string .install_error_no_tier , Snackbar .LENGTH_LONG ).show ();
921- return ;
922- }
923- if (!isStorageSafe ) {
924- Snackbar .make (v , R .string .install_error_no_storage , Snackbar .LENGTH_LONG ).show ();
932+ // 1. Main Lock: Server On
933+ if (mainAct .isServerAlive ) {
934+ Snackbar .make (v , R .string .install_msg_server_running_lock , Snackbar .LENGTH_LONG ).show ();
925935 return ;
926936 }
937+
938+ // 2. HIGH PRIORITY: If this button is working, we allow cancel
927939 if (isDownloadingRootfs ) {
928940 new android .app .AlertDialog .Builder (requireContext ())
929941 .setTitle (getString (R .string .install_btn_cancel_title ))
@@ -935,12 +947,30 @@ private void bindInstallButtonLogic(MainActivity mainAct, File debianRootfs, Fil
935947 btnFastInstall .setText (R .string .install_btn_install );
936948 btnFastInstall .setAlpha (1.0f );
937949 Snackbar .make (getView (), R .string .install_msg_cancelled , Snackbar .LENGTH_SHORT ).show ();
950+ updateDynamicButtons ();
938951 })
939952 .setNegativeButton (getString (R .string .cancel ), null )
940953 .show ();
941954 return ;
942955 }
943956
957+ // 3. If it is not working, but the system is busy with something else: LOCK
958+ if (isSystemBusy ()) {
959+ Snackbar .make (v , getSystemBusyMessage (), Snackbar .LENGTH_LONG ).show ();
960+ return ;
961+ }
962+
963+ // 4. Normal installation startup validations
964+ if (selectedTier == null ) {
965+ Snackbar .make (v , R .string .install_error_no_tier , Snackbar .LENGTH_LONG ).show ();
966+ return ;
967+ }
968+ if (!isStorageSafe ) {
969+ Snackbar .make (v , R .string .install_error_no_storage , Snackbar .LENGTH_LONG ).show ();
970+ return ;
971+ }
972+
973+ // 5. Start the installation...
944974 Runnable executeDownload = () -> {
945975 enableSystemProtection ();
946976 mainAct .invalidateModuleStateTrust ();
@@ -1056,10 +1086,22 @@ public void onError(String error) {
10561086
10571087 private void bindDeleteButtonLogic (MainActivity mainAct , File debianRootfs ) {
10581088 btnFastDelete .setOnClickListener (v -> {
1089+ if (mainAct .isServerAlive ) {
1090+ Snackbar .make (v , R .string .install_msg_server_running_lock , Snackbar .LENGTH_LONG ).show ();
1091+ return ;
1092+ }
1093+ if (isSystemBusy ()) {
1094+ Snackbar .make (v , getSystemBusyMessage (), Snackbar .LENGTH_LONG ).show ();
1095+ return ;
1096+ }
1097+
10591098 new android .app .AlertDialog .Builder (requireContext ())
10601099 .setTitle (R .string .install_dialog_delete_title )
10611100 .setMessage (R .string .install_dialog_delete_msg )
10621101 .setPositiveButton (R .string .install_btn_delete_confirm , (dialog , which ) -> {
1102+ isDeleting = true ;
1103+ mainAct .runOnUiThread (this ::updateDynamicButtons );
1104+
10631105 mainAct .invalidateModuleStateTrust ();
10641106 btnFastDelete .setEnabled (false );
10651107 Snackbar .make (getView (), R .string .install_status_deleting , Snackbar .LENGTH_SHORT ).show ();
@@ -1068,10 +1110,11 @@ private void bindDeleteButtonLogic(MainActivity mainAct, File debianRootfs) {
10681110 try {
10691111 Process p = Runtime .getRuntime ().exec (new String []{"rm" , "-rf" , debianRootfs .getAbsolutePath ()});
10701112 p .waitFor ();
1071- mainAct .runOnUiThread (this ::updateDynamicButtons );
10721113 } catch (Exception e ) {
10731114 mainAct .runOnUiThread (() -> Snackbar .make (getView (), getString (R .string .install_error_delete , e .getMessage ()), Snackbar .LENGTH_LONG ).show ());
10741115 } finally {
1116+ isDeleting = false ;
1117+ mainAct .runOnUiThread (this ::updateDynamicButtons );
10751118 disableSystemProtection ();
10761119 }
10771120 }).start ();
@@ -1085,42 +1128,15 @@ private void bindResetButtonLogic(MainActivity mainAct, File debianRootfs) {
10851128 if (btnAdvancedReset == null ) return ;
10861129 btnAdvancedReset .setOnClickListener (v -> {
10871130
1088- // 1. Safe Abortion Phase
1089- if (isDownloadingRootfs ) {
1090- new android .app .AlertDialog .Builder (requireContext ())
1091- .setTitle (getString (R .string .install_btn_cancel_title ))
1092- .setMessage (getString (R .string .install_btn_cancel_msg ))
1093- .setPositiveButton (getString (R .string .install_btn_cancel_confirm ), (dialog , which ) -> {
1094- if (aria2Manager != null ) aria2Manager .stopDownload ();
1095- disableSystemProtection ();
1096- isDownloadingRootfs = false ;
1097-
1098- // We clean the remains of the failed download if they exist
1099- String arch = getTermuxArch ();
1100- String archSuffix = (arch .contains ("arm" ) && !arch .contains ("64" )) ? "arm" : "aarch64" ;
1101- String tarball = "debian-trixie-" + archSuffix + "-pd-v4.29.0.tar.xz" ;
1102- File partialDownload = new File ("/storage/emulated/0/Download" , tarball );
1103- if (partialDownload .exists ()) partialDownload .delete ();
1104- File ariaState = new File ("/storage/emulated/0/Download" , tarball + ".aria2" );
1105- if (ariaState .exists ()) ariaState .delete ();
1106-
1107- btnAdvancedReset .setText (getString (R .string .install_btn_reset ));
1108- btnAdvancedReset .setEnabled (true );
1109- updateDynamicButtons ();
1110- Snackbar .make (getView (), R .string .install_msg_cancelled , Snackbar .LENGTH_SHORT ).show ();
1111- })
1112- .setNegativeButton (R .string .cancel , null )
1113- .show ();
1131+ if (mainAct .isServerAlive ) {
1132+ Snackbar .make (v , R .string .install_msg_server_running_lock , Snackbar .LENGTH_LONG ).show ();
11141133 return ;
11151134 }
1116-
1117- // 2. IF THE SYSTEM IS ALREADY DOING SOMETHING ELSE OR HAS ALREADY STARTED EXTRACTING
1118- if (btnAdvancedReset .getText ().toString ().contains ("Extract" ) || btnAdvancedReset .getText ().toString ().contains ("Bootstrap" )) {
1119- Snackbar .make (getView (), "System extraction in progress. Please do not interrupt." , Snackbar .LENGTH_SHORT ).show ();
1135+ if (isSystemBusy ()) {
1136+ Snackbar .make (v , getSystemBusyMessage (), Snackbar .LENGTH_LONG ).show ();
11201137 return ;
11211138 }
1122-
1123- // 3. NORMAL STATE: RESET START
1139+ // NORMAL STATE: RESET START
11241140 new android .app .AlertDialog .Builder (requireContext ())
11251141 .setTitle (R .string .install_dialog_reset_title )
11261142 .setMessage (R .string .install_dialog_reset_msg )
@@ -1695,8 +1711,19 @@ private void evaluateLaunchButton() {
16951711
16961712 if (hasSelections ) {
16971713 btnLaunchInstall .setOnClickListener (v -> {
1714+ MainActivity mainAct = (MainActivity ) getActivity ();
1715+ if (mainAct != null && mainAct .isServerAlive ) {
1716+ Snackbar .make (v , R .string .install_msg_server_running_lock , Snackbar .LENGTH_LONG ).show ();
1717+ return ;
1718+ }
1719+ if (isSystemBusy () && !isBatchInstalling ) {
1720+ Snackbar .make (v , getSystemBusyMessage (), Snackbar .LENGTH_LONG ).show ();
1721+ return ;
1722+ }
1723+
16981724 isBatchInstalling = true ;
16991725 saveQueueToPrefs ();
1726+ updateDynamicButtons ();
17001727 processNextInQueue ();
17011728 });
17021729 } else {
@@ -1722,6 +1749,14 @@ private void setupSafLaunchers() {
17221749 private void bindBackupButtonLogic (MainActivity mainAct , File backupsDir , File iiabRootDir ) {
17231750 if (btnAdvancedBackup == null ) return ;
17241751 btnAdvancedBackup .setOnClickListener (v -> {
1752+ if (mainAct .isServerAlive ) {
1753+ Snackbar .make (v , R .string .install_msg_server_running_lock , Snackbar .LENGTH_LONG ).show ();
1754+ return ;
1755+ }
1756+ if (isSystemBusy () && !isBackupInProgress ) {
1757+ Snackbar .make (v , getSystemBusyMessage (), Snackbar .LENGTH_LONG ).show ();
1758+ return ;
1759+ }
17251760 if (isBackupInProgress ) {
17261761 new android .app .AlertDialog .Builder (requireContext ())
17271762 .setTitle (getString (R .string .install_msg_backup_in_progress_title ))
@@ -1989,11 +2024,24 @@ private void refreshRestoreButtonLogic() {
19892024 } else {
19902025 btnAdvancedRestore .setAlpha (1.0f );
19912026 btnAdvancedRestore .setOnClickListener (v -> {
2027+ if (mainAct .isServerAlive ) {
2028+ Snackbar .make (v , R .string .install_msg_server_running_lock , Snackbar .LENGTH_LONG ).show ();
2029+ return ;
2030+ }
2031+ if (isSystemBusy ()) {
2032+ Snackbar .make (v , getSystemBusyMessage (), Snackbar .LENGTH_LONG ).show ();
2033+ return ;
2034+ }
2035+
2036+ isRestoring = true ;
2037+ updateDynamicButtons ();
19922038 Snackbar .make (v , getString (R .string .install_msg_restore_starting , selectedBackupFile ), Snackbar .LENGTH_SHORT ).show ();
19932039 mainAct .invalidateModuleStateTrust ();
19942040
19952041 File backupFile = new File (new File (requireContext ().getFilesDir (), "rootfs/backups" ), selectedBackupFile );
19962042 if (!backupFile .exists ()) {
2043+ isRestoring = false ;
2044+ updateDynamicButtons ();
19972045 Snackbar .make (v , R .string .install_error_backup_missing , Snackbar .LENGTH_SHORT ).show ();
19982046 return ;
19992047 }
@@ -2008,6 +2056,7 @@ private void refreshRestoreButtonLogic() {
20082056 @ Override
20092057 public void onComplete (String destDir ) {
20102058 mainAct .runOnUiThread (() -> {
2059+ isRestoring = false ;
20112060 disableSystemProtection ();
20122061 btnAdvancedRestore .setEnabled (true );
20132062 btnAdvancedRestore .setText (getString (R .string .install_btn_restore ));
@@ -2019,6 +2068,7 @@ public void onComplete(String destDir) {
20192068 @ Override
20202069 public void onError (String error ) {
20212070 mainAct .runOnUiThread (() -> {
2071+ isRestoring = false ;
20222072 disableSystemProtection ();
20232073 btnAdvancedRestore .setEnabled (true );
20242074 btnAdvancedRestore .setText (getString (R .string .install_btn_restore ));
@@ -2032,6 +2082,8 @@ public void onError(String error) {
20322082 }
20332083
20342084 private void importBackupSafely (Uri sourceUri ) {
2085+ isImporting = true ;
2086+ updateDynamicButtons ();
20352087 btnImportBackup .setEnabled (false );
20362088 btnImportBackup .setText (getString (R .string .install_msg_importing ));
20372089 Snackbar .make (getView (), getString (R .string .install_msg_importing ), Snackbar .LENGTH_LONG ).show ();
@@ -2058,6 +2110,7 @@ private void importBackupSafely(Uri sourceUri) {
20582110
20592111 if (getActivity () != null ) {
20602112 getActivity ().runOnUiThread (() -> {
2113+ isImporting = false ;
20612114 btnImportBackup .setEnabled (true );
20622115 btnImportBackup .setText (getString (R .string .install_btn_import_backup ));
20632116 selectedBackupFile = fileName ;
@@ -2068,6 +2121,8 @@ private void importBackupSafely(Uri sourceUri) {
20682121 } catch (Exception e ) {
20692122 if (getActivity () != null ) {
20702123 getActivity ().runOnUiThread (() -> {
2124+ isImporting = false ;
2125+ updateDynamicButtons ();
20712126 btnImportBackup .setEnabled (true );
20722127 btnImportBackup .setText (getString (R .string .install_btn_import_backup ));
20732128 Snackbar .make (getView (), getString (R .string .install_msg_import_failed , e .getMessage ()), Snackbar .LENGTH_LONG ).show ();
@@ -2485,6 +2540,20 @@ private void addCpuEntry(float cpuPercentage) {
24852540 // REGION 8: UTILITIES
24862541 // =========================================================================================
24872542
2543+ private boolean isSystemBusy () {
2544+ return isDownloadingRootfs || isBatchInstalling || isBackupInProgress || isRestoring || isDeleting || isImporting ;
2545+ }
2546+
2547+ private String getSystemBusyMessage () {
2548+ if (isDownloadingRootfs ) return getString (R .string .install_busy_provisioning );
2549+ if (isBatchInstalling ) return getString (R .string .install_busy_modules );
2550+ if (isBackupInProgress ) return getString (R .string .install_busy_backup );
2551+ if (isRestoring ) return getString (R .string .install_busy_restore );
2552+ if (isDeleting ) return getString (R .string .install_busy_delete );
2553+ if (isImporting ) return getString (R .string .install_busy_import );
2554+ return getString (R .string .install_busy_generic );
2555+ }
2556+
24882557 private void enableSystemProtection () {
24892558 // SAFE CHECK
24902559 if (!isAdded () || getContext () == null ) return ;
0 commit comments