diff options
| author | 2023-03-14 20:23:00 -0400 | |
|---|---|---|
| committer | 2023-06-03 00:05:43 -0700 | |
| commit | 3fcc6b11042e86ef05d074fe287a02a40ed4d76e (patch) | |
| tree | 94fc69a57087c07d8fa67f612eb32fce72ad3c26 /src | |
| parent | android: New swipe to refresh color scheme (diff) | |
| download | yuzu-3fcc6b11042e86ef05d074fe287a02a40ed4d76e.tar.gz yuzu-3fcc6b11042e86ef05d074fe287a02a40ed4d76e.tar.xz yuzu-3fcc6b11042e86ef05d074fe287a02a40ed4d76e.zip | |
android: Replace Picasso with Coil
Diffstat (limited to 'src')
7 files changed, 41 insertions, 138 deletions
diff --git a/src/android/app/build.gradle b/src/android/app/build.gradle index c0bd7d459..68e11bb66 100644 --- a/src/android/app/build.gradle +++ b/src/android/app/build.gradle | |||
| @@ -135,9 +135,7 @@ dependencies { | |||
| 135 | implementation 'com.google.android.material:material:1.8.0' | 135 | implementation 'com.google.android.material:material:1.8.0' |
| 136 | implementation 'androidx.preference:preference:1.2.0' | 136 | implementation 'androidx.preference:preference:1.2.0' |
| 137 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1" | 137 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1" |
| 138 | 138 | implementation "io.coil-kt:coil:2.2.2" | |
| 139 | // For loading huge screenshots from the disk. | ||
| 140 | implementation 'com.squareup.picasso:picasso:2.71828' | ||
| 141 | 139 | ||
| 142 | // Allows FRP-style asynchronous operations in Android. | 140 | // Allows FRP-style asynchronous operations in Android. |
| 143 | implementation 'io.reactivex:rxandroid:1.2.1' | 141 | implementation 'io.reactivex:rxandroid:1.2.1' |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt index af4ec63f2..8891705a5 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt | |||
| @@ -5,17 +5,27 @@ package org.yuzu.yuzu_emu.adapters | |||
| 5 | 5 | ||
| 6 | import android.database.Cursor | 6 | import android.database.Cursor |
| 7 | import android.database.DataSetObserver | 7 | import android.database.DataSetObserver |
| 8 | import android.graphics.Bitmap | ||
| 9 | import android.graphics.BitmapFactory | ||
| 10 | import android.net.Uri | ||
| 8 | import android.view.LayoutInflater | 11 | import android.view.LayoutInflater |
| 9 | import android.view.View | 12 | import android.view.View |
| 10 | import android.view.ViewGroup | 13 | import android.view.ViewGroup |
| 14 | import android.widget.ImageView | ||
| 15 | import androidx.appcompat.app.AppCompatActivity | ||
| 11 | import androidx.fragment.app.FragmentActivity | 16 | import androidx.fragment.app.FragmentActivity |
| 17 | import androidx.lifecycle.lifecycleScope | ||
| 12 | import androidx.recyclerview.widget.RecyclerView | 18 | import androidx.recyclerview.widget.RecyclerView |
| 19 | import coil.load | ||
| 13 | import com.google.android.material.color.MaterialColors | 20 | import com.google.android.material.color.MaterialColors |
| 21 | import kotlinx.coroutines.Dispatchers | ||
| 22 | import kotlinx.coroutines.launch | ||
| 23 | import kotlinx.coroutines.withContext | ||
| 24 | import org.yuzu.yuzu_emu.NativeLibrary | ||
| 14 | import org.yuzu.yuzu_emu.R | 25 | import org.yuzu.yuzu_emu.R |
| 15 | import org.yuzu.yuzu_emu.activities.EmulationActivity.Companion.launch | 26 | import org.yuzu.yuzu_emu.activities.EmulationActivity.Companion.launch |
| 16 | import org.yuzu.yuzu_emu.model.GameDatabase | 27 | import org.yuzu.yuzu_emu.model.GameDatabase |
| 17 | import org.yuzu.yuzu_emu.utils.Log | 28 | import org.yuzu.yuzu_emu.utils.Log |
| 18 | import org.yuzu.yuzu_emu.utils.PicassoUtils | ||
| 19 | import org.yuzu.yuzu_emu.viewholders.GameViewHolder | 29 | import org.yuzu.yuzu_emu.viewholders.GameViewHolder |
| 20 | import java.util.* | 30 | import java.util.* |
| 21 | import java.util.stream.Stream | 31 | import java.util.stream.Stream |
| @@ -25,7 +35,8 @@ import java.util.stream.Stream | |||
| 25 | * ContentProviders and Loaders, allows for efficient display of a limited view into a (possibly) | 35 | * ContentProviders and Loaders, allows for efficient display of a limited view into a (possibly) |
| 26 | * large dataset. | 36 | * large dataset. |
| 27 | */ | 37 | */ |
| 28 | class GameAdapter : RecyclerView.Adapter<GameViewHolder>(), View.OnClickListener { | 38 | class GameAdapter(private val activity: AppCompatActivity) : RecyclerView.Adapter<GameViewHolder>(), |
| 39 | View.OnClickListener { | ||
| 29 | private var cursor: Cursor? = null | 40 | private var cursor: Cursor? = null |
| 30 | private val observer: GameDataSetObserver? | 41 | private val observer: GameDataSetObserver? |
| 31 | private var isDatasetValid = false | 42 | private var isDatasetValid = false |
| @@ -51,10 +62,21 @@ class GameAdapter : RecyclerView.Adapter<GameViewHolder>(), View.OnClickListener | |||
| 51 | override fun onBindViewHolder(holder: GameViewHolder, position: Int) { | 62 | override fun onBindViewHolder(holder: GameViewHolder, position: Int) { |
| 52 | if (isDatasetValid) { | 63 | if (isDatasetValid) { |
| 53 | if (cursor!!.moveToPosition(position)) { | 64 | if (cursor!!.moveToPosition(position)) { |
| 54 | PicassoUtils.loadGameIcon( | 65 | holder.imageIcon.scaleType = ImageView.ScaleType.CENTER_CROP |
| 55 | holder.imageIcon, | 66 | activity.lifecycleScope.launch { |
| 56 | cursor!!.getString(GameDatabase.GAME_COLUMN_PATH) | 67 | withContext(Dispatchers.IO) { |
| 57 | ) | 68 | val uri = |
| 69 | Uri.parse(cursor!!.getString(GameDatabase.GAME_COLUMN_PATH)).toString() | ||
| 70 | val bitmap = decodeGameIcon(uri) | ||
| 71 | withContext(Dispatchers.Main) { | ||
| 72 | holder.imageIcon.load(bitmap) { | ||
| 73 | error(R.drawable.no_icon) | ||
| 74 | crossfade(true) | ||
| 75 | } | ||
| 76 | } | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 58 | holder.textGameTitle.text = | 80 | holder.textGameTitle.text = |
| 59 | cursor!!.getString(GameDatabase.GAME_COLUMN_TITLE) | 81 | cursor!!.getString(GameDatabase.GAME_COLUMN_TITLE) |
| 60 | .replace("[\\t\\n\\r]+".toRegex(), " ") | 82 | .replace("[\\t\\n\\r]+".toRegex(), " ") |
| @@ -165,6 +187,16 @@ class GameAdapter : RecyclerView.Adapter<GameViewHolder>(), View.OnClickListener | |||
| 165 | } | 187 | } |
| 166 | } | 188 | } |
| 167 | 189 | ||
| 190 | private fun decodeGameIcon(uri: String): Bitmap { | ||
| 191 | val data = NativeLibrary.GetIcon(uri) | ||
| 192 | return BitmapFactory.decodeByteArray( | ||
| 193 | data, | ||
| 194 | 0, | ||
| 195 | data.size, | ||
| 196 | BitmapFactory.Options() | ||
| 197 | ) | ||
| 198 | } | ||
| 199 | |||
| 168 | private inner class GameDataSetObserver : DataSetObserver() { | 200 | private inner class GameDataSetObserver : DataSetObserver() { |
| 169 | override fun onChanged() { | 201 | override fun onChanged() { |
| 170 | super.onChanged() | 202 | super.onChanged() |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt index ba60a26e8..768159857 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt | |||
| @@ -57,7 +57,6 @@ class MainActivity : AppCompatActivity(), MainView { | |||
| 57 | PlatformGamesFragment.TAG | 57 | PlatformGamesFragment.TAG |
| 58 | ) as PlatformGamesFragment? | 58 | ) as PlatformGamesFragment? |
| 59 | } | 59 | } |
| 60 | PicassoUtils.init() | ||
| 61 | 60 | ||
| 62 | // Dismiss previous notifications (should not happen unless a crash occurred) | 61 | // Dismiss previous notifications (should not happen unless a crash occurred) |
| 63 | EmulationActivity.tryDismissRunningNotification(this) | 62 | EmulationActivity.tryDismissRunningNotification(this) |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/platform/PlatformGamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/platform/PlatformGamesFragment.kt index 73929e186..40e902a37 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/platform/PlatformGamesFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/platform/PlatformGamesFragment.kt | |||
| @@ -10,6 +10,7 @@ import android.view.View | |||
| 10 | import android.view.ViewGroup | 10 | import android.view.ViewGroup |
| 11 | import android.view.ViewTreeObserver.OnGlobalLayoutListener | 11 | import android.view.ViewTreeObserver.OnGlobalLayoutListener |
| 12 | import android.widget.TextView | 12 | import android.widget.TextView |
| 13 | import androidx.appcompat.app.AppCompatActivity | ||
| 13 | import androidx.core.view.ViewCompat | 14 | import androidx.core.view.ViewCompat |
| 14 | import androidx.core.view.WindowInsetsCompat | 15 | import androidx.core.view.WindowInsetsCompat |
| 15 | import androidx.core.view.updatePadding | 16 | import androidx.core.view.updatePadding |
| @@ -40,7 +41,7 @@ class PlatformGamesFragment : Fragment(), PlatformGamesView { | |||
| 40 | } | 41 | } |
| 41 | 42 | ||
| 42 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | 43 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { |
| 43 | adapter = GameAdapter() | 44 | adapter = GameAdapter(requireActivity() as AppCompatActivity) |
| 44 | 45 | ||
| 45 | // Organize our grid layout based on the current view. | 46 | // Organize our grid layout based on the current view. |
| 46 | if (isAdded) { | 47 | if (isAdded) { |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconRequestHandler.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconRequestHandler.kt deleted file mode 100644 index 24ed309a8..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconRequestHandler.kt +++ /dev/null | |||
| @@ -1,25 +0,0 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.utils | ||
| 5 | |||
| 6 | import android.graphics.BitmapFactory | ||
| 7 | import com.squareup.picasso.Picasso | ||
| 8 | import com.squareup.picasso.Request | ||
| 9 | import com.squareup.picasso.RequestHandler | ||
| 10 | import org.yuzu.yuzu_emu.NativeLibrary | ||
| 11 | |||
| 12 | class GameIconRequestHandler : RequestHandler() { | ||
| 13 | override fun canHandleRequest(data: Request): Boolean { | ||
| 14 | return "content" == data.uri.scheme | ||
| 15 | } | ||
| 16 | |||
| 17 | override fun load(request: Request, networkPolicy: Int): Result { | ||
| 18 | val gamePath = request.uri.toString() | ||
| 19 | val data = NativeLibrary.GetIcon(gamePath) | ||
| 20 | val options = BitmapFactory.Options() | ||
| 21 | options.inMutable = true | ||
| 22 | val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size, options) | ||
| 23 | return Result(bitmap, Picasso.LoadedFrom.DISK) | ||
| 24 | } | ||
| 25 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PicassoRoundedCornersTransformation.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PicassoRoundedCornersTransformation.java deleted file mode 100644 index 03057b0d5..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PicassoRoundedCornersTransformation.java +++ /dev/null | |||
| @@ -1,45 +0,0 @@ | |||
| 1 | package org.yuzu.yuzu_emu.utils; | ||
| 2 | |||
| 3 | import android.graphics.Bitmap; | ||
| 4 | import android.graphics.BitmapShader; | ||
| 5 | import android.graphics.Canvas; | ||
| 6 | import android.graphics.Paint; | ||
| 7 | import android.graphics.Rect; | ||
| 8 | import android.graphics.RectF; | ||
| 9 | |||
| 10 | import com.squareup.picasso.Transformation; | ||
| 11 | |||
| 12 | public class PicassoRoundedCornersTransformation implements Transformation { | ||
| 13 | @Override | ||
| 14 | public Bitmap transform(Bitmap icon) { | ||
| 15 | final int width = icon.getWidth(); | ||
| 16 | final int height = icon.getHeight(); | ||
| 17 | final Rect rect = new Rect(0, 0, width, height); | ||
| 18 | final int size = Math.min(width, height); | ||
| 19 | final int x = (width - size) / 2; | ||
| 20 | final int y = (height - size) / 2; | ||
| 21 | |||
| 22 | Bitmap squaredBitmap = Bitmap.createBitmap(icon, x, y, size, size); | ||
| 23 | if (squaredBitmap != icon) { | ||
| 24 | icon.recycle(); | ||
| 25 | } | ||
| 26 | |||
| 27 | Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); | ||
| 28 | Canvas canvas = new Canvas(output); | ||
| 29 | BitmapShader shader = new BitmapShader(squaredBitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP); | ||
| 30 | Paint paint = new Paint(); | ||
| 31 | paint.setAntiAlias(true); | ||
| 32 | paint.setShader(shader); | ||
| 33 | |||
| 34 | canvas.drawRoundRect(new RectF(rect), 10, 10, paint); | ||
| 35 | |||
| 36 | squaredBitmap.recycle(); | ||
| 37 | |||
| 38 | return output; | ||
| 39 | } | ||
| 40 | |||
| 41 | @Override | ||
| 42 | public String key() { | ||
| 43 | return "circle"; | ||
| 44 | } | ||
| 45 | } \ No newline at end of file | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PicassoUtils.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PicassoUtils.java deleted file mode 100644 index 504dc5b6d..000000000 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PicassoUtils.java +++ /dev/null | |||
| @@ -1,57 +0,0 @@ | |||
| 1 | package org.yuzu.yuzu_emu.utils; | ||
| 2 | |||
| 3 | import android.graphics.Bitmap; | ||
| 4 | import android.net.Uri; | ||
| 5 | import android.widget.ImageView; | ||
| 6 | |||
| 7 | import com.squareup.picasso.Picasso; | ||
| 8 | |||
| 9 | import org.yuzu.yuzu_emu.YuzuApplication; | ||
| 10 | import org.yuzu.yuzu_emu.R; | ||
| 11 | |||
| 12 | import java.io.IOException; | ||
| 13 | |||
| 14 | import androidx.annotation.Nullable; | ||
| 15 | |||
| 16 | public class PicassoUtils { | ||
| 17 | private static boolean mPicassoInitialized = false; | ||
| 18 | |||
| 19 | public static void init() { | ||
| 20 | if (mPicassoInitialized) { | ||
| 21 | return; | ||
| 22 | } | ||
| 23 | Picasso picassoInstance = new Picasso.Builder(YuzuApplication.getAppContext()) | ||
| 24 | .addRequestHandler(new GameIconRequestHandler()) | ||
| 25 | .build(); | ||
| 26 | |||
| 27 | Picasso.setSingletonInstance(picassoInstance); | ||
| 28 | mPicassoInitialized = true; | ||
| 29 | } | ||
| 30 | |||
| 31 | public static void loadGameIcon(ImageView imageView, String gamePath) { | ||
| 32 | Picasso | ||
| 33 | .get() | ||
| 34 | .load(Uri.parse(gamePath)) | ||
| 35 | .fit() | ||
| 36 | .centerInside() | ||
| 37 | .config(Bitmap.Config.RGB_565) | ||
| 38 | .error(R.drawable.no_icon) | ||
| 39 | .transform(new PicassoRoundedCornersTransformation()) | ||
| 40 | .into(imageView); | ||
| 41 | } | ||
| 42 | |||
| 43 | // Blocking call. Load image from file and crop/resize it to fit in width x height. | ||
| 44 | @Nullable | ||
| 45 | public static Bitmap LoadBitmapFromFile(String uri, int width, int height) { | ||
| 46 | try { | ||
| 47 | return Picasso.get() | ||
| 48 | .load(Uri.parse(uri)) | ||
| 49 | .config(Bitmap.Config.ARGB_8888) | ||
| 50 | .centerCrop() | ||
| 51 | .resize(width, height) | ||
| 52 | .get(); | ||
| 53 | } catch (IOException e) { | ||
| 54 | return null; | ||
| 55 | } | ||
| 56 | } | ||
| 57 | } | ||