mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2025-07-20 04:45:47 +00:00
Driver Fetcher (#130)
Lets the user download & install drivers from various repositories. Co-authored-by: swurl <swurl@swurl.xyz> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/130 Co-authored-by: Aleksandr Popovich <alekpopo@pm.me> Co-committed-by: Aleksandr Popovich <alekpopo@pm.me>
This commit is contained in:
parent
810df8d01c
commit
972576d2c5
45 changed files with 1131 additions and 254 deletions
|
@ -225,11 +225,14 @@ dependencies {
|
|||
implementation("com.google.android.material:material:1.12.0")
|
||||
implementation("androidx.preference:preference-ktx:1.2.1")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7")
|
||||
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
||||
implementation("io.coil-kt:coil:2.2.2")
|
||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.17.2")
|
||||
implementation("androidx.window:window:1.3.0")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.2.1")
|
||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||
implementation("org.commonmark:commonmark:0.22.0")
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:2.8.9")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:2.8.9")
|
||||
implementation("info.debatty:java-string-similarity:2.0.0")
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
package org.yuzu.yuzu_emu.features.fetcher
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
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
|
||||
import androidx.transition.TransitionSet
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||
|
||||
class DriverGroupAdapter(
|
||||
private val activity: FragmentActivity,
|
||||
private val driverViewModel: DriverViewModel
|
||||
) : RecyclerView.Adapter<DriverGroupAdapter.DriverGroupViewHolder>() {
|
||||
private var driverGroups: List<DriverGroup> = emptyList()
|
||||
|
||||
inner class DriverGroupViewHolder(
|
||||
private val binding: ItemDriverGroupBinding
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
fun bind(group: DriverGroup) {
|
||||
val onClick = {
|
||||
TransitionManager.beginDelayedTransition(
|
||||
binding.root,
|
||||
TransitionSet().addTransition(Fade()).addTransition(ChangeBounds())
|
||||
.setDuration(200)
|
||||
)
|
||||
val isVisible = binding.recyclerReleases.isVisible
|
||||
|
||||
binding.recyclerReleases.visibility = if (isVisible) View.GONE else View.VISIBLE
|
||||
binding.imageDropdownArrow.rotation = if (isVisible) 0f else 180f
|
||||
|
||||
if (!isVisible && binding.recyclerReleases.adapter == null) {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
binding.recyclerReleases.layoutManager =
|
||||
LinearLayoutManager(binding.root.context)
|
||||
binding.recyclerReleases.adapter =
|
||||
ReleaseAdapter(group.releases, activity, driverViewModel)
|
||||
|
||||
binding.recyclerReleases.addItemDecoration(
|
||||
SpacingItemDecoration(
|
||||
(activity.resources.displayMetrics.density * 8).toInt()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.textGroupName.text = group.name
|
||||
binding.textGroupName.setOnClickListener { onClick() }
|
||||
|
||||
binding.imageDropdownArrow.setOnClickListener { onClick() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DriverGroupViewHolder {
|
||||
val binding = ItemDriverGroupBinding.inflate(
|
||||
LayoutInflater.from(parent.context), parent, false
|
||||
)
|
||||
return DriverGroupViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: DriverGroupViewHolder, position: Int) {
|
||||
holder.bind(driverGroups[position])
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = driverGroups.size
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun updateDriverGroups(newDriverGroups: List<DriverGroup>) {
|
||||
driverGroups = newDriverGroups
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,267 @@
|
|||
package org.yuzu.yuzu_emu.features.fetcher
|
||||
|
||||
import android.animation.LayoutTransition
|
||||
import android.content.res.ColorStateList
|
||||
import android.text.Html
|
||||
import android.text.Html.FROM_HTML_MODE_COMPACT
|
||||
import android.text.TextUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.ItemReleaseBinding
|
||||
import org.yuzu.yuzu_emu.fragments.DriverFetcherFragment.Release
|
||||
import androidx.core.net.toUri
|
||||
import androidx.transition.ChangeBounds
|
||||
import androidx.transition.Fade
|
||||
import androidx.transition.TransitionManager
|
||||
import androidx.transition.TransitionSet
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.commonmark.parser.Parser
|
||||
import org.commonmark.renderer.html.HtmlRenderer
|
||||
import org.yuzu.yuzu_emu.databinding.DialogProgressBinding
|
||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
|
||||
class ReleaseAdapter(
|
||||
private val releases: List<Release>,
|
||||
private val activity: FragmentActivity,
|
||||
private val driverViewModel: DriverViewModel
|
||||
) : RecyclerView.Adapter<ReleaseAdapter.ReleaseViewHolder>() {
|
||||
|
||||
inner class ReleaseViewHolder(
|
||||
private val binding: ItemReleaseBinding
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
private var isPreview: Boolean = true
|
||||
private val client = OkHttpClient()
|
||||
private val markdownParser = Parser.builder().build()
|
||||
private val htmlRenderer = HtmlRenderer.builder().build()
|
||||
|
||||
init {
|
||||
binding.root.let { root ->
|
||||
val layoutTransition = root.layoutTransition ?: LayoutTransition().apply {
|
||||
enableTransitionType(LayoutTransition.CHANGING)
|
||||
setDuration(125)
|
||||
}
|
||||
root.layoutTransition = layoutTransition
|
||||
}
|
||||
|
||||
(binding.textBody.parent as ViewGroup).isTransitionGroup = false
|
||||
binding.containerDownloads.isTransitionGroup = false
|
||||
}
|
||||
|
||||
fun bind(release: Release) {
|
||||
binding.textReleaseName.text = release.title
|
||||
binding.badgeLatest.isVisible = release.latest
|
||||
|
||||
// truncates to 150 chars so it does not take up too much space.
|
||||
var bodyPreview = release.body.take(150)
|
||||
bodyPreview = bodyPreview.replace("#", "").removeSurrounding(" ");
|
||||
|
||||
val body =
|
||||
bodyPreview.replace("\\r\\n", "\n").replace("\\n", "\n").replace("\n", "<br>")
|
||||
|
||||
binding.textBody.text = Html.fromHtml(body, FROM_HTML_MODE_COMPACT)
|
||||
|
||||
binding.textBody.setOnClickListener {
|
||||
TransitionManager.beginDelayedTransition(
|
||||
binding.root,
|
||||
TransitionSet().addTransition(Fade()).addTransition(ChangeBounds())
|
||||
.setDuration(100)
|
||||
)
|
||||
|
||||
isPreview = !isPreview
|
||||
if (isPreview) {
|
||||
val body = bodyPreview.replace("\\r\\n", "\n").replace("\\n", "\n")
|
||||
.replace("\n", "<br>")
|
||||
|
||||
binding.textBody.text = Html.fromHtml(body, FROM_HTML_MODE_COMPACT)
|
||||
binding.textBody.maxLines = 3
|
||||
binding.textBody.ellipsize = TextUtils.TruncateAt.END
|
||||
} else {
|
||||
val body = release.body.replace("\\r\\n", "\n\n").replace("\\n", "\n\n")
|
||||
|
||||
try {
|
||||
val doc = markdownParser.parse(body)
|
||||
val html = htmlRenderer.render(doc)
|
||||
binding.textBody.text = Html.fromHtml(html, Html.FROM_HTML_MODE_COMPACT)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
binding.textBody.text = body
|
||||
}
|
||||
|
||||
binding.textBody.maxLines = Integer.MAX_VALUE
|
||||
binding.textBody.ellipsize = null
|
||||
}
|
||||
}
|
||||
|
||||
val onDownloadsClick = {
|
||||
val isVisible = binding.containerDownloads.isVisible
|
||||
TransitionManager.beginDelayedTransition(
|
||||
binding.root,
|
||||
TransitionSet().addTransition(Fade()).addTransition(ChangeBounds())
|
||||
.setDuration(100)
|
||||
)
|
||||
|
||||
binding.containerDownloads.isVisible = !isVisible
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
binding.buttonToggleDownloads.setOnClickListener {
|
||||
onDownloadsClick()
|
||||
}
|
||||
|
||||
binding.imageDownloadsArrow.setOnClickListener {
|
||||
onDownloadsClick()
|
||||
}
|
||||
|
||||
binding.containerDownloads.removeAllViews()
|
||||
|
||||
release.artifacts.forEach { artifact ->
|
||||
val button = MaterialButton(binding.root.context).apply {
|
||||
text = artifact.name
|
||||
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))
|
||||
setIconResource(R.drawable.ic_import)
|
||||
iconTint = ColorStateList.valueOf(
|
||||
MaterialColors.getColor(
|
||||
this,
|
||||
com.google.android.material.R.attr.colorPrimary
|
||||
)
|
||||
)
|
||||
|
||||
elevation = 6f
|
||||
layoutParams = ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
setOnClickListener {
|
||||
val dialogBinding =
|
||||
DialogProgressBinding.inflate(LayoutInflater.from(context))
|
||||
dialogBinding.progressBar.isIndeterminate = true
|
||||
dialogBinding.title.text = context.getString(R.string.installing_driver)
|
||||
dialogBinding.status.text = context.getString(R.string.downloading)
|
||||
|
||||
val progressDialog = MaterialAlertDialogBuilder(context)
|
||||
.setView(dialogBinding.root)
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
|
||||
progressDialog.show()
|
||||
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
try {
|
||||
val request = Request.Builder()
|
||||
.url(artifact.url)
|
||||
.header("Accept", "application/octet-stream")
|
||||
.build()
|
||||
|
||||
val cacheDir = context.externalCacheDir ?: throw IOException(
|
||||
context.getString(R.string.failed_cache_dir)
|
||||
)
|
||||
|
||||
cacheDir.mkdirs()
|
||||
|
||||
val file = File(cacheDir, artifact.name)
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
client.newBuilder()
|
||||
.followRedirects(true)
|
||||
.followSslRedirects(true)
|
||||
.build()
|
||||
.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) {
|
||||
throw IOException("${response.code}")
|
||||
}
|
||||
|
||||
response.body?.byteStream()?.use { input ->
|
||||
FileOutputStream(file).use { output ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
?: throw IOException(context.getString(R.string.empty_response_body))
|
||||
}
|
||||
}
|
||||
|
||||
if (file.length() == 0L) {
|
||||
throw IOException(context.getString(R.string.driver_empty))
|
||||
}
|
||||
|
||||
dialogBinding.status.text = context.getString(R.string.installing)
|
||||
|
||||
val driverData = GpuDriverHelper.getMetadataFromZip(file)
|
||||
val driverPath =
|
||||
"${GpuDriverHelper.driverStoragePath}${FileUtil.getFilename(file.toUri())}"
|
||||
|
||||
if (GpuDriverHelper.copyDriverToInternalStorage(file.toUri())) {
|
||||
driverViewModel.onDriverAdded(Pair(driverPath, driverData))
|
||||
|
||||
progressDialog.dismiss()
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(
|
||||
R.string.successfully_installed,
|
||||
driverData.name
|
||||
),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} else {
|
||||
throw IOException(
|
||||
context.getString(
|
||||
R.string.failed_install_driver,
|
||||
artifact.name
|
||||
)
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
progressDialog.dismiss()
|
||||
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle(context.getString(R.string.driver_failed_title))
|
||||
.setMessage(e.message)
|
||||
.setPositiveButton(R.string.ok) { dialog, _ ->
|
||||
dialog.cancel()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.containerDownloads.addView(button)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ReleaseViewHolder {
|
||||
val binding = ItemReleaseBinding.inflate(
|
||||
LayoutInflater.from(parent.context), parent, false
|
||||
)
|
||||
return ReleaseViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ReleaseViewHolder, position: Int) {
|
||||
holder.bind(releases[position])
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = releases.size
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package org.yuzu.yuzu_emu.features.fetcher
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
class SpacingItemDecoration(private val spacing: Int) : RecyclerView.ItemDecoration() {
|
||||
override fun getItemOffsets(
|
||||
outRect: Rect,
|
||||
view: View,
|
||||
parent: RecyclerView,
|
||||
state: RecyclerView.State
|
||||
) {
|
||||
outRect.bottom = spacing
|
||||
if (parent.getChildAdapterPosition(view) == 0) {
|
||||
outRect.top = spacing
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ object Settings {
|
|||
YuzuApplication.appContext.getString(R.string.preferences_player, player)
|
||||
|
||||
const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
|
||||
const val PREF_SHOULD_SHOW_DRIVER_WARNING = "ShouldShowDriverWarning"
|
||||
const val PREF_SHOULD_SHOW_PRE_ALPHA_WARNING = "ShouldShowPreAlphaWarning"
|
||||
const val PREF_SHOULD_SHOW_PRE_ALPHA_BANNER = "ShouldShowPreAlphaBanner"
|
||||
const val PREF_SHOULD_SHOW_EDENS_VEIL_DIALOG = "ShouldShowEdensVeilDialog"
|
||||
|
|
|
@ -0,0 +1,307 @@
|
|||
package org.yuzu.yuzu_emu.fragments
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.FragmentDriverFetcherBinding
|
||||
import org.yuzu.yuzu_emu.features.fetcher.DriverGroupAdapter
|
||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||
import org.yuzu.yuzu_emu.utils.Log
|
||||
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
import kotlin.getValue
|
||||
|
||||
class DriverFetcherFragment : Fragment() {
|
||||
private var _binding: FragmentDriverFetcherBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private val client = OkHttpClient()
|
||||
|
||||
private val gpuModel: String?
|
||||
get() = GpuDriverHelper.getGpuModel()
|
||||
|
||||
private val adrenoModel: Int
|
||||
get() = parseAdrenoModel()
|
||||
|
||||
private val recommendedDriver: String
|
||||
get() = driverMap.firstOrNull { adrenoModel in it.first }?.second ?: "Unsupported"
|
||||
|
||||
private data class DriverRepo(
|
||||
val name: String = "",
|
||||
val path: String = "",
|
||||
val sort: Int = 0,
|
||||
val useTagName: Boolean = false
|
||||
)
|
||||
|
||||
private val repoList: List<DriverRepo> = 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),
|
||||
DriverRepo("Weab-Chan Freedreno", "Weab-chan/freedreno_turnip-CI", 3),
|
||||
)
|
||||
|
||||
private val driverMap = listOf(
|
||||
IntRange(Integer.MIN_VALUE, 9) to "Unsupported",
|
||||
IntRange(10, 99) to "KIMCHI Latest",
|
||||
IntRange(100, 599) to "Unsupported",
|
||||
IntRange(600, 639) to "Mr. Purple EOL-24.3.4",
|
||||
IntRange(640, 699) to "Mr. Purple T19",
|
||||
IntRange(700, 799) to "Mr. Purple T20", // TODO: Await T21 and update accordingly
|
||||
IntRange(800, 899) to "GameHub Adreno 8xx",
|
||||
IntRange(900, Int.MAX_VALUE) to "Unsupported",
|
||||
)
|
||||
|
||||
private lateinit var driverGroupAdapter: DriverGroupAdapter
|
||||
private val driverViewModel: DriverViewModel by activityViewModels()
|
||||
|
||||
fun parseAdrenoModel(): Int {
|
||||
if (gpuModel == null) {
|
||||
return 0
|
||||
}
|
||||
|
||||
val modelList = gpuModel!!.split(" ")
|
||||
|
||||
// format: Adreno (TM) <ModelNumber>
|
||||
if (modelList.size < 3 || modelList[0] != "Adreno") {
|
||||
return 0
|
||||
}
|
||||
|
||||
val model = modelList[2]
|
||||
|
||||
try {
|
||||
// special case for Axx GPUs (e.g. AYANEO Pocket S2)
|
||||
// driverMap has specific ranges for this
|
||||
// needs to be fixed
|
||||
if (model.startsWith("A")) {
|
||||
return model.substring(1).toInt()
|
||||
}
|
||||
|
||||
return model.toInt()
|
||||
} catch (e: Exception) {
|
||||
// Model parse error, just say unsupported
|
||||
e.printStackTrace()
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
_binding = FragmentDriverFetcherBinding.inflate(inflater)
|
||||
binding.badgeRecommendedDriver.text = recommendedDriver
|
||||
binding.badgeGpuModel.text = gpuModel
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.toolbarDrivers.setNavigationOnClickListener {
|
||||
binding.root.findNavController().popBackStack()
|
||||
}
|
||||
|
||||
binding.listDrivers.layoutManager = LinearLayoutManager(context)
|
||||
driverGroupAdapter = DriverGroupAdapter(requireActivity(), driverViewModel)
|
||||
binding.listDrivers.adapter = driverGroupAdapter
|
||||
|
||||
setInsets()
|
||||
|
||||
fetchDrivers()
|
||||
}
|
||||
|
||||
private fun fetchDrivers() {
|
||||
binding.loadingIndicator.isVisible = true
|
||||
|
||||
val driverGroups = arrayListOf<DriverGroup>()
|
||||
|
||||
repoList.forEach { driver ->
|
||||
val name = driver.name
|
||||
val path = driver.path
|
||||
val useTagName = driver.useTagName
|
||||
val sort = driver.sort
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val request = Request.Builder()
|
||||
.url("https://api.github.com/repos/$path/releases")
|
||||
.build()
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
var releases: ArrayList<Release>
|
||||
try {
|
||||
client.newCall(request).execute().use { response ->
|
||||
if (!response.isSuccessful) {
|
||||
throw IOException(response.body.toString())
|
||||
}
|
||||
|
||||
val body = response.body?.string() ?: return@withContext
|
||||
releases = Release.fromJsonArray(body, useTagName)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
MaterialAlertDialogBuilder(requireActivity().applicationContext)
|
||||
.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()
|
||||
|
||||
releases = ArrayList<Release>()
|
||||
}
|
||||
}
|
||||
|
||||
val driver = DriverGroup(
|
||||
name,
|
||||
releases,
|
||||
sort
|
||||
)
|
||||
|
||||
synchronized(driverGroups) {
|
||||
driverGroups.add(driver)
|
||||
driverGroups.sortBy {
|
||||
it.sort
|
||||
}
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
driverGroupAdapter.updateDriverGroups(driverGroups)
|
||||
|
||||
if (driverGroups.size >= repoList.size) {
|
||||
binding.loadingIndicator.isVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setInsets() =
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
binding.root
|
||||
) { _: View, windowInsets: WindowInsetsCompat ->
|
||||
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||
|
||||
val leftInsets = barInsets.left + cutoutInsets.left
|
||||
val rightInsets = barInsets.right + cutoutInsets.right
|
||||
|
||||
binding.toolbarDrivers.updateMargins(left = leftInsets, right = rightInsets)
|
||||
binding.listDrivers.updateMargins(left = leftInsets, right = rightInsets)
|
||||
|
||||
binding.listDrivers.updatePadding(
|
||||
bottom = barInsets.bottom +
|
||||
resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab)
|
||||
)
|
||||
|
||||
windowInsets
|
||||
}
|
||||
|
||||
data class Artifact(val url: URL, val name: String)
|
||||
|
||||
data class Release(
|
||||
var tagName: String = "",
|
||||
var title: String = "",
|
||||
var body: String = "",
|
||||
var artifacts: List<Artifact> = ArrayList<Artifact>(),
|
||||
var prerelease: Boolean = false,
|
||||
var latest: Boolean = false
|
||||
) {
|
||||
companion object {
|
||||
fun fromJsonArray(jsonString: String, useTagName: Boolean): ArrayList<Release> {
|
||||
val mapper = jacksonObjectMapper()
|
||||
|
||||
try {
|
||||
val rootNode = mapper.readTree(jsonString)
|
||||
|
||||
val releases = ArrayList<Release>()
|
||||
|
||||
var latestRelease: Release? = null
|
||||
|
||||
if (rootNode.isArray) {
|
||||
rootNode.forEach { node ->
|
||||
val release = fromJson(node, useTagName)
|
||||
|
||||
if (latestRelease == null && !release.prerelease) {
|
||||
latestRelease = release
|
||||
release.latest = true
|
||||
}
|
||||
|
||||
releases.add(release)
|
||||
}
|
||||
}
|
||||
|
||||
return releases
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return ArrayList<Release>()
|
||||
}
|
||||
}
|
||||
|
||||
fun fromJson(node: JsonNode, useTagName: Boolean): Release {
|
||||
try {
|
||||
val tagName = node.get("tag_name").toString().removeSurrounding("\"")
|
||||
val body = node.get("body").toString().removeSurrounding("\"")
|
||||
val prerelease = node.get("prerelease").toString().toBoolean()
|
||||
val title = if (useTagName) tagName else node.get("name").toString().removeSurrounding("\"")
|
||||
|
||||
val assets = node.get("assets")
|
||||
val artifacts = ArrayList<Artifact>()
|
||||
if (assets?.isArray == true) {
|
||||
assets.forEach { node ->
|
||||
val urlStr =
|
||||
node.get("browser_download_url").toString().removeSurrounding("\"")
|
||||
|
||||
val url = URL(urlStr)
|
||||
val name = node.get("name").toString().removeSurrounding("\"")
|
||||
|
||||
val artifact = Artifact(url, name)
|
||||
artifacts.add(artifact)
|
||||
}
|
||||
}
|
||||
|
||||
return Release(tagName, title, body, artifacts, prerelease)
|
||||
} catch (e: Exception) {
|
||||
// TODO: handle malformed input.
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
return Release()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class DriverGroup(
|
||||
val name: String,
|
||||
val releases: ArrayList<Release>,
|
||||
val sort: Int,
|
||||
)
|
||||
}
|
|
@ -8,6 +8,7 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updatePadding
|
||||
|
@ -15,6 +16,7 @@ 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.GridLayoutManager
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -23,6 +25,8 @@ 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
|
||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||
|
@ -103,6 +107,10 @@ class DriverManagerFragment : Fragment() {
|
|||
getDriver.launch(arrayOf("application/zip"))
|
||||
}
|
||||
|
||||
binding.buttonFetch.setOnClickListener {
|
||||
binding.root.findNavController().navigate(R.id.action_driverManagerFragment_to_driverFetcherFragment)
|
||||
}
|
||||
|
||||
binding.listDrivers.apply {
|
||||
layoutManager = GridLayoutManager(
|
||||
requireContext(),
|
||||
|
@ -112,6 +120,10 @@ class DriverManagerFragment : Fragment() {
|
|||
}
|
||||
|
||||
setInsets()
|
||||
|
||||
if (!GpuDriverHelper.supportsCustomDriverLoading()) {
|
||||
showDriverWarningDialog()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
@ -139,6 +151,12 @@ class DriverManagerFragment : Fragment() {
|
|||
bottom = barInsets.bottom + fabSpacing
|
||||
)
|
||||
|
||||
binding.buttonFetch.updateMargins(
|
||||
left = leftInsets + fabSpacing,
|
||||
right = rightInsets + fabSpacing,
|
||||
bottom = barInsets.bottom + fabSpacing
|
||||
)
|
||||
|
||||
binding.listDrivers.updatePadding(
|
||||
bottom = barInsets.bottom +
|
||||
resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab)
|
||||
|
@ -195,4 +213,26 @@ class DriverManagerFragment : Fragment() {
|
|||
return@newInstance Any()
|
||||
}.show(childFragmentManager, ProgressDialogFragment.TAG)
|
||||
}
|
||||
|
||||
fun showDriverWarningDialog() {
|
||||
val shouldDisplayGpuWarning =
|
||||
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
.getBoolean(Settings.PREF_SHOULD_SHOW_DRIVER_WARNING, true)
|
||||
if (shouldDisplayGpuWarning) {
|
||||
MessageDialogFragment.newInstance(
|
||||
activity,
|
||||
titleId = R.string.unsupported_gpu,
|
||||
descriptionId = R.string.unsupported_gpu_warning,
|
||||
positiveButtonTitleId = R.string.dont_show_again,
|
||||
negativeButtonTitleId = R.string.close,
|
||||
showNegativeButton = true,
|
||||
positiveAction = {
|
||||
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
.edit() {
|
||||
putBoolean(Settings.PREF_SHOULD_SHOW_DRIVER_WARNING, false)
|
||||
}
|
||||
}
|
||||
).show(requireActivity().supportFragmentManager, MessageDialogFragment.TAG)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,7 +116,7 @@ class HomeSettingsFragment : Fragment() {
|
|||
.actionHomeSettingsFragmentToDriverManagerFragment(null)
|
||||
binding.root.findNavController().navigate(action)
|
||||
},
|
||||
{ GpuDriverHelper.supportsCustomDriverLoading() },
|
||||
{true},
|
||||
R.string.custom_driver_not_supported,
|
||||
R.string.custom_driver_not_supported_description,
|
||||
driverViewModel.selectedDriverTitle
|
||||
|
|
|
@ -455,7 +455,7 @@ class GamesFragment : Fragment() {
|
|||
val leftInsets = barInsets.left + cutoutInsets.left
|
||||
val rightInsets = barInsets.right + cutoutInsets.right
|
||||
val mlpSwipe = binding.swipeRefresh.layoutParams as ViewGroup.MarginLayoutParams
|
||||
if (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_LTR) {
|
||||
if (view.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR) {
|
||||
mlpSwipe.leftMargin = leftInsets
|
||||
mlpSwipe.rightMargin = rightInsets
|
||||
} else {
|
||||
|
|
|
@ -25,13 +25,10 @@ import androidx.core.view.WindowCompat
|
|||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.navigation.NavigationBarView
|
||||
import java.io.File
|
||||
import java.io.FilenameFilter
|
||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.nio.charset.StandardCharsets
|
|||
import java.util.zip.Deflater
|
||||
import java.util.zip.ZipOutputStream
|
||||
import kotlin.IllegalStateException
|
||||
import androidx.core.net.toUri
|
||||
|
||||
object FileUtil {
|
||||
const val PATH_TREE = "tree"
|
||||
|
@ -195,6 +196,10 @@ object FileUtil {
|
|||
* @return String display name
|
||||
*/
|
||||
fun getFilename(uri: Uri): String {
|
||||
if (uri.scheme == "file") {
|
||||
return uri.lastPathSegment?.takeIf { it.isNotEmpty() } ?: throw IOException("Invalid file URI: $uri")
|
||||
}
|
||||
|
||||
val resolver = YuzuApplication.appContext.contentResolver
|
||||
val columns = arrayOf(
|
||||
DocumentsContract.Document.COLUMN_DISPLAY_NAME
|
||||
|
@ -236,7 +241,7 @@ object FileUtil {
|
|||
var size: Long = 0
|
||||
var c: Cursor? = null
|
||||
try {
|
||||
val mUri = Uri.parse(path)
|
||||
val mUri = path.toUri()
|
||||
c = resolver.query(mUri, columns, null, null, null)
|
||||
c!!.moveToNext()
|
||||
size = c.getLong(0)
|
||||
|
|
|
@ -202,6 +202,11 @@ object GpuDriverHelper {
|
|||
hookLibPath: String = GpuDriverHelper.hookLibPath!!
|
||||
): Array<String>?
|
||||
|
||||
external fun getGpuModel(
|
||||
surface: Surface = Surface(SurfaceTexture(true)),
|
||||
hookLibPath: String = GpuDriverHelper.hookLibPath!!
|
||||
): String?
|
||||
|
||||
// Parse the custom driver metadata to retrieve the name.
|
||||
val installedCustomDriverData: GpuDriverMetadata
|
||||
get() = GpuDriverMetadata(File(driverInstallationPath + META_JSON_FILENAME))
|
||||
|
|
|
@ -567,6 +567,30 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getSystemDriverInfo(
|
|||
return j_driver_info;
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_getGpuModel(JNIEnv *env, jobject j_obj, jobject j_surf, jstring j_hook_lib_dir) {
|
||||
const char* file_redirect_dir_{};
|
||||
int featureFlags{};
|
||||
std::string hook_lib_dir = Common::Android::GetJString(env, j_hook_lib_dir);
|
||||
auto handle = adrenotools_open_libvulkan(RTLD_NOW, featureFlags, nullptr, hook_lib_dir.c_str(),
|
||||
nullptr, nullptr, file_redirect_dir_, nullptr);
|
||||
auto driver_library = std::make_shared<Common::DynamicLibrary>(handle);
|
||||
InputCommon::InputSubsystem input_subsystem;
|
||||
auto window =
|
||||
std::make_unique<EmuWindow_Android>(ANativeWindow_fromSurface(env, j_surf), driver_library);
|
||||
|
||||
Vulkan::vk::InstanceDispatch dld;
|
||||
Vulkan::vk::Instance vk_instance = Vulkan::CreateInstance(
|
||||
*driver_library, dld, VK_API_VERSION_1_1, Core::Frontend::WindowSystemType::Android);
|
||||
|
||||
auto surface = Vulkan::CreateSurface(vk_instance, window->GetWindowInfo());
|
||||
|
||||
auto device = Vulkan::CreateDevice(vk_instance, dld, *surface);
|
||||
|
||||
const std::string model_name{device.GetModelName()};
|
||||
|
||||
return Common::Android::ToJString(env, model_name);
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env, jclass clazz) {
|
||||
Core::Crypto::KeyManager::Instance().ReloadKeys();
|
||||
return static_cast<jboolean>(Core::Crypto::KeyManager::Instance().AreKeysLoaded());
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M7,10l5,5 5,-5" />
|
||||
</vector>
|
14
src/android/app/src/main/res/drawable/item_release_box.xml
Normal file
14
src/android/app/src/main/res/drawable/item_release_box.xml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="?attr/colorSurface" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="?attr/colorOutline" />
|
||||
<corners android:radius="8dp" />
|
||||
<padding
|
||||
android:left="12dp"
|
||||
android:top="12dp"
|
||||
android:right="12dp"
|
||||
android:bottom="12dp" />
|
||||
</shape>
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="?attr/colorSurfaceVariant" />
|
||||
<corners android:radius="8dp" />
|
||||
<padding
|
||||
android:left="8dp"
|
||||
android:right="8dp"
|
||||
android:top="0dp"
|
||||
android:bottom="0dp" />
|
||||
</shape>
|
34
src/android/app/src/main/res/layout/dialog_progress.xml
Normal file
34
src/android/app/src/main/res/layout/dialog_progress.xml
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/dialog_progress_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:textAlignment="center"
|
||||
android:textAppearance="?attr/textAppearanceHeadline6"
|
||||
android:textColor="?attr/colorPrimary" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:indeterminate="true"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="center"
|
||||
android:textAppearance="?attr/textAppearanceBody1"
|
||||
android:textColor="?attr/colorOnSurface" />
|
||||
|
||||
</LinearLayout>
|
116
src/android/app/src/main/res/layout/fragment_driver_fetcher.xml
Normal file
116
src/android/app/src/main/res/layout/fragment_driver_fetcher.xml
Normal file
|
@ -0,0 +1,116 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/coordinator_licenses"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/colorSurface">
|
||||
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar_drivers"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true"
|
||||
android:touchscreenBlocksFocus="false"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
|
||||
app:liftOnScrollTargetViewId="@id/list_drivers">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar_drivers"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:touchscreenBlocksFocus="false"
|
||||
app:navigationIcon="@drawable/ic_back"
|
||||
app:title="@string/gpu_driver_fetcher" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/label_gpu_model"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:padding="8dp"
|
||||
android:text="@string/gpu_model"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/appbar_drivers" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/badge_gpu_model"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:background="@drawable/item_release_latest_badge_background"
|
||||
android:textColor="?attr/colorPrimary"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/label_gpu_model"
|
||||
app:layout_constraintStart_toEndOf="@id/label_gpu_model"
|
||||
app:layout_constraintTop_toTopOf="@id/label_gpu_model" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/label_recommended_driver"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:padding="8dp"
|
||||
android:text="@string/recommended_driver"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/label_gpu_model" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/badge_recommended_driver"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:background="@drawable/item_release_latest_badge_background"
|
||||
android:textColor="?attr/colorPrimary"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/label_recommended_driver"
|
||||
app:layout_constraintStart_toEndOf="@id/label_recommended_driver"
|
||||
app:layout_constraintTop_toTopOf="@id/label_recommended_driver" />
|
||||
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="?attr/colorOutline"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/label_recommended_driver" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list_drivers"
|
||||
android:layout_height="0dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginTop="8dp"
|
||||
android:clipToPadding="false"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
app:layout_constraintTop_toBottomOf="@id/divider"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/loadingIndicator"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintEnd_toEndOf="@id/list_drivers"
|
||||
app:layout_constraintStart_toStartOf="@id/list_drivers"
|
||||
app:layout_constraintTop_toTopOf="@id/list_drivers" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -7,6 +7,7 @@
|
|||
android:background="?attr/colorSurface">
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/coordinatorLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
@ -47,4 +48,14 @@
|
|||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
android:id="@+id/button_fetch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|start"
|
||||
android:text="@string/fetch"
|
||||
app:icon="@drawable/ic_import"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
39
src/android/app/src/main/res/layout/item_driver_group.xml
Normal file
39
src/android/app/src/main/res/layout/item_driver_group.xml
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_group_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="?attr/colorControlNormal"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/image_dropdown_arrow" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_dropdown_arrow"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@drawable/ic_dropdown_arrow"
|
||||
app:tint="?attr/colorControlNormal"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:contentDescription="@string/show_releases" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_releases"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_group_name"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:layout_marginTop="8dp" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
82
src/android/app/src/main/res/layout/item_release.xml
Normal file
82
src/android/app/src/main/res/layout/item_release.xml
Normal file
|
@ -0,0 +1,82 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
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:background="@drawable/item_release_box"
|
||||
android:padding="8dp"
|
||||
android:paddingStart="12dp"
|
||||
tools:ignore="RtlSymmetry">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_release_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/badge_latest"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/latest"
|
||||
android:textColor="?attr/colorPrimary"
|
||||
android:textSize="14sp"
|
||||
android:background="@drawable/item_release_latest_badge_background"
|
||||
app:layout_constraintStart_toEndOf="@id/text_release_name"
|
||||
app:layout_constraintTop_toTopOf="@id/text_release_name"
|
||||
app:layout_constraintBottom_toBottomOf="@id/text_release_name"
|
||||
android:layout_marginStart="8dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_body"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="3"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_release_name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/button_toggle_downloads"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/show_downloads"
|
||||
android:textStyle="bold"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_body"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginStart="8dp"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_downloads_arrow"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:contentDescription="@string/show_downloads"
|
||||
android:src="@drawable/ic_dropdown_arrow"
|
||||
app:layout_constraintBottom_toBottomOf="@id/button_toggle_downloads"
|
||||
app:layout_constraintStart_toEndOf="@id/button_toggle_downloads"
|
||||
app:layout_constraintTop_toTopOf="@id/button_toggle_downloads"
|
||||
app:tint="?attr/colorControlNormal" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/container_downloads"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@id/button_toggle_downloads"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:layout_marginTop="8dp" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/home_navigation"
|
||||
app:startDestination="@id/gamesFragment">
|
||||
|
||||
|
@ -111,6 +112,9 @@
|
|||
app:argType="org.yuzu.yuzu_emu.model.Game"
|
||||
app:nullable="true"
|
||||
android:defaultValue="@null" />
|
||||
<action
|
||||
android:id="@+id/action_driverManagerFragment_to_driverFetcherFragment"
|
||||
app:destination="@id/driverFetcherFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/appletLauncherFragment"
|
||||
|
@ -164,5 +168,10 @@
|
|||
android:name="game"
|
||||
app:argType="org.yuzu.yuzu_emu.model.Game" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/driverFetcherFragment"
|
||||
android:name="org.yuzu.yuzu_emu.fragments.DriverFetcherFragment"
|
||||
android:label="fragment_driver_fetcher"
|
||||
tools:layout="@layout/fragment_driver_fetcher" />
|
||||
|
||||
</navigation>
|
||||
|
|
|
@ -365,17 +365,6 @@
|
|||
<string name="user_data_import_success">تم استيراد بيانات المستخدم بنجاح</string>
|
||||
<string name="user_data_export_cancelled">تم إلغاء التصدير</string>
|
||||
|
||||
<!-- Early access upgrade strings -->
|
||||
<string name="early_access">الوصول المبكر</string>
|
||||
<string name="early_access_benefits">مزايا الوصول المبكر</string>
|
||||
<string name="cutting_edge_features">ميزات متطورة</string>
|
||||
<string name="early_access_updates">الوصول المبكر إلى التحديثات</string>
|
||||
<string name="no_manual_installation">لا يوجد التثبيت اليدوي</string>
|
||||
<string name="prioritized_support">الدعم ذو الأولوية</string>
|
||||
<string name="helping_game_preservation">المساعدة في الحفاظ على اللعبة</string>
|
||||
<string name="our_eternal_gratitude">امتناننا الأبدي</string>
|
||||
<string name="are_you_interested">هل انت مهتم؟</string>
|
||||
|
||||
<!-- General settings strings -->
|
||||
<string name="frame_limit_enable">الحد من السرعة</string>
|
||||
<string name="frame_limit_enable_description">يحد من سرعة المحاكاة بنسبة محددة من السرعة العادية</string>
|
||||
|
|
|
@ -334,17 +334,6 @@
|
|||
<string name="licenses_description">ئەو پڕۆژانەی کە یوزوی بۆ ئەندرۆید ڕەخساند</string>
|
||||
<string name="build">بونیات</string>
|
||||
|
||||
<!-- Early access upgrade strings -->
|
||||
<string name="early_access">بەزوویی دەسپێگەشتن</string>
|
||||
<string name="early_access_benefits">سوودەکانی بەزوویی دەسپێگەشتن</string>
|
||||
<string name="cutting_edge_features">تایبەتمەندییە پێشکەوتووەکان</string>
|
||||
<string name="early_access_updates">زوو دەستگەیشتن بە نوێکارییەکان</string>
|
||||
<string name="no_manual_installation">چیتر دامەزراندنی دەستی نییە</string>
|
||||
<string name="prioritized_support">پشتگیری لە پێشینە</string>
|
||||
<string name="helping_game_preservation">یارمەتیدانی پاراستنی یارییەکان</string>
|
||||
<string name="our_eternal_gratitude">سوپاس و پێزانینی هەمیشەییمان</string>
|
||||
<string name="are_you_interested">ئایا تۆ خوازیاریت؟</string>
|
||||
|
||||
<!-- General settings strings -->
|
||||
<string name="frame_limit_enable">سنووردارکردنی خێرایی</string>
|
||||
<string name="frame_limit_enable_description">خێرایی ئیمولەیشن سنووردار دەکات بۆ ڕێژەیەکی دیاریکراو لە خێرایی ئاسایی.</string>
|
||||
|
|
|
@ -326,10 +326,6 @@
|
|||
<string name="user_data_import_success">Uživatelská data byla úspěšně importována.</string>
|
||||
<string name="user_data_export_cancelled">Export zrušen</string>
|
||||
|
||||
<string name="no_manual_installation">Žádná manuální instalace</string>
|
||||
<string name="prioritized_support">Prioritní podpora</string>
|
||||
<string name="our_eternal_gratitude">Naše věčná vděčnost</string>
|
||||
<string name="are_you_interested">Máte zájem?</string>
|
||||
|
||||
<!-- General settings strings -->
|
||||
<string name="frame_limit_enable">Omezit rychlost</string>
|
||||
|
|
|
@ -358,17 +358,6 @@ Wirklich fortfahren?</string>
|
|||
<string name="user_data_import_success">Nutzerdaten erfolgreich importiert</string>
|
||||
<string name="user_data_export_cancelled">Export abgebrochen</string>
|
||||
|
||||
<!-- Early access upgrade strings -->
|
||||
<string name="early_access">Early Access</string>
|
||||
<string name="early_access_benefits">Early Access Vorteile</string>
|
||||
<string name="cutting_edge_features">Neueste Features</string>
|
||||
<string name="early_access_updates">Früherer Zugriff auf Updates</string>
|
||||
<string name="no_manual_installation">Keine manuelle Installation</string>
|
||||
<string name="prioritized_support">Priorisierte Unterstützung</string>
|
||||
<string name="helping_game_preservation">Beitrag zur Erhaltung der Spiele</string>
|
||||
<string name="our_eternal_gratitude">Unsere ewige Dankbarkeit</string>
|
||||
<string name="are_you_interested">Bist du interessiert?</string>
|
||||
|
||||
<!-- General settings strings -->
|
||||
<string name="frame_limit_enable">Limitierte Geschwindigkeit</string>
|
||||
<string name="frame_limit_enable_description">Limitiert die Geschwindigkeit auf einen von dir festgelegten Prozentsatz.</string>
|
||||
|
|
|
@ -390,17 +390,6 @@
|
|||
<string name="user_data_export_cancelled">Exportación cancelada</string>
|
||||
<string name="user_data_import_failed_description">Asegúrese de que las carpetas de datos de usuario estén en la raíz de la carpeta del zip y contengan un archivo config en config/config.ini e inténtelo de nuevo.</string>
|
||||
|
||||
<!-- Early access upgrade strings -->
|
||||
<string name="early_access">Early Access</string>
|
||||
<string name="early_access_benefits">Beneficios Early Access</string>
|
||||
<string name="cutting_edge_features">Características de vanguardia</string>
|
||||
<string name="early_access_updates">Acceso anticipado a las actualizaciones</string>
|
||||
<string name="no_manual_installation">Sin instalación manual</string>
|
||||
<string name="prioritized_support">Soporte prioritario</string>
|
||||
<string name="helping_game_preservation">Ayudarás a la preservación de juegos</string>
|
||||
<string name="our_eternal_gratitude">Nuestra eterna gratitud</string>
|
||||
<string name="are_you_interested">¿Estás interesado?</string>
|
||||
|
||||
<!-- General settings strings -->
|
||||
<string name="frame_limit_enable">Limitar velocidad</string>
|
||||
<string name="frame_limit_enable_description">Limita la velocidad de emulación a un porcentaje específico de la velocidad normal.</string>
|
||||
|
|
|
@ -388,17 +388,6 @@
|
|||
<string name="user_data_export_cancelled">صدور لغو شد</string>
|
||||
<string name="user_data_import_failed_description">مطمئن شوید که پوشههای داده کاربر در ریشه پوشه zip و حاوی یک فایل پیکربندی در config/config.ini هستند سپس دوباره امتحان کنید.</string>
|
||||
|
||||
<!-- Early access upgrade strings -->
|
||||
<string name="early_access">دسترسی زودهنگام</string>
|
||||
<string name="early_access_benefits">مزایای دسترسی زودهنگام</string>
|
||||
<string name="cutting_edge_features">ویژگیهای پیشرفته</string>
|
||||
<string name="early_access_updates">دسترسی زودهنگام به بروزرسانیها</string>
|
||||
<string name="no_manual_installation">بدون نصب دستی</string>
|
||||
<string name="prioritized_support">پشتیبانی اولویت بندی شده</string>
|
||||
<string name="helping_game_preservation">کمک به حفظ بازی</string>
|
||||
<string name="our_eternal_gratitude">سپاس ابدی ما</string>
|
||||
<string name="are_you_interested">مشتاق هستید؟</string>
|
||||
|
||||
<!-- General settings strings -->
|
||||
<string name="frame_limit_enable">محدودیت سرعت</string>
|
||||
<string name="frame_limit_enable_description">سرعت شبیهسازی را به درصد مشخصی از سرعت عادی محدود میکند.</string>
|
||||
|
|
|
@ -390,17 +390,6 @@
|
|||
<string name="user_data_export_cancelled">Exportation annulée</string>
|
||||
<string name="user_data_import_failed_description">Assurez-vous que les dossiers de données utilisateur se trouvent à la racine du dossier ZIP et contiennent un fichier de configuration à config/config.ini, puis réessayez.</string>
|
||||
|
||||
<!-- Early access upgrade strings -->
|
||||
<string name="early_access">Early Access</string>
|
||||
<string name="early_access_benefits">Avantages de l\'Early Access</string>
|
||||
<string name="cutting_edge_features">Fonctionnalités de pointe</string>
|
||||
<string name="early_access_updates">Accès anticipé aux mises à jour</string>
|
||||
<string name="no_manual_installation">Pas d\'installation manuelle</string>
|
||||
<string name="prioritized_support">Assistance prioritaire</string>
|
||||
<string name="helping_game_preservation">Contribuer à la préservation des jeux</string>
|
||||
<string name="our_eternal_gratitude">Notre gratitude éternelle</string>
|
||||
<string name="are_you_interested">Es tu intéressé ?</string>
|
||||
|
||||
<!-- General settings strings -->
|
||||
<string name="frame_limit_enable">Limiter la vitesse</string>
|
||||
<string name="frame_limit_enable_description">Limiter la vitesse d\'émulation à un pourcentage spécifié de la vitesse normale</string>
|
||||
|
|
|
@ -376,17 +376,6 @@
|
|||
<string name="user_data_export_cancelled">ייצוא בוטל</string>
|
||||
<string name="user_data_import_failed_description">ודא שנתוני המשתמש נמצאים בשורש קובץ ה zip ושהוא מכיל קובץ סידור ב config/config.ini ונסה שוב.</string>
|
||||
|
||||
<!-- Early access upgrade strings -->
|
||||
<string name="early_access">גישה מוקדמת</string>
|
||||
<string name="early_access_benefits">יתרונות של גישה מקודמת</string>
|
||||
<string name="cutting_edge_features">תכונות חותכות קצה</string>
|
||||
<string name="early_access_updates">גישה מוקדמת לעדכונים</string>
|
||||
<string name="no_manual_installation">ללא התקנה ידנית</string>
|
||||
<string name="prioritized_support">תמיכה בעדיפות</string>
|
||||
<string name="helping_game_preservation">עוזר בשמירת משחקים</string>
|
||||
<string name="our_eternal_gratitude">התודה האינסופית שלנו</string>
|
||||
<string name="are_you_interested">אתה מעוניין?</string>
|
||||
|
||||
<!-- General settings strings -->
|
||||
<string name="frame_limit_enable">הגבל מהירות</string>
|
||||
<string name="frame_limit_enable_description">מגביל את מהירות האמולציה לאחוז מהירות המבוקש מהמהירות הרגילה.</string>
|
||||
|
|
|
@ -385,16 +385,6 @@
|
|||
<string name="user_data_export_cancelled">Exportálás megszakítva</string>
|
||||
<string name="user_data_import_failed_description">Ellenőrizd, hogy a felhasználói adatok mappái a zip mappa gyökerében vannak, és tartalmaznak egy konfig fájlt a config/config.ini címen, majd próbáld meg újra.</string>
|
||||
|
||||
<!-- Early access upgrade strings -->
|
||||
<string name="early_access">Korai hozzáférés</string>
|
||||
<string name="early_access_benefits">Korai hozzáférés előnyei</string>
|
||||
<string name="cutting_edge_features">Legújabb funkciók</string>
|
||||
<string name="early_access_updates">Korai hozzáférés a frissítésekhez</string>
|
||||
<string name="no_manual_installation">Automatikus telepítések</string>
|
||||
<string name="prioritized_support">Priorizált támogatás</string>
|
||||
<string name="our_eternal_gratitude">Valamint az örök hálánk</string>
|
||||
<string name="are_you_interested">Érdekel a dolog?</string>
|
||||
|
||||
<!-- General settings strings -->
|
||||
<string name="frame_limit_enable">Sebességkorlát</string>
|
||||
<string name="frame_limit_enable_description">Korlátozza az emuláció sebességét a normál sebesség adott százalékára.</string>
|
||||
|
|
|
@ -386,17 +386,6 @@
|
|||
<string name="user_data_export_cancelled">Ekspor Dibatalkan</string>
|
||||
<string name="user_data_import_failed_description">Pastikan folder data pengguna berada di akar folder zip dan berisi file konfigurasi di config/config.ini dan coba lagi.</string>
|
||||
|
||||
<!-- Early access upgrade strings -->
|
||||
<string name="early_access">Akses lebih awal</string>
|
||||
<string name="early_access_benefits">Manfaat Akses Awal</string>
|
||||
<string name="cutting_edge_features">Fitur-fitur yang paling baru</string>
|
||||
<string name="early_access_updates">Akses lebih awal untuk pembaruan</string>
|
||||
<string name="no_manual_installation">Tidak ada instalasi manual</string>
|
||||
<string name="prioritized_support">Dukungan yang diprioritaskan</string>
|
||||
<string name="helping_game_preservation">Membantu pemeliharaan game</string>
|
||||
<string name="our_eternal_gratitude">Ucapan terima kasih kami yang tak terhingga</string>
|
||||
<string name="are_you_interested">Apa kamu tertarik?</string>
|
||||
|
||||
<!-- General settings strings -->
|
||||
<string name="frame_limit_enable">Batas kecepatan</string>
|
||||
<string name="frame_limit_enable_description">Membatasi kecepatan emulasi ke persentase tertentu dari kecepatan normal</string>
|
||||
|
|
|
@ -378,17 +378,6 @@
|
|||
<string name="user_data_export_cancelled">Esportazione annullata</string>
|
||||
<string name="user_data_import_failed_description">Assicurati che la cartella dei Dati dell\'utente stiano nella radice del file.zip e che sia presente una cartella config in config/config.ini, poi, riprova.</string>
|
||||
|
||||
<!-- Early access upgrade strings -->
|
||||
<string name="early_access">Accesso Anticipato</string>
|
||||
<string name="early_access_benefits">Vantaggi dell\'accesso anticipato</string>
|
||||
<string name="cutting_edge_features">Funzionalità all\'avanguardia</string>
|
||||
<string name="early_access_updates">Accesso anticipato agli aggiornamenti</string>
|
||||
<string name="no_manual_installation">Non installare manualmente.</string>
|
||||
<string name="prioritized_support">Supporto prioritario</string>
|
||||
<string name="helping_game_preservation">Aiuta a preservare il gioco</string>
|
||||
<string name="our_eternal_gratitude">La nostra gratitudine eterna</string>
|
||||
<string name="are_you_interested">Sei interessato?</string>
|
||||
|
||||
<!-- General settings strings -->
|
||||
<string name="frame_limit_enable">Limita velocità</string>
|
||||
<string name="frame_limit_enable_description">Limita la velocità dell\'emulazione a una specifica percentuale della velocità normale.</string>
|
||||
|
|
|
@ -357,17 +357,6 @@
|
|||
<string name="user_data_import_success">ユーザデータのインポートに成功しました</string>
|
||||
<string name="user_data_export_cancelled">エクスポートをキャンセルしました</string>
|
||||
|
||||
<!-- Early access upgrade strings -->
|
||||
<string name="early_access">早期アクセス</string>
|
||||
<string name="early_access_benefits">早期アクセスのメリット</string>
|
||||
<string name="cutting_edge_features">最先端の機能</string>
|
||||
<string name="early_access_updates">アップデートへの早期アクセス</string>
|
||||
<string name="no_manual_installation">手動インストールが不要</string>
|
||||
<string name="prioritized_support">優先サポート</string>
|
||||
<string name="helping_game_preservation">ゲームの保存に貢献</string>
|
||||
<string name="our_eternal_gratitude">私たちから永遠の感謝</string>
|
||||
<string name="are_you_interested">興味がありますか?</string>
|
||||
|
||||
<!-- General settings strings -->
|
||||
<string name="frame_limit_enable">エミュレーション速度を制限</string>
|
||||
<string name="frame_limit_enable_description">エミュレーション速度を指定した割合に制限します。</string>
|
||||
|
|
|
@ -385,17 +385,6 @@
|
|||
<string name="user_data_export_cancelled">내보내기 취소됨</string>
|
||||
<string name="user_data_import_failed_description">유저 데이터 폴더가 ZIP 폴더의 루트 디렉토리에 위치하고 config/config.ini 구성 파일이 있는지 확인하고 다시 시도하세요.</string>
|
||||
|
||||
<!-- Early access upgrade strings -->
|
||||
<string name="early_access">앞서 해보기</string>
|
||||
<string name="early_access_benefits">앞서 해보기 혜택</string>
|
||||
<string name="cutting_edge_features">최신 기능</string>
|
||||
<string name="early_access_updates">업데이트 미리 체험</string>
|
||||
<string name="no_manual_installation">수동 설치 불필요</string>
|
||||
<string name="prioritized_support">우선 지원</string>
|
||||
<string name="helping_game_preservation">게임 보존 지원</string>
|
||||
<string name="our_eternal_gratitude">우리의 영원한 감사의 마음</string>
|
||||
<string name="are_you_interested">관심 있으세요?</string>
|
||||
|
||||
<!-- General settings strings -->
|
||||
<string name="frame_limit_enable">속도 제한</string>
|
||||
<string name="frame_limit_enable_description">에뮬레이션 속도를 정상 속도의 지정된 비율로 제한합니다.</string>
|
||||
|
|
|
@ -334,17 +334,6 @@
|
|||
<string name="licenses_description">Prosjekter som gjør Eden for Android mulig</string>
|
||||
<string name="build">Bygg</string>
|
||||
|
||||
<!-- Early access upgrade strings -->
|
||||
<string name="early_access">Tidlig tilgang</string>
|
||||
<string name="early_access_benefits">Fordeler ved tidlig tilgang</string>
|
||||
<string name="cutting_edge_features">Avanserte funksjoner</string>
|
||||
<string name="early_access_updates">Tidlig tilgang til oppdateringer</string>
|
||||
<string name="no_manual_installation">Ingen manuell installasjon</string>
|
||||
<string name="prioritized_support">Prioritert støtte</string>
|
||||
<string name="helping_game_preservation">Bidra til bevaring av spill</string>
|
||||
<string name="our_eternal_gratitude">Vår evige takknemlighet</string>
|
||||
<string name="are_you_interested">Er du interessert?</string>
|
||||
|
||||
<!-- General settings strings -->
|
||||
<string name="frame_limit_enable">Begrense hastigheten</string>
|
||||
<string name="frame_limit_enable_description">Begrenser emuleringshastigheten til en spesifisert prosentandel av normal hastighet.</string>
|
||||
|
|
|
@ -334,17 +334,6 @@
|
|||
<string name="licenses_description">Projekty dzięki którym Eden mógł zostać stworzony</string>
|
||||
<string name="build">Wersja</string>
|
||||
|
||||
<!-- Early access upgrade strings -->
|
||||
<string name="early_access">Wczesny dostęp</string>
|
||||
<string name="early_access_benefits">Korzyści z wcześniejszego dostępu</string>
|
||||
<string name="cutting_edge_features">Nowatorskie funkcje</string>
|
||||
<string name="early_access_updates">Częste aktualizacje</string>
|
||||
<string name="no_manual_installation">Automatyczne aktualizacje</string>
|
||||
<string name="prioritized_support">Priorytetowe wsparcie</string>
|
||||
<string name="helping_game_preservation">Pomoc w problemach z grami</string>
|
||||
<string name="our_eternal_gratitude">Nasza wdzięczność</string>
|
||||
<string name="are_you_interested">Jesteś zainteresowany?</string>
|
||||
|
||||
<!-- General settings strings -->
|
||||
<string name="frame_limit_enable">Limit szybkość</string>
|
||||
<string name="frame_limit_enable_description">Włącz, aby ustawić procentowy limit szybkości emulacji</string>
|
||||
|
|
|
@ -390,17 +390,6 @@
|
|||
<string name="user_data_export_cancelled">Exportação cancelada</string>
|
||||
<string name="user_data_import_failed_description">Verifique se as pastas de dados do usuário estão na raiz da pasta zip, se possuem um arquivo de configuração em config/config.ini e tente novamente.</string>
|
||||
|
||||
<!-- Early access upgrade strings -->
|
||||
<string name="early_access">Acesso Antecipado</string>
|
||||
<string name="early_access_benefits">Benefícios do Acesso Antecipado</string>
|
||||
<string name="cutting_edge_features">Recursos de ponta</string>
|
||||
<string name="early_access_updates">Acesso antecipado a atualizações</string>
|
||||
<string name="no_manual_installation">Sem instalação manual</string>
|
||||
<string name="prioritized_support">Suporte prioritário</string>
|
||||
<string name="helping_game_preservation">Ajuda na preservação dos jogos</string>
|
||||
<string name="our_eternal_gratitude">A nossa eterna gratidão</string>
|
||||
<string name="are_you_interested">Tem interesse?</string>
|
||||
|
||||
<!-- General settings strings -->
|
||||
<string name="frame_limit_enable">Limite de velocidade</string>
|
||||
<string name="frame_limit_enable_description">Limita a velocidade da emulação a uma porcentagem específica da velocidade normal.</string>
|
||||
|
|
|
@ -390,17 +390,6 @@
|
|||
<string name="user_data_export_cancelled">Exportação cancelada</string>
|
||||
<string name="user_data_import_failed_description">Verifiqua se as pastas de dados do utilizados estão na raiz da pasta zip e contêm um arquivo de configuração em config/config.ini e tenta novamente.</string>
|
||||
|
||||
<!-- Early access upgrade strings -->
|
||||
<string name="early_access">Acesso antecipado</string>
|
||||
<string name="early_access_benefits">Benefícios do Acesso Antecipado</string>
|
||||
<string name="cutting_edge_features">Recursos de ponta</string>
|
||||
<string name="early_access_updates">Acesso antecipado a atualizações</string>
|
||||
<string name="no_manual_installation">Sem instalação manual</string>
|
||||
<string name="prioritized_support">Suporte prioritário</string>
|
||||
<string name="helping_game_preservation">Ajuda na preservação dos jogos</string>
|
||||
<string name="our_eternal_gratitude">A nossa eterna gratidão</string>
|
||||
<string name="are_you_interested">Estás interessado?</string>
|
||||
|
||||
<!-- General settings strings -->
|
||||
<string name="frame_limit_enable">Limite de velocidade</string>
|
||||
<string name="frame_limit_enable_description">Limita a velocidade da emulação a uma porcentagem específica da velocidade normal.</string>
|
||||
|
|
|
@ -391,17 +391,6 @@
|
|||
<string name="user_data_export_cancelled">Экспорт отменен</string>
|
||||
<string name="user_data_import_failed_description">Убедитесь что папки пользовательских данных находятся в корне zip-папки и содержат файл конфигурации config/config.ini и повторите попытку.</string>
|
||||
|
||||
<!-- Early access upgrade strings -->
|
||||
<string name="early_access">Ранний доступ</string>
|
||||
<string name="early_access_benefits">Преимущества раннего доступа</string>
|
||||
<string name="cutting_edge_features">Новейшие возможности</string>
|
||||
<string name="early_access_updates">Ранний доступ к обновлениям</string>
|
||||
<string name="no_manual_installation">Без ручной установки</string>
|
||||
<string name="prioritized_support">Приоритетная поддержка</string>
|
||||
<string name="helping_game_preservation">Помощь в презервации игр</string>
|
||||
<string name="our_eternal_gratitude">Наша бесконечная благодарность</string>
|
||||
<string name="are_you_interested">Вы заинтересованы?</string>
|
||||
|
||||
<!-- General settings strings -->
|
||||
<string name="frame_limit_enable">Ограничить скорость</string>
|
||||
<string name="frame_limit_enable_description">Ограничивает скорость эмуляции указанным процентом от нормальной скорости.</string>
|
||||
|
|
|
@ -320,17 +320,6 @@
|
|||
<string name="contributors_description">Зроблено з \u2764 від команди Eden</string>
|
||||
<string name="build">Збірка</string>
|
||||
|
||||
<!-- Early access upgrade strings -->
|
||||
<string name="early_access">Ранній доступ</string>
|
||||
<string name="early_access_benefits">Переваги раннього доступу</string>
|
||||
<string name="cutting_edge_features">Новітні можливості</string>
|
||||
<string name="early_access_updates">Ранній доступ до оновлень</string>
|
||||
<string name="no_manual_installation">Без ручного встановлення</string>
|
||||
<string name="prioritized_support">Пріоритетна підтримка</string>
|
||||
<string name="helping_game_preservation">Допомога в презервації ігор</string>
|
||||
<string name="our_eternal_gratitude">Наша нескінченна вдячність</string>
|
||||
<string name="are_you_interested">Ви зацікавлені?</string>
|
||||
|
||||
<string name="frame_limit_enable">Обмеження швидкості</string>
|
||||
<string name="frame_limit_enable_description">Обмежує швидкість емуляції у відсотках від нормальної.</string>
|
||||
<string name="frame_limit_slider">Відсоток обмеження</string>
|
||||
|
|
|
@ -334,17 +334,6 @@
|
|||
<string name="licenses_description">Các dự án làm cho Eden trên Android trở thành điều có thể</string>
|
||||
<string name="build">Dựng</string>
|
||||
|
||||
<!-- Early access upgrade strings -->
|
||||
<string name="early_access">Early Access</string>
|
||||
<string name="early_access_benefits">Lợi ích của Early Access</string>
|
||||
<string name="cutting_edge_features">Tính năng tiên tiến</string>
|
||||
<string name="early_access_updates">Truy cập sớm vào cập nhật</string>
|
||||
<string name="no_manual_installation">Không có cài đặt thủ công</string>
|
||||
<string name="prioritized_support">Ưu tiên hỗ trợ</string>
|
||||
<string name="helping_game_preservation">Hỗ trợ bảo tồn trò chơi</string>
|
||||
<string name="our_eternal_gratitude">Sự biết ơn vô hạn của chúng tôi</string>
|
||||
<string name="are_you_interested">Bạn có thấy thú vị không?</string>
|
||||
|
||||
<!-- General settings strings -->
|
||||
<string name="frame_limit_enable">Giới hạn tốc độ</string>
|
||||
<string name="frame_limit_enable_description">Giới hạn tốc độ giả lập ở một phần trăm cụ thể của tốc độ bình thường.</string>
|
||||
|
|
|
@ -385,17 +385,6 @@
|
|||
<string name="user_data_export_cancelled">已取消导出数据</string>
|
||||
<string name="user_data_import_failed_description">请确保用户数据文件夹位于 zip 压缩包的根目录,并在 config/config.ini 路径中包含配置文件,然后重试。</string>
|
||||
|
||||
<!-- Early access upgrade strings -->
|
||||
<string name="early_access">抢先体验</string>
|
||||
<string name="early_access_benefits">抢先体验的权益</string>
|
||||
<string name="cutting_edge_features">最新功能</string>
|
||||
<string name="early_access_updates">抢先更新</string>
|
||||
<string name="no_manual_installation">无需手动安装</string>
|
||||
<string name="prioritized_support">优先支持</string>
|
||||
<string name="helping_game_preservation">帮助保留游玩历史</string>
|
||||
<string name="our_eternal_gratitude">我们真诚的感激</string>
|
||||
<string name="are_you_interested">您对此感兴趣吗?</string>
|
||||
|
||||
<!-- General settings strings -->
|
||||
<string name="frame_limit_enable">运行速度限制</string>
|
||||
<string name="frame_limit_enable_description">将运行速度限制为正常速度的指定百分比。</string>
|
||||
|
|
|
@ -391,17 +391,6 @@
|
|||
<string name="user_data_export_cancelled">匯出已取消</string>
|
||||
<string name="user_data_import_failed_description">請確保使用者資料夾位於 zip 壓縮檔的根目錄,並在 config/config.ini 路徑中包含組態檔案,並再試一次。</string>
|
||||
|
||||
<!-- Early access upgrade strings -->
|
||||
<string name="early_access">搶先體驗</string>
|
||||
<string name="early_access_benefits">搶先體驗權益</string>
|
||||
<string name="cutting_edge_features">最新功能</string>
|
||||
<string name="early_access_updates">搶先版更新</string>
|
||||
<string name="no_manual_installation">無需手動安裝</string>
|
||||
<string name="prioritized_support">優先支援</string>
|
||||
<string name="helping_game_preservation">協助遊戲保留</string>
|
||||
<string name="our_eternal_gratitude">我們永遠的感激</string>
|
||||
<string name="are_you_interested">您仍感興趣嗎?</string>
|
||||
|
||||
<!-- General settings strings -->
|
||||
<string name="frame_limit_enable">限制速度</string>
|
||||
<string name="frame_limit_enable_description">將模擬速度限制在標準速度的指定百分比。</string>
|
||||
|
|
|
@ -104,9 +104,10 @@
|
|||
<string name="multiplayer">Multiplayer</string>
|
||||
<string name="multiplayer_description">Host your own game room or join an existing one to play with people</string>
|
||||
<string name="multiplayer_room_title">Room: %1$s</string>
|
||||
<string name="multiplayer_console_id">Console ID: %1$s</string>
|
||||
<string name="multiplayer_console_id">Console ID:%1$s</string>
|
||||
<string name="multiplayer_create_room">Create</string>
|
||||
<string name="multiplayer_join_room">Join</string>
|
||||
<string name="multiplayer_public_room">Browse Public Rooms</string>
|
||||
<string name="multiplayer_username">Username</string>
|
||||
<string name="multiplayer_ip_address">IP Address</string>
|
||||
<string name="multiplayer_ip_port">Port</string>
|
||||
|
@ -167,9 +168,26 @@
|
|||
<string name="multiplayer_unban">Unban</string>
|
||||
<string name="multiplayer_unban_message">Are you sure you want to unban %1$s?</string>
|
||||
<string name="multiplayer_ban">Ban User</string>
|
||||
<string name="multiplayer_room_browser">Public Rooms</string>
|
||||
<string name="multiplayer_no_rooms_found">No public rooms found</string>
|
||||
<string name="multiplayer_password_required">Password Required</string>
|
||||
<string name="multiplayer_player_count">: %1$d/%2$d</string>
|
||||
<string name="multiplayer_game">Game</string>
|
||||
<string name="multiplayer_no_game_info">Any Game</string>
|
||||
<string name="multiplayer_password_protected">Password protected room</string>
|
||||
<string name="multiplayer_hide_full_rooms">Hide Full Rooms</string>
|
||||
<string name="multiplayer_hide_empty_rooms">Hide Empty Rooms</string>
|
||||
<string name="multiplayer_tap_refresh_to_check_again">Tap refresh to check again</string>
|
||||
<string name="multiplayer_search_public_lobbies">Search Lobbies…</string>
|
||||
<string name="emulation_multiplayer">Multiplayer</string>
|
||||
<string name="multiplayer_game_name">Preferred Games</string>
|
||||
<string name="multiplayer_preferred_game_name">Preferred Game</string>
|
||||
<string name="multiplayer_no_game">No Games Found</string>
|
||||
<string name="multiplayer_preferred_game_name_invalid">You must choose a Preferred Game to host a room.</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="ok">Ok</string>
|
||||
<string name="refresh">Refresh</string>
|
||||
<string name="room_list">Room List</string>
|
||||
|
||||
<!-- Setup strings -->
|
||||
<string name="welcome">Welcome!</string>
|
||||
|
@ -240,6 +258,7 @@
|
|||
<string name="invalid_keys_error">Invalid encryption keys</string>
|
||||
<string name="dumping_keys_quickstart_link">https://yuzu-mirror.github.io/help/quickstart/#dumping-decryption-keys</string>
|
||||
<string name="install_keys_failure_description">The selected file is incorrect or corrupt. Please redump your keys.</string>
|
||||
<string name="gpu_driver_fetcher">GPU driver fetcher</string>
|
||||
<string name="gpu_driver_manager">GPU driver manager</string>
|
||||
<string name="install_gpu_driver">Install GPU driver</string>
|
||||
<string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string>
|
||||
|
@ -355,22 +374,10 @@
|
|||
<string name="user_data_import_success">User data imported successfully</string>
|
||||
<string name="user_data_export_cancelled">Export cancelled</string>
|
||||
<string name="user_data_import_failed_description">Make sure the user data folders are at the root of the zip folder and contain a config file at config/config.ini and try again.</string>
|
||||
<string name="support_link">https://discord.gg/hab4Sh8qj6</string>
|
||||
<string name="support_link">https://discord.gg/edenemu</string>
|
||||
<string name="website_link">https://eden-emulator.github.io</string>
|
||||
<string name="github_link">https://git.eden-emu.dev/eden-emu</string>
|
||||
|
||||
<!-- Early access upgrade strings -->
|
||||
<string name="early_access">Early Access</string>
|
||||
<string name="play_store_link">https://play.google.com/store/apps/details?id=org.yuzu.yuzu_emu.ea</string>
|
||||
<string name="early_access_benefits">Early Access Benefits</string>
|
||||
<string name="cutting_edge_features">Cutting-edge features</string>
|
||||
<string name="early_access_updates">Early access to updates</string>
|
||||
<string name="no_manual_installation">No manual installation</string>
|
||||
<string name="prioritized_support">Prioritized support</string>
|
||||
<string name="helping_game_preservation">Helping game preservation</string>
|
||||
<string name="our_eternal_gratitude">Our eternal gratitude</string>
|
||||
<string name="are_you_interested">Are you interested?</string>
|
||||
|
||||
<!-- General settings strings -->
|
||||
<string name="frame_limit_enable">Limit speed</string>
|
||||
<string name="frame_limit_enable_description">Limits emulation speed to a specified percentage of normal speed.</string>
|
||||
|
@ -552,6 +559,7 @@
|
|||
<string name="import_failed">Import failed</string>
|
||||
<string name="cancelling">Cancelling</string>
|
||||
<string name="install">Install</string>
|
||||
<string name="fetch">Fetch</string>
|
||||
<string name="delete">Delete</string>
|
||||
<string name="edit">Edit</string>
|
||||
<string name="export_success">Exported successfully</string>
|
||||
|
@ -580,6 +588,31 @@
|
|||
<string name="system_gpu_driver">System GPU driver</string>
|
||||
<string name="installing_driver">Installing driver…</string>
|
||||
|
||||
<!-- GPU driver fetcher -->
|
||||
<string name="fetch_error">Error while Fetching</string>
|
||||
<string name="check_connection">Check your connection and try again.</string>
|
||||
<string name="show_releases">Show Releases</string>
|
||||
<string name="view_full_release_notes">Release Notes</string>
|
||||
<string name="failed_to_fetch">Failed to fetch</string>
|
||||
<string name="error_during_fetch">Error during Fetch</string>
|
||||
<string name="toggle_release_notes">Toggle release notes</string>
|
||||
<string name="downloads">Downloads</string>
|
||||
<string name="show_downloads">Show Downloads</string>
|
||||
<string name="hide_downloads">Hide Downloads</string>
|
||||
<string name="failed_cache_dir">Cache directory unavailable</string>
|
||||
<string name="empty_response_body">Empty response body</string>
|
||||
<string name="successfully_installed">%1$s Successfully Installed</string>
|
||||
<string name="driver_failed_title">Driver installation failed</string>
|
||||
<string name="failed_install_driver">Failed to install %1$s driver, does your system support it?</string>
|
||||
<string name="driver_empty">Downloaded driver is empty, check your internet</string>
|
||||
<string name="downloading">Downloading…</string>
|
||||
<string name="installing">Installing…</string>
|
||||
<string name="latest">Latest</string>
|
||||
<string name="recommended_driver">Recommended Driver:</string>
|
||||
<string name="gpu_model">GPU Model:</string>
|
||||
<string name="unsupported_gpu">Unsupported GPU</string>
|
||||
<string name="unsupported_gpu_warning">Your GPU does not support driver injection. Attempting to set custom drivers is not recommended.</string>
|
||||
|
||||
<!-- Preferences Screen -->
|
||||
<string name="preferences_settings">Settings</string>
|
||||
<string name="preferences_general">General</string>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue