Android: Don't use separate thread for MotionAlertDialog

This is an Android continuation of bc95c00. We now call
InputDetector::Update immediately after receiving an input event from
Android instead of periodically calling it in a sleep loop. This
improves detection of very short inputs, which are especially likely to
occur for volume buttons on phones (or at least on my phone) if you
don't intentionally keep them held down.
This commit is contained in:
JosJuice
2025-03-16 11:05:06 +01:00
parent 1515cf6ccd
commit 516c1314d2
10 changed files with 210 additions and 90 deletions

View File

@ -113,6 +113,9 @@ static jclass s_core_device_control_class;
static jfieldID s_core_device_control_pointer;
static jmethodID s_core_device_control_constructor;
static jclass s_input_detector_class;
static jfieldID s_input_detector_pointer;
static jmethodID s_runnable_run;
namespace IDCache
@ -525,6 +528,16 @@ jmethodID GetCoreDeviceControlConstructor()
return s_core_device_control_constructor;
}
jclass GetInputDetectorClass()
{
return s_input_detector_class;
}
jfieldID GetInputDetectorPointer()
{
return s_input_detector_pointer;
}
jmethodID GetRunnableRun()
{
return s_runnable_run;
@ -746,6 +759,12 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
"(Lorg/dolphinemu/dolphinemu/features/input/model/CoreDevice;J)V");
env->DeleteLocalRef(core_device_control_class);
const jclass input_detector_class =
env->FindClass("org/dolphinemu/dolphinemu/features/input/model/InputDetector");
s_input_detector_class = reinterpret_cast<jclass>(env->NewGlobalRef(input_detector_class));
s_input_detector_pointer = env->GetFieldID(input_detector_class, "pointer", "J");
env->DeleteLocalRef(input_detector_class);
const jclass runnable_class = env->FindClass("java/lang/Runnable");
s_runnable_run = env->GetMethodID(runnable_class, "run", "()V");
env->DeleteLocalRef(runnable_class);
@ -779,10 +798,11 @@ JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved)
env->DeleteGlobalRef(s_control_class);
env->DeleteGlobalRef(s_control_group_class);
env->DeleteGlobalRef(s_control_reference_class);
env->DeleteGlobalRef(s_control_group_container_class);
env->DeleteGlobalRef(s_emulated_controller_class);
env->DeleteGlobalRef(s_numeric_setting_class);
env->DeleteGlobalRef(s_core_device_class);
env->DeleteGlobalRef(s_core_device_control_class);
env->DeleteGlobalRef(s_control_group_container_class);
env->DeleteGlobalRef(s_input_detector_class);
}
}

View File

@ -112,6 +112,9 @@ jclass GetCoreDeviceControlClass();
jfieldID GetCoreDeviceControlPointer();
jmethodID GetCoreDeviceControlConstructor();
jclass GetInputDetectorClass();
jfieldID GetInputDetectorPointer();
jmethodID GetRunnableRun();
} // namespace IDCache

View File

@ -26,6 +26,7 @@ add_library(main SHARED
Input/CoreDevice.h
Input/EmulatedController.cpp
Input/EmulatedController.h
Input/InputDetector.cpp
Input/InputOverrider.cpp
Input/MappingCommon.cpp
Input/NumericSetting.cpp

View File

@ -0,0 +1,79 @@
// Copyright 2025 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <jni.h>
#include "InputCommon/ControllerInterface/ControllerInterface.h"
#include "InputCommon/ControllerInterface/CoreDevice.h"
#include "InputCommon/ControllerInterface/MappingCommon.h"
#include "jni/AndroidCommon/AndroidCommon.h"
#include "jni/AndroidCommon/IDCache.h"
namespace
{
constexpr auto INPUT_DETECT_INITIAL_TIME = std::chrono::seconds(3);
constexpr auto INPUT_DETECT_CONFIRMATION_TIME = std::chrono::milliseconds(0);
constexpr auto INPUT_DETECT_MAXIMUM_TIME = std::chrono::seconds(5);
} // namespace
static ciface::Core::InputDetector* GetPointer(JNIEnv* env, jobject obj)
{
return reinterpret_cast<ciface::Core::InputDetector*>(
env->GetLongField(obj, IDCache::GetInputDetectorPointer()));
}
extern "C" {
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_InputDetector_finalize(JNIEnv* env, jobject obj)
{
delete GetPointer(env, obj);
}
JNIEXPORT jlong JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_InputDetector_createNew(JNIEnv*, jobject)
{
return reinterpret_cast<jlong>(new ciface::Core::InputDetector);
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_InputDetector_start(
JNIEnv* env, jobject obj, jstring j_default_device, jboolean all_devices)
{
std::vector<std::string> device_strings;
if (all_devices)
device_strings = g_controller_interface.GetAllDeviceStrings();
else
device_strings = {GetJString(env, j_default_device)};
GetPointer(env, obj)->Start(g_controller_interface, device_strings);
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_InputDetector_update(JNIEnv* env, jobject obj)
{
GetPointer(env, obj)->Update(INPUT_DETECT_INITIAL_TIME, INPUT_DETECT_CONFIRMATION_TIME,
INPUT_DETECT_MAXIMUM_TIME);
}
JNIEXPORT jboolean JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_InputDetector_isComplete(JNIEnv* env,
jobject obj)
{
return GetPointer(env, obj)->IsComplete();
}
JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_InputDetector_takeResults(
JNIEnv* env, jobject obj, jstring j_default_device)
{
ciface::Core::DeviceQualifier default_device;
default_device.FromString(GetJString(env, j_default_device));
auto detections = GetPointer(env, obj)->TakeResults();
ciface::MappingCommon::RemoveSpuriousTriggerCombinations(&detections);
return ToJString(env, ciface::MappingCommon::BuildExpression(detections, default_device,
ciface::MappingCommon::Quote::On));
}
}

View File

@ -17,42 +17,9 @@
#include "InputCommon/ControllerInterface/ControllerInterface.h"
#include "InputCommon/ControllerInterface/MappingCommon.h"
#include "jni/AndroidCommon/AndroidCommon.h"
#include "jni/Input/EmulatedController.h"
namespace
{
constexpr auto INPUT_DETECT_INITIAL_TIME = std::chrono::seconds(3);
constexpr auto INPUT_DETECT_CONFIRMATION_TIME = std::chrono::milliseconds(0);
constexpr auto INPUT_DETECT_MAXIMUM_TIME = std::chrono::seconds(5);
} // namespace
extern "C" {
JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_MappingCommon_detectInput(
JNIEnv* env, jclass, jobject j_emulated_controller, jboolean all_devices)
{
ControllerEmu::EmulatedController* emulated_controller =
EmulatedControllerFromJava(env, j_emulated_controller);
const ciface::Core::DeviceQualifier default_device = emulated_controller->GetDefaultDevice();
std::vector<std::string> device_strings;
if (all_devices)
device_strings = g_controller_interface.GetAllDeviceStrings();
else
device_strings = {default_device.ToString()};
auto detections =
g_controller_interface.DetectInput(device_strings, INPUT_DETECT_INITIAL_TIME,
INPUT_DETECT_CONFIRMATION_TIME, INPUT_DETECT_MAXIMUM_TIME);
ciface::MappingCommon::RemoveSpuriousTriggerCombinations(&detections);
return ToJString(env, ciface::MappingCommon::BuildExpression(detections, default_device,
ciface::MappingCommon::Quote::On));
}
JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_MappingCommon_getExpressionForControl(
JNIEnv* env, jclass, jstring j_control, jstring j_device, jstring j_default_device)