diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index 8fc4a82088..e57faf3ff9 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -155,7 +155,6 @@ android {
}
}
-
externalNativeBuild {
cmake {
version = "3.22.1"
diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml
index 353c2f722d..d31deaa355 100644
--- a/src/android/app/src/main/AndroidManifest.xml
+++ b/src/android/app/src/main/AndroidManifest.xml
@@ -12,7 +12,7 @@ SPDX-FileCopyrightText: Eden Emulator Project
SPDX-License-Identifier: GPL-3.0-or-later
-->
-
+
@@ -42,6 +42,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
android:banner="@drawable/tv_banner"
android:fullBackupContent="@xml/data_extraction_rules"
android:dataExtractionRules="@xml/data_extraction_rules_api_31"
+ tools:targetApi="33"
android:enableOnBackInvokedCallback="true">
@@ -85,6 +86,10 @@ SPDX-License-Identifier: GPL-3.0-or-later
+
+
+
+
@@ -100,4 +105,4 @@ SPDX-License-Identifier: GPL-3.0-or-later
-
\ No newline at end of file
+
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
index c0e5983fc6..ba50bcad34 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
@@ -4,7 +4,6 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-
package org.yuzu.yuzu_emu
import android.content.DialogInterface
@@ -17,7 +16,6 @@ import android.widget.TextView
import androidx.annotation.Keep
import androidx.core.net.toUri
import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import net.swiftzer.semver.SemVer
import java.lang.ref.WeakReference
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.fragments.CoreErrorDialogFragment
@@ -28,7 +26,6 @@ import org.yuzu.yuzu_emu.model.InstallResult
import org.yuzu.yuzu_emu.model.Patch
import org.yuzu.yuzu_emu.model.GameVerificationResult
import org.yuzu.yuzu_emu.network.NetPlayManager
-import java.io.File
/**
* Class which contains methods that interact
@@ -276,8 +273,7 @@ object NativeLibrary {
val emulationActivity = sEmulationActivity.get()
if (emulationActivity != null) {
emulationActivity.addNetPlayMessages(type, message)
- }
- else {
+ } else {
NetPlayManager.addNetPlayMessage(type, message)
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
index 40200931b7..1aa300f82d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -4,7 +4,6 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-
package org.yuzu.yuzu_emu.activities
import android.annotation.SuppressLint
@@ -58,7 +57,6 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.NfcReader
import org.yuzu.yuzu_emu.utils.ParamPackage
import org.yuzu.yuzu_emu.utils.ThemeHelper
-import org.yuzu.yuzu_emu.utils.PowerStateUtils
import java.text.NumberFormat
import kotlin.math.roundToInt
@@ -421,7 +419,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
NetPlayManager.addNetPlayMessage(type, msg)
}
-
private var pictureInPictureReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
if (intent.action == actionPlay) {
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 c4652f55e1..11b81a01a6 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
@@ -4,15 +4,10 @@
package org.yuzu.yuzu_emu.adapters
import android.content.DialogInterface
-import android.net.Uri
import android.text.Html
-import android.text.method.LinkMovementMethod
import android.view.LayoutInflater
-import android.view.View
import android.view.ViewGroup
-import android.widget.LinearLayout
import android.widget.ImageView
-import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.pm.ShortcutInfoCompat
@@ -37,7 +32,6 @@ import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.utils.GameIconUtils
import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
-import androidx.recyclerview.widget.RecyclerView
import androidx.core.net.toUri
import androidx.core.content.edit
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@@ -94,7 +88,7 @@ class GameAdapter(private val activity: AppCompatActivity) :
}
VIEW_TYPE_CAROUSEL -> {
val carouselBinding = holder.binding as CardGameCarouselBinding
- //soothens transient flickering
+ // soothens transient flickering
carouselBinding.cardGameCarousel.scaleY = 0f
carouselBinding.cardGameCarousel.alpha = 0f
}
@@ -103,9 +97,21 @@ class GameAdapter(private val activity: AppCompatActivity) :
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder {
val binding = when (viewType) {
- VIEW_TYPE_LIST -> CardGameListBinding.inflate(LayoutInflater.from(parent.context), parent, false)
- VIEW_TYPE_GRID -> CardGameGridBinding.inflate(LayoutInflater.from(parent.context), parent, false)
- VIEW_TYPE_CAROUSEL -> CardGameCarouselBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ VIEW_TYPE_LIST -> CardGameListBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false
+ )
+ VIEW_TYPE_GRID -> CardGameGridBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false
+ )
+ VIEW_TYPE_CAROUSEL -> CardGameCarouselBinding.inflate(
+ LayoutInflater.from(parent.context),
+ parent,
+ false
+ )
else -> throw IllegalArgumentException("Invalid view type")
}
return GameViewHolder(binding, viewType)
@@ -212,7 +218,10 @@ class GameAdapter(private val activity: AppCompatActivity) :
.setIcon(GameIconUtils.getShortcutIcon(activity, game))
.setIntent(game.launchIntent)
.build()
- ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
+ ShortcutManagerCompat.pushDynamicShortcut(
+ YuzuApplication.appContext,
+ shortcut
+ )
}
}
@@ -232,7 +241,7 @@ class GameAdapter(private val activity: AppCompatActivity) :
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
launch()
}
- .setNegativeButton(android.R.string.cancel) { _,_ -> }
+ .setNegativeButton(android.R.string.cancel) { _, _ -> }
.show()
} else {
launch()
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
index 5d72efa350..629b3a983c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
@@ -6,10 +6,8 @@ package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
-import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.lifecycle.LifecycleOwner
-import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.model.HomeSetting
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/ChatDialog.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/ChatDialog.kt
index 2ae0377a95..5d6679bd28 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/ChatDialog.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/ChatDialog.kt
@@ -28,8 +28,7 @@ class ChatMessage(
val username: String, // Username is the community/forum username
val message: String,
val timestamp: String = SimpleDateFormat("HH:mm", Locale.getDefault()).format(Date())
-) {
-}
+)
class ChatDialog(context: Context) : BottomSheetDialog(context) {
private lateinit var binding: DialogChatBinding
@@ -50,7 +49,8 @@ class ChatDialog(context: Context) : BottomSheetDialog(context) {
behavior.state = BottomSheetBehavior.STATE_EXPANDED
behavior.state = BottomSheetBehavior.STATE_EXPANDED
- behavior.skipCollapsed = context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
+ behavior.skipCollapsed =
+ context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
handler.post {
chatAdapter.notifyDataSetChanged()
@@ -133,10 +133,12 @@ class ChatAdapter(private val messages: List) :
fun bind(message: ChatMessage) {
binding.usernameText.text = message.nickname
binding.messageText.text = message.message
- binding.userIcon.setImageResource(when (message.nickname) {
- "System" -> R.drawable.ic_system
- else -> R.drawable.ic_user
- })
+ binding.userIcon.setImageResource(
+ when (message.nickname) {
+ "System" -> R.drawable.ic_system
+ else -> R.drawable.ic_user
+ }
+ )
}
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/LobbyBrowser.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/LobbyBrowser.kt
index 39391d6e53..57fd551e02 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/LobbyBrowser.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/LobbyBrowser.kt
@@ -220,7 +220,7 @@ class LobbyBrowser(context: Context) : BottomSheetDialog(context) {
val baseList = NetPlayManager.getPublicRooms()
val filteredList = baseList.filter { room ->
(!binding.chipHideFull.isChecked || room.members.size < room.maxPlayers) &&
- (!binding.chipHideEmpty.isChecked || room.members.isNotEmpty())
+ (!binding.chipHideEmpty.isChecked || room.members.isNotEmpty())
}
if (binding.searchText.text.toString().isEmpty() &&
@@ -245,7 +245,6 @@ class LobbyBrowser(context: Context) : BottomSheetDialog(context) {
it.score
}.map { it.item }
adapter.updateRooms(sortedList)
-
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/NetPlayDialog.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/NetPlayDialog.kt
index cd3e9a4474..99ab3ebab2 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/NetPlayDialog.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/NetPlayDialog.kt
@@ -16,7 +16,6 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.Button
-import android.widget.EditText
import android.widget.PopupMenu
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
@@ -38,7 +37,6 @@ import org.yuzu.yuzu_emu.network.NetDataValidators
import org.yuzu.yuzu_emu.network.NetPlayManager
import org.yuzu.yuzu_emu.utils.CompatUtils
import org.yuzu.yuzu_emu.utils.GameHelper
-import java.net.InetAddress
class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
private lateinit var adapter: NetPlayAdapter
@@ -55,7 +53,9 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
when {
- NetPlayManager.netPlayIsJoined() -> DialogMultiplayerLobbyBinding.inflate(layoutInflater)
+ NetPlayManager.netPlayIsJoined() -> DialogMultiplayerLobbyBinding.inflate(
+ layoutInflater
+ )
.apply {
setContentView(root)
adapter = NetPlayAdapter()
@@ -77,7 +77,6 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
btnModeration.setOnClickListener {
showModerationDialog()
}
-
}
else -> {
@@ -140,7 +139,8 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
inner class NetPlayAdapter : RecyclerView.Adapter() {
val netPlayItems = mutableListOf()
- abstract inner class NetPlayViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView),
+ abstract inner class NetPlayViewHolder(itemView: View) :
+ RecyclerView.ViewHolder(itemView),
View.OnClickListener {
init {
itemView.setOnClickListener(this)
@@ -167,7 +167,9 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
visibility = if (iconRes != 0) {
setImageResource(iconRes)
View.VISIBLE
- } else View.GONE
+ } else {
+ View.GONE
+ }
}
}
}
@@ -186,14 +188,13 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
override fun onClick(clicked: View) {}
-
private fun showPopupMenu(view: View) {
PopupMenu(view.context, view).apply {
menuInflater.inflate(R.menu.menu_netplay_member, menu)
menu.findItem(R.id.action_kick).isEnabled = isModerator &&
- netPlayItems.name != StringSetting.WEB_USERNAME.getString()
+ netPlayItems.name != StringSetting.WEB_USERNAME.getString()
menu.findItem(R.id.action_ban).isEnabled = isModerator &&
- netPlayItems.name != StringSetting.WEB_USERNAME.getString()
+ netPlayItems.name != StringSetting.WEB_USERNAME.getString()
setOnMenuItemClickListener { item ->
if (item.itemId == R.id.action_kick) {
NetPlayManager.netPlayKickUser(netPlayItems.name)
@@ -201,7 +202,9 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
} else if (item.itemId == R.id.action_ban) {
NetPlayManager.netPlayBanUser(netPlayItems.name)
true
- } else false
+ } else {
+ false
+ }
}
show()
}
@@ -360,12 +363,15 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
val visibilityList: List = listOf(
context.getString(R.string.multiplayer_public_visibility),
- context.getString(R.string.multiplayer_unlisted_visibility),
+ context.getString(R.string.multiplayer_unlisted_visibility)
)
binding.textTitle.text = activity.getString(
- if (isCreateRoom) R.string.multiplayer_create_room
- else R.string.multiplayer_join_room
+ if (isCreateRoom) {
+ R.string.multiplayer_create_room
+ } else {
+ R.string.multiplayer_join_room
+ }
)
// setup listeners etc
@@ -446,7 +452,9 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
)
}
- binding.dropdownLobbyVisibility.setText(context.getString(R.string.multiplayer_unlisted_visibility))
+ binding.dropdownLobbyVisibility.setText(
+ context.getString(R.string.multiplayer_unlisted_visibility)
+ )
binding.dropdownLobbyVisibility.apply {
setAdapter(
@@ -501,8 +509,11 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
binding.btnConfirm.isEnabled = false
binding.btnConfirm.text =
activity.getString(
- if (isCreateRoom) R.string.multiplayer_creating
- else R.string.multiplayer_joining
+ if (isCreateRoom) {
+ R.string.multiplayer_creating
+ } else {
+ R.string.multiplayer_joining
+ }
)
// We don't need to worry about validation because it's already been done.
@@ -546,8 +557,11 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
Toast.makeText(
YuzuApplication.appContext,
- if (isCreateRoom) R.string.multiplayer_create_room_success
- else R.string.multiplayer_join_room_success,
+ if (isCreateRoom) {
+ R.string.multiplayer_create_room_success
+ } else {
+ R.string.multiplayer_join_room_success
+ },
Toast.LENGTH_LONG
).show()
@@ -619,7 +633,9 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = ItemBanListBinding.inflate(
- LayoutInflater.from(parent.context), parent, false
+ LayoutInflater.from(parent.context),
+ parent,
+ false
)
return ViewHolder(binding)
}
@@ -654,6 +670,5 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
notifyItemRemoved(position)
}
}
-
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/fetcher/DriverGroupAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/fetcher/DriverGroupAdapter.kt
index 14d69cb384..1607371cf5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/fetcher/DriverGroupAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/fetcher/DriverGroupAdapter.kt
@@ -13,7 +13,6 @@ import org.yuzu.yuzu_emu.databinding.ItemDriverGroupBinding
import org.yuzu.yuzu_emu.fragments.DriverFetcherFragment.DriverGroup
import androidx.core.view.isVisible
import androidx.fragment.app.FragmentActivity
-import androidx.transition.AutoTransition
import androidx.transition.ChangeBounds
import androidx.transition.Fade
import androidx.transition.TransitionManager
@@ -89,7 +88,9 @@ class DriverGroupAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DriverGroupViewHolder {
val binding = ItemDriverGroupBinding.inflate(
- LayoutInflater.from(parent.context), parent, false
+ LayoutInflater.from(parent.context),
+ parent,
+ false
)
return DriverGroupViewHolder(binding)
}
@@ -105,4 +106,4 @@ class DriverGroupAdapter(
driverGroups = newDriverGroups
notifyDataSetChanged()
}
-}
\ No newline at end of file
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/fetcher/ReleaseAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/fetcher/ReleaseAdapter.kt
index 1dcec3c9f0..8efc5ecdea 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/fetcher/ReleaseAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/fetcher/ReleaseAdapter.kt
@@ -71,7 +71,7 @@ class ReleaseAdapter(
// truncates to 150 chars so it does not take up too much space.
var bodyPreview = release.body.take(150)
- bodyPreview = bodyPreview.replace("#", "").removeSurrounding(" ");
+ bodyPreview = bodyPreview.replace("#", "").removeSurrounding(" ")
val body =
bodyPreview.replace("\\r\\n", "\n").replace("\\n", "\n").replace("\n", "
")
@@ -122,8 +122,11 @@ class ReleaseAdapter(
binding.imageDownloadsArrow.rotation = if (isVisible) 0f else 180f
binding.buttonToggleDownloads.text =
- if (isVisible) activity.getString(R.string.show_downloads)
- else activity.getString(R.string.hide_downloads)
+ if (isVisible) {
+ activity.getString(R.string.show_downloads)
+ } else {
+ activity.getString(R.string.hide_downloads)
+ }
}
binding.buttonToggleDownloads.setOnClickListener {
@@ -139,9 +142,15 @@ class ReleaseAdapter(
release.artifacts.forEach { artifact ->
val button = MaterialButton(binding.root.context).apply {
text = artifact.name
- setTextAppearance(com.google.android.material.R.style.TextAppearance_Material3_LabelLarge)
+ setTextAppearance(
+ com.google.android.material.R.style.TextAppearance_Material3_LabelLarge
+ )
textAlignment = MaterialButton.TEXT_ALIGNMENT_VIEW_START
- setBackgroundColor(context.getColor(com.google.android.material.R.color.m3_button_background_color_selector))
+ setBackgroundColor(
+ context.getColor(
+ com.google.android.material.R.color.m3_button_background_color_selector
+ )
+ )
setIconResource(R.drawable.ic_import)
iconTint = ColorStateList.valueOf(
MaterialColors.getColor(
@@ -199,7 +208,9 @@ class ReleaseAdapter(
input.copyTo(output)
}
}
- ?: throw IOException(context.getString(R.string.empty_response_body))
+ ?: throw IOException(
+ context.getString(R.string.empty_response_body)
+ )
}
}
@@ -211,7 +222,9 @@ class ReleaseAdapter(
val driverData = GpuDriverHelper.getMetadataFromZip(file)
val driverPath =
- "${GpuDriverHelper.driverStoragePath}${FileUtil.getFilename(file.toUri())}"
+ "${GpuDriverHelper.driverStoragePath}${FileUtil.getFilename(
+ file.toUri()
+ )}"
if (GpuDriverHelper.copyDriverToInternalStorage(file.toUri())) {
driverViewModel.onDriverAdded(Pair(driverPath, driverData))
@@ -254,7 +267,9 @@ class ReleaseAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ReleaseViewHolder {
val binding = ItemReleaseBinding.inflate(
- LayoutInflater.from(parent.context), parent, false
+ LayoutInflater.from(parent.context),
+ parent,
+ false
)
return ReleaseViewHolder(binding)
}
@@ -264,4 +279,4 @@ class ReleaseAdapter(
}
override fun getItemCount(): Int = releases.size
-}
\ No newline at end of file
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/fetcher/SpacingItemDecoration.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/fetcher/SpacingItemDecoration.kt
index 8a8c025d8d..47afee1071 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/fetcher/SpacingItemDecoration.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/fetcher/SpacingItemDecoration.kt
@@ -16,4 +16,4 @@ class SpacingItemDecoration(private val spacing: Int) : RecyclerView.ItemDecorat
outRect.top = spacing
}
}
-}
\ No newline at end of file
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
index a558afab47..c31c534bfc 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
@@ -63,13 +63,12 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
SHOW_SHADERS_BUILDING("show_shaders_building"),
DEBUG_FLUSH_BY_LINE("flush_lines"),
- USE_LRU_CACHE("use_lru_cache"),;
+ USE_LRU_CACHE("use_lru_cache");
external fun isRaiiEnabled(): Boolean
// external fun isFrameSkippingEnabled(): Boolean
external fun isFrameInterpolationEnabled(): Boolean
-
override fun getBoolean(needsGlobal: Boolean): Boolean =
NativeConfig.getBoolean(key, needsGlobal)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
index a674857bc1..02950484ac 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
@@ -57,7 +57,7 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
OFFLINE_WEB_APPLET("offline_web_applet_mode"),
LOGIN_SHARE_APPLET("login_share_applet_mode"),
WIFI_WEB_AUTH_APPLET("wifi_web_auth_applet_mode"),
- MY_PAGE_APPLET("my_page_applet_mode"),
+ MY_PAGE_APPLET("my_page_applet_mode")
;
override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
index c7fb646e3a..55ddd5950c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
@@ -13,7 +13,7 @@ enum class StringSetting(override val key: String) : AbstractStringSetting {
DEVICE_NAME("device_name"),
WEB_TOKEN("eden_token"),
- WEB_USERNAME("eden_username"),
+ WEB_USERNAME("eden_username")
;
override fun getString(needsGlobal: Boolean): String = NativeConfig.getString(key, needsGlobal)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
index a269cab254..3124ddd480 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
@@ -21,7 +21,6 @@ import org.yuzu.yuzu_emu.features.settings.model.LongSetting
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.network.NetDataValidators
-import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.NativeConfig
/**
@@ -79,7 +78,7 @@ abstract class SettingsItem(
val needsRuntimeGlobal: Boolean
get() = NativeLibrary.isRunning() && !setting.global &&
- !NativeConfig.isPerGameConfigLoaded()
+ !NativeConfig.isPerGameConfigLoaded()
val clearable: Boolean
get() = !setting.global && NativeConfig.isPerGameConfigLoaded()
@@ -516,7 +515,6 @@ abstract class SettingsItem(
)
)
-
put(
SingleChoiceSetting(
IntSetting.RENDERER_VSYNC,
@@ -724,7 +722,7 @@ abstract class SettingsItem(
val fastmem = object : AbstractBooleanSetting {
override fun getBoolean(needsGlobal: Boolean): Boolean =
BooleanSetting.FASTMEM.getBoolean() &&
- BooleanSetting.FASTMEM_EXCLUSIVES.getBoolean()
+ BooleanSetting.FASTMEM_EXCLUSIVES.getBoolean()
override fun setBoolean(value: Boolean) {
BooleanSetting.FASTMEM.setBoolean(value)
@@ -739,7 +737,7 @@ abstract class SettingsItem(
override var global: Boolean
get() {
return BooleanSetting.FASTMEM.global &&
- BooleanSetting.FASTMEM_EXCLUSIVES.global
+ BooleanSetting.FASTMEM_EXCLUSIVES.global
}
set(value) {
BooleanSetting.FASTMEM.global = value
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
index aea72946f7..41b307869d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SingleChoiceSetting.kt
@@ -18,7 +18,7 @@ class SingleChoiceSetting(
@ArrayRes val choicesId: Int,
@ArrayRes val valuesId: Int,
val warnChoices: List = ArrayList(),
- @StringRes val warningMessage: Int = 0,
+ @StringRes val warningMessage: Int = 0
) : SettingsItem(setting, titleId, titleString, descriptionId, descriptionString) {
override val type = TYPE_SINGLE_CHOICE
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringInputSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringInputSetting.kt
index 85076e680d..1d6de233b8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringInputSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/StringInputSetting.kt
@@ -6,7 +6,6 @@
package org.yuzu.yuzu_emu.features.settings.model.view
-import android.text.Editable
import androidx.annotation.StringRes
import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt
index aa17d05e34..dc9f561eca 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt
@@ -179,7 +179,13 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
override fun afterTextChanged(s: Editable?) {
val isValid = validator(s.toString())
stringInputBinding.editTextLayout.isErrorEnabled = !isValid
- stringInputBinding.editTextLayout.error = if (isValid) null else requireContext().getString(item.errorId)
+ stringInputBinding.editTextLayout.error = if (isValid) {
+ null
+ } else {
+ requireContext().getString(
+ item.errorId
+ )
+ }
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
index bac90eb4ef..3813a3e827 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
@@ -8,7 +8,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.core.content.edit
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
@@ -16,13 +15,11 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs
-import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
import org.yuzu.yuzu_emu.features.input.NativeInput
-import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
index 31e2873b58..182dd0b9db 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -4,8 +4,6 @@
package org.yuzu.yuzu_emu.features.settings.ui
import android.annotation.SuppressLint
-import android.app.Activity
-import android.app.AlertDialog
import android.os.Build
import android.widget.Toast
import androidx.preference.PreferenceManager
@@ -1056,7 +1054,9 @@ class SettingsFragmentPresenter(
}
val staticThemeColor: AbstractIntSetting = object : AbstractIntSetting {
- val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
+ val preferences = PreferenceManager.getDefaultSharedPreferences(
+ YuzuApplication.appContext
+ )
override fun getInt(needsGlobal: Boolean): Int =
preferences.getInt(Settings.PREF_STATIC_THEME_COLOR, 0)
override fun setInt(value: Int) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
index 2879310007..0ec9984607 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
@@ -1,7 +1,6 @@
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
-
package org.yuzu.yuzu_emu.fragments
import android.content.ClipData
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddGameFolderDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddGameFolderDialogFragment.kt
index cc95544d25..e2cb5f600d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddGameFolderDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddGameFolderDialogFragment.kt
@@ -33,7 +33,10 @@ class AddGameFolderDialogFragment : DialogFragment() {
.setPositiveButton(android.R.string.ok) { _: DialogInterface, _: Int ->
val newGameDir = GameDir(folderUriString!!, binding.deepScanSwitch.isChecked)
homeViewModel.setGamesDirSelected(true)
- val calledFromGameFragment = requireArguments().getBoolean("calledFromGameFragment", false)
+ val calledFromGameFragment = requireArguments().getBoolean(
+ "calledFromGameFragment",
+ false
+ )
gamesViewModel.addFolder(newGameDir, calledFromGameFragment)
}
.setNegativeButton(android.R.string.cancel, null)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverFetcherFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverFetcherFragment.kt
index 91670b207d..b8d0f2197e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverFetcherFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverFetcherFragment.kt
@@ -62,14 +62,14 @@ class DriverFetcherFragment : Fragment() {
val path: String = "",
val sort: Int = 0,
val useTagName: Boolean = false,
- val sortMode: SortMode = SortMode.Default,
+ val sortMode: SortMode = SortMode.Default
)
private val repoList: List = listOf(
DriverRepo("Mr. Purple Turnip", "MrPurple666/purple-turnip", 0),
DriverRepo("GameHub Adreno 8xx", "crueter/GameHub-8Elite-Drivers", 1),
DriverRepo("KIMCHI Turnip", "K11MCH1/AdrenoToolsDrivers", 2, true, SortMode.PublishTime),
- DriverRepo("Weab-Chan Freedreno", "Weab-chan/freedreno_turnip-CI", 3),
+ DriverRepo("Weab-Chan Freedreno", "Weab-chan/freedreno_turnip-CI", 3)
)
private val driverMap = listOf(
@@ -81,7 +81,7 @@ class DriverFetcherFragment : Fragment() {
IntRange(700, 710) to "KIMCHI 25.2.0_r5",
IntRange(711, 799) to "Mr. Purple T21",
IntRange(800, 899) to "GameHub Adreno 8xx",
- IntRange(900, Int.MAX_VALUE) to "Unsupported",
+ IntRange(900, Int.MAX_VALUE) to "Unsupported"
)
private lateinit var driverGroupAdapter: DriverGroupAdapter
@@ -124,7 +124,9 @@ class DriverFetcherFragment : Fragment() {
}
override fun onCreateView(
- inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
): View {
_binding = FragmentDriverFetcherBinding.inflate(inflater)
binding.badgeRecommendedDriver.text = recommendedDriver
@@ -178,8 +180,12 @@ class DriverFetcherFragment : Fragment() {
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
- MaterialAlertDialogBuilder(requireActivity()).setTitle(getString(R.string.error_during_fetch))
- .setMessage("${getString(R.string.failed_to_fetch)} ${name}:\n${e.message}")
+ MaterialAlertDialogBuilder(requireActivity()).setTitle(
+ getString(R.string.error_during_fetch)
+ )
+ .setMessage(
+ "${getString(R.string.failed_to_fetch)} $name:\n${e.message}"
+ )
.setPositiveButton(getString(R.string.ok)) { dialog, _ -> dialog.cancel() }
.show()
@@ -188,7 +194,9 @@ class DriverFetcherFragment : Fragment() {
}
val group = DriverGroup(
- name, releases, sort
+ name,
+ releases,
+ sort
)
synchronized(driverGroups) {
@@ -223,7 +231,9 @@ class DriverFetcherFragment : Fragment() {
binding.listDrivers.updateMargins(left = leftInsets, right = rightInsets)
binding.listDrivers.updatePadding(
- bottom = barInsets.bottom + resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab)
+ bottom = barInsets.bottom + resources.getDimensionPixelSize(
+ R.dimen.spacing_bottom_list_fab
+ )
)
windowInsets
@@ -239,11 +249,13 @@ class DriverFetcherFragment : Fragment() {
var artifacts: List = ArrayList(),
var prerelease: Boolean = false,
var latest: Boolean = false,
- var publishTime: LocalDateTime = LocalDateTime.now(),
+ var publishTime: LocalDateTime = LocalDateTime.now()
) {
companion object {
fun fromJsonArray(
- jsonString: String, useTagName: Boolean, sortMode: SortMode
+ jsonString: String,
+ useTagName: Boolean,
+ sortMode: SortMode
): ArrayList {
val mapper = jacksonObjectMapper()
@@ -310,7 +322,16 @@ class DriverFetcherFragment : Fragment() {
}
}
- return Release(tagName, titleName, title, body, artifacts, prerelease, false, localTime)
+ return Release(
+ tagName,
+ titleName,
+ title,
+ body,
+ artifacts,
+ prerelease,
+ false,
+ localTime
+ )
} catch (e: Exception) {
// TODO: handle malformed input.
e.printStackTrace()
@@ -324,6 +345,6 @@ class DriverFetcherFragment : Fragment() {
data class DriverGroup(
val name: String,
val releases: ArrayList,
- val sort: Int,
+ val sort: Int
)
-}
\ No newline at end of file
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
index 47b84a403a..f521343272 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
@@ -25,7 +25,6 @@ import kotlinx.coroutines.withContext
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.adapters.DriverAdapter
import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding
-import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver
@@ -108,7 +107,9 @@ class DriverManagerFragment : Fragment() {
}
binding.buttonFetch.setOnClickListener {
- binding.root.findNavController().navigate(R.id.action_driverManagerFragment_to_driverFetcherFragment)
+ binding.root.findNavController().navigate(
+ R.id.action_driverManagerFragment_to_driverFetcherFragment
+ )
}
binding.listDrivers.apply {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index 6cce31a4eb..a7e564c179 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -45,6 +45,7 @@ import androidx.drawerlayout.widget.DrawerLayout
import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController
import androidx.navigation.fragment.navArgs
import androidx.window.layout.FoldingFeature
@@ -52,7 +53,6 @@ import androidx.window.layout.WindowInfoTracker
import androidx.window.layout.WindowLayoutInfo
import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import com.google.android.material.slider.Slider
import com.google.android.material.textview.MaterialTextView
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
@@ -81,6 +81,13 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.collect
+import org.yuzu.yuzu_emu.utils.CustomSettingsHandler
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
import java.io.File
class EmulationFragment : Fragment(), SurfaceHolder.Callback {
@@ -90,24 +97,29 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private var perfStatsUpdater: (() -> Unit)? = null
private var socUpdater: (() -> Unit)? = null
- private lateinit var cpuBackend: String
- private lateinit var gpuDriver: String
-
private var _binding: FragmentEmulationBinding? = null
+
private val binding get() = _binding!!
private val args by navArgs()
- private lateinit var game: Game
+ private var game: Game? = null
private val emulationViewModel: EmulationViewModel by activityViewModels()
private val driverViewModel: DriverViewModel by activityViewModels()
private var isInFoldableLayout = false
+ private var emulationStarted = false
private lateinit var gpuModel: String
private lateinit var fwVersion: String
+ private var intentGame: Game? = null
+ private var isCustomSettingsIntent = false
+
+ private var perfStatsRunnable: Runnable? = null
+ private var socRunnable: Runnable? = null
+
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is EmulationActivity) {
@@ -125,9 +137,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
super.onCreate(savedInstanceState)
updateOrientation()
- val intentUri: Uri? = requireActivity().intent.data
- var intentGame: Game? = null
- if (intentUri != null) {
+ val intent = requireActivity().intent
+ val intentUri: Uri? = intent.data
+ intentGame = null
+ isCustomSettingsIntent = false
+
+ if (intent.action == CustomSettingsHandler.CUSTOM_CONFIG_ACTION) {
+ handleEmuReadyIntent(intent)
+ return
+ } else if (intentUri != null) {
intentGame = if (Game.extensions.contains(FileUtil.getExtension(intentUri))) {
GameHelper.getGame(requireActivity().intent.data!!, false)
} else {
@@ -135,38 +153,308 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
+ finishGameSetup()
+ }
+
+ /**
+ * Complete the game setup process (extracted for async custom settings handling)
+ */
+ private fun finishGameSetup() {
try {
- game = if (args.game != null) {
- args.game!!
- } else {
- intentGame!!
+ val gameToUse = args.game ?: intentGame
+
+ if (gameToUse == null) {
+ Log.error("[EmulationFragment] No game found in arguments or intent")
+ Toast.makeText(
+ requireContext(),
+ R.string.no_game_present,
+ Toast.LENGTH_SHORT
+ ).show()
+ requireActivity().finish()
+ return
}
- } catch (e: NullPointerException) {
+
+ game = gameToUse
+
+ } catch (e: Exception) {
+ Log.error("[EmulationFragment] Error during game setup: ${e.message}")
Toast.makeText(
requireContext(),
- R.string.no_game_present,
+ "Setup error: ${e.message?.take(30) ?: "Unknown"}",
Toast.LENGTH_SHORT
).show()
requireActivity().finish()
return
}
- // Always load custom settings when launching a game from an intent
- if (args.custom || intentGame != null) {
- SettingsFile.loadCustomConfig(game)
- NativeConfig.unloadPerGameConfig()
- } else {
- NativeConfig.reloadGlobalConfig()
+ try {
+ if (isCustomSettingsIntent) {
+ Log.info("[EmulationFragment] Using custom settings from intent")
+ } else if (intentGame != null && game != null) {
+ val customConfigFile = SettingsFile.getCustomSettingsFile(game!!)
+ if (customConfigFile.exists()) {
+ Log.info(
+ "[EmulationFragment] Found existing custom settings for ${game!!.title}, loading them"
+ )
+ SettingsFile.loadCustomConfig(game!!)
+ } else {
+ Log.info(
+ "[EmulationFragment] No custom settings found for ${game!!.title}, using global settings"
+ )
+ NativeConfig.reloadGlobalConfig()
+ }
+ } else {
+ val isCustomFromArgs = if (game != null && game == args.game) {
+ try {
+ args.custom
+ } catch (e: Exception) {
+ false
+ }
+ } else {
+ false
+ }
+
+ if (isCustomFromArgs && game != null) {
+ SettingsFile.loadCustomConfig(game!!)
+ Log.info("[EmulationFragment] Loading custom settings for ${game!!.title}")
+ } else {
+ Log.info("[EmulationFragment] Using global settings")
+ NativeConfig.reloadGlobalConfig()
+ }
+ }
+ } catch (e: Exception) {
+ Log.error("[EmulationFragment] Error loading configuration: ${e.message}")
+ Log.info("[EmulationFragment] Falling back to global settings")
+ try {
+ NativeConfig.reloadGlobalConfig()
+ } catch (fallbackException: Exception) {
+ Log.error(
+ "[EmulationFragment] Critical error: could not load global config: ${fallbackException.message}"
+ )
+ throw fallbackException
+ }
}
- // Install the selected driver asynchronously as the game starts
- driverViewModel.onLaunchGame()
-
- // So this fragment doesn't restart on configuration changes; i.e. rotation.
- retainInstance = true
- emulationState = EmulationState(game.path) {
+ emulationState = EmulationState(game!!.path) {
return@EmulationState driverViewModel.isInteractionAllowed.value
}
+
+ }
+
+ /**
+ * Handle EmuReady intent for launching games with or without custom settings
+ */
+ private fun handleEmuReadyIntent(intent: Intent) {
+ val titleId = intent.getStringExtra(CustomSettingsHandler.EXTRA_TITLE_ID)
+ val customSettings = intent.getStringExtra(CustomSettingsHandler.EXTRA_CUSTOM_SETTINGS)
+
+ if (titleId != null) {
+ Log.info("[EmulationFragment] Received EmuReady intent for title: $titleId")
+
+ lifecycleScope.launch {
+ try {
+ Toast.makeText(
+ requireContext(),
+ getString(R.string.searching_for_game),
+ Toast.LENGTH_SHORT
+ ).show()
+ val foundGame = CustomSettingsHandler.findGameByTitleId(
+ titleId,
+ requireContext()
+ )
+ if (foundGame == null) {
+ Log.error("[EmulationFragment] Game not found for title ID: $titleId")
+ Toast.makeText(
+ requireContext(),
+ getString(R.string.game_not_found_for_title_id, titleId),
+ Toast.LENGTH_LONG
+ ).show()
+ requireActivity().finish()
+ return@launch
+ }
+
+ val shouldLaunch = showLaunchConfirmationDialog(
+ foundGame.title,
+ customSettings != null
+ )
+ if (!shouldLaunch) {
+ Log.info("[EmulationFragment] User cancelled EmuReady launch")
+ requireActivity().finish()
+ return@launch
+ }
+
+ if (customSettings != null) {
+ intentGame = CustomSettingsHandler.applyCustomSettingsWithDriverCheck(
+ titleId,
+ customSettings,
+ requireContext(),
+ requireActivity(),
+ driverViewModel
+ )
+
+ if (intentGame == null) {
+ Log.error(
+ "[EmulationFragment] Custom settings processing failed for title ID: $titleId"
+ )
+ Toast.makeText(
+ requireContext(),
+ getString(R.string.custom_settings_failed_title),
+ Toast.LENGTH_SHORT
+ ).show()
+
+ val launchWithDefault = askUserToLaunchWithDefaultSettings(
+ foundGame.title,
+ "This could be due to:\n• User cancelled configuration overwrite\n• Driver installation failed\n• Missing required drivers"
+ )
+
+ if (launchWithDefault) {
+ Log.info(
+ "[EmulationFragment] User chose to launch with default settings"
+ )
+ Toast.makeText(
+ requireContext(),
+ getString(R.string.launch_with_default_settings),
+ Toast.LENGTH_SHORT
+ ).show()
+ intentGame = foundGame
+ isCustomSettingsIntent = false
+ } else {
+ Log.info(
+ "[EmulationFragment] User cancelled launch after custom settings failure"
+ )
+ Toast.makeText(
+ requireContext(),
+ getString(R.string.launch_cancelled),
+ Toast.LENGTH_SHORT
+ ).show()
+ requireActivity().finish()
+ return@launch
+ }
+ } else {
+ isCustomSettingsIntent = true
+ }
+ } else {
+ Log.info("[EmulationFragment] Launching game with default settings")
+
+ val customConfigFile = SettingsFile.getCustomSettingsFile(foundGame)
+ if (customConfigFile.exists()) {
+ Log.info("[EmulationFragment] Found existing custom settings for ${foundGame.title}, loading them")
+ SettingsFile.loadCustomConfig(foundGame)
+ } else {
+ Log.info("[EmulationFragment] No custom settings found for ${foundGame.title}, using global settings")
+ }
+
+ Toast.makeText(
+ requireContext(),
+ getString(R.string.launching_game, foundGame.title),
+ Toast.LENGTH_SHORT
+ ).show()
+ intentGame = foundGame
+ isCustomSettingsIntent = false
+ }
+
+ if (intentGame != null) {
+ withContext(Dispatchers.Main) {
+ try {
+ finishGameSetup()
+ Log.info("[EmulationFragment] Game setup complete for intent launch")
+
+ if (_binding != null) {
+ completeViewSetup()
+
+ val driverReady = driverViewModel.isInteractionAllowed.value
+ if (driverReady && !NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
+ Log.info("[EmulationFragment] Starting emulation after async intent setup - driver ready")
+ startEmulation()
+ }
+ }
+ } catch (e: Exception) {
+ Log.error("[EmulationFragment] Error in finishGameSetup: ${e.message}")
+ requireActivity().finish()
+ return@withContext
+ }
+ }
+ } else {
+ Log.error("[EmulationFragment] No valid game found after processing intent")
+ Toast.makeText(
+ requireContext(),
+ getString(R.string.failed_to_initialize_game),
+ Toast.LENGTH_SHORT
+ ).show()
+ requireActivity().finish()
+ }
+ } catch (e: Exception) {
+ Log.error("[EmulationFragment] Error processing EmuReady intent: ${e.message}")
+ Toast.makeText(
+ requireContext(),
+ "Error: ${e.message?.take(50) ?: "Unknown error"}",
+ Toast.LENGTH_LONG
+ ).show()
+ requireActivity().finish()
+ }
+ }
+ } else {
+ Log.error("[EmulationFragment] EmuReady intent missing title_id")
+ Toast.makeText(
+ requireContext(),
+ "Invalid request: missing title ID",
+ Toast.LENGTH_SHORT
+ ).show()
+ requireActivity().finish()
+ }
+ }
+
+ /**
+ * Show confirmation dialog for EmuReady game launches
+ */
+ private suspend fun showLaunchConfirmationDialog(gameTitle: String, hasCustomSettings: Boolean): Boolean {
+ return suspendCoroutine { continuation ->
+ requireActivity().runOnUiThread {
+ val message = if (hasCustomSettings) {
+ getString(
+ R.string.custom_intent_launch_message_with_settings,
+ gameTitle
+ )
+ } else {
+ getString(R.string.custom_intent_launch_message, gameTitle)
+ }
+
+ MaterialAlertDialogBuilder(requireContext())
+ .setTitle(getString(R.string.custom_intent_launch_title))
+ .setMessage(message)
+ .setPositiveButton(getString(R.string.launch)) { _, _ ->
+ continuation.resume(true)
+ }
+ .setNegativeButton(getString(R.string.cancel)) { _, _ ->
+ continuation.resume(false)
+ }
+ .setCancelable(false)
+ .show()
+ }
+ }
+ }
+
+ /**
+ * Ask user if they want to launch with default settings when custom settings fail
+ */
+ private suspend fun askUserToLaunchWithDefaultSettings(gameTitle: String, errorMessage: String): Boolean {
+ return suspendCoroutine { continuation ->
+ requireActivity().runOnUiThread {
+ MaterialAlertDialogBuilder(requireContext())
+ .setTitle(getString(R.string.custom_settings_failed_title))
+ .setMessage(
+ getString(R.string.custom_settings_failed_message, gameTitle, errorMessage)
+ )
+ .setPositiveButton(getString(R.string.launch_with_default_settings)) { _, _ ->
+ continuation.resume(true)
+ }
+ .setNegativeButton(getString(R.string.cancel)) { _, _ ->
+ continuation.resume(false)
+ }
+ .setCancelable(false)
+ .show()
+ }
+ }
}
/**
@@ -187,6 +475,20 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
return
}
+ if (game == null) {
+ Log.warning("[EmulationFragment] Game not yet initialized in onViewCreated - will be set up by async intent handler")
+ return
+ }
+
+ completeViewSetup()
+ }
+
+ private fun completeViewSetup() {
+ if (_binding == null || game == null) {
+ return
+ }
+ Log.info("[EmulationFragment] Starting view setup for game: ${game?.title}")
+
gpuModel = GpuDriverHelper.getGpuModel().toString()
fwVersion = NativeLibrary.firmwareVersion()
@@ -223,10 +525,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
})
binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
- binding.inGameMenu.getHeaderView(0).apply {
- val titleView = findViewById(R.id.text_game_title)
- titleView.text = game.title
- }
+
+ updateGameTitle()
binding.inGameMenu.menu.findItem(R.id.menu_lock_drawer).apply {
val lockMode = IntSetting.LOCK_DRAWER.getInt()
@@ -293,13 +593,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
true
}
-
R.id.menu_multiplayer -> {
emulationActivity?.displayMultiplayerDialog()
true
}
-
R.id.menu_controls -> {
val action = HomeNavigationDirections.actionGlobalSettingsActivity(
null,
@@ -368,8 +666,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
)
- GameIconUtils.loadGameIcon(game, binding.loadingImage)
- binding.loadingTitle.text = game.title
+ GameIconUtils.loadGameIcon(game!!, binding.loadingImage)
+ binding.loadingTitle.text = game!!.title
binding.loadingTitle.isSelected = true
binding.loadingText.isSelected = true
@@ -408,7 +706,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
emulationState.updateSurface()
- // Setup overlays
updateShowStatsOverlay()
updateSocOverlay()
@@ -418,7 +715,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
val cpuBackendLabel = findViewById(R.id.cpu_backend)
val vendorLabel = findViewById(R.id.gpu_vendor)
- titleView.text = game.title
+ titleView.text = game?.title ?: ""
cpuBackendLabel.text = NativeLibrary.getCpuBackend()
vendorLabel.text = NativeLibrary.getGpuDriver()
}
@@ -456,16 +753,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
ViewUtils.showView(binding.loadingIndicator)
}
}
- emulationViewModel.emulationStopped.collect(viewLifecycleOwner) {
- if (it && emulationViewModel.programChanged.value != -1) {
- if (perfStatsUpdater != null) {
- perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
- }
- if (socUpdater != null) {
- socUpdateHandler.removeCallbacks(socUpdater!!)
+ emulationViewModel.emulationStopped.collect(viewLifecycleOwner) { stopped ->
+ if (stopped && emulationViewModel.programChanged.value != -1) {
+ perfStatsRunnable?.let { runnable ->
+ perfStatsUpdateHandler.removeCallbacks(
+ runnable
+ )
}
-
+ socRunnable?.let { runnable -> socUpdateHandler.removeCallbacks(runnable) }
emulationState.changeProgram(emulationViewModel.programChanged.value)
emulationViewModel.setProgramChanged(-1)
emulationViewModel.setEmulationStopped(false)
@@ -473,7 +769,19 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) {
- if (it) startEmulation()
+ Log.info("[EmulationFragment] Driver interaction allowed: $it")
+ if (it && !NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
+ startEmulation()
+ }
+ }
+
+ driverViewModel.onLaunchGame()
+
+ val currentDriverState = driverViewModel.isInteractionAllowed.value
+ Log.info("[EmulationFragment] Checking initial driver state after onLaunchGame: $currentDriverState")
+ if (currentDriverState && !NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
+ Log.info("[EmulationFragment] Starting emulation immediately - driver already ready")
+ startEmulation()
}
}
@@ -485,6 +793,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
updateScreenLayout()
+ Log.info("[EmulationFragment] Calling emulationState.run() - surface will start emulation when available")
emulationState.run(emulationActivity!!.isActivityRecreated, programIndex)
}
}
@@ -518,6 +827,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
+ private fun updateGameTitle() {
+ game?.let {
+ binding.inGameMenu.getHeaderView(0).apply {
+ val titleView = findViewById(R.id.text_game_title)
+ titleView.text = it.title
+ }
+ }
+ }
+
override fun onPause() {
if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) {
emulationState.pause()
@@ -634,7 +952,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
val batteryTemp = getBatteryTemperature()
when (IntSetting.BAT_TEMPERATURE_UNIT.getInt(needsGlobal)) {
0 -> sb.append(String.format("%.1f°C", batteryTemp))
- 1 -> sb.append(String.format("%.1f°F", celsiusToFahrenheit(batteryTemp)))
+ 1 -> sb.append(
+ String.format(
+ "%.1f°F",
+ celsiusToFahrenheit(batteryTemp)
+ )
+ )
}
}
@@ -643,8 +966,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
val battery: BatteryManager =
requireContext().getSystemService(Context.BATTERY_SERVICE) as BatteryManager
- val batteryIntent = requireContext().registerReceiver(null,
- IntentFilter(Intent.ACTION_BATTERY_CHANGED))
+ val batteryIntent = requireContext().registerReceiver(
+ null,
+ IntentFilter(Intent.ACTION_BATTERY_CHANGED)
+ )
val capacity = battery.getIntProperty(BATTERY_PROPERTY_CAPACITY)
val nowUAmps = battery.getIntProperty(BATTERY_PROPERTY_CURRENT_NOW)
@@ -653,7 +978,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
val status = batteryIntent?.getIntExtra(BatteryManager.EXTRA_STATUS, -1)
val isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
- status == BatteryManager.BATTERY_STATUS_FULL
+ status == BatteryManager.BATTERY_STATUS_FULL
if (isCharging) {
sb.append(" ${getString(R.string.charging)}")
@@ -671,20 +996,21 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
if (BooleanSetting.PERF_OVERLAY_BACKGROUND.getBoolean(needsGlobal)) {
- binding.showStatsOverlayText.setBackgroundResource(R.color.yuzu_transparent_black)
+ binding.showStatsOverlayText.setBackgroundResource(
+ R.color.yuzu_transparent_black
+ )
} else {
binding.showStatsOverlayText.setBackgroundResource(0)
}
binding.showStatsOverlayText.text = sb.toString()
}
- perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
+ perfStatsUpdateHandler.postDelayed(perfStatsRunnable!!, 800)
}
- perfStatsUpdateHandler.post(perfStatsUpdater!!)
+ perfStatsRunnable = Runnable { perfStatsUpdater?.invoke() }
+ perfStatsUpdateHandler.post(perfStatsRunnable!!)
} else {
- if (perfStatsUpdater != null) {
- perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
- }
+ perfStatsRunnable?.let { perfStatsUpdateHandler.removeCallbacks(it) }
}
}
@@ -767,47 +1093,62 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
) {
sb.setLength(0)
- if (BooleanSetting.SHOW_DEVICE_MODEL.getBoolean(NativeConfig.isPerGameConfigLoaded())) {
+ if (BooleanSetting.SHOW_DEVICE_MODEL.getBoolean(
+ NativeConfig.isPerGameConfigLoaded()
+ )
+ ) {
sb.append(Build.MODEL)
}
- if (BooleanSetting.SHOW_GPU_MODEL.getBoolean(NativeConfig.isPerGameConfigLoaded())) {
+ if (BooleanSetting.SHOW_GPU_MODEL.getBoolean(
+ NativeConfig.isPerGameConfigLoaded()
+ )
+ ) {
if (sb.isNotEmpty()) sb.append(" | ")
sb.append(gpuModel)
}
if (Build.VERSION.SDK_INT >= 31) {
- if (BooleanSetting.SHOW_SOC_MODEL.getBoolean(NativeConfig.isPerGameConfigLoaded())) {
+ if (BooleanSetting.SHOW_SOC_MODEL.getBoolean(
+ NativeConfig.isPerGameConfigLoaded()
+ )
+ ) {
if (sb.isNotEmpty()) sb.append(" | ")
sb.append(Build.SOC_MODEL)
}
}
- if (BooleanSetting.SHOW_FW_VERSION.getBoolean(NativeConfig.isPerGameConfigLoaded())) {
+ if (BooleanSetting.SHOW_FW_VERSION.getBoolean(
+ NativeConfig.isPerGameConfigLoaded()
+ )
+ ) {
if (sb.isNotEmpty()) sb.append(" | ")
sb.append(fwVersion)
}
binding.showSocOverlayText.text = sb.toString()
- if (BooleanSetting.SOC_OVERLAY_BACKGROUND.getBoolean(NativeConfig.isPerGameConfigLoaded())) {
- binding.showSocOverlayText.setBackgroundResource(R.color.yuzu_transparent_black)
+ if (BooleanSetting.SOC_OVERLAY_BACKGROUND.getBoolean(
+ NativeConfig.isPerGameConfigLoaded()
+ )
+ ) {
+ binding.showSocOverlayText.setBackgroundResource(
+ R.color.yuzu_transparent_black
+ )
} else {
binding.showSocOverlayText.setBackgroundResource(0)
}
}
- socUpdateHandler.postDelayed(socUpdater!!, 1000)
+ socUpdateHandler.postDelayed(socRunnable!!, 1000)
}
- socUpdateHandler.post(socUpdater!!)
+ socRunnable = Runnable { socUpdater?.invoke() }
+ socUpdateHandler.post(socRunnable!!)
} else {
- if (socUpdater != null) {
- socUpdateHandler.removeCallbacks(socUpdater!!)
- }
+ socRunnable?.let { socUpdateHandler.removeCallbacks(it) }
}
}
-
@SuppressLint("SourceLockedOrientationActivity")
private fun updateOrientation() {
emulationActivity?.let {
@@ -919,11 +1260,19 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
Log.debug("[EmulationFragment] Surface changed. Resolution: " + width + "x" + height)
- emulationState.newSurface(holder.surface)
+ if (!emulationStarted) {
+ Log.info("[EmulationFragment] Starting emulation")
+ emulationStarted = true
+ emulationState.newSurface(holder.surface)
+ } else {
+ Log.debug("[EmulationFragment] Emulation already started, updating surface")
+ emulationState.newSurface(holder.surface)
+ }
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
emulationState.clearSurface()
+ emulationStarted = false
}
private fun showOverlayOptions() {
@@ -1096,22 +1445,18 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
inputScaleSlider.apply {
valueTo = 150F
value = IntSetting.OVERLAY_SCALE.getInt().toFloat()
- addOnChangeListener(
- Slider.OnChangeListener { _, value, _ ->
- inputScaleValue.text = "${value.toInt()}%"
- setControlScale(value.toInt())
- }
- )
+ addOnChangeListener { _, value, _ ->
+ inputScaleValue.text = "${value.toInt()}%"
+ setControlScale(value.toInt())
+ }
}
inputOpacitySlider.apply {
valueTo = 100F
value = IntSetting.OVERLAY_OPACITY.getInt().toFloat()
- addOnChangeListener(
- Slider.OnChangeListener { _, value, _ ->
- inputOpacityValue.text = "${value.toInt()}%"
- setControlOpacity(value.toInt())
- }
- )
+ addOnChangeListener { _, value, _ ->
+ inputOpacityValue.text = "${value.toInt()}%"
+ setControlOpacity(value.toInt())
+ }
}
inputScaleValue.text = "${inputScaleSlider.value.toInt()}%"
inputOpacityValue.text = "${inputOpacitySlider.value.toInt()}%"
@@ -1147,7 +1492,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
val cutInsets: Insets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
var left = 0
var right = 0
- if (ViewCompat.getLayoutDirection(v) == ViewCompat.LAYOUT_DIRECTION_LTR) {
+ if (v.layoutDirection == View.LAYOUT_DIRECTION_LTR) {
left = cutInsets.left
} else {
right = cutInsets.right
@@ -1168,7 +1513,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
lateinit var emulationThread: Thread
init {
- // Starting state is stopped.
state = State.STOPPED
}
@@ -1176,7 +1520,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
val isStopped: Boolean
get() = state == State.STOPPED
- // Getters for the current state
@get:Synchronized
val isPaused: Boolean
get() = state == State.PAUSED
@@ -1196,7 +1539,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
- // State changing methods
@Synchronized
fun pause() {
if (state != State.PAUSED) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
index 5fed99e0b0..5763f3120f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
@@ -27,7 +27,6 @@ import androidx.navigation.findNavController
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
-import org.yuzu.yuzu_emu.BuildConfig
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
@@ -35,15 +34,14 @@ import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter
import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
import org.yuzu.yuzu_emu.features.DocumentProvider
+import org.yuzu.yuzu_emu.features.fetcher.SpacingItemDecoration
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.model.DriverViewModel
import org.yuzu.yuzu_emu.model.HomeSetting
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.FileUtil
-import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.Log
-import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
class HomeSettingsFragment : Fragment() {
private var _binding: FragmentHomeSettingsBinding? = null
@@ -112,7 +110,7 @@ class HomeSettingsFragment : Fragment() {
.actionHomeSettingsFragmentToDriverManagerFragment(null)
binding.root.findNavController().navigate(action)
},
- {true},
+ { true },
R.string.custom_driver_not_supported,
R.string.custom_driver_not_supported_description,
driverViewModel.selectedDriverTitle
@@ -125,7 +123,7 @@ class HomeSettingsFragment : Fragment() {
R.drawable.ic_two_users,
{
mainActivity.displayMultiplayerDialog()
- },
+ }
)
)
add(
@@ -254,6 +252,8 @@ class HomeSettingsFragment : Fragment() {
viewLifecycleOwner,
optionsList
)
+ val spacing = resources.getDimensionPixelSize(R.dimen.spacing_small)
+ addItemDecoration(SpacingItemDecoration(spacing))
}
setInsets()
@@ -403,7 +403,7 @@ class HomeSettingsFragment : Fragment() {
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
binding.scrollViewSettings.updatePadding(
- top = barInsets.top,
+ top = barInsets.top
)
binding.homeSettingsList.updatePadding(
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
index 33146c82ca..c02411d1bb 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
@@ -134,10 +134,10 @@ class InstallableFragment : Fragment() {
install = { mainActivity.getFirmware.launch(arrayOf("application/zip")) }
),
Installable(
- R.string.uninstall_firmware,
- R.string.uninstall_firmware_description,
- install = { mainActivity.uninstallFirmware() }
- ),
+ R.string.uninstall_firmware,
+ R.string.uninstall_firmware_description,
+ install = { mainActivity.uninstallFirmware() }
+ ),
Installable(
R.string.install_prod_keys,
R.string.install_prod_keys_description,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt
index 52d9ef43d4..aa18aa2482 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/LicensesFragment.kt
@@ -1,7 +1,6 @@
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
-
package org.yuzu.yuzu_emu.fragments
import android.os.Bundle
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
index 61797f75f5..b7c75c127f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
@@ -78,7 +78,6 @@ class SetupFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
mainActivity = requireActivity() as MainActivity
-
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
object : OnBackPressedCallback(true) {
@@ -129,7 +128,7 @@ class SetupFragment : Fragment() {
0,
{
if (NotificationManagerCompat.from(requireContext())
- .areNotificationsEnabled()
+ .areNotificationsEnabled()
) {
StepState.COMPLETE
} else {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/MidScreenSwipeRefreshLayout.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/MidScreenSwipeRefreshLayout.kt
index 649bea9d54..35d027567e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/MidScreenSwipeRefreshLayout.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/layout/MidScreenSwipeRefreshLayout.kt
@@ -22,7 +22,11 @@ class MidScreenSwipeRefreshLayout @JvmOverloads constructor(
MotionEvent.ACTION_DOWN -> {
startX = ev.x
val width = width
- val center_fraction = resources.getFraction(R.fraction.carousel_midscreenswipe_width_fraction, 1, 1).coerceIn(0f, 1f)
+ val center_fraction = resources.getFraction(
+ R.fraction.carousel_midscreenswipe_width_fraction,
+ 1,
+ 1
+ ).coerceIn(0f, 1f)
val leftBound = ((1 - center_fraction) / 2) * width
val rightBound = leftBound + (width * center_fraction)
allowRefresh = startX >= leftBound && startX <= rightBound
@@ -30,4 +34,4 @@ class MidScreenSwipeRefreshLayout @JvmOverloads constructor(
}
return if (allowRefresh) super.onInterceptTouchEvent(ev) else false
}
-}
\ No newline at end of file
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
index 66f012d1af..72ce006a7e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
@@ -141,7 +141,7 @@ class GamesViewModel : ViewModel() {
}
}
- fun addFolder(gameDir: GameDir, savedFromGameFragment: Boolean) =
+ fun addFolder(gameDir: GameDir, savedFromGameFragment: Boolean) =
viewModelScope.launch {
withContext(Dispatchers.IO) {
NativeConfig.addGameDir(gameDir)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetDataValidators.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetDataValidators.kt
index b3edf35d8e..c2ad475c95 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetDataValidators.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetDataValidators.kt
@@ -27,7 +27,7 @@ object NetDataValidators {
fun roomVisibility(s: String, context: Context): Boolean {
if (s != context.getString(R.string.multiplayer_public_visibility)) {
- return true;
+ return true
}
return token()
@@ -53,4 +53,4 @@ object NetDataValidators {
fun port(s: String): Boolean {
return s.toIntOrNull() in 1..65535
}
-}
\ No newline at end of file
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetPlayManager.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetPlayManager.kt
index 478dea6bdd..1e8e9f97d0 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetPlayManager.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/network/NetPlayManager.kt
@@ -70,7 +70,6 @@ object NetPlayManager {
val gameName: String
)
-
private var messageListener: ((Int, String) -> Unit)? = null
private var adapterRefreshListener: ((Int, String) -> Unit)? = null
@@ -199,7 +198,6 @@ object NetPlayManager {
}
}
-
Handler(Looper.getMainLooper()).post {
if (!isChatOpen) {
// TODO(alekpop, crueter): Improve this, potentially a drawer at the top?
@@ -207,7 +205,6 @@ object NetPlayManager {
}
}
-
messageListener?.invoke(type, msg)
adapterRefreshListener?.invoke(type, msg)
}
@@ -218,19 +215,29 @@ object NetPlayManager {
NetPlayStatus.LOST_CONNECTION -> context.getString(R.string.multiplayer_lost_connection)
NetPlayStatus.NAME_COLLISION -> context.getString(R.string.multiplayer_name_collision)
NetPlayStatus.MAC_COLLISION -> context.getString(R.string.multiplayer_mac_collision)
- NetPlayStatus.CONSOLE_ID_COLLISION -> context.getString(R.string.multiplayer_console_id_collision)
+ NetPlayStatus.CONSOLE_ID_COLLISION -> context.getString(
+ R.string.multiplayer_console_id_collision
+ )
NetPlayStatus.WRONG_VERSION -> context.getString(R.string.multiplayer_wrong_version)
NetPlayStatus.WRONG_PASSWORD -> context.getString(R.string.multiplayer_wrong_password)
- NetPlayStatus.COULD_NOT_CONNECT -> context.getString(R.string.multiplayer_could_not_connect)
+ NetPlayStatus.COULD_NOT_CONNECT -> context.getString(
+ R.string.multiplayer_could_not_connect
+ )
NetPlayStatus.ROOM_IS_FULL -> context.getString(R.string.multiplayer_room_is_full)
NetPlayStatus.HOST_BANNED -> context.getString(R.string.multiplayer_host_banned)
- NetPlayStatus.PERMISSION_DENIED -> context.getString(R.string.multiplayer_permission_denied)
+ NetPlayStatus.PERMISSION_DENIED -> context.getString(
+ R.string.multiplayer_permission_denied
+ )
NetPlayStatus.NO_SUCH_USER -> context.getString(R.string.multiplayer_no_such_user)
NetPlayStatus.ALREADY_IN_ROOM -> context.getString(R.string.multiplayer_already_in_room)
- NetPlayStatus.CREATE_ROOM_ERROR -> context.getString(R.string.multiplayer_create_room_error)
+ NetPlayStatus.CREATE_ROOM_ERROR -> context.getString(
+ R.string.multiplayer_create_room_error
+ )
NetPlayStatus.HOST_KICKED -> context.getString(R.string.multiplayer_host_kicked)
NetPlayStatus.UNKNOWN_ERROR -> context.getString(R.string.multiplayer_unknown_error)
- NetPlayStatus.ROOM_UNINITIALIZED -> context.getString(R.string.multiplayer_room_uninitialized)
+ NetPlayStatus.ROOM_UNINITIALIZED -> context.getString(
+ R.string.multiplayer_room_uninitialized
+ )
NetPlayStatus.ROOM_IDLE -> context.getString(R.string.multiplayer_room_idle)
NetPlayStatus.ROOM_JOINING -> context.getString(R.string.multiplayer_room_joining)
NetPlayStatus.ROOM_JOINED -> context.getString(R.string.multiplayer_room_joined)
@@ -247,7 +254,9 @@ object NetPlayManager {
msg
)
- NetPlayStatus.ADDRESS_UNBANNED -> context.getString(R.string.multiplayer_address_unbanned)
+ NetPlayStatus.ADDRESS_UNBANNED -> context.getString(
+ R.string.multiplayer_address_unbanned
+ )
NetPlayStatus.CHAT_MESSAGE -> msg
else -> ""
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
index 43b9085f50..db1f808017 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
@@ -7,20 +7,13 @@ import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.os.Bundle
-import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.view.ViewTreeObserver
import android.view.inputmethod.InputMethodManager
-import android.widget.ImageButton
import android.widget.PopupMenu
-import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.toArgb
-import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
@@ -111,7 +104,7 @@ class GamesFragment : Fragment() {
}
gameAdapter = GameAdapter(
- requireActivity() as AppCompatActivity,
+ requireActivity() as AppCompatActivity
)
applyGridGamesBinding()
@@ -238,7 +231,9 @@ class GamesFragment : Fragment() {
override fun onResume() {
super.onResume()
if (getCurrentViewType() == GameAdapter.VIEW_TYPE_CAROUSEL) {
- (binding.gridGames as? CarouselRecyclerView)?.restoreScrollState(gamesViewModel.lastScrollPosition)
+ (binding.gridGames as? CarouselRecyclerView)?.restoreScrollState(
+ gamesViewModel.lastScrollPosition
+ )
}
}
@@ -389,7 +384,9 @@ class GamesFragment : Fragment() {
val searchTerm = binding.searchText.text.toString().lowercase(Locale.getDefault())
if (searchTerm.isEmpty()) {
- ((binding.gridGames as? RecyclerView)?.adapter as? GameAdapter)?.submitList(filteredList)
+ ((binding.gridGames as? RecyclerView)?.adapter as? GameAdapter)?.submitList(
+ filteredList
+ )
gamesViewModel.setFilteredGames(filteredList)
return
}
@@ -464,7 +461,9 @@ class GamesFragment : Fragment() {
// Always set margin as original + insets
mlpHeader.leftMargin = (originalHeaderLeftMargin ?: 0) + leftInset
mlpHeader.rightMargin = (originalHeaderRightMargin ?: 0) + rightInset
- mlpHeader.topMargin = (originalHeaderTopMargin ?: 0) + topInset + resources.getDimensionPixelSize(R.dimen.spacing_med)
+ mlpHeader.topMargin = (originalHeaderTopMargin ?: 0) + topInset + resources.getDimensionPixelSize(
+ R.dimen.spacing_med
+ )
binding.header.layoutParams = mlpHeader
binding.noticeText.updatePadding(bottom = spacingNavigation)
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 6d9a2002f5..fffaa1e3ba 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
@@ -6,8 +6,6 @@ package org.yuzu.yuzu_emu.ui.main
import android.content.Intent
import android.net.Uri
import android.os.Bundle
-import android.os.ParcelFileDescriptor
-import android.provider.OpenableColumns
import android.view.View
import android.view.ViewGroup.MarginLayoutParams
import android.view.WindowManager
@@ -49,7 +47,6 @@ import java.io.BufferedOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import androidx.core.content.edit
-import androidx.core.net.toFile
class MainActivity : AppCompatActivity(), ThemeProvider {
private lateinit var binding: ActivityMainBinding
@@ -69,7 +66,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
private var checkedFirmware = false
private val requestBluetoothPermissionsLauncher =
- registerForActivityResult(androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
+ registerForActivityResult(
+ androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions()
+ ) { permissions ->
val granted = permissions.entries.all { it.value }
if (granted) {
// Permissions were granted.
@@ -111,10 +110,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
binding = ActivityMainBinding.inflate(layoutInflater)
-
setContentView(binding.root)
-
checkAndRequestBluetoothPermissions()
if (savedInstanceState != null) {
@@ -151,16 +148,20 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
binding.statusBarShade.setBackgroundColor(
ThemeHelper.getColorWithOpacity(
MaterialColors.getColor(
- binding.root, com.google.android.material.R.attr.colorSurface
- ), ThemeHelper.SYSTEM_BAR_ALPHA
+ binding.root,
+ com.google.android.material.R.attr.colorSurface
+ ),
+ ThemeHelper.SYSTEM_BAR_ALPHA
)
)
if (InsetsHelper.getSystemGestureType(applicationContext) != InsetsHelper.GESTURE_NAVIGATION) {
binding.navigationBarShade.setBackgroundColor(
ThemeHelper.getColorWithOpacity(
MaterialColors.getColor(
- binding.root, com.google.android.material.R.attr.colorSurface
- ), ThemeHelper.SYSTEM_BAR_ALPHA
+ binding.root,
+ com.google.android.material.R.attr.colorSurface
+ ),
+ ThemeHelper.SYSTEM_BAR_ALPHA
)
)
}
@@ -171,7 +172,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
homeViewModel.statusBarShadeVisible.collect(this) { showStatusBarShade(it) }
homeViewModel.contentToInstall.collect(
- this, resetState = { homeViewModel.setContentToInstall(null) }) {
+ this,
+ resetState = { homeViewModel.setContentToInstall(null) }
+ ) {
if (it != null) {
installContent(it)
}
@@ -181,7 +184,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
homeViewModel.checkFirmware.collect(
- this, resetState = { homeViewModel.setCheckFirmware(false) }) {
+ this,
+ resetState = { homeViewModel.setCheckFirmware(false) }
+ ) {
if (it) checkFirmware()
}
@@ -204,7 +209,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
PreferenceManager.getDefaultSharedPreferences(applicationContext).edit() {
putBoolean(Settings.PREF_SHOULD_SHOW_PRE_ALPHA_WARNING, false)
}
- }).show(supportFragmentManager, MessageDialogFragment.TAG)
+ }
+ ).show(supportFragmentManager, MessageDialogFragment.TAG)
}
}
@@ -225,7 +231,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
private fun checkFirmware() {
val resultCode: Int = NativeLibrary.verifyFirmware()
- if (resultCode == 0) return;
+ if (resultCode == 0) return
val resultString: String =
resources.getStringArray(R.array.verifyFirmwareResults)[resultCode]
@@ -313,14 +319,17 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
fun processGamesDir(result: Uri, calledFromGameFragment: Boolean = false) {
contentResolver.takePersistableUriPermission(
- result, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ result,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION
)
val uriString = result.toString()
val folder = gamesViewModel.folders.value.firstOrNull { it.uriString == uriString }
if (folder != null) {
Toast.makeText(
- applicationContext, R.string.folder_already_added, Toast.LENGTH_SHORT
+ applicationContext,
+ R.string.folder_already_added,
+ Toast.LENGTH_SHORT
).show()
return
}
@@ -343,16 +352,19 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
fun processKey(result: Uri, extension: String = "keys") {
contentResolver.takePersistableUriPermission(
- result, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ result,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION
)
- val resultCode: Int = NativeLibrary.installKeys(result.toString(), extension);
+ val resultCode: Int = NativeLibrary.installKeys(result.toString(), extension)
if (resultCode == 0) {
// TODO(crueter): It may be worth it to switch some of these Toasts to snackbars,
// since most of it is foreground-only anyways.
Toast.makeText(
- applicationContext, R.string.keys_install_success, Toast.LENGTH_SHORT
+ applicationContext,
+ R.string.keys_install_success,
+ Toast.LENGTH_SHORT
).show()
gamesViewModel.reloadGames(true)
@@ -384,12 +396,15 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val cacheFirmwareDir = File("${cacheDir.path}/registered/")
ProgressDialogFragment.newInstance(
- this, R.string.firmware_installing
+ this,
+ R.string.firmware_installing
) { progressCallback, _ ->
var messageToShow: Any
try {
FileUtil.unzipToInternalStorage(
- result.toString(), cacheFirmwareDir, progressCallback
+ result.toString(),
+ cacheFirmwareDir,
+ progressCallback
)
val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1
val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2
@@ -423,7 +438,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val firmwarePath =
File(DirectoryInitialization.userDirectory + "/nand/system/Contents/registered/")
ProgressDialogFragment.newInstance(
- this, R.string.firmware_uninstalling
+ this,
+ R.string.firmware_uninstalling
) { progressCallback, _ ->
var messageToShow: Any
try {
@@ -459,12 +475,15 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
ProgressDialogFragment.newInstance(
- this@MainActivity, R.string.verifying_content, false
+ this@MainActivity,
+ R.string.verifying_content,
+ false
) { _, _ ->
var updatesMatchProgram = true
for (document in documents) {
val valid = NativeLibrary.doesUpdateMatchProgram(
- addonViewModel.game!!.programId, document.toString()
+ addonViewModel.game!!.programId,
+ document.toString()
)
if (!valid) {
updatesMatchProgram = false
@@ -480,14 +499,16 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
titleId = R.string.content_install_notice,
descriptionId = R.string.content_install_notice_description,
positiveAction = { homeViewModel.setContentToInstall(documents) },
- negativeAction = {})
+ negativeAction = {}
+ )
}
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
}
private fun installContent(documents: List) {
ProgressDialogFragment.newInstance(
- this@MainActivity, R.string.installing_game_content
+ this@MainActivity,
+ R.string.installing_game_content
) { progressCallback, messageCallback ->
var installSuccess = 0
var installOverwrite = 0
@@ -495,11 +516,14 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
var error = 0
documents.forEach {
messageCallback.invoke(FileUtil.getFilename(it))
- when (InstallResult.from(
- NativeLibrary.installFileToNand(
- it.toString(), progressCallback
+ when (
+ InstallResult.from(
+ NativeLibrary.installFileToNand(
+ it.toString(),
+ progressCallback
+ )
)
- )) {
+ ) {
InstallResult.Success -> {
installSuccess += 1
}
@@ -525,7 +549,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
if (installSuccess > 0) {
installResult.append(
getString(
- R.string.install_game_content_success_install, installSuccess
+ R.string.install_game_content_success_install,
+ installSuccess
)
)
installResult.append(separator)
@@ -533,7 +558,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
if (installOverwrite > 0) {
installResult.append(
getString(
- R.string.install_game_content_success_overwrite, installOverwrite
+ R.string.install_game_content_success_overwrite,
+ installOverwrite
)
)
installResult.append(separator)
@@ -543,7 +569,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
installResult.append(separator)
installResult.append(
getString(
- R.string.install_game_content_failed_count, errorTotal
+ R.string.install_game_content_failed_count,
+ errorTotal
)
)
installResult.append(separator)
@@ -584,7 +611,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
ProgressDialogFragment.newInstance(
- this, R.string.exporting_user_data, true
+ this,
+ R.string.exporting_user_data,
+ true
) { progressCallback, _ ->
val zipResult = FileUtil.zipFromInternalStorage(
File(DirectoryInitialization.userDirectory!!),
@@ -608,7 +637,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
ProgressDialogFragment.newInstance(
- this, R.string.importing_user_data
+ this,
+ R.string.importing_user_data
) { progressCallback, _ ->
val checkStream =
ZipInputStream(BufferedInputStream(contentResolver.openInputStream(result)))
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/CustomSettingsHandler.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/CustomSettingsHandler.kt
new file mode 100644
index 0000000000..508f9de463
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/CustomSettingsHandler.kt
@@ -0,0 +1,314 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.utils
+
+import android.content.Context
+import android.widget.Toast
+import androidx.fragment.app.FragmentActivity
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.model.DriverViewModel
+import org.yuzu.yuzu_emu.model.Game
+import java.io.File
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
+import android.net.Uri
+import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
+
+object CustomSettingsHandler {
+ const val CUSTOM_CONFIG_ACTION = "dev.eden.eden_emulator.LAUNCH_WITH_CUSTOM_CONFIG"
+ const val EXTRA_TITLE_ID = "title_id"
+ const val EXTRA_CUSTOM_SETTINGS = "custom_settings"
+
+ /**
+ * Apply custom settings from a string instead of loading from file
+ * @param titleId The game title ID (16-digit hex string)
+ * @param customSettings The complete INI file content as string
+ * @param context Application context
+ * @return Game object created from title ID, or null if not found
+ */
+ fun applyCustomSettings(titleId: String, customSettings: String, context: Context): Game? {
+ // For synchronous calls without driver checking
+ Log.info("[CustomSettingsHandler] Applying custom settings for title ID: $titleId")
+ // Find the game by title ID
+ val game = findGameByTitleId(titleId, context)
+ if (game == null) {
+ Log.error("[CustomSettingsHandler] Game not found for title ID: $titleId")
+ return null
+ }
+
+ // Check if config already exists - this should be handled by the caller
+ val configFile = getConfigFile(game)
+ if (configFile.exists()) {
+ Log.warning("[CustomSettingsHandler] Config file already exists for game: ${game.title}")
+ }
+
+ // Write the config file
+ if (!writeConfigFile(game, customSettings)) {
+ Log.error("[CustomSettingsHandler] Failed to write config file")
+ return null
+ }
+
+ // Initialize per-game config
+ try {
+ val fileName = FileUtil.getFilename(Uri.parse(game.path))
+ NativeConfig.initializePerGameConfig(game.programId, fileName)
+ Log.info("[CustomSettingsHandler] Successfully applied custom settings")
+ return game
+ } catch (e: Exception) {
+ Log.error("[CustomSettingsHandler] Failed to apply custom settings: ${e.message}")
+ return null
+ }
+ }
+
+ /**
+ * Apply custom settings with automatic driver checking and installation
+ * @param titleId The game title ID (16-digit hex string)
+ * @param customSettings The complete INI file content as string
+ * @param context Application context
+ * @param activity Fragment activity for driver installation dialogs (optional)
+ * @param driverViewModel DriverViewModel for driver management (optional)
+ * @return Game object created from title ID, or null if not found
+ */
+ suspend fun applyCustomSettingsWithDriverCheck(
+ titleId: String,
+ customSettings: String,
+ context: Context,
+ activity: FragmentActivity?,
+ driverViewModel: DriverViewModel?
+ ): Game? {
+ Log.info("[CustomSettingsHandler] Applying custom settings for title ID: $titleId")
+ // Find the game by title ID
+ val game = findGameByTitleId(titleId, context)
+ if (game == null) {
+ Log.error("[CustomSettingsHandler] Game not found for title ID: $titleId")
+ // This will be handled by the caller to show appropriate error message
+ return null
+ }
+
+ // Check if config already exists
+ val configFile = getConfigFile(game)
+ if (configFile.exists() && activity != null) {
+ Log.info(
+ "[CustomSettingsHandler] Config file already exists, asking user for confirmation"
+ )
+ Toast.makeText(
+ activity,
+ activity.getString(R.string.config_exists_prompt),
+ Toast.LENGTH_SHORT
+ ).show()
+ val shouldOverwrite = askUserToOverwriteConfig(activity, game.title)
+ if (!shouldOverwrite) {
+ Log.info("[CustomSettingsHandler] User chose not to overwrite existing config")
+ Toast.makeText(
+ activity,
+ activity.getString(R.string.overwrite_cancelled),
+ Toast.LENGTH_SHORT
+ ).show()
+ return null
+ }
+ }
+
+ // Check for driver requirements if activity and driverViewModel are provided
+ if (activity != null && driverViewModel != null) {
+ val driverPath = extractDriverPath(customSettings)
+ if (driverPath != null) {
+ Log.info("[CustomSettingsHandler] Custom settings specify driver: $driverPath")
+ // Check if driver exists in the driver storage
+ val driverFile = File(driverPath)
+ if (!driverFile.exists()) {
+ Log.error("[CustomSettingsHandler] Required driver not found: $driverPath")
+ Toast.makeText(
+ activity,
+ activity.getString(
+ R.string.custom_settings_failed_message,
+ game.title,
+ activity.getString(R.string.driver_not_found, driverFile.name)
+ ),
+ Toast.LENGTH_LONG
+ ).show()
+ // Don't write config if driver is missing
+ return null
+ }
+
+ // Verify it's a valid driver
+ val metadata = GpuDriverHelper.getMetadataFromZip(driverFile)
+ if (metadata.name == null) {
+ Log.error("[CustomSettingsHandler] Invalid driver file: $driverPath")
+ Toast.makeText(
+ activity,
+ activity.getString(
+ R.string.custom_settings_failed_message,
+ game.title,
+ activity.getString(R.string.invalid_driver_file, driverFile.name)
+ ),
+ Toast.LENGTH_LONG
+ ).show()
+ return null
+ }
+
+ Log.info("[CustomSettingsHandler] Driver verified: ${metadata.name}")
+ }
+ }
+
+ // Only write the config file after all checks pass
+ if (!writeConfigFile(game, customSettings)) {
+ Log.error("[CustomSettingsHandler] Failed to write config file")
+ activity?.let {
+ Toast.makeText(
+ it,
+ it.getString(R.string.config_write_failed),
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ return null
+ }
+
+ // Initialize per-game config
+ try {
+ SettingsFile.loadCustomConfig(game)
+ Log.info("[CustomSettingsHandler] Successfully applied custom settings")
+ activity?.let {
+ Toast.makeText(
+ it,
+ it.getString(R.string.custom_settings_applied),
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ return game
+ } catch (e: Exception) {
+ Log.error("[CustomSettingsHandler] Failed to apply custom settings: ${e.message}")
+ activity?.let {
+ Toast.makeText(
+ it,
+ it.getString(R.string.config_apply_failed),
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ return null
+ }
+ }
+
+ /**
+ * Find a game by its title ID in the user's game library
+ */
+ fun findGameByTitleId(titleId: String, context: Context): Game? {
+ Log.info("[CustomSettingsHandler] Searching for game with title ID: $titleId")
+ // Convert hex title ID to decimal for comparison with programId
+ val programIdDecimal = try {
+ titleId.toLong(16).toString()
+ } catch (e: NumberFormatException) {
+ Log.error("[CustomSettingsHandler] Invalid title ID format: $titleId")
+ return null
+ }
+
+ // Expected hex format with "0" prefix
+ val expectedHex = "0${titleId.uppercase()}"
+ // First check cached games for fast lookup
+ GameHelper.cachedGameList.find { game ->
+ game.programId == programIdDecimal ||
+ game.programIdHex.equals(expectedHex, ignoreCase = true)
+ }?.let { foundGame ->
+ Log.info("[CustomSettingsHandler] Found game in cache: ${foundGame.title}")
+ return foundGame
+ }
+ // If not in cache, perform full game library scan
+ Log.info("[CustomSettingsHandler] Game not in cache, scanning full library...")
+ val allGames = GameHelper.getGames()
+ val foundGame = allGames.find { game ->
+ game.programId == programIdDecimal ||
+ game.programIdHex.equals(expectedHex, ignoreCase = true)
+ }
+ if (foundGame != null) {
+ Log.info("[CustomSettingsHandler] Found game: ${foundGame.title} at ${foundGame.path}")
+ } else {
+ Log.warning("[CustomSettingsHandler] No game found for title ID: $titleId")
+ }
+ return foundGame
+ }
+
+ /**
+ * Get the config file path for a game
+ */
+ private fun getConfigFile(game: Game): File {
+ return SettingsFile.getCustomSettingsFile(game)
+ }
+
+ /**
+ * Get the title ID config file path
+ */
+ private fun getTitleIdConfigFile(titleId: String): File {
+ val configDir = File(DirectoryInitialization.userDirectory, "config/custom")
+ return File(configDir, "$titleId.ini")
+ }
+
+ /**
+ * Write the config file with the custom settings
+ */
+ private fun writeConfigFile(game: Game, customSettings: String): Boolean {
+ return try {
+ val configFile = getConfigFile(game)
+ val configDir = configFile.parentFile
+ if (configDir != null && !configDir.exists()) {
+ configDir.mkdirs()
+ }
+
+ configFile.writeText(customSettings)
+
+ Log.info("[CustomSettingsHandler] Wrote config file: ${configFile.absolutePath}")
+ true
+ } catch (e: Exception) {
+ Log.error("[CustomSettingsHandler] Failed to write config file: ${e.message}")
+ false
+ }
+ }
+
+ /**
+ * Ask user if they want to overwrite existing configuration
+ */
+ private suspend fun askUserToOverwriteConfig(activity: FragmentActivity, gameTitle: String): Boolean {
+ return suspendCoroutine { continuation ->
+ activity.runOnUiThread {
+ MaterialAlertDialogBuilder(activity)
+ .setTitle(activity.getString(R.string.config_already_exists_title))
+ .setMessage(
+ activity.getString(R.string.config_already_exists_message, gameTitle)
+ )
+ .setPositiveButton(activity.getString(R.string.overwrite)) { _, _ ->
+ continuation.resume(true)
+ }
+ .setNegativeButton(activity.getString(R.string.cancel)) { _, _ ->
+ continuation.resume(false)
+ }
+ .setCancelable(false)
+ .show()
+ }
+ }
+ }
+
+ /**
+ * Extract driver path from custom settings INI content
+ */
+ private fun extractDriverPath(customSettings: String): String? {
+ val lines = customSettings.lines()
+ var inGpuDriverSection = false
+
+ for (line in lines) {
+ val trimmed = line.trim()
+ if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
+ inGpuDriverSection = trimmed == "[GpuDriver]"
+ continue
+ }
+
+ if (inGpuDriverSection && trimmed.startsWith("driver_path=")) {
+ return trimmed.substringAfter("driver_path=")
+ }
+ }
+
+ return null
+ }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
index feed8b3cf8..a30eaf5890 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
@@ -197,7 +197,9 @@ object FileUtil {
*/
fun getFilename(uri: Uri): String {
if (uri.scheme == "file") {
- return uri.lastPathSegment?.takeIf { it.isNotEmpty() } ?: throw IOException("Invalid file URI: $uri")
+ return uri.lastPathSegment?.takeIf { it.isNotEmpty() } ?: throw IOException(
+ "Invalid file URI: $uri"
+ )
}
val resolver = YuzuApplication.appContext.contentResolver
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PowerStateUpdater.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PowerStateUpdater.kt
index 58d2d1e7ed..ec492569ce 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PowerStateUpdater.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PowerStateUpdater.kt
@@ -1,7 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
-
package org.yuzu.yuzu_emu.utils
import android.os.Handler
@@ -17,7 +16,6 @@ object PowerStateUpdater {
private var isStarted = false
fun start() {
-
if (isStarted) {
return
}
@@ -43,4 +41,4 @@ object PowerStateUpdater {
}
isStarted = false
}
-}
\ No newline at end of file
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PowerStateUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PowerStateUtils.kt
index 86e3d9c785..48382fad5b 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PowerStateUtils.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PowerStateUtils.kt
@@ -13,7 +13,6 @@ object PowerStateUtils {
@JvmStatic
fun getBatteryInfo(context: Context?): IntArray {
-
if (context == null) {
return intArrayOf(0, 0, 0) // Percentage, IsCharging, HasBattery
}
@@ -42,4 +41,4 @@ object PowerStateUtils {
return results
}
-}
\ No newline at end of file
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
index a035628588..3e138c0244 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
@@ -23,10 +23,12 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings
object ThemeHelper {
const val SYSTEM_BAR_ALPHA = 0.9f
+
// Listener that detects if the theme keys are being changed from the setting menu and recreates the activity
private var listener: SharedPreferences.OnSharedPreferenceChangeListener? = null
- private val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
-
+ private val preferences = PreferenceManager.getDefaultSharedPreferences(
+ YuzuApplication.appContext
+ )
fun setTheme(activity: AppCompatActivity) {
setThemeMode(activity)
@@ -52,6 +54,7 @@ object ThemeHelper {
private fun getSelectedStaticThemeColor(): Int {
val themeIndex = preferences.getInt(Settings.PREF_STATIC_THEME_COLOR, 0)
val themes = arrayOf(
+ R.style.Theme_Eden_Main,
R.style.Theme_Yuzu_Main_Violet,
R.style.Theme_Yuzu_Main_Blue,
R.style.Theme_Yuzu_Main_Cyan,
@@ -120,7 +123,11 @@ object ThemeHelper {
fun ThemeChangeListener(activity: AppCompatActivity) {
listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
- val relevantKeys = listOf(Settings.PREF_STATIC_THEME_COLOR, Settings.PREF_THEME_MODE, Settings.PREF_BLACK_BACKGROUNDS)
+ val relevantKeys = listOf(
+ Settings.PREF_STATIC_THEME_COLOR,
+ Settings.PREF_THEME_MODE,
+ Settings.PREF_BLACK_BACKGROUNDS
+ )
if (key in relevantKeys) {
activity.recreate()
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/CarouselRecyclerView.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/CarouselRecyclerView.kt
index ae40fcb498..8a66ebf11f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/CarouselRecyclerView.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/CarouselRecyclerView.kt
@@ -1,6 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
package org.yuzu.yuzu_emu.ui
import android.content.Context
@@ -53,7 +56,7 @@ class CarouselRecyclerView @JvmOverloads constructor(
var flingMultiplier: Float = 1f
- public var pendingScrollAfterReload: Boolean = false
+ var pendingScrollAfterReload: Boolean = false
var useCustomDrawingOrder: Boolean = false
set(value) {
@@ -76,7 +79,11 @@ class CarouselRecyclerView @JvmOverloads constructor(
private fun getLayoutManagerCenter(layoutManager: RecyclerView.LayoutManager): Int {
return if (layoutManager is LinearLayoutManager) {
- calculateCenter(layoutManager.width, layoutManager.paddingStart, layoutManager.paddingEnd)
+ calculateCenter(
+ layoutManager.width,
+ layoutManager.paddingStart,
+ layoutManager.paddingEnd
+ )
} else {
width / 2
}
@@ -121,13 +128,13 @@ class CarouselRecyclerView @JvmOverloads constructor(
fun shapingFunction(x: Float, option: Int = 0): Float {
return when (option) {
- 0 -> 1f //Off
- 1 -> 1f - x //linear descending
- 2 -> (1f - x) * (1f - x) //Ease out
- 3 -> if (x < 0.05f) 1f else (1f-x) * 0.8f
- 4 -> kotlin.math.cos(x * Math.PI).toFloat() //Cosine
- 5 -> kotlin.math.cos( (1.5f * x).coerceIn(0f, 1f) * Math.PI).toFloat() //Cosine 1.5x trimmed
- else -> 1f //Default to Off
+ 0 -> 1f // Off
+ 1 -> 1f - x // linear descending
+ 2 -> (1f - x) * (1f - x) // Ease out
+ 3 -> if (x < 0.05f) 1f else (1f - x) * 0.8f
+ 4 -> kotlin.math.cos(x * Math.PI).toFloat() // Cosine
+ 5 -> kotlin.math.cos((1.5f * x).coerceIn(0f, 1f) * Math.PI).toFloat() // Cosine 1.5x trimmed
+ else -> 1f // Default to Off
}
}
@@ -143,20 +150,36 @@ class CarouselRecyclerView @JvmOverloads constructor(
val center = getRecyclerViewCenter()
val distance = abs(getChildDistanceToCenter(child))
val internalBorderScale = resources.getFraction(R.fraction.carousel_bordercards_scale, 1, 1)
- val borderScale = preferences.getFloat(CAROUSEL_BORDERCARDS_SCALE, internalBorderScale).coerceIn(0f, 1f)
+ val borderScale = preferences.getFloat(CAROUSEL_BORDERCARDS_SCALE, internalBorderScale).coerceIn(
+ 0f,
+ 1f
+ )
val shapeInput = (distance / center).coerceIn(0f, 1f)
val internalShapeSetting = resources.getInteger(R.integer.carousel_cards_scaling_shape)
- val scalingShapeSetting = preferences.getInt(CAROUSEL_CARDS_SCALING_SHAPE, internalShapeSetting)
+ val scalingShapeSetting = preferences.getInt(
+ CAROUSEL_CARDS_SCALING_SHAPE,
+ internalShapeSetting
+ )
val shapedScaling = shapingFunction(shapeInput, scalingShapeSetting)
val scale = (borderScale + (1f - borderScale) * shapedScaling).coerceIn(0f, 1f)
val maxDistance = width / 2f
val alphaInput = (distance / maxDistance).coerceIn(0f, 1f)
- val internalBordersAlpha = resources.getFraction(R.fraction.carousel_bordercards_alpha, 1, 1)
- val borderAlpha = preferences.getFloat(CAROUSEL_BORDERCARDS_ALPHA, internalBordersAlpha).coerceIn(0f, 1f)
+ val internalBordersAlpha = resources.getFraction(
+ R.fraction.carousel_bordercards_alpha,
+ 1,
+ 1
+ )
+ val borderAlpha = preferences.getFloat(CAROUSEL_BORDERCARDS_ALPHA, internalBordersAlpha).coerceIn(
+ 0f,
+ 1f
+ )
val internalAlphaShapeSetting = resources.getInteger(R.integer.carousel_cards_alpha_shape)
- val alphaShapeSetting = preferences.getInt(CAROUSEL_CARDS_ALPHA_SHAPE, internalAlphaShapeSetting)
+ val alphaShapeSetting = preferences.getInt(
+ CAROUSEL_CARDS_ALPHA_SHAPE,
+ internalAlphaShapeSetting
+ )
val shapedAlpha = shapingFunction(alphaInput, alphaShapeSetting)
val alpha = (borderAlpha + (1f - borderAlpha) * shapedAlpha).coerceIn(0f, 1f)
@@ -185,16 +208,33 @@ class CarouselRecyclerView @JvmOverloads constructor(
val insets = rootWindowInsets
val bottomInset = insets?.getInsets(android.view.WindowInsets.Type.systemBars())?.bottom ?: 0
val internalFactor = resources.getFraction(R.fraction.carousel_card_size_factor, 1, 1)
- val userFactor = preferences.getFloat(CAROUSEL_CARD_SIZE_FACTOR, internalFactor).coerceIn(0f, 1f)
+ val userFactor = preferences.getFloat(CAROUSEL_CARD_SIZE_FACTOR, internalFactor).coerceIn(
+ 0f,
+ 1f
+ )
val cardSize = (userFactor * (height - bottomInset)).toInt()
gameAdapter?.setCardSize(cardSize)
- val internalOverlapFactor = resources.getFraction(R.fraction.carousel_overlap_factor, 1, 1)
- overlapFactor = preferences.getFloat(CAROUSEL_OVERLAP_FACTOR, internalOverlapFactor).coerceIn(0f, 1f)
+ val internalOverlapFactor = resources.getFraction(
+ R.fraction.carousel_overlap_factor,
+ 1,
+ 1
+ )
+ overlapFactor = preferences.getFloat(CAROUSEL_OVERLAP_FACTOR, internalOverlapFactor).coerceIn(
+ 0f,
+ 1f
+ )
overlapPx = (cardSize * overlapFactor).toInt()
- val internalFlingMultiplier = resources.getFraction(R.fraction.carousel_fling_multiplier, 1, 1)
- flingMultiplier = preferences.getFloat(CAROUSEL_FLING_MULTIPLIER, internalFlingMultiplier).coerceIn(1f, 5f)
+ val internalFlingMultiplier = resources.getFraction(
+ R.fraction.carousel_fling_multiplier,
+ 1,
+ 1
+ )
+ flingMultiplier = preferences.getFloat(
+ CAROUSEL_FLING_MULTIPLIER,
+ internalFlingMultiplier
+ ).coerceIn(1f, 5f)
gameAdapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onChanged() {
@@ -290,20 +330,28 @@ class CarouselRecyclerView @JvmOverloads constructor(
View.FOCUS_LEFT -> {
if (position > 0) {
val now = System.currentTimeMillis()
- val repeatDetected = (now - lastFocusSearchTime) < resources.getInteger(R.integer.carousel_focus_search_repeat_threshold_ms)
+ val repeatDetected = (now - lastFocusSearchTime) < resources.getInteger(
+ R.integer.carousel_focus_search_repeat_threshold_ms
+ )
lastFocusSearchTime = now
- if (!repeatDetected) { //ensures the first run
+ if (!repeatDetected) { // ensures the first run
val offset = focused.width - overlapPx
smoothScrollBy(-offset, 0)
}
- findViewHolderForAdapterPosition(position - 1)?.itemView ?: super.focusSearch(focused, direction)
+ findViewHolderForAdapterPosition(position - 1)?.itemView ?: super.focusSearch(
+ focused,
+ direction
+ )
} else {
focused
}
}
View.FOCUS_RIGHT -> {
if (position < itemCount - 1) {
- findViewHolderForAdapterPosition(position + 1)?.itemView ?: super.focusSearch(focused, direction)
+ findViewHolderForAdapterPosition(position + 1)?.itemView ?: super.focusSearch(
+ focused,
+ direction
+ )
} else {
focused
}
@@ -341,7 +389,10 @@ class CarouselRecyclerView @JvmOverloads constructor(
inner class OverlappingDecoration(private val overlap: Int) : ItemDecoration() {
override fun getItemOffsets(
- outRect: Rect, view: View, parent: RecyclerView, state: State
+ outRect: Rect,
+ view: View,
+ parent: RecyclerView,
+ state: State
) {
val position = parent.getChildAdapterPosition(view)
if (position > 0) {
@@ -378,12 +429,17 @@ class CarouselRecyclerView @JvmOverloads constructor(
return layoutManager.findViewByPosition(getClosestChildPosition())
}
- //NEEDED: fixes ghost movement when snapping, but breaks inertial scrolling
+ // NEEDED: fixes ghost movement when snapping, but breaks inertial scrolling
override fun calculateDistanceToFinalSnap(
layoutManager: RecyclerView.LayoutManager,
targetView: View
): IntArray? {
- if (layoutManager !is LinearLayoutManager) return super.calculateDistanceToFinalSnap(layoutManager, targetView)
+ if (layoutManager !is LinearLayoutManager) {
+ return super.calculateDistanceToFinalSnap(
+ layoutManager,
+ targetView
+ )
+ }
val out = IntArray(2)
out[0] = getChildDistanceToCenter(targetView).toInt()
out[1] = 0
@@ -399,11 +455,14 @@ class CarouselRecyclerView @JvmOverloads constructor(
if (layoutManager !is LinearLayoutManager) return RecyclerView.NO_POSITION
val closestPosition = this@CarouselRecyclerView.getClosestChildPosition()
val internalMaxFling = resources.getInteger(R.integer.carousel_max_fling_count)
- val maxFling = preferences.getInt(CAROUSEL_MAX_FLING_COUNT, internalMaxFling).coerceIn(1, 10)
+ val maxFling = preferences.getInt(CAROUSEL_MAX_FLING_COUNT, internalMaxFling).coerceIn(
+ 1,
+ 10
+ )
val rawFlingCount = if (velocityX == 0) 0 else velocityX / 2000
val flingCount = rawFlingCount.coerceIn(-maxFling, maxFling)
- var targetPos = (closestPosition + flingCount).coerceIn(0, layoutManager.itemCount - 1)
+ val targetPos = (closestPosition + flingCount).coerceIn(0, layoutManager.itemCount - 1)
return targetPos
}
}
-}
\ No newline at end of file
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/GradientBorderCardView.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/GradientBorderCardView.kt
new file mode 100644
index 0000000000..7badadd119
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/GradientBorderCardView.kt
@@ -0,0 +1,120 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.views
+
+import android.content.Context
+import android.graphics.*
+import android.util.AttributeSet
+import com.google.android.material.card.MaterialCardView
+import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.features.settings.model.Settings
+import androidx.preference.PreferenceManager
+
+class GradientBorderCardView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : MaterialCardView(context, attrs, defStyleAttr) {
+
+ private val borderPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
+ style = Paint.Style.STROKE
+ strokeWidth = 6f
+ }
+
+ private val borderPath = Path()
+ private val borderRect = RectF()
+ private var showGradientBorder = false
+ private var isEdenTheme = false
+
+ init {
+ setWillNotDraw(false)
+ updateThemeState()
+ }
+
+ private fun updateThemeState() {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ val themeIndex = try {
+ prefs.getInt(Settings.PREF_STATIC_THEME_COLOR, 0)
+ } catch (e: Exception) {
+ 0 // Default to Eden theme if error
+ }
+ isEdenTheme = themeIndex == 0
+ }
+
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ super.onSizeChanged(w, h, oldw, oldh)
+
+ // Update border style based on theme
+ if (isEdenTheme) {
+ // Gradient for Eden theme
+ borderPaint.shader = LinearGradient(
+ 0f, 0f,
+ w.toFloat(), h.toFloat(),
+ context.getColor(R.color.eden_border_gradient_start),
+ context.getColor(R.color.eden_border_gradient_end),
+ Shader.TileMode.CLAMP
+ )
+ } else {
+ // Solid color for other themes
+ borderPaint.shader = null
+ val typedValue = android.util.TypedValue()
+ context.theme.resolveAttribute(com.google.android.material.R.attr.colorPrimary, typedValue, true)
+ borderPaint.color = typedValue.data
+ }
+
+ // Update border rect with padding for stroke
+ val halfStroke = borderPaint.strokeWidth / 2
+ borderRect.set(
+ halfStroke,
+ halfStroke,
+ w - halfStroke,
+ h - halfStroke
+ )
+
+ // Update path with rounded corners
+ borderPath.reset()
+ borderPath.addRoundRect(
+ borderRect,
+ radius,
+ radius,
+ Path.Direction.CW
+ )
+ }
+
+ override fun onFocusChanged(gainFocus: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
+ super.onFocusChanged(gainFocus, direction, previouslyFocusedRect)
+ showGradientBorder = gainFocus
+ invalidate()
+ }
+
+ override fun setSelected(selected: Boolean) {
+ super.setSelected(selected)
+ showGradientBorder = selected
+ invalidate()
+ }
+
+ override fun setPressed(pressed: Boolean) {
+ super.setPressed(pressed)
+ if (pressed) {
+ showGradientBorder = true
+ invalidate()
+ }
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+ if (showGradientBorder && !isPressed) {
+ canvas.drawPath(borderPath, borderPaint)
+ }
+ }
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ updateThemeState()
+ requestLayout()
+ }
+}
diff --git a/src/android/app/src/main/res/drawable/eden_background_gradient.xml b/src/android/app/src/main/res/drawable/eden_background_gradient.xml
new file mode 100644
index 0000000000..476e88bb32
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/eden_background_gradient.xml
@@ -0,0 +1,21 @@
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/src/android/app/src/main/res/drawable/eden_button_primary_background.xml b/src/android/app/src/main/res/drawable/eden_button_primary_background.xml
new file mode 100644
index 0000000000..ec22911d66
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/eden_button_primary_background.xml
@@ -0,0 +1,23 @@
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/android/app/src/main/res/drawable/eden_card_background.xml b/src/android/app/src/main/res/drawable/eden_card_background.xml
new file mode 100644
index 0000000000..a944c19fed
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/eden_card_background.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/android/app/src/main/res/drawable/eden_card_elevated_background.xml b/src/android/app/src/main/res/drawable/eden_card_elevated_background.xml
new file mode 100644
index 0000000000..04ccf3172c
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/eden_card_elevated_background.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/android/app/src/main/res/drawable/eden_card_elevated_selector.xml b/src/android/app/src/main/res/drawable/eden_card_elevated_selector.xml
new file mode 100644
index 0000000000..9c7144c100
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/eden_card_elevated_selector.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/android/app/src/main/res/drawable/eden_dialog_background.xml b/src/android/app/src/main/res/drawable/eden_dialog_background.xml
new file mode 100644
index 0000000000..d06b85d406
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/eden_dialog_background.xml
@@ -0,0 +1,23 @@
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/android/app/src/main/res/drawable/eden_gradient_border.xml b/src/android/app/src/main/res/drawable/eden_gradient_border.xml
new file mode 100644
index 0000000000..d17b57d7ae
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/eden_gradient_border.xml
@@ -0,0 +1,23 @@
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/android/app/src/main/res/drawable/eden_list_item_selector.xml b/src/android/app/src/main/res/drawable/eden_list_item_selector.xml
new file mode 100644
index 0000000000..2689c6d41e
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/eden_list_item_selector.xml
@@ -0,0 +1,59 @@
+
+
+
+ -
+
+
-
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/src/android/app/src/main/res/drawable/popup_menu_background.xml b/src/android/app/src/main/res/drawable/popup_menu_background.xml
new file mode 100644
index 0000000000..f792c97a23
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/popup_menu_background.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/android/app/src/main/res/drawable/theme_card_background.xml b/src/android/app/src/main/res/drawable/theme_card_background.xml
new file mode 100644
index 0000000000..1d369c6519
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/theme_card_background.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/android/app/src/main/res/drawable/theme_dialog_background.xml b/src/android/app/src/main/res/drawable/theme_dialog_background.xml
new file mode 100644
index 0000000000..9d456afb60
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/theme_dialog_background.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/android/app/src/main/res/drawable/theme_list_item_selector.xml b/src/android/app/src/main/res/drawable/theme_list_item_selector.xml
new file mode 100644
index 0000000000..ab6fb2f213
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/theme_list_item_selector.xml
@@ -0,0 +1,25 @@
+
+
+
+ -
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+ -
+
+
+
+
+
diff --git a/src/android/app/src/main/res/layout-land/card_game_carousel.xml b/src/android/app/src/main/res/layout-land/card_game_carousel.xml
index fcc9397a13..4d8e5fd148 100644
--- a/src/android/app/src/main/res/layout-land/card_game_carousel.xml
+++ b/src/android/app/src/main/res/layout-land/card_game_carousel.xml
@@ -1,27 +1,30 @@
-
+ app:cardBackgroundColor="@color/eden_card_background"
+ app:strokeWidth="1dp"
+ app:strokeColor="@color/eden_border">
-
-
+
diff --git a/src/android/app/src/main/res/layout-land/fragment_games.xml b/src/android/app/src/main/res/layout-land/fragment_games.xml
index 14e9d5358c..9bd60c6f89 100644
--- a/src/android/app/src/main/res/layout-land/fragment_games.xml
+++ b/src/android/app/src/main/res/layout-land/fragment_games.xml
@@ -4,7 +4,6 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="?attr/colorSurface"
android:clipChildren="false"
>
@@ -44,7 +43,10 @@
style="?attr/materialCardViewFilledStyle"
android:layout_width="match_parent"
android:layout_height="48dp"
- app:cardCornerRadius="21dp"
+ app:cardCornerRadius="24dp"
+ app:cardBackgroundColor="?attr/colorSurfaceVariant"
+ app:strokeColor="?attr/colorOutline"
+ app:strokeWidth="1dp"
>
\ No newline at end of file
diff --git a/src/android/app/src/main/res/layout-w600dp/activity_main.xml b/src/android/app/src/main/res/layout-w600dp/activity_main.xml
index d4188e7c9d..99d24f3e65 100644
--- a/src/android/app/src/main/res/layout-w600dp/activity_main.xml
+++ b/src/android/app/src/main/res/layout-w600dp/activity_main.xml
@@ -6,7 +6,7 @@
android:id="@+id/coordinator_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="?attr/colorSurface">
+ android:background="@drawable/eden_background_gradient">
+ >
+ android:touchscreenBlocksFocus="false"
+ android:background="@android:color/transparent"
+ app:elevation="0dp">
+ android:orientation="horizontal"
+ android:padding="24dp">
-
-
-
+
+ android:orientation="vertical"
+ android:paddingHorizontal="24dp"
+ android:paddingVertical="20dp">
-
+
-
+
-
+
+
-
-
-
+
+ android:orientation="vertical"
+ android:paddingHorizontal="24dp"
+ android:paddingVertical="20dp">
-
+
-
+
-
+
+
-
-
-
+
+ android:orientation="vertical"
+ android:paddingHorizontal="24dp"
+ android:paddingVertical="20dp">
-
+
-
+
-
+
+
-
-
-
+
+ android:orientation="vertical"
+ android:paddingHorizontal="24dp"
+ android:paddingVertical="20dp">
-
+
-
+
-
+
+
-
+ app:iconPadding="0dp" />
-
+ app:iconPadding="0dp" />
-
+ app:iconPadding="0dp" />
@@ -233,4 +248,4 @@
-
+
\ No newline at end of file
diff --git a/src/android/app/src/main/res/layout-w600dp/fragment_setup.xml b/src/android/app/src/main/res/layout-w600dp/fragment_setup.xml
index 406df9eabd..72a8afc1d4 100644
--- a/src/android/app/src/main/res/layout-w600dp/fragment_setup.xml
+++ b/src/android/app/src/main/res/layout-w600dp/fragment_setup.xml
@@ -23,7 +23,7 @@
+ android:background="?android:attr/colorBackground">
+ app:cardCornerRadius="16dp"
+ app:cardPreventCornerOverlap="true"
+ android:clipChildren="true"
+ android:layout_margin="4dp">
-
-
+
diff --git a/src/android/app/src/main/res/layout/card_game_grid.xml b/src/android/app/src/main/res/layout/card_game_grid.xml
index 31fed597f4..3e0bf6005c 100644
--- a/src/android/app/src/main/res/layout/card_game_grid.xml
+++ b/src/android/app/src/main/res/layout/card_game_grid.xml
@@ -3,22 +3,24 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ android:focusable="false"
+ android:focusableInTouchMode="false">
-
+ app:cardCornerRadius="16dp">
-
+
diff --git a/src/android/app/src/main/res/layout/card_game_list.xml b/src/android/app/src/main/res/layout/card_game_list.xml
index bbb1000628..c694c8fc85 100644
--- a/src/android/app/src/main/res/layout/card_game_list.xml
+++ b/src/android/app/src/main/res/layout/card_game_list.xml
@@ -1,18 +1,18 @@
-
+ app:cardCornerRadius="16dp"
+ app:cardElevation="0dp"
+ app:cardBackgroundColor="@color/eden_card_background"
+ app:strokeWidth="0dp">
-
+
diff --git a/src/android/app/src/main/res/layout/card_home_option.xml b/src/android/app/src/main/res/layout/card_home_option.xml
index 8a19b6b202..00a957b7a5 100644
--- a/src/android/app/src/main/res/layout/card_home_option.xml
+++ b/src/android/app/src/main/res/layout/card_home_option.xml
@@ -2,16 +2,15 @@
+ app:cardCornerRadius="16dp">
+ app:tint="?attr/colorPrimary" />
+ app:cardCornerRadius="24dp"
+ android:background="@drawable/theme_dialog_background">
+ android:background="@android:color/transparent">
+ >
+ android:touchscreenBlocksFocus="false"
+ android:background="@android:color/transparent"
+ app:elevation="0dp">
+ android:orientation="vertical"
+ android:paddingBottom="24dp">
-
-
-
-
-
+
+ android:paddingVertical="20dp"
+ android:paddingHorizontal="20dp"
+ android:orientation="vertical">
-
+
-
+
-
+
+
-
-
-
+
+ android:paddingVertical="20dp"
+ android:paddingHorizontal="20dp"
+ android:orientation="vertical">
-
+
-
+
-
+
+
-
-
-
+
+ android:paddingVertical="20dp"
+ android:paddingHorizontal="20dp"
+ android:orientation="vertical">
-
+
-
+
-
+
+
-
-
-
+
+ android:paddingVertical="20dp"
+ android:paddingHorizontal="20dp"
+ android:orientation="vertical">
-
+
-
+
-
+
+
-
+ app:iconGravity="textStart"
+ app:iconPadding="0dp" />
-
+ app:iconGravity="textStart"
+ app:iconPadding="0dp" />
-
+ app:iconGravity="textStart"
+ app:iconPadding="0dp" />
@@ -232,4 +243,4 @@
-
+
\ No newline at end of file
diff --git a/src/android/app/src/main/res/layout/fragment_games.xml b/src/android/app/src/main/res/layout/fragment_games.xml
index a3747b21da..c77a34ab67 100644
--- a/src/android/app/src/main/res/layout/fragment_games.xml
+++ b/src/android/app/src/main/res/layout/fragment_games.xml
@@ -4,7 +4,6 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="?attr/colorSurface"
>
@@ -57,10 +56,11 @@
@@ -111,10 +112,11 @@
@@ -186,6 +191,7 @@
android:gravity="center"
android:padding="@dimen/spacing_large"
android:text="@string/empty_gamelist"
+ android:textColor="?attr/colorOnBackground"
android:visibility="gone"
/>
@@ -216,9 +222,9 @@
app:icon="@drawable/ic_cartridge_outline"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
- android:textColor="?attr/colorOnPrimaryContainer"
- app:backgroundTint="?attr/colorPrimaryContainer"
- app:iconTint="?attr/colorOnPrimaryContainer"
+ android:textColor="?attr/colorOnPrimary"
+ app:backgroundTint="?attr/colorPrimary"
+ app:iconTint="?attr/colorOnPrimary"
/>
\ No newline at end of file
diff --git a/src/android/app/src/main/res/layout/fragment_home_settings.xml b/src/android/app/src/main/res/layout/fragment_home_settings.xml
index c179f9341a..7f90f71967 100644
--- a/src/android/app/src/main/res/layout/fragment_home_settings.xml
+++ b/src/android/app/src/main/res/layout/fragment_home_settings.xml
@@ -4,7 +4,6 @@
android:id="@+id/scroll_view_settings"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="?attr/colorSurface"
android:scrollbars="vertical"
android:fadeScrollbars="false"
android:clipToPadding="false"
@@ -15,21 +14,21 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:background="?attr/colorSurface"
- android:paddingHorizontal="8dp">
+ android:paddingHorizontal="16dp">
+ android:layout_height="match_parent"
+ android:clipToPadding="false" />
diff --git a/src/android/app/src/main/res/layout/fragment_settings.xml b/src/android/app/src/main/res/layout/fragment_settings.xml
index 66cfebe96d..ad4a5caa56 100644
--- a/src/android/app/src/main/res/layout/fragment_settings.xml
+++ b/src/android/app/src/main/res/layout/fragment_settings.xml
@@ -20,7 +20,7 @@
android:layout_width="match_parent"
android:layout_height="?attr/collapsingToolbarLayoutMediumSize"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
- app:contentScrim="?attr/colorOnSurfaceInverse"
+ app:contentScrim="?attr/colorSurface"
app:scrimVisibleHeightTrigger="100dp">
خلفية Shader
اختيار طريقة ترجمة Shaders
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
محاكاة NVDEC
diff --git a/src/android/app/src/main/res/values-ckb/strings.xml b/src/android/app/src/main/res/values-ckb/strings.xml
index 03d834d71f..28a4ad6d8a 100644
--- a/src/android/app/src/main/res/values-ckb/strings.xml
+++ b/src/android/app/src/main/res/values-ckb/strings.xml
@@ -151,8 +151,8 @@
شادەر باکند
هەڵبژاردنی ڕێگای پێکهێنانی شادەر
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
ئیمولەیشنی NVDEC
diff --git a/src/android/app/src/main/res/values-cs/strings.xml b/src/android/app/src/main/res/values-cs/strings.xml
index 739ebed3fc..7822bc43bb 100644
--- a/src/android/app/src/main/res/values-cs/strings.xml
+++ b/src/android/app/src/main/res/values-cs/strings.xml
@@ -150,8 +150,8 @@
Backend shaderů
Způsob kompilace shaderů
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
Emulace NVDEC
diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml
index 63d3e99173..181dfe3cf0 100644
--- a/src/android/app/src/main/res/values-de/strings.xml
+++ b/src/android/app/src/main/res/values-de/strings.xml
@@ -151,8 +151,8 @@
Shader-Backend
Methode zur Shader-Kompilierung
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
NVDEC-Emulation
diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml
index 8cd0992f89..c814b82a18 100644
--- a/src/android/app/src/main/res/values-es/strings.xml
+++ b/src/android/app/src/main/res/values-es/strings.xml
@@ -151,8 +151,8 @@
Backend de shaders
Elegir cómo se compilan shaders
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
Emulación NVDEC
diff --git a/src/android/app/src/main/res/values-fa/strings.xml b/src/android/app/src/main/res/values-fa/strings.xml
index 2b41dfb0a4..8c6c367ca0 100644
--- a/src/android/app/src/main/res/values-fa/strings.xml
+++ b/src/android/app/src/main/res/values-fa/strings.xml
@@ -151,8 +151,8 @@
بکاند شیدر
انتخاب روش کامپایل و ترجمه شیدرها
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
شبیهسازی NVDEC
diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml
index ac5bf9876c..fa70218bf3 100644
--- a/src/android/app/src/main/res/values-fr/strings.xml
+++ b/src/android/app/src/main/res/values-fr/strings.xml
@@ -151,8 +151,8 @@
Backend shader
Méthode de compilation
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
Émulation NVDEC
diff --git a/src/android/app/src/main/res/values-he/strings.xml b/src/android/app/src/main/res/values-he/strings.xml
index 2d7e575710..0c37525eb5 100644
--- a/src/android/app/src/main/res/values-he/strings.xml
+++ b/src/android/app/src/main/res/values-he/strings.xml
@@ -152,8 +152,8 @@
מנוע שיידרים
בחר כיצד לקמפל שיידרים
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
אמולציית NVDEC
diff --git a/src/android/app/src/main/res/values-hu/strings.xml b/src/android/app/src/main/res/values-hu/strings.xml
index a9448a7e41..8c4c7e3f87 100644
--- a/src/android/app/src/main/res/values-hu/strings.xml
+++ b/src/android/app/src/main/res/values-hu/strings.xml
@@ -151,8 +151,8 @@
Shader backend
Shaderek fordításának módja
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
NVDEC emuláció
diff --git a/src/android/app/src/main/res/values-id/strings.xml b/src/android/app/src/main/res/values-id/strings.xml
index a9aade2ed7..bb05dc0df7 100644
--- a/src/android/app/src/main/res/values-id/strings.xml
+++ b/src/android/app/src/main/res/values-id/strings.xml
@@ -151,8 +151,8 @@
Backend Shader
Pilih cara shader dikompilasi dan diterjemahkan untuk GPU Anda.
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
Emulasi NVDEC
diff --git a/src/android/app/src/main/res/values-it/strings.xml b/src/android/app/src/main/res/values-it/strings.xml
index 4bbc1a78eb..177bd4377d 100644
--- a/src/android/app/src/main/res/values-it/strings.xml
+++ b/src/android/app/src/main/res/values-it/strings.xml
@@ -151,8 +151,8 @@
Backend shader
Scegli come compilare gli shader
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
Emulazione NVDEC
diff --git a/src/android/app/src/main/res/values-ja/strings.xml b/src/android/app/src/main/res/values-ja/strings.xml
index 3a9f55e883..cf8bf2141a 100644
--- a/src/android/app/src/main/res/values-ja/strings.xml
+++ b/src/android/app/src/main/res/values-ja/strings.xml
@@ -151,8 +151,8 @@
シェーダーバックエンド
シェーダーのコンパイル方法
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
NVDECエミュレーション
diff --git a/src/android/app/src/main/res/values-ko/strings.xml b/src/android/app/src/main/res/values-ko/strings.xml
index c8a6a04bdd..7b97782e21 100644
--- a/src/android/app/src/main/res/values-ko/strings.xml
+++ b/src/android/app/src/main/res/values-ko/strings.xml
@@ -151,8 +151,8 @@
셰이더 백엔드
셰이더 컴파일 방식 선택
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
NVDEC 에뮬레이션
비디오 디코딩 처리 방식 선택
diff --git a/src/android/app/src/main/res/values-nb/strings.xml b/src/android/app/src/main/res/values-nb/strings.xml
index 287b2bd071..4efa042a1c 100644
--- a/src/android/app/src/main/res/values-nb/strings.xml
+++ b/src/android/app/src/main/res/values-nb/strings.xml
@@ -151,8 +151,8 @@
Shader-backend
Velg hvordan shadere kompileres
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
NVDEC-emulering
diff --git a/src/android/app/src/main/res/values-pl/strings.xml b/src/android/app/src/main/res/values-pl/strings.xml
index ab4a7b2704..3f8dedbc5d 100644
--- a/src/android/app/src/main/res/values-pl/strings.xml
+++ b/src/android/app/src/main/res/values-pl/strings.xml
@@ -151,8 +151,8 @@
Backend shaderów
Wybierz metodę kompilacji shaderów.
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
Emulacja NVDEC
diff --git a/src/android/app/src/main/res/values-pt-rBR/strings.xml b/src/android/app/src/main/res/values-pt-rBR/strings.xml
index ee83c34c79..f1aad3d730 100644
--- a/src/android/app/src/main/res/values-pt-rBR/strings.xml
+++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml
@@ -151,8 +151,8 @@
Backend de shader
Define como shaders são compilados
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
Emulação NVDEC
diff --git a/src/android/app/src/main/res/values-pt-rPT/strings.xml b/src/android/app/src/main/res/values-pt-rPT/strings.xml
index 896f72cacb..88e0c55370 100644
--- a/src/android/app/src/main/res/values-pt-rPT/strings.xml
+++ b/src/android/app/src/main/res/values-pt-rPT/strings.xml
@@ -151,8 +151,8 @@
Backend de Shader
Método de compilação de shaders.
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
Emulação NVDEC
diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml
index 19d18e13d7..d942bad591 100644
--- a/src/android/app/src/main/res/values-ru/strings.xml
+++ b/src/android/app/src/main/res/values-ru/strings.xml
@@ -151,8 +151,8 @@
Шейдерный бэкенд
Метод компиляции шейдеров
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
Эмуляция NVDEC
diff --git a/src/android/app/src/main/res/values-sr/strings.xml b/src/android/app/src/main/res/values-sr/strings.xml
index b3d6e2a1ef..1c9d7fbe06 100644
--- a/src/android/app/src/main/res/values-sr/strings.xml
+++ b/src/android/app/src/main/res/values-sr/strings.xml
@@ -116,8 +116,8 @@
Схадер Бацкенд
Изаберите како се сјеначици саставе и преведете за ваш ГПУ.
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
НВДЕЦ Емулација
diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml
index 473dff349c..2e25ebb5fc 100644
--- a/src/android/app/src/main/res/values-uk/strings.xml
+++ b/src/android/app/src/main/res/values-uk/strings.xml
@@ -151,8 +151,8 @@
Система обробки шейдерів
Спосіб компіляції шейдерів
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
Емуляція NVDEC
diff --git a/src/android/app/src/main/res/values-vi/strings.xml b/src/android/app/src/main/res/values-vi/strings.xml
index 5d2e59a6d5..553cf4d4b6 100644
--- a/src/android/app/src/main/res/values-vi/strings.xml
+++ b/src/android/app/src/main/res/values-vi/strings.xml
@@ -151,8 +151,8 @@
Backend Shader
Chọn cách biên dịch shader
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
Giả lập NVDEC
diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml
index 95c7352d08..1d044f202b 100644
--- a/src/android/app/src/main/res/values-zh-rCN/strings.xml
+++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml
@@ -150,8 +150,8 @@
着色器后端
选择着色器编译方式
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
NVDEC模拟
diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml
index b9511bb959..3db523c91d 100644
--- a/src/android/app/src/main/res/values-zh-rTW/strings.xml
+++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml
@@ -154,8 +154,8 @@
著色器後端
選擇著色器的編譯與轉譯方式
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
NVDEC模擬
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index 855fd6e769..602003cf8a 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -389,6 +389,7 @@
+ - @string/eden_theme
- @string/violet
- @string/blue
- @string/cyan
@@ -409,6 +410,7 @@
- 6
- 7
- 8
+ - 9
diff --git a/src/android/app/src/main/res/values/eden_colors.xml b/src/android/app/src/main/res/values/eden_colors.xml
new file mode 100644
index 0000000000..fe29e13ef9
--- /dev/null
+++ b/src/android/app/src/main/res/values/eden_colors.xml
@@ -0,0 +1,84 @@
+
+
+
+
+ #FF0080
+ #E6006B
+ #00FFFF
+ #00E6E6
+
+
+ #000000
+ #0D0D0D
+ #1A1A1A
+
+
+ #FFFFFF
+ #FFFFFF
+ #FFFFFF
+ #000000
+
+
+ #FF0080
+ #9D00FF
+ #0080FF
+ #FF8000
+
+
+ #FF0080
+ #9D00FF
+ #00FFFF
+
+
+ #80FF0080
+ #8000FFFF
+ #809D00FF
+ #80FF8000
+
+
+ #333333
+ #555555
+ #FF0080
+ #00FFFF
+
+
+ #1A1A1A
+ #FF0080
+ #00FFFF
+
+
+ #FF0040
+ #00FF80
+ #FFFF00
+ #0080FF
+
+
+ #CC000000
+ #80000000
+ #33000000
+
+
+ #FF0080
+ #00000000
+ #00FFFF
+
+
+ #0D0D0D
+ #1A1A1A
+
+
+ #000000
+ #FF0080
+ #666666
+
+
+ #00000000
+ #00FF0080
+ #0000FFFF
+
+
+ #33FF0080
+ #1A00FFFF
+ #FFFF0080
+
+
\ No newline at end of file
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index cac730b66b..66c1f0ecdf 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -116,8 +116,8 @@
Shader Backend
Choose how shaders are compiled and translated for your GPU.
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
NVDEC Emulation
@@ -767,6 +767,44 @@
Game Requires Firmware
dump and install firmware, or press "OK" to launch anyways.]]>
+
+ Searching for game...
+ Game not found for Title ID: %1$s
+ Custom Settings Failed
+ Failed to apply custom settings for %1$s: %2$s
+ Launch with Default Settings
+ Launch cancelled
+ Custom settings applied
+ Launching %1$s...
+ Failed to initialize game
+ Would you like to launch %1$s with custom settings?
+ Would you like to launch %1$s?
+ Launch Game
+ Launch
+
+
+ Failed to write configuration file
+ Failed to apply configuration
+ Configuration Already Exists
+ Custom settings already exist for %1$s.\n\nWould you like to overwrite the existing configuration?\n\nThis action cannot be undone.
+ Checking for existing configuration...
+ Overwrite cancelled
+ Checking for custom driver: %1$s
+ Custom driver not available for this device
+ Overwrite
+
+
+ GPU Driver Missing
+ The selected custom driver \"%s\" is not installed. Would you like to download and install it now?
+ Downloading driver...
+ Driver installed successfully
+ Driver Installation Failed
+ Failed to install the GPU driver: %s
+ Driver Not Available
+ The selected driver is not available for download.
+ Required driver not installed: %s
+ Invalid driver file: %s
+
Exit emulation
Done
@@ -994,7 +1032,8 @@
Theme Color
- Violet (Default)
+ Eden (Default)
+ Violet
Blue
Cyan
Red
diff --git a/src/android/app/src/main/res/values/styles.xml b/src/android/app/src/main/res/values/styles.xml
index be49a1b348..9f487052c4 100644
--- a/src/android/app/src/main/res/values/styles.xml
+++ b/src/android/app/src/main/res/values/styles.xml
@@ -162,4 +162,131 @@
- @color/yuzu_inversePrimary_pink
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/android/app/src/main/res/values/themes.xml b/src/android/app/src/main/res/values/themes.xml
index 20b789ec45..7f14c2205d 100644
--- a/src/android/app/src/main/res/values/themes.xml
+++ b/src/android/app/src/main/res/values/themes.xml
@@ -2,9 +2,9 @@
+
+