Android app (TWA) in android/, built with podman; serve assetlinks.json for it
This commit is contained in:
parent
44708d5f3c
commit
7900bf5b3e
21 changed files with 313 additions and 0 deletions
6
android/.gitignore
vendored
Normal file
6
android/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
.gradle/
|
||||
build/
|
||||
app/build/
|
||||
out/
|
||||
local.properties
|
||||
*.jks
|
||||
17
android/Containerfile
Normal file
17
android/Containerfile
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# Build image for the Android app: JDK 17, the Android SDK and Gradle. See build.sh.
|
||||
FROM docker.io/library/eclipse-temurin:17-jdk
|
||||
|
||||
ARG CMDLINE_TOOLS=15859902
|
||||
ARG GRADLE=8.14.3
|
||||
ENV ANDROID_HOME=/opt/android-sdk
|
||||
ENV PATH=/opt/gradle/bin:$ANDROID_HOME/cmdline-tools/latest/bin:$PATH
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends unzip && rm -rf /var/lib/apt/lists/*
|
||||
RUN curl -fsSLo /tmp/tools.zip https://dl.google.com/android/repository/commandlinetools-linux-${CMDLINE_TOOLS}_latest.zip \
|
||||
&& mkdir -p $ANDROID_HOME/cmdline-tools && unzip -q /tmp/tools.zip -d /tmp \
|
||||
&& mv /tmp/cmdline-tools $ANDROID_HOME/cmdline-tools/latest && rm /tmp/tools.zip
|
||||
RUN yes | sdkmanager --licenses >/dev/null && sdkmanager "platforms;android-36" "build-tools;35.0.0" "platform-tools" >/dev/null
|
||||
RUN curl -fsSLo /tmp/gradle.zip https://services.gradle.org/distributions/gradle-${GRADLE}-bin.zip \
|
||||
&& unzip -q /tmp/gradle.zip -d /opt && mv /opt/gradle-${GRADLE} /opt/gradle && rm /tmp/gradle.zip
|
||||
|
||||
WORKDIR /src
|
||||
53
android/app/build.gradle
Normal file
53
android/app/build.gradle
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
plugins {
|
||||
id "com.android.application"
|
||||
}
|
||||
|
||||
// The site the app opens. It must serve /.well-known/assetlinks.json naming this app's package and
|
||||
// signing key, or Chrome shows its URL bar over the app (see SAW_ANDROID_* in example.env).
|
||||
def siteURL = "https://cs2.zio.sh"
|
||||
def startPath = "/"
|
||||
|
||||
android {
|
||||
namespace "sh.zio.cs2"
|
||||
compileSdk 36
|
||||
|
||||
defaultConfig {
|
||||
// Never change applicationId once installed anywhere: Android treats it as a different app.
|
||||
applicationId "sh.zio.cs2"
|
||||
minSdk 24
|
||||
targetSdk 36
|
||||
// Bump versionCode for every APK you hand out, or Android refuses to update over the old one.
|
||||
versionCode 2
|
||||
versionName "1.0"
|
||||
|
||||
resValue "string", "launch_url", siteURL + startPath
|
||||
resValue "string", "asset_statements",
|
||||
"[{\\\"relation\\\": [\\\"delegate_permission/common.handle_all_urls\\\"], \\\"target\\\": {\\\"namespace\\\": \\\"web\\\", \\\"site\\\": \\\"" + siteURL + "\\\"}}]"
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
storeFile file(System.getenv("KEYSTORE_FILE") ?: "/keystore/release.jks")
|
||||
storePassword System.getenv("KEYSTORE_PASSWORD")
|
||||
keyAlias "release"
|
||||
keyPassword System.getenv("KEYSTORE_PASSWORD")
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt")
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "com.google.androidbrowserhelper:androidbrowserhelper:2.7.3"
|
||||
}
|
||||
80
android/app/src/main/AndroidManifest.xml
Normal file
80
android/app/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- A Trusted Web Activity: the app is the panel, opened full screen in the phone's Chrome.
|
||||
androidbrowserhelper's LauncherActivity does all of it; there is no code of our own. -->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:manageSpaceActivity="com.google.androidbrowserhelper.trusted.ManageDataLauncherActivity"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar">
|
||||
|
||||
<meta-data
|
||||
android:name="asset_statements"
|
||||
android:resource="@string/asset_statements" />
|
||||
|
||||
<activity
|
||||
android:name="com.google.androidbrowserhelper.trusted.LauncherActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name">
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.DEFAULT_URL"
|
||||
android:value="@string/launch_url" />
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.STATUS_BAR_COLOR"
|
||||
android:resource="@color/theme" />
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.NAVIGATION_BAR_COLOR"
|
||||
android:resource="@color/background" />
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.SPLASH_IMAGE_DRAWABLE"
|
||||
android:resource="@drawable/splash" />
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.SPLASH_SCREEN_BACKGROUND_COLOR"
|
||||
android:resource="@color/background" />
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.FILE_PROVIDER_AUTHORITY"
|
||||
android:value="sh.zio.cs2.fileprovider" />
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- LauncherActivity uses these library activities by name, so they must be declared here
|
||||
(as Bubblewrap's template does), or R8 renames them and the app crashes on launch.
|
||||
ManageDataLauncherActivity is "Manage space" in Android's app info: the site's data. -->
|
||||
<activity
|
||||
android:name="com.google.androidbrowserhelper.trusted.ManageDataLauncherActivity"
|
||||
android:enabled="true"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="false">
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.MANAGE_SPACE_URL"
|
||||
android:value="@string/launch_url" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.APPLICATION_PREFERENCES" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.google.androidbrowserhelper.trusted.FocusActivity" />
|
||||
<!-- Used when the phone has no browser that supports TWAs. -->
|
||||
<activity
|
||||
android:name="com.google.androidbrowserhelper.trusted.WebViewFallbackActivity"
|
||||
android:configChanges="orientation|screenSize" />
|
||||
|
||||
<!-- Hands the splash image to Chrome while the panel loads. -->
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="sh.zio.cs2.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/filepaths" />
|
||||
</provider>
|
||||
</application>
|
||||
</manifest>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 8 KiB |
BIN
android/app/src/main/res/drawable-nodpi/splash.png
Normal file
BIN
android/app/src/main/res/drawable-nodpi/splash.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.6 KiB |
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
6
android/app/src/main/res/values/colors.xml
Normal file
6
android/app/src/main/res/values/colors.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- The panel's colours: the top bar (theme) and page background in styles.css. -->
|
||||
<resources>
|
||||
<color name="theme">#1B1E25</color>
|
||||
<color name="background">#14161B</color>
|
||||
</resources>
|
||||
4
android/app/src/main/res/values/strings.xml
Normal file
4
android/app/src/main/res/values/strings.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">zio.sh</string>
|
||||
</resources>
|
||||
4
android/app/src/main/res/xml/filepaths.xml
Normal file
4
android/app/src/main/res/xml/filepaths.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<files-path name="twa_splash" path="twa_splash/" />
|
||||
</paths>
|
||||
3
android/build.gradle
Normal file
3
android/build.gradle
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
plugins {
|
||||
id "com.android.application" version "8.13.2" apply false
|
||||
}
|
||||
37
android/build.sh
Executable file
37
android/build.sh
Executable file
|
|
@ -0,0 +1,37 @@
|
|||
#!/usr/bin/env bash
|
||||
# Builds the signed release APK into out/ with podman (no local JDK or Android SDK needed).
|
||||
#
|
||||
# The signing key lives outside the repo, in $KEYSTORE_DIR (default ~/.config/simpleadmin-web/android),
|
||||
# and is created on the first run. Back it up: an APK signed with a different key can't update an
|
||||
# installed one, and the panel's assetlinks.json names the key's fingerprint.
|
||||
set -euo pipefail
|
||||
|
||||
here="$(cd "$(dirname "$0")" && pwd)"
|
||||
keydir="${KEYSTORE_DIR:-$HOME/.config/simpleadmin-web/android}"
|
||||
image=simpleadmin-web-android-build
|
||||
|
||||
podman build -q -t "$image" "$here" >/dev/null
|
||||
|
||||
mkdir -p "$keydir"
|
||||
chmod 700 "$keydir"
|
||||
if [[ ! -f "$keydir/release.jks" ]]; then
|
||||
echo "Creating a signing key in $keydir"
|
||||
(umask 077 && openssl rand -hex 24 >"$keydir/password")
|
||||
podman run --rm -v "$keydir":/keystore:Z "$image" keytool -genkeypair -noprompt \
|
||||
-keystore /keystore/release.jks -storetype PKCS12 -storepass "$(cat "$keydir/password")" \
|
||||
-alias release -keyalg RSA -keysize 4096 -validity 10000 -dname "CN=zio.sh"
|
||||
fi
|
||||
|
||||
podman run --rm \
|
||||
-v "$here":/src:Z \
|
||||
-v "$keydir":/keystore:ro,Z \
|
||||
-v simpleadmin-web-android-gradle:/root/.gradle \
|
||||
-e KEYSTORE_PASSWORD="$(cat "$keydir/password")" \
|
||||
"$image" gradle --no-daemon -q assembleRelease
|
||||
|
||||
mkdir -p "$here/out"
|
||||
cp "$here/app/build/outputs/apk/release/app-release.apk" "$here/out/simpleadmin-web.apk"
|
||||
echo "APK: $here/out/simpleadmin-web.apk"
|
||||
echo "Signing key SHA-256 (for SAW_ANDROID_CERT_SHA256):"
|
||||
podman run --rm -v "$keydir":/keystore:ro,Z "$image" keytool -list -keystore /keystore/release.jks \
|
||||
-storepass "$(cat "$keydir/password")" -alias release | sed -n 's/.*SHA-256): //p'
|
||||
2
android/gradle.properties
Normal file
2
android/gradle.properties
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
org.gradle.jvmargs=-Xmx2g
|
||||
android.useAndroidX=true
|
||||
16
android/settings.gradle
Normal file
16
android/settings.gradle
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
pluginManagement {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
rootProject.name = "simpleadmin-web"
|
||||
include ":app"
|
||||
Loading…
Add table
Add a link
Reference in a new issue