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

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:

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:

{
  "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

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).

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

# 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:

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
Description
Private local Android app store — serve APKs over FastAPI and install them on your own devices via a Material Design 3 client.
Readme 120 KiB
Languages
Kotlin 59.1%
Python 27.4%
HTML 13.5%