mirror of
https://github.com/AngelAuraMC/Amethyst-Android.git
synced 2026-04-20 17:47:03 -04:00
Merge pull request #4308 from PojavLauncherTeam/feat/automatic_modloader_install
Feature: Automatic Forge, Fabric and OptiFine installation
This commit is contained in:
@@ -194,6 +194,8 @@ dependencies {
|
||||
|
||||
implementation 'org.tukaani:xz:1.8'
|
||||
implementation 'com.github.PojavLauncherTeam:exp4j:60eaec6f78'
|
||||
implementation 'net.sourceforge.htmlcleaner:htmlcleaner:2.6.1'
|
||||
|
||||
// implementation 'net.sourceforge.streamsupport:streamsupport-cfuture:1.7.0'
|
||||
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
1687691695196
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
1688133008591
|
||||
@@ -1 +1 @@
|
||||
1678189863424
|
||||
1687691695259
|
||||
@@ -1,14 +1,13 @@
|
||||
package com.kdt.mcgui;
|
||||
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
|
||||
import static net.kdt.pojavlaunch.fragments.ProfileEditorFragment.DELETED_PROFILE;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.transition.Slide;
|
||||
import android.transition.Transition;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
@@ -19,15 +18,17 @@ import android.widget.PopupWindow;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
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.fragments.ProfileTypeSelectFragment;
|
||||
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
||||
import net.kdt.pojavlaunch.profiles.ProfileAdapter;
|
||||
import net.kdt.pojavlaunch.fragments.ProfileEditorFragment;
|
||||
import net.kdt.pojavlaunch.profiles.ProfileAdapterExtra;
|
||||
|
||||
import fr.spse.extended_view.ExtendedTextView;
|
||||
|
||||
@@ -36,6 +37,8 @@ import fr.spse.extended_view.ExtendedTextView;
|
||||
* dropdown popup view with a custom direction.
|
||||
*/
|
||||
public class mcVersionSpinner extends ExtendedTextView {
|
||||
private static final int VERSION_SPINNER_PROFILE_CREATE = 0;
|
||||
private static final int VERSION_SPINNER_PROFILE_CREATE_MODDED = 1;
|
||||
public mcVersionSpinner(@NonNull Context context) {
|
||||
super(context);
|
||||
init();
|
||||
@@ -52,8 +55,12 @@ public class mcVersionSpinner extends ExtendedTextView {
|
||||
/* The class is in charge of displaying its own list with adapter content being known in advance */
|
||||
private ListView mListView = null;
|
||||
private PopupWindow mPopupWindow = null;
|
||||
private final ProfileAdapter mProfileAdapter = new ProfileAdapter(getContext(), true);
|
||||
private int mSelectedProfilePosition;
|
||||
private Object mPopupAnimation;
|
||||
private final ProfileAdapter mProfileAdapter = new ProfileAdapter(getContext(), new ProfileAdapterExtra[]{
|
||||
new ProfileAdapterExtra(VERSION_SPINNER_PROFILE_CREATE,
|
||||
R.string.create_profile,
|
||||
ResourcesCompat.getDrawable(getResources(), R.drawable.ic_add, null)),
|
||||
});
|
||||
|
||||
|
||||
/** Set the selection AND saves it as a shared preference */
|
||||
@@ -63,11 +70,9 @@ public class mcVersionSpinner extends ExtendedTextView {
|
||||
.putString(LauncherPreferences.PREF_KEY_CURRENT_PROFILE,
|
||||
mProfileAdapter.getItem(position).toString())
|
||||
.apply();
|
||||
if(mPopupWindow != null) mPopupWindow.dismiss();
|
||||
}
|
||||
|
||||
public void setSelection(int position){
|
||||
mSelectedProfilePosition = position;
|
||||
if(mListView != null) mListView.setSelection(position);
|
||||
mProfileAdapter.setViewProfile(this, (String) mProfileAdapter.getItem(position), false);
|
||||
}
|
||||
@@ -81,6 +86,7 @@ public class mcVersionSpinner extends ExtendedTextView {
|
||||
int endPadding = getContext().getResources().getDimensionPixelOffset(R.dimen._5sdp);
|
||||
setPaddingRelative(startPadding, 0, endPadding, 0);
|
||||
setCompoundDrawablePadding(startPadding);
|
||||
|
||||
int profileIndex;
|
||||
String extra_value = (String) ExtraCore.consumeValue(ExtraConstants.REFRESH_VERSION_SPINNER);
|
||||
if(extra_value != null){
|
||||
@@ -115,13 +121,20 @@ public class mcVersionSpinner extends ExtendedTextView {
|
||||
mListView = (ListView) inflate(getContext(), R.layout.spinner_mc_version, null);
|
||||
mListView.setAdapter(mProfileAdapter);
|
||||
mListView.setOnItemClickListener((parent, view, position, id) -> {
|
||||
if(position == mProfileAdapter.getCount() - 1){
|
||||
mPopupWindow.dismiss();
|
||||
Tools.swapFragment((FragmentActivity) getContext(), ProfileEditorFragment.class,
|
||||
ProfileEditorFragment.TAG, true, new Bundle(1));
|
||||
return;
|
||||
Object item = mProfileAdapter.getItem(position);
|
||||
if(item instanceof String) {
|
||||
hidePopup(true);
|
||||
setProfileSelection(position);
|
||||
}else if(item instanceof ProfileAdapterExtra) {
|
||||
hidePopup(false);
|
||||
ProfileAdapterExtra extra = (ProfileAdapterExtra) item;
|
||||
switch (extra.id) {
|
||||
case VERSION_SPINNER_PROFILE_CREATE:
|
||||
Tools.swapFragment((FragmentActivity) getContext(), ProfileTypeSelectFragment.class,
|
||||
ProfileTypeSelectFragment.TAG, true, null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
setProfileSelection(position);
|
||||
});
|
||||
|
||||
mPopupWindow = new PopupWindow(mListView, MATCH_PARENT, getContext().getResources().getDimensionPixelOffset(R.dimen._184sdp));
|
||||
@@ -142,14 +155,26 @@ public class mcVersionSpinner extends ExtendedTextView {
|
||||
|
||||
// Custom animation, nice slide in
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
|
||||
Slide transition = new Slide(Gravity.BOTTOM);
|
||||
mPopupWindow.setEnterTransition(transition);
|
||||
mPopupWindow.setExitTransition(transition);
|
||||
mPopupAnimation = new Slide(Gravity.BOTTOM);
|
||||
mPopupWindow.setEnterTransition((Transition) mPopupAnimation);
|
||||
mPopupWindow.setExitTransition((Transition) mPopupAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
private void hidePopup(boolean animate) {
|
||||
if(mPopupWindow == null) return;
|
||||
if(!animate && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
mPopupWindow.setEnterTransition(null);
|
||||
mPopupWindow.setExitTransition(null);
|
||||
mPopupWindow.dismiss();
|
||||
mPopupWindow.setEnterTransition((Transition) mPopupAnimation);
|
||||
mPopupWindow.setExitTransition((Transition) mPopupAnimation);
|
||||
}else {
|
||||
mPopupWindow.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
public ProfileAdapter getProfileAdapter() {
|
||||
return mProfileAdapter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -28,6 +28,7 @@ import net.kdt.pojavlaunch.utils.MathUtils;
|
||||
import org.lwjgl.glfw.CallbackBridge;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
@@ -55,7 +56,14 @@ public class JavaGUILauncherActivity extends BaseActivity implements View.OnTouc
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_java_gui_launcher);
|
||||
|
||||
Logger.begin(new File(Tools.DIR_GAME_HOME, "latestlog.txt").getAbsolutePath());
|
||||
try {
|
||||
File latestLogFile = new File(Tools.DIR_GAME_HOME, "latestlog.txt");
|
||||
if (!latestLogFile.exists() && !latestLogFile.createNewFile())
|
||||
throw new IOException("Failed to create a new log file");
|
||||
Logger.begin(latestLogFile.getAbsolutePath());
|
||||
}catch (IOException e) {
|
||||
Tools.showError(this, e, true);
|
||||
}
|
||||
mTouchCharInput = findViewById(R.id.awt_touch_char);
|
||||
mTouchCharInput.setCharacterSender(new AwtCharSender());
|
||||
|
||||
@@ -153,6 +161,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;
|
||||
|
||||
@@ -165,7 +165,7 @@ public class LauncherActivity extends BaseActivity {
|
||||
|
||||
ExtraCore.addExtraListener(ExtraConstants.LAUNCH_GAME, mLaunchGameListener);
|
||||
|
||||
new AsyncVersionList().getVersionList(versions -> ExtraCore.setValue(ExtraConstants.RELEASE_TABLE, versions));
|
||||
new AsyncVersionList().getVersionList(versions -> ExtraCore.setValue(ExtraConstants.RELEASE_TABLE, versions), false);
|
||||
|
||||
mProgressLayout.observe(ProgressLayout.DOWNLOAD_MINECRAFT);
|
||||
mProgressLayout.observe(ProgressLayout.UNPACK_RUNTIME);
|
||||
|
||||
@@ -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;
|
||||
@@ -133,7 +131,10 @@ public class MainActivity extends BaseActivity implements ControlButtonMenuListe
|
||||
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
|
||||
|
||||
try {
|
||||
Logger.begin(new File(Tools.DIR_GAME_HOME, "latestlog.txt").getAbsolutePath());
|
||||
File latestLogFile = new File(Tools.DIR_GAME_HOME, "latestlog.txt");
|
||||
if(!latestLogFile.exists() && !latestLogFile.createNewFile())
|
||||
throw new IOException("Failed to create a new log file");
|
||||
Logger.begin(latestLogFile.getAbsolutePath());
|
||||
// FIXME: is it safe for multi thread?
|
||||
GLOBAL_CLIPBOARD = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
|
||||
touchCharInput.setCharacterSender(new LwjglCharSender());
|
||||
@@ -287,7 +288,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
|
||||
|
||||
@@ -60,6 +60,7 @@ public class PojavApplication extends Application {
|
||||
Tools.APP_NAME = getResources().getString(R.string.app_short_name);
|
||||
|
||||
Tools.DIR_DATA = getDir("files", MODE_PRIVATE).getParent();
|
||||
Tools.DIR_CACHE = getCacheDir();
|
||||
Tools.DIR_ACCOUNT_NEW = Tools.DIR_DATA + "/accounts";
|
||||
Tools.DEVICE_ARCHITECTURE = Architecture.getDeviceArchitecture();
|
||||
//Force x86 lib directory for Asus x86 based zenfones
|
||||
|
||||
@@ -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,14 +79,15 @@ 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();
|
||||
|
||||
public static final String URL_HOME = "https://pojavlauncherteam.github.io";
|
||||
|
||||
public static String NATIVE_LIB_DIR;
|
||||
public static String DIR_DATA; //Initialized later to get context
|
||||
public static File DIR_CACHE;
|
||||
public static String MULTIRT_HOME;
|
||||
public static String LOCAL_RENDERER = null;
|
||||
public static int DEVICE_ARCHITECTURE;
|
||||
@@ -136,6 +139,7 @@ public final class Tools {
|
||||
* Any value (in)directly dependant on DIR_DATA should be set only here.
|
||||
*/
|
||||
public static void initContextConstants(Context ctx){
|
||||
DIR_CACHE = ctx.getCacheDir();
|
||||
DIR_DATA = ctx.getFilesDir().getParent();
|
||||
MULTIRT_HOME = DIR_DATA+"/runtimes";
|
||||
DIR_GAME_HOME = getPojavStorageRoot(ctx).getAbsolutePath();
|
||||
@@ -590,11 +594,15 @@ public final class Tools {
|
||||
}
|
||||
|
||||
public static void dialogOnUiThread(final Activity activity, final CharSequence title, final CharSequence message) {
|
||||
activity.runOnUiThread(() -> new AlertDialog.Builder(activity)
|
||||
activity.runOnUiThread(()->dialog(activity, title, message));
|
||||
}
|
||||
|
||||
public static void dialog(final Context context, final CharSequence title, final CharSequence message) {
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(title)
|
||||
.setMessage(message)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show());
|
||||
.show();
|
||||
}
|
||||
|
||||
public static void openURL(Activity act, String url) {
|
||||
@@ -890,7 +898,7 @@ public final class Tools {
|
||||
sExecutorService.execute(() -> {
|
||||
try {
|
||||
final String name = getFileName(activity, uri);
|
||||
final File modInstallerFile = new File(activity.getCacheDir(), name);
|
||||
final File modInstallerFile = new File(Tools.DIR_CACHE, name);
|
||||
FileOutputStream fos = new FileOutputStream(modInstallerFile);
|
||||
InputStream input = activity.getContentResolver().openInputStream(uri);
|
||||
IOUtils.copy(input, fos);
|
||||
@@ -955,6 +963,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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
package net.kdt.pojavlaunch.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
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 android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
||||
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 net.kdt.pojavlaunch.progresskeeper.ProgressKeeper;
|
||||
|
||||
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(Tools.DIR_CACHE, "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) {
|
||||
if(ProgressKeeper.hasOngoingTasks()) {
|
||||
Toast.makeText(v.getContext(), R.string.tasks_ongoing, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
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 = 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);
|
||||
// This works because the due to the fact that we have transitioned here
|
||||
// without adding a transaction to the back stack, which caused the previous
|
||||
// transaction to be amended (i guess?? thats how the back stack dump looks like)
|
||||
// we can get back to the main fragment with just one back stack pop.
|
||||
// For some reason that amendment causes the transaction to lose its tag
|
||||
// so we cant use the tag here.
|
||||
getParentFragmentManager().popBackStackImmediate();
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package net.kdt.pojavlaunch.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.ExpandableListAdapter;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
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.ModloaderListenerProxy;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class ForgeInstallFragment extends ModVersionListFragment<List<String>> {
|
||||
public static final String TAG = "ForgeInstallFragment";
|
||||
private static ModloaderListenerProxy sTaskProxy;
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTitleText() {
|
||||
return R.string.forge_dl_select_version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNoDataMsg() {
|
||||
return R.string.forge_dl_no_installer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModloaderListenerProxy getTaskProxy() {
|
||||
return sTaskProxy;
|
||||
}
|
||||
@Override
|
||||
public void setTaskProxy(ModloaderListenerProxy proxy) {
|
||||
sTaskProxy = proxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> loadVersionList() throws IOException {
|
||||
return ForgeUtils.downloadForgeVersions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpandableListAdapter createAdapter(List<String> versionList, LayoutInflater layoutInflater) {
|
||||
return new ForgeVersionListAdapter(versionList, layoutInflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Runnable createDownloadTask(Object selectedVersion, ModloaderListenerProxy listenerProxy) {
|
||||
return new ForgeDownloadTask(listenerProxy, (String) selectedVersion, new File(Tools.DIR_CACHE, "forge-installer.jar"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadFinished(Context context, File downloadedFile) {
|
||||
Intent modInstallerStartIntent = new Intent(context, JavaGUILauncherActivity.class);
|
||||
ForgeUtils.addAutoInstallArgs(modInstallerStartIntent, downloadedFile, true);
|
||||
context.startActivity(modInstallerStartIntent);
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,7 @@ package net.kdt.pojavlaunch.fragments;
|
||||
import static net.kdt.pojavlaunch.Tools.shareLog;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
@@ -15,7 +13,6 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
|
||||
import net.kdt.pojavlaunch.CustomControlsActivity;
|
||||
import net.kdt.pojavlaunch.R;
|
||||
import net.kdt.pojavlaunch.Tools;
|
||||
@@ -52,6 +49,11 @@ public class MainMenuFragment extends Fragment {
|
||||
mPlayButton.setOnClickListener(v -> ExtraCore.setValue(ExtraConstants.LAUNCH_GAME, true));
|
||||
|
||||
mShareLogsButton.setOnClickListener((v) -> shareLog(requireContext()));
|
||||
|
||||
mNewsButton.setOnLongClickListener((v)->{
|
||||
Tools.swapFragment(requireActivity(), FabricInstallFragment.class, FabricInstallFragment.TAG, true, null);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
private void runInstallerWithConfirmation(boolean isCustomArgs) {
|
||||
if (ProgressKeeper.getTaskCount() == 0)
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
package net.kdt.pojavlaunch.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ExpandableListAdapter;
|
||||
import android.widget.ExpandableListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import net.kdt.pojavlaunch.R;
|
||||
import net.kdt.pojavlaunch.Tools;
|
||||
import net.kdt.pojavlaunch.modloaders.ModloaderDownloadListener;
|
||||
import net.kdt.pojavlaunch.modloaders.ModloaderListenerProxy;
|
||||
import net.kdt.pojavlaunch.progresskeeper.ProgressKeeper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class ModVersionListFragment<T> extends Fragment implements Runnable, View.OnClickListener, ExpandableListView.OnChildClickListener, ModloaderDownloadListener {
|
||||
public static final String TAG = "ForgeInstallFragment";
|
||||
private ExpandableListView mExpandableListView;
|
||||
private ProgressBar mProgressBar;
|
||||
private LayoutInflater mInflater;
|
||||
private View mRetryView;
|
||||
|
||||
public ModVersionListFragment() {
|
||||
super(R.layout.fragment_mod_version_list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
this.mInflater = LayoutInflater.from(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
((TextView)view.findViewById(R.id.title_textview)).setText(getTitleText());
|
||||
mProgressBar = view.findViewById(R.id.mod_dl_list_progress);
|
||||
mExpandableListView = view.findViewById(R.id.mod_dl_expandable_version_list);
|
||||
mExpandableListView.setOnChildClickListener(this);
|
||||
mRetryView = view.findViewById(R.id.mod_dl_retry_layout);
|
||||
view.findViewById(R.id.forge_installer_retry_button).setOnClickListener(this);
|
||||
ModloaderListenerProxy taskProxy = getTaskProxy();
|
||||
if(taskProxy != null) {
|
||||
mExpandableListView.setEnabled(false);
|
||||
taskProxy.attachListener(this);
|
||||
}
|
||||
new Thread(this).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
ModloaderListenerProxy taskProxy = getTaskProxy();
|
||||
if(taskProxy != null) taskProxy.detachListener();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
T versions = loadVersionList();
|
||||
Tools.runOnUiThread(()->{
|
||||
if(versions != null) {
|
||||
mExpandableListView.setAdapter(createAdapter(versions, 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) {
|
||||
if(ProgressKeeper.hasOngoingTasks()) {
|
||||
Toast.makeText(expandableListView.getContext(), R.string.tasks_ongoing, Toast.LENGTH_LONG).show();
|
||||
return true;
|
||||
}
|
||||
Object forgeVersion = expandableListView.getExpandableListAdapter().getChild(i, i1);
|
||||
ModloaderListenerProxy taskProxy = new ModloaderListenerProxy();
|
||||
Runnable downloadTask = createDownloadTask(forgeVersion, taskProxy);
|
||||
setTaskProxy(taskProxy);
|
||||
taskProxy.attachListener(this);
|
||||
mExpandableListView.setEnabled(false);
|
||||
new Thread(downloadTask).start();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadFinished(File downloadedFile) {
|
||||
Tools.runOnUiThread(()->{
|
||||
Context context = requireContext();
|
||||
getTaskProxy().detachListener();
|
||||
setTaskProxy(null);
|
||||
mExpandableListView.setEnabled(true);
|
||||
// Read the comment in FabricInstallFragment.onDownloadFinished() to see how this works
|
||||
getParentFragmentManager().popBackStack();
|
||||
onDownloadFinished(context, downloadedFile);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataNotAvailable() {
|
||||
Tools.runOnUiThread(()->{
|
||||
Context context = requireContext();
|
||||
getTaskProxy().detachListener();
|
||||
setTaskProxy(null);
|
||||
mExpandableListView.setEnabled(true);
|
||||
Tools.dialog(context,
|
||||
context.getString(R.string.global_error),
|
||||
context.getString(getNoDataMsg()));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadError(Exception e) {
|
||||
Tools.runOnUiThread(()->{
|
||||
Context context = requireContext();
|
||||
getTaskProxy().detachListener();
|
||||
setTaskProxy(null);
|
||||
mExpandableListView.setEnabled(true);
|
||||
Tools.showError(context, e);
|
||||
});
|
||||
}
|
||||
|
||||
public abstract int getTitleText();
|
||||
public abstract int getNoDataMsg();
|
||||
public abstract ModloaderListenerProxy getTaskProxy();
|
||||
public abstract T loadVersionList() throws IOException;
|
||||
public abstract void setTaskProxy(ModloaderListenerProxy proxy);
|
||||
public abstract ExpandableListAdapter createAdapter(T versionList, LayoutInflater layoutInflater);
|
||||
public abstract Runnable createDownloadTask(Object selectedVersion, ModloaderListenerProxy listenerProxy);
|
||||
public abstract void onDownloadFinished(Context context, File downloadedFile);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package net.kdt.pojavlaunch.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.ExpandableListAdapter;
|
||||
|
||||
import net.kdt.pojavlaunch.JavaGUILauncherActivity;
|
||||
import net.kdt.pojavlaunch.R;
|
||||
import net.kdt.pojavlaunch.Tools;
|
||||
import net.kdt.pojavlaunch.modloaders.ModloaderListenerProxy;
|
||||
import net.kdt.pojavlaunch.modloaders.OptiFineDownloadTask;
|
||||
import net.kdt.pojavlaunch.modloaders.OptiFineUtils;
|
||||
import net.kdt.pojavlaunch.modloaders.OptiFineVersionListAdapter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class OptiFineInstallFragment extends ModVersionListFragment<OptiFineUtils.OptiFineVersions> {
|
||||
public static final String TAG = "OptiFineInstallFragment";
|
||||
private static ModloaderListenerProxy sTaskProxy;
|
||||
@Override
|
||||
public int getTitleText() {
|
||||
return R.string.of_dl_select_version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNoDataMsg() {
|
||||
return R.string.of_dl_failed_to_scrape;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModloaderListenerProxy getTaskProxy() {
|
||||
return sTaskProxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptiFineUtils.OptiFineVersions loadVersionList() throws IOException {
|
||||
return OptiFineUtils.downloadOptiFineVersions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTaskProxy(ModloaderListenerProxy proxy) {
|
||||
sTaskProxy = proxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpandableListAdapter createAdapter(OptiFineUtils.OptiFineVersions versionList, LayoutInflater layoutInflater) {
|
||||
return new OptiFineVersionListAdapter(versionList, layoutInflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Runnable createDownloadTask(Object selectedVersion, ModloaderListenerProxy listenerProxy) {
|
||||
return new OptiFineDownloadTask((OptiFineUtils.OptiFineVersion) selectedVersion,
|
||||
new File(Tools.DIR_CACHE, "optifine-installer.jar"), listenerProxy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadFinished(Context context, File downloadedFile) {
|
||||
Intent modInstallerStartIntent = new Intent(context, JavaGUILauncherActivity.class);
|
||||
OptiFineUtils.addAutoInstallArgs(modInstallerStartIntent, downloadedFile);
|
||||
context.startActivity(modInstallerStartIntent);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +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;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -11,17 +8,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 +23,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 +110,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);
|
||||
}));
|
||||
|
||||
|
||||
|
||||
@@ -180,7 +155,7 @@ public class ProfileEditorFragment extends Fragment {
|
||||
|
||||
private MinecraftProfile getProfile(@NonNull String profile){
|
||||
MinecraftProfile minecraftProfile;
|
||||
if(getArguments() == null && !profile.equals(CREATE_PROFILE_MAGIC)) {
|
||||
if(getArguments() == null) {
|
||||
minecraftProfile = new MinecraftProfile(LauncherProfiles.mainProfileJson.profiles.get(profile));
|
||||
mProfileKey = profile;
|
||||
}else{
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package net.kdt.pojavlaunch.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import net.kdt.pojavlaunch.R;
|
||||
import net.kdt.pojavlaunch.Tools;
|
||||
|
||||
public class ProfileTypeSelectFragment extends Fragment {
|
||||
public static final String TAG = "ProfileTypeSelectFragment";
|
||||
public ProfileTypeSelectFragment() {
|
||||
super(R.layout.fragment_profile_type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
view.findViewById(R.id.vanilla_profile).setOnClickListener(v -> Tools.swapFragment(requireActivity(), ProfileEditorFragment.class,
|
||||
ProfileEditorFragment.TAG, false, new Bundle(1)));
|
||||
|
||||
// NOTE: Special care needed! If you wll decide to add these to the back stack, please read
|
||||
// the comment in FabricInstallFragment.onDownloadFinished() and amend the code
|
||||
// in FabricInstallFragment.onDownloadFinished() and ModVersionListFragment.onDownloadFinished()
|
||||
view.findViewById(R.id.optifine_profile).setOnClickListener(v -> Tools.swapFragment(requireActivity(), OptiFineInstallFragment.class,
|
||||
OptiFineInstallFragment.TAG, false, null));
|
||||
view.findViewById(R.id.modded_profile_fabric).setOnClickListener((v)->
|
||||
Tools.swapFragment(requireActivity(), FabricInstallFragment.class, FabricInstallFragment.TAG, false, null));
|
||||
view.findViewById(R.id.modded_profile_forge).setOnClickListener((v)->
|
||||
Tools.swapFragment(requireActivity(), ForgeInstallFragment.class, ForgeInstallFragment.TAG, false, null));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
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 {
|
||||
try {
|
||||
return DownloadUtils.downloadStringCached(FABRIC_LOADER_METADATA_URL,
|
||||
"fabric_loader_versions", (input)->{
|
||||
final List<String> loaderList = new ArrayList<>();
|
||||
try {
|
||||
enumerateMetadata(input, (object) -> {
|
||||
if (onlyStable && !object.getBoolean("stable")) return false;
|
||||
loaderList.add(object.getString("version"));
|
||||
return false;
|
||||
});
|
||||
}catch (JSONException e) {
|
||||
throw new DownloadUtils.ParseException(e);
|
||||
}
|
||||
return loaderList;
|
||||
});
|
||||
}catch (DownloadUtils.ParseException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] getInstallerUrlAndVersion() throws IOException{
|
||||
String installerMetadata = DownloadUtils.downloadString(FABRIC_INSTALLER_METADATA_URL);
|
||||
try {
|
||||
return DownloadUtils.downloadStringCached(FABRIC_INSTALLER_METADATA_URL,
|
||||
"fabric_installer_versions", input -> {
|
||||
try {
|
||||
JSONObject selectedMetadata = enumerateMetadata(installerMetadata, (object) ->
|
||||
object.getBoolean("stable"));
|
||||
if (selectedMetadata == null) return null;
|
||||
return new String[]{selectedMetadata.getString("url"),
|
||||
selectedMetadata.getString("version")};
|
||||
} catch (JSONException e) {
|
||||
throw new DownloadUtils.ParseException(e);
|
||||
}
|
||||
});
|
||||
}catch (DownloadUtils.ParseException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
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) throws JSONException{
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
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.FileNotFoundException;
|
||||
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 ModloaderDownloadListener mListener;
|
||||
public ForgeDownloadTask(ModloaderDownloadListener listener, String forgeVersion, File destinationFile) {
|
||||
this.mListener = listener;
|
||||
this.mForgeUrl = ForgeUtils.getInstallerUrl(forgeVersion);
|
||||
this.mForgeVersion = forgeVersion;
|
||||
this.mDestinationFile = destinationFile;
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
ProgressKeeper.submitProgress(ProgressLayout.INSTALL_MODPACK, 0, R.string.forge_dl_progress, mForgeVersion);
|
||||
try {
|
||||
byte[] buffer = new byte[8192];
|
||||
DownloadUtils.downloadFileMonitored(mForgeUrl, mDestinationFile, buffer, this);
|
||||
mListener.onDownloadFinished(mDestinationFile);
|
||||
}catch (IOException e) {
|
||||
if(e instanceof FileNotFoundException) {
|
||||
mListener.onDataNotAvailable();
|
||||
}else{
|
||||
mListener.onDownloadError(e);
|
||||
}
|
||||
}
|
||||
ProgressKeeper.submitProgress(ProgressLayout.INSTALL_MODPACK, -1, -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProgress(int curr, int max) {
|
||||
int progress100 = (int)(((float)curr / (float)max)*100f);
|
||||
ProgressKeeper.submitProgress(ProgressLayout.INSTALL_MODPACK, progress100, R.string.forge_dl_progress, mForgeVersion);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package net.kdt.pojavlaunch.modloaders;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import net.kdt.pojavlaunch.Tools;
|
||||
import net.kdt.pojavlaunch.utils.DownloadUtils;
|
||||
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
public class ForgeUtils {
|
||||
private static final String FORGE_METADATA_URL = "https://maven.minecraftforge.net/net/minecraftforge/forge/maven-metadata.xml";
|
||||
private static final String FORGE_INSTALLER_URL = "https://maven.minecraftforge.net/net/minecraftforge/forge/%1$s/forge-%1$s-installer.jar";
|
||||
public static List<String> downloadForgeVersions() throws IOException {
|
||||
SAXParser saxParser;
|
||||
try {
|
||||
SAXParserFactory parserFactory = SAXParserFactory.newInstance();
|
||||
saxParser = parserFactory.newSAXParser();
|
||||
}catch (SAXException | ParserConfigurationException e) {
|
||||
e.printStackTrace();
|
||||
// if we cant make a parser we might as well not even try to parse anything
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
//of_test();
|
||||
return DownloadUtils.downloadStringCached(FORGE_METADATA_URL, "forge_versions", input -> {
|
||||
try {
|
||||
ForgeVersionListHandler handler = new ForgeVersionListHandler();
|
||||
saxParser.parse(new InputSource(new StringReader(input)), handler);
|
||||
return handler.getVersions();
|
||||
// IOException is present here StringReader throws it only if the parser called close()
|
||||
// sooner than needed, which is a parser issue and not an I/O one
|
||||
}catch (SAXException | IOException e) {
|
||||
throw new DownloadUtils.ParseException(e);
|
||||
}
|
||||
});
|
||||
}catch (DownloadUtils.ParseException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
public static String getInstallerUrl(String version) {
|
||||
return String.format(FORGE_INSTALLER_URL, version);
|
||||
}
|
||||
|
||||
public static void addAutoInstallArgs(Intent intent, File modInstallerJar, boolean createProfile) {
|
||||
intent.putExtra("javaArgs", "-javaagent:"+ Tools.DIR_DATA+"/forge_installer/forge_installer.jar"
|
||||
+ (createProfile ? "=NPS" : "") + // No Profile Suppression
|
||||
" -jar "+modInstallerJar.getAbsolutePath());
|
||||
intent.putExtra("skipDetectMod", true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package net.kdt.pojavlaunch.modloaders;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseExpandableListAdapter;
|
||||
import android.widget.ExpandableListAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ForgeVersionListAdapter extends BaseExpandableListAdapter implements ExpandableListAdapter {
|
||||
private final List<String> mGameVersions;
|
||||
private final List<List<String>> mForgeVersions;
|
||||
private final LayoutInflater mLayoutInflater;
|
||||
|
||||
public ForgeVersionListAdapter(List<String> forgeVersions, LayoutInflater layoutInflater) {
|
||||
this.mLayoutInflater = layoutInflater;
|
||||
mGameVersions = new ArrayList<>();
|
||||
mForgeVersions = new ArrayList<>();
|
||||
for(String version : forgeVersions) {
|
||||
int dashIndex = version.indexOf("-");
|
||||
String gameVersion = version.substring(0, dashIndex);
|
||||
List<String> versionList;
|
||||
int gameVersionIndex = mGameVersions.indexOf(gameVersion);
|
||||
if(gameVersionIndex != -1) versionList = mForgeVersions.get(gameVersionIndex);
|
||||
else {
|
||||
versionList = new ArrayList<>();
|
||||
mGameVersions.add(gameVersion);
|
||||
mForgeVersions.add(versionList);
|
||||
}
|
||||
versionList.add(version);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGroupCount() {
|
||||
return mGameVersions.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChildrenCount(int i) {
|
||||
return mForgeVersions.get(i).size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGroup(int i) {
|
||||
return getGameVersion(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getChild(int i, int i1) {
|
||||
return getForgeVersion(i, i1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getGroupId(int i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getChildId(int i, int i1) {
|
||||
return i1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasStableIds() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getGroupView(int i, boolean b, View convertView, ViewGroup viewGroup) {
|
||||
if(convertView == null)
|
||||
convertView = mLayoutInflater.inflate(android.R.layout.simple_expandable_list_item_1, viewGroup, false);
|
||||
|
||||
((TextView) convertView).setText(getGameVersion(i));
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getChildView(int i, int i1, boolean b, View convertView, ViewGroup viewGroup) {
|
||||
if(convertView == null)
|
||||
convertView = mLayoutInflater.inflate(android.R.layout.simple_expandable_list_item_1, viewGroup, false);
|
||||
((TextView) convertView).setText(getForgeVersion(i, i1));
|
||||
return convertView;
|
||||
}
|
||||
|
||||
private String getGameVersion(int i) {
|
||||
return mGameVersions.get(i);
|
||||
}
|
||||
|
||||
private String getForgeVersion(int i, int i1){
|
||||
return mForgeVersions.get(i).get(i1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChildSelectable(int i, int i1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package net.kdt.pojavlaunch.modloaders;
|
||||
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ForgeVersionListHandler extends DefaultHandler {
|
||||
private List<String> mForgeVersions;
|
||||
private StringBuilder mCurrentVersion = null;
|
||||
@Override
|
||||
public void startDocument() throws SAXException {
|
||||
mForgeVersions = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void characters(char[] ch, int start, int length) throws SAXException {
|
||||
if(mCurrentVersion != null) mCurrentVersion.append(ch, start, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
|
||||
if(qName.equals("version")) mCurrentVersion = new StringBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String uri, String localName, String qName) throws SAXException {
|
||||
if (qName.equals("version")) {
|
||||
String version = mCurrentVersion.toString();
|
||||
mForgeVersions.add(version);
|
||||
mCurrentVersion = null;
|
||||
}
|
||||
}
|
||||
public List<String> getVersions() {
|
||||
return mForgeVersions;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package net.kdt.pojavlaunch.modloaders;
|
||||
|
||||
import org.htmlcleaner.HtmlCleaner;
|
||||
import org.htmlcleaner.HtmlNode;
|
||||
import org.htmlcleaner.TagNode;
|
||||
import org.htmlcleaner.TagNodeVisitor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
public class OFDownloadPageScraper implements TagNodeVisitor {
|
||||
public static String run(String urlInput) throws IOException{
|
||||
return new OFDownloadPageScraper().runInner(urlInput);
|
||||
}
|
||||
|
||||
private String mDownloadFullUrl;
|
||||
|
||||
private String runInner(String url) throws IOException {
|
||||
HtmlCleaner htmlCleaner = new HtmlCleaner();
|
||||
htmlCleaner.clean(new URL(url)).traverse(this);
|
||||
return mDownloadFullUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(TagNode parentNode, HtmlNode htmlNode) {
|
||||
if(isDownloadUrl(parentNode, htmlNode)) {
|
||||
TagNode tagNode = (TagNode) htmlNode;
|
||||
String href = tagNode.getAttributeByName("href");
|
||||
if(!href.startsWith("https://")) href = "https://optifine.net/"+href;
|
||||
this.mDownloadFullUrl = href;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDownloadUrl(TagNode parentNode, HtmlNode htmlNode) {
|
||||
if(!(htmlNode instanceof TagNode)) return false;
|
||||
if(parentNode == null) return false;
|
||||
TagNode tagNode = (TagNode) htmlNode;
|
||||
if(!(parentNode.getName().equals("span")
|
||||
&& "Download".equals(parentNode.getAttributeByName("id")))) return false;
|
||||
return tagNode.getName().equals("a") &&
|
||||
"onDownload()".equals(tagNode.getAttributeByName("onclick"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package net.kdt.pojavlaunch.modloaders;
|
||||
|
||||
import com.kdt.mcgui.ProgressLayout;
|
||||
|
||||
import net.kdt.pojavlaunch.JMinecraftVersionList;
|
||||
import net.kdt.pojavlaunch.R;
|
||||
import net.kdt.pojavlaunch.Tools;
|
||||
import net.kdt.pojavlaunch.progresskeeper.ProgressKeeper;
|
||||
import net.kdt.pojavlaunch.tasks.AsyncMinecraftDownloader;
|
||||
import net.kdt.pojavlaunch.utils.DownloadUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class OptiFineDownloadTask implements Runnable, Tools.DownloaderFeedback, AsyncMinecraftDownloader.DoneListener {
|
||||
private static final Pattern sMcVersionPattern = Pattern.compile("([0-9]+)\\.([0-9]+)\\.?([0-9]+)?");
|
||||
private final OptiFineUtils.OptiFineVersion mOptiFineVersion;
|
||||
private final File mDestinationFile;
|
||||
private final ModloaderDownloadListener mListener;
|
||||
private final Object mMinecraftDownloadLock = new Object();
|
||||
private Throwable mDownloaderThrowable;
|
||||
|
||||
public OptiFineDownloadTask(OptiFineUtils.OptiFineVersion mOptiFineVersion, File mDestinationFile, ModloaderDownloadListener mListener) {
|
||||
this.mOptiFineVersion = mOptiFineVersion;
|
||||
this.mDestinationFile = mDestinationFile;
|
||||
this.mListener = mListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ProgressKeeper.submitProgress(ProgressLayout.INSTALL_MODPACK, 0, R.string.of_dl_progress, mOptiFineVersion.versionName);
|
||||
try {
|
||||
if(runCatching()) mListener.onDownloadFinished(mDestinationFile);
|
||||
}catch (IOException e) {
|
||||
mListener.onDownloadError(e);
|
||||
}
|
||||
ProgressKeeper.submitProgress(ProgressLayout.INSTALL_MODPACK, -1, -1);
|
||||
}
|
||||
|
||||
public boolean runCatching() throws IOException {
|
||||
String downloadUrl = scrapeDownloadsPage();
|
||||
if(downloadUrl == null) return false;
|
||||
String minecraftVersion = determineMinecraftVersion();
|
||||
if(minecraftVersion == null) return false;
|
||||
if(!downloadMinecraft(minecraftVersion)) {
|
||||
if(mDownloaderThrowable instanceof Exception) {
|
||||
mListener.onDownloadError((Exception) mDownloaderThrowable);
|
||||
}else {
|
||||
Exception exception = new Exception(mDownloaderThrowable);
|
||||
mListener.onDownloadError(exception);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
DownloadUtils.downloadFileMonitored(downloadUrl, mDestinationFile, new byte[8192], this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public String scrapeDownloadsPage() throws IOException{
|
||||
String scrapeResult = OFDownloadPageScraper.run(mOptiFineVersion.downloadUrl);
|
||||
if(scrapeResult == null) mListener.onDataNotAvailable();
|
||||
return scrapeResult;
|
||||
}
|
||||
|
||||
public String determineMinecraftVersion() {
|
||||
Matcher matcher = sMcVersionPattern.matcher(mOptiFineVersion.minecraftVersion);
|
||||
if(matcher.find()) {
|
||||
StringBuilder mcVersionBuilder = new StringBuilder();
|
||||
mcVersionBuilder.append(matcher.group(1));
|
||||
mcVersionBuilder.append('.');
|
||||
mcVersionBuilder.append(matcher.group(2));
|
||||
String thirdGroup = matcher.group(3);
|
||||
if(thirdGroup != null && !thirdGroup.isEmpty() && !"0".equals(thirdGroup)) {
|
||||
mcVersionBuilder.append('.');
|
||||
mcVersionBuilder.append(thirdGroup);
|
||||
}
|
||||
return mcVersionBuilder.toString();
|
||||
}else{
|
||||
mListener.onDataNotAvailable();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean downloadMinecraft(String minecraftVersion) {
|
||||
// the string is always normalized
|
||||
JMinecraftVersionList.Version minecraftJsonVersion = AsyncMinecraftDownloader.getListedVersion(minecraftVersion);
|
||||
if(minecraftJsonVersion == null) return false;
|
||||
try {
|
||||
synchronized (mMinecraftDownloadLock) {
|
||||
new AsyncMinecraftDownloader(null, minecraftJsonVersion, minecraftVersion, this);
|
||||
mMinecraftDownloadLock.wait();
|
||||
}
|
||||
}catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return mDownloaderThrowable == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProgress(int curr, int max) {
|
||||
int progress100 = (int)(((float)curr / (float)max)*100f);
|
||||
ProgressKeeper.submitProgress(ProgressLayout.INSTALL_MODPACK, progress100, R.string.of_dl_progress, mOptiFineVersion.versionName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadDone() {
|
||||
synchronized (mMinecraftDownloadLock) {
|
||||
mDownloaderThrowable = null;
|
||||
mMinecraftDownloadLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadFailed(Throwable throwable) {
|
||||
synchronized (mMinecraftDownloadLock) {
|
||||
mDownloaderThrowable = throwable;
|
||||
mMinecraftDownloadLock.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package net.kdt.pojavlaunch.modloaders;
|
||||
|
||||
import net.kdt.pojavlaunch.utils.DownloadUtils;
|
||||
|
||||
import org.htmlcleaner.HtmlCleaner;
|
||||
import org.htmlcleaner.TagNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class OptiFineScraper implements DownloadUtils.ParseCallback<OptiFineUtils.OptiFineVersions> {
|
||||
private final OptiFineUtils.OptiFineVersions mOptiFineVersions;
|
||||
private List<OptiFineUtils.OptiFineVersion> mListInProgress;
|
||||
private String mMinecraftVersion;
|
||||
|
||||
public OptiFineScraper() {
|
||||
mOptiFineVersions = new OptiFineUtils.OptiFineVersions();
|
||||
mOptiFineVersions.minecraftVersions = new ArrayList<>();
|
||||
mOptiFineVersions.optifineVersions = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptiFineUtils.OptiFineVersions process(String input) throws DownloadUtils.ParseException {
|
||||
HtmlCleaner htmlCleaner = new HtmlCleaner();
|
||||
TagNode tagNode = htmlCleaner.clean(input);
|
||||
traverseTagNode(tagNode);
|
||||
insertVersionContent(null);
|
||||
if(mOptiFineVersions.optifineVersions.size() < 1 ||
|
||||
mOptiFineVersions.minecraftVersions.size() < 1) throw new DownloadUtils.ParseException(null);
|
||||
return mOptiFineVersions;
|
||||
}
|
||||
|
||||
public void traverseTagNode(TagNode tagNode) {
|
||||
if(isDownloadLine(tagNode) && mMinecraftVersion != null) {
|
||||
traverseDownloadLine(tagNode);
|
||||
} else if(isMinecraftVersionTag(tagNode)) {
|
||||
insertVersionContent(tagNode);
|
||||
} else {
|
||||
for(TagNode tagNodes : tagNode.getChildTags()) {
|
||||
traverseTagNode(tagNodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDownloadLine(TagNode tagNode) {
|
||||
return tagNode.getName().equals("tr") &&
|
||||
tagNode.hasAttribute("class") &&
|
||||
tagNode.getAttributeByName("class").startsWith("downloadLine");
|
||||
}
|
||||
|
||||
private boolean isMinecraftVersionTag(TagNode tagNode) {
|
||||
return tagNode.getName().equals("h2") &&
|
||||
tagNode.getText().toString().startsWith("Minecraft ");
|
||||
}
|
||||
|
||||
private void traverseDownloadLine(TagNode tagNode) {
|
||||
OptiFineUtils.OptiFineVersion optiFineVersion = new OptiFineUtils.OptiFineVersion();
|
||||
optiFineVersion.minecraftVersion = mMinecraftVersion;
|
||||
for(TagNode subNode : tagNode.getChildTags()) {
|
||||
if(!subNode.getName().equals("td")) continue;
|
||||
switch(subNode.getAttributeByName("class")) {
|
||||
case "colFile":
|
||||
optiFineVersion.versionName = subNode.getText().toString();
|
||||
break;
|
||||
case "colMirror":
|
||||
optiFineVersion.downloadUrl = getLinkHref(subNode);
|
||||
}
|
||||
}
|
||||
mListInProgress.add(optiFineVersion);
|
||||
}
|
||||
private String getLinkHref(TagNode parent) {
|
||||
for(TagNode subNode : parent.getChildTags()) {
|
||||
if(subNode.getName().equals("a") && subNode.hasAttribute("href")) {
|
||||
return subNode.getAttributeByName("href").replace("http://", "https://");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void insertVersionContent(TagNode tagNode) {
|
||||
if(mListInProgress != null && mMinecraftVersion != null) {
|
||||
mOptiFineVersions.minecraftVersions.add(mMinecraftVersion);
|
||||
mOptiFineVersions.optifineVersions.add(mListInProgress);
|
||||
}
|
||||
if(tagNode != null) {
|
||||
mMinecraftVersion = tagNode.getText().toString();
|
||||
mListInProgress = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package net.kdt.pojavlaunch.modloaders;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import net.kdt.pojavlaunch.Tools;
|
||||
import net.kdt.pojavlaunch.utils.DownloadUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class OptiFineUtils {
|
||||
|
||||
public static OptiFineVersions downloadOptiFineVersions() throws IOException {
|
||||
try {
|
||||
return DownloadUtils.downloadStringCached("https://optifine.net/downloads",
|
||||
"of_downloads_page", new OptiFineScraper());
|
||||
}catch (DownloadUtils.ParseException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void addAutoInstallArgs(Intent intent, File modInstallerJar) {
|
||||
intent.putExtra("javaArgs", "-javaagent:"+ Tools.DIR_DATA+"/forge_installer/forge_installer.jar"
|
||||
+ "=OFNPS" +// No Profile Suppression
|
||||
" -jar "+modInstallerJar.getAbsolutePath());
|
||||
intent.putExtra("skipDetectMod", true);
|
||||
}
|
||||
|
||||
public static class OptiFineVersions {
|
||||
public List<String> minecraftVersions;
|
||||
public List<List<OptiFineVersion>> optifineVersions;
|
||||
}
|
||||
public static class OptiFineVersion {
|
||||
public String minecraftVersion;
|
||||
public String versionName;
|
||||
public String downloadUrl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package net.kdt.pojavlaunch.modloaders;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseExpandableListAdapter;
|
||||
import android.widget.ExpandableListAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class OptiFineVersionListAdapter extends BaseExpandableListAdapter implements ExpandableListAdapter {
|
||||
|
||||
private final OptiFineUtils.OptiFineVersions mOptiFineVersions;
|
||||
private final LayoutInflater mLayoutInflater;
|
||||
|
||||
public OptiFineVersionListAdapter(OptiFineUtils.OptiFineVersions optiFineVersions, LayoutInflater mLayoutInflater) {
|
||||
mOptiFineVersions = optiFineVersions;
|
||||
this.mLayoutInflater = mLayoutInflater;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGroupCount() {
|
||||
return mOptiFineVersions.minecraftVersions.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChildrenCount(int i) {
|
||||
return mOptiFineVersions.optifineVersions.get(i).size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGroup(int i) {
|
||||
return mOptiFineVersions.minecraftVersions.get(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getChild(int i, int i1) {
|
||||
return mOptiFineVersions.optifineVersions.get(i).get(i1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getGroupId(int i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getChildId(int i, int i1) {
|
||||
return i1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasStableIds() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getGroupView(int i, boolean b, View convertView, ViewGroup viewGroup) {
|
||||
if(convertView == null)
|
||||
convertView = mLayoutInflater.inflate(android.R.layout.simple_expandable_list_item_1, viewGroup, false);
|
||||
|
||||
((TextView) convertView).setText((String)getGroup(i));
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getChildView(int i, int i1, boolean b, View convertView, ViewGroup viewGroup) {
|
||||
if(convertView == null)
|
||||
convertView = mLayoutInflater.inflate(android.R.layout.simple_expandable_list_item_1, viewGroup, false);
|
||||
((TextView) convertView).setText(((OptiFineUtils.OptiFineVersion)getChild(i,i1)).versionName);
|
||||
return convertView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChildSelectable(int i, int i1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -29,25 +29,17 @@ import fr.spse.extended_view.ExtendedTextView;
|
||||
*/
|
||||
public class ProfileAdapter extends BaseAdapter {
|
||||
private Map<String, MinecraftProfile> mProfiles;
|
||||
public static final String CREATE_PROFILE_MAGIC = "___extra____profile-create";
|
||||
private final MinecraftProfile dummy = new MinecraftProfile();
|
||||
private MinecraftProfile mCreateProfile;
|
||||
private List<String> mProfileList;
|
||||
private final ProfileAdapterExtra[] mExtraEntires;
|
||||
|
||||
public ProfileAdapter(Context context, boolean enableCreateButton) {
|
||||
public ProfileAdapter(Context context, ProfileAdapterExtra[] extraEntries) {
|
||||
ProfileIconCache.initDefault(context);
|
||||
LauncherProfiles.update();
|
||||
mProfiles = new HashMap<>(LauncherProfiles.mainProfileJson.profiles);
|
||||
if(enableCreateButton) {
|
||||
mCreateProfile = new MinecraftProfile();
|
||||
mCreateProfile.name = context.getString(R.string.create_profile);
|
||||
mCreateProfile.lastVersionId = null;
|
||||
}
|
||||
if(extraEntries == null) mExtraEntires = new ProfileAdapterExtra[0];
|
||||
else mExtraEntires = extraEntries;
|
||||
mProfileList = new ArrayList<>(Arrays.asList(mProfiles.keySet().toArray(new String[0])));
|
||||
if(enableCreateButton) {
|
||||
mProfileList.add(ProfileAdapter.CREATE_PROFILE_MAGIC);
|
||||
mProfiles.put(CREATE_PROFILE_MAGIC, mCreateProfile);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Gets how much profiles are loaded in the adapter right now
|
||||
@@ -55,7 +47,7 @@ public class ProfileAdapter extends BaseAdapter {
|
||||
*/
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mProfileList.size();
|
||||
return mProfileList.size() + mExtraEntires.length;
|
||||
}
|
||||
/*
|
||||
* Gets the profile at a given index
|
||||
@@ -64,12 +56,15 @@ public class ProfileAdapter extends BaseAdapter {
|
||||
*/
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
//safe since the second check in the and statement will be skipped if the first one fails
|
||||
if(position < mProfileList.size() && mProfiles.containsKey(mProfileList.get(position))) {
|
||||
return mProfileList.get(position);
|
||||
}else{
|
||||
return null;
|
||||
int profileListSize = mProfileList.size();
|
||||
int extraPosition = position - profileListSize;
|
||||
if(position < profileListSize){
|
||||
String profileName = mProfileList.get(position);
|
||||
if(mProfiles.containsKey(profileName)) return profileName;
|
||||
}else if(extraPosition >= 0 && extraPosition < mExtraEntires.length) {
|
||||
return mExtraEntires[extraPosition];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int resolveProfileIndex(String name) {
|
||||
@@ -85,8 +80,6 @@ public class ProfileAdapter extends BaseAdapter {
|
||||
public void notifyDataSetChanged() {
|
||||
mProfiles = new HashMap<>(LauncherProfiles.mainProfileJson.profiles);
|
||||
mProfileList = new ArrayList<>(Arrays.asList(mProfiles.keySet().toArray(new String[0])));
|
||||
mProfileList.add(ProfileAdapter.CREATE_PROFILE_MAGIC);
|
||||
mProfiles.put(CREATE_PROFILE_MAGIC, mCreateProfile);
|
||||
super.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@@ -94,7 +87,9 @@ public class ProfileAdapter extends BaseAdapter {
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
View v = convertView;
|
||||
if (v == null) v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_version_profile_layout,parent,false);
|
||||
setViewProfile(v,mProfileList.get(position), true);
|
||||
Object profileObject = getItem(position);
|
||||
if(profileObject instanceof String) setViewProfile(v, (String) profileObject, true);
|
||||
else if(profileObject instanceof ProfileAdapterExtra) setViewExtra(v, (ProfileAdapterExtra) profileObject);
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -131,7 +126,12 @@ public class ProfileAdapter extends BaseAdapter {
|
||||
String selectedProfile = LauncherPreferences.DEFAULT_PREF.getString(LauncherPreferences.PREF_KEY_CURRENT_PROFILE,"");
|
||||
extendedTextView.setBackgroundColor(selectedProfile.equals(nm) ? ColorUtils.setAlphaComponent(Color.WHITE,60) : Color.TRANSPARENT);
|
||||
}else extendedTextView.setBackgroundColor(Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
|
||||
public void setViewExtra(View v, ProfileAdapterExtra extra) {
|
||||
ExtendedTextView extendedTextView = (ExtendedTextView) v;
|
||||
extendedTextView.setCompoundDrawablesRelative(extra.icon, null, extendedTextView.getCompoundsDrawables()[2], null);
|
||||
extendedTextView.setText(extra.name);
|
||||
extendedTextView.setBackgroundColor(Color.TRANSPARENT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package net.kdt.pojavlaunch.profiles;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
public class ProfileAdapterExtra {
|
||||
public final int id;
|
||||
public final int name;
|
||||
public final Drawable icon;
|
||||
|
||||
public ProfileAdapterExtra(int id, int name, Drawable icon) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.icon = icon;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package net.kdt.pojavlaunch.profiles;
|
||||
|
||||
import static net.kdt.pojavlaunch.profiles.ProfileAdapter.CREATE_PROFILE_MAGIC;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.BitmapFactory;
|
||||
@@ -46,21 +44,11 @@ public class ProfileIconCache {
|
||||
icon = ProfileIconCache.submitIcon(resources, profileName, b64Icon.substring(BASE64_PNG_HEADER.length()));
|
||||
}else{
|
||||
Log.i("IconParser","Unsupported icon: "+b64Icon);
|
||||
if(profileName.equals(CREATE_PROFILE_MAGIC)){
|
||||
icon = ProfileIconCache.pushAddProfileIcon(ResourcesCompat.getDrawable(resources, R.drawable.ic_add, null));
|
||||
}else{
|
||||
icon = ProfileIconCache.pushDefaultIcon(profileName);
|
||||
}
|
||||
|
||||
icon = ProfileIconCache.pushDefaultIcon(profileName);
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
|
||||
public static Drawable pushAddProfileIcon(Drawable drawable){
|
||||
sIconCache.put(CREATE_PROFILE_MAGIC, drawable);
|
||||
return drawable;
|
||||
}
|
||||
|
||||
public static Drawable pushDefaultIcon(String key) {
|
||||
sIconCache.put(key, sDefaultIcon);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package net.kdt.pojavlaunch.profiles;
|
||||
|
||||
public interface VersionSelectorListener {
|
||||
void onVersionSelected(String versionId, boolean isSnapshot);
|
||||
}
|
||||
@@ -100,4 +100,8 @@ public class ProgressKeeper {
|
||||
public static synchronized int getTaskCount() {
|
||||
return sProgressStates.size();
|
||||
}
|
||||
|
||||
public static boolean hasOngoingTasks() {
|
||||
return getTaskCount() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -68,7 +68,6 @@ public class AsyncAssetManager {
|
||||
|
||||
Tools.copyAssetFile(ctx, "launcher_profiles.json", Tools.DIR_GAME_NEW, false);
|
||||
Tools.copyAssetFile(ctx,"resolv.conf",Tools.DIR_DATA, false);
|
||||
Tools.copyAssetFile(ctx,"arc_dns_injector.jar",Tools.DIR_DATA, false);
|
||||
} catch (IOException e) {
|
||||
Log.e("AsyncAssetManager", "Failed to unpack critical components !");
|
||||
}
|
||||
@@ -86,6 +85,8 @@ public class AsyncAssetManager {
|
||||
// we repack them to a single file here
|
||||
unpackComponent(ctx, "lwjgl3", false);
|
||||
unpackComponent(ctx, "security", true);
|
||||
unpackComponent(ctx, "arc_dns_injector", true);
|
||||
unpackComponent(ctx, "forge_installer", true);
|
||||
} catch (IOException e) {
|
||||
Log.e("AsyncAssetManager", "Failed o unpack components !",e );
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public class AsyncMinecraftDownloader {
|
||||
/* Allows each downloading thread to have its own RECYCLED buffer */
|
||||
private final ConcurrentHashMap<Thread, byte[]> mThreadBuffers = new ConcurrentHashMap<>(5);
|
||||
|
||||
public AsyncMinecraftDownloader(@NonNull Activity activity, JMinecraftVersionList.Version version, String realVersion,
|
||||
public AsyncMinecraftDownloader(Activity activity, JMinecraftVersionList.Version version, String realVersion,
|
||||
@NonNull DoneListener listener){ // this was there for a reason
|
||||
sExecutorService.execute(() -> {
|
||||
try {
|
||||
@@ -58,7 +58,7 @@ public class AsyncMinecraftDownloader {
|
||||
});
|
||||
}
|
||||
/* we do the throws DownloaderException thing to avoid blanket-catching Exception as a form of anti-lazy-developer protection */
|
||||
private void downloadGame(@NonNull Activity activity, JMinecraftVersionList.Version verInfo, String versionName) throws DownloaderException {
|
||||
private void downloadGame(Activity activity, JMinecraftVersionList.Version verInfo, String versionName) throws DownloaderException {
|
||||
final String downVName = "/" + versionName + "/" + versionName;
|
||||
|
||||
//Downloading libraries
|
||||
@@ -88,7 +88,7 @@ public class AsyncMinecraftDownloader {
|
||||
verInfo = Tools.getVersionInfo(versionName);
|
||||
|
||||
// THIS one function need the activity in the case of an error
|
||||
if(!JRE17Util.installNewJreIfNeeded(activity, verInfo)){
|
||||
if(activity != null && !JRE17Util.installNewJreIfNeeded(activity, verInfo)){
|
||||
ProgressKeeper.submitProgress(ProgressLayout.DOWNLOAD_MINECRAFT, -1, -1);
|
||||
throw new DownloaderException();
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -7,6 +7,8 @@ import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.gson.JsonIOException;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
|
||||
import net.kdt.pojavlaunch.JMinecraftVersionList;
|
||||
@@ -22,7 +24,7 @@ import java.io.IOException;
|
||||
/** Class getting the version list, and that's all really */
|
||||
public class AsyncVersionList {
|
||||
|
||||
public void getVersionList(@Nullable VersionDoneListener listener){
|
||||
public void getVersionList(@Nullable VersionDoneListener listener, boolean secondPass){
|
||||
sExecutorService.execute(() -> {
|
||||
File versionFile = new File(Tools.DIR_DATA + "/version_list.json");
|
||||
JMinecraftVersionList versionList = null;
|
||||
@@ -41,6 +43,11 @@ public class AsyncVersionList {
|
||||
versionList = Tools.GLOBAL_GSON.fromJson(new JsonReader(new FileReader(versionFile)), JMinecraftVersionList.class);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (JsonIOException | JsonSyntaxException e) {
|
||||
e.printStackTrace();
|
||||
versionFile.delete();
|
||||
if(!secondPass)
|
||||
getVersionList(listener, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package net.kdt.pojavlaunch.utils;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
@@ -115,6 +117,46 @@ public class DownloadUtils {
|
||||
conn.disconnect();
|
||||
}
|
||||
|
||||
public static <T> T downloadStringCached(String url, String cacheName, ParseCallback<T> parseCallback) throws IOException, ParseException{
|
||||
File cacheDestination = new File(Tools.DIR_CACHE, "string_cache/"+cacheName);
|
||||
File cacheDestinationDir = cacheDestination.getParentFile();
|
||||
if(cacheDestinationDir != null &&
|
||||
!cacheDestinationDir.exists() &&
|
||||
!cacheDestinationDir.mkdirs()) throw new IOException("Failed to create the cache directory");
|
||||
if(cacheDestination.isFile() &&
|
||||
cacheDestination.canRead() &&
|
||||
System.currentTimeMillis() < (cacheDestination.lastModified() + 86400000)) {
|
||||
try {
|
||||
String cachedString = Tools.read(new FileInputStream(cacheDestination));
|
||||
return parseCallback.process(cachedString);
|
||||
}catch(IOException e) {
|
||||
Log.i("DownloadUtils", "Failed to read the cached file", e);
|
||||
}catch (ParseException e) {
|
||||
Log.i("DownloadUtils", "Failed to parse the cached file", e);
|
||||
}
|
||||
}
|
||||
String urlContent = DownloadUtils.downloadString(url);
|
||||
// if we download the file and fail parsing it, we will yeet outta there
|
||||
// and not cache the unparseable sting. We will return this after trying to save the downloaded
|
||||
// string into cache
|
||||
T parseResult = parseCallback.process(urlContent);
|
||||
|
||||
boolean tryWriteCache = false;
|
||||
if(cacheDestination.exists()) {
|
||||
tryWriteCache = cacheDestination.canWrite();
|
||||
} else {
|
||||
// if it is null, then cacheDestination is the file system root. Very bad.
|
||||
// but let's shield ourselves and just never try to cache the file if that happens
|
||||
if(cacheDestinationDir != null && !cacheDestinationDir.isFile()) tryWriteCache = cacheDestinationDir.canWrite();
|
||||
}
|
||||
if(tryWriteCache) try {
|
||||
Tools.write(cacheDestination.getAbsolutePath(), urlContent);
|
||||
}catch(IOException e) {
|
||||
Log.i("DownloadUtils", "Failed to cache the string", e);
|
||||
}
|
||||
return parseResult;
|
||||
}
|
||||
|
||||
public static void downloadFileMonitoredWithHeaders(String urlInput,File outputFile, @Nullable byte[] buffer,
|
||||
Tools.DownloaderFeedback monitor, String userAgent, String cookies) throws IOException {
|
||||
if (!outputFile.exists()) {
|
||||
@@ -141,5 +183,13 @@ public class DownloadUtils {
|
||||
conn.disconnect();
|
||||
}
|
||||
|
||||
public interface ParseCallback<T> {
|
||||
T process(String input) throws ParseException;
|
||||
}
|
||||
public static class ParseException extends Exception {
|
||||
public ParseException(Exception e) {
|
||||
super(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -173,7 +173,7 @@ public class JREUtils {
|
||||
envMap.put("POJAV_NATIVEDIR", NATIVE_LIB_DIR);
|
||||
envMap.put("JAVA_HOME", jreHome);
|
||||
envMap.put("HOME", Tools.DIR_GAME_HOME);
|
||||
envMap.put("TMPDIR", activity.getCacheDir().getAbsolutePath());
|
||||
envMap.put("TMPDIR", Tools.DIR_CACHE.getAbsolutePath());
|
||||
envMap.put("LIBGL_MIPMAP", "3");
|
||||
|
||||
// On certain GLES drivers, overloading default functions shader hack fails, so disable it
|
||||
@@ -191,7 +191,7 @@ public class JREUtils {
|
||||
|
||||
envMap.put("FORCE_VSYNC", String.valueOf(LauncherPreferences.PREF_FORCE_VSYNC));
|
||||
|
||||
envMap.put("MESA_GLSL_CACHE_DIR", activity.getCacheDir().getAbsolutePath());
|
||||
envMap.put("MESA_GLSL_CACHE_DIR", Tools.DIR_CACHE.getAbsolutePath());
|
||||
if (LOCAL_RENDERER != null) {
|
||||
envMap.put("MESA_GL_VERSION_OVERRIDE", LOCAL_RENDERER.equals("opengles3_virgl")?"4.3":"4.6");
|
||||
envMap.put("MESA_GLSL_VERSION_OVERRIDE", LOCAL_RENDERER.equals("opengles3_virgl")?"430":"460");
|
||||
@@ -200,7 +200,7 @@ public class JREUtils {
|
||||
envMap.put("allow_higher_compat_version", "true");
|
||||
envMap.put("allow_glsl_extension_directive_midshader", "true");
|
||||
envMap.put("MESA_LOADER_DRIVER_OVERRIDE", "zink");
|
||||
envMap.put("VTEST_SOCKET_NAME", activity.getCacheDir().getAbsolutePath() + "/.virgl_test");
|
||||
envMap.put("VTEST_SOCKET_NAME", new File(Tools.DIR_CACHE, ".virgl_test").getAbsolutePath());
|
||||
|
||||
envMap.put("LD_LIBRARY_PATH", LD_LIBRARY_PATH);
|
||||
envMap.put("PATH", jreHome + "/bin:" + Os.getenv("PATH"));
|
||||
@@ -328,7 +328,7 @@ public class JREUtils {
|
||||
|
||||
ArrayList<String> overridableArguments = new ArrayList<>(Arrays.asList(
|
||||
"-Djava.home=" + runtimeHome,
|
||||
"-Djava.io.tmpdir=" + ctx.getCacheDir().getAbsolutePath(),
|
||||
"-Djava.io.tmpdir=" + Tools.DIR_CACHE.getAbsolutePath(),
|
||||
"-Duser.home=" + Tools.DIR_GAME_HOME,
|
||||
"-Duser.language=" + System.getProperty("user.language"),
|
||||
"-Dos.name=Linux",
|
||||
@@ -352,7 +352,7 @@ public class JREUtils {
|
||||
"-Dfml.earlyprogresswindow=false" //Forge 1.14+ workaround
|
||||
));
|
||||
if(LauncherPreferences.PREF_ARC_CAPES) {
|
||||
overridableArguments.add("-javaagent:"+new File(Tools.DIR_DATA,"arc_dns_injector.jar").getAbsolutePath()+"=23.95.137.176");
|
||||
overridableArguments.add("-javaagent:"+new File(Tools.DIR_DATA,"arc_dns_injector/arc_dns_injector.jar").getAbsolutePath()+"=23.95.137.176");
|
||||
}
|
||||
List<String> additionalArguments = new ArrayList<>();
|
||||
for(String arg : overridableArguments) {
|
||||
|
||||
@@ -85,7 +85,7 @@ Java_net_kdt_pojavlaunch_Logger_begin(JNIEnv *env, __attribute((unused)) jclass
|
||||
|
||||
/* open latestlog.txt for writing */
|
||||
const char* logFilePath = (*env)->GetStringUTFChars(env, logPath, NULL);
|
||||
latestlog_fd = open(logFilePath, O_WRONLY | O_TRUNC | O_CREAT, 644);
|
||||
latestlog_fd = open(logFilePath, O_WRONLY | O_TRUNC);
|
||||
if(latestlog_fd == -1) {
|
||||
latestlog_fd = 0;
|
||||
(*env)->ThrowNew(env, ioeClass, strerror(errno));
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,150 @@
|
||||
<?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"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/background_app"
|
||||
android:paddingHorizontal="@dimen/fragment_padding_medium">
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title_textview"
|
||||
style="@style/TextAppearance.AppCompat.Title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/padding_large"
|
||||
android:text="@string/fabric_dl_loader_title"
|
||||
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/view"
|
||||
style="@style/ThickDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginTop="@dimen/padding_large"
|
||||
app:layout_constraintTop_toBottomOf="@+id/title_textview" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/fabric_installer_label_loader_ver"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:text="@string/fabric_dl_loader_version"
|
||||
app:layout_constraintBottom_toTopOf="@+id/fabric_installer_loader_ver_spinner"
|
||||
app:layout_constraintStart_toStartOf="@+id/fabric_installer_loader_ver_spinner" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/fabric_installer_loader_ver_spinner"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/padding_extra_large"
|
||||
android:background="@drawable/background_line"
|
||||
android:minHeight="48dp"
|
||||
android:paddingVertical="0dp"
|
||||
android:paddingStart="7dp"
|
||||
android:paddingEnd="7dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/view" />
|
||||
|
||||
<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: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"
|
||||
tools:layout_editor_absoluteX="13dp">
|
||||
|
||||
<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/modloader_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:text="@string/fabric_dl_game_version"
|
||||
app:layout_constraintBottom_toTopOf="@+id/fabric_installer_version_select_label"
|
||||
app:layout_constraintStart_toStartOf="@+id/fabric_installer_version_select_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/fabric_installer_version_select_label"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/padding_extra_large"
|
||||
android:layout_marginEnd="@dimen/padding_medium"
|
||||
android:background="@drawable/background_line"
|
||||
android:hint="@string/version_select_hint"
|
||||
android:paddingHorizontal="@dimen/padding_heavy"
|
||||
|
||||
|
||||
app:layout_constraintEnd_toStartOf="@+id/fabric_installer_game_version_change"
|
||||
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/fabric_installer_retry_layout" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/fabric_installer_game_version_change"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
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_marginTop="@dimen/padding_heavy"
|
||||
android:layout_marginBottom="@dimen/padding_heavy"
|
||||
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_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>
|
||||
@@ -21,28 +21,31 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="@dimen/_16sdp"
|
||||
android:text="Select Folder"
|
||||
android:layout_marginStart="@dimen/padding_medium"
|
||||
android:text="@string/folder_fragment_select"
|
||||
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/file_selector_create_folder"
|
||||
app:layout_constraintWidth_percent="0.45" />
|
||||
/>
|
||||
|
||||
<com.kdt.mcgui.MineButton
|
||||
android:id="@+id/file_selector_create_folder"
|
||||
android:layout_width="185dp"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="@dimen/_16sdp"
|
||||
android:layout_marginEnd="@dimen/padding_medium"
|
||||
|
||||
|
||||
android:text="Create folder"
|
||||
android:text="@string/folder_fragment_create"
|
||||
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/file_selector_select_folder"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintWidth_percent="0.45" />
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/file_selector_current_path"
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
android:background="@android:color/transparent"
|
||||
android:drawableEnd="@drawable/spinner_arrow"
|
||||
|
||||
app:drawableEndSize="@dimen/_12sdp"
|
||||
app:drawableEndSize="@dimen/padding_heavy"
|
||||
app:drawableStartIntegerScaling="true"
|
||||
app:drawableStartSize="@dimen/_36sdp"
|
||||
app:drawableEndPadding="@dimen/_1sdp"
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/_42sdp"
|
||||
android:layout_marginHorizontal="@dimen/_25sdp"
|
||||
android:layout_marginTop="@dimen/_12sdp"
|
||||
android:layout_marginTop="@dimen/padding_heavy"
|
||||
android:onClick="loginMC"
|
||||
android:text="@string/login_online_login_label"
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
<?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"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/background_app"
|
||||
android:paddingHorizontal="@dimen/fragment_padding_medium">
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title_textview"
|
||||
style="@style/TextAppearance.AppCompat.Title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="center"
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="@dimen/padding_large"
|
||||
android:text="@string/forge_dl_select_version" />
|
||||
|
||||
<View
|
||||
android:id="@+id/view"
|
||||
style="@style/ThickDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginTop="@dimen/padding_large"
|
||||
app:layout_constraintTop_toBottomOf="@+id/title_textview"
|
||||
android:paddingBottom="@dimen/fragment_padding_medium"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text=""
|
||||
android:textColor="@color/primary_text" />
|
||||
|
||||
<ExpandableListView
|
||||
android:scrollbarThumbVertical="@color/minebutton_color"
|
||||
android:id="@+id/mod_dl_expandable_version_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
</ExpandableListView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/mod_dl_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/modloader_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/mod_dl_list_progress"
|
||||
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminate="true" />
|
||||
</LinearLayout>
|
||||
@@ -27,14 +27,14 @@
|
||||
android:id="@+id/vprof_editor_profile_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/_24sdp"
|
||||
android:layout_marginTop="@dimen/padding_extra_large"
|
||||
android:background="@drawable/background_line"
|
||||
android:textSize="@dimen/_13ssp"
|
||||
|
||||
android:ems="10"
|
||||
android:hint="@string/unnamed"
|
||||
android:inputType="textPersonName"
|
||||
android:paddingHorizontal="@dimen/_12sdp"
|
||||
android:paddingHorizontal="@dimen/padding_heavy"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
@@ -51,10 +51,10 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginEnd="@dimen/_4sdp"
|
||||
android:layout_marginEnd="@dimen/padding_medium"
|
||||
android:background="@drawable/background_line"
|
||||
android:hint="Select a version"
|
||||
android:paddingHorizontal="@dimen/_12sdp"
|
||||
android:hint="@string/version_select_hint"
|
||||
android:paddingHorizontal="@dimen/padding_heavy"
|
||||
android:textSize="@dimen/_13ssp"
|
||||
|
||||
app:layout_constraintEnd_toStartOf="@+id/vprof_editor_version_button"
|
||||
@@ -86,11 +86,11 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginEnd="@dimen/_4sdp"
|
||||
android:layout_marginEnd="@dimen/padding_medium"
|
||||
|
||||
android:background="@drawable/background_line"
|
||||
android:hint="@string/use_global_default"
|
||||
android:paddingHorizontal="@dimen/_12sdp"
|
||||
android:paddingHorizontal="@dimen/padding_heavy"
|
||||
android:textSize="@dimen/_13ssp"
|
||||
|
||||
app:layout_constraintEnd_toStartOf="@id/vprof_editor_ctrl_button"
|
||||
@@ -127,7 +127,7 @@
|
||||
android:ems="10"
|
||||
android:hint="@string/use_global_default"
|
||||
android:inputType="text"
|
||||
android:paddingHorizontal="@dimen/_12sdp"
|
||||
android:paddingHorizontal="@dimen/padding_heavy"
|
||||
android:textSize="@dimen/_13ssp"
|
||||
|
||||
app:layout_constraintTop_toBottomOf="@+id/vprof_editor_ctrl_spinner" />
|
||||
@@ -147,12 +147,12 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginEnd="@dimen/_4sdp"
|
||||
android:layout_marginEnd="@dimen/padding_medium"
|
||||
android:background="@drawable/background_line"
|
||||
android:ems="10"
|
||||
|
||||
android:hint=".minecraft"
|
||||
android:paddingHorizontal="@dimen/_12sdp"
|
||||
android:paddingHorizontal="@dimen/padding_heavy"
|
||||
android:textSize="@dimen/_13ssp"
|
||||
|
||||
app:layout_constraintEnd_toStartOf="@id/vprof_editor_path_button"
|
||||
@@ -240,24 +240,25 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="26dp"
|
||||
android:text="@string/global_save"
|
||||
android:layout_marginEnd="@dimen/padding_medium"
|
||||
app:layout_constraintEnd_toStartOf="@+id/vprof_editor_delete_button"
|
||||
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/vprof_editor_profile_renderer"
|
||||
app:layout_constraintWidth_percent="0.45" />
|
||||
/>
|
||||
|
||||
<com.kdt.mcgui.MineButton
|
||||
android:id="@+id/vprof_editor_delete_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:layout_marginStart="@dimen/padding_medium"
|
||||
android:text="@string/global_delete"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/vprof_editor_save_button"
|
||||
app:layout_constraintTop_toTopOf="@+id/vprof_editor_save_button"
|
||||
app:layout_constraintWidth_percent="0.45" />
|
||||
/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</ScrollView>
|
||||
121
app_pojavlauncher/src/main/res/layout/fragment_profile_type.xml
Normal file
121
app_pojavlauncher/src/main/res/layout/fragment_profile_type.xml
Normal file
@@ -0,0 +1,121 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.kdt.DefocusableScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/background_app"
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
|
||||
android:paddingHorizontal="@dimen/fragment_padding_medium"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
||||
|
||||
<!-- Vanilla like version -->
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title_textview"
|
||||
style="@style/TextAppearance.AppCompat.Title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/create_profile_vanilla_like_versions"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/padding_large"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.497"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/guideline2" />
|
||||
|
||||
<View
|
||||
android:id="@+id/view"
|
||||
style="@style/ThickDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginTop="@dimen/padding_large"
|
||||
app:layout_constraintTop_toBottomOf="@+id/title_textview"
|
||||
/>
|
||||
|
||||
<com.kdt.mcgui.MineButton
|
||||
android:id="@+id/vanilla_profile"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/padding_large"
|
||||
android:text="@string/create_profile_vanilla"
|
||||
android:layout_marginTop="@dimen/padding_large"
|
||||
app:layout_constraintTop_toBottomOf="@+id/view" />
|
||||
|
||||
<com.kdt.mcgui.MineButton
|
||||
android:id="@+id/optifine_profile"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/padding_large"
|
||||
android:text="@string/create_profile_optifine"
|
||||
android:layout_marginTop="@dimen/padding_large"
|
||||
app:layout_constraintTop_toBottomOf="@+id/view" />
|
||||
|
||||
|
||||
|
||||
<!-- Modded versions -->
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title_modded_textview"
|
||||
style="@style/TextAppearance.AppCompat.Title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/padding_extra_extra_large"
|
||||
android:text="@string/create_profile_modded_versions"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/guideline" />
|
||||
|
||||
<View
|
||||
android:id="@+id/view_modded"
|
||||
style="@style/ThickDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginTop="@dimen/padding_large"
|
||||
app:layout_constraintTop_toBottomOf="@+id/title_modded_textview"
|
||||
/>
|
||||
|
||||
<com.kdt.mcgui.MineButton
|
||||
android:id="@+id/modded_profile_fabric"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/padding_large"
|
||||
android:layout_marginTop="@dimen/padding_large"
|
||||
android:text="@string/modloader_dl_install_fabric"
|
||||
app:layout_constraintTop_toBottomOf="@+id/view_modded"
|
||||
tools:layout_editor_absoluteX="50dp" />
|
||||
|
||||
<com.kdt.mcgui.MineButton
|
||||
android:id="@+id/modded_profile_forge"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/padding_large"
|
||||
android:layout_marginTop="@dimen/padding_large"
|
||||
|
||||
android:text="@string/modloader_dl_install_forge"
|
||||
|
||||
app:layout_constraintTop_toBottomOf="@+id/modded_profile_fabric" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="0.55" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="0.25" />
|
||||
</LinearLayout>
|
||||
</com.kdt.DefocusableScrollView>
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
android:layout_width="@dimen/_35sdp"
|
||||
android:layout_height="0dp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:paddingVertical="@dimen/_12sdp"
|
||||
android:paddingVertical="@dimen/padding_heavy"
|
||||
android:scaleType="fitCenter"
|
||||
|
||||
android:src="@drawable/ic_menu_delete_forever"
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
android:fadeScrollbars="false"
|
||||
android:scrollbarThumbVertical="@color/minebutton_color"
|
||||
android:paddingVertical="@dimen/_14sdp"
|
||||
android:scrollbarSize="@dimen/_4sdp"
|
||||
android:scrollbarSize="@dimen/padding_medium"
|
||||
android:background="@color/background_bottom_bar"
|
||||
android:divider="@null" />
|
||||
@@ -37,8 +37,8 @@
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/progress_flip_arrow"
|
||||
android:layout_width="@dimen/_24sdp"
|
||||
android:layout_height="@dimen/_24sdp"
|
||||
android:layout_width="@dimen/padding_extra_large"
|
||||
android:layout_height="@dimen/padding_extra_large"
|
||||
android:layout_marginEnd="@dimen/_8sdp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/progress_generic_progressbar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@@ -51,8 +51,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
android:paddingHorizontal="@dimen/_12sdp"
|
||||
android:paddingTop="@dimen/_12sdp"
|
||||
android:paddingHorizontal="@dimen/padding_heavy"
|
||||
android:paddingTop="@dimen/padding_heavy"
|
||||
android:weightSum="3"
|
||||
|
||||
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
|
||||
<!-- Padding -->
|
||||
<dimen name="padding_tiny">2dp</dimen>
|
||||
<dimen name="padding_tiny_plus_one">3dp</dimen>
|
||||
<dimen name="padding_small">4dp</dimen>
|
||||
<dimen name="padding_medium">8dp</dimen>
|
||||
<dimen name="padding_large">16dp</dimen>
|
||||
<dimen name="padding_extra_large">24dp</dimen>
|
||||
<dimen name="padding_extra_extra_large">32dp</dimen>
|
||||
<dimen name="padding_tiny">@dimen/_1sdp</dimen>
|
||||
<dimen name="padding_small">@dimen/_2sdp</dimen>
|
||||
<dimen name="padding_medium">@dimen/_4sdp</dimen>
|
||||
<dimen name="padding_heavy">@dimen/_12sdp</dimen>
|
||||
<dimen name="padding_large">@dimen/_16sdp</dimen>
|
||||
<dimen name="padding_extra_large">@dimen/_24sdp</dimen>
|
||||
<dimen name="padding_extra_extra_large">@dimen/_32sdp</dimen>
|
||||
|
||||
<!-- Main Activity components -->
|
||||
<dimen name="empty_icon_width">60dp</dimen>
|
||||
|
||||
@@ -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>
|
||||
@@ -331,6 +332,8 @@
|
||||
<string name="notification_game_runs">The game is running!</string>
|
||||
<string name="folder_dialog_create">Create</string>
|
||||
<string name="folder_dialog_insert_name">Insert folder name</string>
|
||||
<string name="folder_fragment_create">Create folder</string>
|
||||
<string name="folder_fragment_select">Select Folder</string>
|
||||
<string name="main_login_done">Login done</string>
|
||||
<string name="main_add_account">Add account</string>
|
||||
<string name="tasks_ongoing">Tasks are in progress, please wait</string>
|
||||
@@ -372,4 +375,24 @@
|
||||
<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="modloader_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>
|
||||
<string name="modloader_dl_install_fabric">Create Fabric profile</string>
|
||||
<string name="modloader_dl_install_forge">Create Forge profile</string>
|
||||
<string name="create_profile_vanilla">Create vanilla profile</string>
|
||||
<string name="create_profile_vanilla_like_versions">Vanilla-like versions</string>
|
||||
<string name="create_profile_modded_versions">Modded versions</string>
|
||||
<string name="fabric_dl_loader_title">Select versions</string>
|
||||
<string name="of_dl_select_version">Select OptiFine version</string>
|
||||
<string name="of_dl_failed_to_scrape">Failed to collect data for OptiFine installation</string>
|
||||
<string name="of_dl_progress">Downloading %s</string>
|
||||
<string name="create_profile_optifine">Create OptiFine profile</string>
|
||||
</resources>
|
||||
|
||||
@@ -12,4 +12,12 @@
|
||||
<item name="android:textSize">@dimen/_12ssp</item>
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
<style name="ThickDivider">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">@dimen/_1sdp</item>
|
||||
<item name="android:background">?android:attr/listDivider</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -11,5 +11,7 @@ jar {
|
||||
attributes("Manifest-Version": "1.0",
|
||||
"PreMain-Class": "git.artdeell.arcdns.ArcDNSInjectorAgent")
|
||||
}
|
||||
destinationDirectory.set(file("../app_pojavlauncher/src/main/assets/"))
|
||||
File versionFile = file("../app_pojavlauncher/src/main/assets/components/arc_dns_injector/version")
|
||||
versionFile.write(String.valueOf(new Date().getTime()))
|
||||
destinationDirectory.set(file("../app_pojavlauncher/src/main/assets/components/arc_dns_injector/"))
|
||||
}
|
||||
|
||||
42
forge_installer/.gitignore
vendored
Normal file
42
forge_installer/.gitignore
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
.gradle
|
||||
build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
25
forge_installer/build.gradle
Normal file
25
forge_installer/build.gradle
Normal file
@@ -0,0 +1,25 @@
|
||||
plugins {
|
||||
id 'java-library'
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.json:json:20230618'
|
||||
}
|
||||
|
||||
jar {
|
||||
from {
|
||||
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
|
||||
}
|
||||
File versionFile = file("../app_pojavlauncher/src/main/assets/components/forge_installer/version")
|
||||
versionFile.write(String.valueOf(new Date().getTime()))
|
||||
manifest {
|
||||
attributes("Manifest-Version": "1.0",
|
||||
"PreMain-Class": "git.artdeell.installer_agent.Agent")
|
||||
}
|
||||
destinationDirectory.set(file("../app_pojavlauncher/src/main/assets/components/forge_installer/"))
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package git.artdeell.installer_agent;
|
||||
|
||||
import java.awt.AWTEvent;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.AWTEventListener;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
|
||||
import javax.swing.AbstractButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
public class Agent implements AWTEventListener {
|
||||
private boolean forgeWindowHandled = false;
|
||||
private final boolean suppressProfileCreation;
|
||||
private final boolean optiFineInstallation;
|
||||
private final Timer componentTimer = new Timer();
|
||||
|
||||
public Agent(boolean nps, boolean of) {
|
||||
this.suppressProfileCreation = !nps;
|
||||
this.optiFineInstallation = of;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventDispatched(AWTEvent event) {
|
||||
WindowEvent windowEvent = (WindowEvent) event;
|
||||
Window window = windowEvent.getWindow();
|
||||
if(windowEvent.getID() != WindowEvent.WINDOW_OPENED) return;
|
||||
if(forgeWindowHandled && window instanceof JDialog) { // expecting a new dialog
|
||||
handleDialog(window);
|
||||
return;
|
||||
}
|
||||
if(!forgeWindowHandled) { // false at startup, so we will handle the first window as the Forge one
|
||||
forgeWindowHandled = handleMainWindow(window);
|
||||
checkComponentTimer();
|
||||
}
|
||||
}
|
||||
|
||||
public void checkComponentTimer() {
|
||||
if(forgeWindowHandled) {
|
||||
componentTimer.cancel();
|
||||
componentTimer.purge();
|
||||
return;
|
||||
}
|
||||
componentTimer.schedule(new ComponentTimeoutTask(), 30000);
|
||||
|
||||
}
|
||||
|
||||
public boolean handleMainWindow(Window window) {
|
||||
List<Component> components = new ArrayList<>();
|
||||
insertAllComponents(components, window, new MainWindowFilter());
|
||||
AbstractButton okButton = null;
|
||||
for(Component component : components) {
|
||||
if(component instanceof AbstractButton) {
|
||||
AbstractButton abstractButton = (AbstractButton) component;
|
||||
abstractButton = optiFineInstallation ?
|
||||
handleOptiFineButton(abstractButton) :
|
||||
handleForgeButton(abstractButton);
|
||||
if(abstractButton != null) okButton = abstractButton;
|
||||
}
|
||||
}
|
||||
if(okButton == null) {
|
||||
System.out.println("Failed to set all the UI components, wil try again in the next window");
|
||||
return false;
|
||||
}else{
|
||||
ProfileFixer.storeProfile(optiFineInstallation ? "OptiFine" : "forge");
|
||||
EventQueue.invokeLater(okButton::doClick); // do that after forge actually builds its window, otherwise we set the path too fast
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public AbstractButton handleForgeButton(AbstractButton abstractButton) {
|
||||
switch(abstractButton.getText()) {
|
||||
case "OK":
|
||||
return abstractButton; // return the button, so we can press it after processing other stuff
|
||||
case "Install client":
|
||||
abstractButton.doClick(); // It should be the default, but let's make sure
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public AbstractButton handleOptiFineButton(AbstractButton abstractButton) {
|
||||
if ("Install".equals(abstractButton.getText())) {
|
||||
return abstractButton;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void handleDialog(Window window) {
|
||||
List<Component> components = new ArrayList<>();
|
||||
insertAllComponents(components, window, new DialogFilter()); // ensure that it's a JOptionPane dialog
|
||||
if(components.size() == 1) {
|
||||
// another common trait of them - they only have one option pane in them,
|
||||
// so we can discard the rest of the dialog structure
|
||||
// also allows us to discard dialogs with progress bars which older installers use
|
||||
JOptionPane optionPane = (JOptionPane) components.get(0);
|
||||
if(optionPane.getMessageType() == JOptionPane.INFORMATION_MESSAGE) { // forge doesn't emit information messages for other reasons yet
|
||||
System.out.println("The install was successful!");
|
||||
ProfileFixer.reinsertProfile(optiFineInstallation ? "OptiFine" : "forge", suppressProfileCreation);
|
||||
System.exit(0); // again, forge doesn't call exit for some reason, so we do that ourselves here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void insertAllComponents(List<Component> components, Container parent, ComponentFilter filter) {
|
||||
int componentCount = parent.getComponentCount();
|
||||
for(int i = 0; i < componentCount; i++) {
|
||||
Component component = parent.getComponent(i);
|
||||
if(filter.checkComponent(component)) components.add(component);
|
||||
if(component instanceof Container) {
|
||||
insertAllComponents(components, (Container) component, filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void premain(String args, Instrumentation inst) {
|
||||
boolean noProfileSuppression = false;
|
||||
boolean optifine = false;
|
||||
if(args != null ) {
|
||||
noProfileSuppression = args.contains("NPS"); // No Profile Suppression
|
||||
optifine = args.contains("OF"); // OptiFine
|
||||
}
|
||||
Agent agent = new Agent(noProfileSuppression, optifine);
|
||||
Toolkit.getDefaultToolkit()
|
||||
.addAWTEventListener(agent,
|
||||
AWTEvent.WINDOW_EVENT_MASK);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package git.artdeell.installer_agent;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
public interface ComponentFilter {
|
||||
boolean checkComponent(Component component);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package git.artdeell.installer_agent;
|
||||
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class ComponentTimeoutTask extends TimerTask {
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println("Initialization timed out!");
|
||||
System.exit(17);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package git.artdeell.installer_agent;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class DialogFilter implements ComponentFilter{
|
||||
@Override
|
||||
public boolean checkComponent(Component component) {
|
||||
return component instanceof JOptionPane
|
||||
|| component instanceof JProgressBar;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package git.artdeell.installer_agent;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class MainWindowFilter implements ComponentFilter{
|
||||
@Override
|
||||
public boolean checkComponent(Component component) {
|
||||
return component instanceof JRadioButton
|
||||
|| component instanceof JTextField
|
||||
|| component instanceof JButton;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package git.artdeell.installer_agent;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Random;
|
||||
|
||||
public class ProfileFixer {
|
||||
private static final Random random = new Random();
|
||||
private static final Path profilesPath = Paths.get(System.getProperty("user.home"), ".minecraft", "launcher_profiles.json");
|
||||
private static JSONObject oldProfile = null;
|
||||
public static void storeProfile(String profileName) {
|
||||
try {
|
||||
JSONObject minecraftProfiles = new JSONObject(
|
||||
new String(Files.readAllBytes(profilesPath),
|
||||
StandardCharsets.UTF_8)
|
||||
);
|
||||
JSONObject profilesArray = minecraftProfiles.getJSONObject("profiles");
|
||||
oldProfile = profilesArray.optJSONObject(profileName, null);
|
||||
}catch (IOException | JSONException e) {
|
||||
System.out.println("Failed to store Forge profile: "+e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String pickProfileName(String profileName) {
|
||||
return profileName+random.nextInt();
|
||||
}
|
||||
public static void reinsertProfile(String profileName, boolean suppressProfileCreation) {
|
||||
try {
|
||||
JSONObject minecraftProfiles = new JSONObject(
|
||||
new String(Files.readAllBytes(profilesPath),
|
||||
StandardCharsets.UTF_8)
|
||||
);
|
||||
JSONObject profilesArray = minecraftProfiles.getJSONObject("profiles");
|
||||
if(oldProfile != null) {
|
||||
if(suppressProfileCreation) profilesArray.put("forge", oldProfile); // restore the old profile
|
||||
else {
|
||||
String name = pickProfileName(profileName);
|
||||
while(profilesArray.has(name)) name = pickProfileName(profileName);
|
||||
profilesArray.put(name, oldProfile); // restore the old profile under a new name
|
||||
}
|
||||
}else{
|
||||
if(suppressProfileCreation) profilesArray.remove("forge"); // remove the new profile
|
||||
// otherwise it wont be removed
|
||||
}
|
||||
minecraftProfiles.put("profiles", profilesArray);
|
||||
Files.write(profilesPath, minecraftProfiles.toString().getBytes(StandardCharsets.UTF_8),
|
||||
StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
|
||||
}catch (IOException | JSONException e) {
|
||||
System.out.println("Failed to restore old Forge profile: "+e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,3 +19,4 @@ include ':jre_lwjgl3glfw'
|
||||
include ':app_pojavlauncher'
|
||||
|
||||
include ':arc_dns_injector'
|
||||
include ':forge_installer'
|
||||
Reference in New Issue
Block a user