From ff72d5a1c06079fe8d1801409862afbd19ea4ab5 Mon Sep 17 00:00:00 2001 From: BuildTools Date: Mon, 14 Aug 2023 17:55:08 +0300 Subject: [PATCH] More resilient mod loader installation for the modpack installer --- .../src/main/AndroidManifest.xml | 2 + .../net/kdt/pojavlaunch/LauncherActivity.java | 16 +++ .../modpacks/ModloaderInstallTracker.java | 106 ++++++++++++++++++ .../modloaders/modpacks/api/ModpackApi.java | 20 +--- .../api/NotificationDownloadListener.java | 66 +++++++++++ .../src/main/res/values/strings.xml | 4 + 6 files changed, 195 insertions(+), 19 deletions(-) create mode 100644 app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/ModloaderInstallTracker.java create mode 100644 app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/NotificationDownloadListener.java diff --git a/app_pojavlauncher/src/main/AndroidManifest.xml b/app_pojavlauncher/src/main/AndroidManifest.xml index 457e0cbfd..c08be997c 100644 --- a/app_pojavlauncher/src/main/AndroidManifest.xml +++ b/app_pojavlauncher/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ android:name="android.hardware.type.pc" android:required="false" /> + @@ -82,6 +83,7 @@ android:configChanges="keyboardHidden|orientation|screenSize|keyboard|navigation" android:theme="@style/Theme.AppCompat.DayNight.Dialog" /> 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 76a05254d..c88f2120a 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/LauncherActivity.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/LauncherActivity.java @@ -31,6 +31,7 @@ import net.kdt.pojavlaunch.extra.ExtraCore; import net.kdt.pojavlaunch.extra.ExtraListener; import net.kdt.pojavlaunch.fragments.SelectAuthFragment; +import net.kdt.pojavlaunch.modloaders.modpacks.ModloaderInstallTracker; import net.kdt.pojavlaunch.multirt.MultiRTConfigDialog; import net.kdt.pojavlaunch.prefs.LauncherPreferences; import net.kdt.pojavlaunch.prefs.screens.LauncherPreferenceFragment; @@ -53,6 +54,7 @@ public class LauncherActivity extends BaseActivity { private ImageButton mSettingsButton, mDeleteAccountButton; private ProgressLayout mProgressLayout; private ProgressServiceKeeper mProgressServiceKeeper; + private ModloaderInstallTracker mInstallTracker; /* Allows to switch from one button "type" to another */ private final FragmentManager.FragmentLifecycleCallbacks mFragmentCallbackListener = new FragmentManager.FragmentLifecycleCallbacks() { @@ -167,6 +169,8 @@ public class LauncherActivity extends BaseActivity { new AsyncVersionList().getVersionList(versions -> ExtraCore.setValue(ExtraConstants.RELEASE_TABLE, versions), false); + mInstallTracker = new ModloaderInstallTracker(this); + mProgressLayout.observe(ProgressLayout.DOWNLOAD_MINECRAFT); mProgressLayout.observe(ProgressLayout.UNPACK_RUNTIME); mProgressLayout.observe(ProgressLayout.INSTALL_MODPACK); @@ -174,6 +178,18 @@ public class LauncherActivity extends BaseActivity { mProgressLayout.observe(ProgressLayout.DOWNLOAD_VERSION_LIST); } + @Override + protected void onResume() { + super.onResume(); + mInstallTracker.attach(); + } + + @Override + protected void onPause() { + super.onPause(); + mInstallTracker.detach(); + } + @Override public boolean setFullscreen() { return false; diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/ModloaderInstallTracker.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/ModloaderInstallTracker.java new file mode 100644 index 000000000..8ec11e3fa --- /dev/null +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/ModloaderInstallTracker.java @@ -0,0 +1,106 @@ +package net.kdt.pojavlaunch.modloaders.modpacks; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; + +import net.kdt.pojavlaunch.modloaders.modpacks.api.ModLoader; + +import java.io.File; + +/** + * This class is meant to track the availability of a modloader that is ready to be installed (as a result of modpack installation) + * It is needed because having all this logic spread over LauncherActivity would be clumsy, and I think that this is the best way to + * ensure that the modloader installer will run, even if the user does not receive the notification or something else happens + */ +public class ModloaderInstallTracker implements SharedPreferences.OnSharedPreferenceChangeListener { + private final SharedPreferences mSharedPreferences; + private final Activity mActivity; + + /** + * Create a ModInstallTracker object. This must be done in the Activity's onCreate method. + * @param activity the host activity + */ + public ModloaderInstallTracker(Activity activity) { + mActivity = activity; + mSharedPreferences = getPreferences(activity); + + } + + /** + * Attach the ModloaderInstallTracker to the current Activity. Must be done in the Activity's + * onResume method + */ + public void attach() { + mSharedPreferences.registerOnSharedPreferenceChangeListener(this); + runCheck(); + } + + /** + * Detach the ModloaderInstallTracker from the current Activity. Must be done in the Activity's + * onPause method + */ + public void detach() { + mSharedPreferences.unregisterOnSharedPreferenceChangeListener(this); + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String prefName) { + if(!"modLoaderAvailable".equals(prefName)) return; + runCheck(); + } + + @SuppressLint("ApplySharedPref") + private void runCheck() { + if(!mSharedPreferences.getBoolean("modLoaderAvailable", false)) return; + SharedPreferences.Editor editor = mSharedPreferences.edit().putBoolean("modLoaderAvailable", false); + if(!editor.commit()) editor.apply(); + ModLoader modLoader = deserializeModLoader(mSharedPreferences); + File modInstallFile = deserializeInstallFile(mSharedPreferences); + if(modLoader == null || modInstallFile == null) return; + startModInstallation(modLoader, modInstallFile); + } + + private void startModInstallation(ModLoader modLoader, File modInstallFile) { + Intent installIntent = modLoader.getInstallationIntent(mActivity, modInstallFile); + mActivity.startActivity(installIntent); + } + + private static SharedPreferences getPreferences(Context context) { + return context.getSharedPreferences("modloader_info", Context.MODE_PRIVATE); + } + + /** + * Store the data necessary to start a ModLoader installation for the tracker to start the installer + * sometime. + * @param context the Context + * @param modLoader the ModLoader to store + * @param modInstallFile the installer jar to store + */ + @SuppressLint("ApplySharedPref") + public static void saveModLoader(Context context, ModLoader modLoader, File modInstallFile) { + SharedPreferences.Editor editor = getPreferences(context).edit(); + editor.putInt("modLoaderType", modLoader.modLoaderType); + editor.putString("modLoaderVersion", modLoader.modLoaderVersion); + editor.putString("minecraftVersion", modLoader.minecraftVersion); + editor.putString("modInstallerJar", modInstallFile.getAbsolutePath()); + editor.putBoolean("modLoaderAvailable", true); + editor.commit(); + } + + private static ModLoader deserializeModLoader(SharedPreferences sharedPreferences) { + if(!sharedPreferences.contains("modLoaderType") || + !sharedPreferences.contains("modLoaderVersion") || + !sharedPreferences.contains("minecraftVersion")) return null; + return new ModLoader(sharedPreferences.getInt("modLoaderType", -1), + sharedPreferences.getString("modLoaderVersion", ""), + sharedPreferences.getString("minecraftVersion", "")); + } + + private static File deserializeInstallFile(SharedPreferences sharedPreferences) { + if(!sharedPreferences.contains("modInstallerJar")) return null; + return new File(sharedPreferences.getString("modInstallerJar", "")); + } +} diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModpackApi.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModpackApi.java index 46fe7caed..ff9f9c67e 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModpackApi.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/ModpackApi.java @@ -40,25 +40,7 @@ public interface ModpackApi { PojavApplication.sExecutorService.execute(() -> { ModLoader loaderInfo = installMod(modDetail, selectedVersion); if (loaderInfo == null) return; - - loaderInfo.getDownloadTask(new ModloaderDownloadListener() { - @Override - public void onDownloadFinished(File downloadedFile) { - Intent intent = loaderInfo.getInstallationIntent(context, downloadedFile); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - } - - @Override - public void onDataNotAvailable() { - Toast.makeText(context, "Version gathering failed", Toast.LENGTH_SHORT).show(); - } - - @Override - public void onDownloadError(Exception e) { - Toast.makeText(context, "download failed", Toast.LENGTH_SHORT).show(); - } - }).run(); + loaderInfo.getDownloadTask(new NotificationDownloadListener(context, loaderInfo)).run(); }); } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/NotificationDownloadListener.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/NotificationDownloadListener.java new file mode 100644 index 000000000..34d12ad1a --- /dev/null +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/modloaders/modpacks/api/NotificationDownloadListener.java @@ -0,0 +1,66 @@ +package net.kdt.pojavlaunch.modloaders.modpacks.api; + +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.Build; + +import androidx.core.app.NotificationCompat; + +import net.kdt.pojavlaunch.LauncherActivity; +import net.kdt.pojavlaunch.R; +import net.kdt.pojavlaunch.Tools; +import net.kdt.pojavlaunch.modloaders.ModloaderDownloadListener; +import net.kdt.pojavlaunch.modloaders.modpacks.ModloaderInstallTracker; + +import java.io.File; + +public class NotificationDownloadListener implements ModloaderDownloadListener { + + private final NotificationCompat.Builder mNotificationBuilder; + private final NotificationManager mNotificationManager; + private final Context mContext; + private final ModLoader mModLoader; + + public NotificationDownloadListener(Context context, ModLoader modLoader) { + mModLoader = modLoader; + mContext = context.getApplicationContext(); + mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + mNotificationBuilder = new NotificationCompat.Builder(context, "channel_id") + .setContentTitle(context.getString(R.string.modpack_install_notification_title)) + .setSmallIcon(R.drawable.notif_icon); + } + + @Override + public void onDownloadFinished(File downloadedFile) { + ModloaderInstallTracker.saveModLoader(mContext, mModLoader, downloadedFile); + Intent mainActivityIntent = new Intent(mContext, LauncherActivity.class); + Tools.runOnUiThread(()->sendIntentNotification(mainActivityIntent, R.string.modpack_install_notification_success)); + } + + @Override + public void onDataNotAvailable() { + Tools.runOnUiThread(()->sendEmptyNotification(R.string.modpack_install_notification_data_not_available)); + } + + @Override + public void onDownloadError(Exception e) { + Tools.runOnUiThread(()->sendEmptyNotification(R.string.modpack_install_notification_download_failed)); + } + + private void sendIntentNotification(Intent intent, int contentText) { + PendingIntent pendingInstallIntent = + PendingIntent.getActivity(mContext, 0, + intent, Build.VERSION.SDK_INT >=23 ? PendingIntent.FLAG_IMMUTABLE : 0); + + mNotificationBuilder.setContentText(mContext.getText(contentText)); + mNotificationBuilder.setContentIntent(pendingInstallIntent); + mNotificationManager.notify(3, mNotificationBuilder.build()); + } + + private void sendEmptyNotification(int contentText) { + mNotificationBuilder.setContentText(mContext.getText(contentText)); + mNotificationManager.notify(3, mNotificationBuilder.build()); + } +} diff --git a/app_pojavlauncher/src/main/res/values/strings.xml b/app_pojavlauncher/src/main/res/values/strings.xml index 5958bd3eb..facea4483 100644 --- a/app_pojavlauncher/src/main/res/values/strings.xml +++ b/app_pojavlauncher/src/main/res/values/strings.xml @@ -418,4 +418,8 @@ Downloading mods (%.2f MB / %.2f MB) Downloading mods (File %d out of %d) Applying overrides (%d/%d) + Pojav Modpack Installer + Click here to finish modpack installation + Failed to download mod loader information + Failed to download the mod loader files