app-store/UPLOAD_APP.md

102 lines
3.6 KiB
Markdown

# 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 wrapper` if missing)
- Signing keystore: `/home/jarian/playground/app-store/signing/localstore.keystore` (alias `localstore`, password `localstore123`)
- Server running on port 9800
## Step 1: Build the APK
```bash
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
```bash
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:
```json
{
"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 name
- `description`: Short description shown in the store
- `icon`: `null` if 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 none
- `package_name`: From `AndroidManifest.xml` or `build.gradle`
- `version_name` / `version_code`: From `build.gradle` `defaultConfig`
- `min_sdk` / `target_sdk`: From `build.gradle` `defaultConfig`
- `permissions`: Array of `<uses-permission>` entries from `AndroidManifest.xml`
## Step 4: Trigger server scan
```bash
curl -X POST http://localhost:9800/api/scan
```
Or restart the server if preferred:
```bash
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
```bash
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:
```bash
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 like `version_name` may show `unknown` if 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": null` in the JSON.
- **KSP plugin**: Required for Room. Keep only in `app/build.gradle`, not root `build.gradle`.
- **Navigation**: Use `<fragment>` tag (not `FrameLayout`) with `android: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 false` to avoid blocking builds.