Merge pull request #11248 from t895/offload-unmangle
Android: Offload cover path unmangling to another thread
This commit is contained in:
commit
2340a7eea6
Source/Android/app/src/main/java/org/dolphinemu/dolphinemu
adapters
ui/platform
utils
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
package org.dolphinemu.dolphinemu.adapters;
|
package org.dolphinemu.dolphinemu.adapters;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -27,14 +28,16 @@ public final class GameAdapter extends RecyclerView.Adapter<GameAdapter.GameView
|
||||||
View.OnLongClickListener
|
View.OnLongClickListener
|
||||||
{
|
{
|
||||||
private List<GameFile> mGameFiles;
|
private List<GameFile> mGameFiles;
|
||||||
|
private Activity mActivity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the adapter's observer, which watches for changes to the dataset. The adapter will
|
* Initializes the adapter's observer, which watches for changes to the dataset. The adapter will
|
||||||
* display no data until swapDataSet is called.
|
* display no data until swapDataSet is called.
|
||||||
*/
|
*/
|
||||||
public GameAdapter()
|
public GameAdapter(Activity activity)
|
||||||
{
|
{
|
||||||
mGameFiles = new ArrayList<>();
|
mGameFiles = new ArrayList<>();
|
||||||
|
mActivity = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,7 +73,7 @@ public final class GameAdapter extends RecyclerView.Adapter<GameAdapter.GameView
|
||||||
{
|
{
|
||||||
Context context = holder.itemView.getContext();
|
Context context = holder.itemView.getContext();
|
||||||
GameFile gameFile = mGameFiles.get(position);
|
GameFile gameFile = mGameFiles.get(position);
|
||||||
GlideUtils.loadGameCover(holder, holder.binding.imageGameScreen, gameFile);
|
GlideUtils.loadGameCover(holder, holder.binding.imageGameScreen, gameFile, mActivity);
|
||||||
|
|
||||||
if (GameFileCacheManager.findSecondDisc(gameFile) != null)
|
if (GameFileCacheManager.findSecondDisc(gameFile) != null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -50,7 +50,7 @@ public final class GameRowPresenter extends Presenter
|
||||||
GameFile gameFile = (GameFile) item;
|
GameFile gameFile = (GameFile) item;
|
||||||
|
|
||||||
holder.imageScreenshot.setImageDrawable(null);
|
holder.imageScreenshot.setImageDrawable(null);
|
||||||
GlideUtils.loadGameCover(null, holder.imageScreenshot, gameFile);
|
GlideUtils.loadGameCover(null, holder.imageScreenshot, gameFile, null);
|
||||||
|
|
||||||
holder.cardParent.setTitleText(gameFile.getTitle());
|
holder.cardParent.setTitleText(gameFile.getTitle());
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam
|
||||||
|
|
||||||
int columns = getResources().getInteger(R.integer.game_grid_columns);
|
int columns = getResources().getInteger(R.integer.game_grid_columns);
|
||||||
RecyclerView.LayoutManager layoutManager = new GridLayoutManager(getActivity(), columns);
|
RecyclerView.LayoutManager layoutManager = new GridLayoutManager(getActivity(), columns);
|
||||||
mAdapter = new GameAdapter();
|
mAdapter = new GameAdapter(requireActivity());
|
||||||
|
|
||||||
// Set theme color to the refresh animation's background
|
// Set theme color to the refresh animation's background
|
||||||
mSwipeRefresh.setProgressBackgroundColorSchemeColor(
|
mSwipeRefresh.setProgressBackgroundColorSchemeColor(
|
||||||
|
|
|
@ -2,11 +2,14 @@
|
||||||
|
|
||||||
package org.dolphinemu.dolphinemu.utils;
|
package org.dolphinemu.dolphinemu.utils;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
@ -34,7 +37,9 @@ import java.util.concurrent.Executors;
|
||||||
|
|
||||||
public class GlideUtils
|
public class GlideUtils
|
||||||
{
|
{
|
||||||
private static final ExecutorService executor = Executors.newSingleThreadExecutor();
|
private static final ExecutorService saveCoverExecutor = Executors.newSingleThreadExecutor();
|
||||||
|
private static final ExecutorService unmangleExecutor = Executors.newSingleThreadExecutor();
|
||||||
|
private static final Handler unmangleHandler = new Handler(Looper.getMainLooper());
|
||||||
|
|
||||||
public static void loadGameBanner(ImageView imageView, GameFile gameFile)
|
public static void loadGameBanner(ImageView imageView, GameFile gameFile)
|
||||||
{
|
{
|
||||||
|
@ -61,7 +66,7 @@ public class GlideUtils
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void loadGameCover(GameAdapter.GameViewHolder gameViewHolder, ImageView imageView,
|
public static void loadGameCover(GameAdapter.GameViewHolder gameViewHolder, ImageView imageView,
|
||||||
GameFile gameFile)
|
GameFile gameFile, Activity activity)
|
||||||
{
|
{
|
||||||
if (BooleanSetting.MAIN_SHOW_GAME_TITLES.getBooleanGlobal() && gameViewHolder != null)
|
if (BooleanSetting.MAIN_SHOW_GAME_TITLES.getBooleanGlobal() && gameViewHolder != null)
|
||||||
{
|
{
|
||||||
|
@ -77,134 +82,156 @@ public class GlideUtils
|
||||||
gameViewHolder.binding.textGameCaption.setVisibility(View.GONE);
|
gameViewHolder.binding.textGameCaption.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
String customCoverPath = gameFile.getCustomCoverPath();
|
unmangleExecutor.execute(() ->
|
||||||
Uri customCoverUri = null;
|
|
||||||
boolean customCoverExists = false;
|
|
||||||
if (ContentHandler.isContentUri(customCoverPath))
|
|
||||||
{
|
{
|
||||||
try
|
String customCoverPath = gameFile.getCustomCoverPath();
|
||||||
|
Uri customCoverUri = null;
|
||||||
|
boolean customCoverExists = false;
|
||||||
|
if (ContentHandler.isContentUri(customCoverPath))
|
||||||
{
|
{
|
||||||
customCoverUri = ContentHandler.unmangle(customCoverPath);
|
try
|
||||||
customCoverExists = true;
|
{
|
||||||
|
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();
|
Context context = imageView.getContext();
|
||||||
File cover;
|
boolean finalCustomCoverExists = customCoverExists;
|
||||||
if (customCoverExists)
|
Uri finalCustomCoverUri = customCoverUri;
|
||||||
{
|
|
||||||
Glide.with(context)
|
|
||||||
.load(customCoverUri)
|
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
|
||||||
.centerCrop()
|
|
||||||
.listener(new RequestListener<Drawable>()
|
|
||||||
{
|
|
||||||
@Override public boolean onLoadFailed(@Nullable GlideException e, Object model,
|
|
||||||
Target<Drawable> target, boolean isFirstResource)
|
|
||||||
{
|
|
||||||
GlideUtils.enableInnerTitle(gameViewHolder, imageView);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public boolean onResourceReady(Drawable resource, Object model,
|
File cover = new File(gameFile.getCoverPath(context));
|
||||||
Target<Drawable> target, DataSource dataSource, boolean isFirstResource)
|
boolean cachedCoverExists = cover.exists();
|
||||||
{
|
unmangleHandler.post(() ->
|
||||||
GlideUtils.disableInnerTitle(gameViewHolder);
|
{
|
||||||
return false;
|
// 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)
|
||||||
.into(imageView);
|
{
|
||||||
}
|
// We can't start an image load on a destroyed activity
|
||||||
else if ((cover = new File(gameFile.getCoverPath(context))).exists())
|
if (activity.isDestroyed())
|
||||||
{
|
{
|
||||||
Glide.with(context)
|
return;
|
||||||
.load(cover)
|
}
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
}
|
||||||
.centerCrop()
|
|
||||||
.listener(new RequestListener<Drawable>()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public boolean onLoadFailed(@Nullable GlideException e, Object model,
|
|
||||||
Target<Drawable> target, boolean isFirstResource)
|
|
||||||
{
|
|
||||||
GlideUtils.enableInnerTitle(gameViewHolder, imageView);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
if (finalCustomCoverExists)
|
||||||
public boolean onResourceReady(Drawable resource, Object model,
|
{
|
||||||
Target<Drawable> target, DataSource dataSource, boolean isFirstResource)
|
Glide.with(imageView)
|
||||||
{
|
.load(finalCustomCoverUri)
|
||||||
GlideUtils.disableInnerTitle(gameViewHolder);
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
return false;
|
.centerCrop()
|
||||||
}
|
.error(R.drawable.no_banner)
|
||||||
})
|
.listener(new RequestListener<Drawable>()
|
||||||
.into(imageView);
|
{
|
||||||
}
|
@Override public boolean onLoadFailed(@Nullable GlideException e, Object model,
|
||||||
else if (BooleanSetting.MAIN_USE_GAME_COVERS.getBooleanGlobal())
|
Target<Drawable> target, boolean isFirstResource)
|
||||||
{
|
{
|
||||||
Glide.with(context)
|
GlideUtils.enableInnerTitle(gameViewHolder);
|
||||||
.load(CoverHelper.buildGameTDBUrl(gameFile, CoverHelper.getRegion(gameFile)))
|
return false;
|
||||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
}
|
||||||
.centerCrop()
|
|
||||||
.listener(new RequestListener<Drawable>()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public boolean onLoadFailed(@Nullable GlideException e, Object model,
|
|
||||||
Target<Drawable> target, boolean isFirstResource)
|
|
||||||
{
|
|
||||||
GlideUtils.enableInnerTitle(gameViewHolder, imageView);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override public boolean onResourceReady(Drawable resource, Object model,
|
||||||
public boolean onResourceReady(Drawable resource, Object model,
|
Target<Drawable> target, DataSource dataSource, boolean isFirstResource)
|
||||||
Target<Drawable> target, DataSource dataSource, boolean isFirstResource)
|
{
|
||||||
{
|
GlideUtils.disableInnerTitle(gameViewHolder);
|
||||||
GlideUtils.disableInnerTitle(gameViewHolder);
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
})
|
||||||
})
|
.into(imageView);
|
||||||
.into(new CustomTarget<Drawable>()
|
}
|
||||||
{
|
else if (cachedCoverExists)
|
||||||
@Override
|
{
|
||||||
public void onResourceReady(@NonNull Drawable resource,
|
Glide.with(imageView)
|
||||||
@Nullable Transition<? super Drawable> transition)
|
.load(cover)
|
||||||
{
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
Bitmap cover = ((BitmapDrawable) resource).getBitmap();
|
.centerCrop()
|
||||||
executor.execute(
|
.error(R.drawable.no_banner)
|
||||||
() -> CoverHelper.saveCover(cover, gameFile.getCoverPath(context)));
|
.listener(new RequestListener<Drawable>()
|
||||||
imageView.setImageBitmap(cover);
|
{
|
||||||
}
|
@Override
|
||||||
|
public boolean onLoadFailed(@Nullable GlideException e, Object model,
|
||||||
|
Target<Drawable> target, boolean isFirstResource)
|
||||||
|
{
|
||||||
|
GlideUtils.enableInnerTitle(gameViewHolder);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoadCleared(@Nullable Drawable placeholder)
|
public boolean onResourceReady(Drawable resource, Object model,
|
||||||
{
|
Target<Drawable> target, DataSource dataSource, boolean isFirstResource)
|
||||||
}
|
{
|
||||||
});
|
GlideUtils.disableInnerTitle(gameViewHolder);
|
||||||
}
|
return false;
|
||||||
else
|
}
|
||||||
{
|
})
|
||||||
enableInnerTitle(gameViewHolder, imageView);
|
.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<Drawable>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public boolean onLoadFailed(@Nullable GlideException e, Object model,
|
||||||
|
Target<Drawable> target, boolean isFirstResource)
|
||||||
|
{
|
||||||
|
GlideUtils.enableInnerTitle(gameViewHolder);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onResourceReady(Drawable resource, Object model,
|
||||||
|
Target<Drawable> target, DataSource dataSource, boolean isFirstResource)
|
||||||
|
{
|
||||||
|
GlideUtils.disableInnerTitle(gameViewHolder);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.into(new CustomTarget<Drawable>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onResourceReady(@NonNull Drawable resource,
|
||||||
|
@Nullable Transition<? super Drawable> 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,
|
private static void enableInnerTitle(GameAdapter.GameViewHolder gameViewHolder)
|
||||||
ImageView imageView)
|
|
||||||
{
|
{
|
||||||
Glide.with(imageView.getContext())
|
|
||||||
.load(R.drawable.no_banner)
|
|
||||||
.into(imageView);
|
|
||||||
|
|
||||||
if (gameViewHolder != null && !BooleanSetting.MAIN_SHOW_GAME_TITLES.getBooleanGlobal())
|
if (gameViewHolder != null && !BooleanSetting.MAIN_SHOW_GAME_TITLES.getBooleanGlobal())
|
||||||
{
|
{
|
||||||
gameViewHolder.binding.textGameTitleInner.setVisibility(View.VISIBLE);
|
gameViewHolder.binding.textGameTitleInner.setVisibility(View.VISIBLE);
|
||||||
|
|
Loading…
Reference in New Issue