Просмотр исходного кода

android: Replace Picasso with Coil

Charles Lombardo 3 лет назад
Родитель
Сommit
3fcc6b1104

+ 1 - 3
src/android/app/build.gradle

@@ -135,9 +135,7 @@ dependencies {
     implementation 'com.google.android.material:material:1.8.0'
     implementation 'androidx.preference:preference:1.2.0'
     implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"
-
-    // For loading huge screenshots from the disk.
-    implementation 'com.squareup.picasso:picasso:2.71828'
+    implementation "io.coil-kt:coil:2.2.2"
 
     // Allows FRP-style asynchronous operations in Android.
     implementation 'io.reactivex:rxandroid:1.2.1'

+ 38 - 6
src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt

@@ -5,17 +5,27 @@ package org.yuzu.yuzu_emu.adapters
 
 import android.database.Cursor
 import android.database.DataSetObserver
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.net.Uri
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import android.widget.ImageView
+import androidx.appcompat.app.AppCompatActivity
 import androidx.fragment.app.FragmentActivity
+import androidx.lifecycle.lifecycleScope
 import androidx.recyclerview.widget.RecyclerView
+import coil.load
 import com.google.android.material.color.MaterialColors
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import org.yuzu.yuzu_emu.NativeLibrary
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.activities.EmulationActivity.Companion.launch
 import org.yuzu.yuzu_emu.model.GameDatabase
 import org.yuzu.yuzu_emu.utils.Log
-import org.yuzu.yuzu_emu.utils.PicassoUtils
 import org.yuzu.yuzu_emu.viewholders.GameViewHolder
 import java.util.*
 import java.util.stream.Stream
@@ -25,7 +35,8 @@ import java.util.stream.Stream
  * ContentProviders and Loaders, allows for efficient display of a limited view into a (possibly)
  * large dataset.
  */
-class GameAdapter : RecyclerView.Adapter<GameViewHolder>(), View.OnClickListener {
+class GameAdapter(private val activity: AppCompatActivity) : RecyclerView.Adapter<GameViewHolder>(),
+    View.OnClickListener {
     private var cursor: Cursor? = null
     private val observer: GameDataSetObserver?
     private var isDatasetValid = false
@@ -51,10 +62,21 @@ class GameAdapter : RecyclerView.Adapter<GameViewHolder>(), View.OnClickListener
     override fun onBindViewHolder(holder: GameViewHolder, position: Int) {
         if (isDatasetValid) {
             if (cursor!!.moveToPosition(position)) {
-                PicassoUtils.loadGameIcon(
-                    holder.imageIcon,
-                    cursor!!.getString(GameDatabase.GAME_COLUMN_PATH)
-                )
+                holder.imageIcon.scaleType = ImageView.ScaleType.CENTER_CROP
+                activity.lifecycleScope.launch {
+                    withContext(Dispatchers.IO) {
+                        val uri =
+                            Uri.parse(cursor!!.getString(GameDatabase.GAME_COLUMN_PATH)).toString()
+                        val bitmap = decodeGameIcon(uri)
+                        withContext(Dispatchers.Main) {
+                            holder.imageIcon.load(bitmap) {
+                                error(R.drawable.no_icon)
+                                crossfade(true)
+                            }
+                        }
+                    }
+                }
+
                 holder.textGameTitle.text =
                     cursor!!.getString(GameDatabase.GAME_COLUMN_TITLE)
                         .replace("[\\t\\n\\r]+".toRegex(), " ")
@@ -165,6 +187,16 @@ class GameAdapter : RecyclerView.Adapter<GameViewHolder>(), View.OnClickListener
             }
     }
 
+    private fun decodeGameIcon(uri: String): Bitmap {
+        val data = NativeLibrary.GetIcon(uri)
+        return BitmapFactory.decodeByteArray(
+            data,
+            0,
+            data.size,
+            BitmapFactory.Options()
+        )
+    }
+
     private inner class GameDataSetObserver : DataSetObserver() {
         override fun onChanged() {
             super.onChanged()

+ 0 - 1
src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt

@@ -57,7 +57,6 @@ class MainActivity : AppCompatActivity(), MainView {
                 PlatformGamesFragment.TAG
             ) as PlatformGamesFragment?
         }
-        PicassoUtils.init()
 
         // Dismiss previous notifications (should not happen unless a crash occurred)
         EmulationActivity.tryDismissRunningNotification(this)

+ 2 - 1
src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/platform/PlatformGamesFragment.kt

@@ -10,6 +10,7 @@ import android.view.View
 import android.view.ViewGroup
 import android.view.ViewTreeObserver.OnGlobalLayoutListener
 import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
 import androidx.core.view.ViewCompat
 import androidx.core.view.WindowInsetsCompat
 import androidx.core.view.updatePadding
@@ -40,7 +41,7 @@ class PlatformGamesFragment : Fragment(), PlatformGamesView {
     }
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
-        adapter = GameAdapter()
+        adapter = GameAdapter(requireActivity() as AppCompatActivity)
 
         // Organize our grid layout based on the current view.
         if (isAdded) {

+ 0 - 25
src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconRequestHandler.kt

@@ -1,25 +0,0 @@
-// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.yuzu.yuzu_emu.utils
-
-import android.graphics.BitmapFactory
-import com.squareup.picasso.Picasso
-import com.squareup.picasso.Request
-import com.squareup.picasso.RequestHandler
-import org.yuzu.yuzu_emu.NativeLibrary
-
-class GameIconRequestHandler : RequestHandler() {
-    override fun canHandleRequest(data: Request): Boolean {
-        return "content" == data.uri.scheme
-    }
-
-    override fun load(request: Request, networkPolicy: Int): Result {
-        val gamePath = request.uri.toString()
-        val data = NativeLibrary.GetIcon(gamePath)
-        val options = BitmapFactory.Options()
-        options.inMutable = true
-        val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size, options)
-        return Result(bitmap, Picasso.LoadedFrom.DISK)
-    }
-}

+ 0 - 45
src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PicassoRoundedCornersTransformation.java

@@ -1,45 +0,0 @@
-package org.yuzu.yuzu_emu.utils;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.RectF;
-
-import com.squareup.picasso.Transformation;
-
-public class PicassoRoundedCornersTransformation implements Transformation {
-    @Override
-    public Bitmap transform(Bitmap icon) {
-        final int width = icon.getWidth();
-        final int height = icon.getHeight();
-        final Rect rect = new Rect(0, 0, width, height);
-        final int size = Math.min(width, height);
-        final int x = (width - size) / 2;
-        final int y = (height - size) / 2;
-
-        Bitmap squaredBitmap = Bitmap.createBitmap(icon, x, y, size, size);
-        if (squaredBitmap != icon) {
-            icon.recycle();
-        }
-
-        Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(output);
-        BitmapShader shader = new BitmapShader(squaredBitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
-        Paint paint = new Paint();
-        paint.setAntiAlias(true);
-        paint.setShader(shader);
-
-        canvas.drawRoundRect(new RectF(rect), 10, 10, paint);
-
-        squaredBitmap.recycle();
-
-        return output;
-    }
-
-    @Override
-    public String key() {
-        return "circle";
-    }
-}

+ 0 - 57
src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PicassoUtils.java

@@ -1,57 +0,0 @@
-package org.yuzu.yuzu_emu.utils;
-
-import android.graphics.Bitmap;
-import android.net.Uri;
-import android.widget.ImageView;
-
-import com.squareup.picasso.Picasso;
-
-import org.yuzu.yuzu_emu.YuzuApplication;
-import org.yuzu.yuzu_emu.R;
-
-import java.io.IOException;
-
-import androidx.annotation.Nullable;
-
-public class PicassoUtils {
-    private static boolean mPicassoInitialized = false;
-
-    public static void init() {
-        if (mPicassoInitialized) {
-            return;
-        }
-        Picasso picassoInstance = new Picasso.Builder(YuzuApplication.getAppContext())
-                .addRequestHandler(new GameIconRequestHandler())
-                .build();
-
-        Picasso.setSingletonInstance(picassoInstance);
-        mPicassoInitialized = true;
-    }
-
-    public static void loadGameIcon(ImageView imageView, String gamePath) {
-        Picasso
-                .get()
-                .load(Uri.parse(gamePath))
-                .fit()
-                .centerInside()
-                .config(Bitmap.Config.RGB_565)
-                .error(R.drawable.no_icon)
-                .transform(new PicassoRoundedCornersTransformation())
-                .into(imageView);
-    }
-
-    // Blocking call. Load image from file and crop/resize it to fit in width x height.
-    @Nullable
-    public static Bitmap LoadBitmapFromFile(String uri, int width, int height) {
-        try {
-            return Picasso.get()
-                    .load(Uri.parse(uri))
-                    .config(Bitmap.Config.ARGB_8888)
-                    .centerCrop()
-                    .resize(width, height)
-                    .get();
-        } catch (IOException e) {
-            return null;
-        }
-    }
-}