33import static com .fox2code .mmm .MainApplication .Iof ;
44import static com .fox2code .mmm .manager .ModuleInfo .FLAG_MM_REMOTE_MODULE ;
55
6- import android .Manifest ;
76import android .animation .Animator ;
87import android .animation .AnimatorListenerAdapter ;
98import android .annotation .SuppressLint ;
9+ import android .content .Context ;
1010import android .content .Intent ;
1111import android .content .SharedPreferences ;
12- import android .content .pm .PackageManager ;
1312import android .content .res .Configuration ;
1413import android .content .res .Resources ;
1514import android .graphics .Color ;
16- import android .net .Uri ;
1715import android .os .Build ;
1816import android .os .Bundle ;
1917import android .os .Handler ;
2018import android .os .Looper ;
21- import android .provider .Settings ;
2219import android .util .TypedValue ;
2320import android .view .View ;
2421import android .view .ViewGroup ;
2522import android .view .WindowManager ;
2623import android .view .animation .AccelerateInterpolator ;
2724import android .view .animation .DecelerateInterpolator ;
2825import android .view .inputmethod .EditorInfo ;
29- import android .widget .CheckBox ;
3026import android .widget .Toast ;
3127
3228import androidx .annotation .NonNull ;
3329import androidx .appcompat .widget .SearchView ;
3430import androidx .cardview .widget .CardView ;
35- import androidx .core .app .NotificationManagerCompat ;
36- import androidx .core .content .ContextCompat ;
37- import androidx .preference .PreferenceManager ;
3831import androidx .recyclerview .widget .LinearLayoutManager ;
3932import androidx .recyclerview .widget .RecyclerView ;
4033import androidx .swiperefreshlayout .widget .SwipeRefreshLayout ;
5144import com .fox2code .mmm .repo .RepoModule ;
5245import com .fox2code .mmm .settings .SettingsActivity ;
5346import com .fox2code .mmm .utils .ExternalHelper ;
47+ import com .fox2code .mmm .utils .RuntimeUtils ;
5448import com .fox2code .mmm .utils .io .net .Http ;
5549import com .fox2code .mmm .utils .realm .ReposList ;
5650import com .google .android .material .bottomnavigation .BottomNavigationView ;
57- import com .google .android .material .dialog .MaterialAlertDialogBuilder ;
5851import com .google .android .material .progressindicator .LinearProgressIndicator ;
59- import com .google .android .material .snackbar .Snackbar ;
6052
6153import org .matomo .sdk .extra .TrackHelper ;
6254
6355import java .sql .Timestamp ;
6456import java .util .ArrayList ;
6557import java .util .List ;
66- import java .util .Objects ;
6758
6859import io .realm .Realm ;
6960import io .realm .RealmConfiguration ;
@@ -91,6 +82,7 @@ public class MainActivity extends FoxActivity implements SwipeRefreshLayout.OnRe
9182 private CardView searchCard ;
9283 private SearchView searchView ;
9384 private boolean initMode ;
85+ private RuntimeUtils runtimeUtils ;
9486
9587 public MainActivity () {
9688 this .moduleViewListBuilder = new ModuleViewListBuilder (this );
@@ -171,10 +163,11 @@ protected void onCreate(Bundle savedInstanceState) {
171163 this .moduleListOnline .setLayoutManager (new LinearLayoutManager (this ));
172164 this .moduleList .setItemViewCacheSize (4 ); // Default is 2
173165 this .swipeRefreshLayout .setOnRefreshListener (this );
166+ this .runtimeUtils = new RuntimeUtils ();
174167 // add background blur if enabled
175168 this .updateBlurState ();
176169 //hideActionBar();
177- checkShowInitialSetup ();
170+ runtimeUtils . checkShowInitialSetup (this , this );
178171 this .moduleList .addOnScrollListener (new RecyclerView .OnScrollListener () {
179172 @ Override
180173 public void onScrollStateChanged (@ NonNull RecyclerView recyclerView , int newState ) {
@@ -326,7 +319,8 @@ public void commonNext() {
326319 });
327320 }
328321 updateScreenInsets (); // Fix an edge case
329- if (waitInitialSetupFinished ()) {
322+ Context context = MainActivity .this ;
323+ if (runtimeUtils .waitInitialSetupFinished (context , MainActivity .this )) {
330324 Timber .d ("waiting..." );
331325 return ;
332326 }
@@ -432,7 +426,7 @@ public void commonNext() {
432426 // if system lang is not in MainApplication.supportedLocales, show a snackbar to ask user to help translate
433427 if (!MainApplication .supportedLocales .contains (this .getResources ().getConfiguration ().getLocales ().get (0 ).getLanguage ())) {
434428 // call showWeblateSnackbar() with language code and language name
435- showWeblateSnackbar (this .getResources ().getConfiguration ().getLocales ().get (0 ).getLanguage (), this .getResources ().getConfiguration ().getLocales ().get (0 ).getDisplayLanguage ());
429+ runtimeUtils . showWeblateSnackbar (this , this , this .getResources ().getConfiguration ().getLocales ().get (0 ).getLanguage (), this .getResources ().getConfiguration ().getLocales ().get (0 ).getDisplayLanguage ());
436430 }
437431 ExternalHelper .INSTANCE .refreshHelper (this );
438432 this .initMode = false ;
@@ -466,7 +460,7 @@ private void cardIconifyUpdate() {
466460 this .searchCard .setAlpha (iconified ? 0.80F : 1F );
467461 }
468462
469- private void updateScreenInsets () {
463+ public void updateScreenInsets () {
470464 this .runOnUiThread (() -> this .updateScreenInsets (this .getResources ().getConfiguration ()));
471465 }
472466
@@ -517,7 +511,9 @@ public void refreshUI() {
517511 InstallerInitializer .tryGetMagiskPathAsync (new InstallerInitializer .Callback () {
518512 @ Override
519513 public void onPathReceived (String path ) {
520- checkShowInitialSetup ();
514+ Context context = MainActivity .this ;
515+ MainActivity mainActivity = MainActivity .this ;
516+ runtimeUtils .checkShowInitialSetup (context , mainActivity );
521517 // Wait for doSetupNow to finish
522518 while (doSetupNowRunning ) {
523519 try {
@@ -706,156 +702,4 @@ public void onWindowFocusChanged(boolean hasFocus) {
706702 super .onWindowFocusChanged (hasFocus );
707703 this .updateScreenInsets ();
708704 }
709-
710- @ SuppressLint ("RestrictedApi" )
711- private void ensurePermissions () {
712- if (BuildConfig .DEBUG ) Timber .i ("Ensure Permissions" );
713- // First, check if user has said don't ask again by checking if pref_dont_ask_again_notification_permission is true
714- if (!PreferenceManager .getDefaultSharedPreferences (this ).getBoolean ("pref_dont_ask_again_notification_permission" , false )) {
715- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU && ContextCompat .checkSelfPermission (this , Manifest .permission .POST_NOTIFICATIONS ) != PackageManager .PERMISSION_GRANTED ) {
716- if (BuildConfig .DEBUG ) Timber .i ("Request Notification Permission" );
717- if (FoxActivity .getFoxActivity (this ).shouldShowRequestPermissionRationale (Manifest .permission .POST_NOTIFICATIONS )) {
718- // Show a dialog explaining why we need this permission, which is to show
719- // notifications for updates
720- runOnUiThread (() -> {
721- if (BuildConfig .DEBUG ) Timber .i ("Show Notification Permission Dialog" );
722- MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder (this );
723- builder .setTitle (R .string .permission_notification_title );
724- builder .setMessage (R .string .permission_notification_message );
725- // Don't ask again checkbox
726- View view = getLayoutInflater ().inflate (R .layout .dialog_checkbox , null );
727- CheckBox checkBox = view .findViewById (R .id .checkbox );
728- checkBox .setText (R .string .dont_ask_again );
729- checkBox .setOnCheckedChangeListener ((buttonView , isChecked ) -> PreferenceManager .getDefaultSharedPreferences (this ).edit ().putBoolean ("pref_dont_ask_again_notification_permission" , isChecked ).apply ());
730- builder .setView (view );
731- builder .setPositiveButton (R .string .permission_notification_grant , (dialog , which ) -> {
732- // Request the permission
733- this .requestPermissions (new String []{Manifest .permission .POST_NOTIFICATIONS }, 0 );
734- doSetupNowRunning = false ;
735- });
736- builder .setNegativeButton (R .string .cancel , (dialog , which ) -> {
737- // Set pref_background_update_check to false and dismiss dialog
738- SharedPreferences prefs = PreferenceManager .getDefaultSharedPreferences (this );
739- prefs .edit ().putBoolean ("pref_background_update_check" , false ).apply ();
740- dialog .dismiss ();
741- doSetupNowRunning = false ;
742- });
743- builder .show ();
744- if (BuildConfig .DEBUG ) Timber .i ("Show Notification Permission Dialog Done" );
745- });
746- } else {
747- // Request the permission
748- if (BuildConfig .DEBUG ) Timber .i ("Request Notification Permission" );
749- this .requestPermissions (new String []{Manifest .permission .POST_NOTIFICATIONS }, 0 );
750- if (BuildConfig .DEBUG ) {
751- // Log if granted via onRequestPermissionsResult
752- boolean granted = ContextCompat .checkSelfPermission (this , Manifest .permission .POST_NOTIFICATIONS ) == PackageManager .PERMISSION_GRANTED ;
753- Timber .i ("Request Notification Permission Done. Result: %s" , granted );
754- }
755- doSetupNowRunning = false ;
756- }
757- // Next branch is for < android 13 and user has blocked notifications
758- } else if (Build .VERSION .SDK_INT < Build .VERSION_CODES .TIRAMISU && !NotificationManagerCompat .from (this ).areNotificationsEnabled ()) {
759- runOnUiThread (() -> {
760- MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder (this );
761- builder .setTitle (R .string .permission_notification_title );
762- builder .setMessage (R .string .permission_notification_message );
763- // Don't ask again checkbox
764- View view = getLayoutInflater ().inflate (R .layout .dialog_checkbox , null );
765- CheckBox checkBox = view .findViewById (R .id .checkbox );
766- checkBox .setText (R .string .dont_ask_again );
767- checkBox .setOnCheckedChangeListener ((buttonView , isChecked ) -> PreferenceManager .getDefaultSharedPreferences (this ).edit ().putBoolean ("pref_dont_ask_again_notification_permission" , isChecked ).apply ());
768- builder .setView (view );
769- builder .setPositiveButton (R .string .permission_notification_grant , (dialog , which ) -> {
770- // Open notification settings
771- Intent intent = new Intent ();
772- intent .setAction (Settings .ACTION_APPLICATION_DETAILS_SETTINGS );
773- Uri uri = Uri .fromParts ("package" , getPackageName (), null );
774- intent .setData (uri );
775- startActivity (intent );
776- doSetupNowRunning = false ;
777- });
778- builder .setNegativeButton (R .string .cancel , (dialog , which ) -> {
779- // Set pref_background_update_check to false and dismiss dialog
780- SharedPreferences prefs = PreferenceManager .getDefaultSharedPreferences (this );
781- prefs .edit ().putBoolean ("pref_background_update_check" , false ).apply ();
782- dialog .dismiss ();
783- doSetupNowRunning = false ;
784- });
785- builder .show ();
786- });
787- } else {
788- doSetupNowRunning = false ;
789- }
790- } else {
791- if (BuildConfig .DEBUG )
792- Timber .i ("Notification Permission Already Granted or Don't Ask Again" );
793- doSetupNowRunning = false ;
794- }
795- }
796-
797- // Method to show a setup box on first launch
798- @ SuppressLint ({"InflateParams" , "RestrictedApi" , "UnspecifiedImmutableFlag" , "ApplySharedPref" })
799- private void checkShowInitialSetup () {
800- if (BuildConfig .DEBUG ) Timber .i ("Checking if we need to run setup" );
801- // Check if this is the first launch using prefs and if doSetupRestarting was passed in the intent
802- SharedPreferences prefs = MainApplication .getSharedPreferences ("mmm" );
803- boolean firstLaunch = !Objects .equals (prefs .getString ("last_shown_setup" , null ), "v2" );
804- // First launch
805- // this is intentionally separate from the above if statement, because it needs to be checked even if the first launch check is true due to some weird edge cases
806- if (getIntent ().getBooleanExtra ("doSetupRestarting" , false )) {
807- // Restarting setup
808- firstLaunch = false ;
809- }
810- if (BuildConfig .DEBUG ) {
811- Timber .i ("First launch: %s, pref value: %s" , firstLaunch , prefs .getString ("last_shown_setup" , null ));
812- }
813- if (firstLaunch ) {
814- doSetupNowRunning = true ;
815- // Launch setup wizard
816- if (BuildConfig .DEBUG ) Timber .i ("Launching setup wizard" );
817- // Show setup activity
818- Intent intent = new Intent (this , SetupActivity .class );
819- finish ();
820- startActivity (intent );
821- } else {
822- ensurePermissions ();
823- }
824- }
825-
826- /**
827- * @return true if the load workflow must be stopped.
828- */
829- private boolean waitInitialSetupFinished () {
830- if (BuildConfig .DEBUG ) Timber .i ("waitInitialSetupFinished" );
831- if (doSetupNowRunning ) updateScreenInsets (); // Fix an edge case
832- try {
833- // Wait for doSetupNow to finish
834- while (doSetupNowRunning ) {
835- //noinspection BusyWait
836- Thread .sleep (50 );
837- }
838- } catch (InterruptedException e ) {
839- Thread .currentThread ().interrupt ();
840- return true ;
841- }
842- return doSetupRestarting ;
843- }
844-
845- /**
846- * Shows a snackbar offering to take users to Weblate if their language is not available.
847- *
848- * @param language The language code.
849- * @param languageName The language name.
850- */
851- @ SuppressLint ("RestrictedApi" )
852- private void showWeblateSnackbar (String language , String languageName ) {
853- Snackbar snackbar = Snackbar .make (findViewById (R .id .root_container ), getString (R .string .language_not_available , languageName ), Snackbar .LENGTH_LONG );
854- snackbar .setAction (R .string .ok , v -> {
855- Intent intent = new Intent (Intent .ACTION_VIEW );
856- intent .setData (Uri .parse ("https://translate.nift4.org/engage/foxmmm/?language=" + language ));
857- startActivity (intent );
858- });
859- snackbar .show ();
860- }
861705}
0 commit comments