diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index 89d819d900..05d9fb039c 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -156,7 +156,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..e01dc754eb 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
@@ -204,6 +202,12 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
+
+ // Reset navigation graph with new intent data to recreate EmulationFragment
+ val navHostFragment =
+ supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
+ navHostFragment.navController.setGraph(R.navigation.emulation_navigation, intent.extras)
+
nfcReader.onNewIntent(intent)
InputHandler.updateControllerData()
}
@@ -421,7 +425,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..9e1dc7709e 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
@@ -1,3 +1,6 @@
+// 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
@@ -6,10 +9,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..e35ef741fd 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
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
package org.yuzu.yuzu_emu.features.fetcher
import android.animation.LayoutTransition
@@ -71,7 +74,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 +125,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 +145,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 +211,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 +225,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 +270,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 +282,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..f3d000a739 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
@@ -1,3 +1,6 @@
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
package org.yuzu.yuzu_emu.features.fetcher
import android.graphics.Rect
@@ -16,4 +19,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 4191bef781..fc6675ec63 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
@@ -64,13 +64,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 1b7b1e2152..c2dc10b56c 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,
@@ -731,7 +729,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)
@@ -746,7 +744,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..31d06c1891 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
@@ -1,3 +1,6 @@
+// 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
@@ -18,7 +21,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..b2fde638db 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
@@ -1,3 +1,6 @@
+// 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
@@ -8,7 +11,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 +18,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 364e3b7ba6..3611e979f2 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
@@ -1057,7 +1055,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..516ed22f6f 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,12 @@ 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 kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
import java.io.File
class EmulationFragment : Fragment(), SurfaceHolder.Callback {
@@ -90,24 +96,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 +136,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 +152,309 @@ 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 {
+ when {
+ // Game launched via intent (check for existing custom config)
+ intentGame != null -> {
+ game?.let { gameInstance ->
+ val customConfigFile = SettingsFile.getCustomSettingsFile(gameInstance)
+ if (customConfigFile.exists()) {
+ Log.info("[EmulationFragment] Found existing custom settings for ${gameInstance.title}, loading them")
+ SettingsFile.loadCustomConfig(gameInstance)
+ } else {
+ Log.info("[EmulationFragment] No custom settings found for ${gameInstance.title}, using global settings")
+ NativeConfig.reloadGlobalConfig()
+ }
+ } ?: run {
+ Log.info("[EmulationFragment] No game available, using global settings")
+ NativeConfig.reloadGlobalConfig()
+ }
+ }
+
+ // Normal game launch from arguments
+ else -> {
+ val shouldUseCustom = game?.let { it == args.game && args.custom } ?: false
+
+ if (shouldUseCustom) {
+ 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,
+ getString(R.string.custom_settings_failure_reasons)
+ )
+
+ 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) {
+ // Hide loading indicator immediately for intent launches
+ binding.loadingIndicator.visibility = View.GONE
+ binding.surfaceEmulation.visibility = View.VISIBLE
+
+ completeViewSetup()
+
+ // For intent launches, check if surface is ready and start emulation
+ binding.root.post {
+ if (binding.surfaceEmulation.holder.surface?.isValid == true && !emulationStarted) {
+ emulationStarted = true
+ emulationState.newSurface(binding.surfaceEmulation.holder.surface)
+ }
+ }
+ }
+ } 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,8 +769,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) {
- if (it) startEmulation()
+ if (it && !NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
+ startEmulation()
+ }
}
+
+ driverViewModel.onLaunchGame()
}
private fun startEmulation(programIndex: Int = 0) {
@@ -518,6 +818,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 +943,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 +957,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 +969,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 +987,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 +1084,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 +1251,34 @@ 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) {
+ emulationStarted = true
+
+ // For intent launches, wait for driver initialization to complete
+ if (isCustomSettingsIntent || intentGame != null) {
+ if (!driverViewModel.isInteractionAllowed.value) {
+ Log.info("[EmulationFragment] Intent launch: waiting for driver initialization")
+ // Driver is still initializing, wait for it
+ lifecycleScope.launch {
+ driverViewModel.isInteractionAllowed.collect { allowed ->
+ if (allowed && holder.surface.isValid) {
+ emulationState.newSurface(holder.surface)
+ }
+ }
+ }
+ return
+ }
+ }
+
+ emulationState.newSurface(holder.surface)
+ } else {
+ emulationState.newSurface(holder.surface)
+ }
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
emulationState.clearSurface()
+ emulationStarted = false
}
private fun showOverlayOptions() {
@@ -1096,22 +1451,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 +1498,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 +1519,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
lateinit var emulationThread: Thread
init {
- // Starting state is stopped.
state = State.STOPPED
}
@@ -1176,7 +1526,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 +1545,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..52ee7b01ea 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
@@ -1,3 +1,6 @@
+// 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
@@ -197,7 +200,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 8598810726..6fd266e4c8 100644
--- a/src/android/app/src/main/res/values-ckb/strings.xml
+++ b/src/android/app/src/main/res/values-ckb/strings.xml
@@ -153,8 +153,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 06ed7975b2..8313778751 100644
--- a/src/android/app/src/main/res/values-cs/strings.xml
+++ b/src/android/app/src/main/res/values-cs/strings.xml
@@ -152,8 +152,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 796839d6ff..26825fee51 100644
--- a/src/android/app/src/main/res/values-de/strings.xml
+++ b/src/android/app/src/main/res/values-de/strings.xml
@@ -153,8 +153,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 596130fbad..ff89d9dcc4 100644
--- a/src/android/app/src/main/res/values-es/strings.xml
+++ b/src/android/app/src/main/res/values-es/strings.xml
@@ -153,8 +153,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 07361fd577..618a846015 100644
--- a/src/android/app/src/main/res/values-fa/strings.xml
+++ b/src/android/app/src/main/res/values-fa/strings.xml
@@ -153,8 +153,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 d1d0a7c4ca..9d3738d686 100644
--- a/src/android/app/src/main/res/values-fr/strings.xml
+++ b/src/android/app/src/main/res/values-fr/strings.xml
@@ -153,8 +153,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 29aa00c404..80032d6f02 100644
--- a/src/android/app/src/main/res/values-he/strings.xml
+++ b/src/android/app/src/main/res/values-he/strings.xml
@@ -154,8 +154,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 bb78b1ff98..f846dbed10 100644
--- a/src/android/app/src/main/res/values-hu/strings.xml
+++ b/src/android/app/src/main/res/values-hu/strings.xml
@@ -153,8 +153,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 d060a0c7c0..d7f18f2c38 100644
--- a/src/android/app/src/main/res/values-id/strings.xml
+++ b/src/android/app/src/main/res/values-id/strings.xml
@@ -153,8 +153,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 aea0a9b29f..2f23cc28f9 100644
--- a/src/android/app/src/main/res/values-it/strings.xml
+++ b/src/android/app/src/main/res/values-it/strings.xml
@@ -153,8 +153,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 521a816917..cf92771f14 100644
--- a/src/android/app/src/main/res/values-ja/strings.xml
+++ b/src/android/app/src/main/res/values-ja/strings.xml
@@ -153,8 +153,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 bd0d929514..a39e16cc29 100644
--- a/src/android/app/src/main/res/values-ko/strings.xml
+++ b/src/android/app/src/main/res/values-ko/strings.xml
@@ -153,8 +153,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 1f962ea46c..f3e87bb6a0 100644
--- a/src/android/app/src/main/res/values-nb/strings.xml
+++ b/src/android/app/src/main/res/values-nb/strings.xml
@@ -153,8 +153,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 f41721b761..dbf2ecd07e 100644
--- a/src/android/app/src/main/res/values-pl/strings.xml
+++ b/src/android/app/src/main/res/values-pl/strings.xml
@@ -153,8 +153,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 05af74e6e0..8c2f135134 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
@@ -153,8 +153,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 065b9e1765..0da3a61e20 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
@@ -153,8 +153,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 6f15e9ee44..ec9b24418b 100644
--- a/src/android/app/src/main/res/values-ru/strings.xml
+++ b/src/android/app/src/main/res/values-ru/strings.xml
@@ -153,8 +153,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 274c78436d..1ebdcadd2e 100644
--- a/src/android/app/src/main/res/values-sr/strings.xml
+++ b/src/android/app/src/main/res/values-sr/strings.xml
@@ -118,8 +118,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 579befd866..56a40422cd 100644
--- a/src/android/app/src/main/res/values-uk/strings.xml
+++ b/src/android/app/src/main/res/values-uk/strings.xml
@@ -153,8 +153,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 95a55ab325..bace0834d1 100644
--- a/src/android/app/src/main/res/values-vi/strings.xml
+++ b/src/android/app/src/main/res/values-vi/strings.xml
@@ -153,8 +153,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 fd8cb4219f..136e5f9bb1 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
@@ -152,8 +152,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 32c9053088..f04ca89fb6 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
@@ -156,8 +156,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 b10e90757e..5d8276ab71 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -112,8 +112,8 @@
Shader Backend
Choose how shaders are compiled and translated for your GPU.
GLSL
- GLASMstring>
- Spir-Vstring>
+ GLASM
+ Spir-V
NVDEC Emulation
@@ -230,7 +230,7 @@
Welcome!
- Learn how to setup <b>eden</b> and jump into emulation.
+ Learn how to setup <b>Eden</b> and jump into emulation.
Get started
Keys
Select your <b>prod.keys</b> file with the button below.
@@ -271,7 +271,7 @@
Search and filter games
Select games folder
Manage game folders
- Allows eden to populate the games list
+ Allows Eden to populate the games list
Skip selecting games folder?
Games won\'t be displayed in the Games list if a folder isn\'t selected.
https://yuzu-mirror.github.io/help/quickstart/#dumping-games
@@ -290,7 +290,7 @@
Grant the notification permission with the button below.
Grant permission
Skip granting the notification permission?
- eden won\'t be able to notify you of important information.
+ Eden won\'t be able to notify you of important information.
Permission denied
You denied this permission too many times and now you have to manually grant it in system settings.
About
@@ -314,11 +314,11 @@
Recently added
Retail
Homebrew
- Open eden folder
- Manage eden\'s internal files
+ Open Eden folder
+ Manage Eden\'s internal files
Modify the look of the app
No file manager found
- Could not open eden directory
+ Could not open Eden directory
Please locate the user folder with the file manager\'s side panel manually.
Manage save data
Save data found. Please select an option below.
@@ -340,7 +340,7 @@
Make sure the firmware nca files are at the root of the zip and try again.
Firmware uninstallation failed
Share debug logs
- Share eden\'s log file to debug issues
+ Share Eden\'s log file to debug issues
No log file found
Install game content
Install game updates or DLC
@@ -356,7 +356,7 @@
https://yuzu-mirror.github.io/help/quickstart/#dumping-installed-updates
Custom drivers not supported
Custom driver loading isn\'t currently supported for this device.\nCheck this option again in the future to see if support was added!
- Manage eden data
+ Manage Eden data
Import/export firmware, keys, user data, and more!
Share save file
Failed to export save
@@ -424,16 +424,16 @@
Copied to clipboard
An open-source Switch emulator
Contributors
- Contributors who made eden for Android possible
+ Contributors who made Eden for Android possible
https://git.eden-emu.dev/eden-emu/eden/activity/contributors
- Projects that make eden for Android possible
+ Projects that make Eden for Android possible
Build
User data
Import/export all app data.\n\nWhen importing user data, all existing user data will be deleted!\n\nImporting data from Citron may cause issues. It\'s recommended to manually import all needed data.
Exporting user data…
Importing user data…
Import user data
- Invalid eden backup
+ Invalid Eden backup
User data exported successfully
User data imported successfully
Export cancelled
@@ -770,6 +770,45 @@
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
+ Unable to apply the requested settings. This may be due to missing GPU drivers or configuration issues.
+ 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
@@ -997,7 +1036,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 @@
+
+