pineapple-src/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt

200 lines
7.9 KiB
Kotlin
Executable File

// 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.navigation.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.GridLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
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.StringSetting
import org.yuzu.yuzu_emu.model.Driver.Companion.toDriver
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 org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
import org.yuzu.yuzu_emu.utils.collect
import java.io.File
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()
private val args by navArgs<DriverManagerFragmentArgs>()
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)
driverViewModel.onOpenDriverManager(args.game)
if (NativeConfig.isPerGameConfigLoaded()) {
binding.toolbarDrivers.inflateMenu(R.menu.menu_driver_manager)
driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
binding.toolbarDrivers.setOnMenuItemClickListener {
when (it.itemId) {
R.id.menu_driver_use_global -> {
StringSetting.DRIVER_PATH.global = true
driverViewModel.updateDriverList()
(binding.listDrivers.adapter as DriverAdapter)
.replaceList(driverViewModel.driverList.value)
driverViewModel.showClearButton(false)
true
}
else -> false
}
}
driverViewModel.showClearButton.collect(viewLifecycleOwner) {
binding.toolbarDrivers.menu.findItem(R.id.menu_driver_use_global).isVisible = it
}
}
if (!driverViewModel.isInteractionAllowed.value) {
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)
}
setInsets()
}
override fun onDestroy() {
super.onDestroy()
driverViewModel.onCloseDriverManager(args.game)
}
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)
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
binding.buttonInstall.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)
)
windowInsets
}
private val getDriver =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result == null) {
return@registerForActivityResult
}
ProgressDialogFragment.newInstance(
requireActivity(),
R.string.installing_driver,
false
) { _, _ ->
val driverPath =
"${GpuDriverHelper.driverStoragePath}${FileUtil.getFilename(result)}"
val driverFile = File(driverPath)
// Ignore file exceptions when a user selects an invalid zip
try {
if (!GpuDriverHelper.copyDriverToInternalStorage(result)) {
throw IOException("Driver failed validation!")
}
} catch (_: IOException) {
if (driverFile.exists()) {
driverFile.delete()
}
return@newInstance getString(R.string.select_gpu_driver_error)
}
val driverData = GpuDriverHelper.getMetadataFromZip(driverFile)
val driverInList =
driverViewModel.driverData.firstOrNull { it.second == driverData }
if (driverInList != null) {
return@newInstance getString(R.string.driver_already_installed)
} else {
driverViewModel.onDriverAdded(Pair(driverPath, driverData))
withContext(Dispatchers.Main) {
if (_binding != null) {
val adapter = binding.listDrivers.adapter as DriverAdapter
adapter.addItem(driverData.toDriver())
adapter.selectItem(adapter.currentList.indices.last)
driverViewModel.showClearButton(!StringSetting.DRIVER_PATH.global)
binding.listDrivers
.smoothScrollToPosition(adapter.currentList.indices.last)
}
}
}
return@newInstance Any()
}.show(childFragmentManager, ProgressDialogFragment.TAG)
}
}