android: frontend: Implement basic software keyboard applet.
This commit is contained in:
parent
3ebfc5e97c
commit
2e8892eab4
@ -10,6 +10,9 @@ import android.app.AlertDialog;
|
|||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
import org.citra.citra_android.activities.EmulationActivity;
|
import org.citra.citra_android.activities.EmulationActivity;
|
||||||
import org.citra.citra_android.utils.Log;
|
import org.citra.citra_android.utils.Log;
|
||||||
@ -26,7 +29,15 @@ public final class NativeLibrary {
|
|||||||
*/
|
*/
|
||||||
public static final String TouchScreenDevice = "Touchscreen";
|
public static final String TouchScreenDevice = "Touchscreen";
|
||||||
public static WeakReference<EmulationActivity> sEmulationActivity = new WeakReference<>(null);
|
public static WeakReference<EmulationActivity> sEmulationActivity = new WeakReference<>(null);
|
||||||
|
|
||||||
private static boolean alertResult = false;
|
private static boolean alertResult = false;
|
||||||
|
private static String alertPromptResult = "";
|
||||||
|
private static int alertPromptButton = 0;
|
||||||
|
private static final Object alertPromptLock = new Object();
|
||||||
|
private static boolean alertPromptInProgress = false;
|
||||||
|
private static String alertPromptCaption = "";
|
||||||
|
private static int alertPromptButtonConfig = 0;
|
||||||
|
private static EditText alertPromptEditText = null;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
@ -353,6 +364,86 @@ public final class NativeLibrary {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void retryDisplayAlertPrompt() {
|
||||||
|
if (!alertPromptInProgress) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
displayAlertPromptImpl(alertPromptCaption, alertPromptEditText.getText().toString(), alertPromptButtonConfig).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String displayAlertPrompt(String caption, String text, int buttonConfig) {
|
||||||
|
alertPromptCaption = caption;
|
||||||
|
alertPromptButtonConfig = buttonConfig;
|
||||||
|
alertPromptInProgress = true;
|
||||||
|
|
||||||
|
// Show the AlertDialog on the main thread
|
||||||
|
sEmulationActivity.get().runOnUiThread(() -> displayAlertPromptImpl(alertPromptCaption, text, alertPromptButtonConfig).show());
|
||||||
|
|
||||||
|
// Wait for the lock to notify that it is complete
|
||||||
|
synchronized (alertPromptLock) {
|
||||||
|
try {
|
||||||
|
alertPromptLock.wait();
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
alertPromptInProgress = false;
|
||||||
|
|
||||||
|
return alertPromptResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AlertDialog.Builder displayAlertPromptImpl(String caption, String text, int buttonConfig) {
|
||||||
|
final EmulationActivity emulationActivity = sEmulationActivity.get();
|
||||||
|
alertPromptResult = "";
|
||||||
|
alertPromptButton = 0;
|
||||||
|
|
||||||
|
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
params.leftMargin = params.rightMargin = CitraApplication.getAppContext().getResources().getDimensionPixelSize(R.dimen.dialog_margin);
|
||||||
|
|
||||||
|
// Set up the input
|
||||||
|
alertPromptEditText = new EditText(DolphinApplication.getAppContext());
|
||||||
|
alertPromptEditText.setText(text);
|
||||||
|
alertPromptEditText.setSingleLine();
|
||||||
|
alertPromptEditText.setLayoutParams(params);
|
||||||
|
|
||||||
|
FrameLayout container = new FrameLayout(emulationActivity);
|
||||||
|
container.addView(alertPromptEditText);
|
||||||
|
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity)
|
||||||
|
.setTitle(caption)
|
||||||
|
.setView(container)
|
||||||
|
.setPositiveButton(android.R.string.ok, (dialogInterface, i) ->
|
||||||
|
{
|
||||||
|
alertPromptButton = buttonConfig;
|
||||||
|
alertPromptResult = alertPromptEditText.getText().toString();
|
||||||
|
synchronized (alertPromptLock) {
|
||||||
|
alertPromptLock.notifyAll();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setOnDismissListener(dialogInterface ->
|
||||||
|
{
|
||||||
|
alertPromptResult = "";
|
||||||
|
synchronized (alertPromptLock) {
|
||||||
|
alertPromptLock.notifyAll();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (buttonConfig > 0) {
|
||||||
|
builder.setNegativeButton(android.R.string.cancel, (dialogInterface, i) ->
|
||||||
|
{
|
||||||
|
alertPromptResult = "";
|
||||||
|
synchronized (alertPromptLock) {
|
||||||
|
alertPromptLock.notifyAll();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int alertPromptButton() {
|
||||||
|
return alertPromptButton;
|
||||||
|
}
|
||||||
|
|
||||||
public static void setEmulationActivity(EmulationActivity emulationActivity) {
|
public static void setEmulationActivity(EmulationActivity emulationActivity) {
|
||||||
Log.verbose("[NativeLibrary] Registering EmulationActivity.");
|
Log.verbose("[NativeLibrary] Registering EmulationActivity.");
|
||||||
sEmulationActivity = new WeakReference<>(emulationActivity);
|
sEmulationActivity = new WeakReference<>(emulationActivity);
|
||||||
|
@ -237,6 +237,9 @@ public final class EmulationActivity extends AppCompatActivity {
|
|||||||
mSelectedTitle = savedInstanceState.getString(EXTRA_SELECTED_TITLE);
|
mSelectedTitle = savedInstanceState.getString(EXTRA_SELECTED_TITLE);
|
||||||
mScreenPath = savedInstanceState.getString(EXTRA_SCREEN_PATH);
|
mScreenPath = savedInstanceState.getString(EXTRA_SCREEN_PATH);
|
||||||
mPosition = savedInstanceState.getInt(EXTRA_GRID_POSITION);
|
mPosition = savedInstanceState.getInt(EXTRA_GRID_POSITION);
|
||||||
|
|
||||||
|
// If an alert prompt was in progress when state was restored, retry displaying it
|
||||||
|
NativeLibrary.retryDisplayAlertPrompt();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -17,6 +17,8 @@ static JavaVM* s_java_vm;
|
|||||||
|
|
||||||
static jclass s_native_library_class;
|
static jclass s_native_library_class;
|
||||||
static jmethodID s_display_alert_msg;
|
static jmethodID s_display_alert_msg;
|
||||||
|
static jmethodID s_display_alert_prompt;
|
||||||
|
static jmethodID s_alert_prompt_button;
|
||||||
static jmethodID s_is_portrait_mode;
|
static jmethodID s_is_portrait_mode;
|
||||||
static jmethodID s_landscape_screen_layout;
|
static jmethodID s_landscape_screen_layout;
|
||||||
|
|
||||||
@ -49,6 +51,14 @@ jmethodID GetDisplayAlertMsg() {
|
|||||||
return s_display_alert_msg;
|
return s_display_alert_msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jmethodID GetDisplayAlertPrompt() {
|
||||||
|
return s_display_alert_prompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
jmethodID GetAlertPromptButton() {
|
||||||
|
return s_alert_prompt_button;
|
||||||
|
}
|
||||||
|
|
||||||
jmethodID GetIsPortraitMode() {
|
jmethodID GetIsPortraitMode() {
|
||||||
return s_is_portrait_mode;
|
return s_is_portrait_mode;
|
||||||
}
|
}
|
||||||
@ -85,6 +95,11 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|||||||
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
|
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
|
||||||
s_display_alert_msg = env->GetStaticMethodID(s_native_library_class, "displayAlertMsg",
|
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;Z)Z");
|
||||||
|
s_display_alert_prompt =
|
||||||
|
env->GetStaticMethodID(s_native_library_class, "displayAlertPrompt",
|
||||||
|
"(Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String;");
|
||||||
|
s_alert_prompt_button =
|
||||||
|
env->GetStaticMethodID(s_native_library_class, "alertPromptButton", "()I");
|
||||||
s_is_portrait_mode = env->GetStaticMethodID(s_native_library_class, "isPortraitMode", "()Z");
|
s_is_portrait_mode = env->GetStaticMethodID(s_native_library_class, "isPortraitMode", "()Z");
|
||||||
s_landscape_screen_layout =
|
s_landscape_screen_layout =
|
||||||
env->GetStaticMethodID(s_native_library_class, "landscapeScreenLayout", "()I");
|
env->GetStaticMethodID(s_native_library_class, "landscapeScreenLayout", "()I");
|
||||||
|
@ -11,6 +11,8 @@ namespace IDCache {
|
|||||||
JNIEnv* GetEnvForThread();
|
JNIEnv* GetEnvForThread();
|
||||||
jclass GetNativeLibraryClass();
|
jclass GetNativeLibraryClass();
|
||||||
jmethodID GetDisplayAlertMsg();
|
jmethodID GetDisplayAlertMsg();
|
||||||
|
jmethodID GetDisplayAlertPrompt();
|
||||||
|
jmethodID GetAlertPromptButton();
|
||||||
jmethodID GetIsPortraitMode();
|
jmethodID GetIsPortraitMode();
|
||||||
jmethodID GetLandscapeScreenLayout();
|
jmethodID GetLandscapeScreenLayout();
|
||||||
|
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
// Copyright 2019 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
@ -53,6 +57,58 @@ std::condition_variable running_cv;
|
|||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
|
static std::string GetJString(JNIEnv* env, jstring jstr) {
|
||||||
|
if (!jstr) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* s = env->GetStringUTFChars(jstr, nullptr);
|
||||||
|
std::string result = s;
|
||||||
|
env->ReleaseStringUTFChars(jstr, s);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool DisplayAlertMessage(const char* caption, const char* text, bool yes_no) {
|
||||||
|
JNIEnv* env = IDCache::GetEnvForThread();
|
||||||
|
|
||||||
|
// Execute the Java method.
|
||||||
|
jboolean result = env->CallStaticBooleanMethod(
|
||||||
|
IDCache::GetNativeLibraryClass(), IDCache::GetDisplayAlertMsg(), env->NewStringUTF(caption),
|
||||||
|
env->NewStringUTF(text), yes_no ? JNI_TRUE : JNI_FALSE);
|
||||||
|
|
||||||
|
return result != JNI_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string DisplayAlertPrompt(const char* caption, const char* text, int buttonConfig) {
|
||||||
|
JNIEnv* env = IDCache::GetEnvForThread();
|
||||||
|
|
||||||
|
jstring value = reinterpret_cast<jstring>(env->CallStaticObjectMethod(
|
||||||
|
IDCache::GetNativeLibraryClass(), IDCache::GetDisplayAlertPrompt(),
|
||||||
|
env->NewStringUTF(caption), env->NewStringUTF(text), buttonConfig));
|
||||||
|
|
||||||
|
return GetJString(env, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int AlertPromptButton() {
|
||||||
|
JNIEnv* env = IDCache::GetEnvForThread();
|
||||||
|
|
||||||
|
// Execute the Java method.
|
||||||
|
return static_cast<int>(env->CallStaticIntMethod(IDCache::GetNativeLibraryClass(),
|
||||||
|
IDCache::GetAlertPromptButton()));
|
||||||
|
}
|
||||||
|
|
||||||
|
class AndroidKeyboard final : public Frontend::SoftwareKeyboard {
|
||||||
|
public:
|
||||||
|
void Execute(const Frontend::KeyboardConfig& config) override {
|
||||||
|
SoftwareKeyboard::Execute(config);
|
||||||
|
Finalize(DisplayAlertPrompt("Enter text", config.hint_text.c_str(),
|
||||||
|
static_cast<int>(this->config.button_config)),
|
||||||
|
AlertPromptButton());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShowError(const std::string& error) override {}
|
||||||
|
};
|
||||||
|
|
||||||
static int RunCitra(const std::string& filepath) {
|
static int RunCitra(const std::string& filepath) {
|
||||||
// Citra core only supports a single running instance
|
// Citra core only supports a single running instance
|
||||||
std::lock_guard<std::mutex> lock(running_mutex);
|
std::lock_guard<std::mutex> lock(running_mutex);
|
||||||
@ -72,7 +128,7 @@ static int RunCitra(const std::string& filepath) {
|
|||||||
|
|
||||||
// Register frontend applets
|
// Register frontend applets
|
||||||
Frontend::RegisterDefaultApplets();
|
Frontend::RegisterDefaultApplets();
|
||||||
system.RegisterSoftwareKeyboard(std::make_shared<AndroidKeyboard>(env));
|
system.RegisterSoftwareKeyboard(std::make_shared<AndroidKeyboard>());
|
||||||
|
|
||||||
{
|
{
|
||||||
// Forces a config reload on game boot, if the user changed settings in the UI
|
// Forces a config reload on game boot, if the user changed settings in the UI
|
||||||
@ -139,17 +195,6 @@ static int RunCitra(const std::string& filepath) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string GetJString(JNIEnv* env, jstring jstr) {
|
|
||||||
if (!jstr) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* s = env->GetStringUTFChars(jstr, nullptr);
|
|
||||||
std::string result = s;
|
|
||||||
env->ReleaseStringUTFChars(jstr, s);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Java_org_citra_citra_1android_NativeLibrary_SurfaceChanged(JNIEnv* env, jobject obj,
|
void Java_org_citra_citra_1android_NativeLibrary_SurfaceChanged(JNIEnv* env, jobject obj,
|
||||||
jobject surf) {
|
jobject surf) {
|
||||||
s_surf = ANativeWindow_fromSurface(env, surf);
|
s_surf = ANativeWindow_fromSurface(env, surf);
|
||||||
@ -250,7 +295,8 @@ jintArray Java_org_citra_citra_1android_NativeLibrary_GetBanner(JNIEnv* env, job
|
|||||||
}
|
}
|
||||||
|
|
||||||
jintArray Banner = env->NewIntArray(icon_data.size());
|
jintArray Banner = env->NewIntArray(icon_data.size());
|
||||||
env->SetIntArrayRegion(Banner, 0, env->GetArrayLength(Banner), reinterpret_cast<jint*>(icon_data.data()));
|
env->SetIntArrayRegion(Banner, 0, env->GetArrayLength(Banner),
|
||||||
|
reinterpret_cast<jint*>(icon_data.data()));
|
||||||
|
|
||||||
return Banner;
|
return Banner;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2019 Citra Emulator Project
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
// Initialise and run the emulator
|
#pragma once
|
||||||
static int RunCitra(const std::string& path);
|
|
||||||
|
|
||||||
// Function calls from the Java side
|
// Function calls from the Java side
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -8,4 +8,6 @@
|
|||||||
<dimen name="spacing_small">4dp</dimen>
|
<dimen name="spacing_small">4dp</dimen>
|
||||||
<dimen name="spacing_medlarge">12dp</dimen>
|
<dimen name="spacing_medlarge">12dp</dimen>
|
||||||
<dimen name="spacing_large">16dp</dimen>
|
<dimen name="spacing_large">16dp</dimen>
|
||||||
|
|
||||||
|
<dimen name="dialog_margin">20dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user