android: Add support for concurrent installs
This commit is contained in:
parent
eea2145698
commit
6c7e284f64
|
@ -0,0 +1,62 @@
|
||||||
|
// 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.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import org.yuzu.yuzu_emu.R
|
||||||
|
|
||||||
|
class InstallDialogFragment : DialogFragment() {
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
val titleId = requireArguments().getInt(TITLE)
|
||||||
|
val description = requireArguments().getString(DESCRIPTION)
|
||||||
|
val helpLinkId = requireArguments().getInt(HELP_LINK)
|
||||||
|
|
||||||
|
val dialog = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setPositiveButton(R.string.close, null)
|
||||||
|
.setTitle(titleId)
|
||||||
|
.setMessage(description)
|
||||||
|
|
||||||
|
if (helpLinkId != 0) {
|
||||||
|
dialog.setNeutralButton(R.string.learn_more) { _, _ ->
|
||||||
|
openLink(getString(helpLinkId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dialog.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openLink(link: String) {
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG = "MessageDialogFragment"
|
||||||
|
|
||||||
|
private const val TITLE = "Title"
|
||||||
|
private const val DESCRIPTION = "Description"
|
||||||
|
private const val HELP_LINK = "Link"
|
||||||
|
|
||||||
|
fun newInstance(
|
||||||
|
titleId: Int,
|
||||||
|
description: String,
|
||||||
|
helpLinkId: Int = 0
|
||||||
|
): InstallDialogFragment {
|
||||||
|
val dialog = InstallDialogFragment()
|
||||||
|
val bundle = Bundle()
|
||||||
|
bundle.apply {
|
||||||
|
putInt(TITLE, titleId)
|
||||||
|
putString(DESCRIPTION, description)
|
||||||
|
putInt(HELP_LINK, helpLinkId)
|
||||||
|
}
|
||||||
|
dialog.arguments = bundle
|
||||||
|
return dialog
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
package org.yuzu.yuzu_emu.ui.main
|
package org.yuzu.yuzu_emu.ui.main
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup.MarginLayoutParams
|
import android.view.ViewGroup.MarginLayoutParams
|
||||||
|
@ -42,6 +43,7 @@ import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
|
||||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
|
import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
|
||||||
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
|
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
|
||||||
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
|
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
|
||||||
|
import org.yuzu.yuzu_emu.fragments.InstallDialogFragment
|
||||||
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
|
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
|
||||||
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
|
||||||
|
@ -481,62 +483,110 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val installGameUpdate =
|
val installGameUpdate = registerForActivityResult(
|
||||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) {
|
ActivityResultContracts.OpenMultipleDocuments()
|
||||||
if (it == null) {
|
) { documents: List<Uri> ->
|
||||||
return@registerForActivityResult
|
if (documents.isNotEmpty()) {
|
||||||
}
|
|
||||||
|
|
||||||
IndeterminateProgressDialogFragment.newInstance(
|
IndeterminateProgressDialogFragment.newInstance(
|
||||||
this@MainActivity,
|
this@MainActivity,
|
||||||
R.string.install_game_content
|
R.string.install_game_content
|
||||||
) {
|
) {
|
||||||
val result = NativeLibrary.installFileToNand(it.toString())
|
var installSuccess = 0
|
||||||
|
var installOverwrite = 0
|
||||||
|
var errorBaseGame = 0
|
||||||
|
var errorExtension = 0
|
||||||
|
var errorOther = 0
|
||||||
|
var errorTotal = 0
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.Main) {
|
documents.forEach {
|
||||||
when (result) {
|
when (NativeLibrary.installFileToNand(it.toString())) {
|
||||||
NativeLibrary.InstallFileToNandResult.Success -> {
|
NativeLibrary.InstallFileToNandResult.Success -> {
|
||||||
Toast.makeText(
|
installSuccess += 1
|
||||||
applicationContext,
|
|
||||||
R.string.install_game_content_success,
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> {
|
NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> {
|
||||||
Toast.makeText(
|
installOverwrite += 1
|
||||||
applicationContext,
|
|
||||||
R.string.install_game_content_success_overwrite,
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> {
|
NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> {
|
||||||
MessageDialogFragment.newInstance(
|
errorBaseGame += 1
|
||||||
R.string.install_game_content_failure,
|
|
||||||
R.string.install_game_content_failure_base
|
|
||||||
).show(supportFragmentManager, MessageDialogFragment.TAG)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> {
|
NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> {
|
||||||
MessageDialogFragment.newInstance(
|
errorExtension += 1
|
||||||
R.string.install_game_content_failure,
|
|
||||||
R.string.install_game_content_failure_file_extension,
|
|
||||||
R.string.install_game_content_help_link
|
|
||||||
).show(supportFragmentManager, MessageDialogFragment.TAG)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
MessageDialogFragment.newInstance(
|
errorOther += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
val separator = System.getProperty("line.separator") ?: "\n"
|
||||||
|
val installResult = StringBuilder()
|
||||||
|
if (installSuccess > 0) {
|
||||||
|
installResult.append(
|
||||||
|
getString(
|
||||||
|
R.string.install_game_content_success_install,
|
||||||
|
installSuccess
|
||||||
|
)
|
||||||
|
)
|
||||||
|
installResult.append(separator)
|
||||||
|
}
|
||||||
|
if (installOverwrite > 0) {
|
||||||
|
installResult.append(
|
||||||
|
getString(
|
||||||
|
R.string.install_game_content_success_overwrite,
|
||||||
|
installOverwrite
|
||||||
|
)
|
||||||
|
)
|
||||||
|
installResult.append(separator)
|
||||||
|
}
|
||||||
|
errorTotal = errorBaseGame + errorExtension + errorOther
|
||||||
|
if (errorTotal > 0) {
|
||||||
|
installResult.append(separator)
|
||||||
|
installResult.append(
|
||||||
|
getString(
|
||||||
|
R.string.install_game_content_failed_count,
|
||||||
|
|
||||||
|
)
|
||||||
|
)
|
||||||
|
installResult.append(separator)
|
||||||
|
if (errorBaseGame > 0) {
|
||||||
|
installResult.append(separator)
|
||||||
|
installResult.append(
|
||||||
|
getString(R.string.install_game_content_failure_base)
|
||||||
|
)
|
||||||
|
installResult.append(separator)
|
||||||
|
}
|
||||||
|
if (errorExtension > 0) {
|
||||||
|
installResult.append(separator)
|
||||||
|
installResult.append(
|
||||||
|
getString(R.string.install_game_content_failure_file_extension)
|
||||||
|
)
|
||||||
|
installResult.append(separator)
|
||||||
|
}
|
||||||
|
if (errorOther > 0) {
|
||||||
|
installResult.append(
|
||||||
|
getString(R.string.install_game_content_failure_description)
|
||||||
|
)
|
||||||
|
installResult.append(separator)
|
||||||
|
}
|
||||||
|
InstallDialogFragment.newInstance(
|
||||||
R.string.install_game_content_failure,
|
R.string.install_game_content_failure,
|
||||||
R.string.install_game_content_failure_description,
|
installResult.toString().trim(),
|
||||||
R.string.install_game_content_help_link
|
R.string.install_game_content_help_link
|
||||||
).show(supportFragmentManager, MessageDialogFragment.TAG)
|
).show(supportFragmentManager, MessageDialogFragment.TAG)
|
||||||
|
} else {
|
||||||
|
InstallDialogFragment.newInstance(
|
||||||
|
R.string.install_game_content_success,
|
||||||
|
installResult.toString().trim(),
|
||||||
|
).show(supportFragmentManager, MessageDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return@newInstance installSuccess + installOverwrite + errorTotal
|
||||||
return@newInstance result
|
|
||||||
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,12 +104,14 @@
|
||||||
<string name="share_log_missing">No log file found</string>
|
<string name="share_log_missing">No log file found</string>
|
||||||
<string name="install_game_content">Install game content</string>
|
<string name="install_game_content">Install game content</string>
|
||||||
<string name="install_game_content_description">Install game updates or DLC</string>
|
<string name="install_game_content_description">Install game updates or DLC</string>
|
||||||
<string name="install_game_content_failure">Error installing file to NAND</string>
|
<string name="install_game_content_failure">Error installing file(s) to NAND</string>
|
||||||
<string name="install_game_content_failure_description">Game content installation failed. Please ensure content is valid and that the prod.keys file is installed.</string>
|
<string name="install_game_content_failure_description">Please ensure content(s) are valid and that the prod.keys file is installed.</string>
|
||||||
<string name="install_game_content_failure_base">Installation of base games isn\'t permitted in order to avoid possible conflicts. Please select an update or DLC instead.</string>
|
<string name="install_game_content_failure_base">Installation of base games isn\'t permitted in order to avoid possible conflicts.</string>
|
||||||
<string name="install_game_content_failure_file_extension">The selected file type is not supported. Only NSP and XCI content is supported for this action. Please verify the game content is valid.</string>
|
<string name="install_game_content_failure_file_extension">Only NSP and XCI content is supported. Please verify the game content(s) are valid.</string>
|
||||||
<string name="install_game_content_success">Game content installed successfully</string>
|
<string name="install_game_content_failed_count">%1$d installation error(s)</string>
|
||||||
<string name="install_game_content_success_overwrite">Game content was overwritten successfully</string>
|
<string name="install_game_content_success">Game content(s) installed successfully</string>
|
||||||
|
<string name="install_game_content_success_install">%1$d installed successfully</string>
|
||||||
|
<string name="install_game_content_success_overwrite">%1$d overwritten successfully</string>
|
||||||
<string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
|
<string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string>
|
||||||
|
|
||||||
<!-- About screen strings -->
|
<!-- About screen strings -->
|
||||||
|
|
Reference in New Issue