Kolibri can be deployed as an Android application, enabling mobile access to educational content on tablets and phones. The Android app is built using Python-for-Android and distributed as an APK.
Kolibri includes platform detection for Android environments:
# From kolibri/utils/android.py
ANDROID_PLATFORM_SYSTEM_VALUE = "Android"
def on_android ():
"""Check if running on Android platform."""
return hasattr (sys, "getandroidapilevel" )
This detection is used throughout Kolibri to handle Android-specific behavior:
Security constraints : Android restricts access to /proc, requiring different system information gathering
File system limitations : Different paths and permissions compared to desktop Linux
API level awareness : The getandroidapilevel() method returns the minimum Android API level the Python build targets
The Android API level returned by sys.getandroidapilevel() represents the minimum API level Python was compiled against, not the current runtime Android version.
Build Pipeline
The Android APK is built from the kolibri-static PyPI package:
Git release branch
|
v
kolibri-static
(PyPI package)
|
v
Android APK
Prerequisites
Install Build Tools
Required tools for building Android APK:
Python 3.6+ (matching Kolibri’s requirements)
Android SDK and NDK
Python-for-Android (p4a)
Buildozer (optional, for simplified builds)
Install Kolibri Static
pip install kolibri-static
This package includes all dependencies bundled, ideal for offline environments.
Configuration
Android-Specific Settings
When running on Android, Kolibri uses platform-specific configurations:
# Android detection affects:
# - File descriptor calculations (no /proc access)
# - Thread pool sizing (limited by Android constraints)
# - Storage paths (Android-specific directories)
# - System info gathering (different APIs)
KOLIBRI_HOME on Android
On Android, KOLIBRI_HOME is typically set to:
/data/data/ <package_name>/files/.kolibri
This location provides:
App-private storage
Persistent across app updates
Automatic backup support (if enabled)
Protected from other apps
Environment Variables
Recommended environment variables for Android deployment:
export KOLIBRI_HOME = / data / data / org . learningequality . kolibri / files /. kolibri
export KOLIBRI_HTTP_PORT = 5000
export KOLIBRI_RUN_MODE = android
export KOLIBRI_NO_FILE_BASED_LOGGING = 1 # Log to logcat instead
Building the APK
Using Python-for-Android
Basic build process:
Create Build Recipe
Create a Python-for-Android recipe for Kolibri: # recipe.py
from pythonforandroid.recipe import PythonRecipe
class KolibriRecipe ( PythonRecipe ):
name = 'kolibri'
version = '0.17.0' # Update to match release
url = 'https://pypi.org/packages/source/k/kolibri-static/kolibri-static- {version} .tar.gz'
depends = [ 'python3' , 'sqlite3' , 'openssl' ]
site_packages_name = 'kolibri'
Build with P4A
p4a apk \
--private /path/to/kolibri \
--package org.learningequality.kolibri \
--name "Kolibri" \
--version 0.17.0 \
--bootstrap sdl2 \
--requirements python3,kolibri-static \
--permission INTERNET \
--permission WRITE_EXTERNAL_STORAGE \
--permission READ_EXTERNAL_STORAGE \
--orientation sensor
Using Buildozer
Buildozer simplifies the build process:
Create buildozer.spec
[app]
title = Kolibri
package.name = kolibri
package.domain = org.learningequality
source.dir = .
source.include_exts = py,png,jpg,kv,atlas,json
version = 0.17.0
requirements = python3, kolibri-static ==0.17.0
permissions = INTERNET,WRITE_EXTERNAL_STORAGE,READ_EXTERNAL_STORAGE
orientation = sensor
[buildozer]
log_level = 2
warn_on_root = 1
android.api = 28
android.minapi = 21
android.ndk = 23b
android.sdk = 30
Build APK
Release build: buildozer android release
Application Lifecycle
Starting Kolibri on Android
The Android app needs a service wrapper to run Kolibri:
# main.py
import os
import sys
from android.runnable import run_on_ui_thread
from jnius import autoclass
# Set environment
os.environ[ 'KOLIBRI_HOME' ] = '/data/data/org.learningequality.kolibri/files/.kolibri'
os.environ[ 'KOLIBRI_HTTP_PORT' ] = '5000'
os.environ[ 'KOLIBRI_RUN_MODE' ] = 'android'
# Start Kolibri
from kolibri.utils.cli import main
if __name__ == '__main__' :
# Start in background
sys.argv = [ 'kolibri' , 'start' , '--foreground' ]
main()
Background Service
Create a persistent background service:
from android.service import Service
from android import AndroidService
class KolibriService ( Service ):
def onCreate ( self ):
# Initialize Kolibri
pass
def onStartCommand ( self , intent , flags , startId ):
# Start Kolibri server
return AndroidService. START_STICKY
def onDestroy ( self ):
# Stop Kolibri server
pass
App Configuration
Permissions
Required Android permissions:
< uses-permission android:name = "android.permission.INTERNET" />
< uses-permission android:name = "android.permission.WRITE_EXTERNAL_STORAGE" />
< uses-permission android:name = "android.permission.READ_EXTERNAL_STORAGE" />
< uses-permission android:name = "android.permission.WAKE_LOCK" />
< uses-permission android:name = "android.permission.FOREGROUND_SERVICE" />
Optional permissions:
< uses-permission android:name = "android.permission.ACCESS_NETWORK_STATE" />
< uses-permission android:name = "android.permission.ACCESS_WIFI_STATE" />
Database Considerations
On Android, Kolibri uses SQLite (PostgreSQL is not available):
# Automatically configured for Android
DATABASES = {
"default" : {
"ENGINE" : "kolibri.deployment.default.db.backends.sqlite3" ,
"NAME" : os.path.join( KOLIBRI_HOME , "db.sqlite3" ),
"OPTIONS" : { "timeout" : 100 },
},
}
Android has strict file descriptor limits. The thread pool is automatically constrained to prevent exceeding these limits.
Content Pre-loading
For offline deployment, pre-load content into the APK:
Prepare Content
Export content channels on a desktop system: # Export channel
kolibri manage exportchannel < channel_i d > /path/to/export
# Export content
kolibri manage exportcontent < channel_i d > /path/to/export
Include in APK
Add content to the APK assets: # Copy to assets directory
mkdir -p assets/content
cp -r /path/to/export/ * assets/content/
Update build configuration to include assets.
Import on First Run
Import content from assets on first launch: import shutil
from android.storage import app_storage_path
assets_path = os.path.join(app_storage_path(), 'content' )
if os.path.exists(assets_path):
# Copy to KOLIBRI_HOME
shutil.copytree(
assets_path,
os.path.join(os.environ[ 'KOLIBRI_HOME' ], 'content' )
)
See Offline Setup for more details on content pre-loading.
Distribution
APK Signing
For production distribution, sign the APK:
# Generate keystore (first time only)
keytool -genkey -v \
-keystore kolibri-release.keystore \
-alias kolibri \
-keyalg RSA \
-keysize 2048 \
-validity 10000
# Sign APK
jarsigner -verbose \
-sigalg SHA1withRSA \
-digestalg SHA1 \
-keystore kolibri-release.keystore \
bin/Kolibri-0.17.0-release-unsigned.apk \
kolibri
# Align APK
zipalign -v 4 \
bin/Kolibri-0.17.0-release-unsigned.apk \
bin/Kolibri-0.17.0-release.apk
Distribution Methods
Direct Download Host APK on a web server for direct download
USB Transfer Copy APK to devices via USB for offline distribution
Local Network Share APK over local network using FTP/HTTP server
SD Card Pre-load APK on SD cards for bulk distribution
For Google Play Store distribution, additional compliance and testing requirements apply. Consult Google Play policies.
Testing
Android Testing Features
Kolibri includes Android-specific integration tests:
# From integration_testing/.../android-app-bar.feature
Feature : Android App Bar
The app bar on Android devices has specific behaviors
Scenario : App bar displays on Android
Given I am on an Android device
When I open Kolibri
Then I should see the Android app bar
Testing Checklist
APK installs successfully
App requests appropriate permissions
First launch completes setup wizard
Database initialization succeeds
Server starts and binds to port
Web UI loads in WebView/browser
Content import/export works
User authentication functions
Offline mode operates correctly
Debugging
Debug the Android app using logcat:
# View all logs
adb logcat
# Filter Python logs
adb logcat | grep python
# Filter Kolibri logs
adb logcat | grep kolibri
# Clear logs and follow
adb logcat -c && adb logcat
Troubleshooting
Common Issues
NDK version mismatch # Specify NDK version explicitly
buildozer android debug \n android.ndk = 23b
Missing dependencies # Install build dependencies
sudo apt-get install python3-dev build-essential \
libsqlite3-dev libffi-dev libssl-dev
Port already in use # Use different port
os.environ[ 'KOLIBRI_HTTP_PORT' ] = '5001'
Storage permission denied # Request permissions at runtime (Android 6+)
from android.permissions import request_permissions, Permission
request_permissions([
Permission. WRITE_EXTERNAL_STORAGE ,
Permission. READ_EXTERNAL_STORAGE
])
Best Practices
Minimize APK Size
Use kolibri-static package
Exclude unnecessary dependencies
Use APK split or Android App Bundle
Compress assets
Optimize Performance
Constrain thread pool for mobile
Use appropriate cache sizes
Implement proper lifecycle management
Handle background/foreground transitions
Plan for Offline
Pre-load essential content
Test without network connectivity
Implement proper error handling
Provide offline documentation
Test Thoroughly
Test on multiple devices
Test different Android versions
Test with limited resources
Test update scenarios
Next Steps
Offline Setup Pre-load content for network-free operation
Production Deployment Learn about server-based deployment