Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save import and export #40

Closed
wants to merge 40 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
e42c16b
zip builder
Mar 13, 2024
126169e
add an option to share log file (#38)
Ishan09811 Mar 15, 2024
941c7f3
Merge branch 'wheremyfoodat:master' into master
Ishan09811 Mar 15, 2024
8309f2f
add ui for save import and export
Ishan09811 Mar 16, 2024
6883d6a
Merge pull request #39 from Ishan09811/patch-5
Ishan09811 Mar 16, 2024
447bf21
add strings for the button
Ishan09811 Mar 16, 2024
460c71c
fixes colors
Ishan09811 Mar 16, 2024
c43716f
more ui things
Ishan09811 Mar 16, 2024
a028d51
fix strings
Ishan09811 Mar 16, 2024
4293e44
fix
Ishan09811 Mar 17, 2024
650332a
bonk
Ishan09811 Mar 17, 2024
daae85f
bonk
Ishan09811 Mar 19, 2024
591a275
Bonk
Ishan09811 Mar 19, 2024
b362cf3
done
Ishan09811 Mar 20, 2024
bddb1ea
(wip) add export save functions
Ishan09811 Mar 20, 2024
3508e81
(wip) add export save functions
Ishan09811 Mar 20, 2024
c7a37a5
fixes
Ishan09811 Mar 20, 2024
63ef1f6
fix permission issue
Ishan09811 Mar 20, 2024
4411780
fixes incorrect path
Ishan09811 Mar 20, 2024
2b3f8ac
Bonk
Ishan09811 Mar 20, 2024
97b30e4
fixes incorrect savedata path
Ishan09811 Mar 21, 2024
6d38a56
fixes
Ishan09811 Mar 21, 2024
a90532c
ZipBuilder: fixes
Ishan09811 Mar 21, 2024
fbed49f
fixes
Ishan09811 Mar 28, 2024
995c0a1
bonk
Ishan09811 Mar 28, 2024
7836b93
fixes
Ishan09811 Mar 28, 2024
0c250f2
fix zip builder
Ishan09811 Apr 6, 2024
70c710d
bonk
Ishan09811 Apr 14, 2024
6bfd472
pain
Ishan09811 Apr 14, 2024
6ad1f9c
Fixes
Ishan09811 Apr 14, 2024
78fcd1f
bonk
Ishan09811 Apr 14, 2024
aa4ebcc
hopefully work
Ishan09811 Apr 14, 2024
d6c892a
I hate ``context``
Ishan09811 Apr 14, 2024
30f0bf7
fix wrong zip path
Ishan09811 Apr 14, 2024
8b11a49
bonk
Ishan09811 Apr 14, 2024
38dd6e0
Revert bonk
Ishan09811 Apr 16, 2024
aa8abf3
fix
Ishan09811 Apr 21, 2024
7753c95
add ``ZipExtractor`` class
Ishan09811 Apr 21, 2024
2e44140
Implement SaveData import logic
Ishan09811 May 28, 2024
c5c8a06
fix syntax
Ishan09811 May 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/pandroid/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@

<meta-data android:name="android.game_mode_config"
android:resource="@xml/game_mode_config" />

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.panda3ds.pandroid.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>

<activity
android:name=".app.MainActivity"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@
import com.panda3ds.pandroid.data.game.GameMetadata;
import com.panda3ds.pandroid.utils.CompatUtils;
import com.panda3ds.pandroid.utils.FileUtils;
import com.panda3ds.pandroid.utils.ZipBuilder;
import com.panda3ds.pandroid.utils.GameUtils;
import com.panda3ds.pandroid.view.gamesgrid.GameIconView;
import com.panda3ds.pandroid.lang.Task;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import java.io.File;

public class GameAboutDialog extends BaseSheetDialog {
private final GameMetadata game;
Expand All @@ -47,6 +52,70 @@ protected void onCreate(Bundle savedInstanceState) {
dismiss();
makeShortcut();
});
findViewById(R.id.export_save).setOnClickListener(v -> {
String inputPath = FileUtils.getPrivatePath() + "/" + FileUtils.getName(game.getRealPath()).replaceAll("\\..*", "") + "/SaveData/";
String outputPath = "/storage/emulated/0/Android/media/com.panda3ds.pandroid/";
String outputName = "export.zip";

// Create an instance of ZipBuilder
ZipBuilder zipBuilder = new ZipBuilder(outputPath, outputName);


new Task(()->{
try {
// Begin the zip file creation process
zipBuilder.begin();

// Append files or folders to the zip file
zipBuilder.append(inputPath);

// End the zip file creation process
zipBuilder.end();

System.out.println("Zip file created successfully.");
} catch (Exception e) {
System.err.println("Error creating zip file: " + e.getMessage());
}
}).start();
});

findViewById(R.id.import_save).setOnClickListener(v -> {
String outputPath = FileUtils.getPrivatePath() + "/" + FileUtils.getName(game.getRealPath()).replaceAll("\\..*", "") + "/";

ZipExtractor zipExtractor = new ZipExtractor();

JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 200);
frame.setVisible(true);

JFileChooser fileChooser = new JFileChooser();

fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);

int returnValue = fileChooser.showOpenDialog(frame);

if (returnValue == JFileChooser.APPROVE_OPTION) {
File selectedFile = fileChooser.getSelectedFile();
String selectedFilePath = selectedFile.getAbsolutePath();
new Task(()->{
try {
ZipExtractor.extract(selectedFilePath, outputPath, "SaveData");

System.out.println("Zip file extracted successfully.");
} catch (Exception e) {
System.err.println("Error extracting zip file: " + e.getMessage());
}
}).start();
System.out.println("Selected file path: " + selectedFilePath);
} else {
System.out.println("No file selected");
}

// Dispose of the JFrame
frame.dispose();
});


if (game.getRomPath().startsWith("folder:")) {
findViewById(R.id.remove).setVisibility(View.GONE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable S
setItemClick("general", (item)-> PreferenceActivity.launch(requireContext(), GeneralPreferences.class));
setItemClick("advanced", (item)-> PreferenceActivity.launch(requireContext(), AdvancedPreferences.class));
}

private String getVersionName() {
try {
Context context = PandroidApplication.getAppContext();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,42 @@
import android.content.Intent;
import android.os.Bundle;
import android.os.Build;
import androidx.core.content.FileProvider;
import android.widget.Toast;
import android.content.Intent;
import android.net.Uri;

import androidx.annotation.Nullable;
import androidx.preference.SwitchPreferenceCompat;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;

import com.panda3ds.pandroid.R;
import com.panda3ds.pandroid.app.PandroidApplication;
import com.panda3ds.pandroid.app.base.BasePreferenceFragment;
import com.panda3ds.pandroid.app.services.LoggerService;
import com.panda3ds.pandroid.data.config.GlobalConfig;
import java.io.File;

public class AdvancedPreferences extends BasePreferenceFragment {
@Override
public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) {
setPreferencesFromResource(R.xml.advanced_preferences, rootKey);
setActivityTitle(R.string.advanced_options);

setItemClick("shareLog", pref -> {
shareLogFile();
});
setItemClick("performanceMonitor", pref -> GlobalConfig.set(GlobalConfig.KEY_SHOW_PERFORMANCE_OVERLAY, ((SwitchPreferenceCompat) pref).isChecked()));
setItemClick("shaderJit", pref -> GlobalConfig.set(GlobalConfig.KEY_SHADER_JIT, ((SwitchPreferenceCompat) pref).isChecked()));
setItemClick("loggerService", pref -> {
boolean checked = ((SwitchPreferenceCompat) pref).isChecked();
Context ctx = PandroidApplication.getAppContext();
if (checked) {
findPreference("shareLog").setVisible(true);
ctx.startService(new Intent(ctx, LoggerService.class));
} else {
findPreference("shareLog").setVisible(false);
ctx.stopService(new Intent(ctx, LoggerService.class));
}
GlobalConfig.set(GlobalConfig.KEY_LOGGER_SERVICE, checked);
Expand All @@ -43,8 +55,31 @@ public void onResume() {
}

private void refresh() {
if (GlobalConfig.get(GlobalConfig.KEY_LOGGER_SERVICE)) {
findPreference("shareLog").setVisible(true);
} else {
findPreference("shareLog").setVisible(false);
}
((SwitchPreferenceCompat) findPreference("performanceMonitor")).setChecked(GlobalConfig.get(GlobalConfig.KEY_SHOW_PERFORMANCE_OVERLAY));
((SwitchPreferenceCompat) findPreference("loggerService")).setChecked(GlobalConfig.get(GlobalConfig.KEY_LOGGER_SERVICE));
((SwitchPreferenceCompat) findPreference("shaderJit")).setChecked(GlobalConfig.get(GlobalConfig.KEY_SHADER_JIT));
}

private void shareLogFile() {
String filePath = "/storage/emulated/0/Android/media/com.panda3ds.pandroid/logs/current.txt";
File file = new File(filePath);

// Check if the log file exists and then share
if (file.exists()) {
Uri uri = FileProvider.getUriForFile(requireContext(), "com.panda3ds.pandroid.fileprovider", file);

Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(intent, "Share Log File"));
} else {
Toast.makeText(requireContext(), getString(R.string.no_log_file_found), Toast.LENGTH_SHORT).show();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@

import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.provider.DocumentsContract;
import android.system.Os;
import android.util.Log;

import androidx.documentfile.provider.DocumentFile;
Expand All @@ -19,7 +15,6 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

public class FileUtils {
public static final String MODE_READ = "r";
Expand All @@ -41,6 +36,10 @@ private static DocumentFile parseFile(String path) {
return singleFile;
}

public static long getLength(String path) {
return parseFile(path).length();
}

private static Context getContext() {
return PandroidApplication.getAppContext();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.panda3ds.pandroid.utils;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class ZipBuilder {
private final String outputPath, outputName;
private ZipOutputStream zip;
private OutputStream output;

public ZipBuilder(String path, String name) {
outputPath = path;
outputName = name;
}

public void begin() throws Exception {
String path = outputPath + "/" + outputName;
if (FileUtils.exists(path)) {
FileUtils.delete(path);
FileUtils.createFile(outputPath, outputName);
}
this.output = FileUtils.getOutputStream(path);
this.zip = new ZipOutputStream(output);
zip.setLevel(ZipOutputStream.DEFLATED);
}

public void append(String path) throws Exception {
append(path, "/");
}

private void append(String path, String parent) throws Exception {
String name = FileUtils.getName(path);
if (FileUtils.isDirectory(path)) {
for (String child : FileUtils.listFiles(path)) {
append(path + "/" + child, parent + "/" + name);
}
} else {
ZipEntry entry = new ZipEntry((parent + "/" + name).replaceAll("//", "/"));
entry.setTime(FileUtils.getLastModified(path));
entry.setSize(FileUtils.getLength(path));
zip.putNextEntry(entry);
InputStream input = FileUtils.getInputStream(path);
byte[] buffer = new byte[1024 * 64];
int len;
while ((len = input.read(buffer)) != -1) {
zip.write(buffer, 0, len);
}
input.close();
zip.flush();
zip.closeEntry();
}
}

public void end() throws Exception {
zip.flush();
zip.close();
output.close();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.panda3ds.pandroid.utils;

import android.net.Uri;

import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class ZipExtractor {


public static void extract(String zipFile, String outputFolder, String outputName) throws Exception {
String path = outputFolder;
if (FileUtils.exists(path+"/"+outputName)) {
throw new IllegalArgumentException("File already exists in output location");
}

byte[] buffer = new byte[1024*256];
int bufferLen = 0;

ZipInputStream in = new ZipInputStream(FileUtils.getInputStream(zipFile));
ZipEntry entry = in.getNextEntry();
while (entry != null) {
if (!entry.isDirectory()) {
String[] dir = entry.getName().split("/");
String parent;
{
StringBuilder builder = new StringBuilder();
for (int i = dir[0].length() == 0 ? 1 : 0; i < dir.length - 1; i++) {
builder.append(dir[i]).append("/");
}
parent = builder.toString();
}
if (parent.length() > 0) {
mkdirs(path, parent);
}
if (parent.length() > 0) {
parent = "/" + parent;
}
String name = dir[dir.length-1];
FileUtils.createFile(path+parent, name);
OutputStream out = FileUtils.getOutputStream(path+parent+"/"+name);
while ((bufferLen = in.read(buffer)) != -1) {
out.write(buffer, 0, bufferLen);
}
out.flush();
out.close();
}
entry = in.getNextEntry();
}
in.close();
}

private static void mkdirs(String path, String subs) {
for (String segment: subs.split("/")) {
if (!FileUtils.exists(path+"/"+segment)) {
FileUtils.createDir(path, segment);
}
path = path + "/"+segment;
}
}
}
11 changes: 11 additions & 0 deletions src/pandroid/app/src/main/res/drawable/ic_share.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?colorOnSurface">
<path
android:fillColor="#FF000000"
android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
</vector>
Loading
Loading