From 462bbc0e58091e300255b1870c635929cef886b5 Mon Sep 17 00:00:00 2001 From: artdeell Date: Tue, 17 Oct 2023 17:25:40 +0300 Subject: [PATCH 1/2] Feat[launcher]: request notification permission on startup (+extra handling) --- .../net/kdt/pojavlaunch/LauncherActivity.java | 67 +++++++++++++++++++ .../main/java/net/kdt/pojavlaunch/Tools.java | 6 ++ .../contextexecutor/ContextExecutor.java | 9 +-- .../prefs/LauncherPreferences.java | 4 ++ .../screens/LauncherPreferenceFragment.java | 22 ++++++ .../src/main/res/values/strings.xml | 5 ++ .../src/main/res/xml/pref_main.xml | 6 ++ 7 files changed, 113 insertions(+), 6 deletions(-) diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/LauncherActivity.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/LauncherActivity.java index 552be5aea..ee1e31ef0 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/LauncherActivity.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/LauncherActivity.java @@ -1,15 +1,20 @@ package net.kdt.pojavlaunch; +import android.Manifest; import android.app.NotificationManager; import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Build; import android.os.Bundle; import android.view.View; import android.widget.ImageButton; import android.widget.Toast; import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; +import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentContainerView; @@ -40,6 +45,8 @@ import net.kdt.pojavlaunch.utils.NotificationUtils; import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles; import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile; +import java.lang.ref.WeakReference; + public class LauncherActivity extends BaseActivity { public static final String SETTING_FRAGMENT_TAG = "SETTINGS_FRAGMENT"; @@ -144,13 +151,27 @@ public class LauncherActivity extends BaseActivity { } }; + private ActivityResultLauncher mRequestNotificationPermissionLauncher; + private WeakReference mRequestNotificationPermissionRunnable; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_pojav_launcher); IconCacheJanitor.runJanitor(); + mRequestNotificationPermissionLauncher = registerForActivityResult( + new ActivityResultContracts.RequestPermission(), + isAllowed -> { + if(!isAllowed) handleNoNotificationPermission(); + else { + Runnable runnable = Tools.getWeakReference(mRequestNotificationPermissionRunnable); + if(runnable != null) runnable.run(); + } + } + ); getWindow().setBackgroundDrawable(null); bindViews(); + checkNotificationPermission(); mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); ProgressKeeper.addTaskCountListener(mDoubleLaunchPreventionListener); ProgressKeeper.addTaskCountListener((mProgressServiceKeeper = new ProgressServiceKeeper(this))); @@ -249,6 +270,52 @@ public class LauncherActivity extends BaseActivity { return null; } + private void checkNotificationPermission() { + if(LauncherPreferences.PREF_SKIP_NOTIFICATION_PERMISSION_CHECK || + checkForNotificationPermission()) { + return; + } + + if(ActivityCompat.shouldShowRequestPermissionRationale( + this, + Manifest.permission.POST_NOTIFICATIONS)) { + showNotificationPermissionReasoning(); + return; + } + askForNotificationPermission(null); + } + + private void showNotificationPermissionReasoning() { + new AlertDialog.Builder(this) + .setTitle(R.string.notification_permission_dialog_title) + .setMessage(R.string.notification_permission_dialog_text) + .setPositiveButton(android.R.string.ok, (d, w) -> askForNotificationPermission(null)) + .setNegativeButton(android.R.string.cancel, (d, w)-> handleNoNotificationPermission()) + .show(); + } + + private void handleNoNotificationPermission() { + LauncherPreferences.PREF_SKIP_NOTIFICATION_PERMISSION_CHECK = true; + LauncherPreferences.DEFAULT_PREF.edit() + .putBoolean(LauncherPreferences.PREF_KEY_SKIP_NOTIFICATION_CHECK, true) + .apply(); + Toast.makeText(this, R.string.notification_permission_toast, Toast.LENGTH_LONG).show(); + } + + public boolean checkForNotificationPermission() { + return Build.VERSION.SDK_INT < 33 || ContextCompat.checkSelfPermission( + this, + Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_DENIED; + } + + public void askForNotificationPermission(Runnable onSuccessRunnable) { + if(Build.VERSION.SDK_INT < 33) return; + if(onSuccessRunnable != null) { + mRequestNotificationPermissionRunnable = new WeakReference<>(onSuccessRunnable); + } + mRequestNotificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS); + } + /** Stuff all the view boilerplate here */ private void bindViews(){ mFragmentView = findViewById(R.id.container_fragment); diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java index dfa4291a8..fb818f1c3 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java @@ -75,6 +75,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringWriter; +import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -1108,6 +1109,11 @@ public final class Tools { return false; } + public static T getWeakReference(WeakReference weakReference) { + if(weakReference == null) return null; + return weakReference.get(); + } + /** Return the renderers that are compatible with this device */ public static RenderersList getCompatibleRenderers(Context context) { if(sCompatibleRenderers != null) return sCompatibleRenderers; diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/contextexecutor/ContextExecutor.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/contextexecutor/ContextExecutor.java index 7b17f5cc0..0df6618a1 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/contextexecutor/ContextExecutor.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/contextexecutor/ContextExecutor.java @@ -22,12 +22,12 @@ public class ContextExecutor { } private static void executeOnUiThread(ContextExecutorTask contextExecutorTask) { - Activity activity = getWeakReference(sActivity); + Activity activity = Tools.getWeakReference(sActivity); if(activity != null) { contextExecutorTask.executeWithActivity(activity); return; } - Application application = getWeakReference(sApplication); + Application application = Tools.getWeakReference(sApplication); if(application != null) { contextExecutorTask.executeWithApplication(application); }else { @@ -68,8 +68,5 @@ public class ContextExecutor { sApplication.clear(); } - private static T getWeakReference(WeakReference weakReference) { - if(weakReference == null) return null; - return weakReference.get(); - } + } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java index 51d64bafe..fdabdd8fe 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java @@ -17,6 +17,8 @@ import net.kdt.pojavlaunch.utils.JREUtils; public class LauncherPreferences { public static final String PREF_KEY_CURRENT_PROFILE = "currentProfile"; + public static final String PREF_KEY_SKIP_NOTIFICATION_CHECK = "skipNotificationPermissionCheck"; + public static SharedPreferences DEFAULT_PREF; public static String PREF_RENDERER = "opengles2"; @@ -64,6 +66,7 @@ public class LauncherPreferences { public static boolean PREF_VERIFY_MANIFEST = true; public static String PREF_DOWNLOAD_SOURCE = "default"; + public static boolean PREF_SKIP_NOTIFICATION_PERMISSION_CHECK = false; @@ -109,6 +112,7 @@ public class LauncherPreferences { PREF_ZINK_PREFER_SYSTEM_DRIVER = DEFAULT_PREF.getBoolean("zinkPreferSystemDriver", false); PREF_DOWNLOAD_SOURCE = DEFAULT_PREF.getString("downloadSource", "default"); PREF_VERIFY_MANIFEST = DEFAULT_PREF.getBoolean("verifyManifest", true); + PREF_SKIP_NOTIFICATION_PERMISSION_CHECK = DEFAULT_PREF.getBoolean(PREF_KEY_SKIP_NOTIFICATION_CHECK, false); String argLwjglLibname = "-Dorg.lwjgl.opengl.libname="; for (String arg : JREUtils.parseJavaArguments(PREF_CUSTOM_JAVA_ARGS)) { diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/screens/LauncherPreferenceFragment.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/screens/LauncherPreferenceFragment.java index 3863dee0f..8b9675c68 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/screens/LauncherPreferenceFragment.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/screens/LauncherPreferenceFragment.java @@ -1,6 +1,7 @@ package net.kdt.pojavlaunch.prefs.screens; +import android.app.Activity; import android.content.SharedPreferences; import android.graphics.Color; import android.os.Bundle; @@ -11,6 +12,7 @@ import androidx.annotation.Nullable; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; +import net.kdt.pojavlaunch.LauncherActivity; import net.kdt.pojavlaunch.R; import net.kdt.pojavlaunch.prefs.LauncherPreferences; @@ -20,6 +22,8 @@ import net.kdt.pojavlaunch.prefs.LauncherPreferences; */ public class LauncherPreferenceFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener { + private Preference mRequestNotificationPermissionPreference; + @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { view.setBackgroundColor(Color.parseColor("#232323")); @@ -29,6 +33,12 @@ public class LauncherPreferenceFragment extends PreferenceFragmentCompat impleme @Override public void onCreatePreferences(Bundle b, String str) { addPreferencesFromResource(R.xml.pref_main); + mRequestNotificationPermissionPreference = requirePreference("notification_permission_request"); + Activity activity = getActivity(); + mRequestNotificationPermissionPreference.setVisible( + activity instanceof LauncherActivity && + !((LauncherActivity) activity).checkForNotificationPermission() + ); } @Override @@ -50,6 +60,18 @@ public class LauncherPreferenceFragment extends PreferenceFragmentCompat impleme LauncherPreferences.loadPreferences(getContext()); } + @Override + public boolean onPreferenceTreeClick(@NonNull Preference preference) { + Activity activity = getActivity(); + if(preference.equals(mRequestNotificationPermissionPreference) && + activity instanceof LauncherActivity) { + ((LauncherActivity)activity).askForNotificationPermission(()-> + mRequestNotificationPermissionPreference.setVisible(false) + ); + } + return super.onPreferenceTreeClick(preference); + } + protected Preference requirePreference(CharSequence key) { Preference preference = findPreference(key); if(preference != null) return preference; diff --git a/app_pojavlauncher/src/main/res/values/strings.xml b/app_pojavlauncher/src/main/res/values/strings.xml index 4e9846724..d4219c670 100644 --- a/app_pojavlauncher/src/main/res/values/strings.xml +++ b/app_pojavlauncher/src/main/res/values/strings.xml @@ -359,4 +359,9 @@ Categories Miscellaneous settings Video settings + Permission request + PojavLauncher requires a permission to post notifications to prevent game/modpack downloads from stopping when you leave the app. Without this permission, Android may shut down your downloads when you put PojavLauncher in background mode. + You can always change your mind later by going into Settings + Allow notifications + Click to re-request the notification permission, required for game/modpack background downloads to work properly. diff --git a/app_pojavlauncher/src/main/res/xml/pref_main.xml b/app_pojavlauncher/src/main/res/xml/pref_main.xml index b4fd754f7..3cfee44a6 100644 --- a/app_pojavlauncher/src/main/res/xml/pref_main.xml +++ b/app_pojavlauncher/src/main/res/xml/pref_main.xml @@ -60,6 +60,12 @@ android:summary="@string/preference_force_english_description" /> + + From 109f37fa425fa3bac8968f4f46be2b90efe8ed48 Mon Sep 17 00:00:00 2001 From: artdeell Date: Wed, 18 Oct 2023 21:22:40 +0300 Subject: [PATCH 2/2] Cleaanup[preference]: use onPreferenceClickListener instead of onPreferenceTreeClick --- .../screens/LauncherPreferenceFragment.java | 34 ++++++++----------- .../LauncherPreferenceJavaFragment.java | 15 ++------ 2 files changed, 18 insertions(+), 31 deletions(-) diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/screens/LauncherPreferenceFragment.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/screens/LauncherPreferenceFragment.java index 8b9675c68..b57eacd58 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/screens/LauncherPreferenceFragment.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/screens/LauncherPreferenceFragment.java @@ -22,8 +22,6 @@ import net.kdt.pojavlaunch.prefs.LauncherPreferences; */ public class LauncherPreferenceFragment extends PreferenceFragmentCompat implements SharedPreferences.OnSharedPreferenceChangeListener { - private Preference mRequestNotificationPermissionPreference; - @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { view.setBackgroundColor(Color.parseColor("#232323")); @@ -33,12 +31,22 @@ public class LauncherPreferenceFragment extends PreferenceFragmentCompat impleme @Override public void onCreatePreferences(Bundle b, String str) { addPreferencesFromResource(R.xml.pref_main); - mRequestNotificationPermissionPreference = requirePreference("notification_permission_request"); + setupNotificationRequestPreference(); + } + + private void setupNotificationRequestPreference() { + Preference mRequestNotificationPermissionPreference = requirePreference("notification_permission_request"); Activity activity = getActivity(); - mRequestNotificationPermissionPreference.setVisible( - activity instanceof LauncherActivity && - !((LauncherActivity) activity).checkForNotificationPermission() - ); + if(activity instanceof LauncherActivity) { + LauncherActivity launcherActivity = (LauncherActivity)activity; + mRequestNotificationPermissionPreference.setVisible(!launcherActivity.checkForNotificationPermission()); + mRequestNotificationPermissionPreference.setOnPreferenceClickListener(preference -> { + launcherActivity.askForNotificationPermission(()->mRequestNotificationPermissionPreference.setVisible(false)); + return true; + }); + }else{ + mRequestNotificationPermissionPreference.setVisible(false); + } } @Override @@ -60,18 +68,6 @@ public class LauncherPreferenceFragment extends PreferenceFragmentCompat impleme LauncherPreferences.loadPreferences(getContext()); } - @Override - public boolean onPreferenceTreeClick(@NonNull Preference preference) { - Activity activity = getActivity(); - if(preference.equals(mRequestNotificationPermissionPreference) && - activity instanceof LauncherActivity) { - ((LauncherActivity)activity).askForNotificationPermission(()-> - mRequestNotificationPermissionPreference.setVisible(false) - ); - } - return super.onPreferenceTreeClick(preference); - } - protected Preference requirePreference(CharSequence key) { Preference preference = findPreference(key); if(preference != null) return preference; diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/screens/LauncherPreferenceJavaFragment.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/screens/LauncherPreferenceJavaFragment.java index 1b2ba3f44..dbcf3666d 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/screens/LauncherPreferenceJavaFragment.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/screens/LauncherPreferenceJavaFragment.java @@ -3,14 +3,11 @@ package net.kdt.pojavlaunch.prefs.screens; import static net.kdt.pojavlaunch.Architecture.is32BitsDevice; import static net.kdt.pojavlaunch.Tools.getTotalDeviceMemory; -import android.content.Context; import android.os.Bundle; import android.widget.TextView; import androidx.activity.result.ActivityResultLauncher; -import androidx.annotation.NonNull; import androidx.preference.EditTextPreference; -import androidx.preference.Preference; import net.kdt.pojavlaunch.R; import net.kdt.pojavlaunch.Tools; @@ -21,7 +18,6 @@ import net.kdt.pojavlaunch.prefs.LauncherPreferences; public class LauncherPreferenceJavaFragment extends LauncherPreferenceFragment { private MultiRTConfigDialog mDialogScreen; - private Preference mMultiRTPreference; private final ActivityResultLauncher mVmInstallLauncher = registerForActivityResult(new OpenDocumentWithExtension("xz"), (data)->{ if(data != null) Tools.installRuntimeFromUri(getContext(), data); @@ -52,15 +48,10 @@ public class LauncherPreferenceJavaFragment extends LauncherPreferenceFragment { editJVMArgs.setOnBindEditTextListener(TextView::setSingleLine); } - mMultiRTPreference = findPreference("install_jre"); - } - - @Override - public boolean onPreferenceTreeClick(@NonNull Preference preference) { - if(preference.equals(mMultiRTPreference)) { + requirePreference("install_jre").setOnPreferenceClickListener(preference->{ openMultiRTDialog(); - } - return super.onPreferenceTreeClick(preference); + return true; + }); } private void openMultiRTDialog() {