From 43d3766468d94d6d99b9bec269e2b18e45349bb9 Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Sat, 9 May 2026 17:49:19 +0300 Subject: [PATCH] input: add android input APIs --- src/input/CMakeLists.txt | 15 ++ src/input/ControllerFactory.cpp | 17 ++ src/input/InputManager.cpp | 4 + src/input/InputManager.h | 5 + src/input/api/Android/AndroidController.cpp | 112 ++++++++++ src/input/api/Android/AndroidController.h | 41 ++++ .../api/Android/AndroidControllerProvider.cpp | 18 ++ .../api/Android/AndroidControllerProvider.h | 16 ++ src/input/api/Android/ControllerManager.cpp | 192 ++++++++++++++++++ src/input/api/Android/ControllerManager.h | 97 +++++++++ src/input/api/Device/DeviceController.cpp | 95 +++++++++ src/input/api/Device/DeviceController.h | 57 ++++++ .../api/Device/DeviceControllerProvider.cpp | 7 + .../api/Device/DeviceControllerProvider.h | 16 ++ src/input/api/InputAPI.h | 12 ++ 15 files changed, 704 insertions(+) create mode 100644 src/input/api/Android/AndroidController.cpp create mode 100644 src/input/api/Android/AndroidController.h create mode 100644 src/input/api/Android/AndroidControllerProvider.cpp create mode 100644 src/input/api/Android/AndroidControllerProvider.h create mode 100644 src/input/api/Android/ControllerManager.cpp create mode 100644 src/input/api/Android/ControllerManager.h create mode 100644 src/input/api/Device/DeviceController.cpp create mode 100644 src/input/api/Device/DeviceController.h create mode 100644 src/input/api/Device/DeviceControllerProvider.cpp create mode 100644 src/input/api/Device/DeviceControllerProvider.h diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 4978ef0a..da2d9cfd 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -88,6 +88,21 @@ if (SUPPORTS_WIIMOTE) endif () +if(ANDROID) + target_sources(CemuInput PRIVATE + api/Android/AndroidController.cpp + api/Android/AndroidController.h + api/Android/AndroidControllerProvider.cpp + api/Android/AndroidControllerProvider.h + api/Android/ControllerManager.cpp + api/Android/ControllerManager.h + api/Device/DeviceController.cpp + api/Device/DeviceController.h + api/Device/DeviceControllerProvider.cpp + api/Device/DeviceControllerProvider.h + ) +endif() + if(ENABLE_SDL) target_sources(CemuInput PRIVATE api/SDL/SDLController.cpp diff --git a/src/input/ControllerFactory.cpp b/src/input/ControllerFactory.cpp index 3827e1d4..1382db4c 100644 --- a/src/input/ControllerFactory.cpp +++ b/src/input/ControllerFactory.cpp @@ -22,6 +22,11 @@ #include "input/api/SDL/SDLController.h" #endif +#if BOOST_PLAT_ANDROID +#include "input/api/Android/AndroidController.h" +#include "input/api/Device/DeviceController.h" +#endif + ControllerPtr ControllerFactory::CreateController(InputAPI::Type api, std::string_view uuid, std::string_view display_name) { @@ -104,6 +109,12 @@ ControllerPtr ControllerFactory::CreateController(InputAPI::Type api, std::strin const auto index = ConvertString(uuid); return std::make_shared(index); } +#endif +#if BOOST_PLAT_ANDROID + case InputAPI::Android: + return std::make_shared(uuid, display_name); + case InputAPI::Device: + return std::make_shared(); #endif default: throw std::invalid_argument(fmt::format("unhandled controller api: {}", api)); @@ -175,6 +186,12 @@ ControllerProviderPtr ControllerFactory::CreateControllerProvider(InputAPI::Type #if HAS_WIIMOTE case InputAPI::Wiimote: return std::make_shared(); +#endif +#if BOOST_PLAT_ANDROID + case InputAPI::Android: + return std::make_shared(); + case InputAPI::Device: + return std::make_shared(); #endif default: cemu_assert_debug(false); diff --git a/src/input/InputManager.cpp b/src/input/InputManager.cpp index 0df6b5dd..a052ab3b 100644 --- a/src/input/InputManager.cpp +++ b/src/input/InputManager.cpp @@ -46,6 +46,10 @@ InputManager::InputManager() #if HAS_WIIMOTE create_provider(); #endif +#if BOOST_PLAT_ANDROID + create_provider(); + create_provider(); +#endif m_update_thread_shutdown.store(false); m_update_thread = std::thread(&InputManager::update_thread, this); diff --git a/src/input/InputManager.h b/src/input/InputManager.h index b510474b..68c2ad74 100644 --- a/src/input/InputManager.h +++ b/src/input/InputManager.h @@ -17,6 +17,11 @@ #include "input/api/DSU/DSUControllerProvider.h" #include "input/api/GameCube/GameCubeControllerProvider.h" +#if BOOST_PLAT_ANDROID +#include "input/api/Android/AndroidControllerProvider.h" +#include "input/api/Device/DeviceControllerProvider.h" +#endif + #include "input/emulated/VPADController.h" #include "input/emulated/WPADController.h" diff --git a/src/input/api/Android/AndroidController.cpp b/src/input/api/Android/AndroidController.cpp new file mode 100644 index 00000000..366a797f --- /dev/null +++ b/src/input/api/Android/AndroidController.cpp @@ -0,0 +1,112 @@ +#include "input/api/Android/AndroidController.h" +#include "ControllerManager.h" + +#include + +AndroidController::AndroidController(std::string_view device_descriptor, std::string_view device_name) + : base_type(device_descriptor, device_name) +{ + m_state = ControllerManager::instance().get_state(m_uuid); +} + +ControllerState AndroidController::raw_state() +{ + std::shared_lock lock{m_state->mutex}; + return m_state->inputs; +} + +bool AndroidController::has_rumble() +{ + std::shared_lock lock{m_state->mutex}; + return m_state->hasRumble; +} + +void AndroidController::start_rumble() +{ + if (is_connected() && !has_rumble()) + { + return; + } + + if (m_settings.rumble <= 0 || m_settings.rumble > 1) + { + return; + } + + ControllerManager::instance().vibrate_controller( + m_uuid, + 5000, + static_cast(255.0f * m_settings.rumble)); +} + +void AndroidController::stop_rumble() +{ + if (is_connected() && !has_rumble()) + { + return; + } + + if (m_settings.rumble <= 0) + { + return; + } + + ControllerManager::instance().cancel_controller_vibration(m_uuid); +} + +MotionSample AndroidController::get_motion_sample() +{ + std::shared_lock lock{m_state->mutex}; + return m_state->motionHandler.getMotionSample(); +} + +bool AndroidController::has_motion() +{ + std::shared_lock lock{m_state->mutex}; + return m_state->hasMotion; +} + +bool AndroidController::is_connected() +{ + std::shared_lock lock{m_state->mutex}; + return m_state->isConnected; +} + +std::string AndroidController::get_button_name(uint64 button) const +{ + switch (button) + { + case AKEYCODE_BUTTON_A: return "Button A"; + case AKEYCODE_BUTTON_B: return "Button B"; + case AKEYCODE_BUTTON_C: return "Button C"; + case AKEYCODE_BUTTON_X: return "Button X"; + case AKEYCODE_BUTTON_Y: return "Button Y"; + case AKEYCODE_BUTTON_Z: return "Button Z"; + case AKEYCODE_BUTTON_L1: return "Button L1"; + case AKEYCODE_BUTTON_R1: return "Button R1"; + case AKEYCODE_BUTTON_L2: return "Button L2"; + case AKEYCODE_BUTTON_R2: return "Button R2"; + case AKEYCODE_BUTTON_THUMBL: return "Button ThumbL"; + case AKEYCODE_BUTTON_THUMBR: return "Button ThumbR"; + case AKEYCODE_BUTTON_START: return "Button Start"; + case AKEYCODE_BUTTON_SELECT: return "Button Select"; + case AKEYCODE_BUTTON_MODE: return "Button Mode"; + case AKEYCODE_BUTTON_1: return "Button 1"; + case AKEYCODE_BUTTON_2: return "Button 2"; + case AKEYCODE_BUTTON_3: return "Button 3"; + case AKEYCODE_BUTTON_4: return "Button 4"; + case AKEYCODE_BUTTON_5: return "Button 5"; + case AKEYCODE_BUTTON_6: return "Button 6"; + case AKEYCODE_BUTTON_7: return "Button 7"; + case AKEYCODE_BUTTON_8: return "Button 8"; + case AKEYCODE_BUTTON_9: return "Button 9"; + case AKEYCODE_BUTTON_10: return "Button 10"; + case AKEYCODE_BUTTON_11: return "Button 11"; + case AKEYCODE_BUTTON_12: return "Button 12"; + case AKEYCODE_BUTTON_13: return "Button 13"; + case AKEYCODE_BUTTON_14: return "Button 14"; + case AKEYCODE_BUTTON_15: return "Button 15"; + case AKEYCODE_BUTTON_16: return "Button 16"; + default: return base_type::get_button_name(button); + } +} diff --git a/src/input/api/Android/AndroidController.h b/src/input/api/Android/AndroidController.h new file mode 100644 index 00000000..ba0663b4 --- /dev/null +++ b/src/input/api/Android/AndroidController.h @@ -0,0 +1,41 @@ +#pragma once + +#include "input/api/Android/AndroidControllerProvider.h" +#include "input/api/Controller.h" +#include "input/motion/MotionSample.h" +#include "ControllerManager.h" + +class AndroidController : public Controller +{ + public: + AndroidController(std::string_view device_descriptor, std::string_view device_name); + + std::string_view api_name() const override + { + static_assert(to_string(InputAPI::Android) == "Android"); + return to_string(InputAPI::Android); + } + + InputAPI::Type api() const override + { + return InputAPI::Android; + } + + bool is_connected() override; + + bool has_motion() override; + + MotionSample get_motion_sample() override; + + bool has_rumble() override; + + void start_rumble() override; + void stop_rumble() override; + + std::string get_button_name(uint64 button) const override; + + ControllerState raw_state() override; + + private: + std::shared_ptr m_state; +}; diff --git a/src/input/api/Android/AndroidControllerProvider.cpp b/src/input/api/Android/AndroidControllerProvider.cpp new file mode 100644 index 00000000..b266a799 --- /dev/null +++ b/src/input/api/Android/AndroidControllerProvider.cpp @@ -0,0 +1,18 @@ +#include "input/api/Android/AndroidControllerProvider.h" + +#include "ControllerManager.h" +#include "AndroidController.h" + +std::vector> AndroidControllerProvider::get_controllers() +{ + std::vector> result; + auto controllers = ControllerManager::instance().get_controllers(); + result.reserve(controllers.size()); + + for (const auto& controller : controllers) + { + result.push_back(std::make_shared(controller.descriptor, controller.name)); + } + + return result; +} diff --git a/src/input/api/Android/AndroidControllerProvider.h b/src/input/api/Android/AndroidControllerProvider.h new file mode 100644 index 00000000..f190a561 --- /dev/null +++ b/src/input/api/Android/AndroidControllerProvider.h @@ -0,0 +1,16 @@ +#pragma once + +#include "input/api/ControllerProvider.h" + +class AndroidControllerProvider : public ControllerProviderBase +{ + public: + inline static InputAPI::Type kAPIType = InputAPI::Android; + + InputAPI::Type api() const override + { + return kAPIType; + } + + std::vector> get_controllers() override; +}; diff --git a/src/input/api/Android/ControllerManager.cpp b/src/input/api/Android/ControllerManager.cpp new file mode 100644 index 00000000..14b3f36f --- /dev/null +++ b/src/input/api/Android/ControllerManager.cpp @@ -0,0 +1,192 @@ +#include "ControllerManager.h" + +#include "api/Controller.h" + +ControllerManager& ControllerManager::instance() +{ + static ControllerManager instance; + return instance; +} + +void ControllerManager::vibrate_controller(std::string_view deviceDescriptor, sint64 milliseconds, sint32 amplitude) +{ + std::scoped_lock lock{m_mutex}; + + if (!m_controllerCallbacks) + { + return; + } + + m_controllerCallbacks->vibrate_controller(deviceDescriptor, milliseconds, amplitude); +} + +void ControllerManager::cancel_controller_vibration(std::string_view deviceDescriptor) +{ + std::scoped_lock lock{m_mutex}; + + if (!m_controllerCallbacks) + { + return; + } + + m_controllerCallbacks->cancel_controller_vibration(deviceDescriptor); +} + +void ControllerManager::set_controller_callbacks(std::shared_ptr callbacks) +{ + std::scoped_lock lock{m_mutex}; + m_controllerCallbacks = std::move(callbacks); +} + +void ControllerManager::set_controllers(const std::vector& controllers) +{ + std::scoped_lock lock{m_mutex}; + + for (auto& [descriptor, controller] : m_states) + { + std::scoped_lock stateLock{controller->mutex}; + controller->isConnected = false; + } + + for (const auto& ctrl : controllers) + { + auto it = m_states.find(ctrl.descriptor); + + if (it != m_states.end()) + { + auto statePtr = it->second; + auto& state = *statePtr; + + std::scoped_lock stateLock{state.mutex}; + + state.name = ctrl.name; + state.hasRumble = ctrl.hasRumble; + state.hasMotion = ctrl.hasMotion; + state.isConnected = true; + } + } + + m_controllers = controllers; +} + +std::vector ControllerManager::get_controllers() +{ + std::scoped_lock lock{m_mutex}; + return m_controllers; +} + +std::shared_ptr ControllerManager::get_state(const std::string& deviceDescriptor) +{ + std::scoped_lock lock{m_mutex}; + + auto it = m_states.find(deviceDescriptor); + + if (it != m_states.end()) + { + return it->second; + } + + auto state = std::make_shared(); + + m_states[deviceDescriptor] = state; + + return state; +} + +void ControllerManager::process_key_event(const std::string& deviceDescriptor, int nativeKeyCode, bool isPressed) +{ + update_state(deviceDescriptor, [&](auto& state) { + state.inputs.buttons.SetButtonState(nativeKeyCode, isPressed); + }); +} + +void ControllerManager::process_axis_event(const std::string& deviceDescriptor, int nativeAxisCode, float value) +{ + update_state(deviceDescriptor, [&](auto& state) { + auto& controllerState = state.inputs; + + switch (nativeAxisCode) + { + case AMOTION_EVENT_AXIS_X: + controllerState.axis.x = value; + break; + case AMOTION_EVENT_AXIS_Y: + controllerState.axis.y = value; + break; + case AMOTION_EVENT_AXIS_RX: + case AMOTION_EVENT_AXIS_Z: + controllerState.rotation.x = value; + break; + case AMOTION_EVENT_AXIS_RY: + case AMOTION_EVENT_AXIS_RZ: + controllerState.rotation.y = value; + break; + case AMOTION_EVENT_AXIS_LTRIGGER: + controllerState.trigger.x = value; + break; + case AMOTION_EVENT_AXIS_RTRIGGER: + controllerState.trigger.y = value; + break; + case AMOTION_EVENT_AXIS_HAT_X: + if (value == 0.0f) + { + controllerState.buttons.SetButtonState(kButtonRight, false); + controllerState.buttons.SetButtonState(kButtonLeft, false); + return; + } + else if (value > 0.0f) + controllerState.buttons.SetButtonState(kButtonRight, true); + else + controllerState.buttons.SetButtonState(kButtonLeft, true); + break; + case AMOTION_EVENT_AXIS_HAT_Y: + if (value == 0.0f) + { + controllerState.buttons.SetButtonState(kButtonUp, false); + controllerState.buttons.SetButtonState(kButtonDown, false); + return; + } + else if (value > 0.0f) + controllerState.buttons.SetButtonState(kButtonDown, true); + else + controllerState.buttons.SetButtonState(kButtonUp, true); + break; + default: + break; + } + }); +} + +void ControllerManager::process_motion( + const std::string& deviceDescriptor, + std::chrono::steady_clock::time_point timestamp, + float gyroX, float gyroY, float gyroZ, + float accelX, float accelY, float accelZ) +{ + update_state(deviceDescriptor, [&](auto& state) { + using namespace std::chrono_literals; + + const auto dif = timestamp - state.lastMotionTimestamp; + state.lastMotionTimestamp = timestamp; + + if (dif <= 0ms) + { + return; + } + + if (dif >= 10s) + { + return; + } + + constexpr auto maxDuration = 1s; + auto clamped = std::min>(dif, maxDuration); + + float deltaSeconds = std::chrono::duration_cast>(dif).count(); + + state.motionHandler.processMotionSample( + deltaSeconds, + gyroX, gyroY, gyroZ, + accelX, accelY, accelZ); + }); +} diff --git a/src/input/api/Android/ControllerManager.h b/src/input/api/Android/ControllerManager.h new file mode 100644 index 00000000..31b07c1e --- /dev/null +++ b/src/input/api/Android/ControllerManager.h @@ -0,0 +1,97 @@ +#pragma once + +#include "input/api/ControllerState.h" +#include "input/motion/MotionHandler.h" + +#include + +class ControllerManager +{ + public: + struct ControllerRuntimeState + { + bool isConnected{}; + + std::string name{}; + + ControllerState inputs{}; + + bool hasRumble{}; + bool hasMotion{}; + + std::chrono::steady_clock::time_point lastMotionTimestamp{}; + WiiUMotionHandler motionHandler{}; + + std::shared_mutex mutex{}; + }; + + struct ControllerInfo + { + sint32 id{}; + std::string descriptor{}; + std::string name{}; + bool hasRumble{}; + bool hasMotion{}; + }; + + class ControllerCallbacks + { + public: + virtual void vibrate_controller(std::string_view deviceDescriptor, sint64 milliseconds, sint32 amplitude) = 0; + virtual void cancel_controller_vibration(std::string_view deviceDescriptor) = 0; + }; + + static ControllerManager& instance(); + + void vibrate_controller(std::string_view deviceDescriptor, sint64 milliseconds, sint32 amplitude); + + void cancel_controller_vibration(std::string_view deviceDescriptor); + + void set_controller_callbacks(std::shared_ptr callbacks); + + void set_controllers(const std::vector& controllers); + + std::vector get_controllers(); + + std::shared_ptr get_state(const std::string& deviceDescriptor); + + void process_key_event(const std::string& deviceDescriptor, int nativeKeyCode, bool isPressed); + + void process_axis_event(const std::string& deviceDescriptor, int nativeAxisCode, float value); + + void process_motion( + const std::string& deviceDescriptor, + std::chrono::steady_clock::time_point timestamp, + float gyroX, + float gyroY, + float gyroZ, + float accelX, + float accelY, + float accelZ); + + private: + ControllerManager() = default; + + inline void update_state(const std::string& deviceDescriptor, std::invocable auto f) + { + std::scoped_lock lock{m_mutex}; + + auto it = m_states.find(deviceDescriptor); + + if (it == m_states.end()) + { + return; + } + + auto statePtr = it->second; + auto& state = *statePtr; + std::scoped_lock stateLock{state.mutex}; + + f(state); + } + + std::mutex m_mutex; + std::shared_ptr m_controllerCallbacks; + std::vector m_controllers; + std::unordered_map> m_states; +}; diff --git a/src/input/api/Device/DeviceController.cpp b/src/input/api/Device/DeviceController.cpp new file mode 100644 index 00000000..0c10aaee --- /dev/null +++ b/src/input/api/Device/DeviceController.cpp @@ -0,0 +1,95 @@ +#include "DeviceController.h" + +DeviceController::DeviceController() : base_type("device", "device") +{ +} + +void DeviceController::set_callbacks(std::shared_ptr callbacks) +{ + std::scoped_lock lock(s_mutex); + s_callbacks = std::move(callbacks); +} + +bool DeviceController::is_connected() +{ + return true; +} + +bool DeviceController::has_rumble() +{ + return true; +} + +bool DeviceController::has_motion() +{ + return true; +} + +MotionSample DeviceController::get_motion_sample() +{ + std::scoped_lock lock{s_mutex}; + return s_motion_handler.getMotionSample(); +} + +ControllerState DeviceController::raw_state() +{ + std::scoped_lock lock{s_mutex}; + return s_raw_state; +} + +void DeviceController::start_rumble() +{ + std::scoped_lock lock{s_mutex}; + + if (m_settings.rumble <= 0 || m_settings.rumble > 1 || !s_callbacks) + { + return; + } + + s_callbacks->start_rumble(5000, m_settings.rumble); +} + +void DeviceController::stop_rumble() +{ + std::scoped_lock lock{s_mutex}; + + if (s_callbacks) + { + s_callbacks->stop_rumble(); + } +} + +void DeviceController::process_motion(std::chrono::steady_clock::time_point timestamp, float gx, float gy, float gz, float accx, float accy, float accz) +{ + using namespace std::chrono_literals; + std::scoped_lock lock{s_mutex}; + + auto dif = timestamp - s_last_motion_timestamp; + s_last_motion_timestamp = timestamp; + + if (dif <= 0ms) + { + return; + } + + if (dif >= 10s) + { + return; + } + + constexpr auto maxDuration = 1s; + auto clamped = std::min>(dif, maxDuration); + + float deltaSeconds = std::chrono::duration_cast>(dif).count(); + + s_motion_handler.processMotionSample( + deltaSeconds, + gx, gy, gz, + accx, accy, accz); +} + +void DeviceController::set_motion_enabled(bool enabled) +{ + std::scoped_lock lock{s_mutex}; + s_has_motion = enabled; +} diff --git a/src/input/api/Device/DeviceController.h b/src/input/api/Device/DeviceController.h new file mode 100644 index 00000000..b4ed43b8 --- /dev/null +++ b/src/input/api/Device/DeviceController.h @@ -0,0 +1,57 @@ +#pragma once + +#include "input/api/InputAPI.h" +#include "input/api/Controller.h" +#include "DeviceControllerProvider.h" + +class DeviceController : public Controller +{ + public: + DeviceController(); + + class ControllerCallbacks + { + public: + virtual void start_rumble(sint64 duration, float rumble) = 0; + virtual void stop_rumble() = 0; + }; + + std::string_view api_name() const override + { + static_assert(to_string(InputAPI::Device) == "Device"); + return to_string(InputAPI::Device); + } + + InputAPI::Type api() const override + { + return InputAPI::Device; + } + + static void set_callbacks(std::shared_ptr callbacks); + + bool is_connected() override; + + bool has_rumble() override; + + bool has_motion() override; + + MotionSample get_motion_sample() override; + + ControllerState raw_state() override; + + void start_rumble() override; + + void stop_rumble() override; + + static void process_motion(std::chrono::steady_clock::time_point timestamp, float gx, float gy, float gz, float accx, float accy, float accz); + + static void set_motion_enabled(bool enabled); + + private: + inline static bool s_has_motion{}; + inline static std::mutex s_mutex{}; + inline static WiiUMotionHandler s_motion_handler{}; + inline static std::chrono::steady_clock::time_point s_last_motion_timestamp{}; + inline static std::shared_ptr s_callbacks{}; + inline static ControllerState s_raw_state{}; +}; diff --git a/src/input/api/Device/DeviceControllerProvider.cpp b/src/input/api/Device/DeviceControllerProvider.cpp new file mode 100644 index 00000000..bfce49e3 --- /dev/null +++ b/src/input/api/Device/DeviceControllerProvider.cpp @@ -0,0 +1,7 @@ +#include "DeviceControllerProvider.h" +#include "DeviceController.h" + +std::vector> DeviceControllerProvider::get_controllers() +{ + return {std::make_shared()}; +} diff --git a/src/input/api/Device/DeviceControllerProvider.h b/src/input/api/Device/DeviceControllerProvider.h new file mode 100644 index 00000000..2ab80200 --- /dev/null +++ b/src/input/api/Device/DeviceControllerProvider.h @@ -0,0 +1,16 @@ +#pragma once + +#include "input/api/ControllerProvider.h" + +class DeviceControllerProvider : public ControllerProviderBase +{ + public: + inline static InputAPI::Type kAPIType = InputAPI::Device; + + InputAPI::Type api() const override + { + return kAPIType; + } + + std::vector> get_controllers() override; +}; diff --git a/src/input/api/InputAPI.h b/src/input/api/InputAPI.h index a5f1f926..ab68ae67 100644 --- a/src/input/api/InputAPI.h +++ b/src/input/api/InputAPI.h @@ -14,6 +14,10 @@ namespace InputAPI GameCube, Wiimote, + Android, + + Device, + WGIGamepad, WGIRawController, @@ -42,6 +46,10 @@ namespace InputAPI return "WGIRawController"; case SDLController: return "SDLController"; + case Android: + return "Android"; + case Device: + return "Device"; default: break; } @@ -65,6 +73,10 @@ namespace InputAPI return DSUClient; else if (str == to_string(SDLController)) return SDLController; + else if (str == to_string(Android)) + return Android; + else if (str == to_string(Device)) + return Device; else if (str == "DSU") // legacy return DSUClient;