# Local App Store A private Android app store for your local network. Serve APKs from your own repository and install them on your devices via the Android app or browse/download from any browser. ## Architecture - **Server**: FastAPI application that serves APKs and metadata over HTTP - **Android Client**: Kotlin app with Material Design 3 UI, Room database, and PackageInstaller integration - **Web Frontend**: Single-page HTML/JS app served by the server, accessible from any browser - **Signing**: Self-signed keystore for both the store app and distributed apps ## Quick Start ### 1. Server Setup ```bash cd server # Activate virtual environment source venv/bin/activate # Install dependencies (if not already done) pip install -r requirements.txt # Drop your APKs into the repos/ directory cp /path/to/your/app.apk repos/ # Start the server (runs on all interfaces, port 8080) python main.py ``` The server auto-scans `repos/` on startup. To rescan after adding new APKs: ```bash curl -X POST http://localhost:8080/api/scan ``` You can also add app metadata (name, description, icon, screenshots) by creating a JSON file alongside each APK: ``` repos/ myapp.apk myapp.json # Optional metadata override myapp_screenshots/ # Optional screenshots (*.png) ``` Example `myapp.json`: ```json { "name": "My App", "description": "A great app for doing things", "icon": "res/mipmap-hdpi/ic_launcher.png", "screenshots": ["screenshot1.png", "screenshot2.png"] } ``` ### 2. Web Frontend Once the server is running, open any browser and navigate to: ``` http://:8080 ``` The web frontend provides: - Browse all apps in a responsive grid - Search apps by name or description - View app details (description, permissions, screenshots) - Download APK files directly - Scan repository for new apps ### 3. Signing APKs ```bash cd signing # Generate keystore (already done - localstore.keystore exists) # To regenerate: ./generate-key.sh # Sign an APK before adding it to repos/ ./sign-apk.sh /path/to/your/app.apk ``` All APKs should be signed with the same keystore for consistency. ### 4. Building the Android Client Prerequisites: Java 17, Android SDK (platform-tools, build-tools 34.0.0, platforms android-34). ```bash cd android # Configure SDK path (adjust to your SDK location) echo "sdk.dir=/path/to/android-sdk" > local.properties # Build debug APK ./gradlew :app:assembleDebug # Build release APK (signed with localstore.keystore) ./gradlew :app:assembleRelease ``` APKs are output to `app/build/outputs/apk/`. ### 5. Installing on Android Device ```bash # Connect device via USB or use wireless ADB # Install debug build /home/jarian/android-sdk/platform-tools/adb install app/build/outputs/apk/debug/app-debug.apk # Install release build /home/jarian/android-sdk/platform-tools/adb install app/build/outputs/apk/release/app-release.apk ``` First time setup in the app: 1. Open the app -> go to Settings 2. Enter your server IP address (e.g., `192.168.8.128`) 3. Click "Test Connection" to verify 4. Click "Save" 5. Grant "Install unknown apps" permission when prompted ## API Endpoints | Method | Endpoint | Description | |--------|----------|-------------| | GET | `/` | Web frontend | | GET | `/api/apps` | List all apps (supports `?search=`, `?offset=`, `?limit=`) | | GET | `/api/apps/{id}` | Get app details | | GET | `/api/apps/{id}/download` | Download APK file | | GET | `/api/apps/{id}/icon` | Get app icon (128x128 PNG) | | GET | `/api/apps/{id}/screenshots` | List screenshots | | GET | `/api/apps/{id}/screenshots/{file}` | Get screenshot image | | POST | `/api/apps/update-check` | Check for updates | | POST | `/api/scan` | Trigger repository rescan | | GET | `/api/status` | Server status info | ## Configuration Server config is in `server/config.yaml`: ```yaml server: host: "0.0.0.0" # Bind to all interfaces port: 8080 # HTTP port repository: path: "./repos" # APK storage directory ``` ## Project Structure ``` app-store/ server/ main.py # FastAPI application + web frontend app.py # Pydantic models scanner.py # APK metadata scanner config.yaml # Server configuration requirements.txt # Python dependencies venv/ # Python virtual environment repos/ # Drop APKs here web/ index.html # Web frontend (HTML/CSS/JS) android/ # Gradle project (CLI build) build.gradle # Root build config settings.gradle # Project settings local.properties # SDK path app/ build.gradle # App build config (KSP, Room, etc.) src/main/ AndroidManifest.xml kotlin/com/localstore/ LocalStoreApp.kt data/ # API, models, database, repository, settings ui/ # Fragments, ViewModels, adapters installer/ # PackageInstaller wrapper res/ # Layouts, strings, themes, navigation, colors gradle/ # Gradle wrapper signing/ generate-key.sh # Create keystore sign-apk.sh # Sign APKs localstore.keystore # Self-signed keystore app-debug.apk # Debug build app-release.apk # Release build (signed) README.md ``` ## Android Permissions The app requires: - `INTERNET` - connect to the server - `REQUEST_INSTALL_PACKAGES` - install APKs (user grants in Settings) - `ACCESS_NETWORK_STATE` - check connectivity ## Security Notes - The keystore is self-signed. For production use, consider a proper CA-signed certificate. - The server serves over HTTP. For HTTPS, configure a reverse proxy (nginx/Caddy) with Let's Encrypt. - All APKs should be signed with the same keystore to ensure trust chain. ## Troubleshooting **Apps not appearing after adding to repos/** - Run `POST /api/scan` or restart the server **Icon not loading** - The server extracts icons from APK resources. If extraction fails, specify icon path in the `.json` metadata file. **Installation fails** - Ensure "Install unknown apps" permission is granted for the app store app in Android Settings **Connection fails** - Check server IP in Settings matches your server's actual IP - Ensure both devices are on the same network - Check firewall allows port 8080 **Gradle build fails** - Ensure Java 17 is installed and JAVA_HOME is set - Ensure Android SDK is installed and `local.properties` points to it - Run `./gradlew :app:clean` before rebuilding