Add release/build tooling and README
- Real release build type: R8 shrinking + shrunk resources, with a keep rule for VeilHook specifically — LSPosed finds it by exact class name via the plaintext xposed_init asset, which R8 can't see, so obfuscating it would silently break module loading with no build error to point at why. - Signing config that reads keystore.properties if present (gitignored, generated via scripts/generate-keystore.sh), falling back to debug signing otherwise so assembleRelease always produces something installable. - scripts/release.sh builds and copies a version-stamped APK into dist/. - GitHub Actions workflow building and publishing a release on any tag matching *.*.* (bare semver, e.g. 1.2.0 — no v prefix required), optionally using repo secrets for real release signing in CI. - README covering setup, the feature table, and the DenyList gotcha. No native code in this module, so a single APK already covers every architecture — no per-ABI splitting needed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,62 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "*.*.*"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up JDK 17
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: "17"
|
||||||
|
|
||||||
|
- name: Set up Android SDK
|
||||||
|
uses: android-actions/setup-android@v4
|
||||||
|
with:
|
||||||
|
packages: "platform-tools platforms;android-35 build-tools;35.0.0"
|
||||||
|
|
||||||
|
# Optional — only produces a properly release-signed APK if these repo secrets are set:
|
||||||
|
# KEYSTORE_BASE64 (base64 of a release .jks), KEYSTORE_PASSWORD, KEY_ALIAS, KEY_PASSWORD.
|
||||||
|
# Without them, the build falls back to debug signing (still installable, just not
|
||||||
|
# signed with a dedicated release key).
|
||||||
|
- name: Set up release signing
|
||||||
|
env:
|
||||||
|
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
|
||||||
|
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
|
||||||
|
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
||||||
|
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
if [ -n "$KEYSTORE_BASE64" ]; then
|
||||||
|
echo "$KEYSTORE_BASE64" | base64 -d > release.jks
|
||||||
|
cat > keystore.properties <<EOF
|
||||||
|
storeFile=release.jks
|
||||||
|
storePassword=$KEYSTORE_PASSWORD
|
||||||
|
keyAlias=$KEY_ALIAS
|
||||||
|
keyPassword=$KEY_PASSWORD
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
echo "No KEYSTORE_BASE64 secret set — release APK will be debug-signed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build release APK
|
||||||
|
run: ./gradlew assembleRelease
|
||||||
|
|
||||||
|
- name: Package artifact
|
||||||
|
run: |
|
||||||
|
mkdir -p dist
|
||||||
|
cp app/build/outputs/apk/release/app-release.apk "dist/veil-${GITHUB_REF_NAME}.apk"
|
||||||
|
|
||||||
|
- name: Publish GitHub release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
files: dist/*.apk
|
||||||
@@ -7,3 +7,7 @@ local.properties
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
.cxx/
|
.cxx/
|
||||||
/captures/
|
/captures/
|
||||||
|
keystore.properties
|
||||||
|
*.jks
|
||||||
|
*.keystore
|
||||||
|
/dist/
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# Veil
|
||||||
|
|
||||||
|
An LSPosed module that hides accessibility, debugging, and root/hook state from specific
|
||||||
|
apps, per app, without changing the actual state of the device.
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> Built entirely by AI in one evening to fix a personal problem. YMMV — it works for me
|
||||||
|
> and hasn't been audited beyond that.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Rooted device with a working Zygisk implementation and LSPosed (or an LSPosed-compatible
|
||||||
|
framework such as Vector).
|
||||||
|
- Android 8.1+.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
1. Build (below) or grab a release APK from the repo's Releases page.
|
||||||
|
2. Enable the module in LSPosed Manager and scope it to the target app.
|
||||||
|
3. Open the target app once — it appears in Veil's own settings screen automatically.
|
||||||
|
4. Uncheck anything you don't want hidden for that app.
|
||||||
|
|
||||||
|
If the target app still won't launch, check whether it's on your root manager's DenyList
|
||||||
|
(e.g. KernelSU: app profile → "Umount modules"). An app on that list never gets Zygisk
|
||||||
|
modules injected at all, Veil included. The settings screen's help panel covers this and
|
||||||
|
a few other gotchas.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
Per app, per feature — anything not listed gets everything hidden by default.
|
||||||
|
|
||||||
|
| Feature | Hides |
|
||||||
|
| ------------------------ | ------------------------------------------------------ |
|
||||||
|
| Accessibility services | Any accessibility service running |
|
||||||
|
| USB / wireless debugging | `adb_enabled` / `adb_wifi_enabled` |
|
||||||
|
| Developer options | Developer Options enabled state |
|
||||||
|
| Mock location | Legacy pre-Marshmallow flag only |
|
||||||
|
| Root / manager apps | Configurable package list (default: KernelSU, Magisk) |
|
||||||
|
| Build tags/type | Real build swapped for `release-keys` / `user` |
|
||||||
|
|
||||||
|
Doesn't cover native-level root/hook detection or remote attestation (Play Integrity,
|
||||||
|
SafetyNet) — that's your Zygisk implementation's job, not Veil's.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Requires JDK 17 and Android SDK (platform 35, build-tools 35.0.0).
|
||||||
|
|
||||||
|
```
|
||||||
|
./gradlew assembleDebug # debug-signed APK
|
||||||
|
./gradlew assembleRelease # minified release build, debug-signed unless keystore.properties exists
|
||||||
|
scripts/generate-keystore.sh # create a personal release-signing keystore
|
||||||
|
scripts/release.sh # build + copy signed release APK into dist/
|
||||||
|
```
|
||||||
|
|
||||||
|
## CI releases
|
||||||
|
|
||||||
|
Pushing a tag matching `*.*.*` (e.g. `1.2.0`) triggers `.github/workflows/release.yml`.
|
||||||
|
For a properly signed CI build, set repo secrets: `KEYSTORE_BASE64` (base64 of your
|
||||||
|
`release.jks`), `KEYSTORE_PASSWORD`, `KEY_ALIAS`, `KEY_PASSWORD`.
|
||||||
+34
-1
@@ -1,23 +1,56 @@
|
|||||||
|
import java.util.Properties
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.application")
|
id("com.android.application")
|
||||||
id("org.jetbrains.kotlin.android")
|
id("org.jetbrains.kotlin.android")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val keystorePropertiesFile = rootProject.file("keystore.properties")
|
||||||
|
val keystoreProperties = Properties()
|
||||||
|
val hasReleaseKeystore = keystorePropertiesFile.exists()
|
||||||
|
if (hasReleaseKeystore) {
|
||||||
|
keystoreProperties.load(keystorePropertiesFile.inputStream())
|
||||||
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "space.bdeshi.veil"
|
namespace = "space.bdeshi.veil"
|
||||||
compileSdk = 35
|
compileSdk = 35
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "space.bdeshi.veil"
|
applicationId = "space.bdeshi.veil"
|
||||||
|
// 27 (Android 8.1) is LSPosed/Zygisk's own practical floor — nothing here restricts
|
||||||
|
// it further. No upper bound: compileSdk is a compile-time API surface, not a ceiling
|
||||||
|
// on which OS versions can install and run the app.
|
||||||
minSdk = 27
|
minSdk = 27
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
versionName = "1.0"
|
versionName = "1.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
create("release") {
|
||||||
|
if (hasReleaseKeystore) {
|
||||||
|
storeFile = rootProject.file(keystoreProperties["storeFile"] as String)
|
||||||
|
storePassword = keystoreProperties["storePassword"] as String
|
||||||
|
keyAlias = keystoreProperties["keyAlias"] as String
|
||||||
|
keyPassword = keystoreProperties["keyPassword"] as String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
isMinifyEnabled = false
|
isMinifyEnabled = true
|
||||||
|
isShrinkResources = true
|
||||||
|
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||||
|
// Falls back to debug signing when no keystore.properties exists (e.g. a fresh
|
||||||
|
// clone, or CI without release-signing secrets configured), so assembleRelease
|
||||||
|
// always produces something installable. See scripts/generate-keystore.sh.
|
||||||
|
signingConfig = if (hasReleaseKeystore) {
|
||||||
|
signingConfigs.getByName("release")
|
||||||
|
} else {
|
||||||
|
signingConfigs.getByName("debug")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
# LSPosed discovers the module entry point by fully-qualified class name via the plaintext
|
||||||
|
# assets/xposed_init file, not through anything R8 can see (no manifest reference, no
|
||||||
|
# reflection call it can trace) — keep this class and its members intact or the module
|
||||||
|
# silently stops loading with no build error to point at why.
|
||||||
|
-keep class space.bdeshi.veil.VeilHook { *; }
|
||||||
Executable
+39
@@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Creates a personal release-signing keystore and keystore.properties. Both are gitignored —
|
||||||
|
# back them up yourself if you want release builds to keep the same signing identity across
|
||||||
|
# reinstalls (Android treats a differently-signed update as a different app).
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
if [ -f keystore.properties ]; then
|
||||||
|
echo "keystore.properties already exists — remove it first if you want to regenerate." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
KEYSTORE_PATH="release.jks"
|
||||||
|
ALIAS="veil"
|
||||||
|
|
||||||
|
read -rsp "New keystore password: " STORE_PASSWORD
|
||||||
|
echo
|
||||||
|
read -rsp "Key password (leave blank to reuse the keystore password): " KEY_PASSWORD
|
||||||
|
echo
|
||||||
|
KEY_PASSWORD="${KEY_PASSWORD:-$STORE_PASSWORD}"
|
||||||
|
|
||||||
|
keytool -genkeypair \
|
||||||
|
-keystore "$KEYSTORE_PATH" \
|
||||||
|
-alias "$ALIAS" \
|
||||||
|
-keyalg RSA \
|
||||||
|
-keysize 2048 \
|
||||||
|
-validity 10000 \
|
||||||
|
-storepass "$STORE_PASSWORD" \
|
||||||
|
-keypass "$KEY_PASSWORD" \
|
||||||
|
-dname "CN=Veil, OU=Personal, O=Personal, L=Unknown, ST=Unknown, C=US"
|
||||||
|
|
||||||
|
cat > keystore.properties <<EOF
|
||||||
|
storeFile=$KEYSTORE_PATH
|
||||||
|
storePassword=$STORE_PASSWORD
|
||||||
|
keyAlias=$ALIAS
|
||||||
|
keyPassword=$KEY_PASSWORD
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "Wrote $KEYSTORE_PATH and keystore.properties."
|
||||||
Executable
+15
@@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Builds a release APK and copies it into dist/ with a version-stamped filename.
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
./gradlew assembleRelease
|
||||||
|
|
||||||
|
VERSION=$(grep 'versionName' app/build.gradle.kts | head -1 | sed -E 's/.*"(.*)".*/\1/')
|
||||||
|
APK="app/build/outputs/apk/release/app-release.apk"
|
||||||
|
OUT="dist/veil-${VERSION}.apk"
|
||||||
|
|
||||||
|
mkdir -p dist
|
||||||
|
cp "$APK" "$OUT"
|
||||||
|
|
||||||
|
echo "Built $OUT"
|
||||||
Reference in New Issue
Block a user