More resilient mod loader installation for the modpack installer

This commit is contained in:
BuildTools 2023-08-14 17:55:08 +03:00 committed by ArtDev
parent b1d022cccd
commit ff72d5a1c0
6 changed files with 195 additions and 19 deletions

View File

@ -7,6 +7,7 @@
android:name="android.hardware.type.pc"
android:required="false" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
@ -82,6 +83,7 @@
android:configChanges="keyboardHidden|orientation|screenSize|keyboard|navigation"
android:theme="@style/Theme.AppCompat.DayNight.Dialog" />
<activity
android:process=":gui_installer"
android:name=".JavaGUILauncherActivity"
android:configChanges="keyboardHidden|orientation|screenSize|smallestScreenSize|screenLayout|keyboard|navigation|uiMode"
android:screenOrientation="sensorLandscape" />

View File

@ -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;

View File

@ -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", ""));
}
}

View File

@ -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();
});
}

View File

@ -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());
}
}

View File

@ -418,4 +418,8 @@
<string name="modpack_download_downloading_mods">Downloading mods (%.2f MB / %.2f MB)</string>
<string name="modpack_download_downloading_mods_fc">Downloading mods (File %d out of %d)</string>
<string name="modpack_download_applying_overrides">Applying overrides (%d/%d)</string>
<string name="modpack_install_notification_title">Pojav Modpack Installer</string>
<string name="modpack_install_notification_success">Click here to finish modpack installation</string>
<string name="modpack_install_notification_data_not_available">Failed to download mod loader information</string>
<string name="modpack_install_notification_download_failed">Failed to download the mod loader files</string>
</resources>