android: Use callback to update progress bar dialogs
This commit is contained in:
parent
ccd3dd842f
commit
d79d4d5986
10 changed files with 236 additions and 121 deletions
|
@ -156,22 +156,22 @@ class AddonsFragment : Fragment() {
|
||||||
descriptionId = R.string.invalid_directory_description
|
descriptionId = R.string.invalid_directory_description
|
||||||
)
|
)
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
ProgressDialogFragment.newInstance(
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
R.string.installing_game_content,
|
R.string.installing_game_content,
|
||||||
false
|
false
|
||||||
) {
|
) { progressCallback, _ ->
|
||||||
val parentDirectoryName = externalAddonDirectory.name
|
val parentDirectoryName = externalAddonDirectory.name
|
||||||
val internalAddonDirectory =
|
val internalAddonDirectory =
|
||||||
File(args.game.addonDir + parentDirectoryName)
|
File(args.game.addonDir + parentDirectoryName)
|
||||||
try {
|
try {
|
||||||
externalAddonDirectory.copyFilesTo(internalAddonDirectory)
|
externalAddonDirectory.copyFilesTo(internalAddonDirectory, progressCallback)
|
||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
return@newInstance errorMessage
|
return@newInstance errorMessage
|
||||||
}
|
}
|
||||||
addonViewModel.refreshAddons()
|
addonViewModel.refreshAddons()
|
||||||
return@newInstance getString(R.string.addon_installed_successfully)
|
return@newInstance getString(R.string.addon_installed_successfully)
|
||||||
}.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
|
||||||
} else {
|
} else {
|
||||||
errorMessage.show(parentFragmentManager, MessageDialogFragment.TAG)
|
errorMessage.show(parentFragmentManager, MessageDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,11 +173,11 @@ class DriverManagerFragment : Fragment() {
|
||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
ProgressDialogFragment.newInstance(
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
R.string.installing_driver,
|
R.string.installing_driver,
|
||||||
false
|
false
|
||||||
) {
|
) { _, _ ->
|
||||||
val driverPath =
|
val driverPath =
|
||||||
"${GpuDriverHelper.driverStoragePath}${FileUtil.getFilename(result)}"
|
"${GpuDriverHelper.driverStoragePath}${FileUtil.getFilename(result)}"
|
||||||
val driverFile = File(driverPath)
|
val driverFile = File(driverPath)
|
||||||
|
@ -213,6 +213,6 @@ class DriverManagerFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return@newInstance Any()
|
return@newInstance Any()
|
||||||
}.show(childFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
}.show(childFragmentManager, ProgressDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,6 @@ import org.yuzu.yuzu_emu.utils.FileUtil
|
||||||
import org.yuzu.yuzu_emu.utils.GameIconUtils
|
import org.yuzu.yuzu_emu.utils.GameIconUtils
|
||||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||||
import org.yuzu.yuzu_emu.utils.MemoryUtil
|
import org.yuzu.yuzu_emu.utils.MemoryUtil
|
||||||
import java.io.BufferedInputStream
|
|
||||||
import java.io.BufferedOutputStream
|
import java.io.BufferedOutputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
@ -357,27 +356,17 @@ class GamePropertiesFragment : Fragment() {
|
||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
val inputZip = requireContext().contentResolver.openInputStream(result)
|
|
||||||
val savesFolder = File(args.game.saveDir)
|
val savesFolder = File(args.game.saveDir)
|
||||||
val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
|
val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
|
||||||
cacheSaveDir.mkdir()
|
cacheSaveDir.mkdir()
|
||||||
|
|
||||||
if (inputZip == null) {
|
ProgressDialogFragment.newInstance(
|
||||||
Toast.makeText(
|
|
||||||
YuzuApplication.appContext,
|
|
||||||
getString(R.string.fatal_error),
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
return@registerForActivityResult
|
|
||||||
}
|
|
||||||
|
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
R.string.save_files_importing,
|
R.string.save_files_importing,
|
||||||
false
|
false
|
||||||
) {
|
) { _, _ ->
|
||||||
try {
|
try {
|
||||||
FileUtil.unzipToInternalStorage(BufferedInputStream(inputZip), cacheSaveDir)
|
FileUtil.unzipToInternalStorage(result.toString(), cacheSaveDir)
|
||||||
val files = cacheSaveDir.listFiles()
|
val files = cacheSaveDir.listFiles()
|
||||||
var savesFolderFile: File? = null
|
var savesFolderFile: File? = null
|
||||||
if (files != null) {
|
if (files != null) {
|
||||||
|
@ -422,7 +411,7 @@ class GamePropertiesFragment : Fragment() {
|
||||||
Toast.LENGTH_LONG
|
Toast.LENGTH_LONG
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
}.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -436,11 +425,11 @@ class GamePropertiesFragment : Fragment() {
|
||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
ProgressDialogFragment.newInstance(
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
R.string.save_files_exporting,
|
R.string.save_files_exporting,
|
||||||
false
|
false
|
||||||
) {
|
) { _, _ ->
|
||||||
val saveLocation = args.game.saveDir
|
val saveLocation = args.game.saveDir
|
||||||
val zipResult = FileUtil.zipFromInternalStorage(
|
val zipResult = FileUtil.zipFromInternalStorage(
|
||||||
File(saveLocation),
|
File(saveLocation),
|
||||||
|
@ -452,6 +441,6 @@ class GamePropertiesFragment : Fragment() {
|
||||||
TaskState.Completed -> getString(R.string.export_success)
|
TaskState.Completed -> getString(R.string.export_success)
|
||||||
TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
|
TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
|
||||||
}
|
}
|
||||||
}.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,6 @@ import org.yuzu.yuzu_emu.model.TaskState
|
||||||
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
||||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil
|
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||||
import java.io.BufferedInputStream
|
|
||||||
import java.io.BufferedOutputStream
|
import java.io.BufferedOutputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
|
@ -195,26 +194,20 @@ class InstallableFragment : Fragment() {
|
||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
val inputZip = requireContext().contentResolver.openInputStream(result)
|
|
||||||
val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
|
val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
|
||||||
cacheSaveDir.mkdir()
|
cacheSaveDir.mkdir()
|
||||||
|
|
||||||
if (inputZip == null) {
|
ProgressDialogFragment.newInstance(
|
||||||
Toast.makeText(
|
|
||||||
YuzuApplication.appContext,
|
|
||||||
getString(R.string.fatal_error),
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
return@registerForActivityResult
|
|
||||||
}
|
|
||||||
|
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
R.string.save_files_importing,
|
R.string.save_files_importing,
|
||||||
false
|
false
|
||||||
) {
|
) { progressCallback, _ ->
|
||||||
try {
|
try {
|
||||||
FileUtil.unzipToInternalStorage(BufferedInputStream(inputZip), cacheSaveDir)
|
FileUtil.unzipToInternalStorage(
|
||||||
|
result.toString(),
|
||||||
|
cacheSaveDir,
|
||||||
|
progressCallback
|
||||||
|
)
|
||||||
val files = cacheSaveDir.listFiles()
|
val files = cacheSaveDir.listFiles()
|
||||||
var successfulImports = 0
|
var successfulImports = 0
|
||||||
var failedImports = 0
|
var failedImports = 0
|
||||||
|
@ -287,7 +280,7 @@ class InstallableFragment : Fragment() {
|
||||||
Toast.LENGTH_LONG
|
Toast.LENGTH_LONG
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
}.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val exportSaves = registerForActivityResult(
|
private val exportSaves = registerForActivityResult(
|
||||||
|
@ -297,11 +290,11 @@ class InstallableFragment : Fragment() {
|
||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
ProgressDialogFragment.newInstance(
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
R.string.save_files_exporting,
|
R.string.save_files_exporting,
|
||||||
false
|
false
|
||||||
) {
|
) { _, _ ->
|
||||||
val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
|
val cacheSaveDir = File("${requireContext().cacheDir.path}/saves/")
|
||||||
cacheSaveDir.mkdir()
|
cacheSaveDir.mkdir()
|
||||||
|
|
||||||
|
@ -338,6 +331,6 @@ class InstallableFragment : Fragment() {
|
||||||
TaskState.Completed -> getString(R.string.export_success)
|
TaskState.Completed -> getString(R.string.export_success)
|
||||||
TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
|
TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
|
||||||
}
|
}
|
||||||
}.show(parentFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
}.show(parentFragmentManager, ProgressDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,11 +23,13 @@ import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
|
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
|
||||||
import org.yuzu.yuzu_emu.model.TaskViewModel
|
import org.yuzu.yuzu_emu.model.TaskViewModel
|
||||||
|
|
||||||
class IndeterminateProgressDialogFragment : DialogFragment() {
|
class ProgressDialogFragment : DialogFragment() {
|
||||||
private val taskViewModel: TaskViewModel by activityViewModels()
|
private val taskViewModel: TaskViewModel by activityViewModels()
|
||||||
|
|
||||||
private lateinit var binding: DialogProgressBarBinding
|
private lateinit var binding: DialogProgressBarBinding
|
||||||
|
|
||||||
|
private val PROGRESS_BAR_RESOLUTION = 1000
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
val titleId = requireArguments().getInt(TITLE)
|
val titleId = requireArguments().getInt(TITLE)
|
||||||
val cancellable = requireArguments().getBoolean(CANCELLABLE)
|
val cancellable = requireArguments().getBoolean(CANCELLABLE)
|
||||||
|
@ -61,6 +63,7 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
binding.message.isSelected = true
|
||||||
viewLifecycleOwner.lifecycleScope.apply {
|
viewLifecycleOwner.lifecycleScope.apply {
|
||||||
launch {
|
launch {
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||||
|
@ -97,6 +100,35 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||||
|
taskViewModel.progress.collect {
|
||||||
|
if (it != 0.0) {
|
||||||
|
binding.progressBar.apply {
|
||||||
|
isIndeterminate = false
|
||||||
|
progress = (
|
||||||
|
(it / taskViewModel.maxProgress.value) *
|
||||||
|
PROGRESS_BAR_RESOLUTION
|
||||||
|
).toInt()
|
||||||
|
min = 0
|
||||||
|
max = PROGRESS_BAR_RESOLUTION
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||||
|
taskViewModel.message.collect {
|
||||||
|
if (it.isEmpty()) {
|
||||||
|
binding.message.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
binding.message.visibility = View.VISIBLE
|
||||||
|
binding.message.text = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,6 +140,7 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
||||||
val negativeButton = alertDialog.getButton(Dialog.BUTTON_NEGATIVE)
|
val negativeButton = alertDialog.getButton(Dialog.BUTTON_NEGATIVE)
|
||||||
negativeButton.setOnClickListener {
|
negativeButton.setOnClickListener {
|
||||||
alertDialog.setTitle(getString(R.string.cancelling))
|
alertDialog.setTitle(getString(R.string.cancelling))
|
||||||
|
binding.progressBar.isIndeterminate = true
|
||||||
taskViewModel.setCancelled(true)
|
taskViewModel.setCancelled(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,9 +155,12 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
||||||
activity: FragmentActivity,
|
activity: FragmentActivity,
|
||||||
titleId: Int,
|
titleId: Int,
|
||||||
cancellable: Boolean = false,
|
cancellable: Boolean = false,
|
||||||
task: suspend () -> Any
|
task: suspend (
|
||||||
): IndeterminateProgressDialogFragment {
|
progressCallback: (max: Long, progress: Long) -> Boolean,
|
||||||
val dialog = IndeterminateProgressDialogFragment()
|
messageCallback: (message: String) -> Unit
|
||||||
|
) -> Any
|
||||||
|
): ProgressDialogFragment {
|
||||||
|
val dialog = ProgressDialogFragment()
|
||||||
val args = Bundle()
|
val args = Bundle()
|
||||||
ViewModelProvider(activity)[TaskViewModel::class.java].task = task
|
ViewModelProvider(activity)[TaskViewModel::class.java].task = task
|
||||||
args.putInt(TITLE, titleId)
|
args.putInt(TITLE, titleId)
|
|
@ -8,6 +8,7 @@ import androidx.lifecycle.viewModelScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class TaskViewModel : ViewModel() {
|
class TaskViewModel : ViewModel() {
|
||||||
|
@ -23,13 +24,28 @@ class TaskViewModel : ViewModel() {
|
||||||
val cancelled: StateFlow<Boolean> get() = _cancelled
|
val cancelled: StateFlow<Boolean> get() = _cancelled
|
||||||
private val _cancelled = MutableStateFlow(false)
|
private val _cancelled = MutableStateFlow(false)
|
||||||
|
|
||||||
lateinit var task: suspend () -> Any
|
private val _progress = MutableStateFlow(0.0)
|
||||||
|
val progress = _progress.asStateFlow()
|
||||||
|
|
||||||
|
private val _maxProgress = MutableStateFlow(0.0)
|
||||||
|
val maxProgress = _maxProgress.asStateFlow()
|
||||||
|
|
||||||
|
private val _message = MutableStateFlow("")
|
||||||
|
val message = _message.asStateFlow()
|
||||||
|
|
||||||
|
lateinit var task: suspend (
|
||||||
|
progressCallback: (max: Long, progress: Long) -> Boolean,
|
||||||
|
messageCallback: (message: String) -> Unit
|
||||||
|
) -> Any
|
||||||
|
|
||||||
fun clear() {
|
fun clear() {
|
||||||
_result.value = Any()
|
_result.value = Any()
|
||||||
_isComplete.value = false
|
_isComplete.value = false
|
||||||
_isRunning.value = false
|
_isRunning.value = false
|
||||||
_cancelled.value = false
|
_cancelled.value = false
|
||||||
|
_progress.value = 0.0
|
||||||
|
_maxProgress.value = 0.0
|
||||||
|
_message.value = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setCancelled(value: Boolean) {
|
fun setCancelled(value: Boolean) {
|
||||||
|
@ -43,7 +59,16 @@ class TaskViewModel : ViewModel() {
|
||||||
_isRunning.value = true
|
_isRunning.value = true
|
||||||
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val res = task()
|
val res = task(
|
||||||
|
{ max, progress ->
|
||||||
|
_maxProgress.value = max.toDouble()
|
||||||
|
_progress.value = progress.toDouble()
|
||||||
|
return@task cancelled.value
|
||||||
|
},
|
||||||
|
{ message ->
|
||||||
|
_message.value = message
|
||||||
|
}
|
||||||
|
)
|
||||||
_result.value = res
|
_result.value = res
|
||||||
_isComplete.value = true
|
_isComplete.value = true
|
||||||
_isRunning.value = false
|
_isRunning.value = false
|
||||||
|
|
|
@ -38,12 +38,13 @@ import org.yuzu.yuzu_emu.activities.EmulationActivity
|
||||||
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
|
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||||
import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment
|
import org.yuzu.yuzu_emu.fragments.AddGameFolderDialogFragment
|
||||||
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
|
import org.yuzu.yuzu_emu.fragments.ProgressDialogFragment
|
||||||
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
|
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
|
||||||
import org.yuzu.yuzu_emu.model.AddonViewModel
|
import org.yuzu.yuzu_emu.model.AddonViewModel
|
||||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||||
|
import org.yuzu.yuzu_emu.model.InstallResult
|
||||||
import org.yuzu.yuzu_emu.model.TaskState
|
import org.yuzu.yuzu_emu.model.TaskState
|
||||||
import org.yuzu.yuzu_emu.model.TaskViewModel
|
import org.yuzu.yuzu_emu.model.TaskViewModel
|
||||||
import org.yuzu.yuzu_emu.utils.*
|
import org.yuzu.yuzu_emu.utils.*
|
||||||
|
@ -369,26 +370,23 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
val inputZip = contentResolver.openInputStream(result)
|
|
||||||
if (inputZip == null) {
|
|
||||||
Toast.makeText(
|
|
||||||
applicationContext,
|
|
||||||
getString(R.string.fatal_error),
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
return@registerForActivityResult
|
|
||||||
}
|
|
||||||
|
|
||||||
val filterNCA = FilenameFilter { _, dirName -> dirName.endsWith(".nca") }
|
val filterNCA = FilenameFilter { _, dirName -> dirName.endsWith(".nca") }
|
||||||
|
|
||||||
val firmwarePath =
|
val firmwarePath =
|
||||||
File(DirectoryInitialization.userDirectory + "/nand/system/Contents/registered/")
|
File(DirectoryInitialization.userDirectory + "/nand/system/Contents/registered/")
|
||||||
val cacheFirmwareDir = File("${cacheDir.path}/registered/")
|
val cacheFirmwareDir = File("${cacheDir.path}/registered/")
|
||||||
|
|
||||||
val task: () -> Any = {
|
ProgressDialogFragment.newInstance(
|
||||||
|
this,
|
||||||
|
R.string.firmware_installing
|
||||||
|
) { progressCallback, _ ->
|
||||||
var messageToShow: Any
|
var messageToShow: Any
|
||||||
try {
|
try {
|
||||||
FileUtil.unzipToInternalStorage(BufferedInputStream(inputZip), cacheFirmwareDir)
|
FileUtil.unzipToInternalStorage(
|
||||||
|
result.toString(),
|
||||||
|
cacheFirmwareDir,
|
||||||
|
progressCallback
|
||||||
|
)
|
||||||
val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1
|
val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1
|
||||||
val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2
|
val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2
|
||||||
messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) {
|
messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) {
|
||||||
|
@ -404,18 +402,13 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
getString(R.string.save_file_imported_success)
|
getString(R.string.save_file_imported_success)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
Log.error("[MainActivity] Firmware install failed - ${e.message}")
|
||||||
messageToShow = getString(R.string.fatal_error)
|
messageToShow = getString(R.string.fatal_error)
|
||||||
} finally {
|
} finally {
|
||||||
cacheFirmwareDir.deleteRecursively()
|
cacheFirmwareDir.deleteRecursively()
|
||||||
}
|
}
|
||||||
messageToShow
|
messageToShow
|
||||||
}
|
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
|
||||||
|
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
|
||||||
this,
|
|
||||||
R.string.firmware_installing,
|
|
||||||
task = task
|
|
||||||
).show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val getAmiiboKey =
|
val getAmiiboKey =
|
||||||
|
@ -474,11 +467,11 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
ProgressDialogFragment.newInstance(
|
||||||
this@MainActivity,
|
this@MainActivity,
|
||||||
R.string.verifying_content,
|
R.string.verifying_content,
|
||||||
false
|
false
|
||||||
) {
|
) { _, _ ->
|
||||||
var updatesMatchProgram = true
|
var updatesMatchProgram = true
|
||||||
for (document in documents) {
|
for (document in documents) {
|
||||||
val valid = NativeLibrary.doesUpdateMatchProgram(
|
val valid = NativeLibrary.doesUpdateMatchProgram(
|
||||||
|
@ -501,44 +494,42 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
positiveAction = { homeViewModel.setContentToInstall(documents) }
|
positiveAction = { homeViewModel.setContentToInstall(documents) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun installContent(documents: List<Uri>) {
|
private fun installContent(documents: List<Uri>) {
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
ProgressDialogFragment.newInstance(
|
||||||
this@MainActivity,
|
this@MainActivity,
|
||||||
R.string.installing_game_content
|
R.string.installing_game_content
|
||||||
) {
|
) { progressCallback, messageCallback ->
|
||||||
var installSuccess = 0
|
var installSuccess = 0
|
||||||
var installOverwrite = 0
|
var installOverwrite = 0
|
||||||
var errorBaseGame = 0
|
var errorBaseGame = 0
|
||||||
var errorExtension = 0
|
var error = 0
|
||||||
var errorOther = 0
|
|
||||||
documents.forEach {
|
documents.forEach {
|
||||||
|
messageCallback.invoke(FileUtil.getFilename(it))
|
||||||
when (
|
when (
|
||||||
|
InstallResult.from(
|
||||||
NativeLibrary.installFileToNand(
|
NativeLibrary.installFileToNand(
|
||||||
it.toString(),
|
it.toString(),
|
||||||
FileUtil.getExtension(it)
|
progressCallback
|
||||||
|
)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
NativeLibrary.InstallFileToNandResult.Success -> {
|
InstallResult.Success -> {
|
||||||
installSuccess += 1
|
installSuccess += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> {
|
InstallResult.Overwrite -> {
|
||||||
installOverwrite += 1
|
installOverwrite += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> {
|
InstallResult.BaseInstallAttempted -> {
|
||||||
errorBaseGame += 1
|
errorBaseGame += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> {
|
InstallResult.Failure -> {
|
||||||
errorExtension += 1
|
error += 1
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
errorOther += 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -565,7 +556,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
)
|
)
|
||||||
installResult.append(separator)
|
installResult.append(separator)
|
||||||
}
|
}
|
||||||
val errorTotal: Int = errorBaseGame + errorExtension + errorOther
|
val errorTotal: Int = errorBaseGame + error
|
||||||
if (errorTotal > 0) {
|
if (errorTotal > 0) {
|
||||||
installResult.append(separator)
|
installResult.append(separator)
|
||||||
installResult.append(
|
installResult.append(
|
||||||
|
@ -582,14 +573,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
)
|
)
|
||||||
installResult.append(separator)
|
installResult.append(separator)
|
||||||
}
|
}
|
||||||
if (errorExtension > 0) {
|
if (error > 0) {
|
||||||
installResult.append(separator)
|
|
||||||
installResult.append(
|
|
||||||
getString(R.string.install_game_content_failure_file_extension)
|
|
||||||
)
|
|
||||||
installResult.append(separator)
|
|
||||||
}
|
|
||||||
if (errorOther > 0) {
|
|
||||||
installResult.append(
|
installResult.append(
|
||||||
getString(R.string.install_game_content_failure_description)
|
getString(R.string.install_game_content_failure_description)
|
||||||
)
|
)
|
||||||
|
@ -608,7 +592,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
descriptionString = installResult.toString().trim()
|
descriptionString = installResult.toString().trim()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
|
|
||||||
val exportUserData = registerForActivityResult(
|
val exportUserData = registerForActivityResult(
|
||||||
|
@ -618,16 +602,16 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
ProgressDialogFragment.newInstance(
|
||||||
this,
|
this,
|
||||||
R.string.exporting_user_data,
|
R.string.exporting_user_data,
|
||||||
true
|
true
|
||||||
) {
|
) { progressCallback, _ ->
|
||||||
val zipResult = FileUtil.zipFromInternalStorage(
|
val zipResult = FileUtil.zipFromInternalStorage(
|
||||||
File(DirectoryInitialization.userDirectory!!),
|
File(DirectoryInitialization.userDirectory!!),
|
||||||
DirectoryInitialization.userDirectory!!,
|
DirectoryInitialization.userDirectory!!,
|
||||||
BufferedOutputStream(contentResolver.openOutputStream(result)),
|
BufferedOutputStream(contentResolver.openOutputStream(result)),
|
||||||
taskViewModel.cancelled,
|
progressCallback,
|
||||||
compression = false
|
compression = false
|
||||||
)
|
)
|
||||||
return@newInstance when (zipResult) {
|
return@newInstance when (zipResult) {
|
||||||
|
@ -635,7 +619,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
TaskState.Failed -> R.string.export_failed
|
TaskState.Failed -> R.string.export_failed
|
||||||
TaskState.Cancelled -> R.string.user_data_export_cancelled
|
TaskState.Cancelled -> R.string.user_data_export_cancelled
|
||||||
}
|
}
|
||||||
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
|
|
||||||
val importUserData =
|
val importUserData =
|
||||||
|
@ -644,10 +628,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
return@registerForActivityResult
|
return@registerForActivityResult
|
||||||
}
|
}
|
||||||
|
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
ProgressDialogFragment.newInstance(
|
||||||
this,
|
this,
|
||||||
R.string.importing_user_data
|
R.string.importing_user_data
|
||||||
) {
|
) { progressCallback, _ ->
|
||||||
val checkStream =
|
val checkStream =
|
||||||
ZipInputStream(BufferedInputStream(contentResolver.openInputStream(result)))
|
ZipInputStream(BufferedInputStream(contentResolver.openInputStream(result)))
|
||||||
var isYuzuBackup = false
|
var isYuzuBackup = false
|
||||||
|
@ -676,8 +660,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
// Copy archive to internal storage
|
// Copy archive to internal storage
|
||||||
try {
|
try {
|
||||||
FileUtil.unzipToInternalStorage(
|
FileUtil.unzipToInternalStorage(
|
||||||
BufferedInputStream(contentResolver.openInputStream(result)),
|
result.toString(),
|
||||||
File(DirectoryInitialization.userDirectory!!)
|
File(DirectoryInitialization.userDirectory!!),
|
||||||
|
progressCallback
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
return@newInstance MessageDialogFragment.newInstance(
|
return@newInstance MessageDialogFragment.newInstance(
|
||||||
|
@ -694,6 +679,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
driverViewModel.reloadDriverData()
|
driverViewModel.reloadDriverData()
|
||||||
|
|
||||||
return@newInstance getString(R.string.user_data_import_success)
|
return@newInstance getString(R.string.user_data_import_success)
|
||||||
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
}.show(supportFragmentManager, ProgressDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import android.database.Cursor
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.DocumentsContract
|
import android.provider.DocumentsContract
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import java.io.BufferedInputStream
|
import java.io.BufferedInputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
@ -19,6 +18,7 @@ import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
|
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
|
||||||
import org.yuzu.yuzu_emu.model.TaskState
|
import org.yuzu.yuzu_emu.model.TaskState
|
||||||
import java.io.BufferedOutputStream
|
import java.io.BufferedOutputStream
|
||||||
|
import java.io.OutputStream
|
||||||
import java.lang.NullPointerException
|
import java.lang.NullPointerException
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.util.zip.Deflater
|
import java.util.zip.Deflater
|
||||||
|
@ -283,12 +283,34 @@ object FileUtil {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts the given zip file into the given directory.
|
* Extracts the given zip file into the given directory.
|
||||||
|
* @param path String representation of a [Uri] or a typical path delimited by '/'
|
||||||
|
* @param destDir Location to unzip the contents of [path] into
|
||||||
|
* @param progressCallback Lambda that is called with the total number of files and the current
|
||||||
|
* progress through the process. Stops execution as soon as possible if this returns true.
|
||||||
*/
|
*/
|
||||||
@Throws(SecurityException::class)
|
@Throws(SecurityException::class)
|
||||||
fun unzipToInternalStorage(zipStream: BufferedInputStream, destDir: File) {
|
fun unzipToInternalStorage(
|
||||||
ZipInputStream(zipStream).use { zis ->
|
path: String,
|
||||||
|
destDir: File,
|
||||||
|
progressCallback: (max: Long, progress: Long) -> Boolean = { _, _ -> false }
|
||||||
|
) {
|
||||||
|
var totalEntries = 0L
|
||||||
|
ZipInputStream(getInputStream(path)).use { zis ->
|
||||||
|
var tempEntry = zis.nextEntry
|
||||||
|
while (tempEntry != null) {
|
||||||
|
tempEntry = zis.nextEntry
|
||||||
|
totalEntries++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var progress = 0L
|
||||||
|
ZipInputStream(getInputStream(path)).use { zis ->
|
||||||
var entry: ZipEntry? = zis.nextEntry
|
var entry: ZipEntry? = zis.nextEntry
|
||||||
while (entry != null) {
|
while (entry != null) {
|
||||||
|
if (progressCallback.invoke(totalEntries, progress)) {
|
||||||
|
return@use
|
||||||
|
}
|
||||||
|
|
||||||
val newFile = File(destDir, entry.name)
|
val newFile = File(destDir, entry.name)
|
||||||
val destinationDirectory = if (entry.isDirectory) newFile else newFile.parentFile
|
val destinationDirectory = if (entry.isDirectory) newFile else newFile.parentFile
|
||||||
|
|
||||||
|
@ -304,6 +326,7 @@ object FileUtil {
|
||||||
newFile.outputStream().use { fos -> zis.copyTo(fos) }
|
newFile.outputStream().use { fos -> zis.copyTo(fos) }
|
||||||
}
|
}
|
||||||
entry = zis.nextEntry
|
entry = zis.nextEntry
|
||||||
|
progress++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -313,14 +336,15 @@ object FileUtil {
|
||||||
* @param inputFile File representation of the item that will be zipped
|
* @param inputFile File representation of the item that will be zipped
|
||||||
* @param rootDir Directory containing the inputFile
|
* @param rootDir Directory containing the inputFile
|
||||||
* @param outputStream Stream where the zip file will be output
|
* @param outputStream Stream where the zip file will be output
|
||||||
* @param cancelled [StateFlow] that reports whether this process has been cancelled
|
* @param progressCallback Lambda that is called with the total number of files and the current
|
||||||
|
* progress through the process. Stops execution as soon as possible if this returns true.
|
||||||
* @param compression Disables compression if true
|
* @param compression Disables compression if true
|
||||||
*/
|
*/
|
||||||
fun zipFromInternalStorage(
|
fun zipFromInternalStorage(
|
||||||
inputFile: File,
|
inputFile: File,
|
||||||
rootDir: String,
|
rootDir: String,
|
||||||
outputStream: BufferedOutputStream,
|
outputStream: BufferedOutputStream,
|
||||||
cancelled: StateFlow<Boolean>? = null,
|
progressCallback: (max: Long, progress: Long) -> Boolean = { _, _ -> false },
|
||||||
compression: Boolean = true
|
compression: Boolean = true
|
||||||
): TaskState {
|
): TaskState {
|
||||||
try {
|
try {
|
||||||
|
@ -330,8 +354,10 @@ object FileUtil {
|
||||||
zos.setLevel(Deflater.NO_COMPRESSION)
|
zos.setLevel(Deflater.NO_COMPRESSION)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var count = 0L
|
||||||
|
val totalFiles = inputFile.walkTopDown().count().toLong()
|
||||||
inputFile.walkTopDown().forEach { file ->
|
inputFile.walkTopDown().forEach { file ->
|
||||||
if (cancelled?.value == true) {
|
if (progressCallback.invoke(totalFiles, count)) {
|
||||||
return TaskState.Cancelled
|
return TaskState.Cancelled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,6 +369,7 @@ object FileUtil {
|
||||||
if (file.isFile) {
|
if (file.isFile) {
|
||||||
file.inputStream().use { fis -> fis.copyTo(zos) }
|
file.inputStream().use { fis -> fis.copyTo(zos) }
|
||||||
}
|
}
|
||||||
|
count++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -356,9 +383,14 @@ object FileUtil {
|
||||||
/**
|
/**
|
||||||
* Helper function that copies the contents of a DocumentFile folder into a [File]
|
* Helper function that copies the contents of a DocumentFile folder into a [File]
|
||||||
* @param file [File] representation of the folder to copy into
|
* @param file [File] representation of the folder to copy into
|
||||||
|
* @param progressCallback Lambda that is called with the total number of files and the current
|
||||||
|
* progress through the process. Stops execution as soon as possible if this returns true.
|
||||||
* @throws IllegalStateException Fails when trying to copy a folder into a file and vice versa
|
* @throws IllegalStateException Fails when trying to copy a folder into a file and vice versa
|
||||||
*/
|
*/
|
||||||
fun DocumentFile.copyFilesTo(file: File) {
|
fun DocumentFile.copyFilesTo(
|
||||||
|
file: File,
|
||||||
|
progressCallback: (max: Long, progress: Long) -> Boolean = { _, _ -> false }
|
||||||
|
) {
|
||||||
file.mkdirs()
|
file.mkdirs()
|
||||||
if (!this.isDirectory || !file.isDirectory) {
|
if (!this.isDirectory || !file.isDirectory) {
|
||||||
throw IllegalStateException(
|
throw IllegalStateException(
|
||||||
|
@ -366,7 +398,13 @@ object FileUtil {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var count = 0L
|
||||||
|
val totalFiles = this.listFiles().size.toLong()
|
||||||
this.listFiles().forEach {
|
this.listFiles().forEach {
|
||||||
|
if (progressCallback.invoke(totalFiles, count)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val newFile = File(file, it.name!!)
|
val newFile = File(file, it.name!!)
|
||||||
if (it.isDirectory) {
|
if (it.isDirectory) {
|
||||||
newFile.mkdirs()
|
newFile.mkdirs()
|
||||||
|
@ -381,6 +419,7 @@ object FileUtil {
|
||||||
newFile.outputStream().use { os -> bos.copyTo(os) }
|
newFile.outputStream().use { os -> bos.copyTo(os) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
count++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -427,6 +466,18 @@ object FileUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getInputStream(path: String) = if (path.contains("content://")) {
|
||||||
|
Uri.parse(path).inputStream()
|
||||||
|
} else {
|
||||||
|
File(path).inputStream()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getOutputStream(path: String) = if (path.contains("content://")) {
|
||||||
|
Uri.parse(path).outputStream()
|
||||||
|
} else {
|
||||||
|
File(path).outputStream()
|
||||||
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun getStringFromFile(file: File): String =
|
fun getStringFromFile(file: File): String =
|
||||||
String(file.readBytes(), StandardCharsets.UTF_8)
|
String(file.readBytes(), StandardCharsets.UTF_8)
|
||||||
|
@ -434,4 +485,19 @@ object FileUtil {
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun getStringFromInputStream(stream: InputStream): String =
|
fun getStringFromInputStream(stream: InputStream): String =
|
||||||
String(stream.readBytes(), StandardCharsets.UTF_8)
|
String(stream.readBytes(), StandardCharsets.UTF_8)
|
||||||
|
|
||||||
|
fun DocumentFile.inputStream(): InputStream =
|
||||||
|
YuzuApplication.appContext.contentResolver.openInputStream(uri)!!
|
||||||
|
|
||||||
|
fun DocumentFile.outputStream(): OutputStream =
|
||||||
|
YuzuApplication.appContext.contentResolver.openOutputStream(uri)!!
|
||||||
|
|
||||||
|
fun Uri.inputStream(): InputStream =
|
||||||
|
YuzuApplication.appContext.contentResolver.openInputStream(this)!!
|
||||||
|
|
||||||
|
fun Uri.outputStream(): OutputStream =
|
||||||
|
YuzuApplication.appContext.contentResolver.openOutputStream(this)!!
|
||||||
|
|
||||||
|
fun Uri.asDocumentFile(): DocumentFile? =
|
||||||
|
DocumentFile.fromSingleUri(YuzuApplication.appContext, this)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.utils
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import java.io.BufferedInputStream
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
|
@ -123,7 +122,7 @@ object GpuDriverHelper {
|
||||||
// Unzip the driver.
|
// Unzip the driver.
|
||||||
try {
|
try {
|
||||||
FileUtil.unzipToInternalStorage(
|
FileUtil.unzipToInternalStorage(
|
||||||
BufferedInputStream(copiedFile.inputStream()),
|
copiedFile.path,
|
||||||
File(driverInstallationPath!!)
|
File(driverInstallationPath!!)
|
||||||
)
|
)
|
||||||
} catch (e: SecurityException) {
|
} catch (e: SecurityException) {
|
||||||
|
@ -156,7 +155,7 @@ object GpuDriverHelper {
|
||||||
// Unzip the driver to the private installation directory
|
// Unzip the driver to the private installation directory
|
||||||
try {
|
try {
|
||||||
FileUtil.unzipToInternalStorage(
|
FileUtil.unzipToInternalStorage(
|
||||||
BufferedInputStream(driver.inputStream()),
|
driver.path,
|
||||||
File(driverInstallationPath!!)
|
File(driverInstallationPath!!)
|
||||||
)
|
)
|
||||||
} catch (e: SecurityException) {
|
} catch (e: SecurityException) {
|
||||||
|
|
|
@ -1,8 +1,30 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<com.google.android.material.progressindicator.LinearProgressIndicator xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/message"
|
||||||
|
style="@style/TextAppearance.Material3.BodyMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:layout_marginBottom="6dp"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:marqueeRepeatLimit="marquee_forever"
|
||||||
|
android:requiresFadingEdge="horizontal"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
android:id="@+id/progress_bar"
|
android:id="@+id/progress_bar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="24dp"
|
android:padding="24dp"
|
||||||
app:trackCornerRadius="4dp" />
|
app:trackCornerRadius="4dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
Loading…
Reference in a new issue