Skip to content

Commit ba8b5c7

Browse files
rtibblesclaude
andcommitted
Update README for Chaquopy-based architecture
Rewrite documentation to reflect the new build system, project structure, and development workflow. Covers Gradle setup, Chaquopy Python integration, emulator usage, and CI configuration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 1b5ffdf commit ba8b5c7

1 file changed

Lines changed: 197 additions & 72 deletions

File tree

README.md

Lines changed: 197 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,119 +1,244 @@
1-
# Kolibri Android Installer
1+
# Kolibri Android
22

3-
Wraps Kolibri in an android-compatibility layer. Relies on Python-For-Android to build the APK and for compatibility on the Android platform.
3+
Android application for Kolibri Learning Platform using Chaquopy for Python integration.
44

5-
## Development Flow
5+
## Overview
66

7-
1. Setup a Python virtual environment in which to do development. The Kolibri developer documentation has a [How To guide for doing this with pyenv](https://kolibri-dev.readthedocs.io/en/develop/howtos/pyenv_virtualenv.html) but any Python virtualenv should work.
7+
This project packages [Kolibri](https://learningequality.org/kolibri/) as an Android application using:
8+
- **Chaquopy** - Runs Python code directly on Android
9+
- **HTTP Server + Service Worker** - Python runs an HTTP server, WebView loads content, Service Worker handles caching
10+
- **WorkManager** - Handles background tasks in a separate process
11+
- **Modern Android Architecture** - Clean package structure, lifecycle-aware components
812

9-
2. Ensure you have all [necessary packages for Python for Android](https://python-for-android.readthedocs.io/en/latest/quickstart.html#installing-prerequisites). Ensure you install java version 1.17, `sudo apt install openjdk-17-jdk` , and set it as the default java version: `sudo update-alternatives --auto javac` and `sudo update-alternatives --auto java`.
13+
## Quick Start
1014

11-
3. The `make setup` command will install the Android SDK and Android NDK.
15+
```bash
16+
# 1. Ensure Java 17+ is installed
17+
java -version
1218

13-
N.B. if you would like these to be installed to a different location then you can set an environment variable, e.g.:
14-
By default it is set to `export ANDROID_SDK_ROOT=./android_root`
19+
# 2. Setup Python environment (recommended)
20+
uv sync --extra build
21+
uv pip install -r requirements.txt
22+
source .venv/bin/activate
1523

16-
Run `make setup`.
24+
# 3. Setup Android SDK and emulator
25+
make setup
1726

18-
4. Install the Python dependencies:
27+
# 4. Download Kolibri tar file
28+
make get-tar tar=https://github.com/learningequality/kolibri/releases/download/v0.17.0/kolibri-0.17.0.tar.gz
1929

20-
`pip install -r requirements.txt`
30+
# 5. Build!
31+
make kolibri.apk.unsigned
32+
```
33+
34+
Output: `dist/kolibri-*.apk`
35+
36+
## Development Setup
37+
38+
### Prerequisites
39+
40+
- **Java Development Kit (JDK) 17+**
41+
- Fedora/RHEL: `sudo dnf install java-17-openjdk-devel`
42+
- Ubuntu/Debian: `sudo apt install openjdk-17-jdk`
43+
- macOS: `brew install openjdk@17`
2144

22-
5. Build or download a Kolibri tar file, and place it in the `tar/` directory.
45+
- **Python 3.10+** - For build scripts and Chaquopy
2346

24-
To download a Kolibri WHL file, you can use `make get-tar tar=<URL>` from the command line. It will download it and put it in the correct directory.
47+
### Initial Setup
2548

26-
6. By default the APK/AAB will be built for most architectures supported by Python for Android. To build for a smaller set of architectures, set the `ARCHES` environment variable. Run `p4a archs` to see the available targets.
49+
1. **Clone the repository**
50+
```bash
51+
git clone https://github.com/learningequality/kolibri-installer-android.git
52+
cd kolibri-installer-android
53+
```
2754

28-
7. Run `make p4a_android_project` this will do all of the Python for Android setup up. After, you can run `make kolibri.apk` or `make kolibri.apk.unsigned` if you want to build the apk in the console.
55+
2. **Set up Python virtual environment**
56+
```bash
57+
uv venv
58+
source .venv/bin/activate
59+
uv pip install ".[build]" -r requirements.txt
60+
```
2961

30-
N.B. You will need to rerun this step any time you update the Kolibri WHL file you are using, or any time you update the Python code in this repository.
62+
3. **Set up Android SDK** (automatically downloads SDK, NDK, emulator)
63+
```bash
64+
make setup
65+
```
3166

32-
8. You can now run Android Studio and open the folder `python-for-android/dists/kolibri` as the project folder to work from. You should be able to make updates to Java code, resource files, etc. using Android Studio, and build and run the project using Android Studio, including launching into emulators and real physical devices.
67+
4. **Get Kolibri tar file**
68+
```bash
69+
make get-tar tar=<URL_TO_KOLIBRI_TAR>
70+
```
3371

34-
N.B. When you rerun step 7, it will complain loudly and exit early if you have uncommitted changes in the python-for-android folder. Any changes should be committed (even if in a temporary commit) before rerunning this step, as we use git stash to undo any changes in the Android project caused by the Python for Android project bootstrapping process. Also, when rerunning step 5, the Android version will not have incremented, meaning that any emulator or physical device will need to have Kolibri explicitly uninstalled for any changes to Python code to be updated on install.
72+
5. **Build the APK**
73+
```bash
74+
make kolibri.apk.unsigned
75+
```
3576

36-
## Debugging the app
77+
## Architecture
3778

38-
1. When running the app from Android Studio, if you are using an emulator, it is possible that there will be many warning messages due to GPU emulation. In the logcat tab, update the filter to this `package:mine & -tag:eglCodecCommon` to hide those errors from the logcat output.
79+
The app runs Kolibri as an HTTP server in Python, with a WebView displaying the UI:
3980

40-
## Building from the commandline
81+
```
82+
┌─────────────────────────────────────────────────────────────┐
83+
│ Main Process │
84+
│ ┌────────────────┐ ┌──────────────────┐ │
85+
│ │ WebViewActivity│ │ KolibriServer │ │
86+
│ │ │ │ Service │ │
87+
│ │ ┌──────────┐ │ │ ┌────────────┐ │ │
88+
│ │ │ WebView │──┼─ HTTP ──┼─▶│ Python │ │ │
89+
│ │ │ │ │ │ │ HTTP │ │ │
90+
│ │ │ Service │ │ │ │ Server │ │ │
91+
│ │ │ Worker │ │ │ │ (Kolibri) │ │ │
92+
│ │ └──────────┘ │ │ └────────────┘ │ │
93+
│ └────────────────┘ └──────────────────┘ │
94+
└─────────────────────────────────────────────────────────────┘
95+
96+
┌─────────────────────────────────────────────────────────────┐
97+
│ :task_worker Process │
98+
│ ┌────────────────┐ ┌──────────────────┐ │
99+
│ │ WorkController │────────▶│ WorkManager │ │
100+
│ │ Service │ │ Workers │ │
101+
│ └────────────────┘ └──────────────────┘ │
102+
└─────────────────────────────────────────────────────────────┘
103+
```
104+
105+
### Key Components
41106

42-
1. Run `make kolibri.apk.unsigned` to build the development apk. Watch for success at the end, or errors, which might indicate missing build dependencies or build errors. If successful, there should be an APK in the `dist/` directory.
107+
- **KolibriServerService** - Starts Python HTTP server in background
108+
- **WebViewActivity** - Displays Kolibri UI, requests notification permission
109+
- **WorkManager** - Runs background tasks (imports, syncs) in separate process
110+
- **Task Reconciler** - Syncs WorkManager state with Kolibri's job database
43111

44-
## Installing the apk
45-
1. Connect your Android device over USB, with USB Debugging enabled.
112+
## Project Structure
46113

47-
2. Ensure that `adb devices` brings up your device. Afterward, run `make install` to install onto the device.
114+
```
115+
app/src/main/
116+
├── java/org/learningequality/Kolibri/
117+
│ ├── App.java # Application entry point
118+
│ ├── WebViewActivity.java # Main activity with WebView
119+
│ ├── KolibriServerService.java # HTTP server service
120+
│ ├── KolibriServerViewModel.java # Server state management
121+
│ ├── KolibriEnvironmentSetup.java # Environment configuration
122+
│ ├── WorkController.java # Task scheduling
123+
│ ├── WorkControllerService.java # Worker process IPC
124+
│ ├── notification/ # Notification system
125+
│ ├── task/ # Task worker interfaces
126+
│ │ ├── Task.java # WorkManager wrapper
127+
│ │ └── TaskWorkerImpl.java # Observer pattern for progress
128+
│ ├── workers/ # WorkManager workers
129+
│ │ ├── BaseTaskWorker.java # Base class with notifications
130+
│ │ ├── ForegroundWorker.java # Long-running tasks
131+
│ │ └── BackgroundWorker.java # Short tasks
132+
│ └── util/ # Utility classes
133+
└── python/
134+
├── main.py # HTTP server entry point
135+
├── android_utils.py # Android-specific utilities
136+
├── taskworker.py # Task execution
137+
├── task_reconciler.py # Task state reconciliation
138+
├── task_status.py # Task status updates
139+
└── android_app_plugin/ # Kolibri plugin for Android
140+
└── kolibri_plugin.py # StorageHook for task scheduling
141+
```
48142

143+
## Building
49144

50-
## Running the apk from the terminal
145+
### Debug Build
51146

52-
1. Run `adb shell am start -n org.learningequality.Kolibri/org.kivy.android.PythonActivity`
147+
```bash
148+
make kolibri.apk.unsigned
149+
```
53150

54-
### Server Side
55-
Run `adb logcat -v brief python:D *:F` to get all debug logs from the Kolibri server
151+
Output: `dist/kolibri-*.apk`
56152

57-
### Client side
58-
1. Start the Kolibri server via Android app
59-
2. Open a browser and see debug logs
60-
- If your device doesn't aggressively kill the server, you can open Chrome and use remote debugging tools to see the logs on your desktop.
61-
- You can also leave the app open and port forward the Android device's Kolibri port using [adb](https://developer.android.com/studio/command-line/adb#forwardports):
62-
```
63-
adb forward tcp:8080 tcp:8081
64-
```
65-
then going into your desktop's browser and accessing `localhost:8081`. Note that you can map to any port on the host machine, the second argument.
153+
### Release Build
66154

67-
Alternatively, you can debug the webview directly. Modern Android versions should let you do so from the developer settings.
155+
```bash
156+
export RELEASE_KEYSTORE=/path/to/keystore.jks
157+
export RELEASE_KEYALIAS=key-alias
158+
export RELEASE_KEYSTORE_PASSWD=keystore-password
159+
export RELEASE_KEYALIAS_PASSWD=key-password
160+
make kolibri.apk
161+
```
68162

69-
You could also do so using [Weinre](https://people.apache.org/~pmuellr/weinre/docs/latest/Home.html). Visit the site to learn how to install and setup. You will have to build a custom Kolibri .whl file that contains the weinre script tag in the [base.html file](https://github.com/learningequality/kolibri/blob/develop/kolibri/core/templates/kolibri/base.html).
163+
### All Make Targets
164+
165+
```bash
166+
make help # Show all available targets
167+
make setup # Setup SDK, NDK, and emulator
168+
make kolibri.apk.unsigned # Build debug APK
169+
make kolibri.apk # Build release APK (requires signing keys)
170+
make kolibri.aab # Build release AAB for Play Store
171+
make install # Install to connected device/emulator
172+
make test # Run unit tests
173+
make lint # Run Android linter
174+
make emulator # Start the emulator
175+
make logcat # View Kolibri-specific logs
176+
make clean # Clean build artifacts
177+
```
70178

179+
## Testing
71180

72-
## Helpful commands
73-
- [adb](https://developer.android.com/studio/command-line/adb) is pretty helpful. Here are some useful uses:
74-
- `adb logcat -b all -c` will clear out the device's log. ([Docs](https://developer.android.com/studio/command-line/logcat))
75-
- Logcat also has a large variety of filtering options. Check out the docs for those.
76-
- Uninstall from terminal using `adb shell pm uninstall org.learningequality.Kolibri`. ([Docs](https://developer.android.com/studio/command-line/adb#pm))
77-
- Docker shouldn't be rebuilding very often, so it shouldn't be using that much storage. But if it does, you can run `docker system prune` to clear out all "dangling" images, containers, and layers. If you've been constantly rebuilding, it will likely get you several gigabytes of storage.
181+
```bash
182+
# Run unit tests
183+
make test
78184

79-
## Build on Docker
185+
# Start emulator and install
186+
make emulator
187+
make install
80188

81-
This project was previously developed on Docker, but this method has not recently been tested.
189+
# View logs
190+
make logcat
191+
```
82192

83-
1. Install [docker](https://www.docker.com/community-edition)
193+
## Debugging
84194

85-
2. Build or download a Kolibri WHL file, and place in the `whl/` directory.
195+
```bash
196+
# Kolibri logs
197+
adb logcat -s Kolibri*:V
86198

87-
3. Run `make run_docker`.
199+
# Python logs
200+
adb logcat -v brief python:D *:F
88201

89-
4. The generated APK will end up in the `bin/` folder.
202+
# Task worker process
203+
adb logcat --pid=$(adb shell pidof -s org.learningequality.Kolibri:task_worker)
90204

91-
## Docker Implementation Notes
92-
The image was optimized to limit rebuilding and to be run in a developer-centric way. `scripts/rundocker.sh` describes the options needed to get the build running properly.
205+
# WebView debugging: Chrome → chrome://inspect
206+
```
93207

94-
Unless you need to make edits to the build method or are debugging one of the build dependencies and would like to continue using docker, you shouldn't need to modify that script.
208+
## Common Issues
95209

96-
## Getting a Python shell within the running app context
210+
**Build fails**: Run `make setup` to ensure SDK is configured correctly
211+
```bash
212+
make clean && make setup
213+
```
97214

98-
We implemented code for an SSH server that allows connecting into a running Kolibri Android app and running code in an interactive Python shell. You can use this for developing, testing, and debugging Python code running inside the Android and Kolibri environments, which is handy especially for testing out Pyjnius code, checking environment variables, etc. This will soon be implemented as an Android service that can be turned on over ADB, but in the meantime you can use it a bit like you might use `import ipdb; ipdb.set_trace()` to get an interactive shell at a particular context in your code, as follows:
215+
**No notifications**: Grant notification permission in Android settings (required on Android 13+)
99216

100-
- Drop `import remoteshell` at the spot you want to have the shell get dropped in, and build/run the app.
101-
- Connect the device over ADB, e.g. via USB.
102-
- Run `adb forward tcp:4242 tcp:4242` (needs to be re-run if you disconnect and reconnect the device)
103-
- Run `ssh -p 4242 localhost`
104-
- If the device isn’t provisioned, any username/password will be accepted. Otherwise, use the admin credentials.
105-
- If you get an error about “ssh-rsa”, you can put the following SSH config in:
217+
**Tasks not running**: Check WorkManager state
218+
```bash
219+
adb logcat -s BaseTaskWorker:V WorkController:V
106220
```
107-
Host kolibri-android
108-
HostName localhost
109-
Port 4242
110-
PubkeyAcceptedAlgorithms +ssh-rsa
111-
HostkeyAlgorithms +ssh-rsa
221+
222+
**Emulator won't start**: Check available AVDs and recreate if needed
223+
```bash
224+
make list-avds
225+
make avd # Recreate AVD
112226
```
113-
Then, you should be able to just do “ssh kolibri-android”
114227

115-
## Updating Python for Android
228+
## Contributing
229+
230+
1. Create feature branch
231+
2. Make changes
232+
3. Run linting: `prek run --all-files`
233+
4. Run tests: `make test`
234+
5. Submit pull request
235+
236+
## License
237+
238+
See [LICENSE](LICENSE) file.
116239

117-
We maintain a fork of Python for Android that includes various changes we have made to the source code to support our specific needs. As P4A make new releases, we make a branch from the latest release tag, and then replay the commits on top of this tag using an interactive rebase. Sometimes, this allows us to drop commits as new features are merged into P4A. Our naming convention for the branch on our fork is `from_upstream_<tag_name>`. Any time we push new commits to this branch, we must also update the pinned commit in `requirements.txt`, so that we are always building with a completely predictable version of Python for Android.
240+
## Support
118241

119-
By default we stash any updates to our bootstrap coming from Python for Android, because mostly we have overwritten their bootstrap code to make the relevant changes for us. If there are upstream changes to code we have committed in this repo from the bootstraps, then if the diff is small, it is probably simplest to manually copy in these changes to our committed code. If the diff is larger, or the developer fancies exercising some git-fu, then the make command `make update_project_from_p4a` will update the bootstrap from Python for Android, and not stash any changes that introduces. Through judicious change reversion and diffing, the appropriate changes can then be applied. Here be dragons.
242+
- **Issues**: https://github.com/learningequality/kolibri-installer-android/issues
243+
- **Kolibri Docs**: https://kolibri.readthedocs.io/
244+
- **Chaquopy Docs**: https://chaquo.com/chaquopy/doc/current/

0 commit comments

Comments
 (0)