From fbf0cc2df454687501ee08b06a989fb84c7723b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 21 Oct 2026 12:20:20 +0200 Subject: [PATCH] Android: Detect the accelerometer and gyro instead of assuming them SYSPROP_HAS_ACCELEROMETER was true for every phone and tablet, whether or not it had the sensor. The activity now tells the native side what the sensor manager found, which also answers the new SYSPROP_HAS_GYRO. VR headsets still report neither. Every other platform returns false for the gyro for now. The "Use gyroscope" checkbox uses the new property instead of waiting for gyro data to show up. Co-Authored-By: Claude Opus 5.5 (1M context) --- Common/System/System.h | 1 + Core/TiltEventProcessor.cpp | 4 ---- Core/TiltEventProcessor.h | 2 -- UI/TiltAnalogSettingsScreen.cpp | 3 ++- android/jni/app-android.cpp | 13 ++++++++++++- android/src/org/ppsspp/ppsspp/PpssppActivity.java | 7 ++++++- 6 files changed, 21 insertions(+), 9 deletions(-) diff --git a/Common/System/System.h b/Common/System/System.h index 43b9edaf90..741ccce696 100644 --- a/Common/System/System.h +++ b/Common/System/System.h @@ -163,6 +163,7 @@ enum SystemProperty { SYSPROP_HAS_KEYBOARD, SYSPROP_KEYBOARD_IS_SOFT, SYSPROP_HAS_ACCELEROMETER, // Used to enable/disable tilt input settings + SYSPROP_HAS_GYRO, SYSPROP_HAS_OPEN_DIRECTORY, SYSPROP_HAS_LOGIN_DIALOG, SYSPROP_HAS_TEXT_CLIPBOARD, diff --git a/Core/TiltEventProcessor.cpp b/Core/TiltEventProcessor.cpp index 800766a543..7bb9ebe0dc 100644 --- a/Core/TiltEventProcessor.cpp +++ b/Core/TiltEventProcessor.cpp @@ -371,10 +371,6 @@ bool GyroActive() { return g_seenGyro && time_now_d() - g_lastGyroArrival < 0.2; } -bool HasSeenGyro() { - return g_seenGyro; -} - void ResetTiltEvents() { // Reset the buttons we have marked pressed. __CtrlUpdateButtons(0, tiltButtonsDown); diff --git a/Core/TiltEventProcessor.h b/Core/TiltEventProcessor.h index 724d18c12b..b3d0b9e612 100644 --- a/Core/TiltEventProcessor.h +++ b/Core/TiltEventProcessor.h @@ -16,8 +16,6 @@ void ProcessAccelerometerForFusion(float x, float y, float z); bool ProcessGyro(float x, float y, float z, double timestamp, Lin::Vec3 *gravity); // True if gyro data arrived recently, in which case the accelerometer shouldn't drive tilt directly. bool GyroActive(); -// Whether we've ever seen gyro data, to decide whether to show the gyro setting. -bool HasSeenGyro(); float GetCurrentYAngle(); diff --git a/UI/TiltAnalogSettingsScreen.cpp b/UI/TiltAnalogSettingsScreen.cpp index 43ebc17ebe..6b10eecdde 100644 --- a/UI/TiltAnalogSettingsScreen.cpp +++ b/UI/TiltAnalogSettingsScreen.cpp @@ -22,6 +22,7 @@ #include "Common/Math/math_util.h" #include "Common/Log.h" +#include "Common/System/System.h" #include "Common/Data/Text/I18n.h" #include "Common/UI/ScrollView.h" #include "Common/UI/PopupScreens.h" @@ -113,7 +114,7 @@ void TiltAnalogSettingsScreen::CreateSettingsViews(UI::ViewGroup *settings) { RecreateViews(); }); typeChoice->SetEnabledPtr(&g_Config.bTiltInputEnabled); - if (TiltEventProcessor::HasSeenGyro()) { + if (System_GetPropertyBool(SYSPROP_HAS_GYRO)) { settings->Add(new CheckBox(&g_Config.bTiltUseGyro, co->T("Use gyroscope")))->SetEnabledPtr(&g_Config.bTiltInputEnabled); } settings->Add(new ItemHeader(co->T("Calibration"))); diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 880ceaf273..587140580b 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -164,6 +164,8 @@ static float g_safeInsetRight = 0.0; static float g_safeInsetTop = 0.0; static float g_safeInsetBottom = 0.0; static bool g_hasCameraCutout = false; +static bool g_hasAccelerometer = false; +static bool g_hasGyro = false; // Global ref. Written on the UI thread under g_activityLock. Other threads go through Android_GetActivity. static jobject ppssppActivity; @@ -465,7 +467,10 @@ bool System_GetPropertyBool(SystemProperty prop) { case SYSPROP_HAS_KEYBOARD: return deviceType != DEVICE_TYPE_VR; case SYSPROP_HAS_ACCELEROMETER: - return deviceType == DEVICE_TYPE_MOBILE; + // A headset has the sensors, but tilting it isn't tilt control. + return g_hasAccelerometer && deviceType != DEVICE_TYPE_VR; + case SYSPROP_HAS_GYRO: + return g_hasGyro && deviceType != DEVICE_TYPE_VR; case SYSPROP_CAN_CREATE_SHORTCUT: return false; // We can't create shortcuts directly from game code, but we can from the Android UI. case SYSPROP_DISPLAY_HAS_CAMERA_CUTOUT: @@ -1299,6 +1304,12 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_sendMessageFromJava(JNI g_safeInsetBottom = (float)bottom; g_hasCameraCutout = cutout != 0; } + } else if (msg == "sensors") { + int accelerometer, gyro; + if (2 == sscanf(prm.c_str(), "%d:%d", &accelerometer, &gyro)) { + g_hasAccelerometer = accelerometer != 0; + g_hasGyro = gyro != 0; + } } else if (msg == "inputDeviceConnectedID") { nextInputDeviceID = (InputDeviceID)parseLong(prm); } else if (msg == "inputDeviceConnected") { diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index 44a0cb338c..972fa6a53a 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -726,6 +726,9 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList mSensorManager = (SensorManager)getSystemService(Activity.SENSOR_SERVICE); mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); + String sensors = (mAccelerometer != null ? "1" : "0") + ":" + (mGyroscope != null ? "1" : "0"); + Log.i(TAG, "Sensors (accelerometer:gyro): " + sensors); + NativeApp.sendMessageFromJava("sensors", sensors); surfaceManager = new NativeSurfaceManager(this); TextRenderer.init(this); @@ -1067,7 +1070,9 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList NativeApp.resume(); } updateAudioFocus(this.audioManager, this.audioFocusChangeListener); - mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME); + if (mAccelerometer != null) { + mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME); + } if (mGyroscope != null) { mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME); }