diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MainActivity.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MainActivity.java
index 98aa1cc07..766c835dc 100644
--- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MainActivity.java
+++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MainActivity.java
@@ -510,38 +510,20 @@ public class MainActivity extends BaseActivity implements ControlButtonMenuListe
b.show();
}
- private static void setUri(Context context, String input, Intent intent) {
- if(input.startsWith("file:")) {
- int truncLength = 5;
- if(input.startsWith("file://")) truncLength = 7;
- input = input.substring(truncLength);
- Log.i("MainActivity", input);
- boolean isDirectory = new File(input).isDirectory();
- if(isDirectory) {
- intent.setType(DocumentsContract.Document.MIME_TYPE_DIR);
- }else{
- String type = null;
- String extension = MimeTypeMap.getFileExtensionFromUrl(input);
- if(extension != null) type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
- if(type == null) type = "*/*";
- intent.setType(type);
- }
- intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- intent.setData(DocumentsContract.buildDocumentUri(
- context.getString(R.string.storageProviderAuthorities), input
- ));
- return;
- }
- intent.setDataAndType(Uri.parse(input), "*/*");
- }
-
public static void openLink(String link) {
Context ctx = touchpad.getContext(); // no more better way to obtain a context statically
((Activity)ctx).runOnUiThread(() -> {
try {
- Intent intent = new Intent(Intent.ACTION_VIEW);
- setUri(ctx, link, intent);
- ctx.startActivity(intent);
+ if(link.startsWith("file:")) {
+ int truncLength = 5;
+ if(link.startsWith("file://")) truncLength = 7;
+ String path = link.substring(truncLength);
+ Tools.openPath(ctx, new File(path), false);
+ }else {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setDataAndType(Uri.parse(link), "*/*");
+ ctx.startActivity(intent);
+ }
} catch (Throwable th) {
Tools.showError(ctx, th);
}
@@ -552,9 +534,7 @@ public class MainActivity extends BaseActivity implements ControlButtonMenuListe
Context ctx = touchpad.getContext(); // no more better way to obtain a context statically
((Activity)ctx).runOnUiThread(() -> {
try {
- Intent intent = new Intent(Intent.ACTION_VIEW);
- intent.setDataAndType(DocumentsContract.buildDocumentUri(ctx.getString(R.string.storageProviderAuthorities), path), "*/*");
- ctx.startActivity(intent);
+ Tools.openPath(ctx, new File(path), false);
} catch (Throwable th) {
Tools.showError(ctx, th);
}
diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java
index 0268bce8c..431e8c1fb 100644
--- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java
+++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java
@@ -72,6 +72,7 @@ import org.apache.commons.codec.binary.Hex;
import org.apache.commons.io.IOUtils;
import org.lwjgl.glfw.CallbackBridge;
+import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -82,6 +83,7 @@ import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
+import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.ArrayList;
@@ -1108,17 +1110,55 @@ public final class Tools {
/** Triggers the share intent chooser, with the latestlog file attached to it */
public static void shareLog(Context context){
- Uri contentUri = DocumentsContract.buildDocumentUri(context.getString(R.string.storageProviderAuthorities), Tools.DIR_GAME_HOME + "/latestlog.txt");
+ openPath(context, new File(Tools.DIR_GAME_HOME, "latestlog.txt"), true);
+ }
- Intent shareIntent = new Intent();
- shareIntent.setAction(Intent.ACTION_SEND);
- shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
- shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- shareIntent.setType("text/plain");
+ /**
+ * Determine the MIME type of a File.
+ * @param file The file to determine the type of
+ * @return the type, or the default value *slash* if cannot be determined
+ */
+ public static String getMimeType(File file) {
+ if(file.isDirectory()) return DocumentsContract.Document.MIME_TYPE_DIR;
+ String mimeType = null;
+ try (FileInputStream fileInputStream = new FileInputStream(file)){
+ // Theoretically we don't even need the buffer since we don't care about the
+ // contents of the file after the guess, but mark-supported streams
+ // are a requirement of URLConnection.guessContentTypeFromStream()
+ try(BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream)) {
+ mimeType = URLConnection.guessContentTypeFromStream(bufferedInputStream);
+ }
+ }catch (IOException e) {
+ Log.w("FileMimeType", "Failed to determine MIME type by stream", e);
+ }
+ if(mimeType != null) return mimeType;
+ mimeType = URLConnection.guessContentTypeFromName(file.getName());
+ if(mimeType != null) return mimeType;
+ return "*/*";
+ }
- Intent sendIntent = Intent.createChooser(shareIntent, "latestlog.txt");
- context.startActivity(sendIntent);
+ /**
+ * Open the path specified by a File in a file explorer or in a relevant application.
+ * @param context the current Context
+ * @param file the File to open
+ * @param share whether to open a "Share" or an "Open" dialog.
+ */
+ public static void openPath(Context context, File file, boolean share) {
+ Uri contentUri = DocumentsContract.buildDocumentUri(context.getString(R.string.storageProviderAuthorities), file.getAbsolutePath());
+ String mimeType = getMimeType(file);
+ Intent intent = new Intent();
+ if(share) {
+ intent.setAction(Intent.ACTION_SEND);
+ intent.setType(getMimeType(file));
+ intent.putExtra(Intent.EXTRA_STREAM, contentUri);
+ }else {
+ intent.setAction(Intent.ACTION_VIEW);
+ intent.setDataAndType(contentUri, mimeType);
+ }
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ Intent chooserIntent = Intent.createChooser(intent, file.getName());
+ context.startActivity(chooserIntent);
}
/** Mesure the textview height, given its current parameters */
diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/MainMenuFragment.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/MainMenuFragment.java
index 79a87bccd..c760ecfa8 100644
--- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/MainMenuFragment.java
+++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/MainMenuFragment.java
@@ -1,5 +1,6 @@
package net.kdt.pojavlaunch.fragments;
+import static net.kdt.pojavlaunch.Tools.openPath;
import static net.kdt.pojavlaunch.Tools.shareLog;
import android.content.Intent;
@@ -20,7 +21,12 @@ 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.prefs.LauncherPreferences;
import net.kdt.pojavlaunch.progresskeeper.ProgressKeeper;
+import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles;
+import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile;
+
+import java.io.File;
public class MainMenuFragment extends Fragment {
public static final String TAG = "MainMenuFragment";
@@ -38,6 +44,7 @@ public class MainMenuFragment extends Fragment {
Button mCustomControlButton = view.findViewById(R.id.custom_control_button);
Button mInstallJarButton = view.findViewById(R.id.install_jar_button);
Button mShareLogsButton = view.findViewById(R.id.share_logs_button);
+ Button mOpenDirectoryButton = view.findViewById(R.id.open_files_button);
ImageButton mEditProfileButton = view.findViewById(R.id.edit_profile_button);
Button mPlayButton = view.findViewById(R.id.play_button);
@@ -57,12 +64,24 @@ public class MainMenuFragment extends Fragment {
mShareLogsButton.setOnClickListener((v) -> shareLog(requireContext()));
+ mOpenDirectoryButton.setOnClickListener((v)-> openPath(v.getContext(), getCurrentProfileDirectory(), false));
+
+
mNewsButton.setOnLongClickListener((v)->{
Tools.swapFragment(requireActivity(), GamepadMapperFragment.class, GamepadMapperFragment.TAG, null);
return true;
});
}
+ private File getCurrentProfileDirectory() {
+ String currentProfile = LauncherPreferences.DEFAULT_PREF.getString(LauncherPreferences.PREF_KEY_CURRENT_PROFILE, null);
+ if(!Tools.isValidString(currentProfile)) return new File(Tools.DIR_GAME_NEW);
+ LauncherProfiles.load();
+ MinecraftProfile profileObject = LauncherProfiles.mainProfileJson.profiles.get(currentProfile);
+ if(profileObject == null) return new File(Tools.DIR_GAME_NEW);
+ return Tools.getGameDirPath(profileObject);
+ }
+
@Override
public void onResume() {
super.onResume();
diff --git a/app_pojavlauncher/src/main/res/layout-land/fragment_launcher.xml b/app_pojavlauncher/src/main/res/layout-land/fragment_launcher.xml
index 7881e8747..3735b4724 100644
--- a/app_pojavlauncher/src/main/res/layout-land/fragment_launcher.xml
+++ b/app_pojavlauncher/src/main/res/layout-land/fragment_launcher.xml
@@ -30,11 +30,8 @@
app:layout_constraintGuide_percent="0.5"/>
+ app:layout_constraintTop_toBottomOf="@id/custom_control_button"
+ tools:layout_editor_absoluteX="0dp" />
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/install_jar_button"
+ app:layout_constraintEnd_toStartOf="@id/files_divider"/>
+
+
+
diff --git a/app_pojavlauncher/src/main/res/layout/fragment_launcher.xml b/app_pojavlauncher/src/main/res/layout/fragment_launcher.xml
index e5c9d0301..7cb6d9142 100644
--- a/app_pojavlauncher/src/main/res/layout/fragment_launcher.xml
+++ b/app_pojavlauncher/src/main/res/layout/fragment_launcher.xml
@@ -26,10 +26,8 @@
app:layout_constraintGuide_percent="0.5"/>
+ app:layout_constraintTop_toBottomOf="@id/news_button" />
+ app:layout_constraintTop_toBottomOf="@id/custom_control_button" />
+ app:layout_constraintEnd_toStartOf="@id/files_divider"/>
+
+
+
+
diff --git a/app_pojavlauncher/src/main/res/values-land/styles.xml b/app_pojavlauncher/src/main/res/values-land/styles.xml
index 82b6ade30..afdf92305 100644
--- a/app_pojavlauncher/src/main/res/values-land/styles.xml
+++ b/app_pojavlauncher/src/main/res/values-land/styles.xml
@@ -12,4 +12,11 @@
- @dimen/_14ssp
+
+
+
diff --git a/app_pojavlauncher/src/main/res/values/strings.xml b/app_pojavlauncher/src/main/res/values/strings.xml
index 9e977c1aa..d1c590b06 100644
--- a/app_pojavlauncher/src/main/res/values/strings.xml
+++ b/app_pojavlauncher/src/main/res/values/strings.xml
@@ -415,6 +415,7 @@
Change controller key bindings
Allows you to modify the keyboard keys bound to each controller button
Discord
+ Open game directory
https://discord.gg/pojavlauncher-724163890803638273
Unsuitable username
The username must be between 3–16 characters long, and must only contain latin letters, arabic numerals and underscores.
diff --git a/app_pojavlauncher/src/main/res/values/styles.xml b/app_pojavlauncher/src/main/res/values/styles.xml
index 8171c8814..a9b2199af 100644
--- a/app_pojavlauncher/src/main/res/values/styles.xml
+++ b/app_pojavlauncher/src/main/res/values/styles.xml
@@ -13,6 +13,19 @@
+
+
+
+
+