223 lines
6.3 KiB
Markdown
223 lines
6.3 KiB
Markdown
# 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://<server-ip>: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 |