committed by
GitHub
36 changed files with 1039 additions and 299 deletions
-
14src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
-
2src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
-
117src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt
-
185src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
-
75src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt
-
29src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
-
41src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt
-
8src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt
-
158src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt
-
62src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
-
7src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt
-
97src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
-
4src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt
-
228src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt
-
112src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt
-
9src/android/app/src/main/res/drawable/ic_build.xml
-
9src/android/app/src/main/res/drawable/ic_delete.xml
-
89src/android/app/src/main/res/layout/card_driver_option.xml
-
48src/android/app/src/main/res/layout/fragment_driver_manager.xml
-
7src/android/app/src/main/res/navigation/home_navigation.xml
-
2src/android/app/src/main/res/values-de/strings.xml
-
2src/android/app/src/main/res/values-es/strings.xml
-
2src/android/app/src/main/res/values-fr/strings.xml
-
2src/android/app/src/main/res/values-it/strings.xml
-
2src/android/app/src/main/res/values-ja/strings.xml
-
2src/android/app/src/main/res/values-ko/strings.xml
-
2src/android/app/src/main/res/values-nb/strings.xml
-
2src/android/app/src/main/res/values-pl/strings.xml
-
2src/android/app/src/main/res/values-pt-rBR/strings.xml
-
2src/android/app/src/main/res/values-pt-rPT/strings.xml
-
2src/android/app/src/main/res/values-ru/strings.xml
-
2src/android/app/src/main/res/values-uk/strings.xml
-
2src/android/app/src/main/res/values-zh-rCN/strings.xml
-
2src/android/app/src/main/res/values-zh-rTW/strings.xml
-
2src/android/app/src/main/res/values/dimens.xml
-
7src/android/app/src/main/res/values/strings.xml
@ -0,0 +1,117 @@ |
|||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
package org.yuzu.yuzu_emu.adapters |
|||
|
|||
import android.text.TextUtils |
|||
import android.view.LayoutInflater |
|||
import android.view.View |
|||
import android.view.ViewGroup |
|||
import androidx.recyclerview.widget.AsyncDifferConfig |
|||
import androidx.recyclerview.widget.DiffUtil |
|||
import androidx.recyclerview.widget.ListAdapter |
|||
import androidx.recyclerview.widget.RecyclerView |
|||
import org.yuzu.yuzu_emu.R |
|||
import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding |
|||
import org.yuzu.yuzu_emu.model.DriverViewModel |
|||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper |
|||
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata |
|||
|
|||
class DriverAdapter(private val driverViewModel: DriverViewModel) : |
|||
ListAdapter<Pair<String, GpuDriverMetadata>, DriverAdapter.DriverViewHolder>( |
|||
AsyncDifferConfig.Builder(DiffCallback()).build() |
|||
) { |
|||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DriverViewHolder { |
|||
val binding = |
|||
CardDriverOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false) |
|||
return DriverViewHolder(binding) |
|||
} |
|||
|
|||
override fun getItemCount(): Int = currentList.size |
|||
|
|||
override fun onBindViewHolder(holder: DriverViewHolder, position: Int) = |
|||
holder.bind(currentList[position]) |
|||
|
|||
private fun onSelectDriver(position: Int) { |
|||
driverViewModel.setSelectedDriverIndex(position) |
|||
notifyItemChanged(driverViewModel.previouslySelectedDriver) |
|||
notifyItemChanged(driverViewModel.selectedDriver) |
|||
} |
|||
|
|||
private fun onDeleteDriver(driverData: Pair<String, GpuDriverMetadata>, position: Int) { |
|||
if (driverViewModel.selectedDriver > position) { |
|||
driverViewModel.setSelectedDriverIndex(driverViewModel.selectedDriver - 1) |
|||
} |
|||
if (GpuDriverHelper.customDriverData == driverData.second) { |
|||
driverViewModel.setSelectedDriverIndex(0) |
|||
} |
|||
driverViewModel.driversToDelete.add(driverData.first) |
|||
driverViewModel.removeDriver(driverData) |
|||
notifyItemRemoved(position) |
|||
notifyItemChanged(driverViewModel.selectedDriver) |
|||
} |
|||
|
|||
inner class DriverViewHolder(val binding: CardDriverOptionBinding) : |
|||
RecyclerView.ViewHolder(binding.root) { |
|||
private lateinit var driverData: Pair<String, GpuDriverMetadata> |
|||
|
|||
fun bind(driverData: Pair<String, GpuDriverMetadata>) { |
|||
this.driverData = driverData |
|||
val driver = driverData.second |
|||
|
|||
binding.apply { |
|||
radioButton.isChecked = driverViewModel.selectedDriver == bindingAdapterPosition |
|||
root.setOnClickListener { |
|||
onSelectDriver(bindingAdapterPosition) |
|||
} |
|||
buttonDelete.setOnClickListener { |
|||
onDeleteDriver(driverData, bindingAdapterPosition) |
|||
} |
|||
|
|||
// Delay marquee by 3s |
|||
title.postDelayed( |
|||
{ |
|||
title.isSelected = true |
|||
title.ellipsize = TextUtils.TruncateAt.MARQUEE |
|||
version.isSelected = true |
|||
version.ellipsize = TextUtils.TruncateAt.MARQUEE |
|||
description.isSelected = true |
|||
description.ellipsize = TextUtils.TruncateAt.MARQUEE |
|||
}, |
|||
3000 |
|||
) |
|||
if (driver.name == null) { |
|||
title.setText(R.string.system_gpu_driver) |
|||
description.text = "" |
|||
version.text = "" |
|||
version.visibility = View.GONE |
|||
description.visibility = View.GONE |
|||
buttonDelete.visibility = View.GONE |
|||
} else { |
|||
title.text = driver.name |
|||
version.text = driver.version |
|||
description.text = driver.description |
|||
version.visibility = View.VISIBLE |
|||
description.visibility = View.VISIBLE |
|||
buttonDelete.visibility = View.VISIBLE |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
private class DiffCallback : DiffUtil.ItemCallback<Pair<String, GpuDriverMetadata>>() { |
|||
override fun areItemsTheSame( |
|||
oldItem: Pair<String, GpuDriverMetadata>, |
|||
newItem: Pair<String, GpuDriverMetadata> |
|||
): Boolean { |
|||
return oldItem.first == newItem.first |
|||
} |
|||
|
|||
override fun areContentsTheSame( |
|||
oldItem: Pair<String, GpuDriverMetadata>, |
|||
newItem: Pair<String, GpuDriverMetadata> |
|||
): Boolean { |
|||
return oldItem.second == newItem.second |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,185 @@ |
|||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
package org.yuzu.yuzu_emu.fragments |
|||
|
|||
import android.os.Bundle |
|||
import android.view.LayoutInflater |
|||
import android.view.View |
|||
import android.view.ViewGroup |
|||
import androidx.activity.result.contract.ActivityResultContracts |
|||
import androidx.core.view.ViewCompat |
|||
import androidx.core.view.WindowInsetsCompat |
|||
import androidx.core.view.updatePadding |
|||
import androidx.fragment.app.Fragment |
|||
import androidx.fragment.app.activityViewModels |
|||
import androidx.lifecycle.lifecycleScope |
|||
import androidx.navigation.findNavController |
|||
import androidx.recyclerview.widget.GridLayoutManager |
|||
import com.google.android.material.transition.MaterialSharedAxis |
|||
import kotlinx.coroutines.flow.collectLatest |
|||
import kotlinx.coroutines.launch |
|||
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.model.DriverViewModel |
|||
import org.yuzu.yuzu_emu.model.HomeViewModel |
|||
import org.yuzu.yuzu_emu.utils.FileUtil |
|||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper |
|||
import java.io.IOException |
|||
|
|||
class DriverManagerFragment : Fragment() { |
|||
private var _binding: FragmentDriverManagerBinding? = null |
|||
private val binding get() = _binding!! |
|||
|
|||
private val homeViewModel: HomeViewModel by activityViewModels() |
|||
private val driverViewModel: DriverViewModel by activityViewModels() |
|||
|
|||
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 = FragmentDriverManagerBinding.inflate(inflater) |
|||
return binding.root |
|||
} |
|||
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { |
|||
super.onViewCreated(view, savedInstanceState) |
|||
homeViewModel.setNavigationVisibility(visible = false, animated = true) |
|||
homeViewModel.setStatusBarShadeVisibility(visible = false) |
|||
|
|||
if (!driverViewModel.isInteractionAllowed) { |
|||
DriversLoadingDialogFragment().show( |
|||
childFragmentManager, |
|||
DriversLoadingDialogFragment.TAG |
|||
) |
|||
} |
|||
|
|||
binding.toolbarDrivers.setNavigationOnClickListener { |
|||
binding.root.findNavController().popBackStack() |
|||
} |
|||
|
|||
binding.buttonInstall.setOnClickListener { |
|||
getDriver.launch(arrayOf("application/zip")) |
|||
} |
|||
|
|||
binding.listDrivers.apply { |
|||
layoutManager = GridLayoutManager( |
|||
requireContext(), |
|||
resources.getInteger(R.integer.grid_columns) |
|||
) |
|||
adapter = DriverAdapter(driverViewModel) |
|||
} |
|||
|
|||
viewLifecycleOwner.lifecycleScope.apply { |
|||
launch { |
|||
driverViewModel.driverList.collectLatest { |
|||
(binding.listDrivers.adapter as DriverAdapter).submitList(it) |
|||
} |
|||
} |
|||
launch { |
|||
driverViewModel.newDriverInstalled.collect { |
|||
if (_binding != null && it) { |
|||
(binding.listDrivers.adapter as DriverAdapter).apply { |
|||
notifyItemChanged(driverViewModel.previouslySelectedDriver) |
|||
notifyItemChanged(driverViewModel.selectedDriver) |
|||
driverViewModel.setNewDriverInstalled(false) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
setInsets() |
|||
} |
|||
|
|||
// Start installing requested driver |
|||
override fun onStop() { |
|||
super.onStop() |
|||
driverViewModel.onCloseDriverManager() |
|||
} |
|||
|
|||
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 |
|||
|
|||
val mlpAppBar = binding.toolbarDrivers.layoutParams as ViewGroup.MarginLayoutParams |
|||
mlpAppBar.leftMargin = leftInsets |
|||
mlpAppBar.rightMargin = rightInsets |
|||
binding.toolbarDrivers.layoutParams = mlpAppBar |
|||
|
|||
val mlplistDrivers = binding.listDrivers.layoutParams as ViewGroup.MarginLayoutParams |
|||
mlplistDrivers.leftMargin = leftInsets |
|||
mlplistDrivers.rightMargin = rightInsets |
|||
binding.listDrivers.layoutParams = mlplistDrivers |
|||
|
|||
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab) |
|||
val mlpFab = |
|||
binding.buttonInstall.layoutParams as ViewGroup.MarginLayoutParams |
|||
mlpFab.leftMargin = leftInsets + fabSpacing |
|||
mlpFab.rightMargin = rightInsets + fabSpacing |
|||
mlpFab.bottomMargin = barInsets.bottom + fabSpacing |
|||
binding.buttonInstall.layoutParams = mlpFab |
|||
|
|||
binding.listDrivers.updatePadding( |
|||
bottom = barInsets.bottom + |
|||
resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab) |
|||
) |
|||
|
|||
windowInsets |
|||
} |
|||
|
|||
private val getDriver = |
|||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> |
|||
if (result == null) { |
|||
return@registerForActivityResult |
|||
} |
|||
|
|||
IndeterminateProgressDialogFragment.newInstance( |
|||
requireActivity(), |
|||
R.string.installing_driver, |
|||
false |
|||
) { |
|||
// Ignore file exceptions when a user selects an invalid zip |
|||
try { |
|||
GpuDriverHelper.copyDriverToInternalStorage(result) |
|||
} catch (_: IOException) { |
|||
return@newInstance getString(R.string.select_gpu_driver_error) |
|||
} |
|||
|
|||
val driverData = GpuDriverHelper.customDriverData |
|||
if (driverData.name == null) { |
|||
return@newInstance getString(R.string.select_gpu_driver_error) |
|||
} |
|||
|
|||
val driverInList = |
|||
driverViewModel.driverList.value.firstOrNull { it.second == driverData } |
|||
if (driverInList != null) { |
|||
return@newInstance getString(R.string.driver_already_installed) |
|||
} else { |
|||
driverViewModel.addDriver( |
|||
Pair( |
|||
"${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}", |
|||
driverData |
|||
) |
|||
) |
|||
driverViewModel.setNewDriverInstalled(true) |
|||
} |
|||
return@newInstance Any() |
|||
}.show(childFragmentManager, IndeterminateProgressDialogFragment.TAG) |
|||
} |
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
package org.yuzu.yuzu_emu.fragments |
|||
|
|||
import android.app.Dialog |
|||
import android.os.Bundle |
|||
import android.view.LayoutInflater |
|||
import android.view.View |
|||
import android.view.ViewGroup |
|||
import androidx.fragment.app.DialogFragment |
|||
import androidx.fragment.app.activityViewModels |
|||
import androidx.lifecycle.Lifecycle |
|||
import androidx.lifecycle.lifecycleScope |
|||
import androidx.lifecycle.repeatOnLifecycle |
|||
import com.google.android.material.dialog.MaterialAlertDialogBuilder |
|||
import kotlinx.coroutines.launch |
|||
import org.yuzu.yuzu_emu.R |
|||
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding |
|||
import org.yuzu.yuzu_emu.model.DriverViewModel |
|||
|
|||
class DriversLoadingDialogFragment : DialogFragment() { |
|||
private val driverViewModel: DriverViewModel by activityViewModels() |
|||
|
|||
private lateinit var binding: DialogProgressBarBinding |
|||
|
|||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { |
|||
binding = DialogProgressBarBinding.inflate(layoutInflater) |
|||
binding.progressBar.isIndeterminate = true |
|||
|
|||
isCancelable = false |
|||
|
|||
return MaterialAlertDialogBuilder(requireContext()) |
|||
.setTitle(R.string.loading) |
|||
.setView(binding.root) |
|||
.create() |
|||
} |
|||
|
|||
override fun onCreateView( |
|||
inflater: LayoutInflater, |
|||
container: ViewGroup?, |
|||
savedInstanceState: Bundle? |
|||
): View = binding.root |
|||
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { |
|||
super.onViewCreated(view, savedInstanceState) |
|||
viewLifecycleOwner.lifecycleScope.apply { |
|||
launch { |
|||
repeatOnLifecycle(Lifecycle.State.RESUMED) { |
|||
driverViewModel.areDriversLoading.collect { checkForDismiss() } |
|||
} |
|||
} |
|||
launch { |
|||
repeatOnLifecycle(Lifecycle.State.RESUMED) { |
|||
driverViewModel.isDriverReady.collect { checkForDismiss() } |
|||
} |
|||
} |
|||
launch { |
|||
repeatOnLifecycle(Lifecycle.State.RESUMED) { |
|||
driverViewModel.isDeletingDrivers.collect { checkForDismiss() } |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
private fun checkForDismiss() { |
|||
if (driverViewModel.isInteractionAllowed) { |
|||
dismiss() |
|||
} |
|||
} |
|||
|
|||
companion object { |
|||
const val TAG = "DriversLoadingDialogFragment" |
|||
} |
|||
} |
|||
@ -0,0 +1,158 @@ |
|||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
package org.yuzu.yuzu_emu.model |
|||
|
|||
import androidx.lifecycle.ViewModel |
|||
import androidx.lifecycle.viewModelScope |
|||
import kotlinx.coroutines.Dispatchers |
|||
import kotlinx.coroutines.flow.MutableStateFlow |
|||
import kotlinx.coroutines.flow.StateFlow |
|||
import kotlinx.coroutines.launch |
|||
import kotlinx.coroutines.withContext |
|||
import org.yuzu.yuzu_emu.R |
|||
import org.yuzu.yuzu_emu.YuzuApplication |
|||
import org.yuzu.yuzu_emu.utils.FileUtil |
|||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper |
|||
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata |
|||
import java.io.BufferedOutputStream |
|||
import java.io.File |
|||
|
|||
class DriverViewModel : ViewModel() { |
|||
private val _areDriversLoading = MutableStateFlow(false) |
|||
val areDriversLoading: StateFlow<Boolean> get() = _areDriversLoading |
|||
|
|||
private val _isDriverReady = MutableStateFlow(true) |
|||
val isDriverReady: StateFlow<Boolean> get() = _isDriverReady |
|||
|
|||
private val _isDeletingDrivers = MutableStateFlow(false) |
|||
val isDeletingDrivers: StateFlow<Boolean> get() = _isDeletingDrivers |
|||
|
|||
private val _driverList = MutableStateFlow(mutableListOf<Pair<String, GpuDriverMetadata>>()) |
|||
val driverList: StateFlow<MutableList<Pair<String, GpuDriverMetadata>>> get() = _driverList |
|||
|
|||
var previouslySelectedDriver = 0 |
|||
var selectedDriver = -1 |
|||
|
|||
private val _selectedDriverMetadata = |
|||
MutableStateFlow( |
|||
GpuDriverHelper.customDriverData.name |
|||
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver) |
|||
) |
|||
val selectedDriverMetadata: StateFlow<String> get() = _selectedDriverMetadata |
|||
|
|||
private val _newDriverInstalled = MutableStateFlow(false) |
|||
val newDriverInstalled: StateFlow<Boolean> get() = _newDriverInstalled |
|||
|
|||
val driversToDelete = mutableListOf<String>() |
|||
|
|||
val isInteractionAllowed |
|||
get() = !areDriversLoading.value && isDriverReady.value && !isDeletingDrivers.value |
|||
|
|||
init { |
|||
_areDriversLoading.value = true |
|||
viewModelScope.launch { |
|||
withContext(Dispatchers.IO) { |
|||
val drivers = GpuDriverHelper.getDrivers() |
|||
val currentDriverMetadata = GpuDriverHelper.customDriverData |
|||
for (i in drivers.indices) { |
|||
if (drivers[i].second == currentDriverMetadata) { |
|||
setSelectedDriverIndex(i) |
|||
break |
|||
} |
|||
} |
|||
|
|||
// If a user had installed a driver before the manager was implemented, this zips |
|||
// the installed driver to UserData/gpu_drivers/CustomDriver.zip so that it can |
|||
// be indexed and exported as expected. |
|||
if (selectedDriver == -1) { |
|||
val driverToSave = |
|||
File(GpuDriverHelper.driverStoragePath, "CustomDriver.zip") |
|||
driverToSave.createNewFile() |
|||
FileUtil.zipFromInternalStorage( |
|||
File(GpuDriverHelper.driverInstallationPath!!), |
|||
GpuDriverHelper.driverInstallationPath!!, |
|||
BufferedOutputStream(driverToSave.outputStream()) |
|||
) |
|||
drivers.add(Pair(driverToSave.path, currentDriverMetadata)) |
|||
setSelectedDriverIndex(drivers.size - 1) |
|||
} |
|||
|
|||
_driverList.value = drivers |
|||
_areDriversLoading.value = false |
|||
} |
|||
} |
|||
} |
|||
|
|||
fun setSelectedDriverIndex(value: Int) { |
|||
if (selectedDriver != -1) { |
|||
previouslySelectedDriver = selectedDriver |
|||
} |
|||
selectedDriver = value |
|||
} |
|||
|
|||
fun setNewDriverInstalled(value: Boolean) { |
|||
_newDriverInstalled.value = value |
|||
} |
|||
|
|||
fun addDriver(driverData: Pair<String, GpuDriverMetadata>) { |
|||
val driverIndex = _driverList.value.indexOfFirst { it == driverData } |
|||
if (driverIndex == -1) { |
|||
setSelectedDriverIndex(_driverList.value.size) |
|||
_driverList.value.add(driverData) |
|||
_selectedDriverMetadata.value = driverData.second.name |
|||
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver) |
|||
} else { |
|||
setSelectedDriverIndex(driverIndex) |
|||
} |
|||
} |
|||
|
|||
fun removeDriver(driverData: Pair<String, GpuDriverMetadata>) { |
|||
_driverList.value.remove(driverData) |
|||
} |
|||
|
|||
fun onCloseDriverManager() { |
|||
_isDeletingDrivers.value = true |
|||
viewModelScope.launch { |
|||
withContext(Dispatchers.IO) { |
|||
driversToDelete.forEach { |
|||
val driver = File(it) |
|||
if (driver.exists()) { |
|||
driver.delete() |
|||
} |
|||
} |
|||
driversToDelete.clear() |
|||
_isDeletingDrivers.value = false |
|||
} |
|||
} |
|||
|
|||
if (GpuDriverHelper.customDriverData == driverList.value[selectedDriver].second) { |
|||
return |
|||
} |
|||
|
|||
_isDriverReady.value = false |
|||
viewModelScope.launch { |
|||
withContext(Dispatchers.IO) { |
|||
if (selectedDriver == 0) { |
|||
GpuDriverHelper.installDefaultDriver() |
|||
setDriverReady() |
|||
return@withContext |
|||
} |
|||
|
|||
val driverToInstall = File(driverList.value[selectedDriver].first) |
|||
if (driverToInstall.exists()) { |
|||
GpuDriverHelper.installCustomDriver(driverToInstall) |
|||
} else { |
|||
GpuDriverHelper.installDefaultDriver() |
|||
} |
|||
setDriverReady() |
|||
} |
|||
} |
|||
} |
|||
|
|||
private fun setDriverReady() { |
|||
_isDriverReady.value = true |
|||
_selectedDriverMetadata.value = GpuDriverHelper.customDriverData.name |
|||
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver) |
|||
} |
|||
} |
|||
@ -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="?attr/colorControlNormal" |
|||
android:pathData="M22.7,19l-9.1,-9.1c0.9,-2.3 0.4,-5 -1.5,-6.9 -2,-2 -5,-2.4 -7.4,-1.3L9,6 6,9 1.6,4.7C0.4,7.1 0.9,10.1 2.9,12.1c1.9,1.9 4.6,2.4 6.9,1.5l9.1,9.1c0.4,0.4 1,0.4 1.4,0l2.3,-2.3c0.5,-0.4 0.5,-1.1 0.1,-1.4z" /> |
|||
</vector> |
|||
@ -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="?attr/colorControlNormal" |
|||
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z" /> |
|||
</vector> |
|||
@ -0,0 +1,89 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<com.google.android.material.card.MaterialCardView 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" |
|||
style="?attr/materialCardViewOutlinedStyle" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginHorizontal="16dp" |
|||
android:layout_marginVertical="12dp" |
|||
android:background="?attr/selectableItemBackground" |
|||
android:clickable="true" |
|||
android:focusable="true"> |
|||
|
|||
<LinearLayout |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:orientation="horizontal" |
|||
android:layout_gravity="center" |
|||
android:padding="16dp"> |
|||
|
|||
<RadioButton |
|||
android:id="@+id/radio_button" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_gravity="center_vertical" |
|||
android:clickable="false" |
|||
android:checked="false" /> |
|||
|
|||
<LinearLayout |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:layout_weight="1" |
|||
android:orientation="vertical" |
|||
android:layout_gravity="center_vertical"> |
|||
|
|||
<com.google.android.material.textview.MaterialTextView |
|||
android:id="@+id/title" |
|||
style="@style/TextAppearance.Material3.TitleMedium" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:ellipsize="none" |
|||
android:marqueeRepeatLimit="marquee_forever" |
|||
android:requiresFadingEdge="horizontal" |
|||
android:singleLine="true" |
|||
android:textAlignment="viewStart" |
|||
tools:text="@string/select_gpu_driver_default" /> |
|||
|
|||
<com.google.android.material.textview.MaterialTextView |
|||
android:id="@+id/version" |
|||
style="@style/TextAppearance.Material3.BodyMedium" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginTop="6dp" |
|||
android:ellipsize="none" |
|||
android:marqueeRepeatLimit="marquee_forever" |
|||
android:requiresFadingEdge="horizontal" |
|||
android:singleLine="true" |
|||
android:textAlignment="viewStart" |
|||
tools:text="@string/install_gpu_driver_description" /> |
|||
|
|||
<com.google.android.material.textview.MaterialTextView |
|||
android:id="@+id/description" |
|||
style="@style/TextAppearance.Material3.BodyMedium" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginTop="6dp" |
|||
android:ellipsize="none" |
|||
android:marqueeRepeatLimit="marquee_forever" |
|||
android:requiresFadingEdge="horizontal" |
|||
android:singleLine="true" |
|||
android:textAlignment="viewStart" |
|||
tools:text="@string/install_gpu_driver_description" /> |
|||
|
|||
</LinearLayout> |
|||
|
|||
<Button |
|||
android:id="@+id/button_delete" |
|||
style="@style/Widget.Material3.Button.IconButton" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_gravity="center_vertical" |
|||
android:contentDescription="@string/delete" |
|||
android:tooltipText="@string/delete" |
|||
app:icon="@drawable/ic_delete" |
|||
app:iconTint="?attr/colorControlNormal" /> |
|||
|
|||
</LinearLayout> |
|||
|
|||
</com.google.android.material.card.MaterialCardView> |
|||
@ -0,0 +1,48 @@ |
|||
<?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:id="@+id/coordinator_licenses" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:background="?attr/colorSurface"> |
|||
|
|||
<androidx.coordinatorlayout.widget.CoordinatorLayout |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent"> |
|||
|
|||
<com.google.android.material.appbar.AppBarLayout |
|||
android:id="@+id/appbar_drivers" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:fitsSystemWindows="true" |
|||
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" |
|||
app:navigationIcon="@drawable/ic_back" |
|||
app:title="@string/gpu_driver_manager" /> |
|||
|
|||
</com.google.android.material.appbar.AppBarLayout> |
|||
|
|||
<androidx.recyclerview.widget.RecyclerView |
|||
android:id="@+id/list_drivers" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:clipToPadding="false" |
|||
app:layout_behavior="@string/appbar_scrolling_view_behavior" /> |
|||
|
|||
</androidx.coordinatorlayout.widget.CoordinatorLayout> |
|||
|
|||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton |
|||
android:id="@+id/button_install" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_gravity="bottom|end" |
|||
android:text="@string/install" |
|||
app:icon="@drawable/ic_add" |
|||
app:layout_constraintBottom_toBottomOf="parent" |
|||
app:layout_constraintEnd_toEndOf="parent" /> |
|||
|
|||
</androidx.constraintlayout.widget.ConstraintLayout> |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue