android: Add support for concurrent installs

This commit is contained in:
Abandoned Cart 2023-06-15 22:36:03 -04:00
parent eea2145698
commit 6c7e284f64
3 changed files with 154 additions and 40 deletions

View file

@ -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
}
}
}

View file

@ -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)
} }
}
} }

View file

@ -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 -->