first commit
This commit is contained in:
commit
ad51890cb2
223
README.md
Normal file
223
README.md
Normal file
@ -0,0 +1,223 @@
|
||||
# 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
|
||||
Loading…
x
Reference in New Issue
Block a user