Compile python apps for Android and iOS.
Project description
ksproject
Kivy School Project Manager — A cross-platform build tool for Kivy apps targeting Android (iOS/macOS, windows and linux soon).
ksproject generates a pure AGP (Android Gradle Plugin) Gradle project, downloads the Android SDK, NDK, and JDK completely on demand, and builds your APK, AAB, or AAR.
No buildozer, no python-for-android at build time — just a clean, native Gradle project under project_dist/gradle/ that you can open and debug directly in Android Studio.
Requirements
- Python 3.13
- uv (used for environment and dependency management)
Everything else (Android SDK, NDK, JDK, Gradle wrapper) is downloaded automatically on the first build into .kivyschool/ inside your project directory or globally.
1. Create a New Project
uv init --package hello-world --python 3.13
cd hello-world
uv add git+https://github.com/kivy-school/ksproject@master --dev
uv run ksproject init
ksproject init handles the heavy lifting of project scaffolding. It:
- Writes a starter
main.pyandapp.kvinsrc/hello_world/. - Creates a
src/hello_world/services/directory (with an__init__.py) for Android background services. - Adds a comprehensive
[tool.kivy-school]block to yourpyproject.toml. - Generates
AndroidManifest.tmpl.xmlandbuild.tmpl.gradle.ktsin your project root so you can customize native build behaviors directly. - Creates a
.javafolder for native code injections.
2. Add Kivy
uv add kivy
3. Add Platform-Specific Dependencies
pyjnius is only meaningful on Android. Mark it with a PEP 508 marker so uv won't try to install it on macOS, Linux, or Windows:
[!NOTE] These are already present by default!!
uv add "pyjnius ; sys_platform == 'android'"
Same trick for iOS (coming soon):
uv add "pyobjus ; sys_platform == 'ios'"
Optional scientific stack:
uv add numpy matplotlib
4. Run on Desktop
uv run hello-world
This launches the entry point declared in pyproject.toml — the exact same code that will run on Android, with no changes needed.
5. Build for Android
uv run ksproject android build
The first run downloads the required toolchain:
- Android command-line tools + SDK (API
36, build-tools) - NDK
28c(or your configured version) - Temurin JDK 21 (via
sdkman) - Gradle 9.5.0 wrapper
- CPython-for-Android prebuilt wheels
The output APK lands at:
project_dist/gradle/app/build/outputs/apk/debug/app-debug.apk
Release Builds & Formats
# Build a Release APK
uv run ksproject android build release
# Build an Android App Bundle (.aab) for Google Play
uv run ksproject android build release --bundle
# Build an AAR (library) instead of an app
uv run ksproject android build release --aar
6. Run on an Emulator
List attached devices and available AVDs:
uv run ksproject android devices
Example output:
avd name=Pixel9
device serial=emulator-5554 state=device model=sdk_gphone64_arm64
Boot an AVD by name (boots it, builds the app, installs it, and launches):
uv run ksproject android run --name Pixel9
7. Run on a Real Device
Plug a phone in over USB with USB debugging enabled (Settings → Developer options → USB debugging). Authorise the host when the dialog pops up.
Find its adb serial:
uv run ksproject android devices
# device serial=R3CN30XXXXX state=device model=SM-G998B
Run by serial:
uv run ksproject android run --uuid R3CN30XXXXX
(Append --variant release to run the production build instead of debug).
8. App Signing & Keystore Generation
ksproject includes built-in tools for generating secure keystores and signing your production artifacts (.apk and .aab) using standard Android SDK/JDK tools.
💡 Pro-Tip: Streamline with a .env File
To keep your passwords out of your terminal history and to make commands significantly shorter, save your credentials in a .env file at the root of your project. ksproject will automatically detect them. Create a .env file and add your variables like this:
# .env
# App Signing Credentials
KEYSTORE="my-release-key.jks"
KEYALIAS="myapp"
STOREPASS="SecretPass123"
#KEYPASS="defaults_to_storepass"
1. Generate a Release Keystore
This is required only when your key is outdated or you need to generate a new key for the first time.
Using explicit flags:
uv run ksproject android genkey --out my-release-key.jks --storepass SecretPass123 --keyalias myapp
Using your .env file:
uv run ksproject android genkey --dname "CN=My App, O=My Org"
[!NOTE] You can also append --dname "CN=My App, O=My Org" to either command if you need to provide a specific Distinguished Name string for the certificate.
2. Sign a Built Artifact
The orchestrator automatically detects your built release binary and signs it. Using explicit flags:
# Sign the APK
uv run ksproject android sign --keystore my-release-key.jks --storepass SecretPass123 --keyalias myapp
# Sign an App Bundle (.aab)
uv run ksproject android sign --bundle --keystore my-release-key.jks --storepass SecretPass123 --keyalias myapp
Using your .env file:
# Sign the APK
uv run ksproject android sign
# Sign an App Bundle (.aab)
uv run ksproject android sign --bundle
[!NOTE] By default,
ksproject android signtargets thereleasedirectory. You can optionally pass--variant debugto sign a debug artifact, or pass--keypass <password>if your security profile requires an alias password distinct from your core keystore storage password. Similarly, you can append--variant releaseto therunpipeline to run production builds.
9. Advanced Customization (Templates)
When you run init, ksproject creates two highly important template files in your root directory. The build engine uses these to generate the final project_dist/ payload.
AndroidManifest.tmpl.xml: Add custom broadcast receivers, specific hardware features, or specific intent filters here.ksprojectwill automatically inject your Python services, meta-data, and permissions at build time.build.tmpl.gradle.kts: Modify your core Gradle setup. Need custom Maven repositories orexternalNativeBuildflags? Put them here.- Note on Push Notifications: The default template automatically maps the system environment variable
ONESIGNAL_APP_IDto an Android String Resource (onesignal_app_id), making it instantly available for libraries likepyonesignal.
10. Configuration (pyproject.toml)
Your pyproject.toml is the single source of truth for your app's metadata.
[tool.uv]
index-strategy = "unsafe-best-match"
find-links = ["./wheelhouse"]
[tool.uv.pip]
extra-index-url = [
"https://pypi-index.psychowaspx.workers.dev/simple",
"https://pypi.anaconda.org/kivyschool/simple",
]
find-links = ["./wheelhouse"]
#### kivy-school configuration ####
[tool.kivy-school]
app_name = "Hello World"
[tool.kivy-school.android]
package_name = "org.example.hello_world"
# version_name = "0.1" # change when you want to release a very new version
version_code = 1 # keep increasing to make an update to existing version_name (bug fixes/security releases)
# icon = "relative to pyproject (png)"
# presplash_color = "#FFFFFF"
# presplash = "relative to pyproject (png,jpg,gif)"
# presplash_lottie = "relative to pyproject (json)"
archs = [
"arm64-v8a",
# "x86_64" # Uncomment to support emulators (increases APK size)
]
# API and Toolchain Versions
api = 36
min_api = 24
sdk = "36"
ndk = "28c"
ndk_api = 24
# --- Toolchain Management ---
global_tools = true # Set to false to use project-local SDK/NDK (./.kivyschool)
# global_tools_path = "~/.kivyschool" # Override root path when global_tools = true
# --- Gradle & Manifest Configs ---
gradle_dependencies = [
# "com.onesignal:OneSignal:[5.6.1, 5.9.99]"
]
gradle_plugins = [
# 'id("com.google.gms.google-services") version "4.4.2" apply false'
]
permissions = [
# "POST_NOTIFICATIONS", "INTERNET", "ACCESS_NETWORK_STATE"
]
# --- File Inclusions ---
# Format: [Destination, Source(s)]
# include_files = [
# ["gradle/app", ["./google-services.json", "./some-other-config.xml"]]
# ]
# pre_build = "path/to/android_prebuild.py"
# post_build = "path/to/android_prebuild.py"
byte_compile_python = true
# <meta-data> entries inside <application>
# [tool.kivy-school.android.meta_data]
# "com.google.android.gms.ads.APPLICATION_ID" = "ca-app-pub-xxxxxxxx~xxxxxxxx"
# --- Background Services ---
# [[tool.kivy-school.android.services]]
# name = "MyService1"
# start_type = "START_NOT_STICKY"
# entrypoint = "hello_world.services.myservice1"
# foreground = true
# foreground_service_type = "location|dataSync"
# notification_title = "MyService1 Running"
# notification_text = "Service is managing background data."
# notification_icon = "stat_notify_sync"
#### iOS Configuration (Coming Soon) ####
[tool.kivy-school.ios]
bundle_id = "org.example.hello_world"
info_plist = {}
entitlements = {}
permissions = []
frameworks = []
# developer_team = "ABC123XYZ"
Command Reference
| Command | What it does |
|---|---|
ksproject init |
Scaffold a new project, template files, and pyproject.toml configurations |
ksproject android build [debug|release] |
Build an APK artifact |
ksproject android build --aar [debug|release] |
Build an AAR library instead of an APK |
ksproject android build --bundle [debug|release] |
Build an AAB (Android App Bundle) instead of an APK |
ksproject android build [debug|release] --clean |
Perform a clean step before executing the build task |
ksproject android sign --keystore <file> --storepass <pass> --keyalias <alias> |
Sign the built APK inside the target directory using apksigner |
ksproject android sign --bundle --keystore <file> --storepass <pass> --keyalias <alias> |
Sign the built App Bundle (.aab) inside the target directory using jarsigner |
ksproject android genkey --out <file> --storepass <pass> --keyalias <alias> |
Generate a secure Java Keystore (.jks) using keytool |
ksproject android get-path [sdk|ndk|emulator] |
Print the fully resolved local file system path for the specified tool |
ksproject android devices |
List connected active adb serial devices and available AVD profiles |
ksproject android run --name <AVD> |
Build, install, and launch the target application on a simulated emulator |
ksproject android run --uuid <serial> |
Build, install, and launch the target application on a physical USB device |
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ksproject-0.0.1.post0.tar.gz.
File metadata
- Download URL: ksproject-0.0.1.post0.tar.gz
- Upload date:
- Size: 108.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0cf9589ca949af0933426a491312dfaed94a5677e65fe826b71ff34fbe489693
|
|
| MD5 |
5f0d0897e1c68ddc7be83916b046f97a
|
|
| BLAKE2b-256 |
faee56eb1089399c6ea2c1a3183cdf000a5aa960049a7e4ed937493be6680571
|
Provenance
The following attestation bundles were made for ksproject-0.0.1.post0.tar.gz:
Publisher:
pypi-release.yml on kivy-school/ksproject
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ksproject-0.0.1.post0.tar.gz -
Subject digest:
0cf9589ca949af0933426a491312dfaed94a5677e65fe826b71ff34fbe489693 - Sigstore transparency entry: 2153817940
- Sigstore integration time:
-
Permalink:
kivy-school/ksproject@f8337525b0e72f431bbb7f4b36e4f83dc09bfefe -
Branch / Tag:
refs/tags/0.0.1.post0 - Owner: https://github.com/kivy-school
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-release.yml@f8337525b0e72f431bbb7f4b36e4f83dc09bfefe -
Trigger Event:
push
-
Statement type:
File details
Details for the file ksproject-0.0.1.post0-py3-none-any.whl.
File metadata
- Download URL: ksproject-0.0.1.post0-py3-none-any.whl
- Upload date:
- Size: 119.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff1ce49f893d18e18cb83d7674887b11ef8edf0e711506ced43774705b0fc48d
|
|
| MD5 |
b8baeb1251201c52b233d0d0a4523a7c
|
|
| BLAKE2b-256 |
a57ea75d2a4d387a0c803e7fd5a2c7e44dea096a8362d282cc5a1fce57be5c5d
|
Provenance
The following attestation bundles were made for ksproject-0.0.1.post0-py3-none-any.whl:
Publisher:
pypi-release.yml on kivy-school/ksproject
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ksproject-0.0.1.post0-py3-none-any.whl -
Subject digest:
ff1ce49f893d18e18cb83d7674887b11ef8edf0e711506ced43774705b0fc48d - Sigstore transparency entry: 2153817992
- Sigstore integration time:
-
Permalink:
kivy-school/ksproject@f8337525b0e72f431bbb7f4b36e4f83dc09bfefe -
Branch / Tag:
refs/tags/0.0.1.post0 - Owner: https://github.com/kivy-school
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-release.yml@f8337525b0e72f431bbb7f4b36e4f83dc09bfefe -
Trigger Event:
push
-
Statement type: