From ddb674a9b9d2453acef5cc65ad1793bb96067c73 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Tue, 1 Sep 2026 21:20:38 -0500 Subject: [PATCH] Add support for Android 16.1 keyboard capture Fixes #975 Fixes #1281 Fixes #1505 --- app/src/main/AndroidManifest.xml | 1 + app/src/main/java/com/limelight/Game.java | 53 +++++++++++++---------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7be9a20bf..d916899e6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,6 +6,7 @@ + diff --git a/app/src/main/java/com/limelight/Game.java b/app/src/main/java/com/limelight/Game.java index 7dbfc761c..8d369b3ea 100644 --- a/app/src/main/java/com/limelight/Game.java +++ b/app/src/main/java/com/limelight/Game.java @@ -673,31 +673,38 @@ public class Game extends Activity implements SurfaceHolder.Callback, } public void setMetaKeyCaptureState(boolean enabled) { - // This uses custom APIs present on some Samsung devices to allow capture of - // meta key events while streaming. - try { - Class semWindowManager = Class.forName("com.samsung.android.view.SemWindowManager"); - Method getInstanceMethod = semWindowManager.getMethod("getInstance"); - Object manager = getInstanceMethod.invoke(null); + // Android has native keyboard capture support starting in API 36.1 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA && Build.VERSION.SDK_INT_FULL >= Build.VERSION_CODES_FULL.BAKLAVA_1) { + WindowManager.LayoutParams windowLayoutParams = getWindow().getAttributes(); + windowLayoutParams.setKeyboardCaptureEnabled(enabled); + getWindow().setAttributes(windowLayoutParams); + } + else { + // This uses custom APIs present on some Samsung devices to allow capture of + // meta key events while streaming. + try { + Class semWindowManager = Class.forName("com.samsung.android.view.SemWindowManager"); + Method getInstanceMethod = semWindowManager.getMethod("getInstance"); + Object manager = getInstanceMethod.invoke(null); - if (manager != null) { - Class[] parameterTypes = new Class[2]; - parameterTypes[0] = ComponentName.class; - parameterTypes[1] = boolean.class; - Method requestMetaKeyEventMethod = semWindowManager.getDeclaredMethod("requestMetaKeyEvent", parameterTypes); - requestMetaKeyEventMethod.invoke(manager, this.getComponentName(), enabled); + if (manager != null) { + Class[] parameterTypes = new Class[2]; + parameterTypes[0] = ComponentName.class; + parameterTypes[1] = boolean.class; + Method requestMetaKeyEventMethod = semWindowManager.getDeclaredMethod("requestMetaKeyEvent", parameterTypes); + requestMetaKeyEventMethod.invoke(manager, this.getComponentName(), enabled); + } + else { + LimeLog.warning("SemWindowManager.getInstance() returned null"); + } + } catch (ClassNotFoundException ignored) { + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); } - else { - LimeLog.warning("SemWindowManager.getInstance() returned null"); - } - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); } }