Multiple things

- Add Fabric installer
- Make both installers Fragments
- Add new Tools methods (and move some existing code to use them)
This commit is contained in:
BuildTools 2023-06-26 22:10:55 +03:00
parent df1b52adb7
commit d5935be9aa
27 changed files with 830 additions and 175 deletions

View File

@ -4,8 +4,6 @@ import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
@ -89,7 +87,7 @@ public class ImportControlActivity extends Activity {
}).start();
//Auto show the keyboard
new Handler(Looper.getMainLooper()).postDelayed(() -> {
Tools.MAIN_HANDLER.postDelayed(() -> {
InputMethodManager imm = (InputMethodManager) getApplicationContext().getSystemService(INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, 0);
mEditText.setSelection(mEditText.getText().length());

View File

@ -153,6 +153,7 @@ public class JavaGUILauncherActivity extends BaseActivity implements View.OnTouc
final Runtime runtime = MultiRTUtils.forceReread(jreName);
mSkipDetectMod = getIntent().getExtras().getBoolean("skipDetectMod", false);
if(getIntent().getExtras().getBoolean("openLogOutput", false)) openLogOutput(null);
if (mSkipDetectMod) {
new Thread(() -> launchJavaRuntime(runtime, modFile, javaArgs), "JREMainThread").start();
return;

View File

@ -21,8 +21,6 @@ import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.provider.DocumentsContract;
import android.util.Log;
import android.view.KeyEvent;
@ -287,7 +285,7 @@ public class MainActivity extends BaseActivity implements ControlButtonMenuListe
protected void onPostResume() {
super.onPostResume();
if(minecraftGLView != null) // Useful when backing out of the app
new Handler(Looper.getMainLooper()).postDelayed(() -> minecraftGLView.refreshSize(), 500);
Tools.MAIN_HANDLER.postDelayed(() -> minecraftGLView.refreshSize(), 500);
}
@Override

View File

@ -22,6 +22,8 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.provider.DocumentsContract;
import android.provider.OpenableColumns;
import android.util.ArrayMap;
@ -77,6 +79,7 @@ import java.util.Map;
@SuppressWarnings("IOStreamConstructor")
public final class Tools {
public static final Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
public static String APP_NAME = "null";
public static final Gson GLOBAL_GSON = new GsonBuilder().setPrettyPrinting().create();
@ -959,6 +962,10 @@ public final class Tools {
return runtime;
}
public static void runOnUiThread(Runnable runnable) {
MAIN_HANDLER.post(runnable);
}
public static @NonNull String pickRuntime(MinecraftProfile minecraftProfile, int targetJavaVersion) {
String runtime = getSelectedRuntime(minecraftProfile);
String profileRuntime = getRuntimeName(minecraftProfile.javaDir);

View File

@ -2,7 +2,6 @@ package net.kdt.pojavlaunch.authenticator.microsoft;
import static net.kdt.pojavlaunch.PojavApplication.sExecutorService;
import android.os.Looper;
import android.util.ArrayMap;
import android.util.Log;
@ -13,8 +12,10 @@ import com.kdt.mcgui.ProgressLayout;
import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.Tools;
import net.kdt.pojavlaunch.authenticator.listener.DoneListener;
import net.kdt.pojavlaunch.authenticator.listener.ErrorListener;
import net.kdt.pojavlaunch.authenticator.listener.ProgressListener;
import net.kdt.pojavlaunch.value.MinecraftAccount;
import net.kdt.pojavlaunch.authenticator.listener.*;
import org.json.JSONArray;
import org.json.JSONException;
@ -42,7 +43,6 @@ public class MicrosoftBackgroundLogin {
private final boolean mIsRefresh;
private final String mAuthCode;
private final android.os.Handler mHandler = new android.os.Handler(Looper.getMainLooper());
private static final Map<Long, Integer> XSTS_ERRORS;
static {
XSTS_ERRORS = new ArrayMap<>();
@ -100,13 +100,13 @@ public class MicrosoftBackgroundLogin {
if(doneListener != null) {
MinecraftAccount finalAcc = acc;
mHandler.post(() -> doneListener.onLoginDone(finalAcc));
Tools.runOnUiThread(() -> doneListener.onLoginDone(finalAcc));
}
}catch (Exception e){
Log.e("MicroAuth", e.toString());
if(errorListener != null)
mHandler.post(() -> errorListener.onLoginError(e));
Tools.runOnUiThread(() -> errorListener.onLoginError(e));
}
ProgressLayout.clearProgress(ProgressLayout.AUTHENTICATE_MICROSOFT);
});
@ -289,7 +289,7 @@ public class MicrosoftBackgroundLogin {
/** Wrapper to ease notifying the listener */
private void notifyProgress(@Nullable ProgressListener listener, int step){
if(listener != null){
mHandler.post(() -> listener.onLoginProgress(step));
Tools.runOnUiThread(() -> listener.onLoginProgress(step));
}
ProgressLayout.setProgress(ProgressLayout.AUTHENTICATE_MICROSOFT, step*20);
}

View File

@ -0,0 +1,180 @@
package net.kdt.pojavlaunch.fragments;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Adapter;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import net.kdt.pojavlaunch.JavaGUILauncherActivity;
import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.Tools;
import net.kdt.pojavlaunch.modloaders.FabricDownloadTask;
import net.kdt.pojavlaunch.modloaders.FabricUtils;
import net.kdt.pojavlaunch.modloaders.ModloaderDownloadListener;
import net.kdt.pojavlaunch.modloaders.ModloaderListenerProxy;
import net.kdt.pojavlaunch.profiles.VersionSelectorDialog;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class FabricInstallFragment extends Fragment implements AdapterView.OnItemSelectedListener, ModloaderDownloadListener, Runnable {
public static final String TAG = "FabricInstallTarget";
private static ModloaderListenerProxy sTaskProxy;
private TextView mSelectedVersionLabel;
private String mSelectedLoaderVersion;
private Spinner mLoaderVersionSpinner;
private String mSelectedGameVersion;
private boolean mSelectedSnapshot;
private ProgressBar mProgressBar;
private File mDestinationDir;
private Button mStartButton;
private View mRetryView;
public FabricInstallFragment() {
super(R.layout.fragment_fabric_install);
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
this.mDestinationDir = new File(context.getCacheDir(), "fabric-installer");
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mStartButton = view.findViewById(R.id.fabric_installer_start_button);
mStartButton.setOnClickListener(this::onClickStart);
mSelectedVersionLabel = view.findViewById(R.id.fabric_installer_version_select_label);
view.findViewById(R.id.fabric_installer_game_version_change).setOnClickListener(this::onClickSelect);
mLoaderVersionSpinner = view.findViewById(R.id.fabric_installer_loader_ver_spinner);
mLoaderVersionSpinner.setOnItemSelectedListener(this);
mProgressBar = view.findViewById(R.id.fabric_installer_progress_bar);
mRetryView = view.findViewById(R.id.fabric_installer_retry_layout);
view.findViewById(R.id.fabric_installer_retry_button).setOnClickListener(this::onClickRetry);
if(sTaskProxy != null) {
mStartButton.setEnabled(false);
sTaskProxy.attachListener(this);
}
new Thread(this).start();
}
@Override
public void onDestroyView() {
super.onDestroyView();
if(sTaskProxy != null) {
sTaskProxy.detachListener();
}
}
private void onClickStart(View v) {
sTaskProxy = new ModloaderListenerProxy();
FabricDownloadTask fabricDownloadTask = new FabricDownloadTask(sTaskProxy, mDestinationDir);
sTaskProxy.attachListener(this);
mStartButton.setEnabled(false);
new Thread(fabricDownloadTask).start();
}
private void onClickSelect(View v) {
VersionSelectorDialog.open(v.getContext(), true, (id, snapshot)->{
mSelectedGameVersion = id;
mSelectedVersionLabel.setText(mSelectedGameVersion);
mSelectedSnapshot = snapshot;
if(mSelectedLoaderVersion != null && sTaskProxy == null) mStartButton.setEnabled(true);
});
}
private void onClickRetry(View v) {
mLoaderVersionSpinner.setAdapter(null);
mStartButton.setEnabled(false);
mProgressBar.setVisibility(View.VISIBLE);
mRetryView.setVisibility(View.GONE);
new Thread(this).start();
}
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
Adapter adapter = (Adapter) adapterView.getAdapter();
mSelectedLoaderVersion = (String) adapter.getItem(i);
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
mSelectedLoaderVersion = null;
}
@Override
public void onDownloadFinished(File downloadedFile) {
Tools.runOnUiThread(()->{
Context context = requireContext();
sTaskProxy.detachListener();
sTaskProxy = null;
mStartButton.setEnabled(true);
Intent intent = new Intent(context, JavaGUILauncherActivity.class);
FabricUtils.addAutoInstallArgs(intent, downloadedFile, mSelectedGameVersion, mSelectedLoaderVersion, mSelectedSnapshot, true);
context.startActivity(intent);
});
}
@Override
public void onDataNotAvailable() {
Tools.runOnUiThread(()->{
Context context = requireContext();
sTaskProxy.detachListener();
sTaskProxy = null;
mStartButton.setEnabled(true);
Tools.dialog(context,
context.getString(R.string.global_error),
context.getString(R.string.fabric_dl_cant_read_meta));
});
}
@Override
public void onDownloadError(Exception e) {
Tools.runOnUiThread(()-> {
Context context = requireContext();
sTaskProxy.detachListener();
sTaskProxy = null;
mStartButton.setEnabled(true);
Tools.showError(context, e);
});
}
@Override
public void run() {
try {
List<String> mLoaderVersions = FabricUtils.downloadLoaderVersionList(false);
if (mLoaderVersions != null) {
Tools.runOnUiThread(()->{
Context context = getContext();
if(context == null) return;
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(context, R.layout.support_simple_spinner_dropdown_item, mLoaderVersions);
mLoaderVersionSpinner.setAdapter(arrayAdapter);
mProgressBar.setVisibility(View.GONE);
});
}else{
Tools.runOnUiThread(()-> {
mRetryView.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.GONE);
});
}
}catch (IOException e) {
Tools.runOnUiThread(()-> {
if(getContext() != null) Tools.showError(getContext(), e);
mRetryView.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.GONE);
});
}
}
}

View File

@ -0,0 +1,146 @@
package net.kdt.pojavlaunch.fragments;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ExpandableListView;
import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import net.kdt.pojavlaunch.JavaGUILauncherActivity;
import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.Tools;
import net.kdt.pojavlaunch.modloaders.ForgeDownloadTask;
import net.kdt.pojavlaunch.modloaders.ForgeUtils;
import net.kdt.pojavlaunch.modloaders.ForgeVersionListAdapter;
import net.kdt.pojavlaunch.modloaders.ModloaderDownloadListener;
import net.kdt.pojavlaunch.modloaders.ModloaderListenerProxy;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class ForgeInstallFragment extends Fragment implements Runnable, View.OnClickListener, ExpandableListView.OnChildClickListener, ModloaderDownloadListener {
public static final String TAG = "ForgeInstallFragment";
private static ModloaderListenerProxy sTaskProxy;
private ExpandableListView mExpandableListView;
private ProgressBar mProgressBar;
private File mDestinationFile;
private LayoutInflater mInflater;
private View mRetryView;
public ForgeInstallFragment() {
super(R.layout.fragment_forge_installer);
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
this.mInflater = LayoutInflater.from(context);
this.mDestinationFile = new File(context.getCacheDir(), "forge-installer.jar");
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mProgressBar = view.findViewById(R.id.forge_list_progress_bar);
mExpandableListView = view.findViewById(R.id.forge_expandable_version_list);
mExpandableListView.setOnChildClickListener(this);
mRetryView = view.findViewById(R.id.forge_installer_retry_layout);
view.findViewById(R.id.forge_installer_retry_button).setOnClickListener(this);
if(sTaskProxy != null) {
mExpandableListView.setEnabled(false);
sTaskProxy.attachListener(this);
}
new Thread(this).start();
}
@Override
public void onDestroyView() {
if(sTaskProxy != null) sTaskProxy.detachListener();
super.onDestroyView();
}
@Override
public void run() {
try {
List<String> forgeVersions = ForgeUtils.downloadForgeVersions();
Tools.runOnUiThread(()->{
if(forgeVersions != null) {
mExpandableListView.setAdapter(new ForgeVersionListAdapter(forgeVersions, mInflater));
}else{
mRetryView.setVisibility(View.VISIBLE);
}
mProgressBar.setVisibility(View.GONE);
});
}catch (IOException e) {
Tools.runOnUiThread(()-> {
if (getContext() != null) {
Tools.showError(getContext(), e);
mRetryView.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.GONE);
}
});
}
}
@Override
public void onClick(View view) {
mRetryView.setVisibility(View.GONE);
mProgressBar.setVisibility(View.VISIBLE);
new Thread(this).start();
}
@Override
public boolean onChildClick(ExpandableListView expandableListView, View view, int i, int i1, long l) {
String forgeVersion = (String)expandableListView.getExpandableListAdapter().getChild(i, i1);
sTaskProxy = new ModloaderListenerProxy();
ForgeDownloadTask downloadTask = new ForgeDownloadTask(sTaskProxy, forgeVersion, mDestinationFile);
sTaskProxy.attachListener(this);
mExpandableListView.setEnabled(false);
new Thread(downloadTask).start();
return true;
}
@Override
public void onDownloadFinished(File downloadedFile) {
Tools.runOnUiThread(()->{
Context context = requireContext();
sTaskProxy.detachListener();
sTaskProxy = null;
mExpandableListView.setEnabled(true);
Intent modInstallerStartIntent = new Intent(context, JavaGUILauncherActivity.class);
ForgeUtils.addAutoInstallArgs(modInstallerStartIntent, downloadedFile, true);
context.startActivity(modInstallerStartIntent);
});
}
@Override
public void onDataNotAvailable() {
Tools.runOnUiThread(()->{
Context context = requireContext();
sTaskProxy.detachListener();
sTaskProxy = null;
mExpandableListView.setEnabled(true);
Tools.dialog(context,
context.getString(R.string.global_error),
context.getString(R.string.forge_dl_no_installer));
});
}
@Override
public void onDownloadError(Exception e) {
Tools.runOnUiThread(()->{
Context context = requireContext();
sTaskProxy.detachListener();
sTaskProxy = null;
mExpandableListView.setEnabled(true);
Tools.showError(context, e);
});
}
}

View File

@ -5,7 +5,6 @@ import static net.kdt.pojavlaunch.Tools.shareLog;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.Toast;
@ -19,7 +18,6 @@ import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.Tools;
import net.kdt.pojavlaunch.extra.ExtraConstants;
import net.kdt.pojavlaunch.extra.ExtraCore;
import net.kdt.pojavlaunch.modloaders.ForgeDownloaderDialog;
import net.kdt.pojavlaunch.progresskeeper.ProgressKeeper;
public class MainMenuFragment extends Fragment {
@ -53,7 +51,7 @@ public class MainMenuFragment extends Fragment {
mShareLogsButton.setOnClickListener((v) -> shareLog(requireContext()));
mNewsButton.setOnLongClickListener((v)->{
new ForgeDownloaderDialog().show(view.getContext(), (ViewGroup) view);
Tools.swapFragment(requireActivity(), FabricInstallFragment.class, FabricInstallFragment.TAG, true, null);
return true;
});
}

View File

@ -1,6 +1,5 @@
package net.kdt.pojavlaunch.fragments;
import static net.kdt.pojavlaunch.extra.ExtraCore.getValue;
import static net.kdt.pojavlaunch.profiles.ProfileAdapter.CREATE_PROFILE_MAGIC;
import android.content.Context;
@ -11,17 +10,13 @@ import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import net.kdt.pojavlaunch.JMinecraftVersionList;
import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.Tools;
import net.kdt.pojavlaunch.extra.ExtraConstants;
@ -30,7 +25,7 @@ import net.kdt.pojavlaunch.multirt.MultiRTUtils;
import net.kdt.pojavlaunch.multirt.RTSpinnerAdapter;
import net.kdt.pojavlaunch.multirt.Runtime;
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
import net.kdt.pojavlaunch.profiles.VersionListAdapter;
import net.kdt.pojavlaunch.profiles.VersionSelectorDialog;
import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles;
import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile;
@ -117,28 +112,10 @@ public class ProfileEditorFragment extends Fragment {
});
// Setup the expendable list behavior
mVersionSelectButton.setOnClickListener(v -> {
AlertDialog.Builder builder = new AlertDialog.Builder(mDefaultVersion.getContext());
ExpandableListView expandableListView = (ExpandableListView) LayoutInflater.from(mDefaultVersion.getContext())
.inflate(R.layout.dialog_expendable_list_view , null);
JMinecraftVersionList jMinecraftVersionList = (JMinecraftVersionList) getValue(ExtraConstants.RELEASE_TABLE);
JMinecraftVersionList.Version[] versionArray;
if(jMinecraftVersionList == null || jMinecraftVersionList.versions == null) versionArray = new JMinecraftVersionList.Version[0];
else versionArray = jMinecraftVersionList.versions;
ExpandableListAdapter adapter = new VersionListAdapter(versionArray, mDefaultVersion.getContext());
expandableListView.setAdapter(adapter);
builder.setView(expandableListView);
AlertDialog dialog = builder.show();
expandableListView.setOnChildClickListener((parent, v1, groupPosition, childPosition, id) -> {
String version = ((String) adapter.getChild(groupPosition, childPosition));
mTempProfile.lastVersionId = version;
mDefaultVersion.setText(version);
dialog.dismiss();
return true;
});
});
mVersionSelectButton.setOnClickListener(v -> VersionSelectorDialog.open(v.getContext(), false, (id, snapshot)->{
mTempProfile.lastVersionId = id;
mDefaultVersion.setText(id);
}));

View File

@ -0,0 +1,77 @@
package net.kdt.pojavlaunch.modloaders;
import com.kdt.mcgui.ProgressLayout;
import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.Tools;
import net.kdt.pojavlaunch.progresskeeper.ProgressKeeper;
import net.kdt.pojavlaunch.utils.DownloadUtils;
import java.io.File;
import java.io.IOException;
public class FabricDownloadTask implements Runnable, Tools.DownloaderFeedback{
private final File mDestinationDir;
private final File mDestinationFile;
private final ModloaderDownloadListener mModloaderDownloadListener;
public FabricDownloadTask(ModloaderDownloadListener modloaderDownloadListener, File mDestinationDir) {
this.mModloaderDownloadListener = modloaderDownloadListener;
this.mDestinationDir = mDestinationDir;
this.mDestinationFile = new File(mDestinationDir, "fabric-installer.jar");
}
@Override
public void run() {
ProgressKeeper.submitProgress(ProgressLayout.INSTALL_MODPACK, 0, R.string.fabric_dl_progress);
try {
if(runCatching()) mModloaderDownloadListener.onDownloadFinished(mDestinationFile);
}catch (IOException e) {
mModloaderDownloadListener.onDownloadError(e);
}
ProgressKeeper.submitProgress(ProgressLayout.INSTALL_MODPACK, -1, -1);
}
private boolean runCatching() throws IOException {
if(!mDestinationDir.exists() && !mDestinationDir.mkdirs()) throw new IOException("Failed to create cache directory");
String[] urlAndVersion = FabricUtils.getInstallerUrlAndVersion();
if(urlAndVersion == null) {
mModloaderDownloadListener.onDataNotAvailable();
return false;
}
File versionFile = new File(mDestinationDir, "fabric-installer-version");
boolean shouldDownloadInstaller = true;
if(urlAndVersion[1] != null && versionFile.canRead()) { // if we know the latest version that we have and the server has
try {
shouldDownloadInstaller = !urlAndVersion[1].equals(Tools.read(versionFile.getAbsolutePath()));
}catch (IOException e) {
e.printStackTrace();
}
}
if(shouldDownloadInstaller) {
if (urlAndVersion[0] != null) {
byte[] buffer = new byte[8192];
DownloadUtils.downloadFileMonitored(urlAndVersion[0], mDestinationFile, buffer, this);
if(urlAndVersion[1] != null) {
try {
Tools.write(versionFile.getAbsolutePath(), urlAndVersion[1]);
}catch (IOException e) {
e.printStackTrace();
}
}
return true;
} else {
mModloaderDownloadListener.onDataNotAvailable();
return false;
}
}else{
return true;
}
}
@Override
public void updateProgress(int curr, int max) {
int progress100 = (int)(((float)curr / (float)max)*100f);
ProgressKeeper.submitProgress(ProgressLayout.INSTALL_MODPACK, progress100, R.string.fabric_dl_progress);
}
}

View File

@ -0,0 +1,8 @@
package net.kdt.pojavlaunch.modloaders;
import org.json.JSONException;
import org.json.JSONObject;
public interface FabricMetaReader {
boolean processMetadata(JSONObject jsonObject) throws JSONException;
}

View File

@ -0,0 +1,63 @@
package net.kdt.pojavlaunch.modloaders;
import android.content.Intent;
import net.kdt.pojavlaunch.Tools;
import net.kdt.pojavlaunch.utils.DownloadUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class FabricUtils {
private static final String FABRIC_INSTALLER_METADATA_URL = "https://meta.fabricmc.net/v2/versions/installer";
private static final String FABRIC_LOADER_METADATA_URL = "https://meta.fabricmc.net/v2/versions/loader";
public static List<String> downloadLoaderVersionList(boolean onlyStable) throws IOException {
String loaderMetadata = DownloadUtils.downloadString(FABRIC_LOADER_METADATA_URL);
List<String> loaderList = new ArrayList<>();
if(enumerateMetadata(loaderMetadata, (object)->{
if(onlyStable && !object.getBoolean("stable")) return false;
loaderList.add(object.getString("version"));
return false;
}) == null) return null;
return loaderList;
}
public static String[] getInstallerUrlAndVersion() throws IOException{
String installerMetadata = DownloadUtils.downloadString(FABRIC_INSTALLER_METADATA_URL);
JSONObject selectedMetadata = enumerateMetadata(installerMetadata, (object)-> object.getBoolean("stable"));
if(selectedMetadata == null) return null;
return new String[] {selectedMetadata.optString("url"), selectedMetadata.optString("version")};
}
public static void addAutoInstallArgs(Intent intent, File modInstalllerJar,
String gameVersion, String loaderVersion,
boolean isSnapshot, boolean createProfile) {
intent.putExtra("javaArgs", "-jar " + modInstalllerJar.getAbsolutePath() + " client -dir "+ Tools.DIR_GAME_NEW
+ " -mcversion "+gameVersion +" -loader "+loaderVersion +
(isSnapshot ? " -snapshot" : "") +
(createProfile ? "" : " -noprofile"));
intent.putExtra("openLogOutput", true);
}
private static JSONObject enumerateMetadata(String inputMetadata, FabricMetaReader metaReader) {
try {
JSONArray fullMetadata = new JSONArray(inputMetadata);
JSONObject metadataObject = null;
for(int i = 0; i < fullMetadata.length(); i++) {
metadataObject = fullMetadata.getJSONObject(i);
if(metaReader.processMetadata(metadataObject)) return metadataObject;
}
return metadataObject;
}catch (JSONException e) {
e.printStackTrace();
return null;
}
}
}

View File

@ -1,7 +0,0 @@
package net.kdt.pojavlaunch.modloaders;
public interface ForgeDownloadListener {
void onDownloadFinished();
void onInstallerNotAvailable();
void onDownloadError(Exception e);
}

View File

@ -14,9 +14,9 @@ import java.io.IOException;
public class ForgeDownloadTask implements Runnable, Tools.DownloaderFeedback {
private final String mForgeUrl;
private final String mForgeVersion;
private final File mDestinationFile;
private final ForgeDownloadListener mListener;
public ForgeDownloadTask(ForgeDownloadListener listener, String forgeVersion, File destinationFile) {
public final File mDestinationFile;
private final ModloaderDownloadListener mListener;
public ForgeDownloadTask(ModloaderDownloadListener listener, String forgeVersion, File destinationFile) {
this.mListener = listener;
this.mForgeUrl = ForgeUtils.getInstallerUrl(forgeVersion);
this.mForgeVersion = forgeVersion;
@ -28,10 +28,10 @@ public class ForgeDownloadTask implements Runnable, Tools.DownloaderFeedback {
try {
byte[] buffer = new byte[8192];
DownloadUtils.downloadFileMonitored(mForgeUrl, mDestinationFile, buffer, this);
mListener.onDownloadFinished();
mListener.onDownloadFinished(mDestinationFile);
}catch (IOException e) {
if(e instanceof FileNotFoundException) {
mListener.onInstallerNotAvailable();
mListener.onDataNotAvailable();
}else{
mListener.onDownloadError(e);
}

View File

@ -1,94 +0,0 @@
package net.kdt.pojavlaunch.modloaders;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ExpandableListView;
import android.widget.ProgressBar;
import net.kdt.pojavlaunch.JavaGUILauncherActivity;
import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.Tools;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class ForgeDownloaderDialog implements Runnable, ExpandableListView.OnChildClickListener, ForgeDownloadListener {
private final Handler mHandler = new Handler(Looper.getMainLooper());
private ExpandableListView mExpandableListView;
private ProgressBar mProgressBar;
private AlertDialog mAlertDialog;
private File mDestinationFile;
private Context mContext;
public void show(Context context, ViewGroup root) {
this.mContext = context;
this.mDestinationFile = new File(context.getCacheDir(), "forge-installer.jar");
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
View dialogView = LayoutInflater.from(context).inflate(R.layout.dialog_expandable_forge_list, root, false);
mProgressBar = dialogView.findViewById(R.id.forge_list_progress_bar);
mExpandableListView = dialogView.findViewById(R.id.forge_expandable_version_list);
mExpandableListView.setOnChildClickListener(this);
dialogBuilder.setView(dialogView);
mAlertDialog = dialogBuilder.show();
new Thread(this).start();
}
@Override
public void run() {
try {
List<String> forgeVersions = ForgeUtils.downloadForgeVersions();
mHandler.post(()->{
if(forgeVersions != null) {
mProgressBar.setVisibility(View.GONE);
mExpandableListView.setAdapter(new ForgeVersionListAdapter(forgeVersions, LayoutInflater.from(mContext)));
}else{
mAlertDialog.dismiss();
}
});
}catch (IOException e) {
mHandler.post(()->{
mAlertDialog.dismiss();
Tools.showError(mContext, e);
});
}
}
@Override
public boolean onChildClick(ExpandableListView expandableListView, View view, int i, int i1, long l) {
String forgeVersion = (String)expandableListView.getExpandableListAdapter().getChild(i, i1);
new Thread(new ForgeDownloadTask(this, forgeVersion, mDestinationFile)).start();
mAlertDialog.dismiss();
return true;
}
@Override
public void onDownloadFinished() {
Intent intent = new Intent(mContext, JavaGUILauncherActivity.class);
ForgeUtils.addAutoInstallArgs(intent, mDestinationFile, true); // since it's a user-invoked install, we want to create a new profile
mContext.startActivity(intent);
}
@Override
public void onInstallerNotAvailable() {
mHandler.post(()-> {
mAlertDialog.dismiss();
Tools.dialog(mContext,
mContext.getString(R.string.global_error),
mContext.getString(R.string.forge_dl_no_installer));
});
}
@Override
public void onDownloadError(Exception e) {
mHandler.post(mAlertDialog::dismiss);
Tools.showError(mContext, e);
}
}

View File

@ -0,0 +1,9 @@
package net.kdt.pojavlaunch.modloaders;
import java.io.File;
public interface ModloaderDownloadListener {
void onDownloadFinished(File downloadedFile);
void onDataNotAvailable();
void onDownloadError(Exception e);
}

View File

@ -0,0 +1,61 @@
package net.kdt.pojavlaunch.modloaders;
import java.io.File;
public class ModloaderListenerProxy implements ModloaderDownloadListener {
public static final int PROXY_RESULT_NONE = -1;
public static final int PROXY_RESULT_FINISHED = 0;
public static final int PROXY_RESULT_NOT_AVAILABLE = 1;
public static final int PROXY_RESULT_ERROR = 2;
private ModloaderDownloadListener mDestinationListener;
private Object mProxyResultObject;
private int mProxyResult = PROXY_RESULT_NONE;
@Override
public synchronized void onDownloadFinished(File downloadedFile) {
if(mDestinationListener != null) {
mDestinationListener.onDownloadFinished(downloadedFile);
}else{
mProxyResult = PROXY_RESULT_FINISHED;
mProxyResultObject = downloadedFile;
}
}
@Override
public synchronized void onDataNotAvailable() {
if(mDestinationListener != null) {
mDestinationListener.onDataNotAvailable();
}else{
mProxyResult = PROXY_RESULT_NOT_AVAILABLE;
mProxyResultObject = null;
}
}
@Override
public synchronized void onDownloadError(Exception e) {
if(mDestinationListener != null) {
mDestinationListener.onDownloadError(e);
}else {
mProxyResult = PROXY_RESULT_ERROR;
mProxyResultObject = e;
}
}
public synchronized void attachListener(ModloaderDownloadListener listener) {
switch(mProxyResult) {
case PROXY_RESULT_FINISHED:
listener.onDownloadFinished((File) mProxyResultObject);
break;
case PROXY_RESULT_NOT_AVAILABLE:
listener.onDataNotAvailable();
break;
case PROXY_RESULT_ERROR:
listener.onDownloadError((Exception) mProxyResultObject);
break;
}
mDestinationListener = listener;
}
public synchronized void detachListener() {
mDestinationListener = null;
}
}

View File

@ -24,8 +24,11 @@ public class VersionListAdapter extends BaseExpandableListAdapter implements Exp
private final String[] mGroups;
private final String[] mInstalledVersions;
private final List<?>[] mData;
private final boolean mHideCustomVersions;
private final int mSnapshotListPosition;
public VersionListAdapter(JMinecraftVersionList.Version[] versionList, Context ctx){
public VersionListAdapter(JMinecraftVersionList.Version[] versionList, boolean hideCustomVersions, Context ctx){
mHideCustomVersions = hideCustomVersions;
mLayoutInflater = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
List<JMinecraftVersionList.Version> releaseList = new FilteredSubList<>(versionList, item -> item.type.equals("release"));
@ -43,6 +46,7 @@ public class VersionListAdapter extends BaseExpandableListAdapter implements Exp
ctx.getString(R.string.mcl_setting_veroption_oldalpha)
};
mData = new List[]{ releaseList, snapshotList, betaList, alphaList};
mSnapshotListPosition = 1;
}else{
mGroups = new String[]{
ctx.getString(R.string.mcl_setting_veroption_installed),
@ -52,6 +56,7 @@ public class VersionListAdapter extends BaseExpandableListAdapter implements Exp
ctx.getString(R.string.mcl_setting_veroption_oldalpha)
};
mData = new List[]{Arrays.asList(mInstalledVersions), releaseList, snapshotList, betaList, alphaList};
mSnapshotListPosition = 2;
}
}
@ -116,7 +121,12 @@ public class VersionListAdapter extends BaseExpandableListAdapter implements Exp
return true;
}
public boolean isSnapshotSelected(int groupPosition) {
return groupPosition == mSnapshotListPosition;
}
private boolean areInstalledVersionsAvailable(){
if(mHideCustomVersions) return false;
return !(mInstalledVersions == null || mInstalledVersions.length == 0);
}

View File

@ -0,0 +1,37 @@
package net.kdt.pojavlaunch.profiles;
import static net.kdt.pojavlaunch.extra.ExtraCore.getValue;
import android.content.Context;
import android.view.LayoutInflater;
import android.widget.ExpandableListView;
import androidx.appcompat.app.AlertDialog;
import net.kdt.pojavlaunch.JMinecraftVersionList;
import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.extra.ExtraConstants;
public class VersionSelectorDialog {
public static void open(Context context, boolean hideCustomVersions, VersionSelectorListener listener) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
ExpandableListView expandableListView = (ExpandableListView) LayoutInflater.from(context)
.inflate(R.layout.dialog_expendable_list_view , null);
JMinecraftVersionList jMinecraftVersionList = (JMinecraftVersionList) getValue(ExtraConstants.RELEASE_TABLE);
JMinecraftVersionList.Version[] versionArray;
if(jMinecraftVersionList == null || jMinecraftVersionList.versions == null) versionArray = new JMinecraftVersionList.Version[0];
else versionArray = jMinecraftVersionList.versions;
VersionListAdapter adapter = new VersionListAdapter(versionArray, hideCustomVersions, context);
expandableListView.setAdapter(adapter);
builder.setView(expandableListView);
AlertDialog dialog = builder.show();
expandableListView.setOnChildClickListener((parent, v1, groupPosition, childPosition, id) -> {
String version = adapter.getChild(groupPosition, childPosition);
listener.onVersionSelected(version, adapter.isSnapshotSelected(groupPosition));
dialog.dismiss();
return true;
});
}
}

View File

@ -0,0 +1,5 @@
package net.kdt.pojavlaunch.profiles;
public interface VersionSelectorListener {
void onVersionSelected(String versionId, boolean isSnapshot);
}

View File

@ -6,9 +6,7 @@ import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
import android.util.Log;
@ -28,7 +26,6 @@ import net.kdt.pojavlaunch.progresskeeper.TaskCountListener;
*/
public class ProgressService extends Service implements TaskCountListener {
private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
private NotificationManagerCompat notificationManagerCompat;
/** Simple wrapper to start the service */
@ -85,7 +82,7 @@ public class ProgressService extends Service implements TaskCountListener {
@Override
public void onUpdateTaskCount(int taskCount) {
mainThreadHandler.post(()->{
Tools.MAIN_HANDLER.post(()->{
if(taskCount > 0) {
mNotificationBuilder.setContentText(getString(R.string.progresslayout_tasks_in_progress, taskCount));
notificationManagerCompat.notify(1, mNotificationBuilder.build());

View File

@ -183,6 +183,8 @@ public class AsyncMinecraftDownloader {
os.close();
}
}
} catch (DownloaderException e) {
throw e;
} catch (Throwable e) {
Log.e("AsyncMcDownloader", e.toString(),e );
ProgressKeeper.submitProgress(ProgressLayout.DOWNLOAD_MINECRAFT, -1, -1);

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ExpandableListView
android:id="@+id/forge_expandable_version_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</ExpandableListView>
<ProgressBar
android:id="@+id/forge_list_progress_bar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true" />
</LinearLayout>

View File

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/background_app">
<TextView
android:id="@+id/fabric_installer_label_loader_ver"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="@string/fabric_dl_loader_version"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Spinner
android:id="@+id/fabric_installer_loader_ver_spinner"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:minHeight="48dp"
android:background="@drawable/background_line"
android:paddingVertical="0dp"
android:paddingStart="7dp"
android:paddingEnd="7dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/fabric_installer_label_loader_ver" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="@dimen/_8sdp"
android:paddingHorizontal="@dimen/_8sdp"
android:rotation="180"
android:src="@drawable/spinner_arrow"
app:layout_constraintBottom_toBottomOf="@+id/fabric_installer_loader_ver_spinner"
app:layout_constraintEnd_toEndOf="@+id/fabric_installer_loader_ver_spinner"
app:layout_constraintTop_toTopOf="@+id/fabric_installer_loader_ver_spinner" />
<LinearLayout
android:id="@+id/fabric_installer_retry_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal"
android:paddingTop="@dimen/fragment_padding_medium"
android:paddingBottom="@dimen/fragment_padding_medium"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@+id/fabric_installer_loader_ver_spinner">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:gravity="center_vertical"
android:text="@string/forge_dl_failed_to_load_list"
android:textColor="#FFFF0000"
android:textStyle="bold" />
<Button
android:id="@+id/fabric_installer_retry_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/global_retry" />
</LinearLayout>
<TextView
android:id="@+id/fabric_installer_game_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="@string/fabric_dl_game_version"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/fabric_installer_retry_layout" />
<TextView
android:id="@+id/fabric_installer_version_select_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_weight="1"
android:background="@drawable/background_line"
android:hint="@string/version_select_hint"
android:paddingHorizontal="@dimen/_12sdp"
app:layout_constraintEnd_toStartOf="@+id/fabric_installer_game_version_change"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/fabric_installer_game_version" />
<Button
android:id="@+id/fabric_installer_game_version_change"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginEnd="8dp"
android:text="@string/global_select"
app:layout_constraintBottom_toBottomOf="@+id/fabric_installer_version_select_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/fabric_installer_version_select_label" />
<com.kdt.mcgui.MineButton
android:id="@+id/fabric_installer_start_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:enabled="false"
android:text="@string/fabric_dl_install"
app:layout_constraintBottom_toTopOf="@+id/fabric_installer_progress_bar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/fabric_installer_version_select_label"
app:layout_constraintVertical_bias="1.0" />
<ProgressBar
android:id="@+id/fabric_installer_progress_bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:indeterminate="true"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/background_app">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/forge_dl_select_version"
android:paddingTop="@dimen/fragment_padding_medium"
android:paddingBottom="@dimen/fragment_padding_medium"
android:textColor="@color/primary_text" />
<ExpandableListView
android:scrollbarThumbVertical="@color/minebutton_color"
android:id="@+id/forge_expandable_version_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</ExpandableListView>
<LinearLayout
android:id="@+id/forge_installer_retry_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="@dimen/fragment_padding_medium"
android:paddingBottom="@dimen/fragment_padding_medium"
android:visibility="gone">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:gravity="center_vertical"
android:text="@string/forge_dl_failed_to_load_list"
android:textColor="#FFFF0000"
android:textStyle="bold" />
<Button
android:id="@+id/forge_installer_retry_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/global_retry" />
</LinearLayout>
<ProgressBar
android:id="@+id/forge_list_progress_bar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true" />
</LinearLayout>

View File

@ -53,7 +53,7 @@
android:layout_marginTop="32dp"
android:layout_marginEnd="@dimen/_4sdp"
android:background="@drawable/background_line"
android:hint="Select a version"
android:hint="@string/version_select_hint"
android:paddingHorizontal="@dimen/_12sdp"
android:textSize="@dimen/_13ssp"

View File

@ -128,6 +128,7 @@
<string name="global_error_field_empty">This field can\'t be empty</string>
<string name="global_waiting">Wait</string>
<string name="global_select">Select</string>
<string name="global_retry">Retry</string>
<!-- MainActivity: strings -->
<string name="mcn_exit_title">Application/Game exited with code %d, check latestlog.txt for more details.</string>
@ -372,7 +373,14 @@
<string name="preference_deadzone_scale_description">Increase it if the joystick drifts</string>
<string name="preference_force_big_core_title">Force renderer to run on the big core</string>
<string name="preference_force_big_core_desc">Forces the Minecraft render thread to run on the core with the highest max frequency</string>
<string name="version_select_hint">Select a version</string>
<string name="forge_dl_progress">Downloading installer for %s</string>
<string name="forge_dl_failed_to_load_list">Failed to load the version list</string>
<string name="forge_dl_failed_to_load_list">Failed to load the version list!</string>
<string name="forge_dl_no_installer">Sorry, but this version of Forge does not have an installer, which is not yet supported.</string>
<string name="forge_dl_select_version">Select Forge version</string>
<string name="fabric_dl_progress">Downloading Fabric installer</string>
<string name="fabric_dl_loader_version">Fabric loader version</string>
<string name="fabric_dl_game_version">Minecraft version</string>
<string name="fabric_dl_install">Install</string>
<string name="fabric_dl_cant_read_meta">Failed to read Fabric metadata. Please try again later.</string>
</resources>