Android: Use DialogFragment for AlertMessage
This commit is contained in:
parent
57f14b260b
commit
991eb6ae83
|
@ -8,10 +8,10 @@ package org.dolphinemu.dolphinemu;
|
|||
|
||||
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;
|
||||
|
@ -25,6 +25,9 @@ import java.util.LinkedHashMap;
|
|||
*/
|
||||
public final class NativeLibrary
|
||||
{
|
||||
private static final Object sAlertMessageLock = new Object();
|
||||
private static boolean sIsShowingAlertMessage = false;
|
||||
|
||||
private static WeakReference<EmulationActivity> sEmulationActivity = new WeakReference<>(null);
|
||||
|
||||
/**
|
||||
|
@ -393,6 +396,8 @@ public final class NativeLibrary
|
|||
*/
|
||||
public static native void StopEmulation();
|
||||
|
||||
public static native boolean IsBooting();
|
||||
|
||||
public static native void WaitUntilDoneBooting();
|
||||
|
||||
/**
|
||||
|
@ -440,8 +445,6 @@ public final class NativeLibrary
|
|||
|
||||
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)
|
||||
{
|
||||
|
@ -454,74 +457,57 @@ public final class NativeLibrary
|
|||
}
|
||||
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;
|
||||
sIsShowingAlertMessage = true;
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
emulationActivity.runOnUiThread(() -> AlertMessage.newInstance(caption, text, yesNo)
|
||||
.show(emulationActivity.getSupportFragmentManager(), "AlertMessage"));
|
||||
|
||||
// Show the AlertDialog on the main thread.
|
||||
emulationActivity.runOnUiThread(builder::show);
|
||||
|
||||
// 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.");
|
||||
|
|
|
@ -274,6 +274,7 @@ public final class EmulationActivity extends AppCompatActivity
|
|||
mPlatform = gameToEmulate.getIntExtra(EXTRA_PLATFORM, 0);
|
||||
sUserPausedEmulation = gameToEmulate.getBooleanExtra(EXTRA_USER_PAUSED_EMULATION, false);
|
||||
activityRecreated = false;
|
||||
Toast.makeText(this, R.string.emulation_menu_help, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -297,8 +298,6 @@ public final class EmulationActivity extends AppCompatActivity
|
|||
// 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);
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
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";
|
||||
|
||||
public static AlertMessage newInstance(String title, String message, boolean yesNo)
|
||||
{
|
||||
AlertMessage fragment = new AlertMessage();
|
||||
|
||||
Bundle args = new Bundle();
|
||||
args.putString(ARG_TITLE, title);
|
||||
args.putString(ARG_MESSAGE, message);
|
||||
args.putBoolean(ARG_YES_NO, yesNo);
|
||||
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);
|
||||
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();
|
||||
});
|
||||
}
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
public static boolean getAlertResult()
|
||||
{
|
||||
return sAlertResult;
|
||||
}
|
||||
}
|
|
@ -112,7 +112,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
|
|||
@Override
|
||||
public void onPause()
|
||||
{
|
||||
if (mEmulationState.isRunning())
|
||||
if (mEmulationState.isRunning() && !NativeLibrary.IsShowingAlertMessage())
|
||||
mEmulationState.pause();
|
||||
super.onPause();
|
||||
}
|
||||
|
@ -323,7 +323,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
|
|||
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
|
||||
|
@ -362,7 +362,8 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
|
|||
else if (state == State.PAUSED)
|
||||
{
|
||||
NativeLibrary.SurfaceChanged(mSurface);
|
||||
if (!EmulationActivity.getHasUserPausedEmulation())
|
||||
if (!EmulationActivity.getHasUserPausedEmulation() &&
|
||||
!NativeLibrary.IsShowingAlertMessage())
|
||||
{
|
||||
Log.debug("[EmulationFragment] Resuming emulation.");
|
||||
NativeLibrary.UnPauseEmulation();
|
||||
|
|
|
@ -300,6 +300,12 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_StopEmulatio
|
|||
s_emulation_end_event.Wait();
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_IsBooting(JNIEnv* env,
|
||||
jobject obj)
|
||||
{
|
||||
return static_cast<jboolean>(Core::IsBooting());
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_NativeLibrary_WaitUntilDoneBooting(JNIEnv* env, jobject obj)
|
||||
{
|
||||
|
|
|
@ -170,6 +170,11 @@ void DisplayMessage(std::string message, int time_in_ms)
|
|||
OSD::AddMessage(std::move(message), time_in_ms);
|
||||
}
|
||||
|
||||
bool IsBooting()
|
||||
{
|
||||
return s_is_booting.IsSet() || !s_hardware_initialized;
|
||||
}
|
||||
|
||||
bool IsRunning()
|
||||
{
|
||||
return (GetState() != State::Uninitialized || s_hardware_initialized) && !s_is_stopping;
|
||||
|
@ -674,7 +679,7 @@ State GetState()
|
|||
|
||||
void WaitUntilDoneBooting()
|
||||
{
|
||||
if (s_is_booting.IsSet() || !s_hardware_initialized)
|
||||
if (IsBooting())
|
||||
s_done_booting.Wait();
|
||||
}
|
||||
|
||||
|
|
|
@ -99,6 +99,7 @@ void UndeclareAsCPUThread();
|
|||
|
||||
std::string StopMessage(bool main_thread, std::string_view message);
|
||||
|
||||
bool IsBooting();
|
||||
bool IsRunning();
|
||||
bool IsRunningAndStarted(); // is running and the CPU loop has been entered
|
||||
bool IsRunningInCurrentThread(); // this tells us whether we are running in the CPU thread.
|
||||
|
|
Loading…
Reference in New Issue