From c4c4f09e3da4a9c8e499143698da2815268bf57d Mon Sep 17 00:00:00 2001 From: Alexander Sosedkin Date: Sat, 1 Jun 2024 17:51:25 +0200 Subject: [PATCH] modules/android-integration: add am command --- .github/workflows/emulator.yml | 1 + CHANGELOG.md | 6 ++ modules/environment/android-integration.nix | 36 ++++++++++ modules/module-list.nix | 1 + pkgs/android-integration/termux-am.nix | 33 +++++++++ tests/emulator/android_integration.py | 69 +++++++++++++++++++ tests/emulator/bootstrap_channels.py | 2 + tests/emulator/bootstrap_flakes.py | 2 + .../on-device/config-android-integration.bats | 17 +++++ .../on-device/config-android-integration.nix | 8 +++ 10 files changed, 175 insertions(+) create mode 100644 modules/environment/android-integration.nix create mode 100644 pkgs/android-integration/termux-am.nix create mode 100644 tests/emulator/android_integration.py create mode 100644 tests/on-device/config-android-integration.bats create mode 100644 tests/on-device/config-android-integration.nix diff --git a/.github/workflows/emulator.yml b/.github/workflows/emulator.yml index 16a88f4..490c17e 100644 --- a/.github/workflows/emulator.yml +++ b/.github/workflows/emulator.yml @@ -108,6 +108,7 @@ jobs: # below 28: bootstrap didn't start, IDK why # 34: sometimes work, but doesn't seem stable, even w/o caching images script: + - android_integration - bootstrap_flakes - bootstrap_channels - poke_around diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ae6ecb..9cb6e56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## Release 24.05 (unreleased) +### New Options + +* New options under `android-integration`, + offer some of the tools familiar to Termux users. + Currently it's just `am`. + ### Compatibility considerations * `nixOnDroidConfigurations` `pkgs` argument is now mandatory. diff --git a/modules/environment/android-integration.nix b/modules/environment/android-integration.nix new file mode 100644 index 0000000..06f8ebb --- /dev/null +++ b/modules/environment/android-integration.nix @@ -0,0 +1,36 @@ +# Copyright (c) 2019-2024, see AUTHORS. Licensed under MIT License, see LICENSE. + +{ config, lib, pkgs, ... }: + +let + cfg = config.android-integration; + + termux-am = + pkgs.callPackage (import ../../pkgs/android-integration/termux-am.nix) { }; +in +{ + + ###### interface + + options.android-integration = { + + am.enable = lib.mkOption { + type = lib.types.bool; + default = false; + example = "true"; + description = lib.mdDoc '' + Provide an `am` (activity manager) command. + Is not guaranteed to be a real deal, could be of limited compatibility + with real `am` (like `termux-am`). + ''; + }; + + }; + + ###### implementation + + config = { + environment.packages = + lib.mkIf cfg.am.enable [ termux-am ]; + }; +} diff --git a/modules/module-list.nix b/modules/module-list.nix index 20dfdd0..1400353 100644 --- a/modules/module-list.nix +++ b/modules/module-list.nix @@ -9,6 +9,7 @@ [ ./build/activation.nix ./build/config.nix + ./environment/android-integration.nix ./environment/ca.nix ./environment/etc ./environment/links.nix diff --git a/pkgs/android-integration/termux-am.nix b/pkgs/android-integration/termux-am.nix new file mode 100644 index 0000000..18c8a30 --- /dev/null +++ b/pkgs/android-integration/termux-am.nix @@ -0,0 +1,33 @@ +# Copyright (c) 2019-2024, see AUTHORS. Licensed under MIT License, see LICENSE. + +{ stdenv, fetchFromGitHub, cmake }: + +let + appPath = "/data/data/com.termux.nix/files/apps/com.termux.nix"; + socketPath = "${appPath}/termux-am/am.sock"; +in +stdenv.mkDerivation rec { + name = "termux-am"; + version = "1.5.0"; + src = fetchFromGitHub { + owner = "termux"; + repo = "termux-am-socket"; + rev = version; + sha256 = "sha256-6pCv2HMBRp8Hi56b43mQqnaFaI7y5DfhS9gScANwg2I="; + }; + nativeBuildInputs = [ cmake ]; + patchPhase = '' + # Header generation doesn't seem to work on android + echo "#define SOCKET_PATH \"${socketPath}\"" > termux-am.h + # Fix the bash link so that nix can patch it + path to termux-am-socket + substituteInPlace termux-am.sh.in \ + --replace @TERMUX_PREFIX@/bin/bash /bin/bash \ + --replace \ + "termux-am-socket \"\$am_command_string\"" \ + "$out/bin/termux-am-socket \"\$am_command_string\"" + ''; + postInstall = '' + # Scripts use 'am' as an alias. + ln -s $out/bin/termux-am $out/bin/am + ''; +} diff --git a/tests/emulator/android_integration.py b/tests/emulator/android_integration.py new file mode 100644 index 0000000..87d8c06 --- /dev/null +++ b/tests/emulator/android_integration.py @@ -0,0 +1,69 @@ +import time + +import bootstrap_channels + +from common import screenshot, wait_for + + +def run(d): + nod = bootstrap_channels.run(d) + + d('input text "am"') + d.ui.press('enter') + wait_for(d, 'bash: am: command not found') + screenshot(d, 'no-am') + + # Apply a config that enables am + cfg = ('/data/local/tmp/n-o-d/unpacked/tests/on-device/' + 'config-android-integration.nix') + d(f'input text \'cp {cfg} .config/nixpkgs/nix-on-droid.nix\'') + d.ui.press('enter') + screenshot(d, 'pre-switch') + d('input text "nix-on-droid switch && echo integration tools installed"') + d.ui.press('enter') + wait_for(d, 'integration tools installed') + screenshot(d, 'post-switch') + + # Verify am is there + d('input text "am | head -n2"') + d.ui.press('enter') + wait_for(d, 'termux-am is a wrapper script') + screenshot(d, 'am-appears') + + # Smoke-test that am doesn't work yet + d('input text "am start -a android.settings.SETTINGS 2>&1 | head -n5"') + d.ui.press('enter') + screenshot(d, 'am-invoked for the first time') + wait_for(d, 'Nix requires "Display over other apps" permission') + wait_for(d, 'https://dontkillmyapp.com') + screenshot(d, 'am-wants-permission') + + # ... there might be a notification now, get rid of it + time.sleep(3) + screenshot(d, 'am-wants-permission-3-seconds-later') + if 'text="TermuxAm Socket Server Error"' in d.ui.dump_hierarchy(): + d.ui.open_notification() + time.sleep(1) + screenshot(d, 'notification-opened') + d.ui(text='TermuxAm Socket Server Error').swipe('right') + screenshot(d, 'error-notification-swiped-right') + d.ui.press('back') + screenshot(d, 'back') + + # Grant nix app 'Draw over other apps' permission + nod.permissions += 'android.permission.SYSTEM_ALERT_WINDOW' + + # Smoke-test that am works + d('input text "am start -a android.settings.SETTINGS"') + d.ui.press('enter') + screenshot(d, 'settings-opening') + wait_for(d, 'Search settings') + wait_for(d, 'Network') + screenshot(d, 'settings-awaited') + d.ui.press('back') + screenshot(d, 'back-from-settings') + + # Verify we're back + d('input text "am | head -n2"') + d.ui.press('enter') + wait_for(d, 'termux-am is a wrapper script') diff --git a/tests/emulator/bootstrap_channels.py b/tests/emulator/bootstrap_channels.py index 08d1237..2dd77a2 100644 --- a/tests/emulator/bootstrap_channels.py +++ b/tests/emulator/bootstrap_channels.py @@ -52,3 +52,5 @@ def run(d): wait_for(d, 'c21va2UtdGVzdAo=') screenshot(d, 'success-bootstrap-channels') + + return nod diff --git a/tests/emulator/bootstrap_flakes.py b/tests/emulator/bootstrap_flakes.py index 4d27a60..8131d86 100644 --- a/tests/emulator/bootstrap_flakes.py +++ b/tests/emulator/bootstrap_flakes.py @@ -52,3 +52,5 @@ def run(d): wait_for(d, 'c21va2UtdGVzdAo=') screenshot(d, 'success-bootstrap-flakes') + + return nod diff --git a/tests/on-device/config-android-integration.bats b/tests/on-device/config-android-integration.bats new file mode 100644 index 0000000..d910490 --- /dev/null +++ b/tests/on-device/config-android-integration.bats @@ -0,0 +1,17 @@ +# Copyright (c) 2019-2024, see AUTHORS. Licensed under MIT License, see LICENSE. + +load lib + +@test 'android-integration options can be used' { + bats_require_minimum_version 1.5.0 + run ! command -v am + + cp \ + "$ON_DEVICE_TESTS_DIR/config-android-integration.nix" \ + ~/.config/nixpkgs/nix-on-droid.nix + nix-on-droid switch + + command -v am + + switch_to_default_config +} diff --git a/tests/on-device/config-android-integration.nix b/tests/on-device/config-android-integration.nix new file mode 100644 index 0000000..5c85239 --- /dev/null +++ b/tests/on-device/config-android-integration.nix @@ -0,0 +1,8 @@ +_: + +{ + system.stateVersion = "23.11"; + android-integration = { + am.enable = true; + }; +}