Repository: ReactiveCircus/android-emulator-runner (js)

Issue:
Enabling Hardware Keyboard Input
Hi, is it possible to implement an option to abilitate/disable hardware keyboard input to the emulator? It is as simple as making a field from  ``` hw.keyboard=no``` to ``` hw.keyboard=yes``` in the config.ini file.
Thanks.


Pull request description:
Add support for enabling hardware keyboard.
Resolves #207.

This is basically taken from the Doist fork [here](https://github.com/Doist/android-emulator-runner/commit/4b6ca99f0d657662beca3eb0c22d8e254fbd5b31).

Patch:
diff --git a/README.md b/README.md
index b566855..29161c3 100644
--- a/README.md
+++ b/README.md
@@ -155,6 +155,7 @@ jobs:
 | `disable-animations` | Optional | `true` | Whether to disable animations - `true` or `false`. |
 | `disable-spellchecker` | Optional | `false` | Whether to disable spellchecker - `true` or `false`. |
 | `disable-linux-hw-accel` | Optional | `true` | Whether to disable hardware acceleration on Linux machines - `true` or `false`. Note that this is true by default as Github-hosted Linux runners do not support hardware acceleration. |
+| `enable-hw-keyboard` | Optional | `false` | Whether to enable hardware keyboard - `true` or `false`. |
 | `emulator-build` | Optional | N/A | Build number of a specific version of the emulator binary to use e.g. `6061023` for emulator v29.3.0.0. |
 | `working-directory` | Optional | `./` | A custom working directory - e.g. `./android` if your root Gradle project is under the `./android` sub-directory within your repository. |
 | `ndk` | Optional | N/A | Version of NDK to install - e.g. `21.0.6113669` |
diff --git a/action.yml b/action.yml
index 704386c..71467db 100644
--- a/action.yml
+++ b/action.yml
@@ -41,6 +41,9 @@ inputs:
   disable-linux-hw-accel:
     description: 'whether to disable hardware acceleration on Linux machines - `true` or `false`'
     default: 'true'
+  enable-hw-keyboard:
+    description: 'whether to enable hardware keyboard - `true` or `false`.'
+    default: 'false'
   emulator-build:
     description: 'build number of a specific version of the emulator binary to use - e.g. `6061023` for emulator v29.3.0.0'
   working-directory:
diff --git a/lib/emulator-manager.js b/lib/emulator-manager.js
index f0cfbd7..197395e 100644
--- a/lib/emulator-manager.js
+++ b/lib/emulator-manager.js
@@ -35,7 +35,7 @@ const EMULATOR_BOOT_TIMEOUT_SECONDS = 600;
 /**
  * Creates and launches a new AVD instance with the specified configurations.
  */
-function launchEmulator(apiLevel, target, arch, profile, cores, ramSize, sdcardPathOrSize, avdName, forceAvdCreation, emulatorOptions, disableAnimations, disableSpellChecker, disableLinuxHardwareAcceleration) {
+function launchEmulator(apiLevel, target, arch, profile, cores, ramSize, sdcardPathOrSize, avdName, forceAvdCreation, emulatorOptions, disableAnimations, disableSpellChecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard) {
     return __awaiter(this, void 0, void 0, function* () {
         // create a new AVD if AVD directory does not already exist or forceAvdCreation is true
         const avdPath = `${process.env.ANDROID_AVD_HOME}/${avdName}.avd`;
@@ -51,6 +51,9 @@ function launchEmulator(apiLevel, target, arch, profile, cores, ramSize, sdcardP
         if (ramSize) {
             yield exec.exec(`sh -c \\"printf 'hw.ramSize=${ramSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
         }
+        if (enableHardwareKeyboard) {
+            yield exec.exec(`sh -c \\"printf 'hw.keyboard=yes\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
+        }
         //turn off hardware acceleration on Linux
         if (process.platform === 'linux' && disableLinuxHardwareAcceleration) {
             console.log('Disabling Linux hardware acceleration.');
@@ -70,7 +73,6 @@ function launchEmulator(apiLevel, target, arch, profile, cores, ramSize, sdcardP
         // wait for emulator to complete booting
         yield waitForDevice();
         yield exec.exec(`adb shell input keyevent 82`);
-        // disable animations
         if (disableAnimations) {
             console.log('Disabling animations.');
             yield exec.exec(`adb shell settings put global window_animation_scale 0.0`);
@@ -80,6 +82,9 @@ function launchEmulator(apiLevel, target, arch, profile, cores, ramSize, sdcardP
         if (disableSpellChecker) {
             yield exec.exec(`adb shell settings put secure spell_checker_enabled 0`);
         }
+        if (enableHardwareKeyboard) {
+            yield exec.exec(`adb shell settings put secure show_ime_with_hard_keyboard 0`);
+        }
     });
 }
 exports.launchEmulator = launchEmulator;
diff --git a/lib/input-validator.js b/lib/input-validator.js
index 69c53f2..2dcda71 100644
--- a/lib/input-validator.js
+++ b/lib/input-validator.js
@@ -1,6 +1,6 @@
 "use strict";
 Object.defineProperty(exports, "__esModule", { value: true });
-exports.checkEmulatorBuild = exports.checkDisableLinuxHardwareAcceleration = exports.checkDisableSpellchecker = exports.checkDisableAnimations = exports.checkForceAvdCreation = exports.checkChannel = exports.checkArch = exports.checkTarget = exports.checkApiLevel = exports.VALID_CHANNELS = exports.VALID_ARCHS = exports.VALID_TARGETS = exports.MIN_API_LEVEL = void 0;
+exports.checkEmulatorBuild = exports.checkEnableHardwareKeyboard = exports.checkDisableLinuxHardwareAcceleration = exports.checkDisableSpellchecker = exports.checkDisableAnimations = exports.checkForceAvdCreation = exports.checkChannel = exports.checkArch = exports.checkTarget = exports.checkApiLevel = exports.VALID_CHANNELS = exports.VALID_ARCHS = exports.VALID_TARGETS = exports.MIN_API_LEVEL = void 0;
 exports.MIN_API_LEVEL = 15;
 exports.VALID_TARGETS = ['default', 'google_apis', 'aosp_atd', 'google_atd', 'google_apis_playstore', 'android-wear', 'android-wear-cn', 'android-tv', 'google-tv'];
 exports.VALID_ARCHS = ['x86', 'x86_64', 'arm64-v8a'];
@@ -56,6 +56,12 @@ function checkDisableLinuxHardwareAcceleration(disableLinuxHardwareAcceleration)
     }
 }
 exports.checkDisableLinuxHardwareAcceleration = checkDisableLinuxHardwareAcceleration;
+function checkEnableHardwareKeyboard(enableHardwareKeyboard) {
+    if (!isValidBoolean(enableHardwareKeyboard)) {
+        throw new Error(`Input for input.enable-hw-keyboard should be either 'true' or 'false'.`);
+    }
+}
+exports.checkEnableHardwareKeyboard = checkEnableHardwareKeyboard;
 function checkEmulatorBuild(emulatorBuild) {
     if (isNaN(Number(emulatorBuild)) || !Number.isInteger(Number(emulatorBuild))) {
         throw new Error(`Unexpected emulator build: '${emulatorBuild}'.`);
diff --git a/lib/main.js b/lib/main.js
index b74dcc3..2a27446 100644
--- a/lib/main.js
+++ b/lib/main.js
@@ -99,6 +99,11 @@ function run() {
             input_validator_1.checkDisableLinuxHardwareAcceleration(disableLinuxHardwareAccelerationInput);
             const disableLinuxHardwareAcceleration = disableLinuxHardwareAccelerationInput === 'true';
             console.log(`disable Linux hardware acceleration: ${disableLinuxHardwareAcceleration}`);
+            // enable hardware keyboard
+            const enableHardwareKeyboardInput = core.getInput('enable-hw-keyboard');
+            input_validator_1.checkEnableHardwareKeyboard(enableHardwareKeyboardInput);
+            const enableHardwareKeyboard = enableHardwareKeyboardInput === 'true';
+            console.log(`enable hardware keyboard: ${enableHardwareKeyboard}`);
             // emulator build
             const emulatorBuildInput = core.getInput('emulator-build');
             if (emulatorBuildInput) {
@@ -139,7 +144,7 @@ function run() {
             // install SDK
             yield sdk_installer_1.installAndroidSdk(apiLevel, target, arch, channelId, emulatorBuild, ndkVersion, cmakeVersion);
             // launch an emulator
-            yield emulator_manager_1.launchEmulator(apiLevel, target, arch, profile, cores, ramSize, sdcardPathOrSize, avdName, forceAvdCreation, emulatorOptions, disableAnimations, disableSpellchecker, disableLinuxHardwareAcceleration);
+            yield emulator_manager_1.launchEmulator(apiLevel, target, arch, profile, cores, ramSize, sdcardPathOrSize, avdName, forceAvdCreation, emulatorOptions, disableAnimations, disableSpellchecker, disableLinuxHardwareAcceleration, enableHardwareKeyboard);
             // execute the custom script
             try {
                 // move to custom working directory if set
diff --git a/src/emulator-manager.ts b/src/emulator-manager.ts
index 5bb83d1..151bb79 100644
--- a/src/emulator-manager.ts
+++ b/src/emulator-manager.ts
@@ -19,7 +19,8 @@ export async function launchEmulator(
   emulatorOptions: string,
   disableAnimations: boolean,
   disableSpellChecker: boolean,
-  disableLinuxHardwareAcceleration: boolean
+  disableLinuxHardwareAcceleration: boolean,
+  enableHardwareKeyboard: boolean
 ): Promise<void> {
   // create a new AVD if AVD directory does not already exist or forceAvdCreation is true
   const avdPath = `${process.env.ANDROID_AVD_HOME}/${avdName}.avd`;
@@ -40,6 +41,10 @@ export async function launchEmulator(
     await exec.exec(`sh -c \\"printf 'hw.ramSize=${ramSize}\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
   }
 
+  if (enableHardwareKeyboard) {
+    await exec.exec(`sh -c \\"printf 'hw.keyboard=yes\n' >> ${process.env.ANDROID_AVD_HOME}/"${avdName}".avd"/config.ini`);
+  }
+
   //turn off hardware acceleration on Linux
   if (process.platform === 'linux' && disableLinuxHardwareAcceleration) {
     console.log('Disabling Linux hardware acceleration.');
@@ -63,7 +68,6 @@ export async function launchEmulator(
   await waitForDevice();
   await exec.exec(`adb shell input keyevent 82`);
 
-  // disable animations
   if (disableAnimations) {
     console.log('Disabling animations.');
     await exec.exec(`adb shell settings put global window_animation_scale 0.0`);
@@ -73,6 +77,9 @@ export async function launchEmulator(
   if (disableSpellChecker) {
     await exec.exec(`adb shell settings put secure spell_checker_enabled 0`);
   }
+  if (enableHardwareKeyboard) {
+    await exec.exec(`adb shell settings put secure show_ime_with_hard_keyboard 0`);
+  }
 }
 
 /**
diff --git a/src/input-validator.ts b/src/input-validator.ts
index 939c673..ac74762 100644
--- a/src/input-validator.ts
+++ b/src/input-validator.ts
@@ -54,6 +54,12 @@ export function checkDisableLinuxHardwareAcceleration(disableLinuxHardwareAccele
   }
 }
 
+export function checkEnableHardwareKeyboard(enableHardwareKeyboard: string): void {
+  if (!isValidBoolean(enableHardwareKeyboard)) {
+    throw new Error(`Input for input.enable-hw-keyboard should be either 'true' or 'false'.`);
+  }
+}
+
 export function checkEmulatorBuild(emulatorBuild: string): void {
   if (isNaN(Number(emulatorBuild)) || !Number.isInteger(Number(emulatorBuild))) {
     throw new Error(`Unexpected emulator build: '${emulatorBuild}'.`);
diff --git a/src/main.ts b/src/main.ts
index 98df433..e167b93 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -9,7 +9,8 @@ import {
   checkDisableSpellchecker,
   checkDisableLinuxHardwareAcceleration,
   checkForceAvdCreation,
-  checkChannel
+  checkChannel,
+  checkEnableHardwareKeyboard
 } from './input-validator';
 import { launchEmulator, killEmulator } from './emulator-manager';
 import * as exec from '@actions/exec';
@@ -94,6 +95,12 @@ async function run() {
     const disableLinuxHardwareAcceleration = disableLinuxHardwareAccelerationInput === 'true';
     console.log(`disable Linux hardware acceleration: ${disableLinuxHardwareAcceleration}`);
 
+    // enable hardware keyboard
+    const enableHardwareKeyboardInput = core.getInput('enable-hw-keyboard');
+    checkEnableHardwareKeyboard(enableHardwareKeyboardInput);
+    const enableHardwareKeyboard = enableHardwareKeyboardInput === 'true';
+    console.log(`enable hardware keyboard: ${enableHardwareKeyboard}`);
+
     // emulator build
     const emulatorBuildInput = core.getInput('emulator-build');
     if (emulatorBuildInput) {
@@ -154,7 +161,8 @@ async function run() {
       emulatorOptions,
       disableAnimations,
       disableSpellchecker,
-      disableLinuxHardwareAcceleration
+      disableLinuxHardwareAcceleration,
+      enableHardwareKeyboard
     );
 
     // execute the custom script


Test patch:
diff --git a/__tests__/input-validator.test.ts b/__tests__/input-validator.test.ts
index d861c58..8aeb687 100644
--- a/__tests__/input-validator.test.ts
+++ b/__tests__/input-validator.test.ts
@@ -227,6 +227,27 @@ describe('disable-linux-hw-accel validator tests', () => {
   });
 });
 
+describe('enable-hw-keyboard validator tests', () => {
+  it('Throws if enable-hw-keyboard is not a boolean', () => {
+    const func = () => {
+      validator.checkEnableHardwareKeyboard('yes');
+    };
+    expect(func).toThrowError(`Input for input.enable-hw-keyboard should be either 'true' or 'false'.`);
+  });
+
+  it('Validates successfully if enable-hardware-keyboard is either true or false', () => {
+    const func1 = () => {
+      validator.checkEnableHardwareKeyboard('true');
+    };
+    expect(func1).not.toThrow();
+
+    const func2 = () => {
+      validator.checkEnableHardwareKeyboard('false');
+    };
+    expect(func2).not.toThrow();
+  });
+});
+
 describe('emulator-build validator tests', () => {
   it('Throws if emulator-build is not a number', () => {
     const func = () => {