From 40d6d615e2c535f45ba50177e8cd36c390f2a086 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 28 Aug 2022 23:44:01 +0200 Subject: [PATCH] Android: Fix reading custom covers with SAF If GameFile.getCustomCoverPath returns a mangled URI, we need to unmangle it before passing it to Picasso, since Picasso has no concept of Dolphin's mangled URIs. --- .../dolphinemu/model/GameFileCache.java | 2 +- .../dolphinemu/utils/ContentHandler.java | 7 +++- .../dolphinemu/utils/FileBrowserHelper.java | 9 ++++- .../dolphinemu/utils/PicassoUtils.java | 28 +++++++++++++-- .../dolphinemu/dolphinemu/utils/TvUtil.java | 36 ++++++++++++++----- 5 files changed, 67 insertions(+), 15 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GameFileCache.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GameFileCache.java index ed0f65741a..8ea8d82757 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GameFileCache.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GameFileCache.java @@ -61,7 +61,7 @@ public class GameFileCache String path = dolphinIni.getString(Settings.SECTION_INI_GENERAL, SettingsFile.KEY_ISO_PATH_BASE + i, ""); - if (path.startsWith("content://") ? ContentHandler.exists(path) : new File(path).exists()) + if (ContentHandler.isContentUri(path) ? ContentHandler.exists(path) : new File(path).exists()) { pathSet.add(path); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ContentHandler.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ContentHandler.java index c90c266963..9143375f76 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ContentHandler.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ContentHandler.java @@ -37,6 +37,11 @@ import java.util.function.Predicate; public class ContentHandler { + public static boolean isContentUri(@NonNull String pathOrUri) + { + return pathOrUri.startsWith("content://"); + } + @Keep public static int openFd(@NonNull String uri, @NonNull String mode) { @@ -336,7 +341,7 @@ public class ContentHandler * provider to use URIs without any % characters. */ @NonNull - private static Uri unmangle(@NonNull String uri) throws FileNotFoundException, SecurityException + public static Uri unmangle(@NonNull String uri) throws FileNotFoundException, SecurityException { int lastComponentEnd = getLastComponentEnd(uri); int lastComponentStart = getLastComponentStart(uri, lastComponentEnd); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/FileBrowserHelper.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/FileBrowserHelper.java index 6924fd65ea..937c13259b 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/FileBrowserHelper.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/FileBrowserHelper.java @@ -82,9 +82,16 @@ public final class FileBrowserHelper return isPathEmptyOrValid(path.getStringGlobal()); } + /** + * Returns true if at least one of the following applies: + * + * 1. The input is empty. + * 2. The input is something which is not a content URI. + * 3. The input is a content URI that points to a file that exists and we're allowed to access. + */ public static boolean isPathEmptyOrValid(String path) { - return !path.startsWith("content://") || ContentHandler.exists(path); + return !ContentHandler.isContentUri(path) || ContentHandler.exists(path); } public static void runAfterExtensionCheck(Context context, Uri uri, Set validExtensions, diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/PicassoUtils.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/PicassoUtils.java index 69aabffa7f..bde8b1eb69 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/PicassoUtils.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/PicassoUtils.java @@ -18,6 +18,7 @@ import org.dolphinemu.dolphinemu.model.GameFile; import org.dolphinemu.dolphinemu.viewholders.GameViewHolder; import java.io.File; +import java.io.FileNotFoundException; public class PicassoUtils { @@ -54,12 +55,33 @@ public class PicassoUtils gameViewHolder.textGameCaption.setVisibility(View.GONE); } + String customCoverPath = gameFile.getCustomCoverPath(); + Uri customCoverUri = null; + boolean customCoverExists = false; + if (ContentHandler.isContentUri(customCoverPath)) + { + try + { + customCoverUri = ContentHandler.unmangle(customCoverPath); + customCoverExists = true; + } + catch (FileNotFoundException | SecurityException ignored) + { + // Let customCoverExists remain false + } + } + else + { + customCoverUri = Uri.parse(customCoverPath); + customCoverExists = new File(customCoverPath).exists(); + } + Context context = imageView.getContext(); - File cover = new File(gameFile.getCustomCoverPath()); - if (cover.exists()) + File cover; + if (customCoverExists) { Picasso.get() - .load(cover) + .load(customCoverUri) .noFade() .noPlaceholder() .fit() diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/TvUtil.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/TvUtil.java index 164463cb50..b091ed6423 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/TvUtil.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/TvUtil.java @@ -34,6 +34,7 @@ import org.dolphinemu.dolphinemu.services.SyncProgramsJobService; import org.dolphinemu.dolphinemu.ui.platform.Platform; import java.io.File; +import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; @@ -155,26 +156,43 @@ public class TvUtil } /** - * Leanback lanucher requires a uri for poster art so we create a contentUri and + * Leanback launcher requires a uri for poster art so we create a contentUri and * pass that to LEANBACK_PACKAGE */ public static Uri buildBanner(GameFile game, Context context) { Uri contentUri = null; + File cover; try { - File cover = new File(game.getCustomCoverPath()); - if (cover.exists()) + String customCoverPath = game.getCustomCoverPath(); + + if (ContentHandler.isContentUri(customCoverPath)) + { + try + { + contentUri = ContentHandler.unmangle(customCoverPath); + } + catch (FileNotFoundException | SecurityException ignored) + { + // Let contentUri remain null + } + } + else + { + if ((cover = new File(customCoverPath)).exists()) + { + contentUri = getUriForFile(context, getFileProvider(context), cover); + } + } + + if (contentUri == null && (cover = new File(game.getCoverPath(context))).exists()) { contentUri = getUriForFile(context, getFileProvider(context), cover); } - else if ((cover = new File(game.getCoverPath(context))).exists()) - { - contentUri = getUriForFile(context, getFileProvider(context), cover); - } - context.grantUriPermission(LEANBACK_PACKAGE, contentUri, - FLAG_GRANT_READ_URI_PERMISSION); + + context.grantUriPermission(LEANBACK_PACKAGE, contentUri, FLAG_GRANT_READ_URI_PERMISSION); } catch (Exception e) {