@@ -0,0 +1,112 @@
// SPDX-License-Identifier: GPL-2.0-or-later

package org.dolphinemu.dolphinemu.features.sysupdate.ui

import android.app.Dialog
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.ViewModelProvider
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.dolphinemu.dolphinemu.R
import org.dolphinemu.dolphinemu.databinding.DialogProgressBinding
import org.dolphinemu.dolphinemu.databinding.DialogProgressTvBinding

class SystemUpdateProgressBarDialogFragment : DialogFragment() {
private lateinit var viewModel: SystemUpdateViewModel

private lateinit var binding: DialogProgressBinding
private lateinit var bindingTv: DialogProgressTvBinding

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
viewModel = ViewModelProvider(requireActivity())[SystemUpdateViewModel::class.java]

// We need to set the message to something here, otherwise the text will not appear when we set it later.
val progressDialogBuilder = MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.updating))
.setMessage("")
.setNegativeButton(getString(R.string.cancel), null)
.setCancelable(false)

// TODO: Remove dialog_progress_tv if we switch to an AppCompatActivity for leanback
if (activity is AppCompatActivity) {
binding = DialogProgressBinding.inflate(layoutInflater)
progressDialogBuilder.setView(binding.root)

viewModel.progressData.observe(
this
) { progress: Int ->
binding.updateProgress.progress = progress
}

viewModel.totalData.observe(this) { total: Int ->
if (total == 0) {
return@observe
}
binding.updateProgress.max = total
}
} else {
bindingTv = DialogProgressTvBinding.inflate(layoutInflater)
progressDialogBuilder.setView(bindingTv.root)

viewModel.progressData.observe(
this
) { progress: Int ->
bindingTv.updateProgress.progress = progress
}

viewModel.totalData.observe(this) { total: Int ->
if (total == 0) {
return@observe
}
bindingTv.updateProgress.max = total
}
}

val progressDialog = progressDialogBuilder.create()

viewModel.titleIdData.observe(this) { titleId: Long ->
progressDialog.setMessage(getString(R.string.updating_message, titleId))
}

viewModel.resultData.observe(this) { result: Int ->
if (result == -1) {
// This is the default value, ignore
return@observe
}

val progressBarFragment = SystemUpdateResultFragment()
progressBarFragment.show(parentFragmentManager, SystemUpdateResultFragment.TAG)

dismiss()
}

if (savedInstanceState == null) {
viewModel.startUpdate()
}
return progressDialog
}

// By default, the ProgressDialog will immediately dismiss itself upon a button being pressed.
// Setting the OnClickListener again after the dialog is shown overrides this behavior.
override fun onResume() {
super.onResume()
val alertDialog = dialog as AlertDialog
val negativeButton = alertDialog.getButton(Dialog.BUTTON_NEGATIVE)
negativeButton.setOnClickListener {
alertDialog.setTitle(getString(R.string.cancelling))
alertDialog.setMessage(getString(R.string.update_cancelling))
viewModel.setCanceled()

if (activity is AppCompatActivity)
binding.updateProgress.isIndeterminate = true
else
bindingTv.updateProgress.isIndeterminate = true
}
}

companion object {
const val TAG = "SystemUpdateProgressBarDialogFragment"
}
}

This file was deleted.

@@ -0,0 +1,67 @@
// SPDX-License-Identifier: GPL-2.0-or-later

package org.dolphinemu.dolphinemu.features.sysupdate.ui

import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.ViewModelProvider
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.dolphinemu.dolphinemu.R
import org.dolphinemu.dolphinemu.utils.WiiUtils

class SystemUpdateResultFragment : DialogFragment() {
private val resultKey = "result"

private var mResult = 0

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val viewModel = ViewModelProvider(requireActivity())[SystemUpdateViewModel::class.java]
if (savedInstanceState == null) {
mResult = viewModel.resultData.value!!.toInt()
viewModel.clear()
} else {
mResult = savedInstanceState.getInt(resultKey)
}
val message: String = when (mResult) {
WiiUtils.UPDATE_RESULT_SUCCESS -> getString(R.string.update_success)
WiiUtils.UPDATE_RESULT_ALREADY_UP_TO_DATE -> getString(R.string.already_up_to_date)
WiiUtils.UPDATE_RESULT_REGION_MISMATCH -> getString(R.string.region_mismatch)
WiiUtils.UPDATE_RESULT_MISSING_UPDATE_PARTITION -> getString(R.string.missing_update_partition)
WiiUtils.UPDATE_RESULT_DISC_READ_FAILED -> getString(R.string.disc_read_failed)
WiiUtils.UPDATE_RESULT_SERVER_FAILED -> getString(R.string.server_failed)
WiiUtils.UPDATE_RESULT_DOWNLOAD_FAILED -> getString(R.string.download_failed)
WiiUtils.UPDATE_RESULT_IMPORT_FAILED -> getString(R.string.import_failed)
WiiUtils.UPDATE_RESULT_CANCELLED -> getString(R.string.update_cancelled)
else -> throw IllegalStateException("Unexpected value: $mResult")
}
val title: String = when (mResult) {
WiiUtils.UPDATE_RESULT_SUCCESS,
WiiUtils.UPDATE_RESULT_ALREADY_UP_TO_DATE -> getString(R.string.update_success_title)
WiiUtils.UPDATE_RESULT_REGION_MISMATCH,
WiiUtils.UPDATE_RESULT_MISSING_UPDATE_PARTITION,
WiiUtils.UPDATE_RESULT_DISC_READ_FAILED,
WiiUtils.UPDATE_RESULT_SERVER_FAILED,
WiiUtils.UPDATE_RESULT_DOWNLOAD_FAILED,
WiiUtils.UPDATE_RESULT_IMPORT_FAILED -> getString(R.string.update_failed_title)
WiiUtils.UPDATE_RESULT_CANCELLED -> getString(R.string.update_cancelled_title)
else -> throw IllegalStateException("Unexpected value: $mResult")
}

return MaterialAlertDialogBuilder(requireContext())
.setTitle(title)
.setMessage(message)
.setPositiveButton(R.string.ok) { _: DialogInterface?, _: Int -> dismiss() }
.create()
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt(resultKey, mResult)
}

companion object {
const val TAG = "SystemUpdateResultFragment"
}
}

This file was deleted.

@@ -0,0 +1,83 @@
// SPDX-License-Identifier: GPL-2.0-or-later

package org.dolphinemu.dolphinemu.features.sysupdate.ui

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.dolphinemu.dolphinemu.utils.WiiUpdateCallback
import org.dolphinemu.dolphinemu.utils.WiiUtils

class SystemUpdateViewModel : ViewModel() {
val progressData = MutableLiveData<Int>()
val totalData = MutableLiveData<Int>()
val titleIdData = MutableLiveData<Long>()
val resultData = MutableLiveData<Int>()

private var isRunning = false
private var canceled = false
var region = -1
var discPath: String = ""

init {
clear()
}

fun setCanceled() {
canceled = true
}

fun startUpdate() {
if (isRunning) return
isRunning = true

viewModelScope.launch {
withContext(Dispatchers.IO) {
if (discPath.isNotEmpty()) {
startDiscUpdate(discPath)
} else {
val region: String = when (region) {
0 -> "EUR"
1 -> "JPN"
2 -> "KOR"
3 -> "USA"
else -> ""
}
startOnlineUpdate(region)
}
isRunning = false
}
}
}

private fun startOnlineUpdate(region: String) {
canceled = false
val result = WiiUtils.doOnlineUpdate(region, constructCallback())
resultData.postValue(result)
}

private fun startDiscUpdate(path: String) {
canceled = false
val result = WiiUtils.doDiscUpdate(path, constructCallback())
resultData.postValue(result)
}

fun clear() {
progressData.value = 0
totalData.value = 0
titleIdData.value = 0L
resultData.value = -1
}

private fun constructCallback(): WiiUpdateCallback {
return WiiUpdateCallback { processed: Int, total: Int, titleId: Long ->
progressData.postValue(processed)
totalData.postValue(total)
titleIdData.postValue(titleId)
!canceled
}
}
}
@@ -319,7 +319,7 @@ private static void launchUpdateProgressBarFragment(FragmentActivity activity)
SystemUpdateProgressBarDialogFragment progressBarFragment =
new SystemUpdateProgressBarDialogFragment();
progressBarFragment
.show(activity.getSupportFragmentManager(), "SystemUpdateProgressBarDialogFragment");
.show(activity.getSupportFragmentManager(), SystemUpdateProgressBarDialogFragment.TAG);
progressBarFragment.setCancelable(false);
}

This file was deleted.

@@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-2.0-or-later

package org.dolphinemu.dolphinemu.utils

object WiiUtils {
const val RESULT_SUCCESS = 0
const val RESULT_ERROR = 1
const val RESULT_CANCELLED = 2
const val RESULT_CORRUPTED_SOURCE = 3
const val RESULT_TITLE_MISSING = 4
const val UPDATE_RESULT_SUCCESS = 0
const val UPDATE_RESULT_ALREADY_UP_TO_DATE = 1
const val UPDATE_RESULT_REGION_MISMATCH = 2
const val UPDATE_RESULT_MISSING_UPDATE_PARTITION = 3
const val UPDATE_RESULT_DISC_READ_FAILED = 4
const val UPDATE_RESULT_SERVER_FAILED = 5
const val UPDATE_RESULT_DOWNLOAD_FAILED = 6
const val UPDATE_RESULT_IMPORT_FAILED = 7
const val UPDATE_RESULT_CANCELLED = 8

@JvmStatic
external fun installWAD(file: String): Boolean

@JvmStatic
external fun importWiiSave(file: String, canOverwrite: BooleanSupplier): Int

@JvmStatic
external fun importNANDBin(file: String)
external fun doOnlineUpdate(region: String, callback: WiiUpdateCallback): Int
external fun doDiscUpdate(path: String, callback: WiiUpdateCallback): Int

@JvmStatic
external fun isSystemMenuInstalled(): Boolean

@JvmStatic
external fun isSystemMenuvWii(): Boolean

@JvmStatic
external fun getSystemMenuVersion(): String

@JvmStatic
external fun syncSdFolderToSdImage(): Boolean

@JvmStatic
external fun syncSdImageToSdFolder(): Boolean
}