diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index fe62a28042..29ca48c4b8 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -6,31 +6,34 @@ permissions: jobs: build: runs-on: ubuntu-24.04 + strategy: + matrix: + abi: [arm64-v8a, armeabi] steps: - uses: actions/checkout@v6 - name: Install required packages run: sudo apt-get install -y gcc-multilib - - name: Set up JDK 8 + - name: Set up JDK 17 uses: actions/setup-java@v5 with: - java-version: '8' + java-version: '17' distribution: 'temurin' - name: Setup Android SDK uses: android-actions/setup-android@v3 with: cmdline-tools-version: 9123335 - packages: 'platforms;android-30 ndk;21.4.7075529' + packages: 'platforms;android-36 build-tools;36.0.0 ndk;27.3.13750724' - name: Configure path to NDK - run: sed -i -e 's,NDK = .*,NDK = /usr/local/lib/android/sdk/ndk/21.4.7075529,g' sys/android/Makefile.src + run: sed -i -e 's,NDK = .*,NDK = /usr/local/lib/android/sdk/ndk/27.3.13750724,g' sys/android/Makefile.src - name: Build native Android application - run: cd sys/android && sh ./setup.sh && cd ../.. && make install + run: cd sys/android && sh ./setup.sh && cd ../.. && make install ABI=${{ matrix.abi }} - name: Build NetHack run: cd sys/android && sh gradlew build - uses: actions/upload-artifact@v5 with: - name: nethack-release-unsigned + name: nethack-release-unsigned-${{ matrix.abi }} path: sys/android/app/build/outputs/apk/release/app-release-unsigned.apk - uses: actions/upload-artifact@v5 with: - name: nethack-debug + name: nethack-debug-${{ matrix.abi }} path: sys/android/app/build/outputs/apk/debug/app-debug.apk diff --git a/.gitignore b/.gitignore index 2ff5779b31..2a940139d0 100644 --- a/.gitignore +++ b/.gitignore @@ -83,3 +83,8 @@ Makefile.gcc-orig # /win/ /win/*/*.o + +# Release signing secrets +keystore.properties +*.jks +*.keystore diff --git a/sys/android/.gitignore b/sys/android/.gitignore index 6959d1aef3..8b7aecc5f5 100644 --- a/sys/android/.gitignore +++ b/sys/android/.gitignore @@ -1,6 +1,6 @@ bin -libs/* -assets/nethackdir +app/libs/ +app/assets/nethackdir/ gen .gradle build diff --git a/sys/android/Makefile.src b/sys/android/Makefile.src index e98bbbce2f..6abd99d153 100644 --- a/sys/android/Makefile.src +++ b/sys/android/Makefile.src @@ -87,30 +87,20 @@ SYSOBJ = ioctl.o androidmain.o unixtty.o androidunix.o # CC = arm-linux-gcc # # For cross-compiling with Android NDK -NDK = /path/to/android-ndk-r21e +NDK = /path/to/android-ndk-r27d NDK_TOOLCHAIN = $(NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin -# Choose an ABI -ABI = armeabi -#ABI = armeabi-v7a -#ABI = arm64-v8a -#ABI = x86 -#ABI = x86_64 +# Choose an ABI (normally set in the top-level Makefile) +ABI ?= arm64-v8a CC_armeabi = $(NDK_TOOLCHAIN)/armv7a-linux-androideabi30-clang -LD_armeabi = $(NDK_TOOLCHAIN)/arm-linux-androideabi-ld CC_armeabi-v7a = $(NDK_TOOLCHAIN)/armv7a-linux-androideabi30-clang -LD_armeabi-v7a = $(NDK_TOOLCHAIN)/arm-linux-androideabi-ld CC_arm64-v8a = $(NDK_TOOLCHAIN)/aarch64-linux-android30-clang -LD_arm64-v8a = $(NDK_TOOLCHAIN)/aarch64-linux-android-ld CC_x86 = $(NDK_TOOLCHAIN)/i686-linux-android30-clang -LD_x86 = $(NDK_TOOLCHAIN)/i686-linux-android-ld CC_x86_64 = $(NDK_TOOLCHAIN)/x86_64-linux-android30-clang -LD_x86_64 = $(NDK_TOOLCHAIN)/x86_64-linux-android-ld CC_VAR = CC_$(ABI) -LD_VAR = LD_$(ABI) CC := $($(CC_VAR)) LINK := $(CC) @@ -118,7 +108,13 @@ LINK := $(CC) # Compile normally CFLAGS = -DANDROID -O2 -g -Wno-format -fsigned-char -fPIC -I../include #CFLAGS = -DANDROID -DDEBUG -g -Wno-format -fsigned-char -fPIC -I../include -LFLAGS = -shared -Wl,--fix-cortex-a8 +LFLAGS_armeabi = -Wl,--fix-cortex-a8 +LFLAGS_armeabi-v7a = -Wl,--fix-cortex-a8 +# 16 KB page alignment, required by newer 64-bit Android devices +LFLAGS_arm64-v8a = -Wl,-z,max-page-size=16384 +LFLAGS_x86_64 = -Wl,-z,max-page-size=16384 +LFLAGS_VAR = LFLAGS_$(ABI) +LFLAGS = -shared $($(LFLAGS_VAR)) # LFLAGS = -L/usr/X11R6/lib # OR compile backwards compatible a.out format @@ -408,6 +404,7 @@ Sysunix: $(HOBJ) Makefile Sysandroid: $(HOBJ) Makefile @echo "Loading ..." + rm -rf ../sys/android/app/libs mkdir -p ../sys/android/app/libs/$(ABI) $(LINK) $(LFLAGS) -o ../sys/android/app/libs/$(ABI)/lib$(GAME).so $(HOBJ) $(LIBS) @touch Sysandroid diff --git a/sys/android/Makefile.top b/sys/android/Makefile.top index 20df4a4461..9ee70663cf 100644 --- a/sys/android/Makefile.top +++ b/sys/android/Makefile.top @@ -19,6 +19,13 @@ NHSROOT=. # make NetHack #PREFIX = /usr GAME = nethack + +# Target ABI for the native library: arm64-v8a, armeabi, armeabi-v7a, x86 +# or x86_64. Override with 'make install ABI=...'. The compiled data files +# depend on the target's word size, so the host utilities are built to match +# and one build of the data files only works with one ABI. +ABI = arm64-v8a +export ABI # GAME = nethack.prg #GAMEUID = games #GAMEGRP = bin diff --git a/sys/android/Makefile.utl b/sys/android/Makefile.utl index f11e3de35a..9d969799f9 100644 --- a/sys/android/Makefile.utl +++ b/sys/android/Makefile.utl @@ -92,8 +92,18 @@ NHSROOT=.. # flags for debugging: # CFLAGS = -g -I../include -CFLAGS = -DANDROID -m32 -Wno-format -fsigned-char -I../include -LFLAGS = -m32 +# The compiled data files must match the target's word size, so build the +# host utilities as 32-bit for 32-bit ABIs (needs gcc-multilib) and 64-bit +# otherwise. +ABI ?= arm64-v8a +HOSTBITS_armeabi = -m32 +HOSTBITS_armeabi-v7a = -m32 +HOSTBITS_x86 = -m32 +HOSTBITS_VAR = HOSTBITS_$(ABI) +HOSTBITS = $($(HOSTBITS_VAR)) + +CFLAGS = -DANDROID $(HOSTBITS) -Wno-format -fsigned-char -I../include +LFLAGS = $(HOSTBITS) LIBS = diff --git a/sys/android/README.md b/sys/android/README.md index ed554f8b5d..673bb0c30b 100644 --- a/sys/android/README.md +++ b/sys/android/README.md @@ -9,7 +9,7 @@ work. If you're running Windows you're on your own. ## Preparations - Download and extract Android SDK Command-line Tools [https://developer.android.com/studio/index.html#command-tools]() - - Install JDK 8. Required by Android SDK manager (e.g. [https://adoptium.net/temurin/releases?version=8&os=any&arch=any](Temurin)) + - Install JDK 17. Required by the Android SDK manager and Gradle (e.g. [Temurin](https://adoptium.net/temurin/releases?version=17&os=any&arch=any)) - Install `bison` and `flex`. Used by the native nethack build. - Check out NetHack-Android: `git clone https://github.com/gurrhack/NetHack-Android.git` - Create an env variable called `ANDROID_SDK_ROOT` and point it to the android-sdk installation directory. Used by Gradle. @@ -17,9 +17,9 @@ work. If you're running Windows you're on your own. ### Install Android build tools 1. `cd /path/to/android-sdk/tools/bin` - 2. Update the sdk manager: `./sdkmanager --update`. If you get "NoClassDefFoundError" it's because you're not running JDK 8. Make sure the env variable `JAVA_HOME` points to JDK 8. - 3. Install the platform tools: `./sdkmanager --install "platforms;android-30"` - 4. Install the NDK: `./sdkmanager --install "ndk;21.4.7075529"` + 2. Update the sdk manager: `./sdkmanager --update`. Make sure the env variable `JAVA_HOME` points to JDK 17. + 3. Install the platform tools: `./sdkmanager --install "platforms;android-36" "build-tools;36.0.0"` + 4. Install the NDK: `./sdkmanager --install "ndk;27.3.13750724"` ## Build @@ -32,10 +32,20 @@ work. If you're running Windows you're on your own. 4. `cd ../..` 5. `make install` +This builds for `arm64-v8a`, which works on all current devices. Some newer +devices, e.g. the Galaxy Z Fold6 or Pixel 7 and later, can only run 64-bit code. +To build for another ABI, e.g. older 32-bit devices, pass it to make: +`make install ABI=armeabi-v7a`. The 32-bit ABIs need `gcc-multilib`. + +The compiled data files depend on the target's word size, so an APK only +supports the one ABI it was built for. Run `make spotless` before switching +ABI. Saved games from a 32-bit build can't be loaded by a 64-bit build, and +the reverse is also true. + ### Build the Android application 1. `cd /path/to/NetHack-Android/sys/android` - 2. `./gradlew build` + 2. `sh ./gradlew build` 3. `cd ./app/build/outputs/apk/debug` 4. Copy the APK file from this directory to your device. 5. On your device: locate the APK file, install it and run! diff --git a/sys/android/app/AndroidManifest.xml b/sys/android/app/AndroidManifest.xml index 1cea2da4d7..ee178d511c 100644 --- a/sys/android/app/AndroidManifest.xml +++ b/sys/android/app/AndroidManifest.xml @@ -1,11 +1,10 @@ - + android:name="android.permission.WRITE_EXTERNAL_STORAGE" + android:maxSdkVersion="28"/> + android:allowBackup="true" + android:enableOnBackInvokedCallback="false"> diff --git a/sys/android/app/assets/ver b/sys/android/app/assets/ver index fe4afb0df8..e34885bbc6 100644 --- a/sys/android/app/assets/ver +++ b/sys/android/app/assets/ver @@ -1 +1 @@ -106 +107 diff --git a/sys/android/app/build.gradle b/sys/android/app/build.gradle index 815c6058c7..cb3715344b 100644 --- a/sys/android/app/build.gradle +++ b/sys/android/app/build.gradle @@ -2,24 +2,56 @@ plugins { id 'com.android.application' } +// Release signing: reads keystore.properties (git-ignored) or NETHACK_* env vars. +// Without either, the release build is left unsigned (as CI does). +def signingProps = new Properties() +def signingFile = rootProject.file('keystore.properties') +if (signingFile.exists()) { + signingFile.withInputStream { signingProps.load(it) } +} +def signingValue = { String key -> + signingProps.getProperty(key) ?: System.getenv('NETHACK_' + key.toUpperCase()) +} + android { - compileSdkVersion 30 - buildToolsVersion "30.0.3" + namespace 'com.tbd.NetHack' + compileSdk 36 defaultConfig { - minSdkVersion 7 - targetSdkVersion 30 - versionName '3.6.7-1' - versionCode 3671 + applicationId 'com.tbd.NetHack' + minSdk 21 + targetSdk 36 + versionName '3.6.7-2' + versionCode 3672 } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + signingConfigs { + release { + def path = signingValue('storeFile') + if (path) { + storeFile file(path) + storePassword signingValue('storePassword') + keyAlias signingValue('keyAlias') + keyPassword signingValue('keyPassword') + } + } + } + + buildTypes { + release { + if (signingConfigs.release.storeFile != null) { + signingConfig signingConfigs.release + } + } } - lintOptions { + lint { disable 'MissingClass' } @@ -34,5 +66,5 @@ android { } dependencies { - implementation 'com.tbd.forkfront:lib:1.1' + implementation project(':forkfront') } diff --git a/sys/android/app/res/values/config.xml b/sys/android/app/res/values/config.xml index 9e07a778e4..b7fc42685c 100644 --- a/sys/android/app/res/values/config.xml +++ b/sys/android/app/res/values/config.xml @@ -7,7 +7,7 @@ defaults.nh menu ... # 20s . : ; , e d r z Z q t f w x i E Q P R W T o ^d ^p a A ^t D F p ^x ^o ? true - true + false Gurr Android Nethack 3.6.0 Arc|Bar|Cav|Hea|Kni|Mon|Pri|Rog|Ran|Sam|Tou|Val|Wiz 54 diff --git a/sys/android/build.gradle b/sys/android/build.gradle index 8650a1875d..a1033d4a65 100644 --- a/sys/android/build.gradle +++ b/sys/android/build.gradle @@ -1,19 +1,6 @@ -buildscript { - repositories { - google() - jcenter() - } - - dependencies { - classpath 'com.android.tools.build:gradle:4.1.1' - } -} - -allprojects { - repositories { - google() - jcenter() - } +plugins { + id 'com.android.application' version '8.9.3' apply false + id 'com.android.library' version '8.9.3' apply false } task clean(type: Delete) { diff --git a/sys/android/forkfront/AndroidManifest.xml b/sys/android/forkfront/AndroidManifest.xml new file mode 100644 index 0000000000..33746dc4bb --- /dev/null +++ b/sys/android/forkfront/AndroidManifest.xml @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/sys/android/forkfront/README.md b/sys/android/forkfront/README.md new file mode 100644 index 0000000000..6306f4c9b6 --- /dev/null +++ b/sys/android/forkfront/README.md @@ -0,0 +1 @@ +ForkFront-Android library vendored from https://github.com/gurrhack/ForkFront-Android at d8371451af7a066463bc2597b5f8c5a6c8bffd0d (source only; build config rewritten for AGP 8). diff --git a/sys/android/forkfront/build.gradle b/sys/android/forkfront/build.gradle new file mode 100644 index 0000000000..d7752c1bc6 --- /dev/null +++ b/sys/android/forkfront/build.gradle @@ -0,0 +1,29 @@ +plugins { + id 'com.android.library' +} + +android { + namespace 'com.tbd.forkfront' + compileSdk 36 + + defaultConfig { + minSdk 21 + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src'] + res.srcDirs = ['res'] + } + } +} + +dependencies { + implementation 'cz.msebera.android:httpclient:4.4.1.2' +} diff --git a/sys/android/forkfront/res/drawable-hdpi/sym_keyboard_delete.png b/sys/android/forkfront/res/drawable-hdpi/sym_keyboard_delete.png new file mode 100644 index 0000000000..5139c71790 Binary files /dev/null and b/sys/android/forkfront/res/drawable-hdpi/sym_keyboard_delete.png differ diff --git a/sys/android/forkfront/res/drawable-hdpi/sym_keyboard_return.png b/sys/android/forkfront/res/drawable-hdpi/sym_keyboard_return.png new file mode 100644 index 0000000000..5a5670c320 Binary files /dev/null and b/sys/android/forkfront/res/drawable-hdpi/sym_keyboard_return.png differ diff --git a/sys/android/forkfront/res/drawable-hdpi/sym_keyboard_shift.png b/sys/android/forkfront/res/drawable-hdpi/sym_keyboard_shift.png new file mode 100644 index 0000000000..275769618a Binary files /dev/null and b/sys/android/forkfront/res/drawable-hdpi/sym_keyboard_shift.png differ diff --git a/sys/android/forkfront/res/drawable-hdpi/sym_keyboard_space.png b/sys/android/forkfront/res/drawable-hdpi/sym_keyboard_space.png new file mode 100644 index 0000000000..cef2daa5d6 Binary files /dev/null and b/sys/android/forkfront/res/drawable-hdpi/sym_keyboard_space.png differ diff --git a/sys/android/forkfront/res/drawable-ldpi/sym_keyboard_delete.png b/sys/android/forkfront/res/drawable-ldpi/sym_keyboard_delete.png new file mode 100644 index 0000000000..6cee596824 Binary files /dev/null and b/sys/android/forkfront/res/drawable-ldpi/sym_keyboard_delete.png differ diff --git a/sys/android/forkfront/res/drawable-ldpi/sym_keyboard_shift.png b/sys/android/forkfront/res/drawable-ldpi/sym_keyboard_shift.png new file mode 100644 index 0000000000..d059628469 Binary files /dev/null and b/sys/android/forkfront/res/drawable-ldpi/sym_keyboard_shift.png differ diff --git a/sys/android/forkfront/res/drawable-ldpi/sym_keyboard_space.png b/sys/android/forkfront/res/drawable-ldpi/sym_keyboard_space.png new file mode 100644 index 0000000000..09b94d9e6b Binary files /dev/null and b/sys/android/forkfront/res/drawable-ldpi/sym_keyboard_space.png differ diff --git a/sys/android/forkfront/res/drawable-mdpi/sym_keyboard_return.png b/sys/android/forkfront/res/drawable-mdpi/sym_keyboard_return.png new file mode 100644 index 0000000000..cbe2b152f3 Binary files /dev/null and b/sys/android/forkfront/res/drawable-mdpi/sym_keyboard_return.png differ diff --git a/sys/android/forkfront/res/layout/amount_selector.xml b/sys/android/forkfront/res/layout/amount_selector.xml new file mode 100644 index 0000000000..0585084e71 --- /dev/null +++ b/sys/android/forkfront/res/layout/amount_selector.xml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + +