From 6eb50133598941b166ace27346139929e6315aea Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Fri, 4 Nov 2022 13:02:21 -0400 Subject: [PATCH] Android: Offload cover path unmangling to another thread --- .../dolphinemu/adapters/GameAdapter.java | 7 +- .../dolphinemu/adapters/GameRowPresenter.java | 2 +- .../ui/platform/PlatformGamesFragment.java | 2 +- .../dolphinemu/utils/GlideUtils.java | 261 ++++++++++-------- 4 files changed, 151 insertions(+), 121 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java index 21470269a9..01a8b9dddb 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java @@ -2,6 +2,7 @@ package org.dolphinemu.dolphinemu.adapters; +import android.app.Activity; import android.content.Context; import android.view.LayoutInflater; import android.view.View; @@ -27,14 +28,16 @@ public final class GameAdapter extends RecyclerView.Adapter mGameFiles; + private Activity mActivity; /** * Initializes the adapter's observer, which watches for changes to the dataset. The adapter will * display no data until swapDataSet is called. */ - public GameAdapter() + public GameAdapter(Activity activity) { mGameFiles = new ArrayList<>(); + mActivity = activity; } /** @@ -70,7 +73,7 @@ public final class GameAdapter extends RecyclerView.Adapter { - try + String customCoverPath = gameFile.getCustomCoverPath(); + Uri customCoverUri = null; + boolean customCoverExists = false; + if (ContentHandler.isContentUri(customCoverPath)) { - customCoverUri = ContentHandler.unmangle(customCoverPath); - customCoverExists = true; + try + { + customCoverUri = ContentHandler.unmangle(customCoverPath); + customCoverExists = true; + } + catch (FileNotFoundException | SecurityException ignored) + { + // Let customCoverExists remain false + } } - catch (FileNotFoundException | SecurityException ignored) + else { - // Let customCoverExists remain false + customCoverUri = Uri.parse(customCoverPath); + customCoverExists = new File(customCoverPath).exists(); } - } - else - { - customCoverUri = Uri.parse(customCoverPath); - customCoverExists = new File(customCoverPath).exists(); - } - Context context = imageView.getContext(); - File cover; - if (customCoverExists) - { - Glide.with(context) - .load(customCoverUri) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .centerCrop() - .listener(new RequestListener() - { - @Override public boolean onLoadFailed(@Nullable GlideException e, Object model, - Target target, boolean isFirstResource) - { - GlideUtils.enableInnerTitle(gameViewHolder, imageView); - return false; - } + Context context = imageView.getContext(); + boolean finalCustomCoverExists = customCoverExists; + Uri finalCustomCoverUri = customCoverUri; - @Override public boolean onResourceReady(Drawable resource, Object model, - Target target, DataSource dataSource, boolean isFirstResource) - { - GlideUtils.disableInnerTitle(gameViewHolder); - return false; - } - }) - .into(imageView); - } - else if ((cover = new File(gameFile.getCoverPath(context))).exists()) - { - Glide.with(context) - .load(cover) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .centerCrop() - .listener(new RequestListener() - { - @Override - public boolean onLoadFailed(@Nullable GlideException e, Object model, - Target target, boolean isFirstResource) - { - GlideUtils.enableInnerTitle(gameViewHolder, imageView); - return false; - } + File cover = new File(gameFile.getCoverPath(context)); + boolean cachedCoverExists = cover.exists(); + unmangleHandler.post(() -> + { + // We can't get a reference to the current activity in the TV version. + // Luckily it won't attempt to start loads on destroyed activities. + if (activity != null) + { + // We can't start an image load on a destroyed activity + if (activity.isDestroyed()) + { + return; + } + } - @Override - public boolean onResourceReady(Drawable resource, Object model, - Target target, DataSource dataSource, boolean isFirstResource) - { - GlideUtils.disableInnerTitle(gameViewHolder); - return false; - } - }) - .into(imageView); - } - else if (BooleanSetting.MAIN_USE_GAME_COVERS.getBooleanGlobal()) - { - Glide.with(context) - .load(CoverHelper.buildGameTDBUrl(gameFile, CoverHelper.getRegion(gameFile))) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .centerCrop() - .listener(new RequestListener() - { - @Override - public boolean onLoadFailed(@Nullable GlideException e, Object model, - Target target, boolean isFirstResource) - { - GlideUtils.enableInnerTitle(gameViewHolder, imageView); - return false; - } + if (finalCustomCoverExists) + { + Glide.with(imageView) + .load(finalCustomCoverUri) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .centerCrop() + .error(R.drawable.no_banner) + .listener(new RequestListener() + { + @Override public boolean onLoadFailed(@Nullable GlideException e, Object model, + Target target, boolean isFirstResource) + { + GlideUtils.enableInnerTitle(gameViewHolder); + return false; + } - @Override - public boolean onResourceReady(Drawable resource, Object model, - Target target, DataSource dataSource, boolean isFirstResource) - { - GlideUtils.disableInnerTitle(gameViewHolder); - return false; - } - }) - .into(new CustomTarget() - { - @Override - public void onResourceReady(@NonNull Drawable resource, - @Nullable Transition transition) - { - Bitmap cover = ((BitmapDrawable) resource).getBitmap(); - executor.execute( - () -> CoverHelper.saveCover(cover, gameFile.getCoverPath(context))); - imageView.setImageBitmap(cover); - } + @Override public boolean onResourceReady(Drawable resource, Object model, + Target target, DataSource dataSource, boolean isFirstResource) + { + GlideUtils.disableInnerTitle(gameViewHolder); + return false; + } + }) + .into(imageView); + } + else if (cachedCoverExists) + { + Glide.with(imageView) + .load(cover) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .centerCrop() + .error(R.drawable.no_banner) + .listener(new RequestListener() + { + @Override + public boolean onLoadFailed(@Nullable GlideException e, Object model, + Target target, boolean isFirstResource) + { + GlideUtils.enableInnerTitle(gameViewHolder); + return false; + } - @Override - public void onLoadCleared(@Nullable Drawable placeholder) - { - } - }); - } - else - { - enableInnerTitle(gameViewHolder, imageView); - } + @Override + public boolean onResourceReady(Drawable resource, Object model, + Target target, DataSource dataSource, boolean isFirstResource) + { + GlideUtils.disableInnerTitle(gameViewHolder); + return false; + } + }) + .into(imageView); + } + else if (BooleanSetting.MAIN_USE_GAME_COVERS.getBooleanGlobal()) + { + Glide.with(context) + .load(CoverHelper.buildGameTDBUrl(gameFile, CoverHelper.getRegion(gameFile))) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .centerCrop() + .error(R.drawable.no_banner) + .listener(new RequestListener() + { + @Override + public boolean onLoadFailed(@Nullable GlideException e, Object model, + Target target, boolean isFirstResource) + { + GlideUtils.enableInnerTitle(gameViewHolder); + return false; + } + + @Override + public boolean onResourceReady(Drawable resource, Object model, + Target target, DataSource dataSource, boolean isFirstResource) + { + GlideUtils.disableInnerTitle(gameViewHolder); + return false; + } + }) + .into(new CustomTarget() + { + @Override + public void onResourceReady(@NonNull Drawable resource, + @Nullable Transition transition) + { + Bitmap cover = ((BitmapDrawable) resource).getBitmap(); + saveCoverExecutor.execute( + () -> CoverHelper.saveCover(cover, gameFile.getCoverPath(context))); + imageView.setImageBitmap(cover); + } + + @Override + public void onLoadCleared(@Nullable Drawable placeholder) + { + } + }); + } + else + { + Glide.with(imageView.getContext()) + .load(R.drawable.no_banner) + .into(imageView); + enableInnerTitle(gameViewHolder); + } + }); + }); } - private static void enableInnerTitle(GameAdapter.GameViewHolder gameViewHolder, - ImageView imageView) + private static void enableInnerTitle(GameAdapter.GameViewHolder gameViewHolder) { - Glide.with(imageView.getContext()) - .load(R.drawable.no_banner) - .into(imageView); - if (gameViewHolder != null && !BooleanSetting.MAIN_SHOW_GAME_TITLES.getBooleanGlobal()) { gameViewHolder.binding.textGameTitleInner.setVisibility(View.VISIBLE);