Skip to content

Commit 5477e2b

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 2a20f36 commit 5477e2b

1 file changed

Lines changed: 196 additions & 72 deletions

File tree

README.md

Lines changed: 196 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,119 +1,243 @@
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+
python3 -m venv .venv && source .venv/bin/activate
21+
pip install -r build-requirements.txt -r requirements.txt
1522

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

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

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

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

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.
46+
### Initial Setup
2547

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.
48+
1. **Clone the repository**
49+
```bash
50+
git clone https://github.com/learningequality/kolibri-installer-android.git
51+
cd kolibri-installer-android
52+
```
2753

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.
54+
2. **Set up Python virtual environment**
55+
```bash
56+
python3 -m venv .venv
57+
source .venv/bin/activate
58+
pip install -r build-requirements.txt -r requirements.txt
59+
```
2960

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.
61+
3. **Set up Android SDK** (automatically downloads SDK, NDK, emulator)
62+
```bash
63+
make setup
64+
```
3165

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.
66+
4. **Get Kolibri tar file**
67+
```bash
68+
make get-tar tar=<URL_TO_KOLIBRI_TAR>
69+
```
3370

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.
71+
5. **Build the APK**
72+
```bash
73+
make kolibri.apk.unsigned
74+
```
3575

36-
## Debugging the app
76+
## Architecture
3777

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.
78+
The app runs Kolibri as an HTTP server in Python, with a WebView displaying the UI:
3979

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

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.
106+
- **KolibriServerService** - Starts Python HTTP server in background
107+
- **WebViewActivity** - Displays Kolibri UI, requests notification permission
108+
- **WorkManager** - Runs background tasks (imports, syncs) in separate process
109+
- **Task Reconciler** - Syncs WorkManager state with Kolibri's job database
43110

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

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

142+
## Building
49143

50-
## Running the apk from the terminal
144+
### Debug Build
51145

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

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

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.
152+
### Release Build
66153

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

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).
162+
### All Make Targets
163+
164+
```bash
165+
make help # Show all available targets
166+
make setup # Setup SDK, NDK, and emulator
167+
make kolibri.apk.unsigned # Build debug APK
168+
make kolibri.apk # Build release APK (requires signing keys)
169+
make kolibri.aab # Build release AAB for Play Store
170+
make install # Install to connected device/emulator
171+
make test # Run unit tests
172+
make lint # Run Android linter
173+
make emulator # Start the emulator
174+
make logcat # View Kolibri-specific logs
175+
make clean # Clean build artifacts
176+
```
70177

178+
## Testing
71179

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.
180+
```bash
181+
# Run unit tests
182+
make test
78183

79-
## Build on Docker
184+
# Start emulator and install
185+
make emulator
186+
make install
80187

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

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

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

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

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

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.
204+
# WebView debugging: Chrome → chrome://inspect
205+
```
93206

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.
207+
## Common Issues
95208

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

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:
214+
**No notifications**: Grant notification permission in Android settings (required on Android 13+)
99215

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:
216+
**Tasks not running**: Check WorkManager state
217+
```bash
218+
adb logcat -s BaseTaskWorker:V WorkController:V
106219
```
107-
Host kolibri-android
108-
HostName localhost
109-
Port 4242
110-
PubkeyAcceptedAlgorithms +ssh-rsa
111-
HostkeyAlgorithms +ssh-rsa
220+
221+
**Emulator won't start**: Check available AVDs and recreate if needed
222+
```bash
223+
make list-avds
224+
make avd # Recreate AVD
112225
```
113-
Then, you should be able to just do “ssh kolibri-android”
114226

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

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.
239+
## Support
118240

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.
241+
- **Issues**: https://github.com/learningequality/kolibri-installer-android/issues
242+
- **Kolibri Docs**: https://kolibri.readthedocs.io/
243+
- **Chaquopy Docs**: https://chaquo.com/chaquopy/doc/current/

0 commit comments

Comments
 (0)