diff --git a/android/app/src/main/java/com/github/stenzek/duckstation/GameGridFragment.java b/android/app/src/main/java/com/github/stenzek/duckstation/GameGridFragment.java index 0c71a1a48..3f21c0a5a 100644 --- a/android/app/src/main/java/com/github/stenzek/duckstation/GameGridFragment.java +++ b/android/app/src/main/java/com/github/stenzek/duckstation/GameGridFragment.java @@ -1,7 +1,5 @@ package com.github.stenzek.duckstation; -import android.content.Context; -import android.content.res.Configuration; import android.os.AsyncTask; import android.os.Bundle; import android.util.TypedValue; @@ -17,16 +15,13 @@ import androidx.annotation.Nullable; import androidx.appcompat.widget.PopupMenu; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; -import androidx.recyclerview.widget.DividerItemDecoration; -import androidx.recyclerview.widget.GridLayoutManager; -import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; public class GameGridFragment extends Fragment implements GameList.OnRefreshListener { private static final int SPACING_DIPS = 25; private static final int WIDTH_DIPS = 160; - private MainActivity mParent; + private final MainActivity mParent; private RecyclerView mRecyclerView; private ViewAdapter mAdapter; private GridAutofitLayoutManager mLayoutManager; @@ -69,8 +64,8 @@ public class GameGridFragment extends Fragment implements GameList.OnRefreshList } private static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { - private MainActivity mParent; - private ImageView mImageView; + private final MainActivity mParent; + private final ImageView mImageView; private GameListEntry mEntry; public ViewHolder(@NonNull MainActivity parent, @NonNull View itemView) { @@ -84,9 +79,12 @@ public class GameGridFragment extends Fragment implements GameList.OnRefreshList public void bindToEntry(GameListEntry entry) { mEntry = entry; + // while it loads/generates + mImageView.setImageDrawable(ContextCompat.getDrawable(mParent, R.drawable.ic_media_cdrom)); + final String coverPath = entry.getCoverPath(); if (coverPath == null) { - mImageView.setImageDrawable(ContextCompat.getDrawable(mParent, R.drawable.ic_media_cdrom)); + new GenerateCoverTask(mParent, mImageView, mEntry.getTitle()).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); return; } @@ -125,9 +123,9 @@ public class GameGridFragment extends Fragment implements GameList.OnRefreshList } private static class ViewAdapter extends RecyclerView.Adapter { - private MainActivity mParent; - private LayoutInflater mInflater; - private GameList mGameList; + private final MainActivity mParent; + private final LayoutInflater mInflater; + private final GameList mGameList; public ViewAdapter(@NonNull MainActivity parent, @NonNull GameList gameList) { mParent = parent; diff --git a/android/app/src/main/java/com/github/stenzek/duckstation/GenerateCoverTask.java b/android/app/src/main/java/com/github/stenzek/duckstation/GenerateCoverTask.java new file mode 100644 index 000000000..f503d4e15 --- /dev/null +++ b/android/app/src/main/java/com/github/stenzek/duckstation/GenerateCoverTask.java @@ -0,0 +1,64 @@ +package com.github.stenzek.duckstation; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.os.AsyncTask; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.widget.ImageView; + +import java.lang.ref.WeakReference; + +public class GenerateCoverTask extends AsyncTask { + private final Context mContext; + private final WeakReference mView; + private final String mTitle; + + public GenerateCoverTask(Context context, ImageView view, String title) { + mContext = context; + mView = new WeakReference<>(view); + mTitle = title; + } + + @Override + protected Bitmap doInBackground(Void... voids) { + try { + final Bitmap background = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.cover_placeholder); + if (background == null) + return null; + + final Bitmap bitmap = Bitmap.createBitmap(background.getWidth(), background.getHeight(), background.getConfig()); + final Canvas canvas = new Canvas(bitmap); + final TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + canvas.drawBitmap(background, 0.0f, 0.0f, paint); + + paint.setColor(Color.rgb(255, 255, 255)); + paint.setTextSize(100); + paint.setShadowLayer(1.0f, 0.0f, 1.0f, Color.DKGRAY); + paint.setTextAlign(Paint.Align.CENTER); + + final StaticLayout staticLayout = new StaticLayout(mTitle, paint, + canvas.getWidth(), Layout.Alignment.ALIGN_NORMAL, 1, 0, false); + canvas.save(); + canvas.translate(canvas.getWidth() / 2, (canvas.getHeight() / 2) - (staticLayout.getHeight() / 2)); + staticLayout.draw(canvas); + canvas.restore(); + return bitmap; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + @Override + protected void onPostExecute(Bitmap bitmap) { + ImageView iv = mView.get(); + if (iv != null) + iv.setImageBitmap(bitmap); + } +} diff --git a/android/app/src/main/res/drawable/cover_placeholder.png b/android/app/src/main/res/drawable/cover_placeholder.png new file mode 100644 index 000000000..18dc446fe Binary files /dev/null and b/android/app/src/main/res/drawable/cover_placeholder.png differ