Android: Use SAF paths for scanning
This commit is contained in:
parent
d6d8d21eff
commit
c3f914565f
|
@ -305,19 +305,15 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
|||
applySettings();
|
||||
}
|
||||
} else if (requestCode == REQUEST_IMPORT_PATCH_CODES) {
|
||||
if (data == null)
|
||||
if (data == null || data.getData() == null)
|
||||
return;
|
||||
|
||||
importPatchesFromFile(data.getData());
|
||||
} else if (requestCode == REQUEST_CHANGE_DISC_FILE) {
|
||||
if (data == null)
|
||||
if (data == null || data.getData() == null)
|
||||
return;
|
||||
|
||||
String path = GameDirectoriesActivity.getPathFromUri(this, data.getData());
|
||||
if (path == null)
|
||||
return;
|
||||
|
||||
AndroidHostInterface.getInstance().setMediaFilename(path);
|
||||
AndroidHostInterface.getInstance().setMediaFilename(data.getDataString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -687,7 +683,7 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
|||
}
|
||||
|
||||
private void importPatchesFromFile(Uri uri) {
|
||||
String str = FileUtil.readFileFromUri(this, uri, 512 * 1024);
|
||||
String str = FileHelper.readStringFromUri(this, uri, 512 * 1024);
|
||||
if (str == null || !AndroidHostInterface.getInstance().importPatchCodesFromString(str)) {
|
||||
reportErrorOnUIThread(getString(R.string.emulation_activity_failed_to_import_patch_codes));
|
||||
}
|
||||
|
|
|
@ -3,10 +3,22 @@ package com.github.stenzek.duckstation;
|
|||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.ImageDecoder;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
|
@ -46,6 +58,7 @@ public class FileHelper {
|
|||
|
||||
/**
|
||||
* File helper class - used to bridge native code to Java storage access framework APIs.
|
||||
*
|
||||
* @param context Context in which to perform file actions as.
|
||||
*/
|
||||
public FileHelper(Context context) {
|
||||
|
@ -53,10 +66,187 @@ public class FileHelper {
|
|||
this.contentResolver = context.getContentResolver();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the specified file as a string, under the specified context.
|
||||
*
|
||||
* @param context context to access file under
|
||||
* @param uri uri to write data to
|
||||
* @param maxSize maximum file size to read
|
||||
* @return String containing the file data, otherwise null
|
||||
*/
|
||||
public static String readStringFromUri(final Context context, final Uri uri, int maxSize) {
|
||||
InputStream stream = null;
|
||||
try {
|
||||
stream = context.getContentResolver().openInputStream(uri);
|
||||
} catch (FileNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuilder os = new StringBuilder();
|
||||
try {
|
||||
char[] buffer = new char[1024];
|
||||
InputStreamReader reader = new InputStreamReader(stream, Charset.forName(StandardCharsets.UTF_8.name()));
|
||||
int len;
|
||||
while ((len = reader.read(buffer)) > 0) {
|
||||
os.append(buffer, 0, len);
|
||||
if (os.length() > maxSize)
|
||||
return null;
|
||||
}
|
||||
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (os.length() == 0)
|
||||
return null;
|
||||
|
||||
return os.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the specified file as a byte array, under the specified context.
|
||||
*
|
||||
* @param context context to access file under
|
||||
* @param uri uri to write data to
|
||||
* @param maxSize maximum file size to read
|
||||
* @return byte array containing the file data, otherwise null
|
||||
*/
|
||||
public static byte[] readBytesFromUri(final Context context, final Uri uri, int maxSize) {
|
||||
InputStream stream = null;
|
||||
try {
|
||||
stream = context.getContentResolver().openInputStream(uri);
|
||||
} catch (FileNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
try {
|
||||
byte[] buffer = new byte[512 * 1024];
|
||||
int len;
|
||||
while ((len = stream.read(buffer)) > 0) {
|
||||
os.write(buffer, 0, len);
|
||||
if (maxSize > 0 && os.size() > maxSize) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
if (os.size() == 0)
|
||||
return null;
|
||||
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the specified data to a file referenced by the URI, as the specified context.
|
||||
*
|
||||
* @param context context to access file under
|
||||
* @param uri uri to write data to
|
||||
* @param bytes data to write file to
|
||||
* @return true if write was succesful, otherwise false
|
||||
*/
|
||||
public static boolean writeBytesToUri(final Context context, final Uri uri, final byte[] bytes) {
|
||||
OutputStream stream = null;
|
||||
try {
|
||||
stream = context.getContentResolver().openOutputStream(uri);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bytes != null && bytes.length > 0) {
|
||||
try {
|
||||
stream.write(bytes);
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the file referenced by the URI, under the specified context.
|
||||
*
|
||||
* @param context context to delete file under
|
||||
* @param uri uri to delete
|
||||
* @return
|
||||
*/
|
||||
public static boolean deleteFileAtUri(final Context context, final Uri uri) {
|
||||
try {
|
||||
if (uri.getScheme() == "file") {
|
||||
final File file = new File(uri.getPath());
|
||||
if (!file.isFile())
|
||||
return false;
|
||||
|
||||
return file.delete();
|
||||
}
|
||||
return (context.getContentResolver().delete(uri, null, null) > 0);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the file pointed at by a SAF URI.
|
||||
*
|
||||
* @param context context to access file under
|
||||
* @param uri uri to retrieve file name for
|
||||
* @return the name of the file, or null
|
||||
*/
|
||||
public static String getDocumentNameFromUri(final Context context, final Uri uri) {
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
final String[] proj = {DocumentsContract.Document.COLUMN_DISPLAY_NAME};
|
||||
cursor = context.getContentResolver().query(uri, proj, null, null, null);
|
||||
final int columnIndex = cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_DISPLAY_NAME);
|
||||
cursor.moveToFirst();
|
||||
return cursor.getString(columnIndex);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a bitmap from the provided SAF URI.
|
||||
*
|
||||
* @param context context to access file under
|
||||
* @param uri uri to retrieve file name for
|
||||
* @return a decoded bitmap for the file, or null
|
||||
*/
|
||||
public static Bitmap loadBitmapFromUri(final Context context, final Uri uri) {
|
||||
InputStream stream = null;
|
||||
try {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
|
||||
final ImageDecoder.Source source = ImageDecoder.createSource(context.getContentResolver(), uri);
|
||||
return ImageDecoder.decodeBitmap(source);
|
||||
} else {
|
||||
return MediaStore.Images.Media.getBitmap(context.getContentResolver(), uri);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a file descriptor for a content URI string. Called by native code.
|
||||
*
|
||||
* @param uriString string of the URI to open
|
||||
* @param mode Java open mode
|
||||
* @param mode Java open mode
|
||||
* @return file descriptor for URI, or -1
|
||||
*/
|
||||
public int openURIAsFileDescriptor(String uriString, String mode) {
|
||||
|
@ -73,10 +263,11 @@ public class FileHelper {
|
|||
|
||||
/**
|
||||
* Recursively iterates documents in the specified tree, searching for files.
|
||||
* @param treeUri Root tree in which to search for documents.
|
||||
*
|
||||
* @param treeUri Root tree in which to search for documents.
|
||||
* @param documentId Document ID representing the directory to start searching.
|
||||
* @param flags Native search flags.
|
||||
* @param results Cumulative result array.
|
||||
* @param flags Native search flags.
|
||||
* @param results Cumulative result array.
|
||||
*/
|
||||
private void doFindFiles(Uri treeUri, String documentId, int flags, ArrayList<FindResult> results) {
|
||||
try {
|
||||
|
@ -116,8 +307,9 @@ public class FileHelper {
|
|||
|
||||
/**
|
||||
* Recursively iterates documents in the specified URI, searching for files.
|
||||
*
|
||||
* @param uriString URI containing directory to search.
|
||||
* @param flags Native filter flags.
|
||||
* @param flags Native filter flags.
|
||||
* @return Array of find results.
|
||||
*/
|
||||
public FindResult[] findFiles(String uriString, int flags) {
|
||||
|
|
|
@ -1,293 +0,0 @@
|
|||
package com.github.stenzek.duckstation;
|
||||
|
||||
// https://stackoverflow.com/questions/34927748/android-5-0-documentfile-from-tree-uri
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.ImageDecoder;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.MediaStore;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public final class FileUtil {
|
||||
static String TAG = "TAG";
|
||||
private static final String PRIMARY_VOLUME_NAME = "primary";
|
||||
|
||||
@Nullable
|
||||
public static String getFullPathFromTreeUri(@Nullable final Uri treeUri, Context con) {
|
||||
if (treeUri == null) return null;
|
||||
String volumePath = getVolumePath(getVolumeIdFromTreeUri(treeUri), con);
|
||||
if (volumePath == null) return File.separator;
|
||||
if (volumePath.endsWith(File.separator))
|
||||
volumePath = volumePath.substring(0, volumePath.length() - 1);
|
||||
|
||||
String documentPath = getDocumentPathFromTreeUri(treeUri);
|
||||
if (documentPath.endsWith(File.separator))
|
||||
documentPath = documentPath.substring(0, documentPath.length() - 1);
|
||||
|
||||
if (documentPath.length() > 0) {
|
||||
if (documentPath.startsWith(File.separator))
|
||||
return volumePath + documentPath;
|
||||
else
|
||||
return volumePath + File.separator + documentPath;
|
||||
} else return volumePath;
|
||||
}
|
||||
|
||||
@SuppressLint("ObsoleteSdkInt")
|
||||
private static String getVolumePath(final String volumeId, Context context) {
|
||||
if (volumeId == null)
|
||||
return null;
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return null;
|
||||
try {
|
||||
StorageManager mStorageManager =
|
||||
(StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
|
||||
Class<?> storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
|
||||
Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList");
|
||||
Method getUuid = storageVolumeClazz.getMethod("getUuid");
|
||||
Method getPath = storageVolumeClazz.getMethod("getPath");
|
||||
Method isPrimary = storageVolumeClazz.getMethod("isPrimary");
|
||||
Object result = getVolumeList.invoke(mStorageManager);
|
||||
|
||||
final int length = Array.getLength(result);
|
||||
for (int i = 0; i < length; i++) {
|
||||
Object storageVolumeElement = Array.get(result, i);
|
||||
String uuid = (String) getUuid.invoke(storageVolumeElement);
|
||||
Boolean primary = (Boolean) isPrimary.invoke(storageVolumeElement);
|
||||
|
||||
// primary volume?
|
||||
if (primary && PRIMARY_VOLUME_NAME.equals(volumeId))
|
||||
return (String) getPath.invoke(storageVolumeElement);
|
||||
|
||||
// other volumes?
|
||||
if (uuid != null && uuid.equals(volumeId))
|
||||
return (String) getPath.invoke(storageVolumeElement);
|
||||
}
|
||||
// not found.
|
||||
return null;
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private static String getVolumeIdFromTreeUri(final Uri treeUri) {
|
||||
final String docId = DocumentsContract.getTreeDocumentId(treeUri);
|
||||
final String[] split = docId.split(":");
|
||||
if (split.length > 0) return split[0];
|
||||
else return null;
|
||||
}
|
||||
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private static String getDocumentPathFromTreeUri(final Uri treeUri) {
|
||||
final String docId = DocumentsContract.getTreeDocumentId(treeUri);
|
||||
final String[] split = docId.split(":");
|
||||
if ((split.length >= 2) && (split[1] != null)) return split[1];
|
||||
else return File.separator;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getFullPathFromUri(@Nullable final Uri treeUri, Context con) {
|
||||
if (treeUri == null) return null;
|
||||
String volumePath = getVolumePath(getVolumeIdFromUri(treeUri), con);
|
||||
if (volumePath == null) return File.separator;
|
||||
if (volumePath.endsWith(File.separator))
|
||||
volumePath = volumePath.substring(0, volumePath.length() - 1);
|
||||
|
||||
String documentPath = getDocumentPathFromUri(treeUri);
|
||||
if (documentPath.endsWith(File.separator))
|
||||
documentPath = documentPath.substring(0, documentPath.length() - 1);
|
||||
|
||||
if (documentPath.length() > 0) {
|
||||
if (documentPath.startsWith(File.separator))
|
||||
return volumePath + documentPath;
|
||||
else
|
||||
return volumePath + File.separator + documentPath;
|
||||
} else return volumePath;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private static String getVolumeIdFromUri(final Uri treeUri) {
|
||||
try {
|
||||
final String docId = DocumentsContract.getDocumentId(treeUri);
|
||||
final String[] split = docId.split(":");
|
||||
if (split.length > 0) return split[0];
|
||||
else return null;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private static String getDocumentPathFromUri(final Uri treeUri) {
|
||||
try {
|
||||
final String docId = DocumentsContract.getDocumentId(treeUri);
|
||||
final String[] split = docId.split(":");
|
||||
if ((split.length >= 2) && (split[1] != null)) return split[1];
|
||||
else return File.separator;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String readFileFromUri(final Context context, final Uri uri, int maxSize) {
|
||||
InputStream stream = null;
|
||||
try {
|
||||
stream = context.getContentResolver().openInputStream(uri);
|
||||
} catch (FileNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuilder os = new StringBuilder();
|
||||
try {
|
||||
char[] buffer = new char[1024];
|
||||
InputStreamReader reader = new InputStreamReader(stream, Charset.forName(StandardCharsets.UTF_8.name()));
|
||||
int len;
|
||||
while ((len = reader.read(buffer)) > 0) {
|
||||
os.append(buffer, 0, len);
|
||||
if (os.length() > maxSize)
|
||||
return null;
|
||||
}
|
||||
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (os.length() == 0)
|
||||
return null;
|
||||
|
||||
return os.toString();
|
||||
}
|
||||
|
||||
public static byte[] readBytesFromUri(final Context context, final Uri uri, int maxSize) {
|
||||
InputStream stream = null;
|
||||
try {
|
||||
stream = context.getContentResolver().openInputStream(uri);
|
||||
} catch (FileNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
try {
|
||||
byte[] buffer = new byte[512 * 1024];
|
||||
int len;
|
||||
while ((len = stream.read(buffer)) > 0) {
|
||||
os.write(buffer, 0, len);
|
||||
if (maxSize > 0 && os.size() > maxSize) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
if (os.size() == 0)
|
||||
return null;
|
||||
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
public static boolean writeBytesToUri(final Context context, final Uri uri, final byte[] bytes) {
|
||||
OutputStream stream = null;
|
||||
try {
|
||||
stream = context.getContentResolver().openOutputStream(uri);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bytes != null && bytes.length > 0) {
|
||||
try {
|
||||
stream.write(bytes);
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean deleteFileAtUri(final Context context, final Uri uri) {
|
||||
try {
|
||||
if (uri.getScheme() == "file") {
|
||||
final File file = new File(uri.getPath());
|
||||
if (!file.isFile())
|
||||
return false;
|
||||
|
||||
return file.delete();
|
||||
}
|
||||
return (context.getContentResolver().delete(uri, null, null) > 0);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the file pointed at by a SAF URI.
|
||||
* @param context context to access file under
|
||||
* @param uri uri to retrieve file name for
|
||||
* @return the name of the file, or null
|
||||
*/
|
||||
public static String getDocumentNameFromUri(final Context context, final Uri uri) {
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
final String[] proj = {DocumentsContract.Document.COLUMN_DISPLAY_NAME};
|
||||
cursor = context.getContentResolver().query(uri, proj, null, null, null);
|
||||
final int columnIndex = cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_DISPLAY_NAME);
|
||||
cursor.moveToFirst();
|
||||
return cursor.getString(columnIndex);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static Bitmap loadBitmapFromUri(final Context context, final Uri uri) {
|
||||
InputStream stream = null;
|
||||
try {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
|
||||
final ImageDecoder.Source source =ImageDecoder.createSource(context.getContentResolver(), uri);
|
||||
return ImageDecoder.decodeBitmap(source);
|
||||
} else {
|
||||
return MediaStore.Images.Media.getBitmap(context.getContentResolver(), uri);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -239,38 +239,6 @@ public class GameDirectoriesActivity extends AppCompatActivity {
|
|||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
public static String getPathFromTreeUri(Context context, Uri treeUri) {
|
||||
String path = FileUtil.getFullPathFromTreeUri(treeUri, context);
|
||||
if (path.length() < 5) {
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(R.string.main_activity_error)
|
||||
.setMessage(R.string.main_activity_get_path_from_directory_error)
|
||||
.setPositiveButton(R.string.main_activity_ok, (dialog, button) -> {
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
return null;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static String getPathFromUri(Context context, Uri uri) {
|
||||
String path = FileUtil.getFullPathFromUri(uri, context);
|
||||
if (path.length() < 5) {
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(R.string.main_activity_error)
|
||||
.setMessage(R.string.main_activity_get_path_from_file_error)
|
||||
.setPositiveButton(R.string.main_activity_ok, (dialog, button) -> {
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
return null;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static void addSearchDirectory(Context context, String path, boolean recursive) {
|
||||
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
final String key = recursive ? "GameList/RecursivePaths" : "GameList/Paths";
|
||||
|
@ -289,6 +257,8 @@ public class GameDirectoriesActivity extends AppCompatActivity {
|
|||
Intent i = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
i.addCategory(Intent.CATEGORY_DEFAULT);
|
||||
i.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
|
||||
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
i.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
|
||||
startActivityForResult(Intent.createChooser(i, getString(R.string.main_activity_choose_directory)),
|
||||
REQUEST_ADD_DIRECTORY_TO_GAME_LIST);
|
||||
}
|
||||
|
@ -318,14 +288,18 @@ public class GameDirectoriesActivity extends AppCompatActivity {
|
|||
|
||||
switch (requestCode) {
|
||||
case REQUEST_ADD_DIRECTORY_TO_GAME_LIST: {
|
||||
if (resultCode != RESULT_OK)
|
||||
if (resultCode != RESULT_OK || data.getData() == null)
|
||||
return;
|
||||
|
||||
String path = getPathFromTreeUri(this, data.getData());
|
||||
if (path == null)
|
||||
return;
|
||||
try {
|
||||
getContentResolver().takePersistableUriPermission(data.getData(),
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(this, "Failed to take persistable permission.", Toast.LENGTH_LONG);
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
addSearchDirectory(this, path, true);
|
||||
addSearchDirectory(this, data.getDataString(), true);
|
||||
mDirectoryListAdapter.reload();
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -9,7 +9,6 @@ import android.content.pm.PackageManager;
|
|||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
@ -18,12 +17,10 @@ import android.view.Gravity;
|
|||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
|
@ -195,6 +192,8 @@ public class MainActivity extends AppCompatActivity {
|
|||
Intent i = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
i.addCategory(Intent.CATEGORY_DEFAULT);
|
||||
i.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
|
||||
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
i.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
|
||||
startActivityForResult(Intent.createChooser(i, getString(R.string.main_activity_choose_directory)),
|
||||
REQUEST_ADD_DIRECTORY_TO_GAME_LIST);
|
||||
}
|
||||
|
@ -270,14 +269,18 @@ public class MainActivity extends AppCompatActivity {
|
|||
|
||||
switch (requestCode) {
|
||||
case REQUEST_ADD_DIRECTORY_TO_GAME_LIST: {
|
||||
if (resultCode != RESULT_OK)
|
||||
if (resultCode != RESULT_OK || data.getData() == null)
|
||||
return;
|
||||
|
||||
String path = GameDirectoriesActivity.getPathFromTreeUri(this, data.getData());
|
||||
if (path == null)
|
||||
return;
|
||||
try {
|
||||
getContentResolver().takePersistableUriPermission(data.getData(),
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(this, "Failed to take persistable permission.", Toast.LENGTH_LONG);
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
GameDirectoriesActivity.addSearchDirectory(this, path, true);
|
||||
GameDirectoriesActivity.addSearchDirectory(this, data.getDataString(), true);
|
||||
mGameList.refresh(false, false, this);
|
||||
}
|
||||
break;
|
||||
|
@ -291,14 +294,10 @@ public class MainActivity extends AppCompatActivity {
|
|||
break;
|
||||
|
||||
case REQUEST_START_FILE: {
|
||||
if (resultCode != RESULT_OK)
|
||||
if (resultCode != RESULT_OK || data.getData() == null)
|
||||
return;
|
||||
|
||||
String path = GameDirectoriesActivity.getPathFromUri(this, data.getData());
|
||||
if (path == null)
|
||||
return;
|
||||
|
||||
startEmulation(path, shouldResumeStateByDefault());
|
||||
startEmulation(data.getDataString(), shouldResumeStateByDefault());
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -428,7 +427,7 @@ public class MainActivity extends AppCompatActivity {
|
|||
if (gameListEntry == null)
|
||||
return;
|
||||
|
||||
final Bitmap bitmap = FileUtil.loadBitmapFromUri(this, uri);
|
||||
final Bitmap bitmap = FileHelper.loadBitmapFromUri(this, uri);
|
||||
if (bitmap == null) {
|
||||
Toast.makeText(this, "Failed to open/decode image.", Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
|
|
|
@ -286,13 +286,13 @@ public class MemoryCardEditorActivity extends AppCompatActivity {
|
|||
if (card == null)
|
||||
return;
|
||||
|
||||
final byte[] data = FileUtil.readBytesFromUri(this, uri, 16 * 1024 * 1024);
|
||||
final byte[] data = FileHelper.readBytesFromUri(this, uri, 16 * 1024 * 1024);
|
||||
if (data == null) {
|
||||
displayError(getString(R.string.memory_card_editor_import_card_read_failed, uri.toString()));
|
||||
return;
|
||||
}
|
||||
|
||||
String importFileName = FileUtil.getDocumentNameFromUri(this, uri);
|
||||
String importFileName = FileHelper.getDocumentNameFromUri(this, uri);
|
||||
if (importFileName == null) {
|
||||
importFileName = uri.getPath();
|
||||
if (importFileName == null || importFileName.isEmpty())
|
||||
|
|
|
@ -41,7 +41,7 @@ public class MemoryCardImage {
|
|||
}
|
||||
|
||||
public static MemoryCardImage open(Context context, Uri uri) {
|
||||
byte[] data = FileUtil.readBytesFromUri(context, uri, DATA_SIZE);
|
||||
byte[] data = FileHelper.readBytesFromUri(context, uri, DATA_SIZE);
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
|
@ -145,11 +145,11 @@ public class MemoryCardImage {
|
|||
private static native boolean importCard(byte[] data, String filename, byte[] importData);
|
||||
|
||||
public boolean save() {
|
||||
return FileUtil.writeBytesToUri(context, uri, data);
|
||||
return FileHelper.writeBytesToUri(context, uri, data);
|
||||
}
|
||||
|
||||
public boolean delete() {
|
||||
return FileUtil.deleteFileAtUri(context, uri);
|
||||
return FileHelper.deleteFileAtUri(context, uri);
|
||||
}
|
||||
|
||||
public boolean format() {
|
||||
|
|
Loading…
Reference in New Issue