From 7c86baee50371da1b9aef84dbfb33ae5b097b311 Mon Sep 17 00:00:00 2001 From: OatmealDome Date: Sun, 9 Jan 2022 15:42:26 -0500 Subject: [PATCH 1/7] WiiUpdateCallback: Add interface for update callback --- .../dolphinemu/utils/WiiUpdateCallback.java | 11 ++++++++++ Source/Android/jni/AndroidCommon/IDCache.cpp | 20 +++++++++++++++++++ Source/Android/jni/AndroidCommon/IDCache.h | 3 +++ 3 files changed, 34 insertions(+) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUpdateCallback.java diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUpdateCallback.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUpdateCallback.java new file mode 100644 index 0000000000..cd7096974e --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUpdateCallback.java @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.utils; + +import androidx.annotation.Keep; + +public interface WiiUpdateCallback +{ + @Keep + boolean run(int processed, int total, long titleId); +} diff --git a/Source/Android/jni/AndroidCommon/IDCache.cpp b/Source/Android/jni/AndroidCommon/IDCache.cpp index a5a52a4f36..b125babb81 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.cpp +++ b/Source/Android/jni/AndroidCommon/IDCache.cpp @@ -73,6 +73,9 @@ static jmethodID s_patch_cheat_constructor; static jclass s_riivolution_patches_class; static jfieldID s_riivolution_patches_pointer; +static jclass s_wii_update_cb_class; +static jmethodID s_wii_update_cb_run; + namespace IDCache { JNIEnv* GetEnvForThread() @@ -338,6 +341,16 @@ jfieldID GetRiivolutionPatchesPointer() return s_riivolution_patches_pointer; } +jclass GetWiiUpdateCallbackClass() +{ + return s_wii_update_cb_class; +} + +jmethodID GetWiiUpdateCallbackFunction() +{ + return s_wii_update_cb_run; +} + } // namespace IDCache extern "C" { @@ -474,6 +487,12 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) s_riivolution_patches_pointer = env->GetFieldID(riivolution_patches_class, "mPointer", "J"); env->DeleteLocalRef(riivolution_patches_class); + const jclass wii_update_cb_class = + env->FindClass("org/dolphinemu/dolphinemu/utils/WiiUpdateCallback"); + s_wii_update_cb_class = reinterpret_cast(env->NewGlobalRef(wii_update_cb_class)); + s_wii_update_cb_run = env->GetMethodID(s_wii_update_cb_class, "run", "(IIJ)Z"); + env->DeleteLocalRef(wii_update_cb_class); + return JNI_VERSION; } @@ -498,5 +517,6 @@ JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) env->DeleteGlobalRef(s_gecko_cheat_class); env->DeleteGlobalRef(s_patch_cheat_class); env->DeleteGlobalRef(s_riivolution_patches_class); + env->DeleteGlobalRef(s_wii_update_cb_class); } } diff --git a/Source/Android/jni/AndroidCommon/IDCache.h b/Source/Android/jni/AndroidCommon/IDCache.h index 8dc93d60ba..83eaa85894 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.h +++ b/Source/Android/jni/AndroidCommon/IDCache.h @@ -72,4 +72,7 @@ jmethodID GetPatchCheatConstructor(); jclass GetRiivolutionPatchesClass(); jfieldID GetRiivolutionPatchesPointer(); +jclass GetWiiUpdateCallbackClass(); +jmethodID GetWiiUpdateCallbackFunction(); + } // namespace IDCache From 19e1809cdfbd8c58d8506f4f87462641ac20f798 Mon Sep 17 00:00:00 2001 From: OatmealDome Date: Sun, 9 Jan 2022 15:50:23 -0500 Subject: [PATCH 2/7] WiiUtils: Add function to convert UpdateResult to jint --- .../dolphinemu/dolphinemu/utils/WiiUtils.java | 10 +++++++ Source/Android/jni/WiiUtils.cpp | 30 +++++++++++++++++++ Source/Core/Core/WiiUtils.h | 2 ++ 3 files changed, 42 insertions(+) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUtils.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUtils.java index cd92e21cb5..560a5d9727 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUtils.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUtils.java @@ -10,6 +10,16 @@ public final class WiiUtils public static final int RESULT_CORRUPTED_SOURCE = 3; public static final int RESULT_TITLE_MISSING = 4; + public static final int UPDATE_RESULT_SUCCESS = 0; + public static final int UPDATE_RESULT_ALREADY_UP_TO_DATE = 1; + public static final int UPDATE_RESULT_REGION_MISMATCH = 2; + public static final int UPDATE_RESULT_MISSING_UPDATE_PARTITION = 3; + public static final int UPDATE_RESULT_DISC_READ_FAILED = 4; + public static final int UPDATE_RESULT_SERVER_FAILED = 5; + public static final int UPDATE_RESULT_DOWNLOAD_FAILED = 6; + public static final int UPDATE_RESULT_IMPORT_FAILED = 7; + public static final int UPDATE_RESULT_CANCELLED = 8; + public static native boolean installWAD(String file); public static native int importWiiSave(String file, BooleanSupplier canOverwrite); diff --git a/Source/Android/jni/WiiUtils.cpp b/Source/Android/jni/WiiUtils.cpp index f37ba0426b..f372f65229 100644 --- a/Source/Android/jni/WiiUtils.cpp +++ b/Source/Android/jni/WiiUtils.cpp @@ -35,6 +35,36 @@ static jint ConvertCopyResult(WiiSave::CopyResult result) static_assert(static_cast(WiiSave::CopyResult::NumberOfEntries) == 5); } +static jint ConvertUpdateResult(WiiUtils::UpdateResult result) +{ + switch (result) + { + case WiiUtils::UpdateResult::Succeeded: + return 0; + case WiiUtils::UpdateResult::AlreadyUpToDate: + return 1; + case WiiUtils::UpdateResult::RegionMismatch: + return 2; + case WiiUtils::UpdateResult::MissingUpdatePartition: + return 3; + case WiiUtils::UpdateResult::DiscReadFailed: + return 4; + case WiiUtils::UpdateResult::ServerFailed: + return 5; + case WiiUtils::UpdateResult::DownloadFailed: + return 6; + case WiiUtils::UpdateResult::ImportFailed: + return 7; + case WiiUtils::UpdateResult::Cancelled: + return 8; + default: + ASSERT(false); + return 1; + } + + static_assert(static_cast(WiiUtils::UpdateResult::NumberOfEntries) == 9); +} + extern "C" { JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_utils_WiiUtils_installWAD(JNIEnv* env, diff --git a/Source/Core/Core/WiiUtils.h b/Source/Core/Core/WiiUtils.h index 4b7df0912b..55a4c4e286 100644 --- a/Source/Core/Core/WiiUtils.h +++ b/Source/Core/Core/WiiUtils.h @@ -82,6 +82,8 @@ enum class UpdateResult ImportFailed, // Update was cancelled. Cancelled, + + NumberOfEntries, }; // Return false to cancel the update as soon as the current title has finished updating. From 2fc7671eaf9089d2b5d0a586e98120d786159e9a Mon Sep 17 00:00:00 2001 From: OatmealDome Date: Sun, 9 Jan 2022 15:51:07 -0500 Subject: [PATCH 3/7] WiiUtils: Add doOnlineUpdate() function --- .../dolphinemu/dolphinemu/utils/WiiUtils.java | 2 ++ Source/Android/jni/WiiUtils.cpp | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUtils.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUtils.java index 560a5d9727..2366eba33d 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUtils.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUtils.java @@ -25,4 +25,6 @@ public final class WiiUtils public static native int importWiiSave(String file, BooleanSupplier canOverwrite); public static native void importNANDBin(String file); + + public static native int doOnlineUpdate(String region, WiiUpdateCallback callback); } diff --git a/Source/Android/jni/WiiUtils.cpp b/Source/Android/jni/WiiUtils.cpp index f372f65229..8745d98bdd 100644 --- a/Source/Android/jni/WiiUtils.cpp +++ b/Source/Android/jni/WiiUtils.cpp @@ -8,6 +8,7 @@ #include "jni/AndroidCommon/AndroidCommon.h" #include "jni/AndroidCommon/IDCache.h" +#include "Common/ScopeGuard.h" #include "Core/HW/WiiSave.h" #include "Core/WiiUtils.h" #include "DiscIO/NANDImporter.h" @@ -108,4 +109,23 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_utils_WiiUtils_importNANDB return ""; }); } + +JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_utils_WiiUtils_doOnlineUpdate( + JNIEnv* env, jclass, jstring jRegion, jobject jCallback) +{ + const std::string region = GetJString(env, jRegion); + + jobject jCallbackGlobal = env->NewGlobalRef(jCallback); + Common::ScopeGuard scope_guard([jCallbackGlobal, env] { env->DeleteGlobalRef(jCallbackGlobal); }); + + const auto callback = [&jCallbackGlobal](int processed, int total, u64 title_id) { + JNIEnv* env = IDCache::GetEnvForThread(); + return static_cast(env->CallBooleanMethod( + jCallbackGlobal, IDCache::GetWiiUpdateCallbackFunction(), processed, total, title_id)); + }; + + WiiUtils::UpdateResult result = WiiUtils::DoOnlineUpdate(callback, region); + + return ConvertUpdateResult(result); +} } From 36257f7f42e7dace0814d47ad3cd631f4082942d Mon Sep 17 00:00:00 2001 From: OatmealDome Date: Sun, 9 Jan 2022 15:57:07 -0500 Subject: [PATCH 4/7] WiiUtils: Add function to check if system menu is installed --- .../org/dolphinemu/dolphinemu/utils/WiiUtils.java | 2 ++ Source/Android/jni/WiiUtils.cpp | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUtils.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUtils.java index 2366eba33d..6b933a7879 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUtils.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUtils.java @@ -27,4 +27,6 @@ public final class WiiUtils public static native void importNANDBin(String file); public static native int doOnlineUpdate(String region, WiiUpdateCallback callback); + + public static native boolean isSystemMenuInstalled(); } diff --git a/Source/Android/jni/WiiUtils.cpp b/Source/Android/jni/WiiUtils.cpp index 8745d98bdd..2e51073659 100644 --- a/Source/Android/jni/WiiUtils.cpp +++ b/Source/Android/jni/WiiUtils.cpp @@ -9,7 +9,10 @@ #include "jni/AndroidCommon/IDCache.h" #include "Common/ScopeGuard.h" +#include "Core/CommonTitles.h" #include "Core/HW/WiiSave.h" +#include "Core/IOS/ES/ES.h" +#include "Core/IOS/IOS.h" #include "Core/WiiUtils.h" #include "DiscIO/NANDImporter.h" @@ -128,4 +131,13 @@ JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_utils_WiiUtils_doOnlineUpd return ConvertUpdateResult(result); } + +JNIEXPORT jboolean JNICALL +Java_org_dolphinemu_dolphinemu_utils_WiiUtils_isSystemMenuInstalled(JNIEnv* env, jclass) +{ + IOS::HLE::Kernel ios; + const auto tmd = ios.GetES()->FindInstalledTMD(Titles::SYSTEM_MENU); + + return tmd.IsValid(); +} } From 30d51348f977278eb289890f20e47ac1f25c3b80 Mon Sep 17 00:00:00 2001 From: OatmealDome Date: Sun, 9 Jan 2022 15:58:45 -0500 Subject: [PATCH 5/7] WiiUtils: Add function to get current system menu version --- .../org/dolphinemu/dolphinemu/utils/WiiUtils.java | 2 ++ Source/Android/jni/WiiUtils.cpp | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUtils.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUtils.java index 6b933a7879..42e11a67c0 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUtils.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiiUtils.java @@ -29,4 +29,6 @@ public final class WiiUtils public static native int doOnlineUpdate(String region, WiiUpdateCallback callback); public static native boolean isSystemMenuInstalled(); + + public static native String getSystemMenuVersion(); } diff --git a/Source/Android/jni/WiiUtils.cpp b/Source/Android/jni/WiiUtils.cpp index 2e51073659..42332feea2 100644 --- a/Source/Android/jni/WiiUtils.cpp +++ b/Source/Android/jni/WiiUtils.cpp @@ -140,4 +140,18 @@ Java_org_dolphinemu_dolphinemu_utils_WiiUtils_isSystemMenuInstalled(JNIEnv* env, return tmd.IsValid(); } + +JNIEXPORT jstring JNICALL +Java_org_dolphinemu_dolphinemu_utils_WiiUtils_getSystemMenuVersion(JNIEnv* env, jclass) +{ + IOS::HLE::Kernel ios; + const auto tmd = ios.GetES()->FindInstalledTMD(Titles::SYSTEM_MENU); + + if (!tmd.IsValid()) + { + return ToJString(env, ""); + } + + return ToJString(env, DiscIO::GetSysMenuVersionString(tmd.GetTitleVersion())); +} } From 8ad1292df740eb0fcb902f50557d2ba50582e21c Mon Sep 17 00:00:00 2001 From: OatmealDome Date: Sun, 9 Jan 2022 17:47:30 -0500 Subject: [PATCH 6/7] NativeLibrary: Add function to start system menu --- .../dolphinemu/dolphinemu/NativeLibrary.java | 5 ++++ Source/Android/jni/MainAndroid.cpp | 25 +++++++++++++------ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java index 974832878e..dd2c4e8407 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java @@ -387,6 +387,11 @@ public final class NativeLibrary public static native void Run(String[] path, boolean riivolution, String savestatePath, boolean deleteSavestate); + /** + * Begins emulation of the System Menu. + */ + public static native void RunSystemMenu(); + public static native void ChangeDisc(String path); // Surface Handling diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index 326995bd39..46b2c17680 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -32,6 +32,7 @@ #include "Core/Boot/Boot.h" #include "Core/BootManager.h" +#include "Core/CommonTitles.h" #include "Core/ConfigLoaders/GameConfigLoader.h" #include "Core/ConfigManager.h" #include "Core/Core.h" @@ -547,21 +548,14 @@ static float GetRenderSurfaceScale(JNIEnv* env) return env->CallStaticFloatMethod(native_library_class, get_render_surface_scale_method); } -static void Run(JNIEnv* env, const std::vector& paths, bool riivolution, - BootSessionData boot_session_data = BootSessionData()) +static void Run(JNIEnv* env, std::unique_ptr&& boot, bool riivolution) { - ASSERT(!paths.empty()); - __android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Running : %s", paths[0].c_str()); - std::unique_lock host_identity_guard(s_host_identity_lock); WiimoteReal::InitAdapterClass(); s_have_wm_user_stop = false; - std::unique_ptr boot = - BootParameters::GenerateFromFile(paths, std::move(boot_session_data)); - if (riivolution && std::holds_alternative(boot->parameters)) { const std::string& riivolution_dir = File::GetUserPath(D_RIIVOLUTION_IDX); @@ -624,6 +618,15 @@ static void Run(JNIEnv* env, const std::vector& paths, bool riivolu IDCache::GetFinishEmulationActivity()); } +static void Run(JNIEnv* env, const std::vector& paths, bool riivolution, + BootSessionData boot_session_data = BootSessionData()) +{ + ASSERT(!paths.empty()); + __android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Running : %s", paths[0].c_str()); + + Run(env, BootParameters::GenerateFromFile(paths, std::move(boot_session_data)), riivolution); +} + JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run___3Ljava_lang_String_2Z( JNIEnv* env, jclass, jobjectArray jPaths, jboolean jRiivolution) { @@ -641,6 +644,12 @@ Java_org_dolphinemu_dolphinemu_NativeLibrary_Run___3Ljava_lang_String_2ZLjava_la BootSessionData(GetJString(env, jSavestate), delete_state)); } +JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_RunSystemMenu(JNIEnv* env, + jclass) +{ + Run(env, std::make_unique(BootParameters::NANDTitle{Titles::SYSTEM_MENU}), false); +} + JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_ChangeDisc(JNIEnv* env, jclass, jstring jFile) { From 55378cab393547538275c56bae718a37a3f69737 Mon Sep 17 00:00:00 2001 From: Simonx22 Date: Sun, 9 Jan 2022 14:29:46 -0500 Subject: [PATCH 7/7] Android: Add online system update functionality --- .../Android/app/src/main/AndroidManifest.xml | 1 - .../activities/EmulationActivity.java | 48 ++++++-- ...OnlineUpdateProgressBarDialogFragment.java | 110 ++++++++++++++++++ ...nlineUpdateRegionSelectDialogFragment.java | 42 +++++++ .../ui/OnlineUpdateResultFragment.java | 105 +++++++++++++++++ .../SystemMenuNotInstalledDialogFragment.java | 34 ++++++ .../sysupdate/ui/SystemUpdateViewModel.java | 90 ++++++++++++++ .../fragments/EmulationFragment.java | 13 ++- .../dolphinemu/ui/main/MainActivity.java | 13 +++ .../dolphinemu/ui/main/MainPresenter.java | 60 +++++++++- .../dolphinemu/ui/main/TvMainActivity.java | 8 ++ .../app/src/main/res/menu/menu_game_grid.xml | 11 ++ .../app/src/main/res/values/strings.xml | 28 +++++ 13 files changed, 548 insertions(+), 15 deletions(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/sysupdate/ui/OnlineUpdateProgressBarDialogFragment.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/sysupdate/ui/OnlineUpdateRegionSelectDialogFragment.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/sysupdate/ui/OnlineUpdateResultFragment.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/sysupdate/ui/SystemMenuNotInstalledDialogFragment.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/sysupdate/ui/SystemUpdateViewModel.java diff --git a/Source/Android/app/src/main/AndroidManifest.xml b/Source/Android/app/src/main/AndroidManifest.xml index 1fded39aba..284a00684c 100644 --- a/Source/Android/app/src/main/AndroidManifest.xml +++ b/Source/Android/app/src/main/AndroidManifest.xml @@ -155,4 +155,3 @@ - diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java index 815b5b7396..3b7838a814 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java @@ -9,7 +9,6 @@ import android.content.SharedPreferences; import android.graphics.Rect; import android.os.Bundle; import android.preference.PreferenceManager; -import android.text.TextUtils; import android.util.SparseIntArray; import android.view.InputDevice; import android.view.KeyEvent; @@ -80,12 +79,14 @@ public final class EmulationActivity extends AppCompatActivity private boolean activityRecreated; private String[] mPaths; private boolean mRiivolution; + private boolean mLaunchSystemMenu; private boolean mIgnoreWarnings; private static boolean sUserPausedEmulation; private boolean mMenuToastShown; public static final String EXTRA_SELECTED_GAMES = "SelectedGames"; public static final String EXTRA_RIIVOLUTION = "Riivolution"; + public static final String EXTRA_SYSTEM_MENU = "SystemMenu"; public static final String EXTRA_IGNORE_WARNINGS = "IgnoreWarnings"; public static final String EXTRA_USER_PAUSED_EMULATION = "sUserPausedEmulation"; public static final String EXTRA_MENU_TOAST_SHOWN = "MenuToastShown"; @@ -171,11 +172,9 @@ public final class EmulationActivity extends AppCompatActivity launch(activity, new String[]{filePath}, riivolution); } - public static void launch(FragmentActivity activity, String[] filePaths, boolean riivolution) + private static void performLaunchChecks(FragmentActivity activity, + Runnable continueCallback) { - if (sIgnoreLaunchRequests) - return; - new AfterDirectoryInitializationRunner().runWithLifecycle(activity, true, () -> { if (FileBrowserHelper.isPathEmptyOrValid(StringSetting.MAIN_DEFAULT_ISO) && @@ -185,7 +184,7 @@ public final class EmulationActivity extends AppCompatActivity FileBrowserHelper.isPathEmptyOrValid(StringSetting.MAIN_RESOURCEPACK_PATH) && FileBrowserHelper.isPathEmptyOrValid(StringSetting.MAIN_SD_PATH)) { - launchWithoutChecks(activity, filePaths, riivolution); + continueCallback.run(); } else { @@ -194,12 +193,44 @@ public final class EmulationActivity extends AppCompatActivity builder.setPositiveButton(R.string.yes, (dialogInterface, i) -> SettingsActivity.launch(activity, MenuTag.CONFIG_PATHS)); builder.setNeutralButton(R.string.continue_anyway, (dialogInterface, i) -> - launchWithoutChecks(activity, filePaths, riivolution)); + continueCallback.run()); builder.show(); } }); } + + public static void launchSystemMenu(FragmentActivity activity) + { + if (sIgnoreLaunchRequests) + return; + + performLaunchChecks(activity, () -> + { + launchSystemMenuWithoutChecks(activity); + }); + } + + public static void launch(FragmentActivity activity, String[] filePaths, boolean riivolution) + { + if (sIgnoreLaunchRequests) + return; + + performLaunchChecks(activity, () -> + { + launchWithoutChecks(activity, filePaths, riivolution); + }); + } + + private static void launchSystemMenuWithoutChecks(FragmentActivity activity) + { + sIgnoreLaunchRequests = true; + + Intent launcher = new Intent(activity, EmulationActivity.class); + launcher.putExtra(EmulationActivity.EXTRA_SYSTEM_MENU, true); + activity.startActivity(launcher); + } + private static void launchWithoutChecks(FragmentActivity activity, String[] filePaths, boolean riivolution) { @@ -256,6 +287,7 @@ public final class EmulationActivity extends AppCompatActivity Intent gameToEmulate = getIntent(); mPaths = gameToEmulate.getStringArrayExtra(EXTRA_SELECTED_GAMES); mRiivolution = gameToEmulate.getBooleanExtra(EXTRA_RIIVOLUTION, false); + mLaunchSystemMenu = gameToEmulate.getBooleanExtra(EXTRA_SYSTEM_MENU, false); mIgnoreWarnings = gameToEmulate.getBooleanExtra(EXTRA_IGNORE_WARNINGS, false); sUserPausedEmulation = gameToEmulate.getBooleanExtra(EXTRA_USER_PAUSED_EMULATION, false); mMenuToastShown = false; @@ -288,7 +320,7 @@ public final class EmulationActivity extends AppCompatActivity .findFragmentById(R.id.frame_emulation_fragment); if (mEmulationFragment == null) { - mEmulationFragment = EmulationFragment.newInstance(mPaths, mRiivolution); + mEmulationFragment = EmulationFragment.newInstance(mPaths, mRiivolution, mLaunchSystemMenu); getSupportFragmentManager().beginTransaction() .add(R.id.frame_emulation_fragment, mEmulationFragment) .commit(); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/sysupdate/ui/OnlineUpdateProgressBarDialogFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/sysupdate/ui/OnlineUpdateProgressBarDialogFragment.java new file mode 100644 index 0000000000..7130b09f00 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/sysupdate/ui/OnlineUpdateProgressBarDialogFragment.java @@ -0,0 +1,110 @@ +package org.dolphinemu.dolphinemu.features.sysupdate.ui; + +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.pm.ActivityInfo; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.DialogFragment; +import androidx.lifecycle.ViewModelProvider; + +import org.dolphinemu.dolphinemu.R; + +public class OnlineUpdateProgressBarDialogFragment extends DialogFragment +{ + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) + { + // Store the current orientation to be restored later + final int orientation = getActivity().getRequestedOrientation(); + // Rotating the device while the update is running can result in a title failing to import. + getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED); + + SystemUpdateViewModel viewModel = + new ViewModelProvider(requireActivity()).get(SystemUpdateViewModel.class); + + ProgressDialog progressDialog = new ProgressDialog(requireContext()); + progressDialog.setTitle(getString(R.string.updating)); + // We need to set the message to something here, otherwise the text will not appear when we set it later. + progressDialog.setMessage(""); + progressDialog.setButton(Dialog.BUTTON_NEGATIVE, getString(R.string.cancel), (dialog, i) -> + { + }); + progressDialog.setOnShowListener((dialogInterface) -> + { + // 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. + progressDialog.getButton(Dialog.BUTTON_NEGATIVE).setOnClickListener((view) -> + { + viewModel.setCanceled(); + }); + }); + progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); + + viewModel.getProgressData().observe(this, (@Nullable Integer progress) -> + { + progressDialog.setProgress(progress.intValue()); + }); + + viewModel.getTotalData().observe(this, (@Nullable Integer total) -> + { + if (total == 0) + { + return; + } + + progressDialog.setMax(total.intValue()); + }); + + viewModel.getTitleIdData().observe(this, (@Nullable Long titleId) -> + { + progressDialog.setMessage(getString(R.string.updating_message, titleId)); + }); + + viewModel.getResultData().observe(this, (@Nullable Integer result) -> + { + if (result == -1) + { + // This is the default value, ignore + return; + } + + OnlineUpdateResultFragment progressBarFragment = new OnlineUpdateResultFragment(); + progressBarFragment.show(getParentFragmentManager(), "OnlineUpdateResultFragment"); + + getActivity().setRequestedOrientation(orientation); + + dismiss(); + }); + + if (savedInstanceState == null) + { + final String region; + int selectedItem = viewModel.getRegion(); + + switch (selectedItem) + { + case 0: + region = "EUR"; + break; + case 1: + region = "JPN"; + break; + case 2: + region = "KOR"; + break; + case 3: + region = "USA"; + break; + default: + region = ""; + break; + } + viewModel.startUpdate(region); + } + return progressDialog; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/sysupdate/ui/OnlineUpdateRegionSelectDialogFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/sysupdate/ui/OnlineUpdateRegionSelectDialogFragment.java new file mode 100644 index 0000000000..198008d288 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/sysupdate/ui/OnlineUpdateRegionSelectDialogFragment.java @@ -0,0 +1,42 @@ +package org.dolphinemu.dolphinemu.features.sysupdate.ui; + +import android.app.Dialog; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.DialogFragment; +import androidx.lifecycle.ViewModelProvider; + +import org.dolphinemu.dolphinemu.R; + +public class OnlineUpdateRegionSelectDialogFragment extends DialogFragment +{ + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) + { + String[] items = {getString(R.string.europe), getString( + R.string.japan), getString(R.string.korea), getString(R.string.united_states)}; + int checkedItem = -1; + + return new AlertDialog.Builder(requireContext(), R.style.DolphinDialogBase) + .setTitle(R.string.region_select_title) + .setSingleChoiceItems(items, checkedItem, (dialog, which) -> + { + SystemUpdateViewModel viewModel = + new ViewModelProvider(requireActivity()).get(SystemUpdateViewModel.class); + viewModel.setRegion(which); + + OnlineUpdateProgressBarDialogFragment progressBarFragment = + new OnlineUpdateProgressBarDialogFragment(); + progressBarFragment + .show(getParentFragmentManager(), "OnlineUpdateProgressBarDialogFragment"); + progressBarFragment.setCancelable(false); + + dismiss(); + }) + .create(); + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/sysupdate/ui/OnlineUpdateResultFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/sysupdate/ui/OnlineUpdateResultFragment.java new file mode 100644 index 0000000000..e1f7557944 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/sysupdate/ui/OnlineUpdateResultFragment.java @@ -0,0 +1,105 @@ +package org.dolphinemu.dolphinemu.features.sysupdate.ui; + +import android.app.Dialog; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.DialogFragment; +import androidx.lifecycle.ViewModelProvider; + +import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.utils.WiiUtils; + +public class OnlineUpdateResultFragment extends DialogFragment +{ + private int mResult; + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) + { + SystemUpdateViewModel viewModel = + new ViewModelProvider(requireActivity()).get(SystemUpdateViewModel.class); + if (savedInstanceState == null) + { + mResult = viewModel.getResultData().getValue().intValue(); + viewModel.clear(); + } + else + { + mResult = savedInstanceState.getInt("result"); + } + + String message; + switch (mResult) + { + case WiiUtils.UPDATE_RESULT_SUCCESS: + message = getString(R.string.update_success); + break; + case WiiUtils.UPDATE_RESULT_ALREADY_UP_TO_DATE: + message = getString(R.string.already_up_to_date); + break; + case WiiUtils.UPDATE_RESULT_REGION_MISMATCH: + message = getString(R.string.region_mismatch); + break; + case WiiUtils.UPDATE_RESULT_MISSING_UPDATE_PARTITION: + message = getString(R.string.missing_update_partition); + break; + case WiiUtils.UPDATE_RESULT_DISC_READ_FAILED: + message = getString(R.string.disc_read_failed); + break; + case WiiUtils.UPDATE_RESULT_SERVER_FAILED: + message = getString(R.string.server_failed); + break; + case WiiUtils.UPDATE_RESULT_DOWNLOAD_FAILED: + message = getString(R.string.download_failed); + break; + case WiiUtils.UPDATE_RESULT_IMPORT_FAILED: + message = getString(R.string.import_failed); + break; + case WiiUtils.UPDATE_RESULT_CANCELLED: + message = getString(R.string.update_cancelled); + break; + default: + throw new IllegalStateException("Unexpected value: " + mResult); + } + + String title; + switch (mResult) + { + case WiiUtils.UPDATE_RESULT_SUCCESS: + case WiiUtils.UPDATE_RESULT_ALREADY_UP_TO_DATE: + title = getString(R.string.update_success_title); + break; + case WiiUtils.UPDATE_RESULT_REGION_MISMATCH: + case WiiUtils.UPDATE_RESULT_MISSING_UPDATE_PARTITION: + case WiiUtils.UPDATE_RESULT_DISC_READ_FAILED: + case WiiUtils.UPDATE_RESULT_SERVER_FAILED: + case WiiUtils.UPDATE_RESULT_DOWNLOAD_FAILED: + case WiiUtils.UPDATE_RESULT_IMPORT_FAILED: + title = getString(R.string.update_failed_title); + break; + case WiiUtils.UPDATE_RESULT_CANCELLED: + title = getString(R.string.update_cancelled_title); + break; + default: + throw new IllegalStateException("Unexpected value: " + mResult); + } + + return new AlertDialog.Builder(requireContext(), R.style.DolphinDialogBase) + .setTitle(title) + .setMessage(message) + .setPositiveButton(R.string.ok, (dialog, which) -> + { + dismiss(); + }) + .create(); + } + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) + { + super.onSaveInstanceState(outState); + outState.putInt("result", mResult); + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/sysupdate/ui/SystemMenuNotInstalledDialogFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/sysupdate/ui/SystemMenuNotInstalledDialogFragment.java new file mode 100644 index 0000000000..b70e7682b7 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/sysupdate/ui/SystemMenuNotInstalledDialogFragment.java @@ -0,0 +1,34 @@ +package org.dolphinemu.dolphinemu.features.sysupdate.ui; + +import android.app.Dialog; +import android.os.Bundle; + +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.FragmentManager; + +import org.dolphinemu.dolphinemu.R; + +public class SystemMenuNotInstalledDialogFragment extends DialogFragment +{ + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) + { + return new AlertDialog.Builder(requireContext(), R.style.DolphinDialogBase) + .setTitle(R.string.system_menu_not_installed_title) + .setMessage(R.string.system_menu_not_installed_message) + .setPositiveButton(R.string.yes, (dialog, which) -> + { + FragmentManager fragmentManager = getParentFragmentManager(); + OnlineUpdateRegionSelectDialogFragment dialogFragment = + new OnlineUpdateRegionSelectDialogFragment(); + dialogFragment.show(fragmentManager, "OnlineUpdateRegionSelectDialogFragment"); + dismiss(); + }) + .setNegativeButton(R.string.no, (dialog, which) -> + { + dismiss(); + }) + .create(); + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/sysupdate/ui/SystemUpdateViewModel.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/sysupdate/ui/SystemUpdateViewModel.java new file mode 100644 index 0000000000..ced570f8c3 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/sysupdate/ui/SystemUpdateViewModel.java @@ -0,0 +1,90 @@ +package org.dolphinemu.dolphinemu.features.sysupdate.ui; + +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +import org.dolphinemu.dolphinemu.utils.WiiUtils; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class SystemUpdateViewModel extends ViewModel +{ + private static final ExecutorService executor = Executors.newFixedThreadPool(1); + + private final MutableLiveData mProgressData = new MutableLiveData<>(); + private final MutableLiveData mTotalData = new MutableLiveData<>(); + private final MutableLiveData mTitleIdData = new MutableLiveData<>(); + private final MutableLiveData mResultData = new MutableLiveData<>(); + + private boolean mCanceled = false; + private int mRegion; + + public SystemUpdateViewModel() + { + clear(); + } + + public void setRegion(int region) + { + mRegion = region; + } + + public int getRegion() + { + return mRegion; + } + + public MutableLiveData getProgressData() + { + return mProgressData; + } + + public MutableLiveData getTotalData() + { + return mTotalData; + } + + public MutableLiveData getTitleIdData() + { + return mTitleIdData; + } + + public MutableLiveData getResultData() + { + return mResultData; + } + + public void setCanceled() + { + mCanceled = true; + } + + public void startUpdate(String region) + { + mCanceled = false; + + executor.execute(() -> + { + int result = WiiUtils.doOnlineUpdate(region, ((processed, total, titleId) -> + { + mProgressData.postValue(processed); + mTotalData.postValue(total); + mTitleIdData.postValue(titleId); + + return !mCanceled; + })); + + mResultData.postValue(result); + }); + } + + public void clear() + { + mProgressData.setValue(0); + mTotalData.setValue(0); + mTitleIdData.setValue(0l); + mResultData.setValue(-1); + mCanceled = false; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java index 7f1e158830..01d6ab6719 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java @@ -6,7 +6,6 @@ import android.content.Context; import android.graphics.Rect; import android.os.Bundle; import android.view.LayoutInflater; -import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; @@ -30,6 +29,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C { private static final String KEY_GAMEPATHS = "gamepaths"; private static final String KEY_RIIVOLUTION = "riivolution"; + private static final String KEY_SYSTEM_MENU = "systemMenu"; private InputOverlay mInputOverlay; @@ -37,14 +37,17 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C private boolean mRiivolution; private boolean mRunWhenSurfaceIsValid; private boolean mLoadPreviousTemporaryState; + private boolean mLaunchSystemMenu; private EmulationActivity activity; - public static EmulationFragment newInstance(String[] gamePaths, boolean riivolution) + public static EmulationFragment newInstance(String[] gamePaths, boolean riivolution, + boolean systemMenu) { Bundle args = new Bundle(); args.putStringArray(KEY_GAMEPATHS, gamePaths); args.putBoolean(KEY_RIIVOLUTION, riivolution); + args.putBoolean(KEY_SYSTEM_MENU, systemMenu); EmulationFragment fragment = new EmulationFragment(); fragment.setArguments(args); @@ -77,6 +80,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C mGamePaths = getArguments().getStringArray(KEY_GAMEPATHS); mRiivolution = getArguments().getBoolean(KEY_RIIVOLUTION); + mLaunchSystemMenu = getArguments().getBoolean(KEY_SYSTEM_MENU); } /** @@ -270,6 +274,11 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C Log.debug("[EmulationFragment] Starting emulation thread from previous state."); NativeLibrary.Run(mGamePaths, mRiivolution, getTemporaryStateFilePath(), true); } + if (mLaunchSystemMenu) + { + Log.debug("[EmulationFragment] Starting emulation thread for the Wii Menu."); + NativeLibrary.RunSystemMenu(); + } else { Log.debug("[EmulationFragment] Starting emulation thread."); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.java index 2b9b8defda..711f7d36de 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainActivity.java @@ -14,6 +14,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; +import androidx.lifecycle.ViewModelProvider; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.viewpager.widget.ViewPager; @@ -27,6 +28,9 @@ import org.dolphinemu.dolphinemu.features.settings.model.IntSetting; import org.dolphinemu.dolphinemu.features.settings.model.NativeConfig; import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity; +import org.dolphinemu.dolphinemu.features.sysupdate.ui.OnlineUpdateProgressBarDialogFragment; +import org.dolphinemu.dolphinemu.features.sysupdate.ui.SystemMenuNotInstalledDialogFragment; +import org.dolphinemu.dolphinemu.features.sysupdate.ui.SystemUpdateViewModel; import org.dolphinemu.dolphinemu.services.GameFileCacheManager; import org.dolphinemu.dolphinemu.ui.platform.Platform; import org.dolphinemu.dolphinemu.ui.platform.PlatformGamesView; @@ -36,6 +40,7 @@ import org.dolphinemu.dolphinemu.utils.DirectoryInitialization; import org.dolphinemu.dolphinemu.utils.FileBrowserHelper; import org.dolphinemu.dolphinemu.utils.PermissionsHandler; import org.dolphinemu.dolphinemu.utils.StartupHandler; +import org.dolphinemu.dolphinemu.utils.WiiUtils; /** * The main Activity of the Lollipop style UI. Manages several PlatformGamesFragments, which @@ -142,6 +147,14 @@ public final class MainActivity extends AppCompatActivity { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.menu_game_grid, menu); + + if (WiiUtils.isSystemMenuInstalled()) + { + menu.findItem(R.id.menu_load_wii_system_menu).setTitle( + getString(R.string.grid_menu_load_wii_system_menu_installed, + WiiUtils.getSystemMenuVersion())); + } + return true; } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainPresenter.java index 76f53ddd72..b95b35331f 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/MainPresenter.java @@ -3,18 +3,23 @@ package org.dolphinemu.dolphinemu.ui.main; import android.content.ContentResolver; -import android.content.Context; import android.content.Intent; import android.net.Uri; import androidx.appcompat.app.AlertDialog; -import androidx.core.app.ComponentActivity; +import androidx.activity.ComponentActivity; +import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProvider; import org.dolphinemu.dolphinemu.BuildConfig; import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.activities.EmulationActivity; import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting; import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; +import org.dolphinemu.dolphinemu.features.sysupdate.ui.OnlineUpdateProgressBarDialogFragment; +import org.dolphinemu.dolphinemu.features.sysupdate.ui.SystemMenuNotInstalledDialogFragment; +import org.dolphinemu.dolphinemu.features.sysupdate.ui.SystemUpdateViewModel; import org.dolphinemu.dolphinemu.model.GameFileCache; import org.dolphinemu.dolphinemu.services.GameFileCacheManager; import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner; @@ -40,10 +45,10 @@ public final class MainPresenter private static boolean sShouldRescanLibrary = true; private final MainView mView; - private final ComponentActivity mActivity; + private final FragmentActivity mActivity; private String mDirToAdd; - public MainPresenter(MainView view, ComponentActivity activity) + public MainPresenter(MainView view, FragmentActivity activity) { mView = view; mActivity = activity; @@ -94,6 +99,14 @@ public final class MainPresenter mView.launchOpenFileActivity(REQUEST_GAME_FILE); return true; + case R.id.menu_load_wii_system_menu: + launchWiiSystemMenu(); + return true; + + case R.id.menu_online_system_update: + launchOnlineUpdate(); + return true; + case R.id.menu_install_wad: new AfterDirectoryInitializationRunner().runWithLifecycle(activity, true, () -> mView.launchOpenFileActivity(REQUEST_WAD_FILE)); @@ -287,4 +300,43 @@ public final class MainPresenter { sShouldRescanLibrary = false; } + + private void launchOnlineUpdate() + { + if (WiiUtils.isSystemMenuInstalled()) + { + SystemUpdateViewModel viewModel = + new ViewModelProvider(mActivity).get(SystemUpdateViewModel.class); + viewModel.setRegion(-1); + OnlineUpdateProgressBarDialogFragment progressBarFragment = + new OnlineUpdateProgressBarDialogFragment(); + progressBarFragment + .show(mActivity.getSupportFragmentManager(), "OnlineUpdateProgressBarDialogFragment"); + progressBarFragment.setCancelable(false); + } + else + { + SystemMenuNotInstalledDialogFragment dialogFragment = + new SystemMenuNotInstalledDialogFragment(); + dialogFragment + .show(mActivity.getSupportFragmentManager(), "SystemMenuNotInstalledDialogFragment"); + } + } + + private void launchWiiSystemMenu() + { + WiiUtils.isSystemMenuInstalled(); + + if (WiiUtils.isSystemMenuInstalled()) + { + EmulationActivity.launchSystemMenu(mActivity); + } + else + { + SystemMenuNotInstalledDialogFragment dialogFragment = + new SystemMenuNotInstalledDialogFragment(); + dialogFragment + .show(mActivity.getSupportFragmentManager(), "SystemMenuNotInstalledDialogFragment"); + } + } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java index 58f3bbafb7..77ad01ecf3 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java @@ -378,6 +378,10 @@ public final class TvMainActivity extends FragmentActivity R.drawable.ic_folder, R.string.grid_menu_install_wad)); + rowItems.add(new TvSettingsItem(R.id.menu_load_wii_system_menu, + R.drawable.ic_folder, + R.string.grid_menu_load_wii_system_menu)); + rowItems.add(new TvSettingsItem(R.id.menu_import_wii_save, R.drawable.ic_folder, R.string.grid_menu_import_wii_save)); @@ -386,6 +390,10 @@ public final class TvMainActivity extends FragmentActivity R.drawable.ic_folder, R.string.grid_menu_import_nand_backup)); + rowItems.add(new TvSettingsItem(R.id.menu_online_system_update, + R.drawable.ic_folder, + R.string.grid_menu_online_system_update)); + // Create a header for this row. HeaderItem header = new HeaderItem(R.string.settings, getString(R.string.settings)); diff --git a/Source/Android/app/src/main/res/menu/menu_game_grid.xml b/Source/Android/app/src/main/res/menu/menu_game_grid.xml index 4b7d09ad97..b0fb121779 100644 --- a/Source/Android/app/src/main/res/menu/menu_game_grid.xml +++ b/Source/Android/app/src/main/res/menu/menu_game_grid.xml @@ -25,6 +25,11 @@ android:title="@string/grid_menu_install_wad" app:showAsAction="never"/> + + + + + diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index b0931ac7ad..9c7001b2a5 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -182,6 +182,29 @@ Display messages over the emulation screen area. These messages include memory card writes, video backend and CPU information, and JIT cache clearing. Download Game Covers from GameTDB.com + + Please select a region + Europe + Japan + Korea + United States + + + Updating + Updating title %016x...\nThis can take a while. + The emulated Wii console has been updated. + The emulated Wii console is already up-to-date. + The game\'s region does not match your console\'s. To avoid issues with the system menu, it is not possible to update the emulated console using this disc. + The game disc does not contain any usable update information. + The game disc does not contain any usable update information. + "Could not download update information from Nintendo. Please check your Internet connection and try again. + Could not download update files from Nintendo. Please check your Internet connection and try again. + "Could not install an update to the Wii system memory. Please refer to logs for more information. + The update has been cancelled. It is strongly recommended to finish it in order to avoid inconsistent system software versions. + Update completed + Update failed + Update cancelled + Audio DSP Emulation Engine @@ -391,6 +414,9 @@ Install WAD Import Wii Save Import BootMii NAND Backup + Perform Online System Update + Load Wii System Menu + Load Wii System Menu (%s) Importing... Do not close the app! Successfully installed this title to the NAND. @@ -401,6 +427,8 @@ Failed to import save file. The given file appears to be corrupted or is not a valid Wii save. Failed to import save file. Please launch the game once, then try again. Merging a new NAND over your currently selected NAND will overwrite any channels and savegames that already exist. This process is not reversible, so it is recommended that you keep backups of both NANDs. Are you sure you want to continue? + Not installed + The Wii Menu is currently not installed. Would you like to install it now?\nAn internet connection is required to download the update. It is recommended to download the update on Wi-Fi, as the amount of data downloaded may be large. Details