diff options
| author | 2024-01-22 08:18:53 -0500 | |
|---|---|---|
| committer | 2024-01-22 08:19:20 -0500 | |
| commit | beaab10c8f90291bdf4c9ddcb7494ef7abb97355 (patch) | |
| tree | dc8e1f99c787cbe952efc17529f8271b5a8eca0c | |
| parent | Merge pull request #12739 from t895/debug-keystore (diff) | |
| download | yuzu-beaab10c8f90291bdf4c9ddcb7494ef7abb97355.tar.gz yuzu-beaab10c8f90291bdf4c9ddcb7494ef7abb97355.tar.xz yuzu-beaab10c8f90291bdf4c9ddcb7494ef7abb97355.zip | |
android: Add to launcher button
7 files changed, 116 insertions, 52 deletions
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 e26c2e0ab..b4f4d950f 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 | |||
| @@ -3,9 +3,6 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.adapters | 4 | package org.yuzu.yuzu_emu.adapters |
| 5 | 5 | ||
| 6 | import android.content.Intent | ||
| 7 | import android.graphics.Bitmap | ||
| 8 | import android.graphics.drawable.LayerDrawable | ||
| 9 | import android.net.Uri | 6 | import android.net.Uri |
| 10 | import android.text.TextUtils | 7 | import android.text.TextUtils |
| 11 | import android.view.LayoutInflater | 8 | import android.view.LayoutInflater |
| @@ -15,10 +12,6 @@ import android.widget.Toast | |||
| 15 | import androidx.appcompat.app.AppCompatActivity | 12 | import androidx.appcompat.app.AppCompatActivity |
| 16 | import androidx.core.content.pm.ShortcutInfoCompat | 13 | import androidx.core.content.pm.ShortcutInfoCompat |
| 17 | import androidx.core.content.pm.ShortcutManagerCompat | 14 | import androidx.core.content.pm.ShortcutManagerCompat |
| 18 | import androidx.core.content.res.ResourcesCompat | ||
| 19 | import androidx.core.graphics.drawable.IconCompat | ||
| 20 | import androidx.core.graphics.drawable.toBitmap | ||
| 21 | import androidx.core.graphics.drawable.toDrawable | ||
| 22 | import androidx.documentfile.provider.DocumentFile | 15 | import androidx.documentfile.provider.DocumentFile |
| 23 | import androidx.lifecycle.ViewModelProvider | 16 | import androidx.lifecycle.ViewModelProvider |
| 24 | import androidx.lifecycle.lifecycleScope | 17 | import androidx.lifecycle.lifecycleScope |
| @@ -30,7 +23,6 @@ import kotlinx.coroutines.withContext | |||
| 30 | import org.yuzu.yuzu_emu.HomeNavigationDirections | 23 | import org.yuzu.yuzu_emu.HomeNavigationDirections |
| 31 | import org.yuzu.yuzu_emu.R | 24 | import org.yuzu.yuzu_emu.R |
| 32 | import org.yuzu.yuzu_emu.YuzuApplication | 25 | import org.yuzu.yuzu_emu.YuzuApplication |
| 33 | import org.yuzu.yuzu_emu.activities.EmulationActivity | ||
| 34 | import org.yuzu.yuzu_emu.databinding.CardGameBinding | 26 | import org.yuzu.yuzu_emu.databinding.CardGameBinding |
| 35 | import org.yuzu.yuzu_emu.model.Game | 27 | import org.yuzu.yuzu_emu.model.Game |
| 36 | import org.yuzu.yuzu_emu.model.GamesViewModel | 28 | import org.yuzu.yuzu_emu.model.GamesViewModel |
| @@ -89,36 +81,13 @@ class GameAdapter(private val activity: AppCompatActivity) : | |||
| 89 | ) | 81 | ) |
| 90 | .apply() | 82 | .apply() |
| 91 | 83 | ||
| 92 | val openIntent = | ||
| 93 | Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply { | ||
| 94 | action = Intent.ACTION_VIEW | ||
| 95 | data = Uri.parse(game.path) | ||
| 96 | } | ||
| 97 | |||
| 98 | activity.lifecycleScope.launch { | 84 | activity.lifecycleScope.launch { |
| 99 | withContext(Dispatchers.IO) { | 85 | withContext(Dispatchers.IO) { |
| 100 | val layerDrawable = ResourcesCompat.getDrawable( | ||
| 101 | YuzuApplication.appContext.resources, | ||
| 102 | R.drawable.shortcut, | ||
| 103 | null | ||
| 104 | ) as LayerDrawable | ||
| 105 | layerDrawable.setDrawableByLayerId( | ||
| 106 | R.id.shortcut_foreground, | ||
| 107 | GameIconUtils.getGameIcon(activity, game) | ||
| 108 | .toDrawable(YuzuApplication.appContext.resources) | ||
| 109 | ) | ||
| 110 | val inset = YuzuApplication.appContext.resources | ||
| 111 | .getDimensionPixelSize(R.dimen.icon_inset) | ||
| 112 | layerDrawable.setLayerInset(1, inset, inset, inset, inset) | ||
| 113 | val shortcut = | 86 | val shortcut = |
| 114 | ShortcutInfoCompat.Builder(YuzuApplication.appContext, game.path) | 87 | ShortcutInfoCompat.Builder(YuzuApplication.appContext, game.path) |
| 115 | .setShortLabel(game.title) | 88 | .setShortLabel(game.title) |
| 116 | .setIcon( | 89 | .setIcon(GameIconUtils.getShortcutIcon(activity, game)) |
| 117 | IconCompat.createWithAdaptiveBitmap( | 90 | .setIntent(game.launchIntent) |
| 118 | layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888) | ||
| 119 | ) | ||
| 120 | ) | ||
| 121 | .setIntent(openIntent) | ||
| 122 | .build() | 91 | .build() |
| 123 | ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut) | 92 | ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut) |
| 124 | } | 93 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt index 83a845434..582df0133 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | package org.yuzu.yuzu_emu.fragments | 4 | package org.yuzu.yuzu_emu.fragments |
| 5 | 5 | ||
| 6 | import android.annotation.SuppressLint | 6 | import android.annotation.SuppressLint |
| 7 | import android.content.pm.ShortcutInfo | ||
| 8 | import android.content.pm.ShortcutManager | ||
| 7 | import android.os.Bundle | 9 | import android.os.Bundle |
| 8 | import android.text.TextUtils | 10 | import android.text.TextUtils |
| 9 | import android.view.LayoutInflater | 11 | import android.view.LayoutInflater |
| @@ -84,6 +86,24 @@ class GamePropertiesFragment : Fragment() { | |||
| 84 | view.findNavController().popBackStack() | 86 | view.findNavController().popBackStack() |
| 85 | } | 87 | } |
| 86 | 88 | ||
| 89 | val shortcutManager = requireActivity().getSystemService(ShortcutManager::class.java) | ||
| 90 | binding.buttonShortcut.isEnabled = shortcutManager.isRequestPinShortcutSupported | ||
| 91 | binding.buttonShortcut.setOnClickListener { | ||
| 92 | viewLifecycleOwner.lifecycleScope.launch { | ||
| 93 | withContext(Dispatchers.IO) { | ||
| 94 | val shortcut = ShortcutInfo.Builder(requireContext(), args.game.title) | ||
| 95 | .setShortLabel(args.game.title) | ||
| 96 | .setIcon( | ||
| 97 | GameIconUtils.getShortcutIcon(requireActivity(), args.game) | ||
| 98 | .toIcon(requireContext()) | ||
| 99 | ) | ||
| 100 | .setIntent(args.game.launchIntent) | ||
| 101 | .build() | ||
| 102 | shortcutManager.requestPinShortcut(shortcut, null) | ||
| 103 | } | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 87 | GameIconUtils.loadGameIcon(args.game, binding.imageGameScreen) | 107 | GameIconUtils.loadGameIcon(args.game, binding.imageGameScreen) |
| 88 | binding.title.text = args.game.title | 108 | binding.title.text = args.game.title |
| 89 | binding.title.postDelayed( | 109 | binding.title.postDelayed( |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt index f1ea1e20f..c8a4a2d17 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/Game.kt | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.model | 4 | package org.yuzu.yuzu_emu.model |
| 5 | 5 | ||
| 6 | import android.content.Intent | ||
| 6 | import android.net.Uri | 7 | import android.net.Uri |
| 7 | import android.os.Parcelable | 8 | import android.os.Parcelable |
| 8 | import java.util.HashSet | 9 | import java.util.HashSet |
| @@ -11,6 +12,7 @@ import kotlinx.serialization.Serializable | |||
| 11 | import org.yuzu.yuzu_emu.NativeLibrary | 12 | import org.yuzu.yuzu_emu.NativeLibrary |
| 12 | import org.yuzu.yuzu_emu.R | 13 | import org.yuzu.yuzu_emu.R |
| 13 | import org.yuzu.yuzu_emu.YuzuApplication | 14 | import org.yuzu.yuzu_emu.YuzuApplication |
| 15 | import org.yuzu.yuzu_emu.activities.EmulationActivity | ||
| 14 | import org.yuzu.yuzu_emu.utils.DirectoryInitialization | 16 | import org.yuzu.yuzu_emu.utils.DirectoryInitialization |
| 15 | import org.yuzu.yuzu_emu.utils.FileUtil | 17 | import org.yuzu.yuzu_emu.utils.FileUtil |
| 16 | import java.time.LocalDateTime | 18 | import java.time.LocalDateTime |
| @@ -61,6 +63,12 @@ class Game( | |||
| 61 | val addonDir: String | 63 | val addonDir: String |
| 62 | get() = DirectoryInitialization.userDirectory + "/load/" + programIdHex + "/" | 64 | get() = DirectoryInitialization.userDirectory + "/load/" + programIdHex + "/" |
| 63 | 65 | ||
| 66 | val launchIntent: Intent | ||
| 67 | get() = Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply { | ||
| 68 | action = Intent.ACTION_VIEW | ||
| 69 | data = Uri.parse(path) | ||
| 70 | } | ||
| 71 | |||
| 64 | override fun equals(other: Any?): Boolean { | 72 | override fun equals(other: Any?): Boolean { |
| 65 | if (other !is Game) { | 73 | if (other !is Game) { |
| 66 | return false | 74 | return false |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt index 2e9b0beb8..d05020560 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt | |||
| @@ -5,7 +5,10 @@ package org.yuzu.yuzu_emu.utils | |||
| 5 | 5 | ||
| 6 | import android.graphics.Bitmap | 6 | import android.graphics.Bitmap |
| 7 | import android.graphics.BitmapFactory | 7 | import android.graphics.BitmapFactory |
| 8 | import android.graphics.drawable.LayerDrawable | ||
| 8 | import android.widget.ImageView | 9 | import android.widget.ImageView |
| 10 | import androidx.core.content.res.ResourcesCompat | ||
| 11 | import androidx.core.graphics.drawable.IconCompat | ||
| 9 | import androidx.core.graphics.drawable.toBitmap | 12 | import androidx.core.graphics.drawable.toBitmap |
| 10 | import androidx.core.graphics.drawable.toDrawable | 13 | import androidx.core.graphics.drawable.toDrawable |
| 11 | import androidx.lifecycle.LifecycleOwner | 14 | import androidx.lifecycle.LifecycleOwner |
| @@ -85,4 +88,22 @@ object GameIconUtils { | |||
| 85 | return imageLoader.execute(request) | 88 | return imageLoader.execute(request) |
| 86 | .drawable!!.toBitmap(config = Bitmap.Config.ARGB_8888) | 89 | .drawable!!.toBitmap(config = Bitmap.Config.ARGB_8888) |
| 87 | } | 90 | } |
| 91 | |||
| 92 | suspend fun getShortcutIcon(lifecycleOwner: LifecycleOwner, game: Game): IconCompat { | ||
| 93 | val layerDrawable = ResourcesCompat.getDrawable( | ||
| 94 | YuzuApplication.appContext.resources, | ||
| 95 | R.drawable.shortcut, | ||
| 96 | null | ||
| 97 | ) as LayerDrawable | ||
| 98 | layerDrawable.setDrawableByLayerId( | ||
| 99 | R.id.shortcut_foreground, | ||
| 100 | getGameIcon(lifecycleOwner, game).toDrawable(YuzuApplication.appContext.resources) | ||
| 101 | ) | ||
| 102 | val inset = YuzuApplication.appContext.resources | ||
| 103 | .getDimensionPixelSize(R.dimen.icon_inset) | ||
| 104 | layerDrawable.setLayerInset(1, inset, inset, inset, inset) | ||
| 105 | return IconCompat.createWithAdaptiveBitmap( | ||
| 106 | layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888) | ||
| 107 | ) | ||
| 108 | } | ||
| 88 | } | 109 | } |
diff --git a/src/android/app/src/main/res/drawable/ic_shortcut.xml b/src/android/app/src/main/res/drawable/ic_shortcut.xml new file mode 100644 index 000000000..06e1983b2 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_shortcut.xml | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 2 | android:width="24dp" | ||
| 3 | android:height="24dp" | ||
| 4 | android:viewportWidth="960" | ||
| 5 | android:viewportHeight="960"> | ||
| 6 | <path | ||
| 7 | android:fillColor="?attr/colorControlNormal" | ||
| 8 | android:pathData="M280,920q-33,0 -56.5,-23.5T200,840v-720q0,-33 23.5,-56.5T280,40h400q33,0 56.5,23.5T760,120v160h-80v-40L280,240v480h400v-40h80v160q0,33 -23.5,56.5T680,920L280,920ZM686,520L480,520v120h-80v-120q0,-33 23.5,-56.5T480,440h206l-62,-64 56,-56 160,160 -160,160 -56,-56 62,-64Z" /> | ||
| 9 | </vector> | ||
diff --git a/src/android/app/src/main/res/layout-w600dp/fragment_game_properties.xml b/src/android/app/src/main/res/layout-w600dp/fragment_game_properties.xml index 0b9633855..551f255c0 100644 --- a/src/android/app/src/main/res/layout-w600dp/fragment_game_properties.xml +++ b/src/android/app/src/main/res/layout-w600dp/fragment_game_properties.xml | |||
| @@ -43,16 +43,35 @@ | |||
| 43 | app:layout_constraintStart_toStartOf="parent" | 43 | app:layout_constraintStart_toStartOf="parent" |
| 44 | app:layout_constraintTop_toTopOf="parent"> | 44 | app:layout_constraintTop_toTopOf="parent"> |
| 45 | 45 | ||
| 46 | <Button | 46 | <androidx.constraintlayout.widget.ConstraintLayout |
| 47 | android:id="@+id/button_back" | 47 | android:layout_width="match_parent" |
| 48 | style="?attr/materialIconButtonStyle" | ||
| 49 | android:layout_width="wrap_content" | ||
| 50 | android:layout_height="wrap_content" | 48 | android:layout_height="wrap_content" |
| 51 | android:layout_gravity="start" | ||
| 52 | android:layout_margin="8dp" | 49 | android:layout_margin="8dp" |
| 53 | app:icon="@drawable/ic_back" | 50 | android:orientation="horizontal"> |
| 54 | app:iconSize="24dp" | 51 | |
| 55 | app:iconTint="?attr/colorOnSurface" /> | 52 | <Button |
| 53 | android:id="@+id/button_back" | ||
| 54 | style="?attr/materialIconButtonStyle" | ||
| 55 | android:layout_width="wrap_content" | ||
| 56 | android:layout_height="wrap_content" | ||
| 57 | app:icon="@drawable/ic_back" | ||
| 58 | app:iconSize="24dp" | ||
| 59 | app:iconTint="?attr/colorOnSurface" | ||
| 60 | app:layout_constraintStart_toStartOf="parent" | ||
| 61 | app:layout_constraintTop_toTopOf="parent" /> | ||
| 62 | |||
| 63 | <Button | ||
| 64 | android:id="@+id/button_shortcut" | ||
| 65 | style="?attr/materialIconButtonStyle" | ||
| 66 | android:layout_width="wrap_content" | ||
| 67 | android:layout_height="wrap_content" | ||
| 68 | app:icon="@drawable/ic_shortcut" | ||
| 69 | app:iconSize="24dp" | ||
| 70 | app:iconTint="?attr/colorOnSurface" | ||
| 71 | app:layout_constraintTop_toTopOf="parent" | ||
| 72 | app:layout_constraintEnd_toEndOf="parent" /> | ||
| 73 | |||
| 74 | </androidx.constraintlayout.widget.ConstraintLayout> | ||
| 56 | 75 | ||
| 57 | <com.google.android.material.card.MaterialCardView | 76 | <com.google.android.material.card.MaterialCardView |
| 58 | style="?attr/materialCardViewElevatedStyle" | 77 | style="?attr/materialCardViewElevatedStyle" |
diff --git a/src/android/app/src/main/res/layout/fragment_game_properties.xml b/src/android/app/src/main/res/layout/fragment_game_properties.xml index 72ecbde30..cadd0bc4a 100644 --- a/src/android/app/src/main/res/layout/fragment_game_properties.xml +++ b/src/android/app/src/main/res/layout/fragment_game_properties.xml | |||
| @@ -1,6 +1,5 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <androidx.constraintlayout.widget.ConstraintLayout | 2 | <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| 3 | xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 4 | xmlns:app="http://schemas.android.com/apk/res-auto" | 3 | xmlns:app="http://schemas.android.com/apk/res-auto" |
| 5 | xmlns:tools="http://schemas.android.com/tools" | 4 | xmlns:tools="http://schemas.android.com/tools" |
| 6 | android:layout_width="match_parent" | 5 | android:layout_width="match_parent" |
| @@ -22,16 +21,35 @@ | |||
| 22 | android:orientation="vertical" | 21 | android:orientation="vertical" |
| 23 | android:gravity="center_horizontal"> | 22 | android:gravity="center_horizontal"> |
| 24 | 23 | ||
| 25 | <Button | 24 | <androidx.constraintlayout.widget.ConstraintLayout |
| 26 | android:id="@+id/button_back" | 25 | android:layout_width="match_parent" |
| 27 | style="?attr/materialIconButtonStyle" | ||
| 28 | android:layout_width="wrap_content" | ||
| 29 | android:layout_height="wrap_content" | 26 | android:layout_height="wrap_content" |
| 30 | android:layout_margin="8dp" | 27 | android:layout_margin="8dp" |
| 31 | android:layout_gravity="start" | 28 | android:orientation="horizontal"> |
| 32 | app:icon="@drawable/ic_back" | 29 | |
| 33 | app:iconSize="24dp" | 30 | <Button |
| 34 | app:iconTint="?attr/colorOnSurface" /> | 31 | android:id="@+id/button_back" |
| 32 | style="?attr/materialIconButtonStyle" | ||
| 33 | android:layout_width="wrap_content" | ||
| 34 | android:layout_height="wrap_content" | ||
| 35 | app:icon="@drawable/ic_back" | ||
| 36 | app:iconSize="24dp" | ||
| 37 | app:iconTint="?attr/colorOnSurface" | ||
| 38 | app:layout_constraintStart_toStartOf="parent" | ||
| 39 | app:layout_constraintTop_toTopOf="parent" /> | ||
| 40 | |||
| 41 | <Button | ||
| 42 | android:id="@+id/button_shortcut" | ||
| 43 | style="?attr/materialIconButtonStyle" | ||
| 44 | android:layout_width="wrap_content" | ||
| 45 | android:layout_height="wrap_content" | ||
| 46 | app:icon="@drawable/ic_shortcut" | ||
| 47 | app:iconSize="24dp" | ||
| 48 | app:iconTint="?attr/colorOnSurface" | ||
| 49 | app:layout_constraintEnd_toEndOf="parent" | ||
| 50 | app:layout_constraintTop_toTopOf="parent" /> | ||
| 51 | |||
| 52 | </androidx.constraintlayout.widget.ConstraintLayout> | ||
| 35 | 53 | ||
| 36 | <com.google.android.material.card.MaterialCardView | 54 | <com.google.android.material.card.MaterialCardView |
| 37 | style="?attr/materialCardViewElevatedStyle" | 55 | style="?attr/materialCardViewElevatedStyle" |
| @@ -45,7 +63,7 @@ | |||
| 45 | android:id="@+id/image_game_screen" | 63 | android:id="@+id/image_game_screen" |
| 46 | android:layout_width="175dp" | 64 | android:layout_width="175dp" |
| 47 | android:layout_height="175dp" | 65 | android:layout_height="175dp" |
| 48 | tools:src="@drawable/default_icon"/> | 66 | tools:src="@drawable/default_icon" /> |
| 49 | 67 | ||
| 50 | </com.google.android.material.card.MaterialCardView> | 68 | </com.google.android.material.card.MaterialCardView> |
| 51 | 69 | ||