@@ -27,6 +27,7 @@
import org.dolphinemu.dolphinemu.utils.CompletableFuture;
import org.dolphinemu.dolphinemu.utils.ContentHandler;
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
import org.dolphinemu.dolphinemu.utils.ThreadUtil;
import org.dolphinemu.dolphinemu.utils.WiiUtils;

import java.util.Arrays;
@@ -182,7 +183,7 @@ public void onDirectorySelected(Intent result)

public void installWAD(String path)
{
runOnThreadAndShowResult(R.string.import_in_progress, 0, () ->
ThreadUtil.runOnThreadAndShowResult(mActivity, R.string.import_in_progress, 0, () ->
{
boolean success = WiiUtils.installWAD(path);
int message = success ? R.string.wad_install_success : R.string.wad_install_failure;
@@ -194,7 +195,7 @@ public void importWiiSave(String path)
{
CompletableFuture<Boolean> canOverwriteFuture = new CompletableFuture<>();

runOnThreadAndShowResult(R.string.import_in_progress, 0, () ->
ThreadUtil.runOnThreadAndShowResult(mActivity, R.string.import_in_progress, 0, () ->
{
BooleanSupplier canOverwrite = () ->
{
@@ -255,47 +256,19 @@ public void importNANDBin(String path)
{
dialog.dismiss();

runOnThreadAndShowResult(R.string.import_in_progress, R.string.do_not_close_app, () ->
{
// ImportNANDBin doesn't provide any result value, unfortunately...
// It does however show a panic alert if something goes wrong.
WiiUtils.importNANDBin(path);
return null;
});
ThreadUtil.runOnThreadAndShowResult(mActivity, R.string.import_in_progress,
R.string.do_not_close_app, () ->
{
// ImportNANDBin unfortunately doesn't provide any result value...
// It does however show a panic alert if something goes wrong.
WiiUtils.importNANDBin(path);
return null;
});
});

builder.show();
}

private void runOnThreadAndShowResult(int progressTitle, int progressMessage, Supplier<String> f)
{
AlertDialog progressDialog = new AlertDialog.Builder(mActivity, R.style.DolphinDialogBase)
.create();
progressDialog.setTitle(progressTitle);
if (progressMessage != 0)
progressDialog.setMessage(mActivity.getResources().getString(progressMessage));
progressDialog.setCancelable(false);
progressDialog.show();

new Thread(() ->
{
String result = f.get();
mActivity.runOnUiThread(() ->
{
progressDialog.dismiss();

if (result != null)
{
AlertDialog.Builder builder =
new AlertDialog.Builder(mActivity, R.style.DolphinDialogBase);
builder.setMessage(result);
builder.setPositiveButton(R.string.ok, (dialog, i) -> dialog.dismiss());
builder.show();
}
});
}, mActivity.getResources().getString(progressTitle)).start();
}

public static void skipRescanningLibrary()
{
sShouldRescanLibrary = false;
@@ -236,6 +236,11 @@ public static String getUserDirectory()
return userPath;
}

public static File getGameListCache(Context context)
{
return new File(context.getExternalCacheDir(), "gamelist.cache");
}

private static boolean copyAsset(String asset, File output, Boolean overwrite, Context context)
{
Log.verbose("[DirectoryInitialization] Copying File " + asset + " to " + output);
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: GPL-2.0-or-later

package org.dolphinemu.dolphinemu.utils;

import android.app.Activity;
import android.content.DialogInterface;
import android.content.res.Resources;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;

import org.dolphinemu.dolphinemu.R;

import java.util.function.Supplier;

public class ThreadUtil
{
public static void runOnThreadAndShowResult(Activity activity, int progressTitle,
int progressMessage, @NonNull Supplier<String> f)
{
runOnThreadAndShowResult(activity, progressTitle, progressMessage, f, null);
}

public static void runOnThreadAndShowResult(Activity activity, int progressTitle,
int progressMessage, @NonNull Supplier<String> f,
@Nullable DialogInterface.OnDismissListener onResultDismiss)
{
Resources resources = activity.getResources();
AlertDialog progressDialog = new AlertDialog.Builder(activity, R.style.DolphinDialogBase)
.create();
progressDialog.setTitle(progressTitle);
if (progressMessage != 0)
progressDialog.setMessage(resources.getString(progressMessage));
progressDialog.setCancelable(false);
progressDialog.show();

new Thread(() ->
{
String result = f.get();
activity.runOnUiThread(() ->
{
progressDialog.dismiss();

if (result != null)
{
AlertDialog.Builder builder =
new AlertDialog.Builder(activity, R.style.DolphinDialogBase);
builder.setMessage(result);
builder.setPositiveButton(R.string.ok, (dialog, i) -> dialog.dismiss());
builder.setOnDismissListener(onResultDismiss);
builder.show();
}
});
}, resources.getString(progressTitle)).start();
}
}
@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:id="@+id/text_type"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="@dimen/spacing_medlarge"
tools:text="@string/user_data_new_location"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/barrier_buttons"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/text_path"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintWidth_max="400dp" />

<TextView
android:id="@+id/text_path"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="@dimen/spacing_medlarge"
tools:text="/storage/emulated/0/Android/data/org.dolphinemu.dolphinemu/files"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/barrier_buttons"
app:layout_constraintTop_toBottomOf="@id/text_type"
app:layout_constraintBottom_toTopOf="@id/text_android_11"
app:layout_constraintWidth_max="400dp" />

<TextView
android:id="@+id/text_android_11"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="@dimen/spacing_medlarge"
android:text="@string/user_data_new_location_android_11"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/barrier_buttons"
app:layout_constraintTop_toBottomOf="@id/text_path"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintWidth_max="400dp" />

<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="end"
app:constraint_referenced_ids="text_type,text_path,text_android_11" />

<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier_buttons"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="start"
app:constraint_referenced_ids="button_open_system_file_manager,button_import_user_data,button_export_user_data" />

<Button
android:id="@+id/button_open_system_file_manager"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/spacing_small"
android:text="@string/user_data_open_system_file_manager"
android:textColor="@color/dolphin_white"
app:layout_constraintStart_toEndOf="@id/barrier_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/button_import_user_data"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintWidth_max="400dp" />

<Button
android:id="@+id/button_import_user_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/spacing_small"
android:text="@string/user_data_import"
android:textColor="@color/dolphin_white"
app:layout_constraintStart_toEndOf="@id/barrier_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/button_open_system_file_manager"
app:layout_constraintBottom_toTopOf="@id/button_export_user_data"
app:layout_constraintWidth_max="400dp" />

<Button
android:id="@+id/button_export_user_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/spacing_small"
android:text="@string/user_data_export"
android:textColor="@color/dolphin_white"
app:layout_constraintStart_toEndOf="@id/barrier_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/button_import_user_data"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintWidth_max="400dp" />

</androidx.constraintlayout.widget.ConstraintLayout>
@@ -16,7 +16,7 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/text_path"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintWidth_max="400dp"/>
app:layout_constraintWidth_max="400dp" />

<TextView
android:id="@+id/text_path"
@@ -34,7 +34,9 @@
android:id="@+id/text_android_11"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="@dimen/spacing_medlarge"
android:layout_marginHorizontal="@dimen/spacing_medlarge"
android:layout_marginTop="@dimen/spacing_medlarge"
android:layout_marginBottom="24dp"
android:text="@string/user_data_new_location_android_11"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
@@ -46,11 +48,36 @@
android:id="@+id/button_open_system_file_manager"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/spacing_medlarge"
android:layout_margin="@dimen/spacing_small"
android:text="@string/user_data_open_system_file_manager"
android:textColor="@color/dolphin_white"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_android_11"
app:layout_constraintBottom_toTopOf="@id/button_import_user_data" />

<Button
android:id="@+id/button_import_user_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/spacing_small"
android:text="@string/user_data_import"
android:textColor="@color/dolphin_white"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/button_open_system_file_manager"
app:layout_constraintBottom_toTopOf="@id/button_export_user_data" />

<Button
android:id="@+id/button_export_user_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/spacing_small"
android:text="@string/user_data_export"
android:textColor="@color/dolphin_white"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/button_import_user_data"
app:layout_constraintBottom_toBottomOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
@@ -388,9 +388,17 @@
<string name="user_data_new_location">Your user data is stored in a location which <b>will be deleted</b> when you uninstall the app:</string>
<!-- Android 10 and up support android:hasFragileUserData -->
<string name="user_data_new_location_android_10">Your user data is stored in a location which by default <b>will be deleted</b> when you uninstall the app:</string>
<string name="user_data_new_location_android_11">Because you\'re using Android 11 or newer, not all file manager apps can access this location. However, you can access it using the system file manager (if present on your device), or by connecting your device to a PC.</string>
<string name="user_data_new_location_android_11">Because you\'re using Android 11 or newer, file manager apps can\'t access this folder in the same way as regular folders. You might be able to access the folder using the system file manager (if present on your device), or by connecting your device to a PC.</string>
<string name="user_data_open_system_file_manager">Open System File Manager</string>
<string name="user_data_import">Import User Data</string>
<string name="user_data_export">Export User Data</string>
<string name="user_data_open_system_file_manager_failed">Sorry, Dolphin couldn\'t find the system file manager on your device.</string>
<string name="user_data_import_warning">Are you sure you want to replace your user data with the data in this file? All existing user data will be deleted!</string>
<string name="user_data_import_invalid_file">This file doesn\'t seem to contain Dolphin user data.</string>
<string name="user_data_import_success">The user data has been imported.</string>
<string name="user_data_import_failure">Failed to import user data.</string>
<string name="user_data_export_success">Your user data has been exported.</string>
<string name="user_data_export_failure">Failed to export user data.</string>

<!-- Miscellaneous -->
<string name="yes">Yes</string>
@@ -418,6 +426,7 @@
<string name="grid_menu_load_wii_system_menu">Load Wii System Menu</string>
<string name="grid_menu_load_wii_system_menu_installed">Load Wii System Menu (%s)</string>
<string name="import_in_progress">Importing...</string>
<string name="export_in_progress">Exporting...</string>
<string name="do_not_close_app">Do not close the app!</string>
<string name="wad_install_success">Successfully installed this title to the NAND.</string>
<string name="wad_install_failure">Failed to install this title to the NAND.</string>