Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #8833 from Ebola16/Panic
Android: Use DialogFragment for AlertMessage
  • Loading branch information
JosJuice committed Oct 10, 2020
2 parents 696f08e + c3f34ac commit 5a939cc
Show file tree
Hide file tree
Showing 9 changed files with 182 additions and 67 deletions.
Expand Up @@ -8,10 +8,10 @@

import android.util.DisplayMetrics;
import android.view.Surface;

import androidx.appcompat.app.AlertDialog;
import android.widget.Toast;

import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import org.dolphinemu.dolphinemu.dialogs.AlertMessage;
import org.dolphinemu.dolphinemu.utils.CompressCallback;
import org.dolphinemu.dolphinemu.utils.Log;
import org.dolphinemu.dolphinemu.utils.Rumble;
Expand All @@ -25,6 +25,9 @@
*/
public final class NativeLibrary
{
private static final Object sAlertMessageLock = new Object();
private static boolean sIsShowingAlertMessage = false;

private static WeakReference<EmulationActivity> sEmulationActivity = new WeakReference<>(null);

/**
Expand Down Expand Up @@ -393,6 +396,8 @@ public static native void SetMotionSensorsEnabled(boolean accelerometerEnabled,
*/
public static native void StopEmulation();

public static native boolean IsBooting();

public static native void WaitUntilDoneBooting();

/**
Expand Down Expand Up @@ -440,10 +445,8 @@ public static native boolean ConvertDiscImage(String inPath, String outPath, int

public static native void SetObscuredPixelsTop(int height);

private static boolean alertResult = false;

public static boolean displayAlertMsg(final String caption, final String text,
final boolean yesNo)
final boolean yesNo, final boolean isWarning)
{
Log.error("[NativeLibrary] Alert: " + text);
final EmulationActivity emulationActivity = sEmulationActivity.get();
Expand All @@ -452,76 +455,64 @@ public static boolean displayAlertMsg(final String caption, final String text,
{
Log.warning("[NativeLibrary] EmulationActivity is null, can't do panic alert.");
}
else if (emulationActivity.isIgnoringWarnings() && isWarning)
{
return true;
}
else
{
// Create object used for waiting.
final Object lock = new Object();
AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity,
R.style.DolphinDialogBase)
.setTitle(caption)
.setMessage(text);

// If not yes/no dialog just have one button that dismisses modal,
// otherwise have a yes and no button that sets alertResult accordingly.
if (!yesNo)
// AlertMessages while the core is booting will deadlock when WaitUntilDoneBooting is called.
// Report the AlertMessage text as a toast instead.
if (IsBooting())
{
builder
.setCancelable(false)
.setPositiveButton("OK", (dialog, whichButton) ->
{
dialog.dismiss();
synchronized (lock)
{
lock.notify();
}
});
emulationActivity.runOnUiThread(
() -> Toast.makeText(emulationActivity.getApplicationContext(), text,
Toast.LENGTH_LONG)
.show());
}
else
{
alertResult = false;

builder
.setPositiveButton("Yes", (dialog, whichButton) ->
{
alertResult = true;
dialog.dismiss();
synchronized (lock)
{
lock.notify();
}
})
.setNegativeButton("No", (dialog, whichButton) ->
{
alertResult = false;
dialog.dismiss();
synchronized (lock)
{
lock.notify();
}
});
}
sIsShowingAlertMessage = true;

// Show the AlertDialog on the main thread.
emulationActivity.runOnUiThread(builder::show);
emulationActivity.runOnUiThread(
() -> AlertMessage.newInstance(caption, text, yesNo, isWarning)
.show(emulationActivity.getSupportFragmentManager(), "AlertMessage"));

// Wait for the lock to notify that it is complete.
synchronized (lock)
{
try
// Wait for the lock to notify that it is complete.
synchronized (sAlertMessageLock)
{
lock.wait();
try
{
sAlertMessageLock.wait();
}
catch (Exception ignored)
{
}
}
catch (Exception ignored)

if (yesNo)
{
result = AlertMessage.getAlertResult();
}
}

if (yesNo)
result = alertResult;
}
sIsShowingAlertMessage = false;
return result;
}

public static boolean IsShowingAlertMessage()
{
return sIsShowingAlertMessage;
}

public static void NotifyAlertMessageLock()
{
synchronized (sAlertMessageLock)
{
sAlertMessageLock.notify();
}
}

public static void setEmulationActivity(EmulationActivity emulationActivity)
{
Log.verbose("[NativeLibrary] Registering EmulationActivity.");
Expand Down
Expand Up @@ -85,12 +85,14 @@ public final class EmulationActivity extends AppCompatActivity
private String mSelectedGameId;
private int mPlatform;
private String[] mPaths;
private boolean mIgnoreWarnings;
private static boolean sUserPausedEmulation;

public static final String EXTRA_SELECTED_GAMES = "SelectedGames";
public static final String EXTRA_SELECTED_TITLE = "SelectedTitle";
public static final String EXTRA_SELECTED_GAMEID = "SelectedGameId";
public static final String EXTRA_PLATFORM = "Platform";
public static final String EXTRA_IGNORE_WARNINGS = "IgnoreWarnings";
public static final String EXTRA_USER_PAUSED_EMULATION = "sUserPausedEmulation";

@Retention(SOURCE)
Expand Down Expand Up @@ -272,8 +274,10 @@ protected void onCreate(Bundle savedInstanceState)
mSelectedTitle = gameToEmulate.getStringExtra(EXTRA_SELECTED_TITLE);
mSelectedGameId = gameToEmulate.getStringExtra(EXTRA_SELECTED_GAMEID);
mPlatform = gameToEmulate.getIntExtra(EXTRA_PLATFORM, 0);
mIgnoreWarnings = gameToEmulate.getBooleanExtra(EXTRA_IGNORE_WARNINGS, false);
sUserPausedEmulation = gameToEmulate.getBooleanExtra(EXTRA_USER_PAUSED_EMULATION, false);
activityRecreated = false;
Toast.makeText(this, R.string.emulation_menu_help, Toast.LENGTH_LONG).show();
}
else
{
Expand All @@ -297,8 +301,6 @@ protected void onCreate(Bundle savedInstanceState)
// Set these options now so that the SurfaceView the game renders into is the right size.
enableFullscreenImmersive();

Toast.makeText(this, getString(R.string.emulation_menu_help), Toast.LENGTH_LONG).show();

Rumble.initRumble(this);

setContentView(R.layout.activity_emulation);
Expand Down Expand Up @@ -328,6 +330,7 @@ protected void onSaveInstanceState(@NonNull Bundle outState)
outState.putString(EXTRA_SELECTED_TITLE, mSelectedTitle);
outState.putString(EXTRA_SELECTED_GAMEID, mSelectedGameId);
outState.putInt(EXTRA_PLATFORM, mPlatform);
outState.putBoolean(EXTRA_USER_PAUSED_EMULATION, mIgnoreWarnings);
outState.putBoolean(EXTRA_USER_PAUSED_EMULATION, sUserPausedEmulation);
super.onSaveInstanceState(outState);
}
Expand All @@ -338,6 +341,7 @@ protected void restoreState(Bundle savedInstanceState)
mSelectedTitle = savedInstanceState.getString(EXTRA_SELECTED_TITLE);
mSelectedGameId = savedInstanceState.getString(EXTRA_SELECTED_GAMEID);
mPlatform = savedInstanceState.getInt(EXTRA_PLATFORM);
mIgnoreWarnings = savedInstanceState.getBoolean(EXTRA_IGNORE_WARNINGS);
sUserPausedEmulation = savedInstanceState.getBoolean(EXTRA_USER_PAUSED_EMULATION);
}

Expand Down Expand Up @@ -672,6 +676,16 @@ public void handleMenuAction(@MenuAction int menuAction)
}
}

public boolean isIgnoringWarnings()
{
return mIgnoreWarnings;
}

public void setIgnoreWarnings(boolean value)
{
mIgnoreWarnings = value;
}

public static boolean getHasUserPausedEmulation()
{
return sUserPausedEmulation;
Expand Down
@@ -0,0 +1,96 @@
package org.dolphinemu.dolphinemu.dialogs;

import android.app.Dialog;
import android.os.Bundle;

import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.EmulationActivity;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;

public final class AlertMessage extends DialogFragment
{
private static boolean sAlertResult = false;
private static final String ARG_TITLE = "title";
private static final String ARG_MESSAGE = "message";
private static final String ARG_YES_NO = "yesNo";
private static final String ARG_IS_WARNING = "isWarning";

public static AlertMessage newInstance(String title, String message, boolean yesNo,
boolean isWarning)
{
AlertMessage fragment = new AlertMessage();

Bundle args = new Bundle();
args.putString(ARG_TITLE, title);
args.putString(ARG_MESSAGE, message);
args.putBoolean(ARG_YES_NO, yesNo);
args.putBoolean(ARG_IS_WARNING, isWarning);
fragment.setArguments(args);

return fragment;
}

@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
final EmulationActivity emulationActivity = NativeLibrary.getEmulationActivity();
String title = requireArguments().getString(ARG_TITLE);
String message = requireArguments().getString(ARG_MESSAGE);
boolean yesNo = requireArguments().getBoolean(ARG_YES_NO);
boolean isWarning = requireArguments().getBoolean(ARG_IS_WARNING);
setCancelable(false);

AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity,
R.style.DolphinDialogBase)
.setTitle(title)
.setMessage(message);

// If not yes/no dialog just have one button that dismisses modal,
// otherwise have a yes and no button that sets sAlertResult accordingly.
if (!yesNo)
{
builder.setPositiveButton(android.R.string.ok, (dialog, which) ->
{
dialog.dismiss();
NativeLibrary.NotifyAlertMessageLock();
});
}
else
{
builder.setPositiveButton(android.R.string.yes, (dialog, which) ->
{
sAlertResult = true;
dialog.dismiss();
NativeLibrary.NotifyAlertMessageLock();
})
.setNegativeButton(android.R.string.no, (dialog, which) ->
{
sAlertResult = false;
dialog.dismiss();
NativeLibrary.NotifyAlertMessageLock();
});
}

if (isWarning)
{
builder.setNeutralButton(R.string.ignore_warning_alert_messages, (dialog, which) ->
{
emulationActivity.setIgnoreWarnings(true);
dialog.dismiss();
NativeLibrary.NotifyAlertMessageLock();
});
}

return builder.create();
}

public static boolean getAlertResult()
{
return sAlertResult;
}
}
Expand Up @@ -112,7 +112,7 @@ public void onResume()
@Override
public void onPause()
{
if (mEmulationState.isRunning())
if (mEmulationState.isRunning() && !NativeLibrary.IsShowingAlertMessage())
mEmulationState.pause();
super.onPause();
}
Expand Down Expand Up @@ -323,7 +323,7 @@ public synchronized void clearSurface()
mSurface = null;
Log.debug("[EmulationFragment] Surface destroyed.");

if (state != State.STOPPED)
if (state != State.STOPPED && !NativeLibrary.IsShowingAlertMessage())
{
// In order to avoid dereferencing nullptr, we must not destroy the surface while booting
// the core, so wait here if necessary. An easy (but not 100% consistent) way to reach
Expand Down Expand Up @@ -362,7 +362,8 @@ private void runWithValidSurface()
else if (state == State.PAUSED)
{
NativeLibrary.SurfaceChanged(mSurface);
if (!EmulationActivity.getHasUserPausedEmulation())
if (!EmulationActivity.getHasUserPausedEmulation() &&
!NativeLibrary.IsShowingAlertMessage())
{
Log.debug("[EmulationFragment] Resuming emulation.");
NativeLibrary.UnPauseEmulation();
Expand Down
1 change: 1 addition & 0 deletions Source/Android/app/src/main/res/values/strings.xml
Expand Up @@ -438,5 +438,6 @@ It can efficiently compress both junk data and encrypted Wii data.
<string name="slider_setting_value">%1$d%2$s</string>
<string name="disc_number">Disc %1$d</string>
<string name="disabled_gc_overlay_notice">GameCube Controller 1 is set to \"None\"</string>
<string name="ignore_warning_alert_messages">Ignore for this session</string>

</resources>
2 changes: 1 addition & 1 deletion Source/Android/jni/AndroidCommon/IDCache.cpp
Expand Up @@ -210,7 +210,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved)
const jclass native_library_class = env->FindClass("org/dolphinemu/dolphinemu/NativeLibrary");
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
s_display_alert_msg = env->GetStaticMethodID(s_native_library_class, "displayAlertMsg",
"(Ljava/lang/String;Ljava/lang/String;Z)Z");
"(Ljava/lang/String;Ljava/lang/String;ZZ)Z");
s_do_rumble = env->GetStaticMethodID(s_native_library_class, "rumble", "(ID)V");
s_get_update_touch_pointer =
env->GetStaticMethodID(s_native_library_class, "updateTouchPointer", "()V");
Expand Down

0 comments on commit 5a939cc

Please sign in to comment.