3.6 KiB
3.6 KiB
Upload Android App to Local App Store
Follow these steps to add a new Android app to the Local App Store.
Prerequisites
- Java 17:
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 - Android SDK:
export ANDROID_HOME=/home/jarian/android-sdk - Gradle wrapper in your app project (run
gradle wrapperif missing) - Signing keystore:
/home/jarian/playground/app-store/signing/localstore.keystore(aliaslocalstore, passwordlocalstore123) - Server running on port 9800
Step 1: Build the APK
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
export ANDROID_HOME=/home/jarian/android-sdk
cd /path/to/your/app/project
./gradlew :app:assembleRelease
The signed release APK will be at app/build/outputs/apk/release/app-release.apk.
Step 2: Copy APK to repos
cp app/build/outputs/apk/release/app-release.apk /home/jarian/playground/app-store/server/repos/<app_id>.apk
Use a lowercase, hyphen-separated ID (e.g., myapp, hello-world).
Step 3: Create metadata JSON
Create <app_id>.json in the repos directory alongside the APK:
{
"name": "App Display Name",
"description": "One or two sentences describing the app.",
"icon": null,
"screenshots": [],
"package_name": "com.your.package",
"version_name": "1.0",
"version_code": "1",
"min_sdk": "26",
"target_sdk": "34",
"permissions": ["android.permission.INTERNET"]
}
Field notes:
name: Human-readable display namedescription: Short description shown in the storeicon:nullif no PNG icon in the APK, otherwise the resource path (e.g.,res/drawable-mdpi/ic_launcher.png)screenshots: Array of screenshot filenames, or[]if nonepackage_name: FromAndroidManifest.xmlorbuild.gradleversion_name/version_code: Frombuild.gradledefaultConfigmin_sdk/target_sdk: Frombuild.gradledefaultConfigpermissions: Array of<uses-permission>entries fromAndroidManifest.xml
Step 4: Trigger server scan
curl -X POST http://localhost:9800/api/scan
Or restart the server if preferred:
screen -S appstore -X quit 2>/dev/null
sleep 1
screen -dmS appstore bash -c "cd /home/jarian/playground/app-store/server && source venv/bin/activate && python -c 'import uvicorn, sys; sys.path.insert(0, \".\"); from main import app; uvicorn.run(app, host=\"0.0.0.0\", port=9800)'"
Step 5: Verify
curl -s http://localhost:9800/api/apps | python3 -c "
import json, sys
data = json.load(sys.stdin)
print(f'Total: {data[\"total\"]} apps')
for app in data['apps']:
print(f' - {app[\"name\"]} (v{app[\"version_name\"]}, pkg={app[\"package_name\"]})')
"
Optional: Add to downloads directory
For easy browser download via /dl/ endpoint:
cp app/build/outputs/apk/release/app-release.apk /home/jarian/playground/app-store/server/downloads/<app_id>.apk
Common Issues
- Binary XML manifest parsing: The scanner reads APKs directly without
aapt. Fields likeversion_namemay showunknownif the string parser misses them — always set them in the JSON override. - No PNG icon: If the APK only has XML vector drawables, set
"icon": nullin the JSON. - KSP plugin: Required for Room. Keep only in
app/build.gradle, not rootbuild.gradle. - Navigation: Use
<fragment>tag (notFrameLayout) withandroid:name="androidx.navigation.fragment.NavHostFragment", or add NavHostFragment programmatically in MainActivity. - Data binding: Cannot create typed properties for
<fragment>tags. Use programmatic NavHostFragment instead. - Lint: Set
checkReleaseBuilds falseto avoid blocking builds.